如何使多行 QLabel 省略最后一行,受最大高度限制,但如果文本未填充到最大高度也会缩小?

How to make a multiline QLabel elide the last line, be constrained by a max height, but also shrink if text doesn't fill to max height?

编辑:更新了当前和所需行为的工作示例和示例屏幕截图。

描述

我正在尝试让多行 QLabel 具有这些行为:

  1. 它应该展开以填充可用宽度。
  2. 如果它的文本比它能显示的要长,它应该显示它能显示的所有内容(扩展到它的最大高度)并省略最后一行。
  3. 如果其文本不是它可以显示的最长文本,它应该显示所有文本,但会缩小其高度以适应文本。

标签在布局中。也许我在想这个问题,比如也许它应该是用其他东西包裹的文本区域而不是标签?

无论如何,我找到了涵盖其中一两个行为的解决方案,但不是所有三个行为。我试过调整 Qt 的 ElidedLabel,但我不太清楚如何调整它来实现这三个目标。

我考虑过覆盖 sizeHint(),但我对 Qt 的了解还不够,不知道这样做是否正确。我觉得 ElidedLabel 的布局和大小策略的 layoutSizeConstraint 的正确组合可能使这成为可能,但我不确定。

我稍微修改了 Qt 的 ElidedLabel 示例代码,这样它就不会在其构造函数中获取其内容。

示例代码

https://gist.github.com/jahabrewer/bd0c79ac2255953eeadf87c9767ce693

截图

当前行为

左栏的文本足够短,ElidedLabel 应降低其高度并将垂直 space 割让给其下方的 QLabel。右栏的文本足够长以至于被省略,即 correct/desired.

期望的行为

(明确地说,我想要一个单一的配置,当文本很短时会产生类似左列的行为,当文本很长时会产生像右列一样的行为)

您必须将标签的高度限制为绘制文本所需的最小高度。您可以使用 QFontMetrics class 和方法 QFontMetrics::boundingRect:

获得此高度
QFontMetrics const fontMetrics(font());
QRect const r = fontMetrics.boundingRect(
            QRect(QPoint(0, 0), size()),
            Qt::TextWordWrap | Qt::ElideRight,
            content
            );
qDebug() << "Needed Height:" << r.height();

首先,定义方法QWidget::sizeHint来强制你的标签有一个尺寸:

virtual QSize sizeHint() const override
{
    QFontMetrics const fontMetrics(font());
    QRect const r = fontMetrics.boundingRect(
            QRect(QPoint(0, 0), size()),
            Qt::TextWordWrap | Qt::ElideRight,
            content
            );
    return QSize(width(), r.height());
}

我们只是想缩小高度。这就是为什么我们不会使用 font metrics

返回的宽度

现在,我们将使用调整大小事件来检查是否可以缩小高度:

所以,我们可以覆盖 QWidget::resizeEvent 方法:

virtual void resizeEvent(QResizeEvent* event) override
{
    QFrame::resizeEvent(event); // Process the event. The label is now resized
    QSize const size = sizeHint();
    if (size.height() < height()) // Shrink the height if needed
        resize(QSize(width(), size.height()));
}

如果文本在运行时必须改变,我们必须重新调整大小:

void setText(const QString &newText)
{
   content = newText;
   update();
   adjustSize(); // Will resize the label
}

我用于测试的代码:

QWidget* w = new QWidget();
QVBoxLayout* l = new QVBoxLayout(w);

QString const lorem("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
ElidedLabel* label = new ElidedLabel(lorem);
label->setFrameShape(QFrame::Box); // To see its bounds

l->addWidget(label);
l->addWidget(new QLabel("Text Label"));

QTimer::singleShot(3000, [=]() { label->setText(lorem.left(100)); });

w->show();