qt – heightForWidth标签

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

>显示文字incl. 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_())

最佳答案 我发现这非常有问题.我认为问题的核心如下:

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

因此,您可以看到两类解决方案,如上所述,其中大小需要“正确”约束到所需的大小.

首先:

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

第二:

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

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

> BoxLayoutHfw,VBoxLayoutHfw,HBoxLayoutHfw – QBoxLayout等的替代品
> GridLayoutHfw – QGridLayout的替换.
> FlowLayoutHfw – 替换Qt的FlowLayout(http://doc.qt.io/qt-5/qtwidgets-layouts-flowlayout-example.html).

以及小部件:

> AspectRatioPixmapLabel – 保持纵横比的图像;
> LabelWordWrapWide – 在自动换行之前尝试使用尽可能多的水平空间的自动换行标签.
> VerticalScrollArea – 顾名思义,是一个垂直滚动区域,但是一个可以干净地支持高度宽度的区域.

…加上一些基础设施代码(#defines等),它们应该使布局恢复到它们的Qt等效行为,以及一些支持文件(包括gui_defines.h和layouts.h,它们可以选择布局和基本小部件作为你的条件在这方面的偏好).

我没有成功解决的一个遗留问题是,我认为QLabel的heightForWidth()似乎在某些情况下使用样式表返回稍微错误的值(略微过高估计其空间要求).我怀疑问题出现在QLabelPrivate :: sizeForWidth中,但我只是通过计算一些样式表边框来解决它;它仍然不太正确,但过高估计(导致空白)优于低估(导致剪裁).

点赞