18

在我的 QGraphicsRectItem::paint() 内部,我试图在其 rect() 中绘制项目的名称。但是,对于每个不同的项目,它们的宽度可以是可变的,并且名称也可以是可变的长度。

目前我从最大字体大小开始,检查它是否适合并减少它,直到找到适合的字体大小。到目前为止,我还没有找到一种快速简便的方法来做到这一点。有没有更好或更有效的方法来做到这一点?

谢谢!

void checkFontSize(QPainter *painter, const QString& name) {
 // check the font size - need a better algorithm... this could take awhile
 while (painter->fontMetrics().width(name) > rect().width()) {
  int newsize = painter->font().pointSize() - 1;
  painter->setFont(QFont(painter->font().family(), newsize));
 }
}
4

8 回答 8

14

来自 qtcentre.org 的 Johannes 提供了以下解决方案:

float factor = rect().width() / painter->fontMetrics().width(name);
 if ((factor < 1) || (factor > 1.25))
 {
  QFont f = painter->font();
  f.setPointSizeF(f.pointSizeF()*factor);
  painter->setFont(f);
 }

我在我的程序中试了一下,到目前为止,它似乎工作得很好。我喜欢它,因为它一次性产生结果,但它假设字体宽度像它的高度一样缩放。

http://www.qtcentre.org/threads/27839-For-Qt-4-6-x-how-to-auto-size-text-to-fit-in-a-specified-width

于 2010-02-05T01:29:39.733 回答
3

您可以将 QGraphicsTextItem 作为 rect 项的子项,测量文本项的宽度,然后缩放文本项 (setTransform()) 以适应 rect 项的宽度(和高度)。

于 2010-03-03T09:15:35.743 回答
2

这是我的代码,适合(高度)一个文本,效果很好(我猜错误 < 2%)

void scalePainterFontSizeToFit(QPainter &painter, QFont &r_font, float _heightToFitIn)
{
    float oldFontSize, newFontSize, oldHeight;

    // Init
    oldFontSize=r_font.pointSizeF();

    // Loop
    for (int i=0 ; i<3 ; i++)
    {
        oldHeight = painter.fontMetrics().boundingRect('D').height();
        newFontSize = (_heightToFitIn / oldHeight) * oldFontSize;
        r_font.setPointSizeF(newFontSize);
        painter.setFont(r_font);
        oldFontSize = newFontSize;
        //qDebug() << "OldFontSize=" << oldFontSize << "HtoFitIn=" << _heightToFitIn << "  fontHeight=" << oldHeight << "  newFontSize=" << newFontSize;
    }

    // End
    r_font.setPointSizeF(newFontSize);
    painter.setFont(r_font);
}
于 2016-07-25T07:42:57.627 回答
1
void myClass::adaptFontSize(QPainter * painter, int flags, QRectF drawRect, QString text){
     int flags = Qt::TextDontClip|Qt::TextWordWrap; //more flags if needed
     QRect fontBoundRect = 
           painter->fontMetrics().boundingRect(drawRect.toRect(),flags, text);
     float xFactor = drawRect.width() / fontBoundRect.width();
     float yFactor = drawRect.height() / fontBoundRect.height();
     float factor = xFactor < yFactor ? xFactor : yFactor;
     QFont f = painter->font();
     f.setPointSizeF(f.pointSizeF()*factor);
     painter->setFont(f);

 }

或更准确但贪婪

 void myClass::adaptFontSize(QPainter * painter, int flags, QRectF rect, QString text, QFont font){
     QRect fontBoundRect;
     fontBoundRect = painter->fontMetrics().boundingRect(rect.toRect(),flags, text);
     while(rect.width() < fontBoundRect.width() ||  rect.height() < fontBoundRect.height()){
         font.setPointSizeF(font.pointSizeF()*0.95);
         painter->setFont(font);
         fontBoundRect = painter->fontMetrics().boundingRect(rect.toRect(),flags, text);
     }
 }
于 2011-07-15T16:35:56.603 回答
1

分治:

您可以减少蛮力方法中的通过次数:假设您的首选(最大)字体大小为 40,最小字体大小为 0

如果(40 == 假 && 0 == 真)

  • 20=true //每次猜测将可能性分成两半
  • 30=假
  • 25=真
  • 27=真
  • 28=假
  • 所以27胜

这在哪些方面更好?

这需要 6 次猜测而不是 13 次,即使 20、12 或 39 是正确答案,也总是需要大约 6 次猜测。所以不仅在大多数情况下猜测更少,而且更加一致,这对用户体验很重要。

我认为每次将整数除以一半时所需的猜测次数是您正在查看的范围的平方根加一。Math.sqroot(40-0) + 1 (这只是一个猜测,请随时纠正我。)您的最小字体大小可能不是 0,因此增加这将加快搜索答案的速度。

插图:

就像玩猜猜猜,问“你的名字有 A”的玩家,无论你回答什么,都将可能性减半,通常比每回合问 1 个角色“你的名字 Sam”的玩家更快地找到答案。”是你的名字亚历克斯”

替代方案:从一个好的猜测开始,然后测试准确性t 拟合检验 -2;如果新测试适合测试您跳过的 1 并且您会知道答案,如果不尝试移动另一个 2 等等,但理想情况下,fontMetrics 答案不会超过 4 远...

我怀疑这将产生实际用例的最快平均结果。

假设您想要一个 int 并假设字体度量的不准确性很小,这可能只需要 2 或 3 次猜测。

于 2015-09-23T19:47:09.540 回答
1

基于答案的跟随功能和对答案的评论允许优化字体大小以适应指定矩形中的文本:

QFont optimizeFontSizeToFitTextInRect(QPainter * painter, QRectF drawRect, QString text, int flags = Qt::TextDontClip|Qt::TextWordWrap, double goalError =  0.01, int maxIterationNumber=10){
    painter->save();

    QRect fontBoundRect;
    QFont font;
    double minError = std::numeric_limits<double>::max();
    double error = std::numeric_limits<double>::max();
    int iterationNumber=0;
    while((error > goalError) && (iterationNumber<maxIterationNumber)){
        iterationNumber++;
        fontBoundRect = painter->fontMetrics().boundingRect(drawRect.toRect(),flags, text);
        double xFactor = drawRect.width() / fontBoundRect.width();
        double yFactor = drawRect.height() / fontBoundRect.height();
        double factor;
        if (xFactor<1 && yFactor<1) {
            factor = std::min(xFactor,yFactor);
        } else if (xFactor>1 && yFactor>1) {
            factor = std::max(xFactor,yFactor);
        } else if (xFactor<1 && yFactor>1) {
            factor = xFactor;
        } else {
            factor = yFactor;
        }
        error = abs(factor-1);
        if (factor > 1 ) {
            if (error < minError) {
                minError = error;
            } else {
                break;
            }
        }
        font = painter->font();
        font.setPointSizeF(font.pointSizeF()*factor);
        painter->setFont(font);
    }
    painter->restore();

    return font;
}
于 2019-09-12T07:54:07.480 回答
0

很不幸的是,不行。对此没有简单的解决方案。最明显的性能改进是在文本更改时计算并缓存所需的字体大小,而不是在每个paintEvent 中重新计算它。另一个改进是尝试根据边界矩形的比例来估计正确的大小。制作和测试估计应该让您更快地纠正大小,而不是简单地在每次迭代中将点大小减一。

于 2010-02-04T22:47:19.140 回答
0

这取决于您希望字体大小变化的范围。如果范围很大,递增 1 可能需要很长时间。如果只是几个点大小的问题,速度可能不会成为问题。

如果范围很大,另一种方法是添加更大的间隔,而不是“1”。如果您在一个步骤中超过了所需的大小,请将间隔减少一半。每次你都会以越来越小的数量在最佳尺寸上来回弹跳;当两个连续间隔之间的差异很小时,您可以退出。

这类似于根查找中使用的方法。这可能是矫枉过正,因为在给定应用程序中可以接受显示的字体大小可能相当窄,并且您已经使用的蛮力方法不会消耗太多时间。

于 2010-04-12T15:20:53.687 回答