How to do overlapping custom items in qt? - c++

Like in the title. I have few objects, and i wanted to set graphics to them and overlap each other. I'm setting 18 squares (terrains). I'm adding to this terrains graphics:
setPixmap(QPixmap(":Graphics/Terrain")); like so.
Then i want to add frame to it. I have 4 files of format .png, every one is 200x200px. First one is terrain.png - it's a green square, second and third are frames, about 20px wide with a transparent center (with alpha channel) and the last .png file is City.png, in left corner of City.png, there is a small graphics, rest is transparent (alpha channel). When i'm trying to set the frame by the same way as terrain.png:
setPixmap(QPixmap(":Graphics/Frame"));
it covers all of the terrain.png graphics, despite the fact that the center of frame1.png is transparent.
Furthermore i want to add some others object like for example City.png. How to do it? Mayby exists some diffrent way except using this setPixmap function ?

The setPixmap approach seems to work quite nicely, when used like this:
QGraphicsScene* scene = new QGraphicsScene();
QGraphicsPixmapItem* itemA = new QGraphicsPixmapItem();
itemA->setPixmap(QPixmap(":/NonTransparentImage.png"));
scene->addItem(itemA);
QGraphicsPixmapItem* itemB = new QGraphicsPixmapItem();
itemB->setPixmap(QPixmap(":/TransparentImage.png"));
scene->addItem(itemB);
QGraphicsView* view = new QGraphicsView(scene);
view->show();
As you expect, the transparent itemB overlaps itemA.

Related

How I can work with SVG files in Qt creator?

I have some problem. I need to read SVG files and place content on the QGraphicsView scene. I read documentation about QSvgWidget and QSvgRenderer, but don't understand how get all shapes from file, point coordinates, where place they and have a posibillity to change color, size and other properties. Who can suggest methods, how I can do this? Thanks in advance!
I tried create QSvgWidget and I managed draw one figure with his color, but if figures more than one, they all dye in black color, and I don't know, how I can control this.
ui->setupUi(this);
scene = new QGraphicsScene;
scene->setSceneRect(-200, -100, 400, 200);
svg_widget = new QSvgWidget(":/resources/Test3.svg");
svg_widget->renderer();
svg_rend = new QSvgRenderer;
painter = new QPainter;
svg_rend->render(painter);
svg_widget->render(painter);
scene->addWidget(svg_widget);
ui->graphicsView->setScene(scene);
I understood that after creating QSvgWidget and call renderer(), QSvgRenderer does not affect on work with SVG. Can I combine svg_rend with svg_widget? Or does it make no sense?

Qt - Image merged multiple times gets smaller, includes a "border"

I'm having trouble with Qt.
I'm writing an app that takes a PNG file from database, and allows you to apply multiple layers (each representing a different set of multiple-choice elements (also from database) for different areas of the base PNG), and displays the result within QGraphicsView Widget. However, when I merge the original image with an element, the original gets smaller and gets a "border" around it.
Examples:
Original image - http://i.imgur.com/6kI2M2y.png (sans black area)
The black element is another PNG, in which there's the element and it's surrounded by transparency.
Result after few merges - http://i.imgur.com/wx8UmmK.png
What the hell? The QGraphicsView is 520x520 and doesn't touch any other Widget. I have to get the app to be able to merge the contents of QGraphicsView multiple times without any kind of movement/resizing, as there will be lots and lots of undecided clicking between various elements and layers.
The app works like this: I have two dynamicly generated arrays of buttons with images taken from database, let's say the Base list and Elements list. When I click a button in the Base list, it's loaded into the main QGraphicsView. Now I am supposed to be able to add elements onto that loaded image by clicking buttons on the other list. I do that by getting the content of QGraphicsView widget and drawing the element using a QPainter.
void MainWindow::on_pushButton_clicked() //placeholder for merging testing
{
QPixmap element("C:\\Users\\Petersaber\\Desktop\\warstwa1.png");
QPixmap base = QPixmap::grabWidget(ui->QGraphicsView);
base= oryginal.scaled(QSize(520,520),Qt::KeepAspectRatio, Qt::SmoothTransformation); //resized to QGV
element= element.scaled(QSize(520,520),Qt::KeepAspectRatio, Qt::SmoothTransformation); //resized for QGV
QPainter painter(&base);
painter.drawPixmap(0,0,element);
//the error happens somewhere later from this point
QGraphicsScene* sceneElement = new QGraphicsScene();
QGraphicsPixmapItem* dodany = new QGraphicsPixmapItem(base);
sceneElement->addItem(dodany);
ui->QGraphicsView->setScene(sceneElement);
ui->QGraphicsView->fitInView(sceneElement->sceneRect(),Qt::KeepAspectRatio);
ui->QGraphicsView->show();
}
My guess is that for some reason it takes more than just what's INSIDE of the QGraphicsView, it also takes the 1-2 pixels around it, but why? How to prevent that? I have no idea. I'm a total newbie to Qt, I've only started two days ago.
edit: I am wrong. That's not how it works. The error happens AFTER the merging. Wtf?
It happens regardless of what size the merged images are. After every merge, two white pixels and 1px border always appear between the merged image and the QGraphicsView edge.
edit: I've discovered that the 2px margin is hard-coded into QGraphicsView::fitInView(). I have no idea how to work around that. I've found pieces of code, such as this,
QRectF removeMargin11945(const QRectF& sceneRect, const QSize& viewerSize){
const int bugMargin = 2;
const double mx = sceneRect.width()/viewerSize.width()*bugMargin;
const double my = sceneRect.height()/viewerSize.height()*bugMargin;
return sceneRect.adjusted(mx, my, -mx, -my);
}
, but I don't know how to implement that

Qt GraphicsItem transformation affects all items in scene

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.

How to show part of an image using QT?

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.

Qt GUI Development - Displaying a 2D grid using QGraphicsView

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());