2020-10-16 21:26:10 -04:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
2020-10-16 15:16:28 -04:00
|
|
|
from PyQt5.QtCore import Qt
|
2020-10-17 06:08:52 -04:00
|
|
|
from PyQt5.QtWidgets import QWidget, QComboBox, QToolButton, QHBoxLayout, QVBoxLayout, QMainWindow, QAction, qApp, \
|
2020-12-02 02:47:11 -05:00
|
|
|
QFileDialog, QDialog, QTabWidget
|
2020-10-16 15:16:28 -04:00
|
|
|
|
2020-10-18 00:58:01 -04:00
|
|
|
import json
|
|
|
|
|
|
2020-12-02 02:47:11 -05:00
|
|
|
from firmware_flasher import FirmwareFlasher
|
2020-10-16 15:16:28 -04:00
|
|
|
from keyboard import Keyboard
|
2020-12-02 02:58:52 -05:00
|
|
|
from layout_editor import LayoutEditor
|
2020-10-16 15:16:28 -04:00
|
|
|
from util import tr, find_vial_keyboards, open_device
|
|
|
|
|
|
|
|
|
|
|
2020-10-17 06:08:52 -04:00
|
|
|
class MainWindow(QMainWindow):
|
2020-10-16 15:16:28 -04:00
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
2020-10-18 00:58:01 -04:00
|
|
|
self.keyboard = None
|
2020-10-16 15:16:28 -04:00
|
|
|
self.devices = []
|
2020-10-18 00:58:01 -04:00
|
|
|
self.sideload_json = None
|
|
|
|
|
self.sideload_vid = self.sideload_pid = -1
|
2020-10-16 15:16:28 -04:00
|
|
|
|
|
|
|
|
self.combobox_devices = QComboBox()
|
|
|
|
|
self.combobox_devices.currentIndexChanged.connect(self.on_device_selected)
|
|
|
|
|
|
|
|
|
|
btn_refresh_devices = QToolButton()
|
|
|
|
|
btn_refresh_devices.setToolButtonStyle(Qt.ToolButtonTextOnly)
|
|
|
|
|
btn_refresh_devices.setText(tr("MainWindow", "Refresh"))
|
|
|
|
|
btn_refresh_devices.clicked.connect(self.on_click_refresh)
|
|
|
|
|
|
|
|
|
|
layout_combobox = QHBoxLayout()
|
|
|
|
|
layout_combobox.addWidget(self.combobox_devices)
|
|
|
|
|
layout_combobox.addWidget(btn_refresh_devices)
|
|
|
|
|
|
2020-12-02 02:58:52 -05:00
|
|
|
self.layout_editor = LayoutEditor()
|
2020-12-02 02:47:11 -05:00
|
|
|
flasher = FirmwareFlasher()
|
|
|
|
|
|
|
|
|
|
tabs = QTabWidget()
|
2020-12-02 02:58:52 -05:00
|
|
|
for container, lbl in [(self.layout_editor, "Layout"), (flasher, "Firmware updater")]:
|
2020-12-02 02:47:11 -05:00
|
|
|
w = QWidget()
|
|
|
|
|
w.setLayout(container)
|
|
|
|
|
tabs.addTab(w, tr("MainWindow", lbl))
|
|
|
|
|
|
2020-10-16 15:16:28 -04:00
|
|
|
layout = QVBoxLayout()
|
|
|
|
|
layout.addLayout(layout_combobox)
|
2020-12-02 02:47:11 -05:00
|
|
|
layout.addWidget(tabs)
|
2020-10-17 06:08:52 -04:00
|
|
|
w = QWidget()
|
|
|
|
|
w.setLayout(layout)
|
|
|
|
|
self.setCentralWidget(w)
|
|
|
|
|
|
|
|
|
|
self.init_menu()
|
2020-10-16 15:16:28 -04:00
|
|
|
|
|
|
|
|
# make sure initial state is valid
|
|
|
|
|
self.on_click_refresh()
|
|
|
|
|
|
2020-10-17 06:08:52 -04:00
|
|
|
def init_menu(self):
|
2020-10-18 21:47:47 -04:00
|
|
|
layout_load_act = QAction(tr("MenuFile", "Load saved layout..."), self)
|
2020-10-17 06:08:52 -04:00
|
|
|
layout_load_act.setShortcut("Ctrl+O")
|
|
|
|
|
layout_load_act.triggered.connect(self.on_layout_load)
|
|
|
|
|
|
2020-10-18 21:47:47 -04:00
|
|
|
layout_save_act = QAction(tr("MenuFile", "Save current layout..."), self)
|
2020-10-17 06:08:52 -04:00
|
|
|
layout_save_act.setShortcut("Ctrl+S")
|
|
|
|
|
layout_save_act.triggered.connect(self.on_layout_save)
|
|
|
|
|
|
2020-10-18 21:47:47 -04:00
|
|
|
sideload_json_act = QAction(tr("MenuFile", "Sideload VIA JSON..."), self)
|
2020-10-18 00:58:01 -04:00
|
|
|
sideload_json_act.triggered.connect(self.on_sideload_json)
|
|
|
|
|
|
2020-10-17 06:08:52 -04:00
|
|
|
exit_act = QAction(tr("MenuFile", "Exit"), self)
|
|
|
|
|
exit_act.setShortcut("Ctrl+Q")
|
|
|
|
|
exit_act.triggered.connect(qApp.exit)
|
|
|
|
|
|
|
|
|
|
menubar = self.menuBar()
|
|
|
|
|
file_menu = self.menuBar().addMenu(tr("Menu", "File"))
|
|
|
|
|
file_menu.addAction(layout_load_act)
|
|
|
|
|
file_menu.addAction(layout_save_act)
|
|
|
|
|
file_menu.addSeparator()
|
2020-10-18 00:58:01 -04:00
|
|
|
file_menu.addAction(sideload_json_act)
|
|
|
|
|
file_menu.addSeparator()
|
2020-10-17 06:08:52 -04:00
|
|
|
file_menu.addAction(exit_act)
|
|
|
|
|
|
|
|
|
|
def on_layout_load(self):
|
|
|
|
|
dialog = QFileDialog()
|
|
|
|
|
dialog.setDefaultSuffix("vil")
|
|
|
|
|
dialog.setAcceptMode(QFileDialog.AcceptOpen)
|
|
|
|
|
dialog.setNameFilters(["Vial layout (*.vil)"])
|
|
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
|
|
with open(dialog.selectedFiles()[0], "rb") as inf:
|
|
|
|
|
data = inf.read()
|
2020-12-02 02:58:52 -05:00
|
|
|
self.layout_editor.restore_layout(data)
|
2020-10-17 06:08:52 -04:00
|
|
|
|
|
|
|
|
def on_layout_save(self):
|
|
|
|
|
dialog = QFileDialog()
|
|
|
|
|
dialog.setDefaultSuffix("vil")
|
|
|
|
|
dialog.setAcceptMode(QFileDialog.AcceptSave)
|
|
|
|
|
dialog.setNameFilters(["Vial layout (*.vil)"])
|
|
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
|
|
with open(dialog.selectedFiles()[0], "wb") as outf:
|
2020-12-02 02:58:52 -05:00
|
|
|
outf.write(self.layout_editor.save_layout())
|
2020-10-17 06:08:52 -04:00
|
|
|
|
2020-10-16 15:16:28 -04:00
|
|
|
def on_click_refresh(self):
|
2020-10-18 00:58:01 -04:00
|
|
|
self.devices = find_vial_keyboards(self.sideload_vid, self.sideload_pid)
|
2020-10-16 15:16:28 -04:00
|
|
|
self.combobox_devices.clear()
|
|
|
|
|
|
|
|
|
|
for dev in self.devices:
|
2020-10-18 00:58:01 -04:00
|
|
|
title = "{} {}".format(dev["manufacturer_string"], dev["product_string"])
|
|
|
|
|
if dev["vendor_id"] == self.sideload_vid and dev["product_id"] == self.sideload_pid:
|
|
|
|
|
title += " [sideload]"
|
|
|
|
|
self.combobox_devices.addItem(title)
|
2020-10-16 15:16:28 -04:00
|
|
|
|
|
|
|
|
def on_device_selected(self):
|
2020-10-18 00:58:01 -04:00
|
|
|
if self.keyboard is not None:
|
|
|
|
|
self.keyboard.dev.close()
|
2020-10-16 15:16:28 -04:00
|
|
|
idx = self.combobox_devices.currentIndex()
|
|
|
|
|
if idx >= 0:
|
2020-10-18 00:58:01 -04:00
|
|
|
dev = self.devices[idx]
|
|
|
|
|
self.keyboard = Keyboard(open_device(dev))
|
|
|
|
|
if dev["vendor_id"] == self.sideload_vid and dev["product_id"] == self.sideload_pid:
|
|
|
|
|
self.keyboard.reload(self.sideload_json)
|
|
|
|
|
else:
|
|
|
|
|
self.keyboard.reload()
|
2020-12-02 02:58:52 -05:00
|
|
|
self.layout_editor.rebuild(self.keyboard)
|
2020-10-18 00:58:01 -04:00
|
|
|
|
|
|
|
|
def on_sideload_json(self):
|
|
|
|
|
dialog = QFileDialog()
|
|
|
|
|
dialog.setDefaultSuffix("json")
|
|
|
|
|
dialog.setAcceptMode(QFileDialog.AcceptOpen)
|
|
|
|
|
dialog.setNameFilters(["VIA layout JSON (*.json)"])
|
|
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
|
|
with open(dialog.selectedFiles()[0], "rb") as inf:
|
|
|
|
|
data = inf.read()
|
|
|
|
|
self.sideload_json = json.loads(data)
|
|
|
|
|
self.sideload_vid = int(self.sideload_json["vendorId"], 16)
|
|
|
|
|
self.sideload_pid = int(self.sideload_json["productId"], 16)
|
|
|
|
|
self.on_click_refresh()
|