PDA

View Full Version : QGraphicsView scale question.



hickscorp
14th April 2007, 17:21
Hello to everyone,

Here is what i have:
- a QDialog, containing a QGraphicsView.
- The View is "containing" a Scene, in which i only put a QImage.
- In the QDialog, there is a Zoom out button which scale(.5, .5) on the View, and another Zoom in button which scale(2, 2) the View.

Now i wish to add another widget on the graphics view, which is on top of all others (The QImage), but which doesnt "scales". What is the best thing to do to achieve this?
In other words: do i have to only scale the QImage (So the other widget doesnt scales)? If yes, how? :)

Thanks a lot!
Pierre.

hickscorp
14th April 2007, 17:38
Hello again,

I just figured that i am able to scale directly on a QGraphicsItem... which is very good for me, because it solves my problem.

However i have a related question. Let's take for instance a QGraphicsItem, which originaly was containing a QImage. After i retrieve the QGraphicsItem with QGraphicsView::items(), how can i get a pointer to the QImage it contains, so i can change it's contents without putting a new QPicture each time?

Thanks,
Pierre.

marcel
14th April 2007, 17:49
Do you use QGraphicsPixmapItem to display the pixmap?
If you do, you can use the pixmap() and setPixmap() to get/set the pixmap...

QGraphicsView::items() will return a list of QGraphicsItem. You will have to do a cast to QGrasphicsPixmapItem.

Regards.

hickscorp
14th April 2007, 17:57
Hello Marcel, and thanks for answering.

When adding the item to my scene, i simply do a addItem(myQImage);
Do you confirm that this QImage is then put in a QGraphicsPixmapItem?

Pierre.

marcel
14th April 2007, 18:02
Yes, it will add a QGraphicsPixmapItem since it is the only item that can hold a pixmap.

Why do you use a QImage and why not use addPixmap() ?

hickscorp
14th April 2007, 18:04
Ok, so it's impossible that if i change the QImage, i see the changes in the scene right?
Or does the QGraphicsPixmap work with a pointer to my QImage? oO

marcel
14th April 2007, 18:07
No, it passes a reference to the constructor of QGraphicsPixmapItem and then the operator= of QPixmap will copy your original pixmap into a QGraphicsPixmapItem.

Just checked it in the sources..

marcel
14th April 2007, 18:15
Hey, have you actually implemented this?

Because:


QImage img;
scene->addItem( img );

will not even compile.

Try with QPixmap and addPixmap. Or just construct a QPixmapGraphicsItem from a QPixmap and just use addItem to add it to the scene.

Regards

hickscorp
14th April 2007, 18:19
Ouch :)

So all this means, if i just constructed a QImage of 1600x1200, then the QGraphicsPixmapItem implicitely created when calling addItem(myQImage) will hold another copy of my image, i'm glad i have 1Gb ram then :)

[EDIT:]You're right, what i'm actually doing is grphScToFill->addPixmap(QPixmap::fromImage(cImage, Qt::ColorOnly | Qt::ThresholdDither));

Now another simple question since you seem to know pretty much about the QGraphics framework:
Previously, when the user pressed the zoom +/- buttons, i was scaling the scene. It was very nice, since the point where the view were focused was staying on the center after the scale process. Also, when a zoom level like 0.5 was hit, the scene content was centered within the rectangle of my view...

Now what i am doing, is to scale directly the graphic item instead of the whole scene... But i have a problem doing this: when i scale to a width / height lower than the visible rect, the item is put on the top left corner, and the scene content is still "draggable" to much more top left...

So i have tried to setPos on my item each time i resize it, it doesnt moves in scene. I also tried to change the sceneRect to fit the item, but i wasnt able to get the item size...

What am i doing / understanding wrong?

Thanks ^^

marcel
14th April 2007, 18:26
Now another simple question since you seem to know pretty much about the QGraphics framework:
Not really that much... :)

Use void QGraphicsView::centerOn ( const QPointF (http://www.qtcentre.org/forum/qpointf.html) & pos ).
As pos pass the center of your QGraphicsPixmapItem. Pos is scene coords.

As an alternative, use void QGraphicsView::ensureVisible ( const QRectF (http://www.qtcentre.org/forum/qrectf.html) & rect, int xmargin = 50, int ymargin = 50 ).
rect is scene cords.

Regards.

hickscorp
14th April 2007, 18:40
Hello again :)

Well i'm sorry, but i'm not sure i understand how to do what you recomend :o
When i call centerOn(), i have to give some coordinates to the method. Do i have to give the coordinates of the center of my scalled Item as you say: let's take as example my item is scalled down to 0.5 two times, do i have to take my QGraphicsPixmapItem width / height, divide it by 4 (Get item's size in view local coords system after scale), divide the result by 2 (For the center), and then give to centerOn the result added to the position of my item in the scene?

I tried several methods, for you i have made some screenshots of what i get.
On the pictures, you can clearly see that after the item is resized, it is "stuck" to top left corned, and is still draggable (But not to the center).

Anyway thanks, maybe if i try various stuff i'll find why i'm wrong :)
Pierre.

hickscorp
14th April 2007, 18:46
I just have tried this (Note that iZoomFactor is an int, which is 100 for a scale of 1, 50 for a scale of .5 etc):


pPicture = (QGraphicsPixmapItem*)grphVwOriginalPicture->items().first();
pPicture->scale(.5, .5);
QPoint cCenterOn;
// [EDIT:] Here i get the size of the item in the view coords system, divide it by 2 (For the center) and add it's position.
cCenterOn.setX( (100/pPicture->pixmap().width()*iZoomFactor) / 2 + pPicture->pos().x() );
cCenterOn.setY( (100/pPicture->pixmap().height()*iZoomFactor) / 2 + pPicture->pos().y() );
grphVwOriginalPicture->centerOn(cCenterOn);

But the same problem is here... I'm not sure of what i can try next, but for now i'm stuck.

[EDIT:]Attached is what i want, it's achieved by scaling the whole scene instead of the item. But i cant keep scaling the whole scene, since i'll have to put another item in it which has to not be scaled.

Pierre.

marcel
14th April 2007, 18:51
No, I believe you have to use scene coords instead of view coords. After all, you are trying to center the scene.

BTW: what app are you working on? some kind of photoshop? :) looks pretty good... especially the tabs in the upper control bar.

Regards.

hickscorp
14th April 2007, 18:53
Please see previous post i edited with some screenshots.

So as you say, my misstake is probably the referential. I'll try to look at this more further soon, but for now i have lost the faith :)

What i'm trying to do is actually a plugin for Photoshop. What you see is actually Photoshop :)
We decided to use the QT commercial edition for the gui, since Photoshop exists on Mac and since i worked on QT for some opensource stuff, i was able to make the financials of this company buy QT ^^

Pierre.

marcel
14th April 2007, 19:02
:) Yes... I didn't realize it was photoshop... The plugin looks good.

Use scene coordinates for centerOn and do not take into account the zoom factor. Just use:



view->centerOn( itemCenterPosInSceneCoords ).



Because if you scale only the item, the scale factor of the scene will not change. You will just have a bigger( smaller ) item, depending on the zoom.

hickscorp
14th April 2007, 19:10
Yeah but to get what you call the "item center in the scene", how am i supposed to do, since i cant get the item's width / height?
i would do centerOn(QPoint(item->x()+item->Width()/2, item->y()+item->Height()/2)), but there are not such methods width() height()! So how do i get the item's center?
Pseudo code please, i'm totally lost :)

Pierre.

[EDIT:] Maybe you should just try this: create a graphics view, put a big QImage in it. Then tell the image to scale down, and try to center it. Also when the picture is big, it is draggable off-view.

marcel
14th April 2007, 19:49
Ok. I have played a little bit with QGraphicsView/Scene.

Here's the code:



QGraphicsItem* itm = mDocument->getCurrentPage()->items()[0];
QRectF f= itm->boundingRect();
QPointF mapped1 = itm->mapToScene( f.topLeft() );
QPointF mapped2 = itm->mapToScene( f.bottomRight() );
centerOn( ( mapped2.x() - mapped1.x() )/2, ( mapped2.y() - mapped1.y() )/2 );
That's it...
This is done in a QGraphicsView class...
I guess you can do it wherever you need it.

NOTE: When you scale an item, the top left position always remains the same.

Regards.

hickscorp
15th April 2007, 00:19
Hello sorry for the delay :)

With your code, i have the very same problem. The item is "stuck" to the top left corner, and only can be dragged off-screen when it is little. When it's bigger than the viewable area, it's draggable but can be sragged off-screen in the top left direction too.

There comes a preview of my code:




void CDlgSettingsImpl::on_btnZoomIn_clicked(void) {
if (iZoomFactor>=1600)
return;
iZoomFactor *= 2;
lblZoomLevel->setText(QString::number(iZoomFactor)+"%");

QGraphicsItem* pPicture;
QPointF mapped1, mapped2;
QRectF f;

pPicture = grphVwOriginalPicture->items().first();
pPicture->scale(2, 2);
f = pPicture->boundingRect();
mapped1 = pPicture->mapToScene(f.topLeft());
mapped2 = pPicture->mapToScene(f.bottomRight());
grphVwOriginalPicture->centerOn((mapped2.x()-mapped1.x())/2, (mapped2.y()-mapped1.y())/2);
}
void CDlgSettingsImpl::on_btnZoomOut_clicked(void) {
if (iZoomFactor<=25)
return;
iZoomFactor /= 2;
lblZoomLevel->setText(QString::number(iZoomFactor)+"%");

QGraphicsItem* pPicture;
QPointF mapped1, mapped2;
QRectF f;

pPicture = grphVwOriginalPicture->items().first();
pPicture->scale(.5, .5);
f = pPicture->boundingRect();
mapped1 = pPicture->mapToScene(f.topLeft());
mapped2 = pPicture->mapToScene(f.bottomRight());
grphVwOriginalPicture->centerOn((mapped2.x()-mapped1.x())/2, (mapped2.y()-mapped1.y())/2);
}


Can it be a bug with my QT installation? i havnt tested any of the QGraphics framework on linux, so i'm not sure if it's windows only ...

Pierre.

[EDIT:] Maybe the error is here, so here comes how i create the QGraphicsScene & View stuff (In the dialog's constructor):



// Create the original preview objects.
grphScOriginal = new QGraphicsScene(this);
grphVwOriginalPicture = new QGraphicsView(grphScOriginal, this);
grphVwOriginalPicture->setFrameShape(QFrame::Panel);
grphVwOriginalPicture->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff) ;
grphVwOriginalPicture->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOf f);
grphVwOriginalPicture->setDragMode(QGraphicsView::ScrollHandDrag);
grphVwOriginalPicture->setBackgroundBrush(Qt::darkGray);
fillPreviewFromPlane(grphScOriginal, pInPlane);

void CDlgSettingsImpl::fillPreviewFromPlane(QGraphicsSc ene* grphScToFill, CARGBPlane* pPlane) {
QList<QGraphicsItem*> lstItems = grphScToFill->items();
while (!lstItems.isEmpty()) {
QGraphicsItem* pCurrentItem = lstItems.takeFirst();
grphScToFill->removeItem(pCurrentItem);
delete pCurrentItem;
}
QImage cImage = QImage(pPlane->Width(), pPlane->Height(), QImage::Format_RGB32);
byte* fR = pPlane->fR;
byte* fG = pPlane->fG;
byte* fB = pPlane->fB;
for (int iY=0; iY<pPlane->Height(); iY++)
for (int iX=0; iX<pPlane->Width(); iX++)
cImage.setPixel(iX, iY, qRgb(*fR++, *fG++, *fB++));
grphScToFill->addPixmap(QPixmap::fromImage(cImage, Qt::ColorOnly | Qt::ThresholdDither));
}

hickscorp
15th April 2007, 00:46
I believe i have found the "why". In the documentation, it is said this:
sceneRect : QRectF
This property holds the scene rectangle; the bounding rectangle of the scene.
The scene rectangle defines the extent of the scene. It is primarily used by QGraphicsView to determine the view's default scrollable area, and by QGraphicsScene to manage item indexing.
If unset, or if set to a null QRectF, sceneRect() will return the largest bounding rect of all items on the scene since the scene was created (i.e., a rectangle that grows when items are added to or moved in the scene, but never shrinks).

Which means, if my scene is created containing a QImage of 1600x1200 and if i scale down this image to 800x600, the sceneRect will still be 1600x1200. So i just tried to do a "grphScProcessed->setSceneRect(pPicture->boundingRect());" right after my item is scaled down, but same problem. Any clue please?

Pierre.

hickscorp
15th April 2007, 02:18
ok i think i have finally found what i needed. Here is what i do now, right after changing the scale of my item in the scene:




pPicture = grphVwOriginalPicture->items().first();
pPicture->scale(.5, .5);
cTopLeft = pPicture->mapToScene(pPicture->boundingRect().topLeft());
cBotRight = pPicture->mapToScene(pPicture->boundingRect().bottomRight());
grphScOriginal->setSceneRect(cTopLeft.x(), cTopLeft.y(), cBotRight.x()-cTopLeft.x(), cBotRight.y()-cTopLeft.y());


It seems it's sufficient to set up the scene's view rect to the right rect...

Marcel, thanks for all your help, it's because of you i finally understood what the mappedTo / mappedFrom methods do :)

Pierrre.

marcel
15th April 2007, 02:28
I was just looking over your last 2 posts...
Good you found a solution...

BTW:
setSceneRect( item->boundingRect( )) did not work because boundingRect remains unscaled. It always remains at the original value. That is why the mapping is needed.

Another thing to remember is that when you create the raster it is placed in the scene at position (0,0).
When you scale it, it will shrink, but the top left position is still at (0, 0), that is why the center would not work. I thought that is what you wanted - to see the entire scaled picture, not to see it AND be centered :).

Your solution, set scene rect will shrink the scene size, but I guess that's ok, because you'll set it again if you zoom in/out...

Regards