模态QDialog中无法触发QMenu快捷键的问题求解
问题描述
我有两个窗口:一个包含QMenu和QPushButton的QMainWindow,以及一个仅含QLabel的QDialog。触发QMenu动作时控制台会输出消息,该动作绑定了F10快捷键。点击按钮后通过show()方法打开模态QDialog(未使用exec()/exec_()以避免阻塞主窗口功能),但在QDialog中无法触发主窗口QMenu的动作。若重新定义触发器可使用快捷键,但如何在尽量少修改代码的前提下解决该问题?推测可能因QMainWindow未获得焦点,但由于CustomQDialog设置了self.setModal(True),主窗口永远无法获得焦点。
相关代码文件
File: untitled.py
from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.resize(363, 62) self.centralwidget = QtWidgets.QWidget(MainWindow) self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.pushButton = QtWidgets.QPushButton(self.centralwidget) self.gridLayout.addWidget(self.pushButton, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 363, 21)) self.menuMenu = QtWidgets.QMenu(self.menubar) MainWindow.setMenuBar(self.menubar) self.actionMenu_action_1 = QtWidgets.QAction(MainWindow) self.menuMenu.addAction(self.actionMenu_action_1) self.menubar.addAction(self.menuMenu.menuAction()) self.pushButton.setText("Click to open modal Dialog") self.menuMenu.setTitle("Menu") self.actionMenu_action_1.setText("Menu action 1 (print in console)") self.actionMenu_action_1.setShortcut("F10") QtCore.QMetaObject.connectSlotsByName(MainWindow)
File: untitled_2.py
from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.resize(228, 80) self.gridLayout = QtWidgets.QGridLayout(Dialog) self.label = QtWidgets.QLabel(Dialog) self.label.setAlignment(QtCore.Qt.AlignCenter) self.gridLayout.addWidget(self.label, 0, 0, 1, 1) self.label.setText("Press F10 to test shortcut") QtCore.QMetaObject.connectSlotsByName(Dialog)
File: run_app.py
from PyQt5 import QtCore, QtGui, QtWidgets from untitled import Ui_MainWindow from untitled_2 import Ui_Dialog import sys import os class Run_me: def __init__(self): self.app = QtWidgets.QApplication(sys.argv) self.MainWindow = QtWidgets.QMainWindow() self.ui = Ui_MainWindow() self.ui.setupUi(self.MainWindow) self.MainWindow.show() self.ui.pushButton.clicked.connect(lambda state:self.open_q_dialog(state)) self.ui.actionMenu_action_1.triggered.connect(lambda:print("Action triggered")) sys.exit(self.app.exec_()) def open_q_dialog(self,state): self.Dialog = CustomQDialog() self.dialog_ui = Ui_Dialog() self.dialog_ui.setupUi(self.Dialog) self.Dialog.show() class CustomQDialog(QtWidgets.QDialog): def __init__(self,*args,**kwards): super().__init__(*args,**kwards) self.setModal(True) if __name__ == "__main__": program = Run_me()
解决方案
最简便的修改方式是将QAction的快捷键上下文设置为应用全局范围,这样无论哪个窗口处于激活状态,快捷键都会触发对应的动作。只需在run_app.py中添加一行代码:
self.ui.actionMenu_action_1.setShortcutContext(QtCore.Qt.ApplicationShortcut)
修改后的run_app.py关键代码片段如下:
# ... 其他代码 self.ui.actionMenu_action_1.triggered.connect(lambda:print("Action triggered")) # 添加这一行,设置全局快捷键上下文 self.ui.actionMenu_action_1.setShortcutContext(QtCore.Qt.ApplicationShortcut) sys.exit(self.app.exec_()) # ...
原理说明
- 默认情况下,QAction的快捷键上下文是
Qt.WindowShortcut,只有当动作所属的窗口(此处为MainWindow)获得焦点时,快捷键才会生效。 - 将上下文改为
Qt.ApplicationShortcut后,快捷键会在整个应用程序范围内生效,即使模态对话框处于激活状态,也能触发主窗口的对应动作。
如果不想修改动作的全局上下文,也可以通过在CustomQDialog中重写keyPressEvent,将F10事件转发给主窗口:
class CustomQDialog(QtWidgets.QDialog): def __init__(self,*args,**kwards): super().__init__(*args,**kwards) self.setModal(True) def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_F10: # 找到主窗口并触发动作 main_window = QtWidgets.QApplication.activeWindow().parent() if hasattr(main_window, 'ui') and hasattr(main_window.ui, 'actionMenu_action_1'): main_window.ui.actionMenu_action_1.trigger() super().keyPressEvent(event)
但这种方式需要修改对话框代码,不如设置全局快捷键上下文简洁。
内容的提问来源于stack exchange,提问作者Chris P
相关产品推荐
相关产品推荐

