2010-02-04 12 views
14

Dentro de mi QGraphicsRectItem :: paint(), estoy tratando de dibujar el nombre del elemento dentro de su rect(). Sin embargo, para cada uno de los diferentes elementos, pueden ser de ancho variable y, de forma similar, los nombres pueden ser de longitud variable.Para Qt 4.6.x, ¿cómo auto-tamaño de texto para caber en un ancho especificado?

Actualmente estoy empezando con un tamaño de fuente máximo, comprobando si cabe y disminuyéndolo hasta que encuentre un tamaño de fuente que se ajuste. Hasta ahora, no he podido encontrar una manera rápida y fácil de hacer esto. ¿Hay una forma mejor o más eficiente de hacer esto?

Gracias!

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

Véase también [esta cuestión] (http://stackoverflow.com/q/36575192/1329652). –

Respuesta

14

Johannes de qtcentre.org ofrecieron la siguiente solución:

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

que decidimos probarlo en mi programa y hasta el momento, parece que funciona bastante bien. Me gusta porque produce resultados en una sola pasada, pero asume que el ancho de la fuente se escala como su altura.

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

+1

¿Por qué 1.25? 10 caracteres – marmistrz

+1

Intenté esto y lo encontré inexacto, al menos para algunas fuentes. El método de OP es preciso, mientras que es una fuerza bruta. – Isaac

+0

División de enteros, falta un molde de tipo (flotante). De lo contrario, factor será 0 si es menor que 1. – ragnarius

0

Lamentablemente, no. No hay una solución fácil para esto. La mejora más obvia, basada en el rendimiento, sería calcular y almacenar en caché el tamaño de fuente necesario cuando se cambia el texto en lugar de volver a calcularlo en cada evento de pintura. Otra mejora sería tratar de estimar el tamaño correcto basado en las proporciones del rect límite. Hacer y probar estimaciones debería hacer que corrijas el tamaño mucho más rápido que simplemente disminuir el tamaño del punto en uno por cada iteración.

2

Usted podría tener un QGraphicsTextItem como hijo del elemento rect, medir el ancho del elemento de texto y luego escalar el elemento de texto (setTransform()) para encajar en el ancho del elemento rect (y altura).

0

Esto depende del rango sobre el que espera que varíen los tamaños de fuente. Si el rango es grande, incrementarlo en uno puede llevar mucho tiempo. Si solo se trata de varios tamaños de puntos, la velocidad probablemente no sea un problema.

Si el rango es grande, otro enfoque sería agregar un intervalo más grande, en lugar de '1'. Si supera el tamaño deseado durante un paso, disminuya el intervalo, digamos, a la mitad. Rebotarás hacia adelante y hacia atrás en el tamaño óptimo en cantidades cada vez más pequeñas; cuando la diferencia entre dos intervalos sucesivos es pequeña, puede abandonarla.

Esto es similar al enfoque utilizado en el descubrimiento de raíz. Puede ser excesivo, ya que es probable que el tamaño de las fuentes que será aceptable mostrar en una aplicación determinada sea bastante limitado, y el enfoque de fuerza bruta que ya está utilizando no consumirá demasiado tiempo.

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 más exacto, pero codicioso

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:

usted podría reducir el número de pasadas en su método de fuerza bruta: permite decir que su preferido (máximo) el tamaño de fuente es 40 y usted tiene un tamaño de letra mínimo de 0

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

  • 20 = verdaderas posibilidades // dividir por la mitad con cada adivinar
  • 30 = false
  • 25 = true
  • 27 = true
  • 28 = false
  • así 27 victorias

¿De qué manera es esto mejor?

esto tomó 6 conjeturas en lugar de 13, e incluso si 20, 12 o 39 fueran la respuesta correcta, siempre se necesitarían aproximadamente 6 conjeturas. por lo tanto, la mayoría de las veces no solo son menos conjeturas, sino que es más consistente lo que es importante para la experiencia del usuario.

Creo que el número de conjeturas que se necesitan al dividir las entradas por la mitad cada vez es la raíz cuadrada del rango que está buscando más uno. Math.sqroot (40-0) + 1 (Es solo una suposición, no dude en corregirme). su tamaño de fuente mínimo probablemente no sea 0, por lo que aumentar esto aceleraría la búsqueda de una respuesta.

Ilustración:

Es como jugar Guess Who, los jugadores que preguntan "¿su nombre tiene una A" y reduce las posibilidades de un medio no importa lo que su respuesta es por lo general encuentra la respuesta más rápido que el jugador que pide aproximadamente 1 carácter cada vez "es el nombre de Sam" "es el nombre de Alex"

alternativa: comenzando con una buena suposición, entonces las pruebas de exactitud también me promover trabajar en una lógica de utilizar el resultado proporcionado por La respuesta de Daren usando fontMetrics como una buena conjetura inicial y luego te st it, si cabe la prueba +2 si no se ajusta a la prueba -2; si la nueva prueba cumple con la prueba, omitió 1 y sabrá su respuesta; si no, intente mover otras 2 y así sucesivamente, pero idealmente la respuesta fontMetrics no es más de 4 lejos ...

Sospecho que esto producirá los resultados promedio más rápidos de casos de uso reales.

suponiendo que desea un int y asumiendo que las inexactitudes de las métricas de fuente son mínimas, esto probablemente solo tome 2 o 3 conjeturas.

+0

El [método de Newton] (https://en.wikipedia.org/wiki/Newton%27s_method) es un enfoque similar, y no está limitado artificialmente en la rapidez con que puede converger. Una división y conquista binaria determina solo un bit de resultado por iteración. Newton generalmente converge mucho más rápido que eso en problemas simples como el tamaño de la fuente, asumiendo que la relación entre el tamaño de la fuente y el ancho aumenta monótonamente, como lo haría con las fuentes de tamaño flotante. –

1

Aquí está mi código del ajuste (en heigth) un texto, funciona bastante bien (error de 2% < supongo)

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