I'm new to Qt development so I've being trying to research a solution to a user interface I need to design. My project is to simulate players in an online game moving around a global map. To represent the map I need to display a 2D grid, with each space in the grid representing a region of a map. I then need to display the location of each player in the game. The back-end is all fully working, with the map implemented as a 2D array. I'm just stuck on how to display the grid.
The research I have done has led me to believe a QGraphicsView is the best way to do this, but I can't seem to find a tutorial relevant to what I need. If anyone has any tips on how to implement this it would be much appreciated.
Thanks, Dan
A 2D Grid is nothing more than a set of horizontal and vertical lines. Suppose you have a 500x500 map and you want to draw a grid where the distance between the lines in both directions is 50. The sample code that follows shows you how you can achieve it.
// create a scene and add it your view
QGraphicsScene* scene = new QGraphicsScene;
ui->view->setScene(scene);
// Add the vertical lines first, paint them red
for (int x=0; x<=500; x+=50)
scene->addLine(x,0,x,500, QPen(Qt::red));
// Now add the horizontal lines, paint them green
for (int y=0; y<=500; y+=50)
scene->addLine(0,y,500,y, QPen(Qt::green));
// Fit the view in the scene's bounding rect
ui->view->fitInView(scene->itemsVBoundingRect());
You should check the QGraphicsView and the QGraphicsScene documentation as well as the corresponding examples. Also you can watch the graphics view training videos or some graphics view related videos from the Qt developer days.
Well if you have a constant grid size or even a limited number of grid sizes what i like to do is to draw a grid block in gimp or any other program and then set that as the background brush (draw only bottom and right side of the block) qt will repeat the image and will give you a full grid. I think this is good for performance too.
This is the grid image i used in one of my programs it's 10x10 pixels.
Then call QGraphicsScene setBackgroundBrush as the follwing:
scene->setBackgroundBrush(QBrush(QPixmap(":/grid/grid10.png")));
The more native way is this:
scene = self.getScene() # Your scene.
brush = QBrush()
brush.setColor(QColor('#999'))
brush.setStyle(Qt.CrossPattern) # Grid pattern.
scene.setBackgroundBrush(brush)
borderColor = Qt.black
fillColor = QColor('#DDD')
rect = QRectF(0.0, 0.0, 1280, 720) # Screen res or whatever.
scene.addRect(rect,borderColor,fillColor) # Rectangle for color.
scene.addRect(rect,borderColor,brush) # Rectangle for grid.
Sorry by PyQt...
Suppose a scene is set to the graphicsview then simply below one line will show the grid.
ui->graphicsView->scene()->setBackgroundBrush(Qt::CrossPattern);
There several other values can be passed for ex: Qt::Dense7Pattern
These are members of enum BrushStyle, just click on any used value in Qt creator and it will take you to the enum declaration where you can see all other possible values.
PS:
A scene can be set like this:
ui->graphicsView->setScene(new QGraphicsScene());
Related
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.
I have this embedded Qt application that uses the QGraphics framework to display a web view.
The dimensions of the web view are 1280*720 pixels, and the QGraphicsView is set to render the scene at these coordinates (0,0, 1280x720).
I'm trying to add a loading indicator on the top right corner (at 1100,50), which is a simple PNG image that I rotate every now and then using a QTimeLine.
Code looks like this (I found the transformation trick on the internet):
// loading_indic initialization:
QGraphicsPixmapItem *loading_indic =
new QGraphicsPixmapItem( QPixmap("./resources/loading_64.png") );
loading_indic->setPos(QPoint(1100.0,50.0));
QTimeLine timeline = new QTimeLine(1000);
timeline->setFrameRange(0,steps);
connect(timeline, SIGNAL(valueChanged(qreal)), this, SLOT(updateStep(qreal)));
timeline->start();
// called at each step of a QTimeLine:
void updateStep(qreal step) {
QTransform transformation = QTransform()
// place coordinate system to the center of the image
.translate( width/2.0, height/2.0)
// rotate the image in this new coordinate system
.rotate(new_angle)
// replace the coordinate system to the original
.translate( -width/2.0, -height/2.0);
loading_indic->setTransform(transformation);
}
Now, my problem is that when doing this, it looks like the WebView is translated as well, resulting in everything being displayed in the center of the screen.
Result looks like this:
The webview is supposed to fill the screen, and the loading indicator should be on top right...
My scene contains only two items:
Scene
|
\____ QGraphicsWebView
\____ QGraphicsPixmapItem // loading indicator
What am I doing wrong here?
Solved my problem..
I don't know why, but it looks like adding this PNG item to the scene was screwing up with the scene's rectangle.
Doing this:
_scene.addItem(loading_indic);
loading_indic->setPos(1100.0, 50.0);
_scene.setSceneRect(0.0,0.0,1280.0,720.0); // resets the scene's rectangle ?!
loading_indic->startAnimation();
solved the problem. Now my items are correctly placed on screen.
If somebody has an explanation to this, I'll gladly accept his answer.
I've encountered a problem drawing SFML Text. In my application, I use views as a sort of coordinate system for my application. Thus, a typical view would be 10 x 10 or 20 x 20. All my normal drawing functions work fine, when drawing primitives and lines, etc., and the relevant code doesn't have to know about the coordinate system.
However, when I tried to draw text to the screen, I found that it appeared gigantic. When I reduced the font size drastically, it appeared extremely blurry and pixellated, as if it were trying to render to GIANT pixels that are 1x1 in my view.
Is there a way to draw text with a standard font size, in a way that it won't be affected by the view? Ideally, my text would width/size-wise on any view? How can I accomplish this?
Thanks for any input!
(P.S., I'm using SFML 2.0, for reference)
You can set another view (i.e. apply OpenGL transformation) and render text after it. Here is an example from sfml tutorial:
sf::View View(sf::FloatRect(600, 700, 1400, 1300));
App.SetView(View);
// Draw the game...
App.SetView(App.GetDefaultView());
// Draw the interface...
So, this is my problem: I have this very big image, and I want to show only a specific part of it. After the user pressing a specific key I want the image to move, showing another part of it. The transition from one part of the image to another have to be smooth, animated.
I tried using a QLabel to show the image but it always shows the center of the image, and I do not really know how to make the animation. What would you guys suggest?
Interesting question. Here is something I just tested and seems to work.
Add a QGraphicsView with dimensions the dimensions of the part of the image you want to display, eg 100x100. Create a QGraphicsScene and add it to the view:
QGraphicsScene* pScene = new QGraphicsScene(this);
ui->graphicsView->setScene(pScene);
Now add your image into the scene. In my case I has an image in my resource file. The trick is to set the sceneRect to the position you want to display. I wanted to display an 100x100 part of the image starting from 0,300 :
pItem = pScene->addPixmap(QPixmap::fromImage(QImage(":/photos/image")));
pScene->setSceneRect(0,300,100,100);
In order to test the smooth moving I added a button which when clicked is triggering a slot called move. This slot simply updates the sceneRect. In my simple example I just move the image 100 pixels right. In a real world scenario you could also move it diagonally or vertically and check the image limits.
void move()
{
for (unsigned i=currentX; i<currentX + 100; i++)
{
ui->graphicsView->scene()->setSceneRect(i,300,100,100);
qApp->processEvents();
}
currentX += 100;
}
Notice the currentX variable. It is nothing more than the last image position. Also we must call the processEvents in order to "see" the image moving smoothly.
You could use QPixmap::copy( int x, int y, int width, int height ) to copy a region of the image and display that.
Few options:
Try using a Q3CanvasSprite (within in a Q3Canvas). It is designed more for splitting one image into multiple ones but not for animating between them. You could try abusing it and declaring (say) 100 frames (10 per digit, which would be used as animation steps) or just use the move() method.
Try QGraphicsPixmapItem::setOffset() (within a QGraphicsScene). This might be overkill as QGraphicsScene is made for large number of images).
I'm not sure, but maybe this can be done with QStateMachine and QAbstractAnimation.
I am using QGraphicsScene/QGraphicsView pair in my application.
I had subclassed them for my purpose. The code snippet that generate the pair is below:
itsScene = new QGraphicsScene;
itsView = new QGraphicsView;
itsView->setParent(itsCanvas);
itsView->setGeometry(20,20,1700,720);
itsView->setBackgroundBrush(Qt::black);
itsView->setAlignment(Qt::AlignTop);
itsView->setScene(itsScene);
After adding some widgets into QGraphicsScene my application final UI snapshot is below:
Here my question is why there is some free space above the picture? What may cause this?
I am using some negative coordinates for my widgets. Is it related with that?
The negative coordinates may be the cause. QGraphicsScene calculates its bounding rect from combining the bounds of all items in it.
If you know your scene bounds, call setSceneRect to fix it down to a known rect. This way graphics items placed out of the bound will not cause the scene to expand beyond what you want.