add support for language-specific layouts
parent
5a1322c1be
commit
49dc6d21ab
|
|
@ -5,8 +5,9 @@ from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QVBoxLayout
|
|||
|
||||
from clickable_label import ClickableLabel
|
||||
from keyboard_widget import KeyboardWidget, EncoderWidget
|
||||
from keycodes import keycode_label, keycode_tooltip, keycode_is_mask
|
||||
from keycodes import keycode_label, keycode_tooltip, keycode_is_mask, find_keycode
|
||||
from constants import LAYER_BTN_STYLE, ACTIVE_LAYER_BTN_STYLE
|
||||
from keymaps import KEYMAPS
|
||||
from util import tr
|
||||
|
||||
|
||||
|
|
@ -41,6 +42,8 @@ class KeyboardContainer(QWidget):
|
|||
|
||||
layout_editor.changed.connect(self.on_layout_changed)
|
||||
|
||||
self.keymap_override = KEYMAPS[0][1]
|
||||
|
||||
def rebuild_layers(self):
|
||||
# delete old layer labels
|
||||
for label in self.layer_labels:
|
||||
|
|
@ -67,6 +70,17 @@ class KeyboardContainer(QWidget):
|
|||
self.current_layer = 0
|
||||
self.on_layout_changed()
|
||||
|
||||
def code_is_overriden(self, code):
|
||||
""" Check whether a country-specific keymap overrides a code """
|
||||
key = find_keycode(code)
|
||||
return key is not None and key.qmk_id in self.keymap_override
|
||||
|
||||
def get_label(self, code):
|
||||
""" Get label for a specific keycode """
|
||||
if self.code_is_overriden(code):
|
||||
return self.keymap_override[find_keycode(code).qmk_id]
|
||||
return keycode_label(code)
|
||||
|
||||
def refresh_layer_display(self):
|
||||
""" Refresh text on key widgets to display data corresponding to current layer """
|
||||
|
||||
|
|
@ -82,16 +96,20 @@ class KeyboardContainer(QWidget):
|
|||
else:
|
||||
code = self.keyboard.encoder_layout[(self.current_layer, widget.desc.encoder_idx,
|
||||
widget.desc.encoder_dir)]
|
||||
text = keycode_label(code)
|
||||
text = self.get_label(code)
|
||||
tooltip = keycode_tooltip(code)
|
||||
mask = keycode_is_mask(code)
|
||||
mask_text = keycode_label(code & 0xFF)
|
||||
mask_text = self.get_label(code & 0xFF)
|
||||
if mask:
|
||||
text = text.split("\n")[0]
|
||||
widget.masked = mask
|
||||
widget.setText(text)
|
||||
widget.setMaskText(mask_text)
|
||||
widget.setToolTip(tooltip)
|
||||
if self.code_is_overriden(code):
|
||||
widget.setColor(Qt.blue)
|
||||
else:
|
||||
widget.setColor(None)
|
||||
self.container.update()
|
||||
self.container.updateGeometry()
|
||||
|
||||
|
|
@ -153,3 +171,7 @@ class KeyboardContainer(QWidget):
|
|||
|
||||
self.refresh_layer_display()
|
||||
self.keyboard.set_layout_options(self.layout_editor.pack())
|
||||
|
||||
def set_keymap_override(self, override):
|
||||
self.keymap_override = override
|
||||
self.refresh_layer_display()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class KeyWidget:
|
|||
self.text = ""
|
||||
self.mask_text = ""
|
||||
self.tooltip = ""
|
||||
self.color = None
|
||||
|
||||
self.rotation_x = (KEY_WIDTH + KEY_SPACING) * desc.rotation_x
|
||||
self.rotation_y = (KEY_HEIGHT + KEY_SPACING) * desc.rotation_y
|
||||
|
|
@ -93,6 +94,9 @@ class KeyWidget:
|
|||
def setActive(self, active):
|
||||
self.active = active
|
||||
|
||||
def setColor(self, color):
|
||||
self.color = color
|
||||
|
||||
|
||||
class EncoderWidget(KeyWidget):
|
||||
|
||||
|
|
@ -248,7 +252,8 @@ class KeyboardWidget(QWidget):
|
|||
qp.rotate(key.rotation_angle)
|
||||
qp.translate(-key.rotation_x, -key.rotation_y)
|
||||
|
||||
if key.active or (self.active_key == key and not self.active_mask):
|
||||
active = key.active or (self.active_key == key and not self.active_mask)
|
||||
if active:
|
||||
qp.setPen(active_pen)
|
||||
qp.setBrush(active_brush)
|
||||
|
||||
|
|
@ -259,16 +264,24 @@ class KeyboardWidget(QWidget):
|
|||
# if this is a mask key, draw the inner key
|
||||
if key.masked:
|
||||
qp.setFont(mask_font)
|
||||
qp.save()
|
||||
if key.color is not None and not active:
|
||||
qp.setPen(key.color)
|
||||
qp.drawText(key.nonmask_rect, Qt.AlignCenter, key.text)
|
||||
qp.restore()
|
||||
|
||||
if self.active_key == key and self.active_mask:
|
||||
qp.setPen(active_pen)
|
||||
qp.setBrush(active_brush)
|
||||
|
||||
qp.drawRect(key.mask_rect)
|
||||
if key.color is not None and not active:
|
||||
qp.setPen(key.color)
|
||||
qp.drawText(key.mask_rect, Qt.AlignCenter, key.mask_text)
|
||||
else:
|
||||
# draw the legend
|
||||
if key.color is not None and not active:
|
||||
qp.setPen(key.color)
|
||||
qp.drawText(key.rect, Qt.AlignCenter, key.text)
|
||||
|
||||
qp.restore()
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ class Keycode:
|
|||
|
||||
masked_keycodes = set()
|
||||
recorder_alias_to_keycode = dict()
|
||||
qmk_id_to_keycode = dict()
|
||||
|
||||
def __init__(self, code, qmk_id, label, tooltip=None, masked=False, printable=None, recorder_alias=None):
|
||||
self.code = code
|
||||
self.qmk_id = qmk_id
|
||||
self.qmk_id_to_keycode[qmk_id] = self
|
||||
self.label = label
|
||||
self.tooltip = tooltip
|
||||
# whether this keycode requires another sub-keycode
|
||||
|
|
@ -31,6 +33,10 @@ class Keycode:
|
|||
def find_by_recorder_alias(cls, alias):
|
||||
return cls.recorder_alias_to_keycode.get(alias)
|
||||
|
||||
@classmethod
|
||||
def find_by_qmk_id(cls, qmk_id):
|
||||
return cls.qmk_id_to_keycode.get(qmk_id)
|
||||
|
||||
|
||||
K = Keycode
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
# coding: utf-8
|
||||
|
||||
keymap = {
|
||||
"KC_GRAVE": "²",
|
||||
"KC_1": "1\n&",
|
||||
"KC_2": "2\né",
|
||||
"KC_3": '3\n"',
|
||||
"KC_4": "4\n'",
|
||||
"KC_5": "5\n(",
|
||||
"KC_6": "6\n-",
|
||||
"KC_7": "7\nè",
|
||||
"KC_8": "8\n_",
|
||||
"KC_9": "9\nç",
|
||||
"KC_0": "0\nà",
|
||||
"KC_MINUS": "°\n)",
|
||||
"KC_Q": "A",
|
||||
"KC_W": "Z",
|
||||
"KC_LBRACKET": "¨\n^",
|
||||
"KC_RBRACKET": "£\n$",
|
||||
"KC_A": "Q",
|
||||
"KC_SCOLON": "M",
|
||||
"KC_QUOTE": "%\nù",
|
||||
"KC_NONUS_HASH": "µ\n*",
|
||||
"KC_NONUS_BSLASH": ">\n<",
|
||||
"KC_Z": "W",
|
||||
"KC_M": "?\n,",
|
||||
"KC_COMMA": ".\n;",
|
||||
"KC_DOT": "/\n:",
|
||||
"KC_SLASH": "§\n!",
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# coding: utf-8
|
||||
|
||||
keymap = {
|
||||
"KC_GRAVE": "°\n^",
|
||||
"KC_2": '"\n2',
|
||||
"KC_3": "§\n3",
|
||||
"KC_6": "&\n6",
|
||||
"KC_7": "/\n7",
|
||||
"KC_8": "(\n8",
|
||||
"KC_9": ")\n9",
|
||||
"KC_0": "=\n0",
|
||||
"KC_MINUS": "?\nß",
|
||||
"KC_EQUAL": "`\n´",
|
||||
"KC_Y": "Z",
|
||||
"KC_LBRACKET": "Ü",
|
||||
"KC_RBRACKET": "*\n+",
|
||||
"KC_SCOLON": "Ö",
|
||||
"KC_QUOTE": "Ä",
|
||||
"KC_NONUS_HASH": "'\n#",
|
||||
"KC_NONUS_BSLASH": ">\n<",
|
||||
"KC_Z": "Y",
|
||||
"KC_COMMA": ";\n,",
|
||||
"KC_DOT": ":\n.",
|
||||
"KC_SLASH": "_\n-",
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# coding: utf-8
|
||||
|
||||
keymap = {
|
||||
"KC_GRAVE": "Ё",
|
||||
"KC_2": '"\n2',
|
||||
"KC_3": "№\n3",
|
||||
"KC_4": ";\n4",
|
||||
"KC_6": ":\n6",
|
||||
"KC_7": "?\n7",
|
||||
"KC_Q": "Q\nЙ",
|
||||
"KC_W": "W\nЦ",
|
||||
"KC_E": "E\nУ",
|
||||
"KC_R": "R\nК",
|
||||
"KC_T": "T\nЕ",
|
||||
"KC_Y": "Y\nН",
|
||||
"KC_U": "U\nГ",
|
||||
"KC_I": "I\nШ",
|
||||
"KC_O": "O\nЩ",
|
||||
"KC_P": "P\nЗ",
|
||||
"KC_LBRACKET": "{\n[ Х",
|
||||
"KC_RBRACKET": "}\n] Ъ",
|
||||
"KC_BSLASH": "| /\n\\",
|
||||
"KC_A": "A\nФ",
|
||||
"KC_S": "S\nЫ",
|
||||
"KC_D": "D\nВ",
|
||||
"KC_F": "F\nА",
|
||||
"KC_G": "G\nП",
|
||||
"KC_H": "H\nР",
|
||||
"KC_J": "J\nО",
|
||||
"KC_K": "K\nЛ",
|
||||
"KC_L": "L\nД",
|
||||
"KC_SCOLON": ":\n; Ж",
|
||||
"KC_QUOTE": "\"\n' Э",
|
||||
"KC_Z": "Z\nЯ",
|
||||
"KC_X": "X\nЧ",
|
||||
"KC_C": "C\nС",
|
||||
"KC_V": "V\nМ",
|
||||
"KC_B": "B\nИ",
|
||||
"KC_N": "N\nТ",
|
||||
"KC_M": "M\nЬ",
|
||||
"KC_COMMA": "<\n, Б",
|
||||
"KC_DOT": ">\n. Ю",
|
||||
"KC_SLASH": "? ,\n/ ."
|
||||
}
|
||||
|
|
@ -41,3 +41,7 @@ class KeymapEditor(BasicEditor):
|
|||
|
||||
def restore_layout(self, data):
|
||||
self.keyboard_container.restore_layout(data)
|
||||
|
||||
def set_keymap_override(self, override):
|
||||
self.keyboard_container.set_keymap_override(override)
|
||||
self.tabbed_keycodes.set_keymap_override(override)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
from keycodes import Keycode
|
||||
from keymap import french, german, russian
|
||||
|
||||
KEYMAPS = [
|
||||
("QWERTY", dict()),
|
||||
("French (AZERTY)", french.keymap),
|
||||
("German (QWERTZ)", german.keymap),
|
||||
("Russian (ЙЦУКЕН)", russian.keymap),
|
||||
]
|
||||
|
||||
# make sure that qmk IDs we used are all correct
|
||||
for name, keymap in KEYMAPS:
|
||||
for qmk_id in keymap.keys():
|
||||
if Keycode.find_by_qmk_id(qmk_id) is None:
|
||||
raise RuntimeError("Misconfigured - cannot find QMK keycode {}".format(qmk_id))
|
||||
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QWidget, QComboBox, QToolButton, QHBoxLayout, QVBoxLayout, QMainWindow, QAction, qApp, \
|
||||
QFileDialog, QDialog, QTabWidget
|
||||
QFileDialog, QDialog, QTabWidget, QActionGroup
|
||||
|
||||
import json
|
||||
|
||||
from firmware_flasher import FirmwareFlasher
|
||||
from keymap_editor import KeymapEditor
|
||||
from keymaps import KEYMAPS
|
||||
from layout_editor import LayoutEditor
|
||||
from macro_recorder import MacroRecorder
|
||||
from unlocker import Unlocker
|
||||
|
|
@ -94,6 +95,17 @@ class MainWindow(QMainWindow):
|
|||
keyboard_reset_act = QAction(tr("MenuSecurity", "Reboot to bootloader"), self)
|
||||
keyboard_reset_act.triggered.connect(self.reboot_to_bootloader)
|
||||
|
||||
keyboard_layout_menu = self.menuBar().addMenu(tr("Menu", "Keyboard layout"))
|
||||
keymap_group = QActionGroup(self)
|
||||
for idx, keymap in enumerate(KEYMAPS):
|
||||
act = QAction(tr("KeyboardLayout", keymap[0]), self)
|
||||
act.triggered.connect(lambda checked, x=idx: self.change_keyboard_layout(x))
|
||||
act.setCheckable(True)
|
||||
if idx == 0:
|
||||
act.setChecked(True)
|
||||
keymap_group.addAction(act)
|
||||
keyboard_layout_menu.addAction(act)
|
||||
|
||||
self.security_menu = self.menuBar().addMenu(tr("Menu", "Security"))
|
||||
self.security_menu.addAction(keyboard_unlock_act)
|
||||
self.security_menu.addAction(keyboard_lock_act)
|
||||
|
|
@ -194,3 +206,6 @@ class MainWindow(QMainWindow):
|
|||
if isinstance(self.current_device, VialKeyboard):
|
||||
self.unlocker.perform_unlock(self.current_device.keyboard)
|
||||
self.current_device.keyboard.reset()
|
||||
|
||||
def change_keyboard_layout(self, index):
|
||||
self.keymap_editor.set_keymap_override(KEYMAPS[index][1])
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from constants import KEYCODE_BTN_WIDTH, KEYCODE_BTN_HEIGHT
|
|||
from flowlayout import FlowLayout
|
||||
from keycodes import keycode_tooltip, KEYCODES_BASIC, KEYCODES_ISO, KEYCODES_MACRO, KEYCODES_LAYERS, KEYCODES_QUANTUM, \
|
||||
KEYCODES_BACKLIGHT, KEYCODES_MEDIA
|
||||
from keymaps import KEYMAPS
|
||||
from util import tr
|
||||
|
||||
|
||||
|
|
@ -17,6 +18,8 @@ class TabbedKeycodes(QTabWidget):
|
|||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.keymap_override = None
|
||||
|
||||
self.tab_basic = QScrollArea()
|
||||
self.tab_iso = QScrollArea()
|
||||
self.tab_layers = QScrollArea()
|
||||
|
|
@ -25,6 +28,8 @@ class TabbedKeycodes(QTabWidget):
|
|||
self.tab_media = QScrollArea()
|
||||
self.tab_macro = QScrollArea()
|
||||
|
||||
self.widgets = []
|
||||
|
||||
for (tab, label, keycodes) in [
|
||||
(self.tab_basic, "Basic", KEYCODES_BASIC),
|
||||
(self.tab_iso, "ISO/JIS", KEYCODES_ISO),
|
||||
|
|
@ -40,7 +45,7 @@ class TabbedKeycodes(QTabWidget):
|
|||
elif tab == self.tab_macro:
|
||||
self.layout_macro = layout
|
||||
|
||||
self.create_buttons(layout, keycodes)
|
||||
self.widgets += self.create_buttons(layout, keycodes)
|
||||
|
||||
tab.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
|
||||
tab.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
|
|
@ -53,15 +58,17 @@ class TabbedKeycodes(QTabWidget):
|
|||
|
||||
self.layer_keycode_buttons = []
|
||||
self.macro_keycode_buttons = []
|
||||
self.set_keymap_override(KEYMAPS[0][1])
|
||||
|
||||
def create_buttons(self, layout, keycodes):
|
||||
buttons = []
|
||||
|
||||
for keycode in keycodes:
|
||||
btn = QPushButton(keycode.label.replace("&", "&&"))
|
||||
btn = QPushButton()
|
||||
btn.setFixedSize(KEYCODE_BTN_WIDTH, KEYCODE_BTN_HEIGHT)
|
||||
btn.setToolTip(keycode_tooltip(keycode.code))
|
||||
btn.clicked.connect(lambda st, k=keycode: self.keycode_changed.emit(k.code))
|
||||
btn.keycode = keycode
|
||||
layout.addWidget(btn)
|
||||
buttons.append(btn)
|
||||
|
||||
|
|
@ -73,3 +80,16 @@ class TabbedKeycodes(QTabWidget):
|
|||
btn.deleteLater()
|
||||
self.layer_keycode_buttons = self.create_buttons(self.layout_layers, KEYCODES_LAYERS)
|
||||
self.macro_keycode_buttons = self.create_buttons(self.layout_macro, KEYCODES_MACRO)
|
||||
|
||||
def set_keymap_override(self, override):
|
||||
self.keymap_override = override
|
||||
|
||||
for widget in self.widgets:
|
||||
qmk_id = widget.keycode.qmk_id
|
||||
if qmk_id in self.keymap_override:
|
||||
label = self.keymap_override[qmk_id]
|
||||
widget.setStyleSheet("QPushButton {color: blue;}")
|
||||
else:
|
||||
label = widget.keycode.label
|
||||
widget.setStyleSheet("QPushButton {}")
|
||||
widget.setText(label.replace("&", "&&"))
|
||||
|
|
|
|||
Loading…
Reference in New Issue