How to update window in PyQt5?
Below is a version of your script that shows how to do some simple animation using QApplication.processEvents
, a timer, and sleep. I took the liberty of adding an F5 shortcut for starting a new run, and have tidied up a few other things:
import sys, math, random
from PyQt5.QtWidgets import QApplication, QWidget, QShortcut
from PyQt5.QtGui import QPainter, QPen, QColor, QKeySequence
from PyQt5.QtCore import Qt, QTimer, QThread
WIDTH = 40
grid = []
class Cell():
def __init__(self, i, j):
self.i = i
self.j = j
self.walls = [1, 1, 1, 1] # top, right, bottom, left
self.visited = 0
self.currentCell = 0
def index(self, i, j, cols, rows):
if (i < 0) or (j < 0) or (i > (cols - 1)) or (j > (rows - 1)):
return None
else:
return i + j * cols
def checkNeighbors(self, cols, rows):
neighbors = []
if not (self.index(self.i, self.j - 1, cols, rows) is None):
top = grid[self.index(self.i, self.j - 1, cols, rows)]
if not (self.index(self.i + 1, self.j, cols, rows) is None):
right = grid[self.index(self.i + 1, self.j, cols, rows)]
if not (self.index(self.i, self.j + 1, cols, rows) is None):
bottom = grid[self.index(self.i, self.j + 1, cols, rows)]
if not (self.index(self.i - 1, self.j, cols, rows) is None):
left = grid[self.index(self.i - 1, self.j, cols, rows)]
if 'top' in locals() and not top.visited:
neighbors.append(top)
if 'right' in locals() and not right.visited:
neighbors.append(right)
if 'bottom' in locals() and not bottom.visited:
neighbors.append(bottom)
if 'left' in locals() and not left.visited:
neighbors.append(left)
if len(neighbors) > 0:
r = math.floor(random.uniform(0, len(neighbors)))
return neighbors[r]
else:
return None
class App(QWidget):
def __init__(self):
super().__init__()
self.left = 100
self.top = 100
self.width = 400
self.height = 400
self.cols = math.floor(self.width / WIDTH)
self.rows = math.floor(self.height / WIDTH)
self.active = False
self.initui()
self.init_cells()
def init_cells(self):
if not self.active:
del grid[:]
for j in range(self.rows):
for i in range(self.cols):
cell = Cell(i, j)
grid.append(cell)
QTimer.singleShot(1, self.go)
def go(self):
self.active = True
current = grid[0]
current.visited = 1
current.currentCell = 1
while True:
self.update()
QApplication.processEvents()
QThread.msleep(150)
next = current.checkNeighbors(self.cols, self.rows)
if next is not None:
next.visited = 1
next.currentCell = 1
current.currentCell = 0
current = next
else:
break
self.active = False
def initui(self):
QShortcut(QKeySequence('F5'), self, self.init_cells)
self.setGeometry(self.left, self.top, self.width, self.height)
self.setAutoFillBackground(True)
p = self.palette()
p.setColor(self.backgroundRole(), Qt.lightGray)
self.setPalette(p)
self.show()
def paintEvent(self, e):
for i in grid:
self.draw_cell(i)
def draw_cell(self, cell):
x = cell.i * WIDTH
y = cell.j * WIDTH
# LINES
qp = QPainter(self)
qp.setPen(QPen(Qt.black, 1, Qt.SolidLine))
if cell.walls[0]: # top
qp.drawLine(x , y , x + WIDTH, y)
if cell.walls[1]: # right
qp.drawLine(x + WIDTH, y , x + WIDTH, y + WIDTH)
if cell.walls[2]: # bottom
qp.drawLine(x + WIDTH, y + WIDTH, x , y + WIDTH)
if cell.walls[3]: # left
qp.drawLine(x , y + WIDTH, x , y)
if cell.visited:
if cell.currentCell:
qp.setBrush(QColor(0, 255, 0, 255))
else:
qp.setBrush(QColor(255, 0, 255, 100))
qp.drawRect(x, y, WIDTH, WIDTH)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Jacek Wodecki
Updated on June 04, 2022Comments
-
Jacek Wodecki almost 2 years
I am trying to write a depth first recursive backtracking maze generator with PyQt5. Functionally everything works fine, but not everything is implemented yet, so result may look strange :). I should say that I am quite new to Python and PyQt. I ran into a problem with graphics update. I would like the window to update the graphics based on the outcome of the algorithm which runs in a
while
loop ingo
function in App class. I did some research but nothing works. People tell me to just add line likeself.show()
orself.update()
but none of this works.I understand that I may have done something wrong conceptually, e.g. I should place some part of code somewhere else, or something like that. Or maybe there is some line or two to place somewhere to make it work.
In either case, please help me.
import sys from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow from PyQt5.QtGui import QPainter, QPen, QColor from PyQt5.QtCore import * import math import random import pygame w = 40 grid = [] qp = QPainter() clock = pygame.time.Clock() class Cell(QMainWindow): def __init__(self, i, j): # super().__init__() self.i = i self.j = j self.walls = [1, 1, 1, 1] # top, right, bottom, left self.visited = 0 self.currentCell = 0 def index(self, i, j, cols, rows): if (i < 0) or (j < 0) or (i > (cols - 1)) or (j > (rows - 1)): return None else: return i + j * cols def checkNeighbors(self, cols, rows): neighbors = [] if not (self.index(self.i, self.j - 1, cols, rows) is None): top = grid[self.index(self.i, self.j - 1, cols, rows)] if not (self.index(self.i + 1, self.j, cols, rows) is None): right = grid[self.index(self.i + 1, self.j, cols, rows)] if not (self.index(self.i, self.j + 1, cols, rows) is None): bottom = grid[self.index(self.i, self.j + 1, cols, rows)] if not (self.index(self.i - 1, self.j, cols, rows) is None): left = grid[self.index(self.i - 1, self.j, cols, rows)] if 'top' in locals() and not top.visited: neighbors.append(top) if 'right' in locals() and not right.visited: neighbors.append(right) if 'bottom' in locals() and not bottom.visited: neighbors.append(bottom) if 'left' in locals() and not left.visited: neighbors.append(left) if neighbors.__len__() > 0: r = math.floor(random.uniform(0, neighbors.__len__())) return neighbors[r] else: return None class App(QWidget): def __init__(self): super().__init__() self.left = 100 self.top = 100 self.width = 400 self.height = 400 self.cols = math.floor(self.width/w) self.rows = math.floor(self.height/w) self.init_cells() self.initui() self.go() def init_cells(self): for j in range(self.rows): for i in range(self.cols): cell = Cell(i, j) grid.append(cell) def go(self): current = grid[0] current.visited = 1 current.currentCell = 1 next = current.checkNeighbors(self.cols, self.rows) while not (next is None): next = current.checkNeighbors(self.cols, self.rows) if not (next is None): next.visited = 1 next.currentCell = 1 current.currentCell = 0 current = next # I WANT TO UPDATE WINDOW HERE! def initui(self): self.setGeometry(self.left, self.top, self.width, self.height) self.setAutoFillBackground(True) p = self.palette() p.setColor(self.backgroundRole(), Qt.lightGray) self.setPalette(p) self.show() def paintEvent(self, e): for i in grid: self.draw_cell(i) self.show() def draw_cell(self, cell): x = cell.i*w y = cell.j*w # LINES qp.begin(self) qp.setPen(QPen(Qt.black, 1, Qt.SolidLine)) if cell.walls[0]: # top qp.drawLine(x , y , x + w, y) if cell.walls[1]: # right qp.drawLine(x + w, y , x + w, y + w) if cell.walls[2]: # bottom qp.drawLine(x + w, y + w, x , y + w) if cell.walls[3]: # left qp.drawLine(x , y + w, x , y) if cell.visited: qp.setBrush(QColor(255, 0, 255, 100)) qp.drawRect(x, y, w, w) if cell.currentCell: qp.setBrush(QColor(0, 255, 0, 255)) qp.drawRect(x, y, w, w) qp.end() self.show() if __name__ == '__main__': app = QApplication(sys.argv) ex = App() sys.exit(app.exec_())