Painting in a QLabel with paintEvent

2019-07-27 08:43发布

问题:

I want to paint a rectangle in a certain label, I made a GUI with QtDesigner that generates the whole GUI code in one class called "class Ui_MainWindow(QMainWindow):" and I am using three tabs in my window.

I had a problem using the QMouseEvent on my labels, I found a solution using this code

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
import sys
import cv2
from PyQt5.QtWidgets import QMainWindow, QWidget, QLabel
from PyQt5.QtGui import QPixmap, QImage

class Ui_MainWindow(QMainWindow):

    def __init__(self):
        # super(Ui_MainWindow, self).__init__ ()
        super().__init__()
        self.count = 0
        self.frame = 0
        self.fileName="0"
        self.imagesTab2 = []
        self.k = 0
        self.i = 1
        self.w = 0
        self.h = 0
        self.coordPt = []
        self.dictListClasses = {
            "face" :[],
            "car" : [] }
        self.dictColorClasses = {
            "face" :(0, 100, 255),
            "car" : (0,255, 0) }


    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(655, 364)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab)
        self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.pushButton = QtWidgets.QPushButton(self.tab)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        self.verticalLayout_2.addLayout(self.horizontalLayout)
        self.label = QtWidgets.QLabel(self.tab)
        self.label.setText("")
        self.label.setObjectName("label")
        self.label.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.label.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.setMouseEventDelegate(self.label)
        self.verticalLayout_2.addWidget(self.label)
        self.verticalLayout_2.setStretch(1, 1)
        self.tabWidget.addTab(self.tab, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")
        self.tabWidget.addTab(self.tab_2, "")
        self.verticalLayout.addWidget(self.tabWidget)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 655, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "Open"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab 1"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Tab 2"))

        self.pushButton.clicked.connect(self.openFileTab2)

    def setMouseEventDelegate (self, setQWidget):
        def subWidgetMousePressEvent(e: QtGui.QMouseEvent):
            if self.imagesTab2:
                if e.button() == Qt.LeftButton:
                    po = int(e.x()*self.w/self.label.width())
                    pa = int(e.y()*self.h/self.label.height())
                    self.coordPt = [(po, pa)]
                    print("coords click = ", self.coordPt)
                    self.begin = e.pos()
                    self.end = e.pos()
                    self.label.update()
                    print("begin 1= ",self.begin, self.end)

        def subWidgetMouseMoveEvent(e: QtGui.QMouseEvent):
            if self.imagesTab2:
                self.end = e.pos()
                self.label.update()

        def subWidgetMouseReleaseEvent(e: QtGui.QMouseEvent):
            if e.button() == Qt.LeftButton:
                if self.imagesTab2:
                    if e.x() >= 0 and e.y() >= 0 and e.x()<self.label.width() and e.y()<self.label.height():
                        po = int(e.x()*self.w/self.label.width())
                        pa = int(e.y()*self.h/self.label.height())
                        self.coordPt.append((po, pa))
                        print("coords release = ", self.coordPt)
                        self.begin = e.pos()
                        self.end = e.pos()
                        self.label.update()
                        print("begin 2= ",self.begin, self.end)
                        self.dictListClasses["car"].append(self.coordPt)
                        print("LL + ", self.dictListClasses)
        setQWidget.mousePressEvent = subWidgetMousePressEvent
        setQWidget.mouseReleaseEvent = subWidgetMouseReleaseEvent
        setQWidget.mouseMoveEvent = subWidgetMouseMoveEvent

    def openFileTab2(self):
        self.imagesTab2, _ = QtWidgets.QFileDialog.getOpenFileNames(None,"Select one or more images to open", "/Images","Images (*.jpg *.jpeg .*bmp .*png);;All Files (*)")
        if self.imagesTab2:
            self.showImageInLabel(self.imagesTab2[0])
            # self.label_9.setText("Image 1")

    def showImageInLabel(self, img):
        pix = QPixmap(img)
        self.w = pix.width()
        self.h = pix.height()
        print("Image resolution = ", '(',self.w,')', '(',self.h,')')
        pix = pix.scaled(self.size(), aspectRatioMode=QtCore.Qt.KeepAspectRatio, ) # To scale image for example and keep its Aspect Ration  
        self.label.setPixmap(pix)
        self.label.setScaledContents(True)


    def nextImageTab2(self):
        if not self.imagesTab2:
            print("No files, please open one or more images")
            return
        if self.i < len(self.imagesTab2):
            path = self.imagesTab2[self.i]
            self.showImageInLabel(path)
            self.i += 1
            print(self.i)
            x = "Image " + str(self.i)
            # self.label_9.setText(x)

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

The code above is to override the three functions : mousePressEvent, mouseReleaseEvent and mouseMoveEvent because if I use only def mouseMoveEvent (self, event): for example, it doesn't work on my label I don't know how to set it to work on a certain label

So I tried to override the QPaintEvent function but if I use the code like above and use this setQWidget.paintEvent = subWidgetPaintEvent the label disappears and i find only a rectangle

Can anyone helps me to use the events on my Label like in this code and thanks

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QPainter, QColor, QBrush
from PyQt5 import QtWidgets, QtCore, QtGui

class Labella(QLabel):

    def __init__(self, parent):
        super().__init__(parent=parent)
        self.setStyleSheet('QFrame {background-color:white;}')
        self.resize(300, 300)
        self.begin = QtCore.QPoint()
        self.end = QtCore.QPoint()

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        br = QtGui.QBrush(QtGui.QColor(100, 10, 10, 40))  
        qp.setBrush(br)   
        qp.drawRect(QtCore.QRect(self.begin, self.end))       

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = event.pos()
        self.update()
        print("beegin = ", self.begin)
        print("end 1 = ", self.end)

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

    def mouseReleaseEvent(self, event):
        self.begin = event.pos()
        self.end = event.pos()
        self.update()

    def drawRectangles(self, qp):    
        qp.setBrush(QColor(255, 0, 0, 100))
        qp.save() # save the QPainter config

        qp.drawRect(10, 15, 20, 20)

        qp.setBrush(QColor(0, 0, 255, 100))
        qp.drawRect(50, 15, 20, 20)

        qp.restore() # restore the QPainter config            
        qp.drawRect(100, 15, 20, 20)

class Example(QWidget):

    def __init__(self):
        super().__init__()

    lb = Labella(self)

    self.setGeometry(300, 300, 350, 300)
    self.setWindowTitle('Colours')
    self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

回答1:

The solution is to promote the Labella to be used in Qt Designer, for this you must first create the file labella.py.

labella.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Labella(QtWidgets.QLabel):
    def __init__(self, parent):
        super().__init__(parent=parent)
        self.setStyleSheet('QFrame {background-color:white;}')
        self.resize(300, 300)
        self.begin = QtCore.QPoint()
        self.end = QtCore.QPoint()

    def paintEvent(self, event):
        super().paintEvent(event)
        qp = QtGui.QPainter(self)
        br = QtGui.QBrush(QtGui.QColor(100, 10, 10, 40))  
        qp.setBrush(br)   
        qp.drawRect(QtCore.QRect(self.begin, self.end))       

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = event.pos()
        self.update()
        print("beegin = ", self.begin)
        print("end 1 = ", self.end)

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

    def drawRectangles(self, qp):    
        qp.setBrush(QColor(255, 0, 0, 100))
        qp.save() # save the QPainter config
        qp.drawRect(10, 15, 20, 20)
        qp.setBrush(QColor(0, 0, 255, 100))
        qp.drawRect(50, 15, 20, 20)
        qp.restore() # restore the QPainter config            
        qp.drawRect(100, 15, 20, 20)

Then we place in the same folder the labella.py and the .ui that will call mainwindow.ui

.
├── labella.py
└── mainwindow.ui

You open the .ui file by obtaining the following:

Press the right click on the QLabel and select the option: Promote to ...

Fill in the fields as shown in the following image, then press the Add button and then the Promote button:

and finally generate the .py again with the help of pyuic5:

pyuic5 mainwindow.ui -o mainwindow.py -x