diff --git a/cellacdc/apps.py b/cellacdc/apps.py index 0a21cd8b..7396c788 100755 --- a/cellacdc/apps.py +++ b/cellacdc/apps.py @@ -1,6 +1,7 @@ import os import sys import re +from typing import Literal import datetime import pathlib from collections import defaultdict @@ -12200,7 +12201,12 @@ def showEvent(self, event) -> None: self.resize(int(self.width()*2), self.height()) class ShortcutEditorDialog(QBaseDialog): - def __init__(self, widgetsWithShortcut: dict, parent=None): + def __init__( + self, widgetsWithShortcut: dict, + delObjectKey='', + delObjectButton: Literal['Middle click', 'Left click']='Middle click', + parent=None + ): self.cancel = True super().__init__(parent) @@ -12212,9 +12218,24 @@ def __init__(self, widgetsWithShortcut: dict, parent=None): self.shortcutLineEdits = {} scrollArea = QScrollArea(self) + scrollArea.setWidgetResizable(True) scrollAreaWidget = QWidget() entriesLayout = QGridLayout() - for row, (name, widget) in enumerate(widgetsWithShortcut.items()): + + self.delObjShortcutLineEdit = widgets.ShortcutLineEdit( + allowModifiers=True + ) + self.delObjShortcutLineEdit.setText(delObjectKey) + self.delObjButtonCombobox = QComboBox() + self.delObjButtonCombobox.addItems(['Middle click', 'Left click']) + self.delObjButtonCombobox.setCurrentText(delObjectButton) + entriesLayout.addWidget(QLabel('Delete object:'), 0, 0) + entriesLayout.addWidget(self.delObjShortcutLineEdit, 0, 1) + entriesLayout.addWidget( + self.delObjButtonCombobox, 0, 2, alignment=Qt.AlignLeft + ) + + for row, (name, widget) in enumerate(widgetsWithShortcut.items(), start=1): label = QLabel(f'{name}:') shortcutLineEdit = widgets.ShortcutLineEdit() if hasattr(widget, 'keyPressShortcut'): @@ -12231,6 +12252,10 @@ def __init__(self, widgetsWithShortcut: dict, parent=None): entriesLayout.addWidget(shortcutLineEdit, row, 1) self.shortcutLineEdits[name] = shortcutLineEdit + entriesLayout.setColumnStretch(0, 0) + entriesLayout.setColumnStretch(1, 1) + entriesLayout.setColumnStretch(2, 0) + scrollAreaWidget.setLayout(entriesLayout) scrollArea.setWidget(scrollAreaWidget) buttonsLayout = widgets.CancelOkButtonsLayout() @@ -12263,6 +12288,14 @@ def ok_cb(self): self.customShortcuts[name] = ( text, shortcutLineEdit.keySequence ) + delObjButtonText = self.delObjButtonCombobox.currentText() + delObjQtButton = ( + Qt.MouseButton.LeftButton if delObjButtonText == 'Left click' + else Qt.MouseButton.MiddleButton + ) + self.delObjAction = ( + self.delObjShortcutLineEdit.keySequence, delObjQtButton + ) self.close() def showEvent(self, event) -> None: diff --git a/cellacdc/gui.py b/cellacdc/gui.py index d33ca707..35a129bb 100755 --- a/cellacdc/gui.py +++ b/cellacdc/gui.py @@ -1411,7 +1411,7 @@ def isPanImageClick(self, mouseEvent, modifiers): left_click = mouseEvent.button() == Qt.MouseButton.LeftButton return modifiers == Qt.AltModifier and left_click - def isMiddleClick(self, mouseEvent, modifiers): + def isDefaultMiddleClick(self, mouseEvent, modifiers): if sys.platform == 'darwin': middle_click = ( mouseEvent.button() == Qt.MouseButton.LeftButton @@ -1421,6 +1421,35 @@ def isMiddleClick(self, mouseEvent, modifiers): else: middle_click = mouseEvent.button() == Qt.MouseButton.MiddleButton return middle_click + + def isMiddleClick(self, mouseEvent, modifiers): + if self.delObjAction is None: + return self.isDefaultMiddleClick(mouseEvent, modifiers) + + delObjKeySequence, delObjQtButton = self.delObjAction + + isMatchKey = self.delObjToolAction.isChecked() + + if not isMatchKey: + isAltKeySequence = delObjKeySequence == QKeySequence(Qt.Key_Alt) + isAltModifier = modifiers == Qt.AltModifier + isMatchKey = isAltKeySequence and isAltModifier + + if not isMatchKey: + isCtrlKeySequence = delObjKeySequence == QKeySequence(Qt.Key_Control) + isCtrlModifier = modifiers == Qt.ControlModifier + isMatchKey = isCtrlKeySequence and isCtrlModifier + + if not isMatchKey: + isShiftKeySequence = delObjKeySequence == QKeySequence(Qt.Key_Shift) + isShiftModifier = modifiers == Qt.ShiftModifier + isMatchKey = isShiftKeySequence and isShiftModifier + + middle_click = ( + mouseEvent.button() == delObjQtButton and isMatchKey + ) + + return middle_click def gui_createCursors(self): pixmap = QPixmap(":wand_cursor.svg") @@ -2683,7 +2712,16 @@ def gui_createControlsToolbar(self): # other toolbars --> placeholder placeHolderToolbar = widgets.ToolBar("Place holder", self) self.addToolBar(Qt.TopToolBarArea, placeHolderToolbar) - placeHolderToolbar.addWidget(QToolButton(self)) + self.delObjToolAction = QAction(self) + self.delObjToolAction.setCheckable(True) + self.delObjToolAction.setToolTip( + 'Customisable delete object action\n\n' + 'Go to the `Settings --> Customise keyboard shortcuts...` menu ' + 'on the top menubar\n' + 'to customise the action required to delete ' + 'an object with a click.' + ) + placeHolderToolbar.addAction(self.delObjToolAction) placeHolderToolbar.setMovable(False) self.placeHolderToolbar = placeHolderToolbar self.placeHolderToolbar.setVisible(False) @@ -21882,6 +21920,22 @@ def initShortcuts(self): if 'keyboard.shortcuts' not in cp: cp['keyboard.shortcuts'] = {} + if 'delete_object.action' not in cp: + self.delObjAction = None + else: + delObjKeySequenceText = cp['delete_object.action']['Key sequence'] + delObjButtonText = cp['delete_object.action']['Mouse button'] + delObjQtButton = ( + Qt.MouseButton.LeftButton if delObjButtonText == 'Left click' + else Qt.MouseButton.MiddleButton + ) + delObjKeySequence = QKeySequence(delObjKeySequenceText) + self.delObjAction = ( + QKeySequence(delObjKeySequenceText), delObjQtButton + ) + if delObjKeySequenceText: + self.delObjToolAction.setShortcut(delObjKeySequence) + shortcuts = {} for name, widget in self.widgetsWithShortcut.items(): if name not in cp.options('keyboard.shortcuts'): @@ -21928,6 +21982,23 @@ def setShortcuts(self, shortcuts: dict, save=True): for name, (text, shortcut) in shortcuts.items(): cp['keyboard.shortcuts'][name] = text + if self.delObjAction is not None: + delObjKeySequence, delObjQtButton = self.delObjAction + delObjKeySequenceText = delObjKeySequence.toString() + delObjKeySequenceText = ( + delObjKeySequenceText.encode('ascii', 'ignore').decode('utf-8') + ) + delObjButtonText = ( + 'Left click' if delObjQtButton == Qt.MouseButton.LeftButton + else 'Middle click' + ) + cp['delete_object.action'] = { + 'Key sequence': delObjKeySequenceText, + 'Mouse button': delObjButtonText + } + if delObjKeySequenceText: + self.delObjToolAction.setShortcut(delObjKeySequence) + with open(shortcut_filepath, 'w') as ini: cp.write(ini) @@ -21936,6 +22007,8 @@ def editShortcuts_cb(self): win.exec_() if win.cancel: return + + self.delObjAction = win.delObjAction self.setShortcuts(win.customShortcuts) def toggleOverlayColorButton(self, checked=True): diff --git a/cellacdc/widgets.py b/cellacdc/widgets.py index 1a946979..31bc3375 100755 --- a/cellacdc/widgets.py +++ b/cellacdc/widgets.py @@ -3331,9 +3331,10 @@ def paintEvent(self, e): p.end() class ShortcutLineEdit(QLineEdit): - def __init__(self, parent=None): + def __init__(self, parent=None, allowModifiers=False): self.keySequence = None super().__init__(parent) + self._allowModifiers = allowModifiers self.setAlignment(Qt.AlignCenter) def setText(self, text): @@ -3350,19 +3351,29 @@ def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: self.setText('') return - - modifers_value = event.modifiers().value if PYQT6 else event.modifiers() - keySequence = QKeySequence(modifers_value | event.key()).toString() - modifers_value = event.modifiers().value if PYQT6 else event.modifiers() - keySequence = QKeySequence(modifers_value | event.key()).toString() + isAltKey = event.key()==Qt.Key_Alt + isCtrlKey = event.key()==Qt.Key_Control + isShiftKey = event.key()==Qt.Key_Shift + isModifierKey = isAltKey or isCtrlKey or isShiftKey + + modifiers = event.modifiers() + modifers_value = modifiers.value if PYQT6 else modifiers + if isModifierKey: + keySequence = QKeySequence(modifers_value).toString() + else: + keySequence = QKeySequence(modifers_value | event.key()).toString() + keySequence = keySequence.encode('ascii', 'ignore').decode('utf-8') self.setText(keySequence) self.key = event.key() def keyReleaseEvent(self, event: QKeyEvent) -> None: if self.text().endswith('+'): - self.setText('') + if not self._allowModifiers: + self.setText('') + else: + self.setText(self.text().rstrip('+').strip()) class selectStartStopFrames(QGroupBox):