2
votes

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.

1

1 Answers

3
votes

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"