解释:
正如我在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()