I have project which display inner structure of ELF binary files. I use QGraphicsView and QGraphicsScene. So I can display blocks on graphics scene, but I need after mouse click on single block display content of this block. I am trying use function:
void MainWindow::mousePressEvent(QMouseEvent* event)
{
ui->textEdit->append(QString("x = %1, y = %2").arg(event->Pos().x()).arg(event->Pos().y()));
}
But position is tracking in whole window. Problem is, that width and height of window is dynamical and height of QGraphicsScene may be greater than window's. So I can't track single block.
I tryed some function like:
mapFromScene, mapToScene, mapFromGlobal, ... but I don't know, how theese functions works, but position what I get wasn't right.
It occurred to me that position should be calculated by scrollbar of QGraphicalView and size of window, but it's hard to calculated precisely and it's not correct solution.
Some idea, how get true position on graphic scene? Thanks for any help.
QGraphics, like other graphics frameworks, uses multiple coordinate systems and you need to consider which system you're working in when dealing with coordinates.
We can think of a QGraphicsSceneas a world, within which there are objects that are represented by QGraphicsItems ( or QGraphicsObject's. if you want them to use signals and slots).
To view an area of the world, we use one or more QGraphicsViews.
In order to map between the different coordinate systems, Qt provides the useful mapTo and mapFrom functions.
Starting at the top, with a QGraphicsView, we can convert a coordinate and use QGraphicsView::mapToScene to get the coordinate in the scene's coordinate system. From this, if an item resides at that coordinate, we can get the coordinate relative to the item, using QGraphicsItem::mapFromScene.
So, with the three levels (view, scene and item) think about which system the coordinate is in and where you want to be dealing with it.
how get true position on graphic scene?
Hopefully you should now be thinking that the coordinate you have been provided in the MainWindow is in the view's coordinate system and you can map to the scene's coordinate system.
QPointF scenePoint = mapToScene(event->Pos());
However, this is overcomplicating things and you'll find it easier to work directly with the scene and the items it contains, by overloading the item's own mouseMove/Press/Release events, which provide QGraphicsScene coordinates.
I solved this problem with make my object, which inherit QGraphicView (MyQGraphicView) and then I declared function mousePressedEvent for this object. This help me.
Related
Some background -
Say you have QGraphicsScene, and only one view, which is a 1-1 scale with the scene.
You have a QRect A, which is represents an external view of the scene, with a pre-defined pixel size.
You have a QRect A1 which is a smaller rect inside of A.
How do you translate A1 to the scene, such that it is scaled correctly (i.e. if it's 1/4 of rect A, it will occupy 1/4 of the scene), and then undo that transform to scale a rect created in the scene to fit in rect A correctly?
I can do all this brute force, but I'm wondering if there's a way using Qt's built in classes...
After looking over some examples to try and find similar uses, I realized I'm totally missing the point - I can just set A/A1 directly to the scene, and scale the view (via the totally obvious but somehow completely overlooked until now QGraphicsView::fitInView(..)) to fit the rects inside. No rect transforms necessary. Total 'duh' moment. :)
I will need to transform mouse clicks and points in the view when interacting with it, but there is a whole nice set of mapTo* mapFrom* that will handle that nicely.
TL;DR - Use fitInView()
I have a QGraphicsView on a widget which im adding a number of items to.
some of these are polygons and some are ellipses (Both QGraphicsItems) when i zoom the Graphics view
void Test::on_verticalSlider_sliderMoved(int position)
{
ui->graphicsView->scale(1.1,1.1);
}
the ellipses just get bigger and bigger but i want them to shrink so that they basically stay the same shape no matter how far zoomed in i am, so basically i want the polygons to be zoomed in on but not the points i have plotted onto these polygons...if that makes sense
dont know it makes it easier but all of the ellipse points plotted at any one time are within the same QGraphicsItemGroup so there could be a flag i can set on the entire group to do this?
anyway i would be greatful for any help with this
thanks
EDIT ------ CODE SAMPLE
item is the QGraphicsEllipseItem, and m_group_point is a QGraphicsItemGroup
item->setParentItem(m_group_point.get());
then i add the group to a scene
m_scene2->addItem(m_group_point.get());
then add that scene to the view
ui->graphicsView->setScene(m_scene2.get());
they are .get because they are shared pointers
well, ignoreTransformation indeed a proper way to go.
QGraphicsItem::ItemIgnoresTransformations: The item ignores inherited transformations (i.e., its position is still anchored to its parent, but the parent or view rotation, zoom or shear transformations are ignored). This flag is useful for keeping text label items horizontal and unscaled, so they will still be readable if the view is transformed. When set, the item's view geometry and scene geometry will be maintained separately. You must call deviceTransform() to map coordinates and detect collisions in the view. By default, this flag is disabled. This flag was introduced in Qt 4.3.
About stay in the middle, you should move ellipses within scene to proper position, so they have proper scene coordinates, and then you instruct to ignore transformation of the view, so they will ignore any zooming/rotation/etc as mentioned in documentation.
The Qt docs states this about the flag QGraphicsItem::ItemIgnoresTransformations: -
The item ignores inherited transformations (i.e., its position is
still anchored to its parent, but the parent or view rotation, zoom or
shear transformations are ignored).
Which is what you want. You've added your items to a QGraphcisItemGroup. For this, Qt help states: -
QGraphicsItemGroup ignores the ItemIgnoresTransformations flag on its
children (i.e., with respect to the geometry of the group item, the
children are treated as if they were transformable).
Reading into this, the QGraphicsItem uses the ItemIgnoreTransformations flag based on its parent, which in your case is the QGraphicsGroup, but this class ignores the flag on its children, which is likely to be causing the issue you're seeing.
Therefore, do not set the flag on the parent group, but on its children.
Try setting the ItemIgnoresTransformations flag on the objects that you want to keep constant-sized.
I was wondering whether the following can be easily implemented in OpenGL:
divide window into multiple panels
put each panel into place using normalized coordinates (0-1) so that if the window gets re-sized, the panel will stay in the correct position
draw directly into one of the previously defined panels
The result should look something like the following picture:
So far I've been drawing my objects directly into the window, offsetting the coordinates with the respective value for each panel. However this doesn't feel like the right way of doing things. Any suggestions and example code from experienced developers is highly appreciated!
EDIT: What I've read so far, using glScissor or glViewport might be two ways of accomplishing what I want, however I don't know what the pros and cons are for going either route (your insights are very welcome!). Additionally, I would really prefer to define some kind of panel, return its handle and just draw into that.
If you use glScissor() you just define the clipping rectangle (i.e. where to draw). This might be everything you need.
With glViewport() you essentially call glScissor() behind the scenes as well as changing how coordinates are mapped to screen space.
If you want to limit drawing to one of the panels (without using local coordinates), use glScissor(). Otherwise, use glViewport().
As for the panel, I'd probably use some abstract base class:
class Panel {
// ...
virtual void OnDraw(void) = 0;
void Draw(void) {
glViewport(x, y, w, h);
OnDraw();
glViewport(0, 0, parent_w, parent_h);
}
}
I am trying to display several images in the MainWindow and have the ability to perform certain actions when these images receive mousePressEvent.
For this, I thought I would create a new class, derived from QWidget, which had a QImage private member. With this, I as able to override the paintEvent and the mousePressEvent to both display the image and catch mouse input.
However, the problem is that the image drawn in the MainWindow has the widget size and its form (rectangular), and the image does not possess the same form (not even a regular form). This causes some problems because I am able to catch the mousePressEvent on parts that do not belong to my Image, but do belong to the Widget area.
Does anyone have any ideas on how to solve this?
I am still very new to QT, so don't please don't mind any huge mistake :)
Update:
Ok, I tried another approach.
I now have a graphicView on my MainWindow, with a graphicScene linked to it. To this scene, I added some graphicItems.
To implement my graphicItems I had to overload the boundingRect and paint method. As far as the documentation goes, my understanding is that QT uses shape method (which calls boundingRect) to determine the shape of the object to draw. This could possibly be the solution to my problem, or at least one solution to improve the accuracy of the drawn shape.
As of now, i am returning a square with my image size in boundingRect. Does anyone know how I can "adapt" the shape to my image (which resembles a cylinder)?
If you want to use QWidget you should take look at the mask concept to refine the space occupied by your images (be careful about the performance of this implementation).
If you decide to go with the QGraphicsItems you can re-implement the virtual method QPainterPath QGraphicsItem::shape () const to return a path representing as well as possible the boundary of your images.
The Qt documentation says :
The shape is used for many things, including collision detection, hit tests, and for the QGraphicsScene::items() functions.
I encourage you to go with the QGraphics system, it's sounds more appropriate in your case.
What I understand is that you want to shrink/expand/transform your image to fit a custom form (like a cylinder).
What I suggest is to use the OpenGL module QGLWidget and to map each coordinate of your rectangular image (0,0) (0,1) (1,0) (1,1) to a custom form created in OpenGL.
It will act as a texture mapping.
Edit: you can still catch mouse input in OpenGL
I subclassed QGraphicsItem and reimplemented paint.
In paint I wrote something like this for labeling the item:
painter->drawText("Test",10,40);
After some time I think It may be useful to handle labeling with seperate item. So I wrote something like this.
QGraphicsTextItem *label = new QGraphicsTextItem("TEST",this);
setPos(10,40);
But two "TEST" drawing do not appear in the same place on screen. I guess difference may be related with item coordinates - scene coordinates. I tried all mapFrom... and mapTo... combinations inside QGraphicsItem interface but no progress. I want to drawings to appear in the same place on screen.
What I miss?
I assume that you are using the same font size and type in both cases. If the difference in position is very small the reason can be the QGraphicTextItem is using some padding for the text it contains. I would try to use QGraphicsSimpleTextItem that is not going to add fancy stuff internally and see if you still have the same problem. The coordinates system is the same one if you use painter or setPost so that is not the problem. If this doesn't help I will suggest to specify the same rect for both to avoid Qt adding it owns separation spaces.