Explanation:
As I already pointed out in this post: The handling of mouse events between the widgets goes from children to parents, that is, if the child does not accept the event (it does not use it) then it will pass the event to the parent.. That can be verified using the following example:
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()
If the QPushButton is pressed then the mouseReleaseEvent method of the QTableWidget will not be invoked because the QPushButton consumes it, unlike when the QLabel is pressed since it does not consume it and the QTableWidget does.
Solution:
As I pointed out in the other post, a possible solution is to use an eventfilter to the QWindow and to be able to filter by verifying that the click is given on a cell.
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()