pyqt5 is running slow

So my program is a Sudoku and so far, I have finished the code that generates boards. I am trying to take this an implement it into pyqt5. I have done this through by making a button correspond to each number in Sudoku board (so 81 buttons). I have a pc that is capable of running triple A games at high frame rates(if that matters). What could be the issue? I have put my code below.

import random
import numpy as np

import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg


# populates a row in random spaces
def populate():
    row = [0] * 9
    num_in_box = random.randint(3, 6)
    while True:
        spot = random.randint(0, 8)
        r = random.randint(1, 9)
        if r not in row:
            row[spot] = r
        if row.count(0) == (9 - num_in_box):
            break
    return row


# populates a grid in random spaces - has duplicates in column, row and box
mapped = list()
for x in range(9): mapped.append(populate())


# checks every number in column and row and returns numbers in list
def col_row_nums(array, row, col):
    check_col = [j for j in array[:, col] if j != 0]
    check_row = [i for i in array[row] if i != 0]
    if array[row][col] in check_col:
        check_col.remove(array[row][col])
    return check_col + check_row


# checks every number box and returns numbers in list
def box_nums(array, box_row):
    reshaped_box = np.reshape(array, (27, 3))
    list_boxes_in_rows = list()
    for a in range(3):
        for b in range(3):
            for c in range(3):
                p2 = list(np.reshape((reshaped_box[c::3]), (3, 9)))
                for d in range(3): list_boxes_in_rows.append(p2[a])
    array_boxes_in_rows = np.array(list_boxes_in_rows)
    return [k for k in array_boxes_in_rows[box_row] if k != 0]


# Basically goes through each number and removes any duplicates so each column, row and box all have only one set of numbers 1 - 9
def finalize_board():
    box_rows_num = -1
    for x in range(9):
        for y in range(9):
            box_rows_num += 1
            arr = np.array(mapped)
            possible_nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
            col_row_duplicates = list()
            box_duplicates = list()
            used_nums = set(col_row_nums(arr, x, y)) | set(box_nums(arr, box_rows_num))

            for w in used_nums:
                col_row_count = col_row_nums(arr, x, y).count(w)
                box_count = box_nums(arr, box_rows_num).count(w)
                if col_row_count > 1: col_row_duplicates.append(w)
                if box_count > 1: box_duplicates.append(w)
            if mapped[x][y] in col_row_duplicates or mapped[x][y] in box_duplicates:
                remaining_nums = list(set(possible_nums) - set(used_nums))
                if len(remaining_nums) > 0: mapped[x][y] = random.choice(remaining_nums)
    return mapped


class MainWindow(qtw.QWidget):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # -------------------------------------------------------------------------------------------
        all_buttons = list()
        layout = qtw.QGridLayout()

        for x in range(9):
            for y in range(9):
                if finalize_board()[x][y] == 0: finalize_board()[x][y] = ' '
                all_buttons.append(layout.addWidget(qtw.QPushButton(str(finalize_board()[x][y])), x, y))

        self.setLayout(layout)

        # -------------------------------------------------------------------------------------------
        self.show()


if __name__ == '__main__':
    app = qtw.QApplication(sys.argv)
    w = MainWindow()
    sys.exit(app.exec_())

Answer

The problem is that you are executing the finalize_board function for each button, so if the function takes T seconds and you have N buttons (in your case 81), also in each iteration you are using K times that function (in your case 3), then the total time is T * N * K.

The solution is to invoke finalize_board only once.

class MainWindow(qtw.QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        all_buttons = list()
        layout = qtw.QGridLayout(self)

        board = finalize_board()

        for x, rows in enumerate(board):
            for y, cell in enumerate(rows):
                button = qtw.QPushButton(str(cell))
                layout.addWidget(button, x, y)
                all_buttons.append(button)
        self.show()