Using QGraphicsView to display a local map - qt

I'm trying to use QGraphicsView in order to display a map image and draw some items on top of it. The map boundaries are in a Cartesian coordinate system, for example NE(-500,200) to SW(600,-350). I know how to map image pixels <--> my coordinate system.
I would have to achieve the following:
Add a map image to the scene and tell Qt how to map the actual image pixels to scene coordinates.
Add graphic items at their real position, e.g. (-100,200)
Doing (2) is straightforward - simply add the item to the scene. How do I achieve (1)? what should I do after I call scene->addPixmap()?
Edit - A few clarifications:
I'm mapping an indoor area of a few hundred meters
The map will change at real-time in two ways:
The map gets bigger every few seconds
The graphic items move, change colors, etc.

Put the pixmap into a QGraphicsPixmapItem and place it in the scene.
Call setScale() to map the QGraphicsPixmapItem so 1 meter maps to 1 unit in the scene coordinate. ie. setScale(0.1) if 10 pixels in the pixmap equal 1 meter.
Update the pixmap and scale of the item as needed.
Call fitInView() to zoom to the pixmap.
Place other graphic items in the scene. Treat the units of the scene coordinate as meters.
...
Profit! :)

Related

How to remove Ghost Lines drawn in qgraphicsview

I am trying to make a simple program in which I have added a qgraphics scene and in this I have added a QGraphicsRectItem. I have implemented mouse press event, paint event, bounding rect. Now I have drawn a point on one side of rectangle because there can be multiple rectangle I can drop on screen so just to differentiate between them of different color. Now I can move my rectangle inside graphics seen and can increase the size of rectangle by moving it's one side at a time. The problem that I am facing is when I trying to draw point on one side of rectangle at the time of moving it, it leaves traces on graphics scene. can I remove the ghost lines?
This happens either because your boundingRect method isn't correct, or because you forgot to call prepareGeometryChange before making changes that affect the boundingRect result. Your boundingRect needs to include space for line widths, for example; that's a common mistake.

How to get absolute coordinates with QGraphicsScene?

Let's say I have five objects, with the top-left red square and the larger black square both inserted into the scene at 0,0.
.
Later, let's say I want to move the bottom right corner of the black square to the bottom right corner of the bottom-right square. The most obvious thing to do would be:
bS.setRect( bS.rect().setBottomRight(redBR.rect().bottomRight) );
That's not exactly correct code, but the idea is simple. I would set the coordinate of the black square's bottom right corner to that of the red square's bottom right corner. Because that's where I want it to be. But in Qt, this seems to be impossible.
Because the call to redBR thinks it's at the origin. Every item thinks it's at the origin. Which means I can never know the coordinates of any item, anywhere, ever.
Is there a way to force QGraphicsScene to tell me the actual coordinates? Alternatively, is there some other graphics framework that uses real coordinates? I'm not going to insist on using Qt if there is no way to make it work.
Any help would be welcome. Please bear in mind my goal is not: "drag corner of box by manipulator". My goal is to be able to put items at coordinates whenever I want an item to be at a specific coordinate.
Edit:
Here's an example of what I mean. The big box hasn't been connected yet, so don't worry about it's coordinates. The problem is that if I don't remap the points coming out of the little box, every box believes it is at the origin. But if I do, then the y values vacillate between 0, 1, and -1.
The base class QGraphicsItem has quite some useful functionality for mapping between scene and item coordinates. One useful function is for mapping a point in item coordinates to scene coordinates:
QPointF QGraphicsItem::mapToScene(const QPointF &point)
There is however also a direct mapping available between points in different QGraphicsItems, which is probably the easiest to use here:
QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPointF &point)
which can be used to map a point in redBR to a point in bS like:
bS.rect().setBottomRight(bS.mapFromItem( &redBR, redBR.rect().bottomRight() ) )

Draw 32x32 Tiled Images in QT

I'm curious to learn how to split an image into 32x32 (or really any size) selectable tiles and display them in Qt? An example is the Tiled Map Editor (the panel in the lower right corner of the screen). I'm thinking of trying to create a 2D level editor in Qt for fun but I'm fairly new to Qt and have yet to find the answer to this specific question. Or maybe I just don't know how to phrase the question.
Add your main image to a QPixmap, then for each tile that you want to create, call the QPixmap's copy function. This allows you to specify the area that you want to copy and returns you a new QPixmap with that area.
With each QPixmap tile you create using copy, use this to create a QGraphicsPixmapItem, which you then add to the QGraphicsScene.
So, you'd do something like this: -
// assuming your source image is in your resources qrc file
QPixmap srcImage(":/images/srcImage.png");
//in a loop for x and a 2nd loop for y
// copy a section of the source image
QPixmap tileImg = srcImage.copy(x, y, w, h);
// create the tile
QGraphicsPixmapItem* pTile = new QGraphicsPixmapItem(tileImg);
// add the pTile to the scene.
Using QGraphicsPixmapItem will provide you with a tiled object that can be positioned in the scene, selected and moved about.

Drawing a pixmap using QPainter::drawPixmap in qt

I am able to paint a pixmap by using QPainter::drawPixmap, but I am having trouble with the sizing. The pixmap is being drawn onto many different scenes. Some of the scenes are very large, and some are very small. This results in the pixmap drawn being either looking very large or very small, depending on the size of the scene (or viewport, whatever its called). I need the pixmap to look the same size everytime, regardless of the dimensions of the scene it is being placed into.
Basically, I want it to work similar to drawPoint, where you can specify the length and width of the point in pixels, so the point looks the same size every time.
The following line of code is inside my paint function of the QGraphicsItem I subclassed:
painter_p->drawPixmap( pos(), MYPIXMAP );
with pos() returning the QPointF I need to draw the pixmap at.
Can't you use QGraphicsPixmapItem? It'd do exactly what you want.

How to get visible scene rectangle of QGraphicsView?

I'm displaying a map built as rectangle of QGraphicsPixmapitem items (each item stands for one map tile). Because my map is quite large (around 30 MB of PNG files) I want to be able to load pixmaps on demand only when they're visible for user in QGraphicsView and unload when they became invisible.
Is there any way to figure out visible scene rectangle?
This gives you the visible scene rectangle:
sceneRect = graphicsView.mapToScene(graphicsView.rect()).boundingRect()
In case there is a shear or rotation transformation present it gives you the bounding rectangle of the visible scene area. If you do not have such transformations (only shift or zoom) the returned rectangle is the exact scene area.
Now for your real problem of efficiently display a huge tiles map in a scene? You could load the tiles in background and first evaluate if your Qt framework isn't already optimized for big pixmap that are outside the visible range. 30 MB also doesn't sound so big that it wouldn't fit into memory.
QGraphicsView inherits the QWidget::geometry() function. You can use this to determine its location and size within its parent widget. (Outside of its constructor)
The QGrapicsScene can be larger than the QGraphicsView. The default QGraphicsView will add horizontal and vertical scroll bars to house the QGraphicsScene. I imagine you would like to do something like this:
//create a QGraphicsScene (for this example *scene) that is the size of your entire map.
QGraphicsScene *scene=new QGraphicsScene(0,0,mapWidth,mapHeight);
//create a QGraphicsView* named view that is the size of your visible area
//I'm assuming visibleHeight and visibleWidth do not change (this is your viewing window)
QGraphicsView *view=new QGraphicsView(0,0,visibleWidth,visibleHeight);
view->setScene(scene);
Have the user control the x and y position of the scene that triggers some custom signal like sceneMoved(int,int). Before you redraw the scene, call a slot to check the new position of the scene:
connect(this,SIGNAL(sceneMoved(int,int)),this,SLOT(drawScene(int,int)));
void SomeClass::drawScene(int newX, int newY){
//if you already have a pointer to the scene do this, or call
//QGraphicsView::scene();
int oldX=scene->geometry()->x();
int oldY=scene->geometry()->y();
//now that you have your oldX, oldY, newX, and newY, visibleWidth, visibleHeight
//you can determine what you need to redraw, what you need to delete, and what can stay
}
There is still a lot of if..else, but you get the point. I suggest trying to segment your map into squares the size of your visible area.

Resources