aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--main.py50
-rw-r--r--tkinter.py50
3 files changed, 105 insertions, 1 deletions
diff --git a/README.md b/README.md
index 9319663..6ee5021 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,11 @@ To create a card deck, use `sqlite3 cards "CREATE TABLE cards (idx INTEGER PRIMA
Now build this project with `gcc sd.c segmenttree.c -o sd -lsqlite3 -O2 -march=native` and run `./sd` to enjoy a fast flash cards experience! The program will display the `key` of a randomly selected card. Press any key to show the `val` of the card. Now press either `y` or `n` depending on whether you got the card correct, and the program adjusts that card's weight.
-If you're wondering where the name came from, this is the C port of [SD](https://git.exozy.me/a/SD), which was named after a common type of flash card. 😀
+There is also a Python GUI which requires PyQt6. Run it with `python main.py PATH_TO_SDC_BINARY`. You can optionally pass additional flags to SDC. Alternatively, there is a Tkinter GUI, `python tkinter.py PATH_TO_SD_BINARY`, that only requires the Python standard library but does not support Wayland. The GUIs are not compatible with the original unmaintained SD which lacks noninteractive mode, but it would be easy to add that to SD.
+
+It should also be fairly easy to write your own SD clone or GUI, so if you write one, I'd be glad to link to it here.
+
+If you're wondering where the name came from, this is the C port of SD, which was named after a common type of flash card. 😀
## Performance
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..8bba5b5
--- /dev/null
+++ b/main.py
@@ -0,0 +1,50 @@
+import subprocess
+import sys
+from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel
+from PyQt6.QtGui import QFont
+from PyQt6.QtCore import Qt
+
+
+class SDGUI(QMainWindow):
+ def __init__(self):
+ super().__init__()
+
+ self.setGeometry(100, 100, 1280, 720)
+ self.setWindowTitle('SD GUI')
+
+ # Start SD
+ self.proc = subprocess.Popen(
+ sys.argv[1:] + ['-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE
+ )
+
+ # Display key
+ key = self.proc.stdout.readline().decode('utf-8')
+ self.label = QLabel(key, self)
+ self.label.setFont(QFont('Arial', 48))
+ self.label.setWordWrap(True)
+ self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.setCentralWidget(self.label)
+
+ def keyPressEvent(self, event):
+ if self.label.text().count('\n') == 2 and event.text() not in 'yn':
+ # Manually quit here because stdout.readline blocks and doesn't error when SD exits
+ self.close()
+
+ # Write to SD
+ self.proc.stdin.write((event.text() + '\n').encode('utf-8'))
+ self.proc.stdin.flush()
+
+ # Get response
+ text = self.proc.stdout.readline().decode('utf-8')
+ if text.startswith('>'):
+ # Key
+ self.label.setText(text)
+ else:
+ # Value
+ self.label.setText(self.label.text() + text)
+
+
+app = QApplication(sys.argv)
+window = SDGUI()
+window.show()
+app.exec()
diff --git a/tkinter.py b/tkinter.py
new file mode 100644
index 0000000..99dcaa1
--- /dev/null
+++ b/tkinter.py
@@ -0,0 +1,50 @@
+import subprocess
+import sys
+import tkinter
+from tkinter import ttk
+from tkinter import font
+
+
+# Initialize Tk root
+root = tkinter.Tk()
+root.geometry('1280x720')
+root.title('SD GUI')
+
+# Start SD
+proc = subprocess.Popen(
+ sys.argv[1:] + ['-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE
+)
+
+# Display key
+key = proc.stdout.readline().decode('utf-8')
+label = ttk.Label(root, text=key, font=font.Font(size=48), wraplength=900)
+label.place(relx=0.5, rely=0.5, anchor='center')
+
+
+def key_handler(event):
+ if label['text'].count('\n') == 2 and event.char not in 'yn':
+ # Manually quit here because stdout.readline blocks and doesn't error when SD exits
+ root.quit()
+
+ # Write to SD
+ proc.stdin.write((event.char + '\n').encode('utf-8'))
+ proc.stdin.flush()
+
+ # Get response
+ text = proc.stdout.readline().decode('utf-8')
+ if text.startswith('>'):
+ # Key
+ label['text'] = text
+ else:
+ # Value
+ label['text'] += text
+
+
+def on_closing():
+ root.quit()
+
+
+# Run the main loop
+root.bind('<Key>', key_handler)
+root.protocol('WM_DELETE_WINDOW', on_closing)
+root.mainloop()