处理父小部件和子小部件上的鼠标事件(按钮不消耗事件?)
Posted
技术标签:
【中文标题】处理父小部件和子小部件上的鼠标事件(按钮不消耗事件?)【英文标题】:Handle mouse events on both parent and child widgets (button don't consume event?) 【发布时间】:2021-10-05 10:22:19 【问题描述】:这是我的设置:
我的QTableWidget
上有一个eventFilter
来处理mousePress
和mouseRelease
事件。
我还有一个自定义类供 QPushButton
处理 mousePress
和 mouseRelease
事件。
但是,当我通过单击并按住按钮触发 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 中告诉我。
【问题讨论】:
【参考方案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 我建议您不要编辑原始帖子,而是创建一个新帖子,因为您向我指出的内容超出了您当前帖子的描述。请阅读链接以上是关于处理父小部件和子小部件上的鼠标事件(按钮不消耗事件?)的主要内容,如果未能解决你的问题,请参考以下文章
Flutter:在 Navigation.pop 上刷新父小部件