2

While experimenting with Python and PyQt5 I got stuck on a problem. I have in my GUI few labels (QLabel) and images (QPixmap) and I want to draw something on them, depending on what the main program does. I can't figure out how though. For example, I change text on labels calling setLabels() from class BinColUI and I would like to draw something on them (i.e. QPainter.drawLine()) just after that. What I tried is not working, there's nothing drawn. My unsuccesful attempt is commented out in setLabels(). How do I do it?

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QPen
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget


class BinColUI(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUi()
        self.createLabelTop()
        self.createLabelBot()

    def initUi(self):
        self.setWindowTitle('Bin Collection')
        self.setFixedSize(500, 500)
        # self.setStyleSheet('background-color: white')
        self.generalLayout = QVBoxLayout()
        self._centralWidget = QWidget(self)
        self.setCentralWidget(self._centralWidget)
        self._centralWidget.setLayout(self.generalLayout)

    def paintEvent(self, event):
        self.qp = QPainter()
        self.qp.begin(self)
        self.drawLine(event, self.qp)
        self.qp.end()

    def drawLine(self, event, qp):
        pen = QPen(Qt.red, 3, Qt.SolidLine)
        qp.setPen(pen)
        qp.drawLine(5, 5, 495, 5)
        qp.drawLine(495, 5, 495, 495)
        qp.drawLine(495, 495, 5, 495)
        qp.drawLine(5, 495, 5, 5)

    def createLabelTop(self):
        self.label_top = QLabel('PLEASE WAIT')
        self.label_top.setAlignment(Qt.AlignCenter)
        self.label_top.setFixedSize(450, 60)
        self.label_top.setStyleSheet("font: 14pt Bahnschrift; color: black; background-color: yellow")
        self.generalLayout.addWidget(self.label_top, alignment=Qt.AlignCenter)

    def createLabelBot(self):
        self.label_bot = QLabel('PLEASE WAIT')
        self.label_bot.setAlignment(Qt.AlignCenter)
        self.label_bot.setFixedSize(450, 60)
        self.label_bot.setStyleSheet("font: 14pt Bahnschrift; color: black; background-color: yellow")
        self.generalLayout.addWidget(self.label_bot, alignment=Qt.AlignCenter)

    def setLabels(self, texttop, textbot):
        # qp = QPainter(self.label_top)
        self.label_top.setText(texttop)
        self.label_bot.setText(textbot)
        # pen = QPen(Qt.red, 3)
        # qp.setPen(pen)
        # qp.drawLine(10, 10, 50, 50)
        # self.label_top.repaint()


class BinColCtrl:

    def __init__(self, view: BinColUI):
        self._view = view
        self.calculateResult()

    def calculateResult(self):
        line_top = 'NEW LABEL TOP'
        line_bottom = 'NEW LABEL BOTTOM'
        self._view.setLabels(line_top, line_bottom)


def main():
    """Main function."""
    # Create an instance of `QApplication`
    bincol = QApplication(sys.argv)
    window = BinColUI()
    window.show()
    BinColCtrl(view=window)
    sys.exit(bincol.exec_())


if __name__ == '__main__':
    main()
cyanidem
  • 103
  • 2
  • 9
  • Explain yourself better, where do you want to draw? In the upper or lower QLabel or elsewhere? – eyllanesc Jan 22 '20 at 18:40
  • I want to draw on upper QLabel (label_top). For now, but when/if I figure it out I want to draw on other widgets as well. – cyanidem Jan 22 '20 at 18:43

2 Answers2

5

In general, the painting of a QWidget (QLabel, QPushButton, etc.) should only be done in the paintEvent method as the OP seems to know. And that painting depends on the information that the widget has, for example QLabel uses a text and draws the text, OR uses a QPixmap and draws based on that pixmap. So in this case you must create a QPixmap where the line is painted, and pass that QPixmap to the QLabel to paint it.

def setLabels(self, texttop, textbot):
    pixmap = QPixmap(self.label_top.size())
    pixmap.fill(Qt.transparent)
    qp = QPainter(pixmap)
    pen = QPen(Qt.red, 3)
    qp.setPen(pen)
    qp.drawLine(10, 10, 50, 50)
    qp.end()
    self.label_top.setPixmap(pixmap)
    self.label_bot.setText(textbot)

enter image description here

Update:

I can't have text and drawn line on the label?

As I already pointed out in the initial part of my answer: Either you paint a text or you paint a QPixmap, you can't do both in a QLabel.

Can I draw line then text on it using QPainter.drawText()?

Yes, you can use all the methods to paint the text in the QPixmap: be creative :-). For example:

def setLabels(self, texttop, textbot):
    pixmap = QPixmap(self.label_top.size())
    pixmap.fill(Qt.transparent)
    qp = QPainter(pixmap)
    pen = QPen(Qt.red, 3)
    qp.setPen(pen)
    qp.drawLine(10, 10, 50, 50)

    qp.drawText(pixmap.rect(), Qt.AlignCenter, texttop)

    qp.end()
    self.label_top.setPixmap(pixmap)
    self.label_bot.setText(textbot)

enter image description here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
0

I find out an answer with the help of this thread PySide6 app crashes when using QPainter.drawLine()

So modified draw_something should look like this:

    def draw_something(self):
        # Setup canvas for drawing
        canvas = QPixmap(self.ui.label_2.size()) # Make new pixmap and set size from the size of the label
        canvas.fill(Qt.GlobalColor.transparent) # Set color
        self.ui.label_2.setPixmap(canvas) # Set label pixmap to THIS canvas
        pm = self.ui.label_2.pixmap() # Make new instance label's pixmap
        painter = QPainter(canvas) # Setup painter to use it
        painter.drawLine(10, 10, 300, 200) # drawwing begins
        painter.end()
        self.ui.label_2.setPixmap(pm) # Set label pixmap to use this pixmap