6

I need a QLabel whose width should not adapt to the contained text but which is resizeable by the user (or the layout to be exact). If the text is too long for the width of the QLabel it should simply be clipped.

This question is somehow the reverse of How to make QLabel expand width geometry to accommodate text. However, the content of that question didn't help me. Neither did Setting text on a QLabel in a layout, doesn't resize.

Background

The QLabel will display identifiers (single words) coming from another system. Sometimes those identifiers change many times a second, which makes the whole layout flickering. The QLabel is part of a vertical dock so the width of the dock flickers.

On the other hand, it should be up to the user to decide how much of the identifiers s/he could see. So I want to allow the user to change the width of the dock such that the width of the QLabel adapts to that.

Solution attempts

To achieve this I set the horizontal size policy to QSizePolicy::Preferred and derived my own label class from QLabel in which I've overridden sizeHint() to return a fixed size. But that didn't change the behavior.

I know I could apply QFontMetrics to compute the width of the text and then cut it off to fit into the QLabel width. But that seems not to be the right solution, particularly as I would like to have the last letter itself clipped if it does not fit in entirely to give the user the clue that the identifier is too long to be displayed.

Versions

  • Qt 5.5.1
  • GCC 5.4.0
  • All from current Ubuntu 16.04 repository
p-a-o-l-o
  • 9,807
  • 2
  • 22
  • 35
bjhend
  • 1,538
  • 11
  • 25
  • Did you try to call `QLabel::setScaledContents(true)`? Does it have an effect on the text in the label? – vahancho Feb 06 '18 at 11:30
  • @vahancho: Thanks for the suggestion. I wasn't aware of that and just tried it out. Unfortunately, it didn't help. – bjhend Feb 06 '18 at 11:38
  • Well, as an alternative I would suggest to put your label in a scroll area. – vahancho Feb 06 '18 at 11:48
  • @vahancho: Thanks, I started playing with a scroll area around my ``QLabel`` and it looks promising. Maybe you add this suggestion as an answer. – bjhend Feb 06 '18 at 13:39

4 Answers4

4

Scaling the text is not a good idea, as scaled text will be hardly visible in case of long strings and small labels. As an alternative I would put my label in a scroll area so it can hold label of any size without resizing itself (and prevent my GUI from flickering). Here is a simple example how to to it:

QLabel *label = new QLabel;
label->setAlignment(Qt::AlignTop);

QScrollArea *scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(label);

label->setText("ThisIsVeryLargeStringThatIWantToPutIntoALabel");
scrollArea->show();

This scroll area can lay into a dockable window.

vahancho
  • 20,808
  • 3
  • 47
  • 55
  • Putting my ``QLabel`` into a ``QScrollArea`` and setting its properties accordingly (mainly margins and turning off scroll bars) achieved my goal. – bjhend Feb 06 '18 at 15:04
3

I think i found a rather dirty solution to your problem which may cause more issues, but you could try it. It simply prevents setText from resizing the label, while it still allows the user and layout to resize.

void CustomLabel::setText(const QString text)
{
    max = maximumSize();
    min = minimumSize();
    setMinimumSize(size());
    setMaximumSize(size());
    settingText = true;

    QLabel::setText(text);
}


void CustomLabel::resizeEvent(QResizeEvent *event)
{
    QLabel::resizeEvent(event);
    if(settingText){
        setMinimumSize(min);
        setMaximumSize(max);
        settingText = false;
    }
}
Ole
  • 31
  • 3
  • Interesting idea. It really makes the text clipped as I like to have it. However, it also prevents the widget from becoming resized at all. Moving the call of ``QLabel::resizeEvent(event)`` after the ``if`` block didn't help neither, nor setting min and max size in ``CustomLabel::resizeEvent`` to ``event->size()``. – bjhend Feb 06 '18 at 14:12
2

Make your own label class, extending QWidget, this way:

#include <QWidget>

class DisplayWidget : public QWidget
{
    Q_OBJECT
    QString _text;
public:
    explicit DisplayWidget(QString text, QWidget *parent = nullptr);

    QString text() const;
    void setText(QString text);

protected:
    void paintEvent(QPaintEvent *event);
};

The implementation is quite simple:

#include "displaywidget.h"

#include <QPainter>
#include <QFontMetrics>

bool DisplayWidget::ellipsis() const { return _ellipsis; }
void DisplayWidget::setEllipsis(bool ellipsis) { _ellipsis = ellipsis; }

DisplayWidget::DisplayWidget(QString text, QWidget *parent) : QWidget(parent), _text(text), _ellipsis(false) {}

QString DisplayWidget::text() const { return _text; }

void DisplayWidget::setText(QString text)
{
    _text = text;
    update();
}

void DisplayWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);

    QFontMetrics metrics(painter.font());

    int maxwidth = rect().width();
    QString text = _text;
    int length = _text.size();
    while(length > 0 && metrics.width(text, length) > maxwidth)
    {
        --length;
    }
    if(length < _text.size())
    {
        text = text.left(length);
        if(_ellipsis)
        {
            const QString ellipsis = " ...";
            maxwidth -= metrics.width(ellipsis);
            while(length > 0 && metrics.width(text, length) > maxwidth)
            {
                --length;
            }
            if(length > 0)
            {
                text = text.left(length);
            }
            else
            {
                text = "";
            }
            text.append(ellipsis);
        }
    }
    painter.drawText(rect(), Qt::AlignLeft, text);
}

As you can see, most of the code is in the paintEvent overridden method. The important thing, here, is having font metrics at hand, to decide on the fly how much of the text has to be shown (I added ellipsis at the end of partially shown text) without changing the text property itself. Just add instances of this class to a vertical layout inside the dock. I think it could work.

p-a-o-l-o
  • 9,807
  • 2
  • 22
  • 35
  • Thanks for the code. This is the solution I had in mind in _Solution attempts_ above with applying ``QFontMetrics``. However, I would like to have a solution where the text is clipped of at exactly the last pixel which fits into the current width. Besides, I'm afraid it would cost some performance to shorten the text character by character, because text updates sometimes come every 10 millisecs for several ``QLabel``s, but that is open to tests. – bjhend Feb 06 '18 at 14:21
  • I edited the code for better performance and set ellipsis on or off. – p-a-o-l-o Feb 06 '18 at 14:58
0

I had exactly the same problem and found an elegant solution: Set the widget's minimumSize().

To understand why this works you have to know that a QLabel always reports the full width of its text as minimum size. The QWidget documentation says:

QLayout will never resize a widget to a size smaller than the minimum size hint unless minimumSize() is set or the size policy is set to QSizePolicy::Ignore. If minimumSize() is set, the minimum size hint will be ignored.

If you set the minimum size to a reasonable value, e.g. 40px, layouts will ignore the hint and shrink the QLabel as needed.

Example:

QLabel *label = new QLabel;
label->setMinimumSize(40, 0);
label->setText("VeryVeryLongTextThatShouldNeverFitInTheSmallWindow");
chrset
  • 581
  • 6
  • 11