为何QUiLoader加载的QSlider无法重写wheelEvent?
为什么通过QUiLoader加载的QSlider无法用赋值wheelEvent的方式禁用滚轮?
我尝试阻止QSlider的滚轮事件,但发现两段代码表现不一致:直接通过代码创建的QSlider可以用wheelEvent=lambda e:None禁用滚轮,但通过QUiLoader加载UI文件得到的QSlider用这个方法完全无效,想知道原因。
无效代码(QSlider仍可通过滚轮移动)
from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QSlider from PySide2.QtUiTools import QUiLoader class widget(QWidget): def __init__(sf): super().__init__() loader = QUiLoader() form = loader.load('untitled.ui', sf) sf.ly_main = form.findChild(QVBoxLayout, 'verticalLayout') sf.sl_1 = form.findChild(QSlider, 'horizontalSlider') sf.setLayout(sf.ly_main) sf.sl_1.wheelEvent = lambda e : None if __name__ == '__main__': app = QApplication([]) w = widget() w.show() app.exec_()
有效代码(QSlider无法通过滚轮移动)
from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QSlider class widget(QWidget): def __init__(sf): super().__init__() sf.ly_main = QVBoxLayout() sf.sl_1 = QSlider() sf.ly_main.addWidget(sf.sl_1) sf.setLayout(sf.ly_main) sf.sl_1.wheelEvent = lambda e : None if __name__ == '__main__': app = QApplication([]) w = widget() w.show() app.exec_()
untitled.ui 内容
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Form</class> <widget class="QWidget" name="Form"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> <widget class="QWidget" name="verticalLayoutWidget"> <property name="geometry"> <rect> <x>10</x> <y>10</y> <width>371</width> <height>271</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QDoubleSpinBox" name="doubleSpinBox"/> </item> <item> <widget class="QSlider" name="horizontalSlider"> <property name="mouseTracking"> <bool>false</bool> </property> <property name="focusPolicy"> <enum>Qt::ClickFocus</enum> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> </layout> </widget> </widget> <resources/> <connections/> </ui>
原因解释
核心区别在于Qt事件派发机制对两种创建方式的处理逻辑不同:
- 直接Python创建QSlider:此时的滑块是PySide2提供的Python绑定类实例,Qt事件派发时会优先检查Python对象上是否有自定义的事件处理方法(比如你赋值的
wheelEvent),如果存在就执行Python层面的方法,跳过C++基类的实现,因此赋值lambda e:None能直接禁用滚轮。 - QUiLoader加载的QSlider:这类控件是Qt在C层面直接实例化的原生对象,PySide2仅为其提供一个Python包装层。Qt的事件会直接派发到C层面的虚函数,不会去检查Python包装对象上的属性赋值,所以你在Python层面给
wheelEvent赋值的方法根本不会被调用,滚轮事件依然由C++的默认逻辑处理,滑块自然会移动。
可行的解决方法
方法1:子类化QSlider并注册给QUiLoader
先自定义一个忽略滚轮事件的Slider类:
from PySide2.QtWidgets import QSlider class NoWheelSlider(QSlider): def wheelEvent(self, event): # 忽略滚轮事件,不传递给父类处理 event.ignore()
然后在加载UI时注册这个自定义类,并修改UI文件中的滑块类为NoWheelSlider:
from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout from PySide2.QtUiTools import QUiLoader class widget(QWidget): def __init__(sf): super().__init__() loader = QUiLoader() # 注册自定义滑块类 loader.registerCustomWidget(NoWheelSlider) form = loader.load('untitled.ui', sf) sf.ly_main = form.findChild(QVBoxLayout, 'verticalLayout') sf.sl_1 = form.findChild(NoWheelSlider, 'horizontalSlider') sf.setLayout(sf.ly_main) if __name__ == '__main__': app = QApplication([]) w = widget() w.show() app.exec_()
注意:需要修改UI文件中滑块的class属性,将
<widget class="QSlider" name="horizontalSlider">改为<widget class="NoWheelSlider" name="horizontalSlider">
方法2:给滑块安装事件过滤器
在父组件中通过事件过滤器拦截滑块的滚轮事件:
from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QSlider from PySide2.QtUiTools import QUiLoader from PySide2.QtCore import QEvent class widget(QWidget): def __init__(sf): super().__init__() loader = QUiLoader() form = loader.load('untitled.ui', sf) sf.ly_main = form.findChild(QVBoxLayout, 'verticalLayout') sf.sl_1 = form.findChild(QSlider, 'horizontalSlider') sf.setLayout(sf.ly_main) # 给滑块安装事件过滤器 sf.sl_1.installEventFilter(sf) def eventFilter(self, obj, event): # 拦截滑块的滚轮事件 if obj == self.sl_1 and event.type() == QEvent.Wheel: return True # 返回True表示事件已被处理,不再传递 return super().eventFilter(obj, event) if __name__ == '__main__': app = QApplication([]) w = widget() w.show() app.exec_()
内容的提问来源于stack exchange,提问作者DJ125000
相关产品推荐
相关产品推荐

