2010-02-04 17 views
14

Wewnątrz mojego QGraphicsRectItem :: paint(), próbuję narysować nazwę przedmiotu w jego rect(). Jednak dla każdego z różnych elementów mogą one mieć różną szerokość i podobnie nazwy mogą mieć różną długość.W Qt 4.6.x, w jaki sposób automatycznie dopasować rozmiar tekstu do określonej szerokości?

Obecnie zaczynam od maksymalnej wielkości czcionki, sprawdzając, czy pasuje i zmniejszając ją, dopóki nie znajdę rozmiaru czcionki, który pasuje. Do tej pory nie byłem w stanie znaleźć szybkiego i łatwego sposobu na zrobienie tego. Czy istnieje lepszy lub bardziej wydajny sposób na zrobienie tego?

Dzięki!

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

Zobacz też [to pytanie] (http://stackoverflow.com/q/36575192/1329652). –

Odpowiedz

14

Johannes od qtcentre.org zaproponował następujące rozwiązanie:

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

dałem jej spróbować w moim programie i jak dotąd, wydaje się całkiem dobrze. Podoba mi się, ponieważ daje wyniki w jednym przebiegu, ale zakłada, że ​​szerokość czcionki jest taka sama jak jej wysokość.

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

+1

Dlaczego 1.25? 10 znaków – marmistrz

+1

Próbowałem tego i okazało się niedokładne, przynajmniej dla niektórych czcionek. Metoda OP jest dokładna, podczas gdy jest brutalną siłą. – Isaac

+0

Brak podziału na liczbę całkowitą, rzut typu (pływający). W przeciwnym razie współczynnik będzie wynosił 0, jeśli jest mniejszy niż 1. – ragnarius

0

Niestety, nie. Nie ma na to łatwego rozwiązania. Najbardziej oczywistym usprawnieniem pod względem wydajności byłoby obliczenie i buforowanie wymaganego rozmiaru czcionki, gdy tekst zostanie zmieniony zamiast przeliczania go w każdym paintEvent. Kolejną poprawą będzie próba oszacowania prawidłowego rozmiaru na podstawie proporcji prostującego obwiedni. Sporządzanie i testowanie oszacowań powinno przyspieszyć skorygowanie rozmiaru, zamiast po prostu zmniejszać rozmiar czcionki o jedną po każdej iteracji.

2

Można mieć QGraphicsTextItem jako dziecko elementu rect, zmierzyć szerokość adnotację, a następnie przeskalować adnotację (setTransform()), aby pasowały do ​​szerokości elementu rect (oraz wysokość).

0

To zależy od zakresu, w jakim można się różnić rozmiarami czcionki. Jeśli zakres jest duży, zwiększanie o jeden może zająć dużo czasu. Jeśli to tylko kwestia kilku rozmiarów, prędkość prawdopodobnie nie będzie problemem.

Jeśli zakres jest duży, innym rozwiązaniem byłoby dodanie dłuższego interwału zamiast "1". Jeśli przekroczysz pożądany rozmiar podczas jednego kroku, zmniejsz przedział o, powiedzmy, połowę. Będziesz podskakiwał do przodu i do tyłu w optymalnym rozmiarze za każdym razem o mniejsze i mniejsze kwoty; kiedy różnica między dwoma kolejnymi interwałami jest mała, możesz wyjść.

Jest to podobne do podejścia stosowanego przy odnajdywaniu rootów. Może to być przesada, ponieważ rozmiar czcionek, które można zaakceptować do wyświetlenia w danej aplikacji, prawdopodobnie będzie dość wąski, a metoda brute force, z której już korzystasz, nie pochłonie zbyt wiele czasu.

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

} 

lub bardziej dokładne, ale chciwi

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

dzielą przejęcie:

Można zredukować liczbę przejazdów w swojej brutalnej metody życie: powiedzmy preferowany (maksymalna) rozmiar czcionki to 40, a rozmiar czcionki wynosi co najmniej 0

jeśli (40 == false & & 0 == prawda)

  • 20 = prawdziwych możliwości // dzielenie na pół każdego odgadnąć
  • 30 = fałszywe
  • 25 = prawda
  • 27 = prawda
  • 28 = fałszywe
  • więc 27 zwycięstw

W jaki sposób jest to lepiej?

zajęło to 6 domysłów zamiast 13, a nawet jeśli 20, 12 lub 39 były właściwą odpowiedzią, to zawsze zajmie około 6 zgadowań. więc nie tylko jest to mniej domysłów przez większość czasu, ale jest bardziej spójne, co jest ważne dla doświadczenia użytkownika.

Sądzę, że liczba domysłów, jakie można uzyskać, dzieląc liczbę całkowitą o połowę za każdym razem, to pierwiastek kwadratowy z zakresu, którego szukasz w plusie. Math.sqroot (40-0) + 1 (To tylko przypuszczenie, możesz mnie poprawić.) Twój minimalny rozmiar czcionki prawdopodobnie nie wynosi 0, więc zwiększenie tego przyspieszyłoby wyszukiwanie odpowiedzi.

Ilustracja:

To jak gra Guess Who, gracze, którzy pytają „robi swoje nazwisko mieć A” i odcina możliwości w połowie bez względu na to, co odpowiedzieć zwykle znajdzie odpowiedź szybciej niż gracz, który prosi o 1 znak każdy zakręt „to nazwa Sam” „to nazwa Alex”

Alternatywa: zaczynając dobrym przypuszczeniem, a następnie testowanie dokładności Chciałbym również promować pracę w jakiejś logiki używać rezultat dostarczony przez Odpowiedź Darena przy użyciu fontMetrics jako dobry początek, a następnie te st it, jeśli pasuje do testu +2, jeśli nie pasuje do testu -2; jeśli nowy test pasuje do testu pominiętego 1 i będziesz znał swoją odpowiedź, jeśli nie, spróbuj przesunąć kolejne 2 itd., ale idealnie odpowiedź fontMetrics nie jest większa niż 4 daleko ...

Podejrzewam to wytworzy najszybsze średnie wyniki rzeczywistych przypadków użycia.

zakładając, że chcesz int i zakładając, że niedokładności w metryce są minimalne, prawdopodobnie zajmie to tylko 2 lub 3 wartości domyślne.

+0

Metoda [Newtona] (https://en.wikipedia.org/wiki/Newton%27s_method) jest podobnym podejściem i nie jest sztucznie ograniczona, jak szybko może skupiać. Binarny podział i zdobywanie określa tylko jeden bit wyniku na iterację. Newton zazwyczaj będzie się zbiegał znacznie szybciej niż przy prostych problemach, takich jak rozmiar czcionki, zakładając, że zależność między rozmiarem czcionki a szerokością rośnie monotonicznie, tak jak w przypadku czcionek o rozmiarze swobodnym. –

1

Oto mój kod dopasowanie (w heigth) tekstu, działa całkiem dobrze (błąd < 2% Chyba)

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