如何使 QGridLayout 调整单元格大小完全相同?

How to make QGridLayout resize cells the exact same size?

每当我调整 window(一个 QDialog)的大小时,参考查看器和选定查看器(QScrollArea 的子类)应该始终具有完全相同的大小,即使在调整大小事件之后也是如此。 但是,两次中有一次,我将选定查看器(右侧的 QScrollArea 小部件)的大小缩小了 1 个像素。两次中一次,我的意思是每个奇数像素数。

似乎 QGridLayout 将最右侧的面板强制缩小到较小的尺寸,这可能是由于向下舍入 space 的值仍然可用。

我使用 QGridLayout,因为我需要工具栏在面板之间居中对齐并且效果很好。

Here is a screencast 演示问题:每次将选定查看器(右侧面板)的宽度调整为比左侧面板短一个像素时,您都会看到滚动条出现。

这主要是我正在做的事情:

       verticalLayout = QVBoxLayout(self)
       verticalLayout.setSpacing(0)
       verticalLayout.setContentsMargins(0, 0, 0, 0)
       gridLayout = QGridLayout()
       # Minimum width for the toolbar in the middle:
       gridLayout.setColumnMinimumWidth(1, 30)
       gridLayout.setColumnStretch(0,1)
       gridLayout.setColumnStretch(1,0)
       gridLayout.setColumnStretch(2,1)
       gridLayout.setSpacing(3)
    
       selectedImageViewer = ScrollAreaImageViewer(self)
       gridLayout.addWidget(selectedImageViewer, 0, 0, 3, 1)

       verticalToolBar = QToolBar(self)
       verticalToolBar.setOrientation(Qt.Orientation(Qt.Vertical))

       gridLayout.addWidget(verticalToolBar, 1, 1, 1, 1, Qt.AlignCenter)

       referenceImageViewer = ScrollAreaImageViewer(self)
       gridLayout.addWidget(referenceImageViewer, 0, 2, 3, 1)

       verticalLayout.addLayout(gridLayout)

我在下面的 QVBoxLayout 中添加了另一个小部件,但它与此处无关。 我试过添加 spacers 但它似乎没有任何改变:

gridLayout.addItem(QSpacerItem(5,0, QSizePolicy.Minimum), 1, 3, 1, 1, Qt.Alignment(Qt.AlignCenter))

有没有办法确保两个查看器获得相同的大小,而无需在每个 resizeEvent() 上对它们使用 resize()? 或者这实际上应该被视为 Qt 中的错误?

我尝试了以下解决滚动条闪烁问题的方法:

def resizeEvent(self, event):
    self.gridLayout.setColumnMinimumWidth(0, self.selectedImageViewer.size().width())
    self.gridLayout.setColumnMinimumWidth(2, self.selectedImageViewer.size().width())

但是两次大小仍然相差一个像素。

编辑:这是一个最小的可重现示例

from PyQt5.QtCore import QSize, Qt
from PyQt5.QtWidgets import (QDialog, QLayout, QVBoxLayout,
    QLabel, QSizePolicy, QToolBar, QGridLayout,
    QWidget, QApplication )

class MyWidget(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.label = QLabel(self)

    def resizeEvent(self, event):
        self.label.setText(f"{self.size()}")

class MyDialog(QDialog):
    def __init__(self, parent):
        super().__init__(parent)
        self.setMinimumSize(QSize(500, 100))
        self.verticalLayout = QVBoxLayout(self)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout = QGridLayout()
        self.gridLayout.setColumnMinimumWidth(1, 30)
        self.gridLayout.setColumnStretch(0,1)
        self.gridLayout.setColumnStretch(1,0)
        self.gridLayout.setColumnStretch(2,1)
        self.gridLayout.setSpacing(3)

        self.selectedImageViewer = MyWidget(self)
        self.gridLayout.addWidget(self.selectedImageViewer, 0, 0, 3, 1)
        self.verticalToolBar = QToolBar(self)
        self.verticalToolBar.setOrientation(Qt.Orientation(Qt.Vertical))
        self.gridLayout.addWidget(self.verticalToolBar, 1, 1, 1, 1, Qt.AlignCenter)
        self.referenceImageViewer = MyWidget(self)
        self.gridLayout.addWidget(self.referenceImageViewer, 0, 2, 3, 1)
        self.verticalLayout.addLayout(self.gridLayout)

def main():
    app = QApplication([()])
    window = QWidget()
    dialog = MyDialog(window)
    dialog.show()
    return app.exec()

if __name__ == "__main__":
    main()

我假设问题出在将 ScrollArea 用于 referenceImageViewer,因此在每个调整大小事件中,实际的 referenceImageViewer 都在尝试为其自身添加水平滚动条。

作为解决方案,您可以

  1. 将referenceImageViewer的调整策略设置为(QAbstractScrollArea.AdjustIgnored或QAbstractScrollArea.AdjustToContents)。
  2. 尝试在 gridLayout 中使用 Widgets 而不是 ScrollAreaImageViewer 实例,然后在该 widgets 中添加 ScrollAreaImageViewer。

已编辑。

There must be difference between width of 1st and 3rd widgets as long as ToolBar's width is fixed. E.g when window width is 501 and toolbar width is fixed at 20 auto alignment can't equally divide remaining 481 pixels in a half..

As a solution your toolbar must be resizable too. For reducing ToolBar width changes you can increase 1st and 3rd column stretch in GridLayout for example to value 8, and set 2nd column stretch to 1, so layout will automatically adjust width of each column.