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 (i.e. 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_()