5
votes

I am making an application where I need to draw figures (i.e. rectangles) above a picture and resize the whole scene. I'm using QGraphicsView and QGraphicsScene. I can't figure out why but when I was using fitIntoView() I was unable to draw figures on top of a picture (they probably were drawn behind the picture or outside the bounds).

Now I'm using QPixmap.scaled() to make the image fit in the QGraphicsScene. It works fine except when I need to display a big image and then zoom it in. Because I scaled the image to fit, it became smaller and when I call QGraphicsView.scale() the image scales as small one and I can't find a way to "scale back" the image to original size.

In the zoom part of code I tried to replace pixmap with its not scaled copy but I can't scale it with the same proportions as my figures scaling. Any help would be appreciated!


Here I load the image and scale it

QPixmap pix;
pix = pixmap->scaled(wid, hei,Qt::KeepAspectRatio, Qt::SmoothTransformation);
scn = new QGraphicsScene(pw);
pw->setScene(scn);
p = new QGraphicsPixmapItem(pix);
p->setPixmap(pix);
scn->addItem(p);

I draw figures like this

rect = new QGraphicsRectItem(x,y,w,h);
rect->setPen(QPen(getColor(), 1, Qt::SolidLine, Qt::FlatCap));
scn->addItem(rect);

And here's how I zoom

pw->scale(scaleFactor_,scaleFactor_);

Update: Now I have a QWidget with kind of toolbar on the top, the rest of free space is filled with a custom QGraphicsView. This is a derived class for grabbing mouse events, there's nothing more but overrides of event handlers. I'm adding PainterGraphicsView in my widget's constructor:

pw = new PainterGraphicsView();
ui->gridLayout_3->addWidget(pw);

I'm recreating the scene every time I load a new image into it:

scn = new QGraphicsScene(pw);
pw->setScene(scn);
QRect r = QRect(0,0, pw->width()-5, pw->height()-5);
scn->setSceneRect(r);

After I loaded an image I can draw figures on top of it, it looks like this:

enter image description here

And the scaled contents look like this:

enter image description here

As you can see, figures scaled together with a pixmap and this is exactly how I need it to work. The only thing I have to fix is the low quality of scaled pixmap. As I already said, I tried to replace pixmap with the original one in my ZoomIn method, but I can't do it saving figures' position.

void DrawerWidget::on_btZoom_clicked()
{
    p->setPixmap(pix); //p is QGraphicsPixmapItem, pix is an original pixmap
    p->setScale(0.5); //how can I scale the pixmap together with figures?? How to calculate the required scale?
    pw->scale(scaleFactor_,scaleFactor_);
}

enter image description here


Update 2:

A short explanation on my drawing figures method: (though I personally don't think that this is the issue, I'm interested is there a way to scale QGraphicsPixmapItem using setScale() to fit in QGraphicsView size)

I'm getting start_ and end_ coordinates from mouse events arguments (QMouseEvent *e)

 end_=pw->mapToScene(e->pos());
 start_=pw->mapToScene(e->pos());

I add the figure on mousepress:

rect = new QGraphicsRectItem(start_.x(),start_.y(),1,1);
scn->addItem(rect);

And I redraw the figure on mouse move

rect->setRect(Graphics::GetFigure(start_.x(),start_.y(),end_.x(),end_.y()));

GetFigure is a method for calculating the figure's rect, here's main part of it:

 static QRectF GetFigure(double startX, double startY, double finalX, double finalY)
    {
      QRectF shape;
      shape.setX(startX);
      shape.setY(startY);
      shape.setWidth(qFabs(finalX - startX));
      shape.setHeight(qFabs(finalY - startY));
      return shape;
   }
2
I can't understand why you scale the QPixmap. If you want to scale the image and maintain the resolution you can scale the QGraphicsPixmapItem instead.Fabio
Hi @Fabio! Thank you for the response! I scale the QPixmap because I can scale it to fit in width and height, and with the QGraphicsPixmapItem I have to calculate the scale rate somehow which I don't know how... :(lena
It is okay with the image smaller then QGraphicsScene size, but I need to make large image smaller. As I understood I need to use scale from 0 to 1 butI have a strange result. I just tried setScale(0.5) and I expected QGraphicsPixmapItem to have twice less size, but I only got a square containing a part of original image inside. What can I miss here?lena
Sorry but it's not very clear. We need more code and maybe also some screenshot.Fabio
I'm suspicious that your real problem is that you can't change QGraphicsView::scale without causing your overlays to appear off-screen because you're not transforming your points between coordinate spaces correctly when placing your items based on mouse events.cgmb

2 Answers

4
votes

The scaling operation is destructive, once you scale down you end up with less data than you started with.

IF you want to be able to restore the original size keep a copy of the original pixmap.

You can create the QGraphicsPixmapItem from the original, non-scaled pixmap and then use QGraphicsPixmapItem::setScale(qreal) to change the size on level graphics item, not on level pixmap data. This way scaling should not be destructive, and you should not lose quality if you scale it up to the original size.

IF you want to scale multiple items (and not run into different transformation origin problems) you have a few options:

  • scale the entire view - that will scale everything
  • add the items as children of a parent item, then scaling the parent will scale its children as well

Item transformations accumulate from parent to child

  • add the items to a group using createItemGroup():

The items are all reparented to the group, and their positions and transformations are mapped to the group

1
votes

I think your problem is that you set the scene rect as the QGraphicsView rect. This is not correct because the window reference system is different from the scene reference system. You don't need to set the scene rect, because the scene rect grows automatically when you add some items. If you want to limit the scene rect to the pixmap rect, you can set the scene rect as the QPixampItem bounding rect.

You don't need to scale the original pixmap. To scale your view, simply call QGrapicsView::scale. You can also call QGrapicsView::fitInView to fit the image in the view.

If you need to add some rects drawn by user (i.e. in window coordinates), you can use QGraphiscView::mapToScene to map the window coordinates to scene coordinates

I suggest to read the documentation about reference systems: The Graphics View Coordinate System