Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PyQt5 triggering a paintEvent() with keyPressEvent()

I am trying to learn PyQt vector painting. Currently I am stuck in trying to pass information to paintEvent() method which I guess, should call other methods:

I am trying to paint different numbers to a fundamental block (here drawFundBlock() method, which should draw some lines). The code is trying to check if right arrow is pressed-> drawFundamental block and if number is pressed (now trying to simply draw "5"), it will draw that number on a certain area on that fundamental block. But I can't seem to get the QPainter to work. It seems it's calling the paintEvent() override method twice now (why?). Some people have suggested the update() method, but I have no idea how to still pass any arguments to paintEvent(), which should determine whether to draw "fundblock" or "number". Now the code uses update() for demonstration but this simply moves the lines - But the already Added Lines should remain!

Any help?

# Test QPainter etc.

from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QPen, QColor, QFont
from PyQt5.QtCore import Qt, QPoint, pyqtSignal, QRect
import sys

class Example(QWidget):

    paintTrigger = pyqtSignal()

    def __init__(self):
        super().__init__()
        self.initUI()
        self.ydist = 15
        self.eveType = "drawBlock"
        self.currentRegion = QRect(50,50,50,80)
        #self.paintTrigger[self.eveType].connect(lambda:self.paintEvent())

        self.x0=5
        self.x1=25
        self.y0=5
        self.y1=25

    def initUI(self):
        self.setGeometry(300,300,280,270)
        self.setWindowTitle('Painter training')
        self.show()

    # How to pass info here, which type of drawing should be done (block or number)?
    def paintEvent(self,event):
        qp = QPainter(self)
        qp.begin(self)  
        self.drawFundBlock(qp)
        qp.end()

    def drawFundBlock(self,qp):
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        pen.setStyle(Qt.DashLine)

        qp.setPen(pen)
        for i in range(1,10):
            #qp.drawLine(0,i*self.ydist,40,i*self.ydist)
            qp.drawLine(self.x0,i*self.y0,self.x1,self.y0*i)

        #notePoint=QPoint(200,200)
        #qp.drawText(notePoint,"5")

    def drawNumber(self,qp,notePoint):
        pen = QPen(Qt.black,2,Qt.SolidLine)
        #qp.setPen(QColor(200,200,200))
        qp.setPen(pen)
        qp.setFont(QFont('Arial', 10))
        qp.drawText(notePoint,"5")

    def nextRegion(self):
        self.x0=self.x0+30
        self.x1=self.x1+30
        self.y0=self.y0+30
        self.y1=self.y1+30

    def keyPressEvent(self,event):
        # Did the user press a button??
        gey=event.key()
        if gey == Qt.Key_M: 
            print("Key 'm' pressed!")
        elif gey == Qt.Key_Right:
            print("Right key pressed!, call drawFundBlock()")
            #self.paintTrigger["drawBlock"].emit()
            #self.paintEvent()
            self.update()
            self.nextRegion()

        elif gey == Qt.Key_5:
            print("#5 pressed, call drawNumber()")
            #self.paintTrigger["drawNo"].emit()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())
like image 282
HammieTime Avatar asked Jan 30 '23 08:01

HammieTime


1 Answers

QPaintEvent should not be called directly, we must do it through update(), this will take care of calling it internally when necessary.

Each time QPaintEvent is called this clean is space where it is going to draw so it does not save memory of the previous drawings, a simple solution is to first paint a QPixmap where it will store what you have painted previously and then paint the widget with that QPixmap.

Another thing is that the following 2 instructions are equivalent:

1.


painter = QPainter(some_QPaintDevice)

2.


painter = QPainter()
painter.begin(some_QPaintDevice)

Both methods serve to pass the object where it is going to be painted, and in your case you are assigning 2 times the same widget.

To facilitate the drawing I have proposed the method drawBackground, this method needs to be filled with self.func, the first parameter must be the name of the function and the second a dictionary with the parameters that are needed except the QPainter.

Code

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.mModified = True
        self.initUI()
        self.currentRegion = QRect(50, 50, 50, 80)
        self.x0 = 5
        self.x1 = 25
        self.y0 = 5
        self.y1 = 25
        self.mPixmap = QPixmap()
        self.func = (None, None)

    def initUI(self):
        self.setGeometry(300, 300, 280, 270)
        self.setWindowTitle('Painter training')
        self.show()

    def paintEvent(self, event):
        if self.mModified:
            pixmap = QPixmap(self.size())
            pixmap.fill(Qt.white)
            painter = QPainter(pixmap)
            painter.drawPixmap(0, 0, self.mPixmap)
            self.drawBackground(painter)
            self.mPixmap = pixmap
            self.mModified = False

        qp = QPainter(self)
        qp.drawPixmap(0, 0, self.mPixmap)

    def drawBackground(self, qp):
        func, kwargs = self.func
        if func is not None:
            kwargs["qp"] = qp
            func(**kwargs)

    def drawFundBlock(self, qp):
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        pen.setStyle(Qt.DashLine)

        qp.setPen(pen)
        for i in range(1, 10):
            qp.drawLine(self.x0, i * self.y0, self.x1, self.y0 * i)

    def drawNumber(self, qp, notePoint):
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        qp.setPen(pen)
        qp.setFont(QFont('Arial', 10))
        qp.drawText(notePoint, "5")

    def nextRegion(self):
        self.x0 += 30
        self.x1 += 30
        self.y0 += 30
        self.y1 += 30

    def keyPressEvent(self, event):
        gey = event.key()
        self.func = (None, None)
        if gey == Qt.Key_M:
            print("Key 'm' pressed!")
        elif gey == Qt.Key_Right:
            print("Right key pressed!, call drawFundBlock()")
            self.func = (self.drawFundBlock, {})
            self.mModified = True
            self.update()
            self.nextRegion()
        elif gey == Qt.Key_5:
            print("#5 pressed, call drawNumber()")
            self.func = (self.drawNumber, {"notePoint": QPoint(100, 100)})
            self.mModified = True
            self.update()
like image 166
eyllanesc Avatar answered Jan 31 '23 22:01

eyllanesc