2010-02-04 10 views
14

All'interno del mio QGraphicsRectItem :: paint(), sto cercando di disegnare il nome dell'elemento all'interno del suo rect(). Tuttavia, per ciascuno dei diversi articoli, possono essere di larghezza variabile e nomi simili possono essere di lunghezza variabile.Per Qt 4.6.x, come dimensionare automaticamente il testo per adattarlo a una larghezza specificata?

Attualmente sto iniziando con una dimensione massima del carattere, controllando se si adatta e decrescendo fino a trovare una dimensione del carattere che si adatta. Finora, non sono stato in grado di trovare un modo facile e veloce per farlo. C'è un modo migliore o più efficace per farlo?

Grazie!

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

Vedi anche [questa domanda] (http://stackoverflow.com/q/36575192/1.329.652). –

risposta

14

Johannes dal qtcentre.org offerto la seguente soluzione:

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

ho dato una prova nel mio programma e finora, sembra funzionare abbastanza bene. Mi piace perché produce risultati in un unico passaggio, ma presuppone che la larghezza del font sia uguale alla sua altezza.

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

+1

Perché 1,25? 10 caratteri – marmistrz

+1

Ho provato questo e trovato inaccurato, almeno per alcuni tipi di carattere. Il metodo dell'OP è preciso, mentre è una forza bruta. – Isaac

+0

Divisione intera, manca un cast di tipo (float). Altrimenti il ​​fattore sarà 0 se è inferiore a 1. – ragnarius

0

Sfortunatamente, no. Non c'è una soluzione facile a questo. Il miglioramento più ovvio, in termini di prestazioni, sarebbe calcolare e memorizzare nella cache la dimensione del carattere necessaria quando il testo viene modificato, invece di ricalcolarlo in ogni paintEvent. Un altro miglioramento sarebbe provare a stimare la dimensione corretta in base alle proporzioni del rettangolo di delimitazione. Fare e testare le stime dovrebbe portarti a correggere le dimensioni molto più rapidamente del semplice decremento della dimensione del punto di una per ogni iterazione.

2

Si potrebbe avere un QGraphicsTextItem come un bambino dell'elemento rect, misurare la larghezza della nota di testo e quindi scalare l'elemento di testo (setTransform()) per adattarsi larghezza dell'elemento rect (e altezza).

0

Dipende dall'intervallo in cui ci si aspetta che le dimensioni dei caratteri siano variabili. Se l'intervallo è ampio, l'incremento di uno può richiedere molto tempo. Se si tratta solo di diverse dimensioni dei punti, la velocità probabilmente non sarà un problema.

Se l'intervallo è elevato, un altro approccio sarebbe quello di aggiungere un intervallo più ampio, anziché "1". Se si supera la dimensione desiderata durante un passaggio, diminuire l'intervallo di, ad esempio, metà. Dovrai rimbalzare avanti e indietro per le dimensioni ottimali di volta in volta di quantità sempre più piccole; quando la differenza tra due intervalli successivi è piccola, puoi uscire.

Questo è simile all'approccio utilizzato nel rilevamento delle radici. Potrebbe essere eccessivo, dal momento che la dimensione dei caratteri che sarà accettabile da visualizzare in una determinata applicazione è probabilmente abbastanza ristretta e l'approccio della forza bruta che stai già utilizzando non consumerà molto tempo.

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

} 

o più accurata, ma avidi

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

Divide una conquista:

si potrebbe ridurre il numero di passaggi nel metodo forza bruta: diciamo vostro preferito (massimo) la dimensione del font è 40 e hai una dimensione minima del carattere di 0

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

  • 20 = vere possibilità // divide a metà con ogni indovinare
  • 30 = falso
  • 25 = true
  • 27 = true
  • 28 = false
  • così 27 vittorie

In che modo questo è meglio?

ci sono voluti 6 tentativi invece di 13, e anche se 20, 12 o 39 erano la risposta giusta, ci vorrebbero sempre circa 6 ipotesi. quindi non è solo il numero di tentativi che si verifica per la maggior parte del tempo, è più coerente e importante per l'esperienza dell'utente.

Penso che il numero di tentativi necessari per dividere la metà ogni volta sia la radice quadrata dell'intervallo che si sta cercando più uno. Math.sqroot (40-0) + 1 (Questa è solo una supposizione, sentiti libero di correggermi.) la tua dimensione minima del font probabilmente non è 0, quindi aumentare questo accelererebbe la ricerca di una risposta.

Illustrazione:

È come giocare Guess Who, i giocatori che chiedono "fa il tuo nome avere un A" e taglia le possibilità in mezza non importa quale si risponde in genere trova la risposta più veloce rispetto al giocatore che chiede circa 1 carattere ogni turno "è il nome di Sam" "è il nome di Alex"

alternativa: a partire da una buona congettura, poi prova per la precisione vorrei anche promuovere lavorare in una logica di utilizzare il risultato fornito da La risposta di Daren usando fontMetrics come buona ipotesi di partenza e poi te st it, se si adatta test +2 se non si adatta test -2; se il nuovo test si adatta prova il 1 che hai saltato e saprai la tua risposta, se non provi a spostare un altro 2 e così via, ma idealmente la risposta di fontMetrics non è più di 4 lontana ...

Sospetto che questo produrrà i risultati medi più veloci dei casi d'uso reali.

partendo dal presupposto che si desidera un valore int e presupponendo che le imprecisioni dei caratteri siano minime, ciò probabilmente richiederà solo 2 o 3 ipotesi.

+0

Il [metodo di Newton] (https://en.wikipedia.org/wiki/Newton%27s_method) è un approccio simile, e non è artificialmente limitato nella velocità con cui può convergere. Un binario divide et impera determina solo un bit di risultato per iterazione. Generalmente, i problemi di Newton convergono molto più rapidamente rispetto a problemi semplici come il dimensionamento dei caratteri, presupponendo che la relazione tra la dimensione del carattere e la larghezza aumenti monotonicamente, come per i caratteri a dimensione di float. –

1

Ecco il mio codice la misura (in altezza) un testo, funziona abbastanza bene (errore < 2% immagino)

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