C++ Qt5 C++;根据标签宽度调整字体大小

C++ Qt5 C++;根据标签宽度调整字体大小,c++,layout,qt5,qlabel,C++,Layout,Qt5,Qlabel,我已经写了一个我认为非常好的字体大小调整算法,我在QGridLayout的标签上使用它。该算法本身运行得相当好。但是,在我第一次运行该算法时,布局尚未绘制,因此,拥有标签的网格小部件尚未调整到布局的大小,这意味着我的width()调用是错误的,尽管我在调整大小之前分配了标签文本(这无关紧要)。我的问题是,什么是最好的方式来知道边界矩形已经创建,以适应布局,所以我的计算是在实际的标签宽度 注意,这是Qt5.7,所以我知道fm.width()已经过时,但这是Raspbian中可用的 我的应用程序正在

我已经写了一个我认为非常好的字体大小调整算法,我在QGridLayout的标签上使用它。该算法本身运行得相当好。但是,在我第一次运行该算法时,布局尚未绘制,因此,拥有标签的网格小部件尚未调整到布局的大小,这意味着我的width()调用是错误的,尽管我在调整大小之前分配了标签文本(这无关紧要)。我的问题是,什么是最好的方式来知道边界矩形已经创建,以适应布局,所以我的计算是在实际的标签宽度

注意,这是Qt5.7,所以我知道
fm.width()
已经过时,但这是Raspbian中可用的

我的应用程序正在读取Sonos元数据,并将其显示在7英寸RPi显示屏上。我正在调整元数据的大小,该元数据太长,无法放入布局网格中。因此,我有一个QStackedLayout,其中包含一组要显示的布局(Sonos不播放时的时钟,播放时的元数据),我经常在本地Sonos服务器上查询是否有元数据要显示。这很好。但没有发生的是,在第一次显示Sonos元数据布局时,QGridLayout实际上还没有被显示出来(我相信),因此标签太大。在我第一次填写元数据后的某个时刻,网格布局会“显示”,标签的大小也会正确。问题是,到那时,元数据的设置已经完成,标签在某些情况下看起来很有趣

    int pointSize = FontSize::Default;

    for (auto label : m_labels) {
        QFont f = label->font();

        if (label->accessibleName() == "title")
            pointSize = FontSize::Title;

        QFontMetrics fm(f);
        if (fm.width(label->text()) > label->width()) {
            float factor = (float)label->width() / (float)fm.width(label->text());
            if (factor <= .6) {
                factor = .6;
            }
            f.setPointSizeF((f.pointSize() * factor) * .9);
            qDebug() << __FUNCTION__ << ": label width:" << label->width();
            qDebug() << __FUNCTION__ << ": font width:" << fm.width(label->text());
            qDebug() << __FUNCTION__ << ": Calculated font scaling factor to be" << (float)(factor * .9);
            qDebug() << __FUNCTION__ << ": Reset font size for text\"" << label->text() << "\" to" << f.pointSize();
        }
        else
            f.setPointSize(pointSize);

        label->setFont(f);
    }
下一次调用标签时,标签宽度最终将正确,宽度为584。但是,因为我并不总是第二次调用它,而且因为有时Sonos元数据会打嗝并导致显示恢复,所以它可能总是错误的,或者可能只是第一次正确,不管发生什么

我已经考虑过尝试重载我的gui应用程序paintEvent(QPaintEvent*event),但是QPaintEvent类没有给我一种方法来确定哪个小部件调用了该事件,所以我不能忽略该事件,直到我想要它。至少,我还没有确定,所以如果可能的话,请让我知道

我尝试过重载showEvent,但除非第一次显示框架,否则它不会运行,因此没有帮助

我考虑过将QGridLayout子类化,以简单地重载paintEvent()使用它,但我确实有一个问题。在这个布局中有一个进度条,这意味着当我从Sonos服务器获取元数据时,paintEvent将每半秒触发一次。因为我不知道paintEvent是为时间更新触发的,还是为设置的文本触发的,我得到了很多调整大小的尝试

我还只是在下一个事件中再次运行了该算法,但当字体重新对齐时,这看起来真是一个奇怪的图形问题,我不喜欢它

最后,我可能只是将QLabel子类化,简单地重载paintEvent,并将该自定义标签仅用于可调整大小的标签。然而,布局中的标签是在其位于布局中的容器之前还是之后绘制的?我想我今天会测试这一点,但我不相信它会起作用

不过我觉得这有一个更简单的解决方案。我就是想不出来。我借用了提供调整大小算法的代码。这很好,但是是迭代的。我一直在修补一个基于数学的解决方案来计算,而不是仅仅尝试通过调整大小来调整大小,直到它工作为止。但是,这个解决方案仍然不能很好地工作,无法满足您的需要这并不慢,而且会做暗示的事情。它会调整字体大小,直到适合为止

这一切都是在paintEvent()中完成的,因为使用resizeEvent()可能会导致无限大的调整循环,或者布局可能会自行调整大小,突然所有内容看起来都发生了移动/挤压/拉伸。在paintEvent中执行此操作意味着每次更新文本时只调用一次,而不是其他方式

我可能会回到这里,试图找到一个基于数学的解决方案。我仍然认为这不是正确的方法,但我发现一些方法现在已经足够有效了

新标签

#ifndef NEWLABEL_H
#define NEWLABEL_H

#include <QtCore/QtCore>
#include <QtWidgets/QtWidgets>
#include <cmath>

class NewLabel : public QLabel
{
    Q_OBJECT

public:
    explicit NewLabel(const QString &text, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
    explicit NewLabel(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
    virtual ~NewLabel() {}

    void setDefaultPointSize(int p)
    {
        m_defaultPointSize = static_cast<float>(p);
    }
    
protected:
    void paintEvent(QPaintEvent *e) override;
    
private:
    float getWidgetMaximumFontSize(QWidget *widget, QString text);
    float m_defaultPointSize;
};

#endif
\ifndef NEWLABEL\u H
#定义新标签
#包括
#包括
#包括
类NewLabel:publicqlabel
{
Q_对象
公众:
显式NewLabel(常量QString&text,QWidget*parent=nullptr,Qt::WindowFlags f=Qt::WindowFlags());
显式NewLabel(QWidget*parent=nullptr,Qt::WindowFlags f=Qt::WindowFlags());
虚拟~NewLabel(){}
void setDefaultPointSize(int p)
{
m_defaultPointSize=static_cast(p);
}
受保护的:
无效paintEvent(QPaintEvent*e)覆盖;
私人:
浮点getWidgetMaximumFontSize(QWidget*小部件,QString文本);
浮点m_defaultPointSize;
};
#恩迪夫
NewLabel.cpp

#define FONT_PRECISION (0.5)

NewLabel::NewLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) : QLabel(text, parent, f)
{
    m_defaultPointSize = 12;
}

NewLabel::NewLabel(QWidget *parent, Qt::WindowFlags f) :
    QLabel(parent, f)
{
    m_defaultPointSize = 12;
}

void NewLabel::paintEvent(QPaintEvent *e)
{
    QFont newFont = font();
    float fontSize = getWidgetMaximumFontSize(this, this->text());
    if (fontSize < m_defaultPointSize) {
        newFont.setPointSizeF(fontSize);
        setFont(newFont);
    }
    QLabel::paintEvent(e);
}

float NewLabel::getWidgetMaximumFontSize(QWidget *widget, QString text)
{
    QFont font = widget->font();
    const QRect widgetRect = widget->contentsRect();
    const float widgetWidth = widgetRect.width();
    const float widgetHeight = widgetRect.height();

    QRectF newFontSizeRect;
    float currentSize = font.pointSizeF();

    float step = currentSize/2.0;

    /* If too small, increase step */
    if (step<=FONT_PRECISION){
        step = FONT_PRECISION*4.0;
    }

    float lastTestedSize = currentSize;

    float currentHeight = 0;
    float currentWidth = 0;
    if (text==""){
        return currentSize;
    }

    /* Only stop when step is small enough and new size is smaller than QWidget */
    while(step>FONT_PRECISION || (currentHeight > widgetHeight) || (currentWidth > widgetWidth)){
        /* Keep last tested value */
        lastTestedSize = currentSize;

        /* Test label with its font */
        font.setPointSizeF(currentSize);
        /* Use font metrics to test */
        QFontMetricsF fm(font);

        /* Check if widget is QLabel */
        QLabel *label = qobject_cast<QLabel*>(widget);
        if (label) {
            newFontSizeRect = fm.boundingRect(widgetRect, (label->wordWrap()?Qt::TextWordWrap:0) | label->alignment(), text);
        }
        else{
            newFontSizeRect = fm.boundingRect(widgetRect,  0, text);
        }

        currentHeight = newFontSizeRect.height();
        currentWidth = newFontSizeRect.width();

        /* If new font size is too big, decrease it */
        if ((currentHeight > widgetHeight) || (currentWidth > widgetWidth)){
            //qDebug() << "-- contentsRect()" << label->contentsRect() << "rect"<< label->rect() << " newFontSizeRect" << newFontSizeRect << "Tight" << text << currentSize;
            currentSize -=step;
            /* if step is small enough, keep it constant, so it converge to biggest font size */
            if (step>FONT_PRECISION){
                step/=2.0;
            }
            /* Do not allow negative size */
            if (currentSize<=0){
                break;
            }
        }
        /* If new font size is smaller than maximum possible size, increase it */
        else{
            //qDebug() << "++ contentsRect()" << label->contentsRect() << "rect"<< label->rect() << " newFontSizeRect" << newFontSizeRect << "Tight" << text << currentSize;
            currentSize +=step;
        }
    }
    return lastTestedSize;
}
#定义字体精度(0.5)
NewLabel::NewLabel(常量QString&text,QWidget*parent,Qt::WindowFlags f):QLabel(text,parent,f)
{
m_defaultPointSize=12;
}
NewLabel::NewLabel(QWidget*父项,Qt::WindowFlags f):
QLabel(父,f)
{
m_defaultPointSize=12;
}
void NewLabel::paintEvent(QPaintEvent*e)
{
QFont newFont=font();
float fontSize=getWidgetMaximumFontSize(this,this->text());
如果(fontSizefont();
const QRect widgetRect=widget->contentsRect();
常量浮点widgetWidth=widgetRect.width();
常量浮点widgetHeight=widgetRect.height();
QRectF-newfontsi;
float currentSize=font.pointSizeF();
浮动步长=currentSize/2.0;
/*如果太小,增加步长*/
if(stepFONT|u精度| | |(currentHeight>widgetHeight)| |(currentWidth>widgetWidth)){
/*保持上次测试值*/
lastTestedSize=当前大小;
/*测试标签及其字体*/
font.setPointSizeF(当前大小);
/*美国
#define FONT_PRECISION (0.5)

NewLabel::NewLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) : QLabel(text, parent, f)
{
    m_defaultPointSize = 12;
}

NewLabel::NewLabel(QWidget *parent, Qt::WindowFlags f) :
    QLabel(parent, f)
{
    m_defaultPointSize = 12;
}

void NewLabel::paintEvent(QPaintEvent *e)
{
    QFont newFont = font();
    float fontSize = getWidgetMaximumFontSize(this, this->text());
    if (fontSize < m_defaultPointSize) {
        newFont.setPointSizeF(fontSize);
        setFont(newFont);
    }
    QLabel::paintEvent(e);
}

float NewLabel::getWidgetMaximumFontSize(QWidget *widget, QString text)
{
    QFont font = widget->font();
    const QRect widgetRect = widget->contentsRect();
    const float widgetWidth = widgetRect.width();
    const float widgetHeight = widgetRect.height();

    QRectF newFontSizeRect;
    float currentSize = font.pointSizeF();

    float step = currentSize/2.0;

    /* If too small, increase step */
    if (step<=FONT_PRECISION){
        step = FONT_PRECISION*4.0;
    }

    float lastTestedSize = currentSize;

    float currentHeight = 0;
    float currentWidth = 0;
    if (text==""){
        return currentSize;
    }

    /* Only stop when step is small enough and new size is smaller than QWidget */
    while(step>FONT_PRECISION || (currentHeight > widgetHeight) || (currentWidth > widgetWidth)){
        /* Keep last tested value */
        lastTestedSize = currentSize;

        /* Test label with its font */
        font.setPointSizeF(currentSize);
        /* Use font metrics to test */
        QFontMetricsF fm(font);

        /* Check if widget is QLabel */
        QLabel *label = qobject_cast<QLabel*>(widget);
        if (label) {
            newFontSizeRect = fm.boundingRect(widgetRect, (label->wordWrap()?Qt::TextWordWrap:0) | label->alignment(), text);
        }
        else{
            newFontSizeRect = fm.boundingRect(widgetRect,  0, text);
        }

        currentHeight = newFontSizeRect.height();
        currentWidth = newFontSizeRect.width();

        /* If new font size is too big, decrease it */
        if ((currentHeight > widgetHeight) || (currentWidth > widgetWidth)){
            //qDebug() << "-- contentsRect()" << label->contentsRect() << "rect"<< label->rect() << " newFontSizeRect" << newFontSizeRect << "Tight" << text << currentSize;
            currentSize -=step;
            /* if step is small enough, keep it constant, so it converge to biggest font size */
            if (step>FONT_PRECISION){
                step/=2.0;
            }
            /* Do not allow negative size */
            if (currentSize<=0){
                break;
            }
        }
        /* If new font size is smaller than maximum possible size, increase it */
        else{
            //qDebug() << "++ contentsRect()" << label->contentsRect() << "rect"<< label->rect() << " newFontSizeRect" << newFontSizeRect << "Tight" << text << currentSize;
            currentSize +=step;
        }
    }
    return lastTestedSize;
}