动态更改QLabel的字体大小以适应可用空间 [英] Dynamically change font size of QLabel to fit available space

查看:123
本文介绍了动态更改QLabel的字体大小以适应可用空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用3个 QLabel 比例尺进行水平布局,以使用其所有可用空间.更具体地说,这就是我所拥有的

I'm trying to make an horizontal layout with 3 QLabel scale use all its available space. More specifically, this is what I have

这是我的目标

目前,通过使用滑块更改qlabels的样式表来获得第二张图像.另外,由于我在组框内的布局中具有三个标签,因此组框的大小可以调整以适合其内容,很酷.

At the moment, the second image is achieved by changing the stylesheet of the qlabels with a slider. Additionally, since I have the the three labels in a layout inside a groupbox, the groupbox resizes to fit its contents, cool.

现在,我想放下滑块方法,而是在移动拆分器时自动调整可用空间.在问题中,OP重新实现了 resizeEvent ,而且我还看到其他帖子对此有所建议,但有所变化与此 while(!doesFit)或类似的内容来逐个指出.

Now I wanted to drop the slider approach and instead autofit the space available when moving the splitters. In this question, OP reimplements the resizeEvent, and I've seen other posts suggesting the same, changing point by point with this while( !doesFit ) or something similar.

在大小调整事件和splitterMoved事件上,我都尝试使用此方法.但是,这种方法很容易导致反馈循环和其他显示错误.在另一个问题中,他们建议启用ignoreSizePolicy以防止大小策略重新触发sizeevent,但是我喜欢qt如何处理布局的大小,如何保持最小大小,然后在用户坚持的情况下折叠小部件.如果 HLayout 忽略由 QLabels 触发的调整大小事件,也许仍然有效,恕我直言,这是不干净的.

I tried using this approach, both on the resize event and on the splitterMoved event. However, this approach is way prone to feedback loops and other display errors caused. In the other question, they suggest enabling ignoreSizePolicy to prevent the size policy retriggering the sizeevent, but I like how qt handles the size of the layout, how it keeps a minimum size and then it folds the widget if the user insists. Maybe it would work if the HLayout would ignore the resize events triggered by the QLabels, still IMHO unclean thought.

我想知道这是否是实现此目标的推荐方法,并且是否存在不太不稳定的解决方案,可能使用样式表.我还可以删除某些行为,即最小大小限制(以便用户可能隐藏组框).

I was wondering if that's the recommended way of achieving this, and wether a less unstable solution exists, maybe using using stylesheets. There are some behaviours that I could also drop, the minimum size limit (so the user could potentially hide the groupbox).

如果这是推荐的方法,那么如果我有三个单独的标签,其中一个(数字)可以动态,快速地更改其文本,那么应该如何使用字体度量?它不应该对性能产生影响,并且 while 循环会让我警惕.

If that's the recommended way of doing it, how should I use the fontmetrics if I have three separate labels, one of which (the number) changes its text dynamically and rapidly? It should not have an impact on performance, and that while loop makes me wary.

这听起来不像 while(!fit)方法要削减它.还是呢?

It doesn't sound like the while(!fit) approach is going to cut it. Or does it?

-编辑有关重复的问题

另一则帖子创建了一个事件过滤器,如果重新处理带有3个标签的布局,该过滤器也可能会起作用.我最终使用了第一个提到的帖子的版本,并加上了评论中提到的帖子的变体.如果问题再次出现,我将发布答案.

Another post creates an event filter, which might also work if reworked to deal with a layout with 3 labels. I finally used a version of the first mentioned post with the variation of the post mentioned in the comments. I'll post the answer if the question is reopened.

推荐答案

一个人可以从此答案中应用牛顿方法.可以处理给定布局中的所有小部件.它将可用于具有可设置字体的任何窗口小部件,而不仅适用于 QLabel .

One could apply the Newton's method approach from this answer to work on all widgets in a given layout. It will work on any widget with a settable font, not only on a QLabel.

给定一个良好的起点时,例如,牛顿算法可以相当快地收敛.交互式调整大小时.循环只执行一次并非非常规.另一方面, QWidget :: sizeHint 是整数值,并且小部件可以舍入小数字体大小,因此有时迭代速度比预期的要慢.迭代次数有上限,以确保良好的性能.

The Newton's algorithm converges reasonably quickly when given a good starting point, e.g. when resizing interactively. It's not atypical to have the loop execute only once. On the other hand, QWidget::sizeHint is integer-valued and and widgets may round fractional font sizes, thus sometimes the iteration is a bit slower than one would expect. The number of iterations is capped to ensure decent performance.

标签的自定义替代品提供了 QSizeF sizeHintF(),在此效果更好.

A custom replacement for the label, that provided a QSizeF sizeHintF(), would work better here.

窗口小部件的最小大小有点麻烦,因为随着窗口小部件内容的更改,大小不会更新.不过,可以很容易地对此进行补救.

The minimum sizing for the widgets is a bit of a stretch, as the size is not updated as the widget contents change. This could be remedied easily, though.

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

class LabelStretcher : public QObject {
   Q_OBJECT
   static constexpr const char kMinimumsAcquired[] = "ls_minimumsAcquired";
   static constexpr const char kStretcherManaged[] = "ls_stretcherManaged";
public:
   LabelStretcher(QObject *parent = 0) : QObject(parent) {
      apply(qobject_cast<QWidget*>(parent));
   }
   void apply(QWidget *widget) {
      if (!widget) return;
      setManaged(widget);
      setMinimumSize(widget);
      widget->installEventFilter(this);
   }
   void setManaged(QWidget *w, bool managed = true) {
      w->setProperty(kStretcherManaged, managed);
   }
protected:
   bool eventFilter(QObject * obj, QEvent * ev) override {
      auto widget = qobject_cast<QWidget*>(obj);
      if (widget && ev->type() == QEvent::Resize)
         resized(widget);
      return false;
   }
private:
   void onLayout(QLayout *layout, const std::function<void(QWidget*)> &onWidget) {
      if (!layout) return;
      auto N = layout->count();
      for (int i = 0; i < N; ++i) {
         auto item = layout->itemAt(i);
         onWidget(item->widget());
         onLayout(item->layout(), onWidget);
      }
   }
   void setFont(QLayout *layout, const QFont &font) {
      onLayout(layout, [&](QWidget *widget){ setFont(widget, font); });
   }
   void setFont(QWidget *widget, const QFont &font) {
      if (!widget || !widget->property(kStretcherManaged).toBool()) return;
      widget->setFont(font);
      setFont(widget->layout(), font);
   }
   void setMinimumSize(QWidget *widget) {
      if (widget->layout()) return;
      widget->setMinimumSize(widget->minimumSizeHint());
   }
   static int dSize(const QSizeF & inner, const QSizeF & outer) {
      auto dy = inner.height() - outer.height();
      auto dx = inner.width() - outer.width();
      return std::max(dx, dy);
   }
   qreal f(qreal fontSize, QWidget *widget) {
      auto font = widget->font();
      font.setPointSizeF(fontSize);
      setFont(widget, font);
      auto d = dSize(widget->sizeHint(), widget->size());
      qDebug() << "f:" << fontSize << "d" << d;
      return d;
   }
   qreal df(qreal fontSize, qreal dStep, QWidget *widget) {
      fontSize = std::max(dStep + 1.0, fontSize);
      return (f(fontSize + dStep, widget) - f(fontSize - dStep, widget)) / dStep;
   }
   void resized(QWidget *widget) {
      qDebug() << "pre: " << widget->minimumSizeHint() << widget->sizeHint() << widget->size();
      if (!widget->property(kMinimumsAcquired).toBool()) {
         onLayout(widget->layout(), [=](QWidget *widget){ setMinimumSize(widget); });
         widget->setProperty(kMinimumsAcquired, true);
      }

       // Newton's method
      auto font = widget->font();
      auto fontSize = font.pointSizeF();
      qreal dStep = 1.0;
      int i;
      for (i = 0; i < 10; ++i) {
         auto prevFontSize = fontSize;
         auto d = df(fontSize, dStep, widget);
         if (d == 0) {
            dStep *= 2.0;
            continue;
         }
         fontSize -= f(fontSize, widget)/d;
         fontSize = std::max(dStep + 1.0, fontSize);
         auto change = fabs(prevFontSize - fontSize)/fontSize;
         qDebug() << "d:" << d << " delta" << change;
         if (change < 0.01) break; // we're within 1% of target
      }
      font.setPointSizeF(fontSize);
      setFont(widget, font);
      qDebug() << "post:" << i << widget->minimumSizeHint() << widget->sizeHint() << widget->size();
   }
};
constexpr const char LabelStretcher::kMinimumsAcquired[];
constexpr const char LabelStretcher::kStretcherManaged[];

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   QWidget w;
   QGridLayout layout{&w};
   LabelStretcher stretch{&w};
   QLabel labels[6];
   QString texts[6] = {"V", "30.0", "kts", "H", "400.0", "ft"};
   int i = 0, j = 0, k = 0;
   for (auto & label : labels) {
      stretch.setManaged(&label);
      label.setFrameStyle(QFrame::Box);
      label.setText(texts[k++]);
      if (j == 0) label.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
      else if (j == 1) label.setAlignment(Qt::AlignCenter);
      layout.addWidget(&label, i, j++);
      if (j >= 3) { i++; j=0; }
   }
   w.show();
   return app.exec();
}
#include "main.moc"

这篇关于动态更改QLabel的字体大小以适应可用空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆