【问题标题】:Window updates only when it resizes in PyQt5仅在 PyQt5 中调整大小时窗口才会更新
【发布时间】:2018-12-04 05:56:56
【问题描述】:

应用程序应显示像素网格中的图像。所有像素的颜色应该每秒改变 30 次。启动应用程序几秒钟后,像素更新将停止。当窗口调整大小时,像素更新恢复。随着像素网络的长期更新,CPU消耗大大增加。我在 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,但是,它们不影响问题

应用截图

【问题讨论】:

    标签: python python-3.x pyqt pyqt5


    【解决方案1】:

    GUI不随线程更新的原因是Qt禁止从另一个线程更新图形元素,更多信息请阅读GUI Thread and Worker Thread。如果任务不繁重,则不应使用线程,例如,如果我们使用以下代码测试它何时消耗更改颜色:

    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_())
    

    【讨论】:

      猜你喜欢
      • 2015-05-08
      • 2013-07-13
      • 2016-07-08
      • 1970-01-01
      • 1970-01-01
      • 2013-04-14
      • 2021-01-14
      • 1970-01-01
      • 2013-09-19
      相关资源
      最近更新 更多