PyQt5:如何在QGraphicsView的代理QFrame获焦点时调用方法
在PyQt5中监听QGraphicsView内代理QFrame的焦点事件
问题描述
在PyQt5中,若通过QGraphicsScene.addWidget()将QFrame作为代理部件添加到QGraphicsView的场景中,如何在该QFrame获得焦点(比如鼠标点击)时调用指定方法?
文件:run_me.py
from focus import Ui_Dialog from PyQt5 import QtWidgets, QtCore, QtGui from win32api import GetSystemMetrics import sys import time class Run_me: def __init__(self): self.app = QtWidgets.QApplication(sys.argv) self.Dialog = QtWidgets.QDialog() self.ui = Ui_Dialog() self.ui.setupUi(self.Dialog) self.Dialog.showMaximized() self.max_width = int(0.9*GetSystemMetrics(0)) self.max_height = int(0.85*GetSystemMetrics(1)) self.Dialog.setFixedWidth(self.max_width) self.Dialog.setFixedHeight(self.max_height) self.center() self.frame = QtWidgets.QFrame() self.verticalLayout = QtWidgets.QVBoxLayout(self.frame) self.lineedit = QtWidgets.QLineEdit(self.frame) self.verticalLayout.addWidget(self.lineedit) self.scene = QtWidgets.QGraphicsScene() self.ui.graphicsView.setScene(self.scene) self.ui.graphicsView.setFixedHeight(self.ui.graphicsView.height()) self.ui.graphicsView.setFixedWidth(self.ui.graphicsView.width()) #self.ui.graphicsView.setSceneRect(0, 0, self.scene_width, self.total_height) self.proxy = self.scene.addWidget(self.frame) self.proxy.setPos(100,100) self._filter = Filter(self) self.frame.installEventFilter(self._filter) sys.exit(self.app.exec()) def center(self): screen_width = GetSystemMetrics(0) screen_height = GetSystemMetrics(1) dw = self.app.desktop() taskbar_height = dw.screenGeometry().height() - dw.availableGeometry().height() taskbar_width = dw.screenGeometry().width() - dw.availableGeometry().width() if taskbar_height<100: available_screen_height = screen_height - taskbar_height available_screen_width = screen_width else: available_screen_height = screen_height available_screen_width = screen_width - taskbar_width qdialog_width = self.Dialog.frameSize().width() qdialog_height = self.Dialog.frameSize().height() x = (available_screen_width - qdialog_width) / 2 y = (available_screen_height - qdialog_height) / 2 width = self.Dialog.size().width() height = self.Dialog.size().height() self.Dialog.move(int(x),int(y)) self.Dialog.setFixedWidth(width) self.Dialog.setFixedHeight(height) def frame_focused(self): print("Frame focused") class Filter(QtCore.QObject): def __init__(self,main_self): super().__init__() self.main_self = main_self def eventFilter(self, widget, event): # FocusIn event if event.type() == QtCore.QEvent.FocusIn: self.main_self.frame_focused() return False else: # we don't care about other events return False if __name__ == "__main__": program = Run_me()
文件:focus.py
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'telephone_calls.ui' # # Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(599, 351) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) Dialog.setSizePolicy(sizePolicy) self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) self.verticalLayout.setObjectName("verticalLayout") self.graphicsView = QtWidgets.QGraphicsView(Dialog) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth()) self.graphicsView.setSizePolicy(sizePolicy) self.graphicsView.setStyleSheet("QGraphicsView#graphicsView{\n" " border: 1px solid #ABABAB;\n" " background:white;\n" "}") self.graphicsView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored) self.graphicsView.setObjectName("graphicsView") self.verticalLayout.addWidget(self.graphicsView) self.retranslateUi(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "Στοιχεία ηχητικών κλήσεων")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) Dialog = QtWidgets.QDialog() ui = Ui_Dialog() ui.setupUi(Dialog) Dialog.show() sys.exit(app.exec_())
解决方案
你使用事件过滤器的思路是正确的,但需要调整几个细节确保焦点事件能被正常捕获:
- 设置QFrame的焦点策略
默认QFrame的focusPolicy为NoFocus,无法主动获取焦点,需要手动设置为StrongFocus或ClickFocus,让它能接收焦点事件:
self.frame = QtWidgets.QFrame() self.frame.setFocusPolicy(QtCore.Qt.StrongFocus) # 添加此行
事件过滤器的返回值处理
对于不需要拦截的事件,返回False即可(你的代码此处逻辑正确),这样不会阻止事件传递给目标部件,仅完成监听触发。可选:监听鼠标点击事件
如果需要点击QFrame时直接触发方法,不管焦点状态,可在事件过滤器中同时捕获MouseButtonPress事件:
def eventFilter(self, widget, event): if event.type() == QtCore.QEvent.FocusIn or event.type() == QtCore.QEvent.MouseButtonPress: self.main_self.frame_focused() return False return super().eventFilter(widget, event)
调整后,点击QFrame区域(包括空白处)时,frame_focused()方法会被触发,控制台输出"Frame focused"。
内容的提问来源于stack exchange,提问作者Chris P
相关产品推荐
相关产品推荐

