145 lines
3.0 KiB
Go
145 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
qt "github.com/mappu/miqt/qt6"
|
|
"github.com/mappu/miqt/qt6/mainthread"
|
|
)
|
|
|
|
func formatButtonText(s string) string {
|
|
// Escape & used as an alt-accelerator
|
|
s = strings.ReplaceAll(s, `&`, `&&`)
|
|
|
|
// Replace " - " with newlines
|
|
s = strings.ReplaceAll(s, ` - `, "\n")
|
|
return s
|
|
}
|
|
|
|
func main() {
|
|
|
|
qt.NewQApplication(os.Args)
|
|
appName := "SortVote"
|
|
qt.QCoreApplication_SetApplicationName(appName)
|
|
|
|
if len(os.Args) < 2 {
|
|
qt.QMessageBox_Critical(nil, appName, "Usage: ./sortvote filename.txt")
|
|
os.Exit(1)
|
|
}
|
|
|
|
b, err := os.ReadFile(os.Args[1])
|
|
if err != nil {
|
|
qt.QMessageBox_Critical(nil, appName, fmt.Sprintf("Error reading file %q: %s", os.Args[1], err.Error()))
|
|
os.Exit(1)
|
|
}
|
|
|
|
file_lines := strings.Split(string(b), "\n")
|
|
entries := make([]string, 0, len(file_lines))
|
|
for _, line := range file_lines {
|
|
if len(line) > 0 && line[0] != '#' {
|
|
entries = append(entries, line)
|
|
}
|
|
}
|
|
|
|
// Start with a randomized order for fairness
|
|
|
|
for i := range entries {
|
|
j := rand.Intn(i + 1)
|
|
entries[i], entries[j] = entries[j], entries[i]
|
|
}
|
|
|
|
//
|
|
|
|
ui := NewMainWindowUi()
|
|
ui.MainWindow.SetWindowTitle(os.Args[1] + " - " + appName)
|
|
|
|
ui.infoArea.AddItem(fmt.Sprintf("Sorting %d entries", len(entries)))
|
|
ui.infoArea.AddItem("Use [Q] and [Z] to select with the keyboard")
|
|
ui.infoArea.ScrollToBottom()
|
|
|
|
active := true
|
|
ret := make(chan bool, 0)
|
|
ui.b1.OnClicked(func() { // Set up click handlers only once
|
|
ret <- true
|
|
})
|
|
ui.b2.OnClicked(func() {
|
|
ret <- false
|
|
})
|
|
|
|
ui.MainWindow.OnKeyPressEvent(func(super func(event *qt.QKeyEvent), event *qt.QKeyEvent) {
|
|
if !active {
|
|
super(event)
|
|
return
|
|
}
|
|
|
|
keytext := strings.ToLower(event.Text())
|
|
if keytext == `q` {
|
|
ret <- true
|
|
|
|
} else if keytext == `z` {
|
|
ret <- false
|
|
|
|
} else { // not our event
|
|
super(event)
|
|
}
|
|
|
|
})
|
|
|
|
ui.MainWindow.Show()
|
|
|
|
ui.infoArea.SetFocusPolicy(qt.NoFocus) // prevent stealing keyboard input
|
|
|
|
comparisons := 0
|
|
|
|
go func() {
|
|
sort.SliceStable(entries, func(i, j int) bool {
|
|
|
|
// Towards the end, sort.SliceStable has the pattern of putting one
|
|
// entry in the top button all the time
|
|
// Maybe shuffle them
|
|
shuf := (rand.Int()%2 == 0)
|
|
if shuf {
|
|
i, j = j, i
|
|
}
|
|
|
|
mainthread.Wait(func() {
|
|
comparisons++
|
|
|
|
ui.infoArea.AddItem(fmt.Sprintf("Comparison #%d", comparisons))
|
|
ui.infoArea.ScrollToBottom()
|
|
|
|
ui.b1.SetText(formatButtonText(entries[i]))
|
|
ui.b2.SetText(formatButtonText(entries[j]))
|
|
})
|
|
|
|
if shuf {
|
|
return !<-ret
|
|
}
|
|
return <-ret
|
|
})
|
|
active = false
|
|
|
|
mainthread.Start(func() {
|
|
ui.b1.SetVisible(false)
|
|
ui.b2.SetVisible(false)
|
|
|
|
// Sorting 39 entries took 77 comparisons
|
|
// Finished sorting 37 entries in 219 comparisons
|
|
// 37 x log_2(37) = 192 so we are 15% over(?)
|
|
|
|
ui.infoArea.AddItem(fmt.Sprintf("Finished sorting %d entries in %d comparisons", len(entries), comparisons))
|
|
|
|
for i, ent := range entries {
|
|
ui.infoArea.AddItem(fmt.Sprintf("%02d. %s", i+1, ent))
|
|
ui.infoArea.ScrollToBottom()
|
|
}
|
|
})
|
|
}()
|
|
|
|
qt.QApplication_Exec()
|
|
}
|