Окно обновляется только когда оно изменяет размер в PyQt5 - PullRequest
0 голосов
/ 04 декабря 2018

Приложение должно отображать изображение из сетки пикселей.Цвет всех пикселей должен меняться 30 раз в секунду.После запуска приложение работает в течение нескольких секунд, после чего обновление пикселей прекращается.При изменении размера окна обновление пикселей возобновляется.При длительном обновлении пиксельной сети потребление ЦП значительно возрастает.Я проверил это на Windows, и там обновление пикселя останавливается почти немедленно.Использовал библиотеку Threading и библиотеку PyQt5 для отображения интерфейса.Как сделать стабильные пиксельные обновления в сетке?

Вот мой код:

from random import choice, randint
from sys import argv
from threading import Thread
from time import sleep

from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QIcon, QPalette
from PyQt5.QtWidgets import (QApplication, QFrame, QGridLayout, QMainWindow,
                             QMenu, QToolBar, QWidget)


class EmulatorWindow(QMainWindow):
    spacing = None
    app_running = True

    def __init__(self, spacing=1, screen_resolution=(16, 16)):
        super().__init__()
        self.spacing = spacing

        # Pixel Grid
        self.grid = QGridLayout()
        self.grid.setContentsMargins(0, 0, 0, 0)
        self.grid.setSpacing(self.spacing)
        for x in range(0, screen_resolution[0]):
            for y in range(0, screen_resolution[1]):
                pixel = QWidget()
                pixel.setAutoFillBackground(True) 
                self.grid.addWidget(pixel, y, x)

        # Application thread
        self.applicationThread = Thread(target=self.applicationRunner, args=())
        self.applicationThread.start()

        # Window Properties
        self.setGeometry(300, 300, 450, 495)
        self.setWindowTitle('Pixels Grid')

        widget = QWidget()
        widget.setLayout(self.grid)
        self.setCentralWidget(widget)
        self.setMinimumSize(QSize(450, 495))
        self.show()

    def applicationRunner(self):
        color = 0
        while True:
            if self.app_running == False:
                break
            for x in range(0, 16):
                for y in range(0, 16):
                    self.grid.itemAtPosition(x, y).widget().setPalette(QPalette([Qt.red, Qt.blue, Qt.green][color]))
            sleep(1 / 30)
            color = color + 1
            if color == 3:
                color = 0

    def switchSpacing(self):
        self.grid.setSpacing(self.spacing if self.grid.spacing() == 0 else 0)

if __name__ == '__main__':
    app = QApplication(argv)
    ex = EmulatorWindow()
    app.exec_()
    ex.app_running = False

Скриншот монитора активности

ВСнимок экрана - MenuBar и ToolBar, но они не влияют на проблему

Снимок экрана приложения

1 Ответ

0 голосов
/ 04 декабря 2018

Причина, по которой графический интерфейс не обновляется потоком, заключается в том, что Qt запрещает обновление графических элементов из другого потока, для получения дополнительной информации читайте Поток графического интерфейса и рабочий поток .Поток не должен использоваться, если задача не тяжелая, например, если мы тестируем, когда он потребляет изменение цвета, используя следующий код:

t = QtCore.QElapsedTimer()
t.start()
pal = QtGui.QPalette([QtCore.Qt.red, QtCore.Qt.blue, QtCore.Qt.green][self._color])
for x in range(self.grid.rowCount()):
    for y in range(self.grid.columnCount()):
        w = self.grid.itemAtPosition(x, y).widget()
        if w is not None:
            w.setPalette(pal)
self._color = (self._color +1) % 3
print(t.elapsed(), " milliseconds")

Получение следующих результатов:

4  milliseconds
2  milliseconds
2  milliseconds
3  milliseconds
2  milliseconds
3  milliseconds
3  milliseconds
2  milliseconds
3  milliseconds
# ...

Поддерживая мое утверждение, что это не тяжелая задача, поэтому в этом случае вам следует использовать QTimer, который позволяет вам выполнять периодические задачи:

from PyQt5 import QtCore, QtGui, QtWidgets

class EmulatorWindow(QtWidgets.QMainWindow):
    def __init__(self, spacing=1, screen_resolution=(16, 16)):
        super().__init__()
        self.spacing = spacing
        # Pixel Grid
        self.grid = QtWidgets.QGridLayout()
        self.grid.setContentsMargins(0, 0, 0, 0)
        self.grid.setSpacing(self.spacing)
        for x in range(screen_resolution[0]):
            for y in range(screen_resolution[1]):
                pixel = QtWidgets.QWidget(autoFillBackground=True)
                self.grid.addWidget(pixel, y, x)
        # Window Properties
        self.setGeometry(300, 300, 450, 495)
        self.setWindowTitle('Pixels Grid')

        widget = QtWidgets.QWidget()
        self.setCentralWidget(widget)
        widget.setLayout(self.grid)
        self.setMinimumSize(QtCore.QSize(450, 495))
        self._color = 0
        timer = QtCore.QTimer(self, interval=1000/30, timeout=self.applicationRunner)
        timer.start()

    @QtCore.pyqtSlot()
    def applicationRunner(self):
        pal = QtGui.QPalette([QtCore.Qt.red, QtCore.Qt.blue, QtCore.Qt.green][self._color])
        for x in range(self.grid.rowCount()):
            for y in range(self.grid.columnCount()):
                w = self.grid.itemAtPosition(x, y).widget()
                if w is not None:
                    w.setPalette(pal)
        self._color = (self._color +1) % 3

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    ex = EmulatorWindow()
    ex.show()
    sys.exit(app.exec_())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...