如何使用事件过滤器处理QComboBox的滚轮事件?
Qt6中滚轮事件被控件窃取的解决方案问题
在Qt6环境下,QComboBox、QSpinBox等控件即便未获得焦点,甚至将focusPolicy设置为StrongFocus,仍会窃取本该由QScrollArea等父部件处理的鼠标滚轮事件。
子类化控件的解决方案(不够优雅)
可以通过子类化控件并重写wheelEvent事件处理器来忽略此类事件,但这种方式需要为每个存在问题的控件创建子类,扩展性差:
class MyComboBox(QtWidgets.QComboBox): def wheelEvent(self, event: QtGui.QWheelEvent) -> None: if not self.hasFocus(): event.ignore() else: super().wheelEvent(event)
事件过滤器的尝试(未达预期)
Qt提供了事件过滤器机制,只需创建一个过滤器即可应用于任意数量的不同控件,本应更具扩展性和优雅性,但当前实现并未按预期工作:
class WheelEventFilter(QtCore.QObject): """Ignores wheel events when a widget does not already have focus.""" def eventFilter(self, watched: QtCore.QObject, event: QtCore.QEvent) -> bool: if ( isinstance(watched, QtWidgets.QWidget) and not watched.hasFocus() and event.type() == QtCore.QEvent.Type.Wheel ): # 此方式会过滤事件,但同时阻止事件向上传递给父部件 return True # 此方式无法让被监视控件真正忽略事件 event.ignore() return False else: return super().eventFilter(watched, event)
当前的问题是:希望仅让被监视控件忽略滚轮事件,同时允许事件向上传递给父部件处理,但上述代码无法实现这一点。
核心疑问
能否使用事件过滤器实现与上述子类化wheelEvent处理器完全相同的效果?
可复现问题的完整示例
当鼠标悬停在任一组合框上滚动时,组合框会获取焦点并窃取滚轮事件:
import sys from PySide6 import QtWidgets, QtCore class MyWidget(QtWidgets.QWidget): def __init__(self) -> None: super().__init__() # 布局 self._layout = QtWidgets.QVBoxLayout() self.setLayout(self._layout) # 滚动区域内的主部件布局 self._mainwidget = QtWidgets.QWidget() self._mainlayout = QtWidgets.QVBoxLayout() self._mainwidget.setLayout(self._mainlayout) # 创建多个组合框 self._widgets = {} num_widgets = 20 for i in range(num_widgets): combo = QtWidgets.QComboBox() combo.addItems([str(x) for x in range(1, 11)]) combo.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) self._mainlayout.addWidget(combo) self._widgets[i] = combo # 滚动区域设置 self._scrollarea = QtWidgets.QScrollArea(self) self._scrollarea.setWidgetResizable(True) self._scrollarea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOn) self._scrollarea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOn) self._layout.addWidget(self._scrollarea) # 将主部件放入滚动区域 self._scrollarea.setWidget(self._mainwidget) def main() -> None: app = QtWidgets.QApplication(sys.argv) widget = MyWidget() widget.show() app.exec() if __name__ == "__main__": main()
内容的提问来源于stack exchange,提问作者Brendan Abel
相关产品推荐
相关产品推荐

