heightForWidth标签

时间:2013-01-09 15:02:23

标签: qt pyqt

4 - 5年我需要一个具有以下属性的小部件

  • 显示包含的文字HTML
  • 文字应该包裹在几行
  • 当窗口小部件放入布局时,应调整窗口小部件的高度,使文本完全适合窗口小部件几何体

应该在布局中使用此子窗口小部件,以提供有关布局中其他GUI元素如何工作但仅占用最小空间以显示其内容的一些详细信息。

我认为这很容易 - 但每当我回到挑战时,我总是放弃。

主要问题是当实现heightForWidth()并且使用带有setHeightForWidth(True)的QSizePolicy时,布局会中断。它可以缩小到无限小。显然这是Qt bug。

另一种方法是在发生resizeEvent()时调用updateGeometry()并使用宽度相关的高度调用setFixedHeight(h)。但这也会产生一些奇怪的布局行为。

如果有人对如何处理这个问题有任何好的建议,请告诉我。

下面我添加了一个重现布局大小调整行为的代码段。

致以最诚挚的问候,

的Mads

import sys
from PyQt4 import QtCore, QtGui

class Square(QtGui.QLabel):
    def __init__(self, parent=None):
        QtGui.QLabel.__init__(self, parent)
        self.setAutoFillBackground(True)

        palette = QtGui.QPalette()
        palette.setColor(QtGui.QPalette.Window, QtGui.QColor('red'))
        self.setPalette(palette)

        policy = self.sizePolicy()
        policy.setHeightForWidth(True)
        self.setSizePolicy(policy)

    def sizeHint(self):
        return QtCore.QSize(128, 128)

    def heightForWidth(self, width):
        return width

class Widget(QtGui.QWidget):
    def __init__(self, parent=None):
        # Call base class constructor
        QtGui.QWidget.__init__(self, parent)

        # Add a layout
        layout = QtGui.QVBoxLayout()
        self.setLayout(layout)

        # Add Square
        label = Square()
        layout.addWidget(label)

        spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
        layout.addItem(spacerItem)

        # Some dummy button
        self._push_button = QtGui.QPushButton('Press me')
        layout.addWidget(self._push_button)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)

    widget = Widget()
    widget.show()

    sys.exit(app.exec_())

2 个答案:

答案 0 :(得分:2)

我发现这非常有问题。我认为问题的核心如下:

  • Qt布局通常分配给我们可以调用父窗口小部件的窗口小部件(parentwidget->setLayout(layout))。
  • 布局询问他们的子窗口小部件(或子布局)的大小首选项(最小大小,大小提示[首选大小]和最大大小)。这是通过QLayoutItem派生类(例如QWidgetItem)以稍微复杂的方式完成的。
  • 宽度高(HFW)小部件 - 具有hasHeightForWidth() == true并提供int heightForWidth(int width)的小部件 - 描述其对布局的约束的能力有限,因为它们可以提供{{1} },minimumSizeHint()sizeHint()。如有必要,他们还可以调用heightForWidth(width)setMinimumSize()等函数。但大多数Qt布局,例如setMaximumSize()QVBoxLayoutQHBoxLayout,在提供自己的最小值/首选/最大值时,请不要特别关注QGridLayout结果其父级的大小,因为他们通过heightForWidth(width)QLayout::minimumSize()QLayout::sizeHint()这样做 - 所有这些都不会被调用有关布局目标大小的信息(在Catch中)这样的情况),所以他们不能轻易地为他们的孩子提供QLayout::maximumSize()价值。
  • 因此,布局会询问其孩子在没有多少HFW思想的情况下他们想要多大,因此设置自己的最小/首选/最大大小(可能与其他约束一起确定父小部件的大小)。
  • 在布局告诉其父级(及其父级等)需要多少空间之后,Qt计算出它认为一切应该有多大。布局通过其width调用。现在布局知道它有多大。它通过setGeometry(const QRect& layout_rect)为其子女分配空间。
  • 仅在此时让布局发现了它的最终宽度。所以到目前为止,它无法为其孩子提供最终宽度,因此HFW小部件无法知道他们的最终宽度,直到他们最终布局。到目前为止,布局及其父级可能已经设置为错误的高度(可能太大;可能太小)。
  • 小部件/布局互动的精彩描述位于http://kdemonkey.blogspot.co.uk/2013/11/understanding-qwidget-layout-flow.html;除此之外,您最好还是查看Qt源代码本身。

因此,您可以看到两类解决方案,正如您在上面所概述的那样,大小需要"正确"受限于所需。

第一个:

  • HFW小部件,如child->setGeometry() - 使用自动换行的派生类,或想要修正其宽高比的图像,可以为其最小和最大尺寸提供合理的值,并且是合理的QLabel(是他们的大小喜欢。)
  • 然后,当他们布置时,他们(1)拦截sizeHint()以找出他们的新宽度(例如来自QWidget::resizeEvent(QResizeEvent* event)); (2)通过他们自己的event->size()函数计算他们的首选高度; (3)例如,heightForWidth()后跟setFixedHeight(height)
  • 强迫他们的身高
  • 这往往合理地起作用,除了,任何想要将其大小与这样的HFW小部件相匹配的父小部件必须做同样的事情,例如拦截updateGeometry(),如果父窗口小部件的布局为resizeEvent,则执行类似hasHeightForWidth() == true的操作。
  • 这导致了faff,因为你必须在长长的父母身份中修改可能任意的小部件。
  • 它也可能导致相当多的重绘,这可能会很慢并导致视觉闪烁。

第二个:

  • 重写布局。
  • 我发现的最好的方法是让布局计算他们孩子的几何形状,假设某种标准矩形(Qt本身通常在绘制布局时以640x480开始);根据该草案提供身高信息;然后,当调用setGeometry()时,如果生成的高度(基于可能的新宽度)与之前公布的布局不匹配,则通过调用setFixedHeight(layout->heightForWidth(width())); updateGeometry();重新约束。
  • 这允许您使用任意小部件,而HFW小部件只需要支持parent->setFixedHeight()hasHeightForWidth()以使新布局(及其父窗口小部件以及使用此机制的任何祖先布局)进行调整高度。
  • 它可以导致一些重绘,但通常不会太多,因为它发生在每个布局而不是每个小部件的基础上。

我已将C ++代码放在http://egret.psychol.cam.ac.uk/code/2017_01_16_qt_height_for_width/以获取以下布局:

以及以下小部件:

  • FlowLayout - 图像保持宽高比;
  • AspectRatioPixmapLabel - 自动换行标签,在封装之前尝试使用尽可能多的水平空间。
  • LabelWordWrapWide - 正如其名称所暗示的那样,是一个垂直滚动区域,但是一个可以干净地支持高度宽度的区域。

...加上一些基础设施代码(VerticalScrollArea等)应该使布局恢复到他们的Qt等效行为,以及一些支持文件(包括#define和{{ 1}}使得布局和基本小部件的选择在这方面取决于您的偏好。)

我未能成功解决的一个遗留问题是,我认为gui_defines.h layouts.h似乎返回了稍微错误的值(略微过高估计其空间要求)某些情况。我怀疑这个问题出现在QLabel中,但我通过计算一些样式表边框来解决这个问题并不完美;它仍然不太正确,但过高估计(导致空白)优于低估(导致剪裁)。

答案 1 :(得分:0)

除固定策略外,大小提示不会决定子窗口小部件的大小。如果类似自动调整大小的策略将由父母过剩。