11

I am using Qt´s QGraphicsView - and QGraphicsItem-subclasses. is there a way to not scale the graphical representation of the item in the view when the view rectangle is changed, e.g. when zooming in. The default behavior is that my items scale in relation to my view rectangle.

I would like to visualize 2d points which should be represented by a thin rectangle which should not scale when zooming in the view. See a typical 3d modelling software for reference where vertex points are always shown at the same size.

Thanks!

AAEM
  • 1,837
  • 2
  • 18
  • 26
Lars Bilke
  • 4,940
  • 6
  • 45
  • 63
  • 1
    Also take a peak at http://doc.trolltech.com/4.5/qpen.html#setCosmetic if you're using a custom paint routine – mpen Aug 31 '09 at 02:54

5 Answers5

12

Set the QGraphicItem's flag QGraphicsItem::ItemIgnoresTransformations to true does not work for you?

AAEM
  • 1,837
  • 2
  • 18
  • 26
Ariya Hidayat
  • 12,523
  • 3
  • 46
  • 39
  • 5
    This indeed ensures the size of the item is correct, but at least in my case the positions are off after the transform. For example, when I draw a polygon in my app, and add child rectangle items in an unscaled (1:1) view, I get the following result: http://grafit.mchtr.pw.edu.pl/~szczedar/nozoom.png . After scaling and with the flag set, it looks like this: http://grafit.mchtr.pw.edu.pl/~szczedar/zoomout.png – neuviemeporte Mar 14 '11 at 14:10
  • 1
    The docs say the item with the flag set stays anchored to the parent, but I created the rect object with the the polygon as the parent and it didn't work. Tried the underlying pixmap item as a parent as well, no change. – neuviemeporte Mar 14 '11 at 14:14
10

I got into the same problem, and it took me a while to figure it out. This is how I solved it.

Extend a QGraphicsItem class, override paint(). Inside the paint(), reset the transformation's scaling factor to 1(which are m11 and m22), and save the m11(x scaling factor) and m22(y scaling factor) before the reset. Then, draw like you would normally do but multiply your x with m11 and y with m22. This avoids drawing with the default transformation, but explicitly calculates the positions according to the scene's transformation.

void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget)
{
    QTransform t = painter->transform();
    qreal m11 = t.m11(), m22 = t.m22();
    painter->save(); // save painter state
    painter->setTransform(QTransform(1, t.m12(), t.m13(),
                                     t.m21(), 1, t.m23(), t.m31(),
                                     t.m32(), t.m33()));
    int x = 0, y = 0; // item's coordinates
    painter->drawText(x*m11, y*m22, "Text"); // the text itself will not be scaled, but when the scene is transformed, this text will still anchor correctly
    painter->restore(); // restore painter state
}

The following code block is drawing with default transformation

void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget)
{
    int x = 0, y = 0;
    painter->drawText(x, y, "Text"); 
}

You can try both to see the difference. Hope this helps.

Kai
  • 38,985
  • 14
  • 88
  • 103
JLin
  • 101
  • 1
  • 2
2

How about this:

#include <QtGui/QApplication>
#include <QtGui/QGraphicsScene>
#include <QtGui/QGraphicsView>
#include <QtGui/QGraphicsRectItem>

int main(int argc, char* argv[]) {
    QApplication app(argc, argv);
    QGraphicsScene scene;
    scene.addText("Hello, world!");
    QRect rect(50, 50, 100, 100);
    QGraphicsRectItem* recti = scene.addRect(rect);
    QGraphicsView view(&scene);

    // Set scale for the view
    view.scale(10.0, 5.0);

    // Set the inverse transformation for the item
    recti->setTransform(view.transform().inverted());

    view.show();
    return app.exec();
}

As you can see the text is scaled up but the rectangle is not. Note that this does not only prevent the scaling for the rectangle but and other transformation.

stribika
  • 3,146
  • 2
  • 23
  • 21
2

The following solution worked perfectly for me:

void MyDerivedQGraphicsItem::paint(QPainter *painter, const StyleOptionGraphicsItem *option, QWidget *widget)
{
    double scaleValue = scale()/painter->transform().m11();
    painter->save();
    painter->scale(scaleValue, scaleValue);
    painter->drawText(...);
    painter->restore();
    ...
}

We can also multiply the scaleValue by other mesures we want to keep its size constant outside the save/restore environment.

    QPointF ref(500, 500);
    QPointF vector = scaleValue * QPointF(100, 100);
    painter->drawLine(ref+vector, ref-vector);
Adriel Jr
  • 2,451
  • 19
  • 25
1

I found that if I derive a new class and reimpliment the paint function I can do

void MyDerivedQGraphicsItem::paint(QPainter *painter, 
                                   const QStyleOptionGraphicsItem *option, 
                                   QWidget *widget)
{
  double scaleValue = scale();
  double scaleX = painter->transform().m11();
  setScale(scaleValue / scaleX);
  QGraphicsSvgItem::paint(painter,option,widget);
}

This is the best way of doing it that I have found so far, but I am still tinkering around.

enriched
  • 400
  • 4
  • 8
  • 1
    Hm. This did not work for me (in my case I'm subclassing QGraphicsEllipseItem). The item still gets larger when I zoom the view in. Did you do anything besides the above? – estan Nov 21 '13 at 11:24
  • Bah sorry, it actually did work. But I'd be interested in if you found any other way? Because when I zoom the view fast (using scroll wheel) I can notice some flicker :/ – estan Nov 21 '13 at 11:26
  • Sorry, no, I was still having some problems with it but it got abandoned. – enriched Jan 15 '14 at 19:08
  • setScale scedules a redraw of the item so I guess that your code can lead to an infinite loop. And painter->transform().m11() gives the scale if and only if no rotation is applied to the item. – user1482030 Jun 19 '17 at 08:50