Render widgets to different positions on paintdevice - qt

With Qt I want to print multiple widgets onto a single DINA4-page.
Say I have 3 widgets that I want to place directly next to each other:
-------
| A |
-------
| B |
-------
| C |
-------
As I render them in my code below, all 3 widgets are rendered above each other at the postion of C (or even A and B are not rendered at all, I don't see it). I intended to move each widget a little bit down by the size of the last widget, to render it directly next to the last one. This seems not to work:
int lastWidgetHeight;
lastWidgetHeight= 0;
for (int i = 0; i < this->widgets.size(); i++){
Wid* e = widgets.at(i);
QWidget *printBox = e->printArea();
// define float scaleX and scaleY [...]
QPainter painter;
painter.begin(printer);
painter.scale(scaleX,scaleY);
// render and translate in y-direction:
printBox->render(&painter, QPoint(0,lastWidgetHeight)); // <-- My idea
painter.end();
lastWidgetHeight+= e->height();
}
If all 3 widgets are rendered, then how does the loop affect the position of A and B, since I create a new QPainter-Object each time?

You can set your 3 elements in a layout
QVVoxLayout *Vertical_layout;
this->getLayout()->addLayout(this->Vertical_layout);
Vertical_layout->addItem(ElementA);
Vertical_layout->addItem(ElementB);
Vertical_layout->addItem(ElementC);
and you can set the distance between them with:
Vertical_layout- >setContentsMargins(left,top,right,bottom);
With that they will always be in their place.

Related

Lines thickness changes spontaneously when scaling QGraphicsView

I am drawing lines in Qt using Graphics View framework. Since i want my picture to take the same portion of space when the window is resized, I override MainWindow::resizeEvent, so that graphics view is rescaled according to the resize event:
void MainWindow::resizeEvent(QResizeEvent *event) {
int w = event->size().width(), h = event->size().height();
int prev_w = event->oldSize().width(), prev_h = event->oldSize().height();
if (prev_w != -1) {
int s1 = std::min(prev_w, prev_h), s2 = std::min(w, h);
qreal k = (qreal)s2 / s1;
std::cerr << k << std::endl;
ui->graphicsView->scale(k, k);
}
}
However, doing so, my lines (that should have thickness of 1 pixel) sometimes have different thickness after resize. As I understand, it happens because coordinates of the objects after transforming to the GraphicsView are real, so are sometimes drawn with different number of pixels. That is unacceptable! I want lines to have same 1-pixel thickness all the time.
So, my question is: what is the usual solution for this problem? For now (based on my assumption above) I can only think of deleting all objects and creating new with integer coordinates, but rescaled (manually).
You need to set your line drawing to "cosmetic" in the QPen. This makes the lines non-scalable. Otherwise, Qt scales the line widths along with the scaling of the view. Look up QPen::setCosmetic. By default, drawing lines is not cosmetic.

what is the best way to show tile map and some other object in graphicsview?

recently i start to learn Qt and now i'm working on GCS project that it must have a map with some tiled imges and and some graphics item like Plan,the path and also on over off all some gauge.
so we have 3 kind of item:
Tiled map in the background so that its change by scrolling .
in the middle there is a picture of airplane that move by gps changes and also its way .
on the all on off these items there 3 or 4 gauge like speed meter, horizontal gauge and altimeter gauge there are must be solid in somewhere of graphicsview and not change when scrolling down/up or left right
The question is what is the best way to implement this ?
here is first look of my project:
in first look gauge are not over map but i want to be ! i want to have bigger map screen with gauges include it !
And here is map updater code :
void mainMap::update()
{
m_scene->clear();
QString TilePathTemp;
QImage *imageTemp = new QImage();
int X_Start=visibleRect().topLeft().x()/256;
int X_Num=qCeil((float)visibleRect().bottomRight().x()/256.0f-(float)visibleRect().topLeft().x()/256.0f);
int Y_Start=visibleRect().topLeft().y()/256;
int Y_Num=qCeil((float)visibleRect().bottomRight().y()/256.0f-(float)visibleRect().topLeft().y()/256.0f);
LastCenterPoint->setX(visibleRect().center().x());
LastCenterPoint->setY(visibleRect().center().y());
X_Start=(X_Start-X_MAP_MARGIN)>0?(X_Start-X_MAP_MARGIN):0;
Y_Start=(Y_Start-Y_MAP_MARGIN)>0?(Y_Start-Y_MAP_MARGIN):0;
X_Num+=X_MAP_MARGIN;
Y_Num+=Y_MAP_MARGIN;
qDebug()<<"XS:"<<X_Start<<" Num:"<<X_Num;
qDebug()<<"YS:"<<Y_Start<<" Num:"<<Y_Num;
for(int x=X_Start;x<=X_Start+X_Num;x++){
for(int y=Y_Start;y<=Y_Start+Y_Num;y++){
if(Setting->value("MapType",gis::Hybrid).toInt()==gis::Hybrid) TilePathTemp=Setting->value("MapPath","/Users/M410/Documents/Map").toString()+"/Hybrid/gh_"+QString::number(x)+"_"+QString::number(y)+"_"+QString::number(ZoomLevel)+".jpeg" ;
else if(Setting->value("MapType",gis::Sattelite).toInt()==gis::Sattelite) TilePathTemp=Setting->value("MapPath","/Users/M410/Documents/Map").toString()+"/Sattelite/gs_"+QString::number(x)+"_"+QString::number(y)+"_"+QString::number(ZoomLevel)+".jpeg" ;
else if(Setting->value("MapType",gis::Street).toInt()==gis::Street) TilePathTemp=Setting->value("MapPath","/Users/M410/Documents/Map").toString()+"/Street/gm_"+QString::number(x)+"_"+QString::number(y)+"_"+QString::number(ZoomLevel)+".jpeg" ;
QFileInfo check_file(TilePathTemp);
// check if file exists and if yes: Is it really a file and no directory?
if (check_file.exists() && check_file.isFile()) {
// qDebug()<<"Exist!";
imageTemp->load(TilePathTemp);
QPixmap srcImage = QPixmap::fromImage(*imageTemp);
//QPixmap srcImage("qrc:/Map/File1.jpeg");
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(srcImage);
item->setPos(QPointF(x*256, y*256));
m_scene->addItem(item);
// centerOn( width() / 2.0f , height() / 2.0f );
} else {
qDebug()<<"NOT Exist!";
}
}
}
Really, you should consider using QML. The advantage of using QML instead of QGraphicsView is you can iterate a lot faster than if you were working directly in C++. The primary downside is generally increased memory usage and incompatibility with QWidgets.
So if you need unique graphics, and very little "standard widget" stuff, you should use QML first and then QGraphicsView ONLY IF requirements dictate it.
Specific to your project though, Qt has a Map type which could be useful: https://doc.qt.io/qt-5/qml-qtlocation-map.html

Game Maker: create an ennemy at a random position on the ground

I know a bit about coding (in Python, C and XHTML) and I'm trying to understand the basics of Game Maker. I have created a room with enemies moving, colliding to the walls and all, but now, I'd like to randomly spawn enemies in the room as long as they are on a ground. For now, it only works when I spawn them randomly.
Here is the code I put in the Create event of the obj_enemy but obviously something isn't working since it does not spawn any enemy at all.
Also, don't know if it matters, but if I haven't already placed myself an obj_enemy in the room, they do not spawn neither...
// INIT //
dir = -1; // direction
movespeed = 3; // movement speed
hsp = 0; // horizontal speed
vsp = 0; // vertical speed
grav = 0.5; // gravity
// CREATE //
// Find a random X position in the room
var randx = random(room_width);
// Find a random Y position in the room
var randy = random(room_height);
// If the random position is empty
if position_empty (randx, randy)
{
// If there is a block
// 16 pixels under
// the random Y position
// (the sprite of obj_enemy is 32x32 pixels)
if place_meeting (randx, randy+16, obj_block01)
{
// If there is less than 4 ennemies
if instance_number (obj_ennemy) <= 4
{
// Create an ennemy
instance_create(randx, randy, obj_ennemy);
}
}
}
This is the create event of obj_enemy. if there are no obj_enemy's in the room then this code will never get run!
You either need to start with at least one enemy in the room or create a controller object in charge of creating enemies that you put into the room instead (I recommend this approach).
Also even if the code does get run then the chances of spawning an enemy just over a wall in the correct location is quite small so you will have to run the program many times before you see it happen. To avoid this just put the spawn code into a while true loop and break from it once 4 enemies have been spawned:
while (instance_number (obj_ennemy) <= 4)
{
// Find a random X position in the room
var randx = random(room_width);
// Find a random Y position in the room
var randy = random(room_height);
// If the random position is empty
if position_empty (randx, randy)
{
// If there is a block
// 16 pixels under
// the random Y position
// (the sprite of obj_enemy is 32x32 pixels)
if place_meeting (randx, randy+16, obj_block01)
{
// Create an ennemy
instance_create(randx, randy, obj_ennemy);
}
}
}

Javafx gridpane width setting

I need to make some kind of table in which I store simple Region nodes (I will do other stuff on them afterwards - such as megring cells horizontally and giving them other properties, Labels for example) Table will have lots of columns, >200. My goal is to force every grid to have the same width (very important) and to generate that table dynamically.
Here is part of my code that generates that table.
GridPane schedule = new GridPane();
for (int j = 0; j < horizontalGridCount; ++j) {
ColumnConstraints cc = new ColumnConstraints();
cc.setPercentWidth(100/horizontalGridCount);
schedule.getColumnConstraints().add(cc);
}
for (int i = 0; i < verticalGridCount; ++i) {
for (int j = 0; j < horizontalGridCount; ++j) {
final Region grid = new Region();
grid.setStyle("-fx-background-color: #dddddd;");
grid.setPrefHeight(30); // set to make regions visible on screen
grid.setPrefWidth(10); // set to make regions visible on screen
schedule.add(grid, j, i);
}
}
schedule.gridLinesVisibleProperty().set(true);
Here is the output I get on my screen. As you can observe some grids are close-grained then the others
Do you have any idea why it is that wrong and how to fix that?
P.S. It's my first post here, I hope I've done everything right ;)
You are using integer division logic, which rounds things. Use floating point logic instead:
cc.setPercentWidth(100.0/horizontalGridCount);
Note the 100.0 which makes 100.0 (and the division result) a non-integer.
Also don't set the grid lines visible in your target app, that setting is only for debugging (I guess this is why you have it there and set to true, but just a reminder in case):
schedule.gridLinesVisibleProperty().set(true);
If you want borders on your grid cells you can review this demo program.

How to set font Bold to a particular row in table widget

i want to set my font as bold in particular row column position of my tablewidget.
I did like this but getting break.
QFont font("Helvetica", 12, QFont::Bold);
overviewTable->item(2,2)->setFont(font);
Please Help
I think everything is ok. Here what docs said:
void QTableWidgetItem::setFont ( const QFont & font )
Sets the font used to display the item's text to the given font.
Maybe your overviewTable const?
ADDED:
This variant works fine for my Qt 4.6:
tableWidget = new QTableWidget(12, 3, this);
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 3; j++) {
QTableWidgetItem *newItem = new QTableWidgetItem(tr("%1").arg(
(i+1)*(j+1)));
tableWidget->setItem(i, j, newItem);
}
}
QFont font;
font.setBold(true);
tableWidget->item(2, 2)->setFont(font);
Maybe you are getting break because you didn't call setItem() to set an item for the cell (2, 2) before you use overviewTable->item(2,2). As the Qt document says,
QTableWidgetItem * QTableWidget::item(int row, int column) const
Returns the item for the given row and column if one has been set;
otherwise returns 0.
That is, your overviewTable->item(2,2) probably returns 0, thus causes a Segmentation fault in the setFont() call.
So your means to setting font is completely right. You just need to call setItem() at first as mosg's answer suggests.
ADDED:
if your overviewTable is a QTableWidget created in Qt Designer, then in the Designer a double-click on a cell (just to enter its editing mode, no need to actually enter anything) will have the effect of calling setItem() for that cell. Later in your code you can directly using the item() function without having to call setItem() first.

Resources