Search code examples
pythonpyqtpyqt4pyqt5

PyQt5: QPainter overlay QWidget


To this question I am referring to the answer from @Kuba Ober Draw Rectangular overlay on QWidget at click

My problem: I dont know how to "translate" the C++ (or C?) into Python. :-(

Thus I ask this "duplicated" question again, and wish anyone would help to rewrite the code to achieve the overlay effect in PyQt5.

As an example, I prepare here some code:

# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class MyApp(QWidget):
    def __init__(self):
        super(MyApp, self).__init__()
        self.initUI()

    def initUI(self):
        self.text = "hello world"
        self.setGeometry(100, 100, 400, 300)
        self.setWindowTitle('Draw Demo')

        self.btn = QPushButton("Butten should be overlayed", self)
        self.btn.setFixedSize(200, 200)
        self.btn.move(40, 40)

        self.show()

    def paintEvent(self, event):
        qp = QPainter()
        qp.begin(self)
        qp.setPen(QColor(Qt.red))
        qp.setFont(QFont('Arial', 20))

        qp.drawText(10, 50, "hello Python")
        qp.setPen(QColor(Qt.blue))
        qp.drawLine(10, 100, 100, 100)
        qp.drawRect(10, 150, 150, 100)

        qp.setPen(QColor(Qt.red))
        qp.drawEllipse(100, 50, 100, 50)
#        qp.drawPixmap(220, 10, QPixmap("python.jpg"))
        qp.fillRect(200, 175, 150, 100, QBrush(Qt.SolidPattern))
        qp.end()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

Target is: Let the Button shown in the background (overlayed by QPainter), but it is transparent to mouse event. Thus I could click the button, even though it is covered by QPainter.

Any help will be highly appreciated!


Solution

  • The translation from C ++ to Python is simple if we know the equivalences and differences between both languages as I show below:

    overLay.py

    import sys
    
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    
    
    class OverLay(QWidget):
        def __init__(self, *args, **kwargs):
            QWidget.__init__(self, *args, **kwargs)
            self.setAttribute(Qt.WA_NoSystemBackground)
            self.setAttribute(Qt.WA_TransparentForMouseEvents)
    
        def paintEvent(self, event):
            painter = QPainter(self)
            painter.fillRect(self.rect(), QColor(80, 80, 255, 128))
    
    class Filter(QObject):
        def __init__(self, *args, **kwargs):
            QObject.__init__(self, *args, **kwargs)
            self.m_overlay = None
            self.m_overlayOn = None
    
        def eventFilter(self, obj, event):
            if not obj.isWidgetType():
                return False
            if event.type() == QEvent.MouseButtonPress:
                if not self.m_overlay:
                    self.m_overlay = OverLay(obj.parentWidget())
                self.m_overlay.setGeometry(obj.geometry())
                self.m_overlayOn = obj
                self.m_overlay.show()
            elif event.type() == QEvent.Resize:
                if self.m_overlay and self.m_overlayOn == obj:
                    self.m_overlay.setGeometry(obj.geometry())
            return False
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        filt = Filter()
        window = QWidget()
        lay = QHBoxLayout(window)
        for text in ( "Foo", "Bar", "Baz "):
            label = QLabel(text)
            lay.addWidget(label)
            label.installEventFilter(filt)
        window.setMinimumSize(300, 250)
        window.show()
        sys.exit(app.exec_())
    

    Understanding what you want to do in your widget you must use the following code:

    main.py

    # -*- coding: utf-8 -*-
    
    import sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    
    from overlay import Filter
    
    
    class MyApp(QWidget):
        def __init__(self):
            super(MyApp, self).__init__()
            self.initUI()
    
        def initUI(self):
            self.text = "hello world"
            self.setGeometry(100, 100, 400, 300)
            self.setWindowTitle('Draw Demo')
    
            self.btn = QPushButton("Butten should be overlayed", self)
            self.btn.setFixedSize(200, 200)
            self.btn.move(40, 40)
            filt = Filter(self)
            self.btn.installEventFilter(filt)
    
            self.show()
    
        [...]
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        window = MyApp()
        window.show()
        sys.exit(app.exec_())