How do I resize a QGraphicsView to show the whole QGraphicsScene?
-
@Asperamanca , thank you for the answer. I still don't have much experience with Qt, so I think it is better to explain what I would like to do:
- Load images (BMP, PNG or others) from a file or retrieve them from an instrument and display them in a window with the possibility to zoom in and out and to scale the displayed images if the user requests it;
- Get live data from an instrument and display them in a chart (QChart, QLineSeries);
- Add comments (normally text) to the images and the chart;
- Merge things together and save and print them.
After having read in the docs it seemed to me that for this purpose a QGraphicsView with a QGraphicsScene would be the best choice. What would you use instead?
As far as fitInView is concerned, if I am right it scales the content so that it fits into the view but this is the opposite of what I am looking for.
-
Dear all,
in my application I have got a QMainWindow with a QGraphicsView that contains a QGraphicsScene. The QGraphicsScene contains a QLabel to display a QImage. I know that there are easier ways to display an image using a QLabel, but I have chosen this approach because I would later like to add other items to the scene.
Whenever an image is loaded (QGraphicsScene::clear(), QGraphicsScene::addWidget(label), ...), I set the scene size to the size of the label and I would like my window to resize automatically so that it shows the whole scene, i.e. the window should either grow or shrink, depending on the current size of the scene.
Currently I use the following code to do this:
void MainWindow::fitWindow() { const auto leftMargin = ui->centralwidget->layout()->contentsMargins().left(); const auto topMargin = ui->centralwidget->layout()->contentsMargins().top(); const auto rightMargin = ui->centralwidget->layout()->contentsMargins().right(); const auto bottomMargin = ui->centralwidget->layout()->contentsMargins().bottom(); const auto diffWidth = m_graphicsScene->sceneRect().width() - ui->centralwidget->width(); const auto diffHeight = m_graphicsScene->sceneRect().height() - ui->centralwidget->height(); const auto frameWidth = ui->graphicsView->frameWidth(); QSize diffSize = QSize(diffWidth + leftMargin + rightMargin + 2 * frameWidth, diffHeight + topMargin + bottomMargin + 2 * frameWidth); resize(size() + diffSize); }
This seems to work, but I don't know if my code is valid for all cases of frames, styles, etc. and I wonder if it is the correct way or "best practice" to do it. As I normally always think too complicated, there might be an easier way to achieve what I need. Any hints?
Additional question: should I use a QGraphicsPixmapItem instead of a QLabel to display the image? I need zooming and scaling.
Thanks you very much,
Ralf
@DL5EU
I was going to answer this last night but I was too tired!First with regard to the widget (
QLabel
). As @Asperamanca says, you don't want to use widgets on aQGraphicsScene
/View
if you don't have a specific use case. For displaying an image you should be usingQGraphicsPixmapItem
. That is that issue dealt with.Now for the resizing you want.
The best/easiest/most suitable is usually to use QGraphicsView::fitInView(). As @Asperamanca says this answers your question's title of "How do I resize a QGraphicsView to show the whole QGraphicsScene?" (if used to size to
QGraphicsScene::sceneRect()
). I would urge you to look at that and see if that is actually what you want/need/would be happy with.However, it does not address your phrase "i.e. the window should either grow or shrink, depending on the current size of the scene.". I take that mean you want the size of the gfx view, or the window it is in, to change to fit the scene size exactly. That would keep the scale at 1:1 (you would probably have to add scrollbars if the scene size can be bigger than some maximum window size). To achieve that you would need to alter the view and/or parent window's actual size. Something like your code. I don't know about frame borders etc.
I would suggest it is "unusual" to resize your whole application's main (or other) window as a scene grows or shrinks. Like I said, you will also have problems if the scene gets large (not to mention, if it gets very small too!). Consider whether
fitInView()
isn't a better solution. It works by changing the scale of the view (i.e. "zooming" in or out), as well as possibly adding scrollbars to the view.UPDATE
You have responded to @Asperamanca withAs far as fitInView is concerned, if I am right it scales the content so that it fits into the view but this is the opposite of what I am looking for.
Indeed, as I wrote that is exactly what it does. If you really don't want that, and want to change your actual window size, you must pursue something like your code and use that to resize parents. I don't think I have seen a (graphics or otherwise) application which behaves like this.
-
@JonB
When I start the application, the main window size is 800x600 pixels (current configuration). When I load an image, it might be a screenshot of 1024*600 pixels coming from my spectrum analyser (too big to be shown in an 800x600 window, so scrollbars are displayed) or it might as well have only 512x349 pixels coming from an old oscilloscope (too small to fit in the window, so white space is shown around the image).That is the reason why one idea was to resize the window every time a new image is shown. I could as well use "fitInView" and scale the image depending on the current view size. Or just accept what Qt does by default, perhaps not the worst solution. I don't have experience with other applications like this. How would you handle this? The Qt examples often handle only simple cases.
-
@JonB
When I start the application, the main window size is 800x600 pixels (current configuration). When I load an image, it might be a screenshot of 1024*600 pixels coming from my spectrum analyser (too big to be shown in an 800x600 window, so scrollbars are displayed) or it might as well have only 512x349 pixels coming from an old oscilloscope (too small to fit in the window, so white space is shown around the image).That is the reason why one idea was to resize the window every time a new image is shown. I could as well use "fitInView" and scale the image depending on the current view size. Or just accept what Qt does by default, perhaps not the worst solution. I don't have experience with other applications like this. How would you handle this? The Qt examples often handle only simple cases.
@DL5EU
(I think) I would allow the user to resize the main window, rather than my program altering it dynamically at runtime. (If you show your pictures in their own popup window each time that's different and you could size that as you create it for each picture, but that's different.) I would usefitInView()
if you are happy shrinking/growing images to fit the viewport, or stick to 1:1 scale and scrollbars if you want to maintain exact resolution. But I am not the greatest UI designer (I regard end-users as irritating but a necessary evil) and I don't know what your application requires, so it's up to you. -
I would also suggest that growing the whole window should, at most, be an option the user can choose. Also, can the user manually resize the window, and if so, would that stop growing it?
But more generally: From what you describe, I believe you would be better served using QtQuick and QML. It can do all you need in an integrated way, it could automatically grow your windows should you want that (QQuickView::SizeViewToRootObject), it offers GPU-accelerated rendering using different backends (DirectX, OpenGL, Vulcan, Metal) without you needing to worry about it.
A number of examples are deployed together with Qt, so you can play around a little and see whether it appeals to you. -
@Asperamanca
I agree that resizing the main window automatically should be an option that the user can choose.However, after some more hours of searching and thinking I have found out that it is in fact very easy to achieve exactly what I want:
void MainWindow::fitWindow() { QSize viewSize = ui->graphicsView->viewport()->size(); resize(size() + (m_image.size() - viewSize)); }
I have also found out how to scale the displayed image by using QTransform. Not being an experienced Qt programmer makes me spending more time searching for a solution than necessary :-)
-
-
@Asperamanca
I agree that resizing the main window automatically should be an option that the user can choose.However, after some more hours of searching and thinking I have found out that it is in fact very easy to achieve exactly what I want:
void MainWindow::fitWindow() { QSize viewSize = ui->graphicsView->viewport()->size(); resize(size() + (m_image.size() - viewSize)); }
I have also found out how to scale the displayed image by using QTransform. Not being an experienced Qt programmer makes me spending more time searching for a solution than necessary :-)
@DL5EU
Yes to yourfitWindow()
code, if it does indeed give what you want.QTransform
lest you do a variety of transformations, but you have to look up what eachm...
variable controls. For just scaling note there is QGraphicsItem::setScale(qreal factor), which could be applied to theQGraphicsPixmapItem
you should now be using for your image item. -
@JonB
Thank you for the hint! Scaling only one item is probably what I need if I have more than one graphics object on the scene and I don't want to scale them all. I will try that. As I said: not being an expert... :-)@DL5EU
There is aQTransfrom
for eachQGraphicsItem
. I think there is one for the scene as a whole, but that's different. Calling a directQGraphicsItem::setScale(qreal factor)
affects only that gfx item. I believe that (as well as dedicated "rotate" or "translate" etc. methods) is just a shortcut/simpler way of manipulating them...
members of the transform, it just addresses the necessary member(s) for you, with a more helpful name. -
@JonB
When I call QGraphicsPixmapItem::setScale(), the item is scaled as expected, but I was not able to find out how I can get the size of the scaled item. boundingRect() remains the same (seems logical as scaling takes place during display).I would like to adjust the scrollbars or adapt the scene size (whatever is necessary) to make the scrollbars appear as soon as a part of the scaled image is invisible (outside of the view/viewport).
Do you know a helpful tutorial that explains all this?
-
@JonB
When I call QGraphicsPixmapItem::setScale(), the item is scaled as expected, but I was not able to find out how I can get the size of the scaled item. boundingRect() remains the same (seems logical as scaling takes place during display).I would like to adjust the scrollbars or adapt the scene size (whatever is necessary) to make the scrollbars appear as soon as a part of the scaled image is invisible (outside of the view/viewport).
Do you know a helpful tutorial that explains all this?
@DL5EU
I don't know the answer to your questions. I would play with whatever you are doing to see what is going on. A clear, minimal example of what you are trying/want/doesn't work posted here or possibly on stackoverflow might get you an answer. You could even try in ChatGPT, not that I would normally recommend it! Have a play withQGraphicsView
methods likecenterOn
,ensureVisible()
andfitInView()
; check how they treat a scaledQGraphicsItem
. I would expect that if part of a (scaled or not) image/item it outside of the view/viewport that would cause scrollbars to appear automatically, unless scaled causes a problem. -
@JonB
When I call QGraphicsPixmapItem::setScale(), the item is scaled as expected, but I was not able to find out how I can get the size of the scaled item. boundingRect() remains the same (seems logical as scaling takes place during display).I would like to adjust the scrollbars or adapt the scene size (whatever is necessary) to make the scrollbars appear as soon as a part of the scaled image is invisible (outside of the view/viewport).
Do you know a helpful tutorial that explains all this?
@DL5EU See documentation of QGraphicsItem::setScale:
The scale is combined with the item's rotation(), transform() and transformations() to map the item's coordinate system to the parent item.
So in order to see the actual size, you can use
auto scaledRectSize = item->mapToParent(item->boundingRect()).boundingRect().size();
- Take the item's boundingRect (in item coordinates)
- Map the rectangle to parent coordinates
- Since the result is a QPolygonF (after all, rotations could be involved in the coordinate transformation), we take QPolygonF's boundingRect (since we know no rotation is actually involved)
- Then we take it's size
Recommended reading: https://doc.qt.io/qt-6/graphicsview.html#the-graphics-view-coordinate-system