метка heightForWidth

4-5 лет Мне нужен виджет со следующими свойствами

  • Показать текст вкл. HTML
  • Текст должен быть обернут несколькими строками
  • Когда виджет помещается в макет, высота виджета должна быть отрегулирована таким образом, чтобы текст точно соответствовал геометрии виджета

Этот субвиджет должен использоваться в макете, чтобы предоставить некоторые детали о том, как работают другие элементы GUI в макете, но только потребляет минимальное пространство для отображения его содержимого.

Я думал, что это было легко, но каждый раз, когда я возвращаюсь к вызову, я всегда заканчиваю, отказываясь.

Основная проблема заключается в том, что макет разбивается при реализации heightForWidth() и используется QSizePolicy с setHeightForWidth (True). Он может сжиматься до бесконечно малой. По-видимому, это ошибка Qt.

Другой подход - вызвать updateGeometry (), когда возникает resizeEvent() и вызывает setFixedHeight (h), используя высоту, зависящую от ширины. Но это также порождает какое-то странное поведение макета.

Если у кого-нибудь есть какие-то хорошие предложения о том, как подойти к этому, пожалуйста, дайте мне знать.

Ниже я включаю фрагмент, который воспроизводит поведение изменения размера макета.

С наилучшими пожеланиями,

Мадс

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
добавлено отредактировано
Просмотры: 2
nl ja de
Клябельная словечка печально печатается. Со всем потенциальным межтекстовым html-дизайном, шрифтами и перерывами, по-видимому, очень сложно решить
добавлено автор cppguy, источник
Да, я подозревал что-то в этом роде. Слишком плохо, функция кажется чем-то вроде общего интереса.
добавлено автор repoman, источник

2 ответы

Я нашел это чрезвычайно проблематичным. Я думаю, что суть проблемы заключается в следующем:

  • Qt layouts are typically assigned to a widget that we can call the parent widget (with parentwidget->setLayout(layout)).
  • Layouts ask their child widgets (or child layouts) for their size preferences (minimum size, size hint [preferred size], and maximum size). This is done in a slightly complex way via a QLayoutItem-derived class (e.g. QWidgetItem).
  • Height-for-width (HFW) widgets -- those having hasHeightForWidth() == true and providing int heightForWidth(int width) -- have limited ability to describe their constraints to layouts, in that they can offer a minimumSizeHint(), a sizeHint(), and a heightForWidth(width). They can, if necessary, also call functions like setMinimumSize() and setMaximumSize(). But most Qt layouts, like QVBoxLayout, QHBoxLayout, and QGridLayout, don't pay particular attention to the heightForWidth(width) results when offering their own min/preferred/max size to their parent, because they do so via QLayout::minimumSize(), QLayout::sizeHint(), and QLayout::maximumSize() -- none of which are called with information about the layout's target size (in a Catch-22-like situation), so they can't easily provide the width value to their children.
  • So, the layout asks its children how big they want to be without much HFW thought, and thus sets its own minimum/preferred/maximum size (determining, potentially in conjunction with other constraints, the size of the parent widget).
  • After the layout has told its parent (and its parent, etc.) how much space it needs, Qt works out how big it thinks everything should be. The layout is called via 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 this point has the layout discovered its final width. So up to this point, it cannot offer a final width to its children, and thus HFW widgets can't know their final width until they're being laid out finally. By this time, the layout and its parent may already have been set to the wrong height (can be too big; can be too small).
  • An excellent description of the widget/layout interaction is at http://kdemonkey.blogspot.co.uk/2013/11/understanding-qwidget-layout-flow.html; beyond this, you're best off looking at the Qt source code itself.

Таким образом, вы видите две категории решений, как вы описали выше, где размер должен быть «должным образом» привязан к требуемому.

Первый:

  • HFW widgets, like QLabel-derived classes using word wrap, or images wanting to fix their aspect ratio, can provide sensible values for their minimum and maximum size, and a sensible sizeHint() (being the size they'd like to be).
  • Then, when they're laid out, they (1) intercept QWidget::resizeEvent(QResizeEvent* event) to find out their new width (e.g. from event->size()); (2) calculate their preferred height via their own heightForWidth() function; and (3) force their height via, for example, setFixedHeight(height) followed by updateGeometry().
  • This tends to work reasonably, except that any parent widget that wants to match its size to such an HFW widget has to do the same thing, e.g. intercepting resizeEvent, and if the parent widget has a layout with hasHeightForWidth() == true, doing something like setFixedHeight(layout->heightForWidth(width())); updateGeometry();.
  • That leads to faff as you have to modify potentially arbitrary widgets in a long line of parenthood.
  • It can also lead to quite a lot of redrawing, which can be slow and cause visual flickering.

Второй:

  • Rewrite the layouts.
  • The best approach I've found is to have layouts calculate their children's geometry assuming some sort of standard rectangle (Qt itself often starts with a 640x480 one when drafting a layout); offering height information based on this draft; then, when setGeometry() is called, if the resulting height (based on a potentially new width) doesn't match what the layout previously advertised, re-constraining by calling parent->setFixedHeight().
  • This allows you to use arbitrary widgets, and HFW widgets only need to support hasHeightForWidth() and heightForWidth() to have the new layouts (and their parent widgets, and any ancestor layouts using this mechanism) adjust their height.
  • It can lead to some redrawing, but often not too much, as it happens on a per-layout not per-widget basis.

Я поместил код C ++ в http://egret.psychol.cam.ac. uk/code/2017_01_16_qt_height_for_width/ для следующих макетов:

и следующие виджеты:

  • AspectRatioPixmapLabel -- image maintaining its aspect ratio;
  • LabelWordWrapWide -- word-wrapping label that tries to use as much horizontal space before it word-wraps.
  • VerticalScrollArea -- as its name suggests, a vertical scroll area, but one that supports height-for-width cleanly.

... плюс некоторый код инфраструктуры ( #define и т. д.), который должен заставить макеты вернуться к поведению их эквивалента Qt и некоторым файлам поддержки (включая gui_defines.h и layouts.h , которые делают выбор макета и базового виджета условным в соответствии с вашими предпочтениями в этом отношении).

Одна из оставшихся проблем, с которой я не успешно справился, заключается в том, что я думаю, что QLabel heightForWidth() , похоже, возвращает несколько неправильных значений (слегка переоценивает их требования к пространству) со списками стилей в некоторые обстоятельства. Я подозреваю, что проблема заключается в QLabelPrivate :: sizeForWidth , но я только что не работал над ней, вычисляя некоторые границы стилей; это все еще не совсем правильно, но переоценка (приводящая к пробелу) лучше, чем недооценка (приводящая к отсечению).

2
добавлено

Указание размера не будет определять размер дочернего виджета, кроме фиксированной политики. Если политики, зависящие от авторезистора, будут излишни от родителей.

0
добавлено