How to update window in PyQt5?

10,839

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_())
Share:
10,839
Jacek Wodecki
Author by

Jacek Wodecki

Updated on June 04, 2022

Comments

  • Jacek Wodecki
    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 in go function in App class. I did some research but nothing works. People tell me to just add line like self.show() or self.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_())