Label heightForWidth

4-5 years old I need a widget with the following properties

  • Show text on HTML
  • The text should be wrapped in several lines
  • When the widget is placed in the layout, the height of the widget must be adjusted so that the text exactly matches the geometry of the widget

This sub-widget should be used in the layout to provide some details on how the other GUI elements in the layout work, but only consumes minimal space to display its contents.

I thought it was easy, but every time I get back to the challenge, I always end up refusing.

The main problem is that the layout breaks when implementing heightForWidth () and uses QSizePolicy with setHeightForWidth (True). It can shrink to infinitesimal. This seems to be a Qt bug.

Another approach is to call updateGeometry () when resizeEvent () occurs and calls setFixedHeight (h), using a height dependent width. But it also leads to some weird layout behavior.

If anyone has any good suggestions on how to approach this, please let me know.

Below I include a snippet that reproduces the behavior of the resize layout.

Yours faithfully,

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_()) 
+4
source share
2 answers

I found this extremely problematic. I think the essence of the problem is this:

  • Qt layouts are usually assigned to a widget, which we can call the parent widget (with parentwidget->setLayout(layout) ).
  • Layouts specify child widgets (or child layouts) for their size preferences (minimum size, size hint [preferred size], and maximum size). This is accomplished in a somewhat complicated way using the QLayoutItem -derived class (e.g. QWidgetItem ).
  • Height widgets for width (HFW) - those that have hasHeightForWidth() == true and provide int heightForWidth(int width) - have limited ability to describe their layout restrictions, since they can offer minimumSizeHint() , a sizeHint() , a heightForWidth(width) . They can, if necessary, also call functions like setMinimumSize() and setMaximumSize() . But most Qt layouts, such as QVBoxLayout , QHBoxLayout and QGridLayout , do not pay much attention to the results of heightForWidth(width) , offering their own minimum / preferred / maximum size for their parent, because they do this through QLayout::minimumSize() , QLayout::sizeHint() and QLayout::maximumSize() - none of them are called with information about the size of the target layout (in a situation like Catch-22), so they cannot easily provide a width value for their children.
  • Thus, the layout sets its children how much they want to be without a special HFW, and thus sets its own minimum / preferred / maximum size (potentially determining, together with other restrictions, the size of the parent widget).
  • After the layout told the parents (and its parents, etc.) how much space he needs, Qt shows how big he believes that everything should be. The layout is called through its setGeometry(const QRect& layout_rect) . Now the layout knows how big it is. It assigns space to its children with child->setGeometry() .
  • But only at that moment did the layout reveal its final width. Thus, up to this point, he cannot provide the final width to his children, and therefore HFW widgets cannot know their final width until they are laid out completely. By this time, the layout and its parent can already be set to the wrong height (may be too large, may be too small). A.
  • An excellent description of the widget / layout interaction - http://kdemonkey.blogspot.co.uk/2013/11/understanding-qwidget-layout-flow.html ; besides this, you are best off looking at the Qt source code.

So, you see two categories of solutions, as you described above, where the size should be β€œproperly” tied to the required one.

First:

  • HFW widgets, such as QLabel -extensive classes using word wrap, or images that want to correct their aspect ratio, can provide reasonable values ​​for their minimum and maximum size and a reasonable sizeHint() (being size, how to be).
  • Then, when they are laid out, they (1) intercept QWidget::resizeEvent(QResizeEvent* event) to find out their new width (for example, from event->size() ); (2) calculate their preferred height through its own function heightForWidth() ; and (3) the strength of their height, for example, setFixedHeight(height) , followed by updateGeometry() .
  • This works reasonably, except that any parent widget that wants to match its size with such an HFW widget should do the same, for example. intercepting resizeEvent , and if the parent widget has a layout with hasHeightForWidth() == true , do something like setFixedHeight(layout->heightForWidth(width())); updateGeometry(); setFixedHeight(layout->heightForWidth(width())); updateGeometry(); .
  • This leads to the fact that you have to change potentially arbitrary widgets in a long line of parental control.
  • It can also lead to quite a few redraws, which can be slow and cause visual flicker.

The second:

  • Rewrite the layouts.
  • The best approach I found is for the mock-ups to compute their child geometry, assuming some kind of standard rectangle (Qt itself often starts with 640x480 when designing the layout); providing height information based on this project; then when setGeometry () is called if the resulting height (based on a potentially new width) does not match what was previously advertised, re-restricting it to parent->setFixedHeight() .
  • This allows you to use custom widgets, and HFW widgets must support hasHeightForWidth() and heightForWidth() in order to have new layouts (and their parent widgets and any ancestor layouts using this mechanism) to adjust their height.
  • This can lead to some redrawing, but often not too much, as it happens on the basis of each layout and not on the basis of the widget.

I added C ++ code to http://egret.psychol.cam.ac.uk/code/2017_01_16_qt_height_for_width/ for the following layouts:

and the following widgets:

  • AspectRatioPixmapLabel - an image that supports its aspect ratio;
  • LabelWordWrapWide is a shortcut for a word cover that tries to use so much horizontal space in front of a word.
  • VerticalScrollArea - as its name implies - is a vertical scroll area, but which maintains height for width.

... plus some infrastructure code ( #define , etc.), which should make the layouts return to their equivalent Qt behavior and some support files (including gui_defines.h and layouts.h ) that make the layout and base widgets dependent from your preferences in this regard).

One residual problem that I have not successfully dealt with is that I think QLabel heightForWidth() seems to return some incorrect values ​​(slightly overestimating their space requirements) with styles in some circumstances. I suspect that the problem is in QLabelPrivate::sizeForWidth , but I just did not work on it, calculating some borders of the stylesheets; this is still not entirely correct, but revaluation (leading to a gap) is better than underestimation (leading to clipping).

+2
source

The size of the tooltip will not determine the size of the child widget, except for a fixed policy. If policies depending on the type of autoresistor are redundant parents.

0
source

All Articles