简体   繁体   中英

Proper way to detect when Ctrl (or other modifier key) is pressed

I want to condition an action on whether or not a modifier key (Ctrl) is pressed. One workaround I have found is to install an event filter and use QApplication.queryKeyboardModifiers() to detect when Ctrl is pressed, and QApplication.keyboardModifiers() to detect when Ctrl is released:

from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import QApplication, QMainWindow

class MainWindow(QMainWindow):

    ctrl_signal = Signal(bool)

    def __init__(self):
        QMainWindow.__init__(self)
        self.installEventFilter(self)
        self.ctrl_signal.connect(self.ctrl_slot)

    def eventFilter(self, _object, e):
        if QApplication.queryKeyboardModifiers() == Qt.CTRL: # This runs twice, and only on key press (not release)
            print("Ctrl pressed")
            self.ctrl_signal.emit(True)
        elif QApplication.keyboardModifiers() == Qt.CTRL: # This runs once, but only on release
            print("Ctrl released")
            self.ctrl_signal.emit(False)
        return False

    def ctrl_slot(self, e):
        print("e: ", e)  # Do something

app = QApplication([])
window = MainWindow()
window.show()
app.exec_()

However, I am concerned that this is an unintended use of the .queryKeyboardModifiers() and .keyboardModifiers() functions, and therefore will likely lead to more trouble later on. Is there a proper way to detect when a modifier key is pressed/released in isolation (ie without any other keys being pressed)?

Though I am using PySide6, I'll accept answers in C++ or PyQt if they're helpful.

What you are currently doing is to check if the Ctrl key was pressed every time an event happens (for example click, move, resize, etc) and it seems that that is not your goal but only to detect when is the change so you must improve your filter for the Qt.KeyPress or Qt.KeyRelease event. On the other hand, your method will not work if you want to detect when another child widget consumes an event since it will not be propagated to the parent, instead it is better to apply the filter to the QWindow since the keyboard events arrive when it has the focus and does not depend on the logic of the children.

from PySide6.QtCore import Qt, Signal, QObject, QEvent
from PySide6.QtWidgets import QApplication, QMainWindow


class ControlHelper(QObject):
    ctrl_signal = Signal(bool)

    def __init__(self, window):
        super().__init__(window)
        self._window = window

        self.window.installEventFilter(self)

    @property
    def window(self):
        return self._window

    def eventFilter(self, obj, event):
        if obj is self.window:
            if event.type() == QEvent.KeyPress:
                if event.key() == Qt.Key_Control:
                    self.ctrl_signal.emit(True)
            if event.type() == QEvent.KeyRelease:
                if event.key() == Qt.Key_Control:
                    self.ctrl_signal.emit(False)
        return super().eventFilter(obj, event)


class MainWindow(QMainWindow):
    ctrl_signal = Signal(bool)

    def ctrl_slot(self, e):
        print("e: ", e)


app = QApplication([])
window = MainWindow()
window.show()

helper = ControlHelper(window.windowHandle())
helper.ctrl_signal.connect(window.ctrl_slot)

app.exec_()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM