创建事件过滤器

11 投票
2 回答
12938 浏览
提问于 2025-04-16 01:41

我正在尝试在我的树形视图中启用删除键。到目前为止,我的代码是这样的:

class delkeyFilter(QObject):
    delkeyPressed = pyqtSignal()

    def eventFilter(self,  obj,  event):
        if event.type() == QEvent.KeyPress:
            if event.key() == Qt.Key_Delete:
                self.delkeyPressed.emit()
                print 'delkey pressed'
                return True
        return False

我这样连接了 eventfilter

    filter = delkeyFilter(self.dataTreeView)
    self.dataTreeView.installEventFilter(filter)

我为什么在创建过滤器时需要传递 self.dataTreeview 呢?如果不传它就不行。

2 个回答

4

键盘事件的处理已经在 QAbstractItemView 中实现了。你只需要创建一个树形视图的子类,然后实现 keyPressEvent 方法就可以了。

class MyTreeView(QTreeView):

    delkeyPressed = pyqtSignal()

    def __init__(self):
        QTreeView.__init__(self)

    def keyPressEvent(self, event): #QKeyEvent
        if event.key() == Qt.Key_Delete:
            self.delkeyPressed.emit()
            print 'del key pressed'

        # pass the event up the chain or we will eat the event
        QTreeView.keyPressEvent(self, event)

`

13

@balpha说得对。简单来说,如果你没有传入一个父对象,或者没有确保filter实例有一个有效的引用,它就会被垃圾回收。

PyQt使用SIP来连接Qt的C++实现。根据SIP文档

当一个C++实例被包装时,会创建一个对应的Python对象。这个Python对象在垃圾回收方面的表现是你所期待的——当它的引用计数降到零时,它会被垃圾回收。那么,那个对应的C++实例会发生什么呢?显而易见的答案是,实例的析构函数会被调用。然而,库的API可能会说,当实例被传递给某个特定的函数时,库会接管这个实例的所有权,也就是说,调用实例析构函数的责任从SIP生成的模块转移到了库。

一个实例的所有权也可能与另一个实例相关联。这意味着,如果拥有的实例被销毁,拥有者实例也会自动被销毁。SIP会跟踪这些关系,以确保Python的循环垃圾回收器能够检测并打破拥有和被拥有实例之间的任何引用循环。这个关联是通过拥有实例对被拥有实例的引用来实现的。

以上内容意味着,如果你把一个Python对象传给一个接管所有权的Qt对象,即使你没有确保对特定对象的引用被保持,一切也会正常工作。

所以,重申一下@balpha在评论中说的内容,这里有一个解决方法,适用于你不想把对象传给构造函数的情况:

self.filter = delkeyFilter()
self.dataTreeView.installEventFilter(self.filter)

撰写回答