PyQt:最大化窗口时如何防止处理多个调整大小事件?

问题描述 投票:0回答:2

我有一个

QMainWindow
,其中包含一个子
QWidget
,其自身又包含一个
QLabel

当窗口最大化时(例如,通过单击窗口上的最大化图标),

QLabel.resizeEvent()
处理程序会被多次调用(据说是跟随窗口逐渐放大,直到占据整个桌面空间)。

事件处理程序中的代码调用

setPixmap()
来缩放标签像素图。这是一个相对较长的操作,会减慢进程。标签代码:

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QLabel, QFrame, QGridLayout
from PyQt5.QtGui import QImageReader, QPixmap

class DisplayArea(QLabel):
    def __init__(self):
        super().__init__()
        self.pix_map = None
        self.init_ui()

    def init_ui(self):
        self.setMinimumSize(1, 1)
        self.setStyleSheet("border:1px solid black;")

    def set_image(self, image):
        self.pix_map = QPixmap.fromImage(image)
        self.scale_image(self.size())

    def scale_image(self, size):
        if self.pix_map is None:
            return

        scaled = self.pix_map.scaled(size, Qt.KeepAspectRatio)
        self.setPixmap(scaled)

    def resizeEvent(self, e):
        self.scale_image(e.size())
        super().resizeEvent(e)

当窗口达到其最终大小时,是否可以仅处理该事件一次?

python pyqt pyqt5 qlabel qpixmap
2个回答
1
投票

问题在于,在窗口最大化时,resizeEvent 被调用了很多次,而同样的次数就是您所说的scale_image。一种可能的可能是除非经过一段时间才更新。在以下示例中,仅调整大于 100 毫秒的时间(您必须校准的时间):

from PyQt5 import QtCore, QtGui, QtWidgets

class DisplayArea(QtWidgets.QLabel):
    def __init__(self):
        super().__init__()
        self.pix_map = QtGui.QPixmap()
        self._flag = False
        self.init_ui()

    def init_ui(self):
        self.setMinimumSize(1, 1)
        self.setStyleSheet("border:1px solid black;")

    def set_image(self, image):
        self.pix_map = QtGui.QPixmap.fromImage(image)
        self.scale_image()

    def scale_image(self):
        if self.pix_map.isNull():
            return
        scaled = self.pix_map.scaled(self.size(), QtCore.Qt.KeepAspectRatio)
        self.setPixmap(scaled)

    def resizeEvent(self, e):
        if not self._flag:
            self._flag = True
            self.scale_image()
            QtCore.QTimer.singleShot(100, lambda: setattr(self, "_flag", False))
        super().resizeEvent(e)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QMainWindow()
    da = DisplayArea()
    da.set_image(QtGui.QImage("logo.png"))
    w.setCentralWidget(da)
    w.show()
    sys.exit(app.exec_())

0
投票

只是添加一个今天对我有用的想法。

eyllanesc 有一个可以打开或关闭的控制标志的想法。在想到一个适用于我的特定情况的细微变化之前,我对这个想法进行了挣扎。我拥有的是一个具有可变高度行的

QTableView
。当
QTableView
调整大小时,我希望整个表格调整每一行的行高。我对
resizeEvent
的覆盖是这样的:

def resizeEvent(self, event):
    super().resizeEvent(event)
    QtCore.QTimer.singleShot(0, self.resizeRowsToContents)

我对

resizeRowsToContents
的覆盖看起来像这样:

def resizeRowsToContents(self):
    v_header = self.verticalHeader()
    for row in range(self.model().rowCount()):
        hint = self.sizeHintForRow(row)
        v_header.resizeSection(row, hint)    

NB 我在构造函数中也有这一行:

self.horizontalHeader().sectionResized.connect(self.resizeRowsToContents)   

这确实有时会导致“闪烁”或更确切地说“振荡”,特别是 v 滚动条显示然后再次消失,然后重新出现,然后消失之间的振荡......(留给它自己的设备它会这样做无限地,每隔几毫秒就有一个新的调用!)。

为了恢复稳定性,我发现有一个简单的布尔标志是不够的。但拥有一个三态整数控制变量确实可以解决问题。我将其添加到构造函数中:

self.resizing_rows = 0

并制作了

resizeRowsToContents
,如下所示:

def resizeRowsToContents(self):
    if self.resizing_rows > 1:
         logger.info(f'+++ resizeRowsToContents')
         return
    self.resizing_rows += 1
    v_header = self.verticalHeader()
    for row in range(self.model().rowCount()):
        hint = self.sizeHintForRow(row)
        v_header.resizeSection(row, hint)    
    def end_resizing_rows():
        self.resizing_rows = 0
    QtCore.QTimer.singleShot(100, end_resizing_rows)

这具有所需的阻尼效果,但仍然可以调整行的大小。确实,为了进行适当的调整,有必要调用

resizeRowsToContents
,而不是一次,而是两次......然后停止。我无法根据机制知识来解释这一点......但它可能适用于其他类似的不需要的 Qt“振荡”/“闪烁”现象。

© www.soinside.com 2019 - 2024. All rights reserved.