From aa53481d21101570107f474a6166c4687ccb15d0 Mon Sep 17 00:00:00 2001 From: Jared Beller Date: Mon, 11 Jan 2021 23:27:48 -0500 Subject: [PATCH 1/5] scale keycode buttons relative to text size --- src/main/python/constants.py | 3 +-- src/main/python/tabbed_keycodes.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/python/constants.py b/src/main/python/constants.py index f69ccd8..f0c70b3 100644 --- a/src/main/python/constants.py +++ b/src/main/python/constants.py @@ -4,8 +4,7 @@ KEY_WIDTH = 40 KEY_HEIGHT = KEY_WIDTH KEY_SPACING = 4 -KEYCODE_BTN_WIDTH = 50 -KEYCODE_BTN_HEIGHT = KEYCODE_BTN_WIDTH +KEYCODE_BTN_RATIO = 3 WINDOW_WIDTH, WINDOW_HEIGHT = 1024, 768 diff --git a/src/main/python/tabbed_keycodes.py b/src/main/python/tabbed_keycodes.py index f43d048..3f46110 100644 --- a/src/main/python/tabbed_keycodes.py +++ b/src/main/python/tabbed_keycodes.py @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later -from PyQt5.QtCore import Qt, pyqtSignal +from PyQt5.QtCore import Qt, QSize, pyqtSignal from PyQt5.QtWidgets import QTabWidget, QWidget, QPushButton, QScrollArea, QApplication from PyQt5.QtGui import QPalette -from constants import KEYCODE_BTN_WIDTH, KEYCODE_BTN_HEIGHT +from constants import KEYCODE_BTN_RATIO from flowlayout import FlowLayout from keycodes import keycode_tooltip, KEYCODES_BASIC, KEYCODES_ISO, KEYCODES_MACRO, KEYCODES_LAYERS, KEYCODES_QUANTUM, \ KEYCODES_BACKLIGHT, KEYCODES_MEDIA, KEYCODES_SPECIAL @@ -65,8 +65,7 @@ class TabbedKeycodes(QTabWidget): buttons = [] for keycode in keycodes: - btn = QPushButton() - btn.setFixedSize(KEYCODE_BTN_WIDTH, KEYCODE_BTN_HEIGHT) + btn = KeycodeButton() btn.setToolTip(keycode_tooltip(keycode.code)) btn.clicked.connect(lambda st, k=keycode: self.keycode_changed.emit(k.code)) btn.keycode = keycode @@ -100,3 +99,10 @@ class TabbedKeycodes(QTabWidget): label = widget.keycode.label widget.setStyleSheet("QPushButton {}") widget.setText(label.replace("&", "&&")) + + +class KeycodeButton(QPushButton): + + def sizeHint(self): + size = KEYCODE_BTN_RATIO * self.fontMetrics().height() + return QSize(size, size) From 8cf5957b26f424c6d19b417bc805c703123d6652 Mon Sep 17 00:00:00 2001 From: Jared Beller Date: Tue, 12 Jan 2021 18:58:47 -0500 Subject: [PATCH 2/5] scale keyboard widget relative to text size --- src/main/python/constants.py | 5 ++--- src/main/python/keyboard_widget.py | 36 +++++++++++++++++------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/python/constants.py b/src/main/python/constants.py index f0c70b3..54f49fa 100644 --- a/src/main/python/constants.py +++ b/src/main/python/constants.py @@ -1,8 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later -KEY_WIDTH = 40 -KEY_HEIGHT = KEY_WIDTH -KEY_SPACING = 4 +KEY_SIZE_RATIO = 2.667 +KEY_SPACING_RATIO = 0.267 KEYCODE_BTN_RATIO = 3 diff --git a/src/main/python/keyboard_widget.py b/src/main/python/keyboard_widget.py index 9e92ccb..9e77e88 100644 --- a/src/main/python/keyboard_widget.py +++ b/src/main/python/keyboard_widget.py @@ -4,12 +4,12 @@ from PyQt5.QtGui import QPainter, QColor, QPainterPath, QTransform, QBrush, QPol from PyQt5.QtWidgets import QWidget, QToolTip, QApplication from PyQt5.QtCore import Qt, QSize, QRect, QPointF, pyqtSignal, QEvent, QRectF -from constants import KEY_WIDTH, KEY_SPACING, KEY_HEIGHT, KEYBOARD_WIDGET_PADDING, KEYBOARD_WIDGET_MASK_PADDING +from constants import KEY_SIZE_RATIO, KEY_SPACING_RATIO, KEYBOARD_WIDGET_PADDING, KEYBOARD_WIDGET_MASK_PADDING class KeyWidget: - def __init__(self, desc, shift_x=0, shift_y=0): + def __init__(self, desc, scale, shift_x=0, shift_y=0): self.active = False self.masked = False self.desc = desc @@ -18,25 +18,28 @@ class KeyWidget: 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 + size = int(round(scale * (KEY_SIZE_RATIO + KEY_SPACING_RATIO))) + spacing = int(round(scale * KEY_SPACING_RATIO)) + + self.rotation_x = size * desc.rotation_x + self.rotation_y = size * desc.rotation_y self.rotation_angle = desc.rotation_angle self.shift_x = shift_x self.shift_y = shift_y - self.x = (KEY_WIDTH + KEY_SPACING) * desc.x - self.y = (KEY_HEIGHT + KEY_SPACING) * desc.y - self.w = (KEY_WIDTH + KEY_SPACING) * desc.width - KEY_SPACING - self.h = (KEY_HEIGHT + KEY_SPACING) * desc.height - KEY_SPACING + self.x = size * desc.x + self.y = size * desc.y + self.w = size * desc.width - spacing + self.h = size * desc.height - spacing self.rect = QRect(self.x, self.y, self.w, self.h) self.has2 = desc.width2 != desc.width or desc.height2 != desc.height or desc.x2 != 0 or desc.y2 != 0 - self.x2 = self.x + (KEY_WIDTH + KEY_SPACING) * desc.x2 - self.y2 = self.y + (KEY_WIDTH + KEY_SPACING) * desc.y2 - self.w2 = (KEY_WIDTH + KEY_SPACING) * desc.width2 - KEY_SPACING - self.h2 = (KEY_HEIGHT + KEY_SPACING) * desc.height2 - KEY_SPACING + self.x2 = self.x + size * desc.x2 + self.y2 = self.y + size * desc.y2 + self.w2 = size * desc.width2 - spacing + self.h2 = size * desc.height2 - spacing self.bbox = self.calculate_bbox(QRectF(self.x, self.y, self.w, self.h)) self.polygon = QPolygonF(self.bbox + [self.bbox[0]]) @@ -158,11 +161,12 @@ class KeyboardWidget(QWidget): def add_keys(self, keys): top_x = top_y = 1e6 + scale_factor = self.fontMetrics().height() # find the global top-left position, all the keys will be shifted to the left/up by that position for key, cls in keys: if key.layout_index == -1: - obj = cls(key) + obj = cls(key, scale_factor) p = obj.polygon.boundingRect().topLeft() top_x = min(top_x, p.x()) top_y = min(top_y, p.y()) @@ -170,7 +174,7 @@ class KeyboardWidget(QWidget): # obtain common widgets, that is, ones which are always displayed and require no extra transforms for key, cls in keys: if key.layout_index == -1: - self.common_widgets.append(cls(key, -top_x + KEYBOARD_WIDGET_PADDING, -top_y + KEYBOARD_WIDGET_PADDING)) + self.common_widgets.append(cls(key, scale_factor, -top_x + KEYBOARD_WIDGET_PADDING, -top_y + KEYBOARD_WIDGET_PADDING)) # top-left position for specific layout layout_x = defaultdict(lambda: defaultdict(lambda: 1e6)) @@ -179,7 +183,7 @@ class KeyboardWidget(QWidget): # determine top-left position for every layout option for key, cls in keys: if key.layout_index != -1: - obj = cls(key) + obj = cls(key, scale_factor) idx, opt = key.layout_index, key.layout_option p = obj.polygon.boundingRect().topLeft() layout_x[idx][opt] = min(layout_x[idx][opt], p.x()) @@ -191,7 +195,7 @@ class KeyboardWidget(QWidget): idx, opt = key.layout_index, key.layout_option shift_x = layout_x[idx][opt] - layout_x[idx][0] shift_y = layout_y[idx][opt] - layout_y[idx][0] - obj = cls(key, -shift_x - top_x + KEYBOARD_WIDGET_PADDING, -shift_y - top_y + KEYBOARD_WIDGET_PADDING) + obj = cls(key, scale_factor, -shift_x - top_x + KEYBOARD_WIDGET_PADDING, -shift_y - top_y + KEYBOARD_WIDGET_PADDING) self.widgets_for_layout[idx][opt].append(obj) def update_layout(self): From dc63304827c3b440252c9b61dacf40966b6da8cf Mon Sep 17 00:00:00 2001 From: Jared Beller Date: Tue, 12 Jan 2021 20:03:38 -0500 Subject: [PATCH 3/5] isolate logic for square ui buttons and fix layer selector --- src/main/python/constants.py | 2 +- src/main/python/keyboard_container.py | 5 +++-- src/main/python/square_button.py | 19 +++++++++++++++++++ src/main/python/tabbed_keycodes.py | 11 +++-------- 4 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 src/main/python/square_button.py diff --git a/src/main/python/constants.py b/src/main/python/constants.py index 54f49fa..45bddc5 100644 --- a/src/main/python/constants.py +++ b/src/main/python/constants.py @@ -3,7 +3,7 @@ KEY_SIZE_RATIO = 2.667 KEY_SPACING_RATIO = 0.267 -KEYCODE_BTN_RATIO = 3 +KEYCODE_BTN_RATIO = 3.333 WINDOW_WIDTH, WINDOW_HEIGHT = 1024, 768 diff --git a/src/main/python/keyboard_container.py b/src/main/python/keyboard_container.py index e30ecae..7801064 100644 --- a/src/main/python/keyboard_container.py +++ b/src/main/python/keyboard_container.py @@ -9,6 +9,7 @@ from keyboard_widget import KeyboardWidget, EncoderWidget 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 square_button import SquareButton from util import tr @@ -54,8 +55,8 @@ class KeyboardContainer(QWidget): # create new layer labels for x in range(self.keyboard.layers): - btn = QPushButton(str(x)) - btn.setFixedSize(25, 25) + btn = SquareButton(str(x)) + btn.setRelSize(1.667) btn.setCheckable(True) btn.clicked.connect(lambda state, idx=x: self.switch_layer(idx)) self.layout_layers.addWidget(btn) diff --git a/src/main/python/square_button.py b/src/main/python/square_button.py new file mode 100644 index 0000000..84ae2ae --- /dev/null +++ b/src/main/python/square_button.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +from PyQt5.QtCore import QSize +from PyQt5.QtWidgets import QPushButton + +class SquareButton(QPushButton): + + def __init__(self, parent=None): + super().__init__(parent) + + self.scale = 1.2 + + def setRelSize(self, ratio): + self.scale = ratio + self.updateGeometry() + + def sizeHint(self): + size = int(round(self.fontMetrics().height() * self.scale)) + return QSize(size, size) diff --git a/src/main/python/tabbed_keycodes.py b/src/main/python/tabbed_keycodes.py index 3f46110..3be1701 100644 --- a/src/main/python/tabbed_keycodes.py +++ b/src/main/python/tabbed_keycodes.py @@ -9,6 +9,7 @@ from flowlayout import FlowLayout from keycodes import keycode_tooltip, KEYCODES_BASIC, KEYCODES_ISO, KEYCODES_MACRO, KEYCODES_LAYERS, KEYCODES_QUANTUM, \ KEYCODES_BACKLIGHT, KEYCODES_MEDIA, KEYCODES_SPECIAL from keymaps import KEYMAPS +from square_button import SquareButton from util import tr @@ -65,7 +66,8 @@ class TabbedKeycodes(QTabWidget): buttons = [] for keycode in keycodes: - btn = KeycodeButton() + btn = SquareButton() + btn.setRelSize(KEYCODE_BTN_RATIO) btn.setToolTip(keycode_tooltip(keycode.code)) btn.clicked.connect(lambda st, k=keycode: self.keycode_changed.emit(k.code)) btn.keycode = keycode @@ -99,10 +101,3 @@ class TabbedKeycodes(QTabWidget): label = widget.keycode.label widget.setStyleSheet("QPushButton {}") widget.setText(label.replace("&", "&&")) - - -class KeycodeButton(QPushButton): - - def sizeHint(self): - size = KEYCODE_BTN_RATIO * self.fontMetrics().height() - return QSize(size, size) From 77473d9df9325040c7bc4d08fbc1aae78a0d4dba Mon Sep 17 00:00:00 2001 From: Jared Beller Date: Wed, 13 Jan 2021 00:20:47 -0500 Subject: [PATCH 4/5] force keyboard widget to recalulate layout each resize --- src/main/python/keyboard_widget.py | 112 ++++++++++++++++------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/src/main/python/keyboard_widget.py b/src/main/python/keyboard_widget.py index 9e77e88..6529449 100644 --- a/src/main/python/keyboard_widget.py +++ b/src/main/python/keyboard_widget.py @@ -18,28 +18,32 @@ class KeyWidget: self.tooltip = "" self.color = None - size = int(round(scale * (KEY_SIZE_RATIO + KEY_SPACING_RATIO))) - spacing = int(round(scale * KEY_SPACING_RATIO)) - - self.rotation_x = size * desc.rotation_x - self.rotation_y = size * desc.rotation_y self.rotation_angle = desc.rotation_angle - self.shift_x = shift_x - self.shift_y = shift_y - self.x = size * desc.x - self.y = size * desc.y - self.w = size * desc.width - spacing - self.h = size * desc.height - spacing - - self.rect = QRect(self.x, self.y, self.w, self.h) - self.has2 = desc.width2 != desc.width or desc.height2 != desc.height or desc.x2 != 0 or desc.y2 != 0 - self.x2 = self.x + size * desc.x2 - self.y2 = self.y + size * desc.y2 - self.w2 = size * desc.width2 - spacing - self.h2 = size * desc.height2 - spacing + self.update_position(scale, shift_x, shift_y) + + def update_position(self, scale, shift_x=0, shift_y=0): + size = scale * (KEY_SIZE_RATIO + KEY_SPACING_RATIO) + spacing = scale * KEY_SPACING_RATIO + + self.rotation_x = size * self.desc.rotation_x + self.rotation_y = size * self.desc.rotation_y + + self.shift_x = shift_x + self.shift_y = shift_y + self.x = size * self.desc.x + self.y = size * self.desc.y + self.w = size * self.desc.width - spacing + self.h = size * self.desc.height - spacing + + self.rect = QRect(self.x, self.y, self.w, self.h) + + self.x2 = self.x + size * self.desc.x2 + self.y2 = self.y + size * self.desc.y2 + self.w2 = size * self.desc.width2 - spacing + self.h2 = size * self.desc.height2 - spacing self.bbox = self.calculate_bbox(QRectF(self.x, self.y, self.w, self.h)) self.polygon = QPolygonF(self.bbox + [self.bbox[0]]) @@ -143,7 +147,7 @@ class KeyboardWidget(QWidget): self.common_widgets = [] # layout-specific widgets - self.widgets_for_layout = defaultdict(lambda: defaultdict(list)) + self.widgets_for_layout = [] # widgets in current layout self.widgets = [] @@ -154,60 +158,64 @@ class KeyboardWidget(QWidget): def set_keys(self, keys, encoders): self.common_widgets = [] - self.widgets_for_layout = defaultdict(lambda: defaultdict(list)) + self.widgets_for_layout = [] self.add_keys([(x, KeyWidget) for x in keys] + [(x, EncoderWidget) for x in encoders]) self.update_layout() def add_keys(self, keys): + scale_factor = self.fontMetrics().height() + + for key, cls in keys: + if key.layout_index == -1: + self.common_widgets.append(cls(key, scale_factor)) + else: + self.widgets_for_layout.append(cls(key, scale_factor)) + + def place_widgets(self): top_x = top_y = 1e6 scale_factor = self.fontMetrics().height() - # find the global top-left position, all the keys will be shifted to the left/up by that position - for key, cls in keys: - if key.layout_index == -1: - obj = cls(key, scale_factor) - p = obj.polygon.boundingRect().topLeft() - top_x = min(top_x, p.x()) - top_y = min(top_y, p.y()) + self.widgets = [] - # obtain common widgets, that is, ones which are always displayed and require no extra transforms - for key, cls in keys: - if key.layout_index == -1: - self.common_widgets.append(cls(key, scale_factor, -top_x + KEYBOARD_WIDGET_PADDING, -top_y + KEYBOARD_WIDGET_PADDING)) + # find the global top-left position, all the keys will be shifted to the left/up by that position + for widget in self.common_widgets: + widget.update_position(scale_factor) + p = widget.polygon.boundingRect().topLeft() + top_x = min(top_x, p.x()) + top_y = min(top_y, p.y()) + + # place common widgets, that is, ones which are always displayed and require no extra transforms + for widget in self.common_widgets: + widget.update_position(scale_factor, -top_x + KEYBOARD_WIDGET_PADDING, -top_y + KEYBOARD_WIDGET_PADDING) + self.widgets.append(widget) # top-left position for specific layout layout_x = defaultdict(lambda: defaultdict(lambda: 1e6)) layout_y = defaultdict(lambda: defaultdict(lambda: 1e6)) # determine top-left position for every layout option - for key, cls in keys: - if key.layout_index != -1: - obj = cls(key, scale_factor) - idx, opt = key.layout_index, key.layout_option - p = obj.polygon.boundingRect().topLeft() - layout_x[idx][opt] = min(layout_x[idx][opt], p.x()) - layout_y[idx][opt] = min(layout_y[idx][opt], p.y()) + for widget in self.widgets_for_layout: + widget.update_position(scale_factor) + idx, opt = widget.desc.layout_index, widget.desc.layout_option + p = widget.polygon.boundingRect().topLeft() + layout_x[idx][opt] = min(layout_x[idx][opt], p.x()) + layout_y[idx][opt] = min(layout_y[idx][opt], p.y()) # obtain widgets for all layout options now that we know how to shift them - for key, cls in keys: - if key.layout_index != -1: - idx, opt = key.layout_index, key.layout_option + for widget in self.widgets_for_layout: + idx, opt = widget.desc.layout_index, widget.desc.layout_option + if opt == self.layout_editor.get_choice(idx): shift_x = layout_x[idx][opt] - layout_x[idx][0] shift_y = layout_y[idx][opt] - layout_y[idx][0] - obj = cls(key, scale_factor, -shift_x - top_x + KEYBOARD_WIDGET_PADDING, -shift_y - top_y + KEYBOARD_WIDGET_PADDING) - self.widgets_for_layout[idx][opt].append(obj) + widget.update_position(scale_factor, -shift_x - top_x + KEYBOARD_WIDGET_PADDING, -shift_y - top_y + KEYBOARD_WIDGET_PADDING) + self.widgets.append(widget) def update_layout(self): """ Updates self.widgets for the currently active layout """ # determine widgets for current layout - self.widgets = [] - self.widgets += self.common_widgets - for idx in self.widgets_for_layout.keys(): - option = self.layout_editor.get_choice(idx) - self.widgets += self.widgets_for_layout[idx][option] - + self.place_widgets() self.widgets = list(filter(lambda w: not w.desc.decal, self.widgets)) self.widgets.sort(key=lambda w: (w.y, w.x)) @@ -223,6 +231,7 @@ class KeyboardWidget(QWidget): self.height = max_h + 2 * KEYBOARD_WIDGET_PADDING self.update() + self.updateGeometry() def paintEvent(self, event): qp = QPainter() @@ -318,6 +327,9 @@ class KeyboardWidget(QWidget): self.clicked.emit() self.update() + def resizeEvent(self, ev): + self.update_layout() + def select_next(self): """ Selects next key based on their order in the keymap """ @@ -344,6 +356,8 @@ class KeyboardWidget(QWidget): QToolTip.showText(ev.globalPos(), key.tooltip) else: QToolTip.hideText() + if ev.type() == QEvent.LayoutRequest: + self.update_layout() return super().event(ev) def set_enabled(self, val): From bb4159fe62230b9cbc32fd579be41d6bd6a0cb75 Mon Sep 17 00:00:00 2001 From: Jared Beller Date: Wed, 13 Jan 2021 00:30:55 -0500 Subject: [PATCH 5/5] optimization: don't recompute key widget if nothing changed --- src/main/python/keyboard_widget.py | 57 ++++++++++++++++-------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/main/python/keyboard_widget.py b/src/main/python/keyboard_widget.py index 6529449..6b8e5b2 100644 --- a/src/main/python/keyboard_widget.py +++ b/src/main/python/keyboard_widget.py @@ -17,6 +17,7 @@ class KeyWidget: self.mask_text = "" self.tooltip = "" self.color = None + self.scale = 0 self.rotation_angle = desc.rotation_angle @@ -25,39 +26,41 @@ class KeyWidget: self.update_position(scale, shift_x, shift_y) def update_position(self, scale, shift_x=0, shift_y=0): - size = scale * (KEY_SIZE_RATIO + KEY_SPACING_RATIO) - spacing = scale * KEY_SPACING_RATIO + if self.scale != scale or self.shift_x != shift_x or self.shift_y != shift_y: + self.scale = scale + size = self.scale * (KEY_SIZE_RATIO + KEY_SPACING_RATIO) + spacing = self.scale * KEY_SPACING_RATIO - self.rotation_x = size * self.desc.rotation_x - self.rotation_y = size * self.desc.rotation_y + self.rotation_x = size * self.desc.rotation_x + self.rotation_y = size * self.desc.rotation_y - self.shift_x = shift_x - self.shift_y = shift_y - self.x = size * self.desc.x - self.y = size * self.desc.y - self.w = size * self.desc.width - spacing - self.h = size * self.desc.height - spacing + self.shift_x = shift_x + self.shift_y = shift_y + self.x = size * self.desc.x + self.y = size * self.desc.y + self.w = size * self.desc.width - spacing + self.h = size * self.desc.height - spacing - self.rect = QRect(self.x, self.y, self.w, self.h) + self.rect = QRect(self.x, self.y, self.w, self.h) - self.x2 = self.x + size * self.desc.x2 - self.y2 = self.y + size * self.desc.y2 - self.w2 = size * self.desc.width2 - spacing - self.h2 = size * self.desc.height2 - spacing + self.x2 = self.x + size * self.desc.x2 + self.y2 = self.y + size * self.desc.y2 + self.w2 = size * self.desc.width2 - spacing + self.h2 = size * self.desc.height2 - spacing - self.bbox = self.calculate_bbox(QRectF(self.x, self.y, self.w, self.h)) - self.polygon = QPolygonF(self.bbox + [self.bbox[0]]) - self.draw_path = self.calculate_draw_path() - self.draw_path2 = self.calculate_draw_path2() + self.bbox = self.calculate_bbox(QRectF(self.x, self.y, self.w, self.h)) + self.polygon = QPolygonF(self.bbox + [self.bbox[0]]) + self.draw_path = self.calculate_draw_path() + self.draw_path2 = self.calculate_draw_path2() - # calculate areas where the inner keycode will be located - # nonmask = outer (e.g. Rsft_T) - # mask = inner (e.g. KC_A) - self.nonmask_rect = QRectF(self.x, self.y, self.w, self.h / 2) - self.mask_rect = QRectF(self.x + KEYBOARD_WIDGET_MASK_PADDING, self.y + self.h / 2, - self.w - 2 * KEYBOARD_WIDGET_MASK_PADDING, self.h / 2 - KEYBOARD_WIDGET_MASK_PADDING) - self.mask_bbox = self.calculate_bbox(self.mask_rect) - self.mask_polygon = QPolygonF(self.mask_bbox + [self.mask_bbox[0]]) + # calculate areas where the inner keycode will be located + # nonmask = outer (e.g. Rsft_T) + # mask = inner (e.g. KC_A) + self.nonmask_rect = QRectF(self.x, self.y, self.w, self.h / 2) + self.mask_rect = QRectF(self.x + KEYBOARD_WIDGET_MASK_PADDING, self.y + self.h / 2, + self.w - 2 * KEYBOARD_WIDGET_MASK_PADDING, self.h / 2 - KEYBOARD_WIDGET_MASK_PADDING) + self.mask_bbox = self.calculate_bbox(self.mask_rect) + self.mask_polygon = QPolygonF(self.mask_bbox + [self.mask_bbox[0]]) def calculate_bbox(self, rect): x1 = rect.topLeft().x()