如何在 QPaintEvent 覆盖下获取小部件以注册鼠标事件

How to get widgets under a QPaintEvent overlay to register mouse events

我正在使用带有 QPaintEvent 的叠加层来绘制其他小部件。我仍然需要下面的小部件来注册鼠标事件。当我使用 WA_TransparentForMouseEvents 标志调用 raise() 时,我再次获得了对小部件的控制权,但是当然,失去了 paintevent,因为它不再注册任何鼠标事件。我在这里有什么选择?

class Overlay(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Overlay, self).__init__(parent)

        self.hotBox = parent
        self.resize(self.hotBox.width, self.hotBox.height)


    def paintEvent(self, event):
        #args: [QEvent]
        if any ([self.hotBox.name=="main", self.hotBox.name=="viewport"]):
            self.raise_()
            self.setWindowFlags(QtCore.Qt.WA_TransparentForMouseEvents)

            #Initialize painter
            painter = QtGui.QPainter(self)
            pen = QtGui.QPen(QtGui.QColor(115, 115, 115), 3, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
            painter.setPen(pen)
            painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
            painter.setBrush(QtGui.QColor(115, 115, 115))
            painter.drawEllipse(self.hotBox.point, 5, 5)

            #perform paint
            if self.hotBox.mousePosition:
                mouseX = self.hotBox.mousePosition.x()
                mouseY = self.hotBox.mousePosition.y()
                line = QtCore.QLine(mouseX, mouseY, self.hotBox.point.x(), self.hotBox.point.y())
                painter.drawLine(line)
                painter.drawEllipse(mouseX-5, mouseY-5, 10, 10)

如果您想使用事件 mouseXXXEvent 创建叠加层,您必须使用 eventFilter,对于此解决方案,我基于 this answer of @KubaOber 添加了更多功能:

import sys
from PySide2 import QtCore, QtGui, QtWidgets


class Overlay(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Overlay, self).__init__(parent)
        self.setAttribute(QtCore.Qt.WA_NoSystemBackground)
        self.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)

        self.start_line, self.end_line = QtCore.QPoint(), QtCore.QPoint()

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.fillRect(self.rect(), QtGui.QColor(80, 80, 255, 128))
        if not self.start_line.isNull() and not self.end_line.isNull():
            painter.drawLine(self.start_line, self.end_line)

    def mousePressEvent(self, event):
        self.start_line = event.pos()
        self.end_line = event.pos()
        self.update()

    def mouseMoveEvent(self, event):
        self.end_line = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        self.start_line = QtCore.QPoint()
        self.end_line = QtCore.QPoint()

class OverlayFactoryFilter(QtCore.QObject):
    def __init__(self, parent=None):
        super(OverlayFactoryFilter, self).__init__(parent)
        self.m_overlay = None

    def setWidget(self, w):
        w.installEventFilter(self)
        if self.m_overlay is None:
            self.m_overlay = Overlay()
        self.m_overlay.setParent(w)

    def eventFilter(self, obj, event):
        if not obj.isWidgetType():
            return False

        if event.type() == QtCore.QEvent.MouseButtonPress:
            self.m_overlay.mousePressEvent(event)
        elif event.type() == QtCore.QEvent.MouseButtonRelease:
            self.m_overlay.mouseReleaseEvent(event)
        elif event.type() == QtCore.QEvent.MouseMove:
            self.m_overlay.mouseMoveEvent(event)
        elif event.type() == QtCore.QEvent.MouseButtonDblClick:
            self.m_overlay.mouseDoubleClickEvent(event)

        elif event.type() == QtCore.QEvent.Resize:
            if self.m_overlay and self.m_overlay.parentWidget() == obj:
                self.m_overlay.resize(obj.size())
        elif event.type() == QtCore.QEvent.Show:
            self.m_overlay.raise_()
        return super(OverlayFactoryFilter, self).eventFilter(obj, event)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    factory = OverlayFactoryFilter()
    w = QtWidgets.QWidget()
    factory.setWidget(w)
    button = QtWidgets.QPushButton("Press me", w)
    w.show()
    sys.exit(app.exec_())