I have a QGraphicsView inside a Horizontal Layout. The QGraphicsScene contained in the QGraphics scene is pretty large (thousands of pixels), so scroll bars show up by default to allow me to scroll through the view.
Every time I update the scene inside the graphics scene, I delete the old scene, make a new one, populate it with objects, and call view->setScene(scene). When I do this, however, the scroll bars default to the top left corner of the QGraphicsView, instead of staying where they were at.
I tried saving the QGraphicsScene::sceneRect before destroying the scene, then doing a setSceneRect() afterwards to restore it, but that doesn't work since that only affects the scene itself, not the QGraphicsView that's displaying it. I also tried to do view->verticalScrollBar()->setValue(), but that doesn't appear to affect the scroll bars at all.
How can I access the scroll bars that are created for me by default, and set them to a value that was stored earlier?
Fixed by not deleting previous scene until after the new one is set.
e.g.
QGraphicsScene *previousScene = scene;
scene = new QGraphicsScene(0, 0, levelPlist.value("level_width").toInt(), levelPlist.value("level_height").toInt());
// config scene
QGraphicsView *view = ui->graphicsView;
view->setScene(scene);
delete previousScene;
Before I give a possible solution, I would highly recommend an approach that doesn't involve destroying your scene. If you're just clear()'ing your scene, you should be able to call setSceneRect() before clearing it, which will prevent the scene from automatically resizing its rect. When it's repopulated you can call setSceneRect() with a default-constructed QRect to give it control over the size of the rect once again.
If you really need to save/restore the scroll bars as stated, the following approach has worked for me in the past:
I've had to do something similar (for different reasons), and the best solution I've come up involves doing something like this:
Before clearing the scene, save off the positions of your vertical and horizontal scrollbars
Before reconstructing the scene, connect to the QScrollBar::rangeChanged() signal with a queued signal/slot connection. This signal should get fired after you've repopulated your scene, and it indicates that the scroll bars have been reconfigured by the view.
In your slot, disconnect from the rangeChanged() signal, and restore the scroll bar positions
Note that this is pretty much guaranteed to cause some flicker/jumpiness.
Related
I'm trying to draw a stacked bar graph on Qt, i followed the tutorial on the documentation but when i try to put the graph inside a QGraphicsView i get a lo of unused space and can't manage to make the scene fit the view.
My code is the same as the documentation one plus the following to make the scene show up in the view:
QGraphicsScene *scene = new QGraphicsScene(this);
scene->addWidget(chartView);
ui->view->setScene(scene);
And what i get is this
As you can see there is a lot of unused space and it makes the text disappear, i wanted to make the graph fit the view but i can't seem to find a way to do so.
I tried in many different ways using
ui->view->ensureVisible ( scene->sceneRect() );
ui->view->fitInView( scene->sceneRect(),Qt::KeepAspectRatio);
and
ui->view->setSceneRect(0,0,ui->view->frameSize().width(),ui->view->frameSize().height());
but nothing seems to work (the second solution just moves the graph to the top left)
As per the comment... the real issue is the basic sizing of chartView rather than anything to do with QGraphicsView or QGraphicsScene.
When a QWidget is added to a QGraphicsScene the resulting QGraphicsProxyWidget will generally honour the size hint and policy of that widget.
In addition, though, the QGraphicsScene will set the scene rect to the bounding rectangle of all scene items and the QGraphicsView will then position the scene according to whatever viewport anchor is in force. The end result can be visually misleading if the scene has a complex set of items or has a bounding rectangle smaller than that displayed within the GraphicsView.
So if a widget does look odd when added to a QGraphicsScene it's normally a good idea to test by just showing it as a top level window on the desktop and make sure it behaves as expected before going any further.
I have created a QGraphicsView with a scene set on it with QGraphicsRectItem in it.
[fig-1]
I want that when I double click on the green rectangle marked with thick black border, the scene gets filled with only this rectangle and all its children also get expanded in same proportion.
So, when I double click , I should see something like this in complete view:
I have handled the double click event but do not know how to handle this.
Also, when I double click again, I get back to the previous state (as shown in fig-1)
As the documentation states for QGraphicsView::fitInView
Ensures that item fits tightly inside the view, scaling the view according to aspectRatioMode.
So, assuming pView is your QGraphicsViewand thisis the rectangle you want to view, call fitInView from the overloaded double click event of QGraphicsRectItem: -
pView->fitInView(this);
Based on the comments that only the rect and its children should be visible, one option to guarantee this would be, on double-click, to create a separate scene, move the rect to the new scene and switch the view to the new scene.
Just as a QPushButton provides a default clicked() signal, I expected QScrollArea to have a sliderChanged() or similar signal. Interestingly, the QScrollBar does have such a signal.
All I would like to do is to know what part of the huge widget inside the scroll area is visible, whenever the user scrolls it.
There are many solutions, none of which seem elegant to me:
subclass QScrollArea
subclass the widget inside the scroll area, and re-implement its paint event.
create a custom veiwport, using QScrollBar
periodically poll the position of the widget inside the scroll area. This seems to be the worst solution.
Is there a way without subclassing?
There is QAbstractSlider::valueChanged() signal that is emitted when the slider value has changed, with the new slider value as argument. This will notify you as soon as you scroll your view.
WRT the second problem, neither of mentioned points necessary. You need to:
1) Get the position of inner widget (if any) related to the scroll area:
QPoint p = scrollArea->widget()->pos();
It use to be a negative coordinates if you scrolled your view down/right or null without scrolling.
2) Get the size of the visible area
QSize s = scrollArea->viewport()->size();
With these two values you can construct a QRect that will represent the visible area of your inner widget.
I'm stuck on how to approach this. I have a QGraphicsItem within a scene, and I'm passing a hover event from the scene to this child. While the move event occurs (I'm just using mouseMoveEvent with mouse tracking on), I want another QGraphicsItem to follow the cursor.
I don't need any collision detection, drag and drop, etc. Just an item that follows the cursor. The only two ways I can think of are...
While the mouse moves, draw a new QGraphicsItem at the mouse position. I would need to clear the scene, redraw everything, and draw the new position on top.
Somehow use the animation framework and whenever the mouse moves, animate the QGraphicsItem to move to the new mouse position in 1 millisecond.
I'm probably either overthinking this or unaware of another way to do this... any suggestions?
I did it like this
Create the GraphicsItem cursor which will be moved with mouse-cursor, and store its pointer somewhere (in scene subclass for instance. I have a toolset, so for me it's in one of these tools)
Set its Z-Value (QGraphicsItem::setZValue) so that the cursor will be painted above all other items in your scene
Track QGraphicsScene::mouseMoveEvent events, forward these events down to the cursor pointer, and update item's position
that's it.
I guess it corresponds to your solution 1, except you don't have to clear the scene thanks to z-value feature.
In my application I have QGraphicsScene with pixmap added and all is viewed in QGraphicsView with scrollbars off.
The program window is smaller then pixmap and when I press arrow keys or move mouse wheel the pixmap is being moved.
How do I disable that so even if the pixmap is bigger than window it won't be moved by keyboard or mouse unless I use my events for that?
(I tried to set interactive property to false but that didn't work)
I believe the easiest solution would be to set FocusPolicy of QGraphicsView to NoFocus and then process all key events in main window.
ui->graphicsView->setFocusPolicy( Qt::NoFocus );
I think the easy way is to use QGraphicsView::setSceneRect() to set the area that the view is allowed to visualize.
If you set the view's sceneRect() to the actual size of the view widget (or just slightly smaller), then it won't be able to scroll or pan outside of this rectangle.
I don't know if there's an easier way, but the moderately easy way would be to make a class that inherits your QGraphicsView class, and overrides the mouse and key press events.
The harder way would involve installing an event filter on the widget.
In my opinion, the hardest brute-force solution would be to detect the change through some sort of signal, and set it back to how it was.