I am making an app using Qt (currently 4.8) which displays a literal map from a large number of QGraphicsScene items. I would like to annotate the view with a scale. My requirement for the scale is that it is permanently fixed w.r.t the viewport widget. It needs to be updated whenever the view scale changes (zoom in, etc). There are other possible overlay items as well (compass, etc) so I'd prefer a generic solution.
I have looked at earlier questions around this which suggest:
using the ItemIgnoresTransform
using an overlay pixmap.
I tried IgnoresTransform but that way didn't work right: I couldn't figure out how to fix it in place in (say) the bottom corner of the viewport and was having some difficulty getting the text and lines always displaying in the correct size.
I scrapped that and subclassed QGraphicsView, adding an overlay pixmap by reimplementing the paintEvent (calls original one, then paints the overlay pixmap on top), and an alignment option to indicate where it goes. Coding some pixmap paint code produces a usable scale on the view. Yay! ... but it doesn't work with scrolls - I get "shattered" renderings of the scale all over, or sometimes no scale at all. I think this is because QGraphicsView::scrollViewportBy() uses viewport()->scroll() so I wondered if switching to ViewportSmartUpdate would help, but it doesn't help enough. I'd prefer not to switch to ViewportFullUpdate as that would likely slow the app down too much (there are millions of items in the scene and that would require a full repaint just to move around).
So. Any ideas from here? Would adapting my pixmap code to write to a new mostly-transparent Widget that is overlaid on the viewport be a better way?
Thanks for any help...
Though it may not be the best way of doing this, in the past I've added custom widgets to the window that holds the QGraphicsView / QGraphicsScene, which have the same graphic style as the QGraphicObjects in the scene. When the view is then used to move objects in the scene, or the scene itself, the items on the window remain in the same place.
Hope that helps.
Related
This is a sequel to another question here in which I was not precise while describing my goal.
As mentioned in the linked question, I wish to save a QML which is embedded in a QQuickWidget and it is larger than the window size. The QQuickWindow grabWindow() method captures only the window area and hence I tried the following code after I visually displayed it:
QQuickWidget* content..
content->setSource(QUrl("qml:/main.qml"));
QPixmap *pm = content->grab(QRect(QPoint(0,0),QSize(-1,-1));
pm->save("someFilename.png", 0, 100);
So, it is definitely not the issue of saving image after rendering. The used QML code is just a plain Rectangle. The proposed solutions in the previous question only grabs content falling within the window.
Any suggestions? Thanks! :)
Addendum:
I have tried the following but didn't work:
QImage paintdev(largeWidth, largeHeight, QImage:Format_RBG32);
content->render(paintdev, QPoint(0,0), QRegion(QRect(0,0,largeWidth, largeHeight), QWidget::DrawChildren);
paintdev.save(fileName, 0, 100);
This should by logic solve the issue of window size since there is no window. Any comments?
Ok, so I solved it by manually shifting the QML by the window height and saving all the images from the window captures and collate it to form the original image.
Not too much work though, but I am still mystified by the QWidget render() method which did not work.
Thanks for all the replies!
If your QML content is larger than the window size, the part that is out of screen is not drawn. Hence, there is no way to capture something out of screen, unless you use 2 monitors and extend view. This last approach would work.
I'm making fractal creator software. So I need one scene per fractal and these scenes need to be "layered" because of zooming rubber band.
I've already tried to write it the "widget" way, so I had one custom widget called "canvas". In my canvas class I overrided paintEvent, and inside this event I rendered current fractal. Everytime when somebody clicked to menu with another fractal, I called update() method and new fractal was rendered. To zooming I used overriding of mouse events and update() of canvas. At the first time I repainted the whole canvas, but it was very very slow. After that I repainted only the part under the rubber band, but still slow when I'd like to select some bigger area and other problems with repainting.
So I've looked for another way to do it. Layers. I've found the QStackedWidget, but I didn't find way how to make visible both of my layers and the top one to be transparent. After that, I've found QGraphicsScene and this seems to be the best way to do it. But I don't know the correct procedure to do it. Below are two procedures I'm thinking about:
Create QGraphicsView
Instead of the widget, the canvas will be QGraphicsScene
I'll override some QGraphicsScene event (but I don't know which one - drawItems() is obsolete and override update() seems wrong to me, but maybe...)
When other fractal will be chosen, I'll repaint canvas by calling update() the same way as in my "widget" solution
In the foreground layer will be zooming rubber band
or:
Create QGraphicsView
Instead of the widget, the canvas will be QGraphicsScene
Every fractal will be the child of QGraphicsItem
When other fractal will be chosen, I'll remove the old one fractal
item and replace it by new one and probably call invalidate()
In the foreground layer will be zooming rubber band - I think, that
it's common behaviour of the QGraphicsScene isn't it?
Is one of my reasonings correct? Do you suggest anything else? Fractals are complicated in the calculations and It's very important to repaint only if it is necessary. Could you help me, please?
Thank you :-)
Edit: "zooming rubber band" explanation:
I'm sorry for my expression "zooming rubber band". It means scale (zoom) the area below the selection made by the rubber band - zooming the same way as in Photoshop CS5 (for example). And I'd like to know what part of the scene is repainted while selecting this way. If there is repainted whole scene, or the part of the scene below selected area, or there is nothing repainted and rubber band selection is done in separate layer.
I hope my explanation helped :-).
In Qt, a QGraphicsScene can be thought of as a world of items, with a QGraphicsView as a window into that world. Therefore, you should be adding items to the QGraphicsScene, based on QGraphicsItem (or QGraphicsObject if you want signals and slots).
In your situation, I'd create a Fractal class that inherits from QGraphicsItem and add that to the scene. Ensure to override the necessary pure virtual functions such as boundingRect and paint.
Do not calculate the fractal code in the paint function. I suggest the Fractal class stores a QPixmap (or QImage if you're drawing at the pixel level) and render the fractal to this. Then perodically, in the paint function, the Fractal class would render the contents of the QPixmap with a call to painter->drawImage or painter->drawPixmap; whichever is relevant in this case.
As for zooming, your Fractal class can then response to being scaled, appropriately changing the rendering on the internal representation.
I'm trying to create a QTableView that can be zoomed in and out like in Excel.
A similar question was asked here: Zooming function on a QWidget
However, I'm subclassing the QTableView in PyQt and not C so reimplementing the entire PaintEvent method is a bit evolved. The source code for that is a bit complex: https://qt.gitorious.org/qt/tiittane-qt/source/bdd4a9149789f60974603e1f7621d51378f0a108:src/gui/itemviews/qtableview.cpp#L1282
I'm looking to see if there are any other viable options to have a zoom able TableView. My first attempt was by setting the font size then realized each column and row widths would have to scale as well which can become slow. Then realized changing the font would change the print. It didn't seems like an elegant solution. Changing the scale of the painter before painting seems like the more elegant solution but would have to re-implement and translate quite a bit of code to python to do so. I'm wondering if there are any other hooks to get this done.
Thanks
If you can use QTableWidget instead then you can create a QGraphicsScene and add it to that. Then you easily control the scale of the widget within.
If you want vertical and horizontal headers always visible I think you will have to turn off the table's scrollbars (which would end up zoomed, probably not what you want anyways) and have the scrollbars part of the panel that contains the graphics scene (probably panel would be a QAbstractScrollArea with 4 cells in layout: one cell for scene, one for horiz scrollbar, one for vert scrollbar, and one for the corner maybe empty), and connect them to the table's scroll behavior.
There is no built-in method to zoom on a view.
The simplest way to separate the size of the font on the screen, versus the size of the font saved or printed, is to basically have two fonts. One to be displayed on the screen you can call 'zoom', versus the other to be saved/printed and call that 'font size'.
Note this answer is cut and pasted from the same question:
Zooming a view in PyQt?
I am building a node graph in a QGraphicsView and I am currently implementing panning.
I used the following question "how to pan images in QGraphicsView" to start but the panning is limited by the scrollbar range.
I also tried the translate method but it gives the same result. The view is limited to a certain rectangle.
I would like to pan without limits, the graph can becomes quite large and it is useful to be able to work in different area of the scene (one graph here, another graph over there, etc).
If you take a look at this video, at the 3 minute mark you'll see the demonstration panning the screen. The application here is one I developed and although it doesn't show it, the real estate of the board appears limitless when panning.
What I did for this was to create a QGraphicsScene of 32000 x 32000 and start the application with the view at the centre of the QGraphicsScene. The test team spent ages trying to pan to the edge of the graphics scene and everyone gave up before getting there - perhaps the scene could have been smaller!
The scroll bar policies are set to off and translation is done by moving the QGraphicsView via its translate function, passing in the delta of either touch, or mouse movement that is applied in the mouseMoveEvent.
Done this way, you need not worry about exceeding the scroll bar range and there was no problem creating a very large QGraphicsScene as it's just a coordinate space.
I came across the same issue. However, setting the scene to something big and leaving it I do not think is the best option. I have developed a dynamic way of changing the scene size so it lets you move freely. You can find it in this other stack overflow answer.
You want to plot graphs. Try out this Qt library - QCustomPlot , it will save you hours of hard work.
I'm developing a Flex 2 application, and I noticed that part of the library which is responsible for moving GUI windows (TitleWindows) around when you drag them with the mouse gets confused if there is a clickable (buttonMode = true) sprite beneath them. When I say confused, I mean that the window is moved around normally for a while, but then at some point "jumps" into the upper left corner of the flash app, and makes very minor movement there. Then at some other point it jumps back. It is more difficult to explain than to experience, so please go and see for yourself. Here's how to reproduce the problem:
Go to http://www.panocast.com
In the left sidebar, choose "Real Estate"
Just below the bottom right corner of the flash window, choose "high res" by clicking on the rightmost icon.
When (part of) the video loads, click on the staircase. A TitleWindow will pop up.
Try dragging it around the screen. When the mouse cursor is moved above one of the clickable areas (like the staircase), the window is misplaced.
(Sorry, but can't give you a direct link, part of the page is generated dynamically.)
(What's makes the problem even more interesting is that for me, in "low res" mode, the problem does not occur! There is very little difference between the various modes.) I would really appreciate if someone told me what was going on here and how it can be fixed.
I'm not sure if it matters, but the underlying sprite is actually not just plain sprite, rather it is a Papervision3D renderer object with some 3D elements in it. I'm telling this because it is possible that the incorrect mouse coordinates somehow come from the texture UV mapped on the clickable objects.
I've managed to replicate this on the low res mode as well, so I don't think it's related to the resolution.
This looks to be because the MouseEvent is being handled by the TitleWindow AND the Papervision3D window. Perhaps you need to force stopImmediatePropagation() on one or the other? Or maybe switch off the MouseEvent handling for the Pv3D window when the TitleWindow pops up?
That's a tough one to debug without some source; something's apparently calling either move() or setting x and y properties on that TitleWindow and scheduling it be moved.
When I first read the post, it "smelled" like maybe a rotation miscalculation somewhere (using Math.atan vs. Math.atan2 can sometimes have that kind of effect), so you're right, it could have something to do with PaperVision, assuming you're not using Math.atan or setting rotation properties yourself anywhere. Just thought I'd mention it, though it's probably not happening in your case. You never know, though. ;)
More likely the LayoutManager is moving the component in response to a property change on the component. The Flex docs explain that in addition to setting its x and y properties, and explicit calls to move(), a UIComponent's move event can also be triggered when any of the following other properties change:
minWidth
minHeight
maxWidth
maxHeight
explicitWidth
explicitHeight
PaperVision or no, maybe that info might help you isolate the source of the move. Good luck.
I got this figured out. Apparently, this is a Papervision3D problem. There is a class deep inside Papervision3D called VirtualMouse, which is supposed to generate MouseEvents programmatically. This happens, for example, when the user interacts with any of the interactive objects on stage, e.g., a Plane with an interactive material on it (as in my case).
The problem is that the x and y coordinates of the generated event represent texture UV coordinates (just as I suspected) and not real world screen coordinates. When a TitleWindow (or any Panel object) is dragged, a "mouseMove" handler (among others) is added to the SystemManager, which then uses the stageX and stageY properties of the event object to determine the new position of the window. Unfortunately for VirtualMouse's mouse events, these are invalid, since the original x,y coordinates, which are probably used to determine the global stage coordinates are, as I said, not screen coordinates.
Honestly, I'm still unsure whether the events dispatched by VirtualMouse are used anywhere within Papervision3D itself, or they are just offered for convenience, but they sure make it difficult to integrate a viewport into a Flex program. Assuming that such events aren't necessary for PV3D itself, there is a one-liner fix for my problem, which must be added right after the creation of the viewport:
viewport.interactiveSceneManager.virtualMouse.
disableEvent(MouseEvent.MOUSE_MOVE);
BTW., there was a very similar (or rather, as it turns out, the same) bug with dragging sliders, also fixed by this line.