Для Qt 4.6.x, как автоматически задать размер шрифта в указанной ширине?

Внутри моего 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));
 }
}

Ответы

Ответ 1

Йоханнес из qtcentre.org предложил следующее решение:

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

Ответ 2

У вас может быть QGraphicsTextItem в качестве дочернего элемента прямого элемента, измерение ширины текстового элемента и затем масштабирование текстового элемента (setTransform()) для вставки в ширину (и высоту) прямоугольника.

Ответ 3

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);
     }
 }

Ответ 4

Разделите завоевателя:

Вы можете уменьшить количество проходов в методе грубой силы: скажем, ваш предпочтительный (максимальный) размер шрифта равен 40, и у вас минимальный размер шрифта 0

if (40 == false && 0 == true)

  • 20 = true//разделить возможности пополам с каждой догадкой
  • 30 = ложь
  • 25 = истина
  • 27 = истина
  • 28 = ложь
  • так 27 побед

Каким образом это лучше?

это заняло 6 догадок вместо 13, и даже если 20, 12 или 39 были правильным ответом, это всегда принимало бы около 6 догадок. поэтому в большинстве случаев это не так много догадок, это более последовательное, что важно для пользователей.

Я думаю, что количество угадываний, которое требуется при делении ints на половину каждый раз, - это квадратный корень из диапазона, в котором вы смотрите, плюс один. Math.sqroot(40-0) + 1 (Это просто предположение, не стесняйтесь меня исправлять). ваш минимальный размер шрифта, вероятно, не равен 0, поэтому это ускорит поиск ответа.

Иллюстрация:

Это похоже на игру Guess Who, игроки, которые спрашивают "имеет ли ваше имя A", и сокращает возможности пополам, независимо от того, что вы отвечаете, обычно находит ответ быстрее, чем игрок, который спрашивает около 1 символа в каждом повороте "- это ваше имя Сэм" твоё имя Алекс"

Альтернатива: начиная с хорошей догадки, затем тестирование на точность Я также хотел бы продвигать работу в некоторой логике, чтобы использовать результат, предоставленный Дареном, с помощью fontMetrics в качестве хорошего исходного предположения, а затем проверить его, если он подходит для теста +2, если он не подходит для теста -2; если новый тест подходит для теста 1, вы пропустили, и вы узнаете свой ответ, если не попытаетесь переместить еще 2 и т.д., но в идеале ответ fontMetrics не превышает 4...

Я подозреваю, что это приведет к самым быстрым средним результатам фактических случаев использования.

Предполагая, что вы хотите использовать int и считаете, что неточности шрифта неточно минимальны, это, вероятно, займет всего 2 или 3 догадки.

Ответ 5

Вот мой код, подходящий (в начале) текст, работает довольно хорошо (ошибка < 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);
}

Ответ 6

К сожалению, нет. Там нет простого решения. Наиболее очевидным улучшением, с точки зрения производительности будет вычисление и кеширование необходимого размера шрифта при изменении текста, а не пересчет его в каждом paintEvent. Еще одним улучшением было бы попытаться оценить правильный размер, основанный на пропорциях ограничивающего прямоугольника. При составлении и тестировании оценок вам нужно скорректировать размер намного быстрее, чем просто уменьшить размер точки на каждую итерацию.

Ответ 7

Это зависит от диапазона, по которому вы ожидаете изменения размеров шрифта. Если диапазон большой, увеличение на единицу может занять много времени. Если это просто вопрос нескольких размеров, скорость, вероятно, не будет проблемой.

Если диапазон большой, другой подход заключается в добавлении большего интервала вместо "1". Если вы превысите желаемый размер за один шаг, уменьшите интервал, скажем, на половину. Вы будете возвращаться туда и обратно по оптимальному размеру каждый раз меньшими и меньшими размерами; когда разница между двумя последовательными интервалами мала, вы можете выйти.

Это похоже на подход, используемый при поиске корня. Это может быть чрезмерным, так как размер шрифтов, которые будут приемлемы для отображения в данном приложении, скорее всего, будет довольно узким, и подход грубой силы, который вы уже используете, не будет потреблять много времени.