【问题标题】:Handle mouse events on both parent and child widgets (button don't consume event?)处理父小部件和子小部件上的鼠标事件(按钮不消耗事件?)
【发布时间】:2021-10-05 10:22:19
【问题描述】:

这是我的设置:

我的QTableWidget 上有一个eventFilter 来处理mousePressmouseRelease 事件。

我还有一个自定义类供 QPushButton 处理 mousePressmouseRelease 事件。

但是,当我通过单击并按住按钮触发 mousePress 事件时,QTableWidget 看不到该事件。

以防万一这是自定义 QPushButton 类和 QTableWidget eventFilter 的代码。

class Button(QPushButton):
    key = None

    def __init__(self, title, parent):
        super().__init__(title, parent)

    def mousePressEvent(self, e):

        super().mousePressEvent(e)

        if e.button() == Qt.LeftButton:
            print('press')


class TableWidget(QTableWidget):

    def __init__(self, rows, columns, parent=None):
        QTableWidget.__init__(self, rows, columns, parent)
        self._last_index = QPersistentModelIndex()
        self.viewport().installEventFilter(self)

    def eventFilter(self, source, event):
        if event.type() == QEvent.MouseButtonRelease:
            print("mouse release")
        return QTableWidget.eventFilter(self, source, event)

我希望看到的是,当我按下自定义按钮然后释放鼠标时,它会在控制台中打印“鼠标释放”。

这不会发生。

但是当我单击表格中除按钮之外的任何位置时,它确实会成功打印。

如果您希望我添加任何额外信息,请在 cmets 中告诉我。

【问题讨论】:

    标签: python pyqt pyqt5 pyqt4


    【解决方案1】:

    解释:

    正如我在this post: 中已经指出的那样小部件之间鼠标事件的处理是从子级到父级的,也就是说,如果子级不接受该事件(它不使用它),那么它将将事件传递给父级。。可以使用以下示例进行验证:

    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import (
        QApplication,
        QLabel,
        QPushButton,
        QTableWidget,
        QTableWidgetItem,
        QVBoxLayout,
        QWidget,
    )
    
    
    class TableWidget(QTableWidget):
        def mouseReleaseEvent(self, event):
            super().mouseReleaseEvent(event)
            print("released")
    
    
    class Widget(QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.tablewidget = TableWidget(4, 4)
    
            container = QWidget()
            lay = QVBoxLayout(container)
            lay.addWidget(QLabel("QLabel", alignment=Qt.AlignCenter))
            lay.addWidget(QPushButton("QPushButton"))
            container.setFixedSize(container.sizeHint())
    
            item = QTableWidgetItem()
            self.tablewidget.setItem(0, 0, item)
            item.setSizeHint(container.sizeHint())
            self.tablewidget.setCellWidget(0, 0, container)
            self.tablewidget.resizeRowsToContents()
            self.tablewidget.resizeColumnsToContents()
    
            lay = QVBoxLayout(self)
            lay.addWidget(self.tablewidget)
    
    
    def main():
        import sys
    
        app = QApplication(sys.argv)
    
        widget = Widget()
        widget.resize(640, 480)
        widget.show()
    
        sys.exit(app.exec_())
    
    
    if __name__ == "__main__":
        main()
    

    如果 QPushButton 被按下,则不会调用 QTableWidget 的 mouseReleaseEvent 方法,因为 QPushButton 会消耗它,这与按下 QLabel 时不同,因为它不消耗它而 QTableWidget 消耗它。

    解决方案:

    正如我在另一篇文章中指出的那样,一种可能的解决方案是对 QWindow 使用事件过滤器,并能够通过验证是否在单元格上单击来进行过滤。

    from PyQt5.QtCore import (
        pyqtSignal,
        QEvent,
        QObject,
        QPoint,
        Qt,
    )
    from PyQt5.QtWidgets import (
        QApplication,
        QLabel,
        QPushButton,
        QTableWidget,
        QTableWidgetItem,
        QVBoxLayout,
        QWidget,
    )
    
    
    class MouseObserver(QObject):
        pressed = pyqtSignal(QPoint, QPoint)
        released = pyqtSignal(QPoint, QPoint)
        moved = pyqtSignal(QPoint, QPoint)
    
        def __init__(self, window):
            super().__init__(window)
            self._window = window
    
            self.window.installEventFilter(self)
    
        @property
        def window(self):
            return self._window
    
        def eventFilter(self, obj, event):
            if self.window is obj:
                if event.type() == QEvent.MouseButtonPress:
                    self.pressed.emit(event.pos(), event.globalPos())
                elif event.type() == QEvent.MouseMove:
                    self.moved.emit(event.pos(), event.globalPos())
                elif event.type() == QEvent.MouseButtonRelease:
                    self.released.emit(event.pos(), event.globalPos())
            return super().eventFilter(obj, event)
    
    
    class Widget(QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.tablewidget = QTableWidget(4, 4)
    
            container = QWidget()
            lay = QVBoxLayout(container)
            lay.addWidget(QLabel("QLabel", alignment=Qt.AlignCenter))
            lay.addWidget(QPushButton("QPushButton"))
            container.setFixedSize(container.sizeHint())
    
            item = QTableWidgetItem()
            self.tablewidget.setItem(0, 0, item)
            item.setSizeHint(container.sizeHint())
            self.tablewidget.setCellWidget(0, 0, container)
            self.tablewidget.resizeRowsToContents()
            self.tablewidget.resizeColumnsToContents()
    
            lay = QVBoxLayout(self)
            lay.addWidget(self.tablewidget)
    
        def handle_window_released(self, window_pos, global_pos):
            lp = self.tablewidget.viewport().mapFromGlobal(global_pos)
            ix = self.tablewidget.indexAt(lp)
            if ix.isValid():
                print(ix.row(), ix.column())
    
    
    def main():
        import sys
    
        app = QApplication(sys.argv)
    
        widget = Widget()
        widget.resize(640, 480)
        widget.show()
    
        mouse_observer = MouseObserver(widget.windowHandle())
        mouse_observer.released.connect(widget.handle_window_released)
    
        sys.exit(app.exec_())
    
    
    if __name__ == "__main__":
        main()
    

    【讨论】:

    • 问题是在一个单元格中实际上有 2 个按钮,我需要能够分辨出这两个按钮中的哪一个被按下。是否有解决方法来检测单个单元格中的两个按钮被按下?并且能够记录新闻,并且不仅可以从按钮上释放鼠标(因为我打算最终使用自定义行为拖动按钮)也许有一种方法可以拒绝事件/不使用它?
    • @VadimTatarnikov 正如我在之前的评论中已经指出的那样:如果您需要帮助,请提供minimal reproducible example。看来你有特殊要求,所以你需要更详细地解释,所以我邀请你创建一个新的帖子。请阅读How to Ask
    • 是的,我正计划添加更多细节,它只是相当复杂,所以我需要找时间将其分解为更简单的形式。
    • @VadimTatarnikov 我建议您不要编辑原始帖子,而是创建一个新帖子,因为您向我指出的内容超出了您当前帖子的描述。请阅读链接
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 2012-03-25
    • 1970-01-01
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    相关资源
    最近更新 更多