I'm writing a simulation GUI, in which QtPainter is draws a Pixmap instantly when the windows is opening. I use different buttons, that are supposed to result in painting on top of the pixmap (lines and other geometric forms)
I've tried writing functions that take the QtPainter object as an argument and use button.clicked.connect() to call the functions but the drawings never appear on screen. As I am new to PyQt I am not sure with how it works, but I guess I can only paint by calling the paintEvent() function, but if I write all geometric forms in paintEvent(), how do I make sure they only appear when the button is pressed?
To draw directly onto a widget you can override it's paintEvent
. The thing to remember when doing so is that paintEvent
fires every time the Widget deems it necessary to redraw itself e.g. when it has been resized or moved. This means that when implementing your own version of paintEvent
you need to draw all the shapes you want drawn on your widget. Note that paintEvent
is rarely called directly. If you want the widget to redraw itself, you should call update()
which will schedule a paintEvent
for you.
Here is a simple example where arbitrary rectangles are added to a canvas when clicking on a button. The rectangles are stored in an array in the Canvas
object. In Canvas.paintEvent
, we create an instance of QPainter
and use this object to draw all the rectangles in the array.
from PyQt5 import QtWidgets, QtGui, QtCore
from random import randrange
class Canvas(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.rectangles = []
def add_rectangle(self, rect, color):
self.rectangles.append((rect, color))
self.update()
def paintEvent(self, event):
painter = QtGui.QPainter(self)
brush = QtGui.QBrush(QtCore.Qt.white)
painter.setBrush(brush)
painter.drawRect(event.rect())
pen = QtGui.QPen()
pen.setWidth(3)
for rect, color in self.rectangles:
pen.setColor(color)
painter.setPen(pen)
brush.setColor(color)
painter.setBrush(brush)
painter.drawRect(rect)
class MyWindow(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.canvas = Canvas(self)
self.button = QtWidgets.QPushButton('Add rectangle')
self.button.clicked.connect(self.add_rectangle)
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.addWidget(self.canvas)
self.layout.addWidget(self.button)
self.resize(500,500)
def add_rectangle(self):
w = self.canvas.width()
h = self.canvas.height()
x0, y0 = randrange(w), randrange(h)
x1, y1 = randrange(w), randrange(h)
shape = QtCore.QRect(min(x0,x1), min(y0,y1), abs(x0-x1), abs(y0-y1))
color = QtGui.QColor.fromRgb(*(randrange(256) for i in range(3)), 180)
self.canvas.add_rectangle(shape, color)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
window = MyWindow()
window.show()
app.exec()
Great thank you for your support! I will try to implement it on my simulation later, but it looks exactly like what I've been looking for!
It works apart that instead of self.update() you have to use self.repaint()