diff --git a/src/main/python/main.py b/src/main/python/main.py index 9d2ae19..a189160 100644 --- a/src/main/python/main.py +++ b/src/main/python/main.py @@ -64,7 +64,7 @@ if __name__ == '__main__': appctxt = ApplicationContext() # 1. Instantiate ApplicationContext init_logger() qt_exception_hook = UncaughtHook() - window = MainWindow() + window = MainWindow(appctxt) window.resize(WINDOW_WIDTH, WINDOW_HEIGHT) window.show() exit_code = appctxt.app.exec_() # 2. Invoke appctxt.app.exec_() diff --git a/src/main/python/main_window.py b/src/main/python/main_window.py index 7443428..8c32fe8 100644 --- a/src/main/python/main_window.py +++ b/src/main/python/main_window.py @@ -28,8 +28,9 @@ import themes class MainWindow(QMainWindow): - def __init__(self): + def __init__(self, appctx): super().__init__() + self.appctx = appctx self.settings = QSettings("Vial", "Vial") themes.set_theme(self.get_theme()) @@ -57,7 +58,7 @@ class MainWindow(QMainWindow): self.keymap_editor = KeymapEditor(self.layout_editor) self.firmware_flasher = FirmwareFlasher(self) self.macro_recorder = MacroRecorder() - self.qmk_settings = QmkSettings() + self.qmk_settings = QmkSettings(self.appctx) self.matrix_tester = MatrixTest(self.layout_editor) self.rgb_configurator = RGBConfigurator() diff --git a/src/main/python/qmk_settings.py b/src/main/python/qmk_settings.py index c9e6720..3b0f823 100644 --- a/src/main/python/qmk_settings.py +++ b/src/main/python/qmk_settings.py @@ -1,41 +1,105 @@ # SPDX-License-Identifier: GPL-2.0-or-later +import json import struct from PyQt5 import QtCore -from PyQt5.QtWidgets import QVBoxLayout, QCheckBox, QGridLayout, QLabel, QWidget, QSizePolicy +from PyQt5.QtWidgets import QVBoxLayout, QCheckBox, QGridLayout, QLabel, QWidget, QSizePolicy, QTabWidget, QSpinBox, \ + QHBoxLayout, QPushButton from basic_editor import BasicEditor from vial_device import VialKeyboard +class GenericOption: + + def __init__(self, option, container): + self.row = container.rowCount() + self.option = option + self.container = container + + self.container.addWidget(QLabel(option["title"]), self.row, 0) + + +class BooleanOption(GenericOption): + + def __init__(self, option, container): + super().__init__(option, container) + + self.checkbox = QCheckBox() + self.container.addWidget(self.checkbox, self.row, 1) + + +class IntegerOption(GenericOption): + + def __init__(self, option ,container): + super().__init__(option, container) + + self.spinbox = QSpinBox() + self.container.addWidget(self.spinbox, self.row, 1) + + class QmkSettings(BasicEditor): - def __init__(self): + def __init__(self, appctx): super().__init__() - - w = QWidget() - w.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) - self.container = QGridLayout() - w.setLayout(self.container) - self.addWidget(w) - self.setAlignment(w, QtCore.Qt.AlignHCenter) - - self.container.addWidget(QLabel("Always send Escape if Alt is pressed"), 0, 0) - self.container.addWidget(QCheckBox(), 0, 1) - self.container.addWidget(QLabel("Always send Escape if Control is pressed"), 1, 0) - self.chk_ctrl = QCheckBox() - self.chk_ctrl.stateChanged.connect(self.on_checked) - self.container.addWidget(self.chk_ctrl, 1, 1) - self.container.addWidget(QLabel("Always send Escape if GUI is pressed"), 2, 0) - self.container.addWidget(QCheckBox(), 2, 1) - self.container.addWidget(QLabel("Always send Escape if Shift is pressed"), 3, 0) - self.container.addWidget(QCheckBox(), 3, 1) - + self.appctx = appctx self.keyboard = None + self.tabs_widget = QTabWidget() + self.addWidget(self.tabs_widget) + buttons = QHBoxLayout() + buttons.addStretch() + buttons.addWidget(QPushButton("Save")) + buttons.addWidget(QPushButton("Undo")) + buttons.addWidget(QPushButton("Reset")) + self.addLayout(buttons) + + self.tabs = [] + self.create_gui() + + @staticmethod + def populate_tab(tab, container): + options = [] + for field in tab["fields"]: + if field["type"] == "boolean": + options.append(BooleanOption(field, container)) + elif field["type"] == "integer": + options.append(IntegerOption(field, container)) + else: + raise RuntimeError("unsupported field type: {}".format(field)) + return options + + def create_gui(self): + with open(self.appctx.get_resource("qmk_settings.json"), "r") as inf: + settings = json.load(inf) + + for tab in settings["tabs"]: + w = QWidget() + w.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) + container = QGridLayout() + w.setLayout(container) + l = QVBoxLayout() + l.addWidget(w) + l.setAlignment(w, QtCore.Qt.AlignHCenter) + w2 = QWidget() + w2.setLayout(l) + self.tabs_widget.addTab(w2, tab["name"]) + self.tabs.append(self.populate_tab(tab, container)) + + # self.container.addWidget(QLabel("Always send Escape if Alt is pressed"), 0, 0) + # self.container.addWidget(QCheckBox(), 0, 1) + # self.container.addWidget(QLabel("Always send Escape if Control is pressed"), 1, 0) + # self.chk_ctrl = QCheckBox() + # self.chk_ctrl.stateChanged.connect(self.on_checked) + # self.container.addWidget(self.chk_ctrl, 1, 1) + # self.container.addWidget(QLabel("Always send Escape if GUI is pressed"), 2, 0) + # self.container.addWidget(QCheckBox(), 2, 1) + # self.container.addWidget(QLabel("Always send Escape if Shift is pressed"), 3, 0) + # self.container.addWidget(QCheckBox(), 3, 1) + def reload_settings(self): gresc = self.keyboard.qmk_settings_get(1)[0] - self.chk_ctrl.setChecked(gresc & 2) + # self.chk_ctrl.setChecked(gresc & 2) def on_checked(self, state): data = struct.pack("B", int(self.chk_ctrl.isChecked()) * 2) diff --git a/src/main/resources/base/qmk_settings.json b/src/main/resources/base/qmk_settings.json new file mode 100644 index 0000000..6244779 --- /dev/null +++ b/src/main/resources/base/qmk_settings.json @@ -0,0 +1,48 @@ +{ + "tabs": [ + { + "name": "Grave Escape", + "fields": [ + { "type": "boolean", "title": "Always send Escape if Alt is pressed", "qsid": 1, "bit": 0 }, + { "type": "boolean", "title": "Always send Escape if Control is pressed", "qsid": 1, "bit": 1 }, + { "type": "boolean", "title": "Always send Escape if GUI is pressed", "qsid": 1, "bit": 2 }, + { "type": "boolean", "title": "Always send Escape if Shift is pressed", "qsid": 1, "bit": 3 } + ] + }, + { + "name": "Debounce", + "fields": [ + { "type": "integer", "title": "Debounce time (ms)", "qsid": 2, "min": 0, "max": 1000 } + ] + }, + { + "name": "Auto Shift", + "fields": [ + { "type": "boolean", "title": "Enable", "qsid": 3, "bit": 0 }, + { "type": "boolean", "title": "Enable for modifiers", "qsid": 3, "bit": 1 }, + { "type": "integer", "title": "Timeout", "qsid": 4, "min": 0, "max": 1000 }, + { "type": "boolean", "title": "Do not Auto Shift special keys", "qsid": 3, "bit": 2 }, + { "type": "boolean", "title": "Do not Auto Shift numeric keys", "qsid": 3, "bit": 3 }, + { "type": "boolean", "title": "Do not Auto Shift alpha characters", "qsid": 3, "bit": 4 }, + { "type": "boolean", "title": "Enable keyrepeat", "qsid": 3, "bit": 5 }, + { "type": "boolean", "title": "Disable automatically keyrepeating when AUTO_SHIFT_TIMEOUT is exceeded", "qsid": 3, "bit": 6 } + ] + }, + { + "name": "One Shot Keys", + "fields": [ + { "type": "integer", "title": "Tapping this number of times holds the key until tapped once again", "qsid": 5, "min": 0, "max": 50 }, + { "type": "integer", "title": "Time (in ms) before the one shot key is released", "qsid": 6, "min": 0, "max": 100000 } + ] + }, + { + "name": "Tap-Hold", + "fields": [ + { "type": "integer", "title": "TAPPING_TERM", "qsid": 7, "min": 0, "max": 10000 }, + { "type": "boolean", "title": "PERMISSIVE_HOLD", "qsid": 8, "bit": 0 }, + { "type": "boolean", "title": "IGNORE_MOD_TAP_INTERRUPT", "qsid": 8, "bit": 1 }, + { "type": "boolean", "title": "TAPPING_FORCE_HOLD", "qsid": 8, "bit": 2 } + ] + } + ] +}