2012-07-12 8 views
7

Tengo un QGraphicsTextItem criaron a un QGraphicsItem. Quiero que el QGraphicsTextItem que siempre residen directamente por encima de la QGraphicsItem, pero también quiero que el texto siga siendo el mismo tamaño cuando el factor de escala está por debajo de 1, es decir, el texto sigue siendo el tamaño que se encuentra en un factor de escala de 1 incluso cuando los gráficos de los padres el artículo es escalado más pequeño. He encontrado que establecer el indicador QGraphicsItem::ItemIgnoresTransformations en verdadero cuando el factor de escala es inferior a 1 hace que el truco para retener el tamaño.mantener la posición relativa del niño después de aplicar QGraphicsItem :: ItemIgnoresTransformations

Pero me parece que no puede encontrar una manera de obtener la posición del texto de permanecer siempre por encima del QGraphicsItem. ¿Hay alguna forma de hacer esto? Traté de usar la función deviceTransform(), pero el texto aún se movió fuera de QGraphicsItem mientras lo desplazaba. Lo que es peor es que algunos de los elementos de texto comenzaron a "sacudirse", es decir, comenzaron a cambiar continuamente su posición ligeramente, de modo que parecía que estaban temblando. Si esta es la función que necesito usar, supongo que no sé cómo usarla correctamente.

En el constructor de mi QGraphicsItem He añadido un QGraphicsTextItem:

fTextItem = new QGraphicsTextItem(getName(), this); 
fTextItem->setFlag(QGraphicsItem::ItemIgnoresTransformations); 

Aquí es fragmento de código de la función de pintura QGraphicsItem

qreal lod = painter->worldTransform().m22(); 
if(lod <= 1.0) { 
    fTextItem-setFlag(QGraphicsItem::ItemIgnoresTransformations); 
    fTextItem->setPos(fTextItem->deviceTransform(view-viewportTransform()).inverted().map(view->mapFromScene(mapToScene(0,0)))); 
} else { 
    fTextItem->setFlag(QGraphicsItem::ItemIgnoresTransformations, false); 
    fTextItem->setPos(0, 0); 
} 
+0

qué no debe fijar el indicador QGraphicsTextItem objetos de ignorar la transformación de los padres en lugar de la del QGraphicsItem? – ArmenB

Respuesta

4

responsabilidad: esto puede ser excesiva para lo que estamos tratando de hacer Tuvimos algunas restricciones adicionales en nuestro proyecto que hicieron que esta solución fuera más fácil para nosotros.

Tuvimos que hacer algo similar en un proyecto, y terminó siendo más fácil para nosotros no usar ItemIgnoresTransformations y en su lugar rodar nuestra propia transformación. Esta es la función principal que utilizamos para crear una transformación de solo traducción (sin escala) para dibujar un elemento en una ubicación específica. Es posible que pueda modificarlo para su uso.

static QTransform GenerateTranslationOnlyTransform(
    const QTransform &original_transform, 
    const QPointF &target_point) { 
    // To draw the unscaled icons, we desire a transform with scaling factors 
    // of 1 and shearing factors of 0 and the appropriate translation such that 
    // our icon center ends up at the same point. According to the 
    // documentation, QTransform transforms a point in the plane to another 
    // point using the following formulas: 
    // x' = m11*x + m21*y + dx 
    // y' = m22*y + m12*x + dy 
    // 
    // For our new transform, m11 and m22 (scaling) are 1, and m21 and m12 
    // (shearing) are 0. Since we want x' and y' to be the same, we have the 
    // following equations: 
    // m11*x + m21*y + dx = x + dx[new] 
    // m22*y + m12*x + dy = y + dy[new] 
    // 
    // Thus, 
    // dx[new] = m11*x - x + m21*y + dx 
    // dy[new] = m22*y - y + m12*x + dy 
    qreal dx = original_transform.m11() * target_point.x() 
      - target_point.x() 
      + original_transform.m21() * target_point.y() 
      + original_transform.m31(); 
    qreal dy = original_transform.m22() * target_point.y() 
      - target_point.y() 
      + original_transform.m12() * target_point.x() 
      + original_transform.m32(); 

    return QTransform::fromTranslate(dx, dy); 
} 

Para utilizarlo, realice la QPainter transformación que se pasa al método de pintura y hacer algo como:

painter->save(); 
painter->setTransform(GenerateTranslationOnlyTransform(painter->transform(), 
                 some_point)); 
// Draw your item. 
painter->restore(); 
+0

En este momento parece demasiado para mi problema :) Pero un acercamiento sólido hacia abajo si no hay una manera fácil. ¡Muchas gracias! – KD07

+0

Intenté usar esta transformación personalizada en mi aplicación. Funciona en la medida en que la transformación es preocupante, pero aún así las posiciones de mis artículos están desactivadas. Quiero que mi elemento secundario (tenga una transformación personalizada) permanezca en el punto (0,0) del padre. Paso un cierto punto como (0,0). – KD07

6

Mi sugerencia es subclase QGraphicsSimpleTextItem de esta manera:

class TextItem 
    : public QGraphicsSimpleTextItem 
{ 
public: 
    TextItem(const QString &text) 
     : QGraphicsSimpleTextItem(text) 
    { 

    } 
    void paint(QPainter *painter, 
     const QStyleOptionGraphicsItem *option, QWidget *widget) 
    { 
     painter->translate(boundingRect().topLeft()); 
     QGraphicsSimpleTextItem::paint(painter, option, widget); 
     painter->translate(-boundingRect().topLeft()); 
    } 
    QRectF boundingRect() const 
    { 
     QRectF b = QGraphicsSimpleTextItem::boundingRect(); 
     return QRectF(b.x()-b.width()/2.0, b.y()-b.height()/2.0, 
      b.width(), b.height()); 
    } 
}; 
QGraphicsSimpleTextItem *mText = new TextItem("Item"); 
scene()->addItem(mText); 
mText->setFlag(QGraphicsItem::ItemIgnoresTransformations, true); 
mText->setPos(itemToFollow->pos()); 
+0

Tenga en cuenta que KD07 preguntó acerca de 'QGraphicsTextItem' (que admite el formato HTML/de texto enriquecido). Otra revisión de 'QGraphicsSimpleTextItem' se puede encontrar en [Qt Center] (http://www.qtcentre.org/threads/51168-QGraphicsTextItem-center-based-coordinates). – handle

0

Agregando a la respuesta de Dave Mateer, creo que sería útil agregar que en algún escenario, también debe mantener el rectángulo de delimitación (así como la forma) del objeto. Para mí, necesito modificar un poco el boundingRect() para el comportamiento correcto de selección de objetos. Recuerde que el rect del objeto se escalará y se transformará como de costumbre si NO usamos el indicador ItemIgnoresTransformations. Por lo tanto, también debemos volver a escalar el boundingRect para mantener el efecto de independencia de la vista.

Mantener el rectángulo delimitador independiente de la vista resulta bastante fácil: simplemente tome el factor de escala desde deviceTransform(m_view->viewportTransform()).inverted().m11() y multiplique esta constante por su rectángulo delimitador de coordenadas local. Por ejemplo:

qreal m = this->deviceTransform(m_view->viewportTransform()).inverted().m11(); 
return QRectF(m*(m_shapeX), m*(m_shapeY), 
       m*(m_shapeR), m*(m_shapeR)); 
0

¡Gran respuesta de Dave Mateer! Tuve el problema de querer definir un factor de escala diferente en diferentes niveles de zoom.Esto es cómo lo hice:

void MyGraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem* option, QWidget* widget) 
{ 
    //save painter for later operations 
    painter->save(); 
    QTransform originalTransform = painter->transform(); 
    QPointF originalCenter = rect().center(); 
    qreal dx = originalTransform.m11() * originalCenter.x() + originalTransform.m21() * originalCenter.y() + originalTransform.m31(); 
    qreal dy = originalTransform.m22() * originalCenter.y() + originalTransform.m12() * originalCenter.x() + originalTransform.m32(); 
    //normally our target scale factor is 1, meaning the item has keeps its size, regardless of zoom 
    //we adjust the scale factor though when the item is smaller than one pixel in comparison to the background image 
    qreal factor = 1.0; 
    //check if scale factor if bigger that the item size, and thus it occupies less that a pixel in comparision to the background image 
    if (rect().width() < originalTransform.m11()) { 
     //calculate adjusted scale factor 
     factor = originalTransform.m11()/rect().width(); 
    } 
    //adjust position according to scale factor 
    dx -= factor * originalCenter.x(); 
    dy -= factor * originalCenter.y(); 
    //set the new transform for painting 
    painter->setTransform(QTransform::fromScale(factor, factor) * QTransform::fromTranslate(dx, dy)); 
    //now paint... 
    QGraphicsXYZItem::paint(painter, option, widget); 
    //restore original painter 
    painter->restore(); 
} 

Sí es necesario ajustar el rectángulo delimitador también en ese caso:

QRectF MyGraphicsItem::boundingRect() const 
{ 
    QRectF rect = QGraphicsEllipseItem::boundingRect(); 
    //this is a bit hackish, let me know if you know another way... 
    if (scene() != NULL && scene()->views().at(0) != NULL) 
    { 
     //get viewport transform 
     QTransform itemTransform = scene()->views().at(0)->transform(); 
     QPointF originalCenter = rect.center(); 
     //calculate back-projected original size of item 
     qreal realSizeX = rect.width()/itemTransform.m11(); 
     qreal realSizeY = rect.height()/itemTransform.m11(); 
     //check if scale factor is bigger that the item size, and thus it occupies less that a pixel in comparison 
     //to the background image and adjust size back to equivalent of 1 pixel 
     realSizeX = realSizeX < 1.0 ? 1.0 : realSizeX; 
     realSizeY = realSizeY < 1.0 ? 1.0 : realSizeY; 
     //set adjusted position and size according to scale factor 
     rect = QRectF(rect.center().x() - realSizeX/2.0, rect.center().y() - realSizeY/2.0, realSizeX, realSizeY); 
    } 
    return rect; 
} 

Con esta solución, el elemento de trabajo muy bien en mi caso.

0

aquí es una solución Ideé de complejidad muy moderado:

1) Obtener el boundingRect() de la matriz y asignarla a la escena 2) tomar el mínimo de X e Y de esta lista de puntos, esto es el origen real de su artículo, en la escena coordina 3) establecer la posición del niño

en PySide:

br = parent.mapToScene(parent.boundingRect()) 
    realX = min([item.x() for item in br]) 
    realY = min([item.y() for item in br]) 
    child.setPos(parent.mapFromScene(realX, realY)) #modify according to need 
0

he encontrado otra solución, que no implica jugar con cualquier transformación o por escala/posicionamiento a mano. Hay una indirecta en la descripción QGraphicsItem::ItemIgnoresTransformations bandera:

QGraphicsItem::ItemIgnoresTransformations

El elemento ignora transformaciones hereditarias (por ejemplo, su posición se todavía anclado a su padre, pero el padre o el punto de vista de rotación, zoom o las transformaciones de corte son ignoradas). [...]

¡Y esa es la clave! Necesitamos dos elementos: un padre que mantendrá la posición relativa (sin ningún conjunto de banderas) y un elemento hijo que hará el sorteo en el punto del padre (0,0) (con el indicador QGraphicsItem::ItemIgnoresTransformations establecido). ¡Simple como eso!

he encapsulado esta funcionalidad en una sola clase - Éste es el código:

#include <QGraphicsItem> 
#include <QPainter> 

class SampleShape : public QGraphicsItem 
{ 
private: 
    /* This class implements shape drawing */ 
    class SampleShapeImpl : public QGraphicsItem 
    { 
    public: 
     SampleShapeImpl (qreal len, QGraphicsItem *parent = nullptr) 
      : QGraphicsItem(parent), m_len(len) 
     { 
      /* ignore transformations (!) */ 
      setFlag(QGraphicsItem::ItemIgnoresTransformations); 
     } 

     QRectF boundingRect (void) const override 
     { 
      /* sample bounding rectangle */ 
      return QRectF(-m_len, -m_len, m_len*2, m_len*2); 
     } 

     void paint (QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override 
     { 
      /* draw a shape, (0,0) is an anchor */ 
      painter->drawLine(0, -m_len, 0, m_len); 
      painter->drawLine(-m_len, 0, m_len, 0); 
      // ... 
     } 

    private: 
     qreal m_len; // sample shape parameter 
    }; 

public: 
    /* This is actually almost an empty class, you only need to set 
    * a position and pass any parameters to a SampleShapeImpl class. 
    */ 
    SampleShape (qreal x, qreal y, qreal len, QGraphicsItem *parent = nullptr) 
     : QGraphicsItem(parent), m_impl(len, this) // <-- IMPORTANT!!! 
    { 
     /* set position at (x, y), view transformations will apply */ 
     setPos(x, y); 
    } 

    QRectF boundingRect (void) const override 
    { 
     return QRectF(); // it's just a point, no size 
    } 

    void paint (QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override 
    { 
     // empty, drawing is done in SampleShapeImpl 
    } 

private: 
    SampleShapeImpl m_impl; 
}; 
Cuestiones relacionadas