2012-07-12 16 views
7

Ho un oggetto QGraphicsTextItem associato a un oggetto QGraphics. Voglio che QGraphicsTextItem risieda sempre direttamente sopra QGraphicsItem, ma voglio anche che il testo rimanga della stessa dimensione quando il fattore di scala scende sotto 1, cioè il testo rimane la dimensione è su un fattore di scala di 1 anche quando la grafica principale l'articolo è ridimensionato più piccolo. Ho trovato che impostare il flag QGraphicsItem::ItemIgnoresTransformations su true quando il fattore di scala è inferiore a 1 fa il trucco per mantenere la dimensione.Mantenimento della posizione secondaria relativa dopo l'applicazione di QGraphicsItem :: ItemIgnoresTransformations

Ma non riesco a trovare un modo per ottenere che la posizione del testo rimanga sempre al di sopra del QGraphicsItem. C'è un modo per fare questo? Ho provato a utilizzare la funzione deviceTransform(), ma il testo è stato spostato da QGraphicsItem mentre si scorreva. Quel che è peggio è che alcuni degli elementi di testo iniziarono a "tremare", cioè iniziarono a cambiare continuamente la loro posizione leggermente, così da sembrare che tremassero. Se questa è la funzione che devo usare, suppongo di non sapere come usarla correttamente.

Nel costruttore della mia QGraphicsItem Ho una QGraphicsTextItem:

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

Ecco frammento di codice da funzioni vernice di 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

non si dovrebbe impostare il QGraphicsTextItem bandiera oggetti di ignorare la trasformazione genitore al posto del l'QGraphicsItem? – ArmenB

risposta

4

Esonero di responsabilità: questo può essere eccessivo per quello che si stanno cercando di fare. Abbiamo avuto alcune restrizioni aggiuntive nel nostro progetto che hanno reso questa soluzione la più semplice per noi.

che dovevamo fare qualcosa di simile in un progetto, ed è finito per essere più facile per noi non usiamo per ItemIgnoresTransformations and Roll nostra trasformano invece. Ecco la funzione principale che usiamo per creare una trasformazione di sola traduzione (senza ridimensionamento) per disegnare un oggetto in una posizione specifica. Potresti essere in grado di modificarlo per il tuo utilizzo.

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

Per utilizzare, prendere la QPainter trasformazione che viene passato al metodo vernice e fare qualcosa di simile:

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

A questo punto sembra eccessivo per il mio problema :) Ma approccio di ground up solido se non esiste un modo semplice. Molte grazie! – KD07

+0

Ho provato a utilizzare questa trasformazione personalizzata nella mia applicazione. Funziona tanto quanto la trasformazione è una preoccupazione, ma le mie posizioni sugli oggetti sono ancora fuori. Voglio che l'elemento figlio (con trasformazione personalizzata) rimanga al punto (0,0) del genitore. Passo some_point come (0,0). – KD07

6

Il mio suggerimento è quello di creare una sottoclasse QGraphicsSimpleTextItem in questo modo:

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

Si noti che KD07 ha chiesto di 'QGraphicsTextItem' (che supporta la formattazione di testo RTF/HTML). Un'altra interpretazione di 'QGraphicsSimpleTextItem' può essere trovata su [Qt Center] (http://www.qtcentre.org/threads/51168-QGraphicsTextItem-center-based-coordinates). – handle

0

Aggiungendo la risposta di Dave Mateer, penso che sarebbe utile aggiungere che in alcuni scenari, dovresti anche mantenere il rettangolo di delimitazione (e anche la forma) del rispettivo oggetto. Per quanto mi riguarda, ho bisogno di modificare lo boundingRect() un po 'troppo per il corretto comportamento di selezione degli oggetti. Ricorda che il retto limite dell'oggetto verrà ridimensionato e trasformato come al solito se NON utilizziamo il flag ItemIgnoresTransformations. Quindi abbiamo anche bisogno di ridimensionare il boundingRect per mantenere l'effetto di indipendenza della vista.

Per mantenere il rettangolo di delimitazione indipendente dalla vista risulta essere abbastanza semplice: basta prendere il fattore di scala da deviceTransform(m_view->viewportTransform()).inverted().m11() e moltiplicare questa costante sul rettangolo di delimitazione delle coordinate locali. Ad esempio:

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

Grande risposta di Dave Mateer! Ho avuto il problema che volevo definire un fattore di scala diverso a diversi livelli di zoom.Ecco come ho fatto:

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

Si ha bisogno di regolare il rettangolo di delimitazione anche in quel 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 questa soluzione il lavoro oggetto molto bene nel mio caso.

0

Ecco una soluzione ho ideato di complessità molto moderato:

1) Prendi il boundingRect() del genitore e la mappa di scena 2) prendere il minimo X e Y di questo elenco di punti, questo è la vera origine del tuo articolo, in scena coordinate 3) impostare la posizione del bambino

in 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

ho trovato un'altra soluzione, che non comporta scherzi con eventuali trasformazioni o da ridimensionamento/posizionamento manuale. C'è un suggerimento nella descrizione QGraphicsItem::ItemIgnoresTransformations bandiera:

QGraphicsItem::ItemIgnoresTransformations

L'articolo ignora trasformazioni ereditarie (cioè, sua posizione viene ancorata al suo genitore, ma il genitore o vista rotazione, zoom o le trasformazioni di taglio vengono ignorate). [...]

E questa è la chiave! Abbiamo bisogno di due elementi: un genitore che manterrà la posizione relativa (senza alcun flag impostato) e un elemento figlio che eseguirà il disegno al punto (0,0) del genitore (con il flag QGraphicsItem::ItemIgnoresTransformations impostato). Semplice come quella!

ho incapsulato questa funzionalità in una singola classe - Ecco il codice:

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