clouvis
clouvis

Reputation: 575

How to make the text fill all the QLabel's space?

I am working on a PyQt5 project, but would be happy to read C++/Qt answer as well, because C++ solutions may work on Python too.

I have a MainWindow with a horizontal layout, and a QLabel in it.

My QLabel's size Policy is "Expanding", therefore all my window is filled by the QLabel.

However, the text displayed by my QLabel does not change its size. I would like the text to grow when the window grows, and be as big as possible, in the limit of the QLabel size.

I have heard of QWidget::adjustSize() but could not figure out how to use it. The option scaledContents for my QLabel on QtDesigner does not do anything, so I guess it is only useful for when using pixmap.

For the moment, my solution is to reimplement the resizeEvent() method of my window and change the font size of my label with setFont(). But I think that there must be an easier solution. Moreover, my resizeEvent() method is not very good because I make a linear relation between myWindowWidth* myWindowHeight and myTextFontSize, therefore when only myWindowWidth increase, myTextFontSize increases and forces myWindowHeight to increase, which is bad.

Upvotes: 3

Views: 4066

Answers (2)

pavlidvg
pavlidvg

Reputation: 21

I had a similar problem, only I needed the title of a QButton to dynamically change font size whenever the text got too big to fit my Button. The working parts of the implementation only take into account the button width, so making it work with a label (or any other widget if you can access its size and text properties) will only require 2 line changes.

`calculate_font_size(font: QFont, button,minimum_font_size = 9):
font_size = QtGui.QFontMetrics(font)
target_width = button.maximumWidth() - 2  # add some padding
k = font_size.width(button.text())

if k < target_width:  # if this size of text already fits, don't change it
    return font.pointSize()
else:
    ratio = k / target_width  # calculate how much bigger the text is compared to the button. E.g. if ratio = 1.2, it's 20% bigger
    size_correct = font.pointSize() / ratio
    if size_correct < minimum_font_size:
        size_correct = minimum_font_size  # make it readble and word wrap it instead of making it extra small
    return (size_correct)`

change the button argument to a Label, and change the button.maximumWidth() and button.text() properties on lines 2 and 3, to the width of the label and the text you want to fit in the label.

This function by design, only scales the fonts DOWN. This is just what I needed in my project. A hack for this if you also want to scale up the text, is to always run the function with an unreasonably high starting font size (e.g. 3000), and it always give you the correct size to fit.

Upvotes: 2

This is a quick sketch that might point to a solution. Instead of deriving from a label, an event filter can be installed on any label to make its text fill the available space. The filter uses the existing scaledContents property, and extends its applicability to text content.

The label's font size is adjusted using Newton's algorithm to fill the available space. Some adjustments would be needed to make it work with labels that enable word wrap; the font size should then never overshoot what fits.

// https://github.com/KubaO/stackoverflown/tree/master/questions/label-text-size-36575192
#include <QtWidgets>

class LabelStretcher : public QObject {
   Q_OBJECT
public:
   LabelStretcher(QObject * parent = 0) : QObject(parent) {
      apply(qobject_cast<QLabel*>(parent));
   }
   void apply(QLabel * label) { if (label) label->installEventFilter(this); }
protected:
   bool eventFilter(QObject * obj, QEvent * ev) Q_DECL_OVERRIDE {
      if (ev->type() != QEvent::Resize) return false;
      auto label = qobject_cast<QLabel*>(obj);
      if (!label || label->text().isEmpty() || !label->hasScaledContents()) return false;
      qDebug() << "pre: " << label->minimumSizeHint() << label->sizeHint() << label->size();

      static auto dSize = [](const QSize & inner, const QSize & outer) -> int {
         auto dy = inner.height() - outer.height();
         auto dx = inner.width() - outer.width();
         return std::max(dx, dy);
      };
      static auto f = [](qreal fontSize, QLabel * label) -> qreal {
         auto font = label->font();
         font.setPointSizeF(fontSize);
         label->setFont(font);
         auto d = dSize(label->sizeHint(), label->size());
         qDebug() << "f:" << fontSize << "d" << d;
         return d;
      };
      static auto df = [](qreal fontSize, QLabel * label) -> qreal {
         if (fontSize < 1.0) fontSize = 1.0;
         return f(fontSize + 0.5, label) - f(fontSize - 0.5, label);
      };

      // Newton's method
      auto font = label->font();
      auto fontSize = font.pointSizeF();
      int i;
      for (i = 0; i < 5; ++i) {
         auto d = df(fontSize, label);
         qDebug() << "d:" << d;
         if (d < 0.1) break;
         fontSize -= f(fontSize, label)/d;
      }
      font.setPointSizeF(fontSize);
      label->setFont(font);
      qDebug() << "post:" << i << label->minimumSizeHint() << label->sizeHint() << label->size();
      return false;
   }
};

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   QLabel label{"Hello There!"};
   label.setScaledContents(true);
   label.show();
   LabelStretcher stretch(&label);
   return app.exec();
}

#include "main.moc"

Upvotes: 4

Related Questions