PySide6窗口显示前后修改Label颜色的性能差异原因探究
问题:Qt样式表与QPalette修改Label背景色的性能差异
我正在构建一个Label网格,可根据网格位置的数值持续修改其背景色。采用三种方式定位指定网格的Label并修改颜色:changeBox1和changeBox2使用样式表(style sheet),changeBox3直接修改QPalette。
测试发现,在类的__init__函数中调用这些方法时,运行速度远快于窗口调用show()之后。其中直接修改QPalette的changeBox3方法性能始终稳定,而使用样式表的方法性能差异显著。疑惑这是否与Qt(或PySide)在部件实例化后处理样式表的方式有关?
使用Python 3.10.15,以下是500次运行的平均耗时数据:
Average execution time of changeBox1 in init over 500 runs: 1.0947501286864281e-06 seconds Average execution time of changeBox2 in init over 500 runs: 4.599159583449364e-07 seconds Average execution time of changeBox3 in init over 500 runs: 2.514749765396118e-06 seconds Average execution time of changeBox1 after window.show over 500 runs: 1.4484249986708164e-05 seconds Average execution time of changeBox2 after window.show over 500 runs: 1.4263415709137917e-05 seconds Average execution time of changeBox3 after window.show over 500 runs: 2.624250017106533e-06 seconds Average execution time of myLabel1 over 500 runs: 1.668350026011467e-05 seconds Average execution time of myLabel2 over 500 runs: 1.4353999868035316e-05 seconds
最小可复现示例(MWE)
import sys import timeit from PySide6.QtCore import QSize, Qt from PySide6.QtGui import QColor, QFont, QPalette from PySide6.QtWidgets import ( QApplication, QGridLayout, QLabel, QMainWindow, QWidget, ) arrows = ["↑", "↓", "→", "←"] class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("My App") self.setMinimumSize(QSize(400, 300)) self.layout = QGridLayout() self.layout.setSpacing(3) xMax = 4 yMax = 5 steps = xMax * yMax midStep = steps // 2 + 1 cStep = 255 // midStep cRed = 255 cGreen = 0 cBlue = 0 self.labels = {} for b in range(steps): label = f"T{b}\n{arrows[b % 4]}" (x,y) = divmod(b, xMax) color = f"{cRed:02x}{cGreen:02x}{cBlue:02x}" self.labels[(x, y)] = self.addBox(color, label, x, y) if b < midStep: cGreen += cStep else: cRed -= cStep widget = QWidget() widget.setLayout(self.layout) self.setCentralWidget(widget) runs = 500 time1 = timeit.timeit(lambda: self.changeBox1(2, 2), number=runs) time2 = timeit.timeit(lambda: self.changeBox2(1, 1), number=runs) time3 = timeit.timeit(lambda: self.changeBox3(0, 0), number=runs) print( f"Average execution time of changeBox1 in init over {runs} runs: {time1 / runs} seconds" ) print( f"Average execution time of changeBox2 in init over {runs} runs: {time2 / runs} seconds" ) print( f"Average execution time of changeBox3 in init over {runs} runs: {time3 / runs} seconds" ) # Find the fastest way to change the color of a specific widget def changeBoxColor(self): self.changeBox1(2, 2) self.changeBox2(1, 1) self.changeBox3(0, 0) runs = 500 time1 = timeit.timeit(lambda: self.changeBox1(2, 2), number=runs) time2 = timeit.timeit(lambda: self.changeBox2(1, 1), number=runs) time3 = timeit.timeit(lambda: self.changeBox3(0, 0), number=runs) print( f"Average execution time of changeBox1 after window.show over {runs} runs: {time1 / runs} seconds" ) print( f"Average execution time of changeBox2 after window.show over {runs} runs: {time2 / runs} seconds" ) print( f"Average execution time of changeBox3 after window.show over {runs} runs: {time3 / runs} seconds" ) def myLabelTimer(self): runs = 500 time1 = timeit.timeit(lambda: self.myLabel1("f0f0f0", "Test"), number=runs) time2 = timeit.timeit(lambda: self.myLabel2("f0f0f0", "Test"), number=runs) print( f"Average execution time of myLabel1 over {runs} runs: {time1 / runs} seconds" ) print( f"Average execution time of myLabel2 over {runs} runs: {time2 / runs} seconds" ) def myLabel1(self, color, text): tBox = QLabel(text) tBox.setAutoFillBackground(True) tBox.setAlignment(Qt.AlignCenter) tBox.setStyleSheet(f"background: #{color}; font-style: bold; font-size: 24pt") return tBox def myLabel2(self, color, text): tBox = QLabel(text) tBox.setAutoFillBackground(True) tBox.setAlignment(Qt.AlignCenter) palette = tBox.palette() palette.setColor(QPalette.Window, QColor(f"#{color}")) tBox.setPalette(palette) font = QFont() font.setPointSize(24) font.setWeight(QFont.Bold) tBox.setFont(font) return tBox def addBox(self, color, text, x, y): box = self.myLabel2(color, text) self.layout.addWidget(box, x, y) return box def changeBox1(self, x, y): cell = self.layout.itemAtPosition(x, y).widget() cell.setStyleSheet( f"background: #{'0000ff'}; font-style: bold; font-size: 24pt" ) def changeBox2(self, x, y): self.labels[(x, y)].setStyleSheet( f"background: #{'00f0ff'}; font-style: bold; font-size: 24pt" ) def changeBox3(self, x, y): palette = self.labels[(x, y)].palette() palette.setColor(QPalette.Window, QColor("#0080ff")) self.labels[(x, y)].setPalette(palette) app = QApplication(sys.argv) window = MainWindow() window.show() window.changeBoxColor() window.myLabelTimer() app.exec()
QGraphicsScene/QGraphicsView版本代码
import sys import timeit from PySide6.QtCore import QSize, QPoint, Qt from PySide6.QtGui import QColor, QFont, QPalette from PySide6.QtWidgets import ( QApplication, QLabel, QWidget, QGraphicsScene, QGraphicsView, QHBoxLayout, ) arrows = ["↑", "↓", "→", "←"] class Window(QWidget): xMax : int = 4 yMax : int = 5 boxSize : int = 75 labels = {} def __init__(self): super().__init__() self.setWindowTitle("My App") self.setMinimumSize(QSize(self.xMax * self.boxSize, self.yMax * self.boxSize )) self.scene = QGraphicsScene(0, 0, self.xMax * self.boxSize, self.yMax * self.boxSize) label = self.label("0000ff", "Test",QPoint(self.boxSize,self.boxSize)) self.scene.addWidget(label) self.buildGrid() # Define our layout. view = QGraphicsView(self.scene) hbox = QHBoxLayout(self) hbox.addWidget(view) self.setLayout(hbox) def label(self, color, text, pos : QPoint): tBox: QLabel = QLabel(text) tBox.setFixedSize(self.boxSize,self.boxSize) tBox.setAutoFillBackground(True) tBox.setAlignment(Qt.AlignCenter) palette = tBox.palette() palette.setColor(QPalette.Window, QColor(f"#{color}")) tBox.setPalette(palette) font = QFont() font.setPointSize(24) font.setWeight(QFont.Bold) tBox.setFont(font) tBox.move(pos) return tBox def buildGrid(self): steps = self.xMax * self.yMax midStep = steps // 2 + 1 cStep = 255 // midStep cRed = 255 cGreen = 0 cBlue = 0 for b in range(steps): label = f"T{b}\n{arrows[b % 4]}" (y,x) = divmod(b, self.xMax) color = f"{cRed:02x}{cGreen:02x}{cBlue:02x}" self.labels[(x, y)] = self.label(color, label, QPoint(x * self.boxSize,y * self.boxSize)) self.scene.addWidget(self.labels[(x, y)]) if b < midStep: cGreen += cStep else: cRed -= cStep def changeBox(self, x, y): palette = self.labels[(x, y)].palette() palette.setColor(QPalette.Window, QColor("#0080ff")) self.labels[(x, y)].setPalette(palette) app = QApplication(sys.argv) window = Window() window.show() window.changeBox(0, 0) runs = 500 time1 = timeit.timeit(lambda: window.changeBox(2, 2), number=runs) print( f"Average execution time of changeBox in init over {runs} runs: {time1 / runs} seconds" ) app.exec()
内容的提问来源于stack exchange,提问作者JKomp
相关产品推荐
相关产品推荐

