QGraphicsItemGroup::boundingRect() not updating - c++

I've been trying to use QGraphicsItemGroup to get the bounding rectangle of a group of QGraphicsItem*s. It appears to me that the bounding rectangle is correctly determined when I insert all of the items into the group; but if I then move items in the group, the bounding rectangle does not update to contain the moved items as I expect. I cannot find indication in the Documentation as to whether what I'm seeing is correct behavior or not; my guess is that I'm either misunderstanding how the QGraphicsItemGroup works or misusing it
An example that I've been using to test:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTransform>
#include <QGraphicsEllipseItem>
#include <QDebug>
#include <QTimer>
#include <cmath>
const double pi = 3.14;
QTransform rotation(double degrees)
{
double a = pi/180 * degrees;
double sina = sin(a);
double cosa = cos(a);
return QTransform(cosa, sina, -sina, cosa, 0, 0);
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
//Shouldn't execute until after window.show() and application.exec()
//are called
QTimer::singleShot(1, this, &MainWindow::build);
}
void MainWindow::build()
{
QGraphicsEllipseItem* el1 = scene->addEllipse(-5, -5, 10, 10);
scene->addLine(0, 0, 0, 10)->setParentItem(el1);
QGraphicsEllipseItem* el2 = scene->addEllipse(-5, -5, 10, 10);
scene->addLine(0, 0, 0, 10)->setParentItem(el2);
QGraphicsEllipseItem* el3 = scene->addEllipse(-5, -5, 10, 10);
scene->addLine(0, 0, 0, 10)->setParentItem(el3);
QGraphicsEllipseItem* el4 = scene->addEllipse(-5, -5, 10, 10);
scene->addLine(0, 0, 0, 10)->setParentItem(el4);
QGraphicsItemGroup* group = new QGraphicsItemGroup;
group->addToGroup(el1);
group->addToGroup(el2);
group->addToGroup(el3);
group->addToGroup(el4);
scene->addItem(group);
scene->addRect(group->boundingRect());
QTransform translate2(1, 0, 0, 1, 10, 10);
QTransform t2 = /*rotation(45) **/ translate2;
el2->setTransform(t2);
QTransform translate3(1, 0, 0, 1, 20, 20);
QTransform t3 = /*rotation(-45) **/ translate3 * t2;
el3->setTransform(t3);
QTransform translate4(1, 0, 0, 1, 20, -20);
QTransform t4 = translate4 * t3;
el4->setTransform(t4);
qDebug() << t4.dx() << t4.dy() << atan2(t4.m12(), t4.m22())*180/pi;
QTransform t4i = t4.inverted();
qDebug() << t4i.dx() << t4i.dy() << atan2(t4i.m12(), t4i.m22())*180/pi;
scene->addRect(group->boundingRect());
}
MainWindow::~MainWindow()
{
delete ui;
}
The final scene displayed looks like this
Actual outcome
But, I was expecting something like this, with a small box from the first boundingRect, and a larger one for the second
Expected outcome
Am I misunderstanding how the QGraphicsItemGroup works, or am I using it incorrectly?
Qt Version: 5.10
OS: Ubuntu 16.04
Compiler GCC 5.4

The boundingrect is only updated at additem. Therefore the method childrenBoundingRect() must be used instead of boundingRect().
In a separate subclass of QGraphicsItemGroup say MyQGraphicsItemGroup we can overload the method virtual QRectF boundingRect() const override so that childrenBoundingRect() is called.
QRectF MyQGraphicsItemGroup::boundingRect() const
{
// must be overloaded, otherwise the boundingrect will only be actualized on
// additem is actualized. This leads to the fact that the boundingrect
// will not close around the word items after e.g., moving them.
return childrenBoundingRect();
}

The QGraphicsItemGroup documentation doesn't seem to mention this, but it only recalculates the boundingRect for a QGraphicsItemGroup after the view and QGraphicsScene is shown.
Adding the items afterwards
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene;
QGraphicsView mainView(&scene);
scene.setSceneRect(0, 0, 800, 800);
QGraphicsEllipseItem* el1 = scene.addEllipse(-5, -5, 10, 10);
scene.addLine(0, 0, 0, 10)->setParentItem(el1);
QGraphicsEllipseItem* el2 = scene.addEllipse(-5, -5, 10, 10);
scene.addLine(0, 0, 0, 10)->setParentItem(el2);
QGraphicsEllipseItem* el3 = scene.addEllipse(-5, -5, 10, 10);
scene.addLine(0, 0, 0, 10)->setParentItem(el3);
QGraphicsEllipseItem* el4 = scene.addEllipse(-5, -5, 10, 10);
scene.addLine(0, 0, 0, 10)->setParentItem(el4);
QGraphicsItemGroup* group = new QGraphicsItemGroup;
scene.addItem(group);
QTransform translate2(1, 0, 0, 1, 10, 10);
QTransform t2 = /*rotation(45) **/ translate2;
el2->setTransform(t2);
QTransform translate3(1, 0, 0, 1, 20, 20);
QTransform t3 = /*rotation(-45) **/ translate3 * t2;
el3->setTransform(t3);
QTransform translate4(1, 0, 0, 1, 20, -20);
QTransform t4 = translate4 * t3;
el4->setTransform(t4);
mainView.show();
group->addToGroup(el1);
group->addToGroup(el2);
group->addToGroup(el3);
group->addToGroup(el4);
scene.addRect(group->boundingRect());
qDebug() << group->sceneBoundingRect() << endl << group->boundingRect();
scene.addRect(group->sceneBoundingRect());
return a.exec();
}
Results in the bounding rect of all members of the group

Related

Why doesn't setPos() work when using addPolygon() method?

Consider this class called Hex to draw a hexagon :
// Hex.h
#include <QGraphicsPolygonItem>
#include <QPointF>
#include <QVector>
#include <QImage>
#include <QBrush>
#include <QPen>
class Hex : public QGraphicsPolygonItem {
public:
// constructor
Hex(QGraphicsItem* parent = NULL);
// getters/setters
QPolygonF *getHexagon() {return hexagon;}
QBrush *getBrush() {return brush;}
protected:
QBrush *brush;
QPolygonF *hexagon;
};
and
// Hex.cpp
#include "hex.h"
Hex::Hex(QGraphicsItem *parent) {
// set Points
QVector<QPointF> hexPoints;
hexPoints << QPointF(1, 0) << QPointF(0, 1) << QPointF(0, 2)
<< QPointF(1, 3) << QPointF(2, 2) << QPointF(2 ,1);
// scale the poly
int SCALE_BY = 40;
for (size_t i = 0, n = hexPoints.size(); i < n ; i++) {
hexPoints[i] *= SCALE_BY;
}
//create a QPyolygon with the scaled points
hexagon = new QPolygonF(hexPoints);
// draw the polygon
setPolygon(*hexagon);
}
In main.cpp :
#include <QGraphicsView>
#include <QGraphicsScene>
#include "hex.h"
int main(int argc, char *argv[]){
QApplication a(argc, argv);
QGraphicsScene *scene = new QGraphicsScene();
QGraphicsView *view = new QGraphicsView();
Hex *hex0 = new Hex();
Hex *hex1 = new Hex();
hex0->setPos(400, 400);
hex1->setPos(300, 300);
scene->addItem(hex0);
scene->addItem(hex1);
scene->setSceneRect(0,0 , 1024, 768);
view->setScene(scene);
view->show();
return a.exec();
}
Ok. this is ok and setPos works fine.
But using addPolygon instead of addItem to set picture for the hexagons results in setPos not working and all the hexagons cover each other at the first coordinates given to them.
Like this :
// main.cpp
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include "hex.h"
int main(int argc, char *argv[]){
QApplication a(argc, argv);
QGraphicsScene *scene = new QGraphicsScene();
QGraphicsView *view = new QGraphicsView();
Hex *hex0 = new Hex();
Hex *hex1 = new Hex();
hex0->setPos(400, 400);
hex1->setPos(300, 300);
// set picture for hexagons:
QBrush* brush = new QBrush(QImage("image.png"));
scene->addPolygon(*hex0->getHexagon(), QPen(Qt::black) ,*brush);
scene->addPolygon(*hex1->getHexagon(), QPen(Qt::black) ,*brush);
scene->setSceneRect(0,0 , 1024, 768);
view->setScene(scene);
view->show();
return a.exec();
}
Why does setPos() has this behavior and how to move the pictured hexagons.
Thanks in advance.
setPos () does not modify the QPolygonF but moves the item in scene coordinates, instead QPolygonF are drawn with respect to the internal coordinate system of the item. Therefore, if you want to observe the initial behavior, you have 2 options:
Move the QGraphicsPolygonItem:
QGraphicsPolygonItem *p0 = scene->addPolygon(*hex0->getHexagon(), QPen(Qt::black) ,*brush);
QGraphicsPolygonItem *p1 = scene->addPolygon(*hex1->getHexagon(), QPen(Qt::black) ,*brush);
p0->setPos(400, 400);
p1->setPos(300, 300);
Move the QPolygonF:
scene.addPolygon((*hex0->getHexagon()).translated(400, 400), QPen(Qt::black));
scene.addPolygon((*hex0->getHexagon()).translated(300, 300), QPen(Qt::black));
Your confusion comes from the fact that QGraphicsPolygonItem already has a QPolygonF and a QBrush member, you don't need extra ones.
Not only that, but addPolygon constructs a whole new QGraphicsPolygonItem:
Creates and adds a polygon item to the scene, and returns the item pointer. The polygon is defined by polygon, and its pen and brush are initialized to pen and brush.
I would suggest that Hex shouldn't be a class, it should be a function:
QGraphicsSceneItem * makeHex(QPointF pos, int scale = 40) {
QVector<QPointF> hexPoints{ QPointF(1, 0), QPointF(0, 1), QPointF(0, 2), QPointF(1, 3), QPointF(2, 2), QPointF(2 ,1) };
// scale the poly
for (auto & point : hexpoints) {
point *= scale;
}
auto * hex = new QGraphicsPolygonItem();
hex->setPolygon(hexPoints);
hex->setPos(pos);
hex->setBrush(QImage("image.png"));
return hex;
}
Or
QGraphicsSceneItem * addHex(QGraphicsScene *scene, int scale = 40) {
QVector<QPointF> hexPoints{ QPointF(1, 0), QPointF(0, 1), QPointF(0, 2), QPointF(1, 3), QPointF(2, 2), QPointF(2 ,1) };
// scale the poly
for (auto & point : hexpoints) {
point *= scale;
}
return scene->addPolygon(hexpoints, QPen(Qt::black), QImage("image.png"));
}

What exactly are the positioning rules for text in Qt?

Here's a very basic piece of code which:
Measures the size a piece of text would take.
Draws the rectangle which corresponds to this size at coordinates (100, 25).
Displays text at coordinates (100, 25).
auto str = "Hello, World!";
auto metrix = window->fontMetrics();
auto text = scene->addText(str);
text->setPos(100, 25);
text->setDefaultTextColor(Qt::white);
auto r = metrix.boundingRect(str);
int x, y, w, h;
r.getRect(&x, &y, &w, &h);
scene->addRect(100, 25, w, h, QPen(Qt::white));
The scene in code is a QGraphicsScene with no specific customizations, with the exception of a border set to zero.
I would expect the text to be exactly inside the rectangle. The text is however shifted by a few pixels to the left and a few more pixels to the bottom. Why?
Solution
Setting the document margins to 0, as #NgocMinhNguyen suggested, might seem to work, but it is not a real solution, because you lose the margins. It would be better, if you could get the actual geometry, including margins etc. For that purpose you can use QGraphicsTextItem::boundingRect() instead of QFontMetrics::boundingRect.
Example
Here is a minimal and complete example I have written for you, in order to demonstrate the proposed solution:
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QBoxLayout>
struct MainWindow : public QWidget
{
MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
QPointF p(100, 25);
auto *l = new QVBoxLayout(this);
auto *view = new QGraphicsView(this);
auto *textItem = new QGraphicsTextItem(tr("HHHHHHHH"));
auto *rectItem = new QGraphicsRectItem(textItem->boundingRect()
.adjusted(0, 0, -1, -1));
textItem->setPos(p);
rectItem->setPos(p);
view->setScene(new QGraphicsScene(this));
view->scene()->addItem(textItem);
view->scene()->addItem(rectItem);
l->addWidget(view);
resize(300, 300);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Note: Please note how I create the rectangle. There is a difference between
auto *item = new QGraphicsRectItem(100, 25, w, h);
and
auto *item = new QGraphicsRectItem(0, 0, w, h);
item->setPos(100, 25);
Result
This example produces the following result:
QGraphicsTextItem is held by QTextDocument, which can have a margin.
Setting the margin to 0 and the rectangle will be correctly drawn.
text->document()->setDocumentMargin(0);

convert Qt Designer project into C++ code

I converted my .ui file via uic into a header file, which, as far as I know, contains the header AND the implementation of the .ui file.
But what part of it is the header and what is the implementation?
Is there a way to seperate them properly?
#ifndef PATCHBOT_GUI_H
#define PATCHBOT_GUI_H
#include <QtCore/QVariant>
#include <QtGui/QIcon>
#include <QtWidgets/QApplication>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QFrame>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QScrollBar>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_MainWindow
{
public:
QWidget *centralwidget;
QGridLayout *gridLayout;
QFrame *selectColonyFrame;
QPushButton *selectColonyButton;
QFrame *currentColonyFrame;
QLabel *currentColonyLabel;
QFrame *missionControlsFrame;
QVBoxLayout *verticalLayout;
QLabel *misisonControlsLabel;
QGridLayout *missionControlsGridLayout;
QPushButton *startButton;
QPushButton *abortButton;
QPushButton *singleStepButton;
QPushButton *autoButton;
QPushButton *pauseButton;
QFrame *botControlsFrame;
QGridLayout *gridLayout_2;
QGridLayout *botControlsGridLayout;
QPushButton *downButton;
QPushButton *upButton;
QPushButton *waitButton;
QPushButton *leftButton;
QPushButton *deleteButton;
QPushButton *rightButton;
QLabel *botControlsLabel;
QComboBox *repeatDropdown;
QFrame *frame_6;
QFrame *botCommandFrame;
QTextEdit *botCommandTextEdit;
QFrame *mapFrame;
QGridLayout *gridLayout_6;
QLabel *label;
QScrollBar *verticalScrollBar;
QScrollBar *horizontalScrollBar;
QMenuBar *menubar;
QStatusBar *statusbar;
void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(819, 634);
MainWindow->setMinimumSize(QSize(580, 400));
centralwidget = new QWidget(MainWindow);
centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
gridLayout = new QGridLayout(centralwidget);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
gridLayout->setVerticalSpacing(6);
gridLayout->setContentsMargins(-1, 0, -1, 0);
selectColonyFrame = new QFrame(centralwidget);
selectColonyFrame->setObjectName(QString::fromUtf8("selectColonyFrame"));
selectColonyFrame->setMinimumSize(QSize(150, 40));
selectColonyFrame->setLayoutDirection(Qt::RightToLeft);
selectColonyFrame->setFrameShape(QFrame::StyledPanel);
selectColonyFrame->setFrameShadow(QFrame::Raised);
selectColonyButton = new QPushButton(selectColonyFrame);
selectColonyButton->setObjectName(QString::fromUtf8("selectColonyButton"));
selectColonyButton->setGeometry(QRect(80, 0, 150, 40));
selectColonyButton->setMinimumSize(QSize(150, 40));
selectColonyButton->setMaximumSize(QSize(150, 50));
QFont font;
font.setPointSize(11);
font.setBold(true);
font.setWeight(75);
selectColonyButton->setFont(font);
gridLayout->addWidget(selectColonyFrame, 0, 1, 1, 1);
currentColonyFrame = new QFrame(centralwidget);
currentColonyFrame->setObjectName(QString::fromUtf8("currentColonyFrame"));
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(currentColonyFrame->sizePolicy().hasHeightForWidth());
currentColonyFrame->setSizePolicy(sizePolicy);
currentColonyFrame->setMinimumSize(QSize(0, 25));
currentColonyFrame->setFrameShape(QFrame::StyledPanel);
currentColonyFrame->setFrameShadow(QFrame::Raised);
currentColonyLabel = new QLabel(currentColonyFrame);
currentColonyLabel->setObjectName(QString::fromUtf8("currentColonyLabel"));
currentColonyLabel->setGeometry(QRect(10, 10, 301, 16));
currentColonyLabel->setMinimumSize(QSize(220, 15));
currentColonyLabel->setMaximumSize(QSize(460, 25));
QFont font1;
font1.setPointSize(10);
currentColonyLabel->setFont(font1);
gridLayout->addWidget(currentColonyFrame, 0, 0, 1, 1);
missionControlsFrame = new QFrame(centralwidget);
missionControlsFrame->setObjectName(QString::fromUtf8("missionControlsFrame"));
QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Preferred);
sizePolicy1.setHorizontalStretch(8);
sizePolicy1.setVerticalStretch(8);
sizePolicy1.setHeightForWidth(missionControlsFrame->sizePolicy().hasHeightForWidth());
missionControlsFrame->setSizePolicy(sizePolicy1);
missionControlsFrame->setMaximumSize(QSize(300, 180));
missionControlsFrame->setFrameShape(QFrame::WinPanel);
missionControlsFrame->setFrameShadow(QFrame::Raised);
verticalLayout = new QVBoxLayout(missionControlsFrame);
verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
misisonControlsLabel = new QLabel(missionControlsFrame);
misisonControlsLabel->setObjectName(QString::fromUtf8("misisonControlsLabel"));
QFont font2;
font2.setPointSize(11);
misisonControlsLabel->setFont(font2);
misisonControlsLabel->setAlignment(Qt::AlignCenter);
verticalLayout->addWidget(misisonControlsLabel);
missionControlsGridLayout = new QGridLayout();
missionControlsGridLayout->setObjectName(QString::fromUtf8("missionControlsGridLayout"));
missionControlsGridLayout->setHorizontalSpacing(20);
missionControlsGridLayout->setVerticalSpacing(10);
missionControlsGridLayout->setContentsMargins(15, 20, 15, 0);
startButton = new QPushButton(missionControlsFrame);
startButton->setObjectName(QString::fromUtf8("startButton"));
startButton->setMinimumSize(QSize(80, 30));
startButton->setMaximumSize(QSize(130, 50));
QFont font3;
font3.setPointSize(12);
startButton->setFont(font3);
missionControlsGridLayout->addWidget(startButton, 0, 0, 1, 1);
abortButton = new QPushButton(missionControlsFrame);
abortButton->setObjectName(QString::fromUtf8("abortButton"));
abortButton->setMinimumSize(QSize(80, 30));
abortButton->setMaximumSize(QSize(130, 50));
abortButton->setFont(font3);
abortButton->setLayoutDirection(Qt::LeftToRight);
abortButton->setAutoDefault(false);
abortButton->setFlat(false);
missionControlsGridLayout->addWidget(abortButton, 0, 1, 1, 1);
singleStepButton = new QPushButton(missionControlsFrame);
singleStepButton->setObjectName(QString::fromUtf8("singleStepButton"));
singleStepButton->setMinimumSize(QSize(80, 30));
singleStepButton->setMaximumSize(QSize(130, 50));
singleStepButton->setFont(font3);
missionControlsGridLayout->addWidget(singleStepButton, 1, 0, 1, 1);
autoButton = new QPushButton(missionControlsFrame);
autoButton->setObjectName(QString::fromUtf8("autoButton"));
autoButton->setMinimumSize(QSize(80, 30));
autoButton->setMaximumSize(QSize(130, 50));
autoButton->setFont(font3);
autoButton->setLayoutDirection(Qt::LeftToRight);
autoButton->setAutoDefault(false);
autoButton->setFlat(false);
missionControlsGridLayout->addWidget(autoButton, 1, 1, 1, 1);
pauseButton = new QPushButton(missionControlsFrame);
pauseButton->setObjectName(QString::fromUtf8("pauseButton"));
pauseButton->setMinimumSize(QSize(80, 30));
pauseButton->setMaximumSize(QSize(130, 50));
pauseButton->setFont(font3);
pauseButton->setLayoutDirection(Qt::LeftToRight);
pauseButton->setAutoDefault(false);
pauseButton->setFlat(false);
missionControlsGridLayout->addWidget(pauseButton, 2, 1, 1, 1);
verticalLayout->addLayout(missionControlsGridLayout);
gridLayout->addWidget(missionControlsFrame, 3, 1, 1, 1);
botControlsFrame = new QFrame(centralwidget);
botControlsFrame->setObjectName(QString::fromUtf8("botControlsFrame"));
QSizePolicy sizePolicy2(QSizePolicy::Preferred, QSizePolicy::Preferred);
sizePolicy2.setHorizontalStretch(4);
sizePolicy2.setVerticalStretch(4);
sizePolicy2.setHeightForWidth(botControlsFrame->sizePolicy().hasHeightForWidth());
botControlsFrame->setSizePolicy(sizePolicy2);
botControlsFrame->setMinimumSize(QSize(160, 120));
botControlsFrame->setMaximumSize(QSize(320, 240));
botControlsFrame->setFrameShape(QFrame::WinPanel);
botControlsFrame->setFrameShadow(QFrame::Raised);
gridLayout_2 = new QGridLayout(botControlsFrame);
gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2"));
botControlsGridLayout = new QGridLayout();
botControlsGridLayout->setObjectName(QString::fromUtf8("botControlsGridLayout"));
botControlsGridLayout->setSizeConstraint(QLayout::SetMinimumSize);
downButton = new QPushButton(botControlsFrame);
downButton->setObjectName(QString::fromUtf8("downButton"));
downButton->setMinimumSize(QSize(30, 30));
downButton->setMaximumSize(QSize(50, 50));
downButton->setLayoutDirection(Qt::LeftToRight);
QIcon icon;
icon.addFile(QString::fromUtf8(":/grafics/grafics/pfeile/pfeil_unten.tga"), QSize(), QIcon::Normal, QIcon::Off);
downButton->setIcon(icon);
downButton->setIconSize(QSize(50, 50));
botControlsGridLayout->addWidget(downButton, 2, 1, 1, 1);
upButton = new QPushButton(botControlsFrame);
upButton->setObjectName(QString::fromUtf8("upButton"));
upButton->setMinimumSize(QSize(30, 30));
upButton->setMaximumSize(QSize(50, 50));
upButton->setSizeIncrement(QSize(0, 0));
QIcon icon1;
icon1.addFile(QString::fromUtf8(":/grafics/grafics/pfeile/pfeil_oben.tga"), QSize(), QIcon::Normal, QIcon::Off);
upButton->setIcon(icon1);
upButton->setIconSize(QSize(50, 50));
botControlsGridLayout->addWidget(upButton, 0, 1, 1, 1);
waitButton = new QPushButton(botControlsFrame);
waitButton->setObjectName(QString::fromUtf8("waitButton"));
waitButton->setMinimumSize(QSize(30, 30));
waitButton->setMaximumSize(QSize(50, 50));
QFont font4;
font4.setFamily(QString::fromUtf8("MS Shell Dlg 2"));
font4.setPointSize(15);
waitButton->setFont(font4);
waitButton->setContextMenuPolicy(Qt::DefaultContextMenu);
QIcon icon2;
QString iconThemeName = QString::fromUtf8("&#9664");
if (QIcon::hasThemeIcon(iconThemeName)) {
icon2 = QIcon::fromTheme(iconThemeName);
} else {
icon2.addFile(QString::fromUtf8("."), QSize(), QIcon::Normal, QIcon::Off);
}
waitButton->setIcon(icon2);
botControlsGridLayout->addWidget(waitButton, 1, 1, 1, 1);
leftButton = new QPushButton(botControlsFrame);
leftButton->setObjectName(QString::fromUtf8("leftButton"));
leftButton->setMinimumSize(QSize(30, 30));
leftButton->setMaximumSize(QSize(50, 50));
QIcon icon3;
icon3.addFile(QString::fromUtf8(":/grafics/grafics/pfeile/pfeil_links.tga"), QSize(), QIcon::Normal, QIcon::Off);
leftButton->setIcon(icon3);
leftButton->setIconSize(QSize(50, 50));
botControlsGridLayout->addWidget(leftButton, 1, 0, 1, 1);
deleteButton = new QPushButton(botControlsFrame);
deleteButton->setObjectName(QString::fromUtf8("deleteButton"));
deleteButton->setMinimumSize(QSize(30, 30));
deleteButton->setMaximumSize(QSize(50, 50));
QFont font5;
font5.setPointSize(15);
deleteButton->setFont(font5);
botControlsGridLayout->addWidget(deleteButton, 0, 2, 1, 1);
rightButton = new QPushButton(botControlsFrame);
rightButton->setObjectName(QString::fromUtf8("rightButton"));
rightButton->setMinimumSize(QSize(30, 30));
rightButton->setMaximumSize(QSize(50, 50));
rightButton->setSizeIncrement(QSize(0, 0));
QIcon icon4;
icon4.addFile(QString::fromUtf8(":/grafics/grafics/pfeile/pfeil_rechts.tga"), QSize(), QIcon::Normal, QIcon::Off);
rightButton->setIcon(icon4);
rightButton->setIconSize(QSize(50, 50));
botControlsGridLayout->addWidget(rightButton, 1, 2, 1, 1);
gridLayout_2->addLayout(botControlsGridLayout, 1, 0, 3, 1);
botControlsLabel = new QLabel(botControlsFrame);
botControlsLabel->setObjectName(QString::fromUtf8("botControlsLabel"));
botControlsLabel->setFont(font2);
botControlsLabel->setAlignment(Qt::AlignCenter);
gridLayout_2->addWidget(botControlsLabel, 0, 0, 1, 2);
repeatDropdown = new QComboBox(botControlsFrame);
repeatDropdown->addItem(QString());
repeatDropdown->addItem(QString());
repeatDropdown->addItem(QString());
repeatDropdown->addItem(QString());
repeatDropdown->addItem(QString());
repeatDropdown->setObjectName(QString::fromUtf8("repeatDropdown"));
repeatDropdown->setMinimumSize(QSize(80, 30));
repeatDropdown->setMaximumSize(QSize(130, 50));
QFont font6;
font6.setPointSize(15);
font6.setBold(true);
font6.setItalic(false);
font6.setWeight(75);
repeatDropdown->setFont(font6);
repeatDropdown->setFocusPolicy(Qt::WheelFocus);
repeatDropdown->setLayoutDirection(Qt::LeftToRight);
repeatDropdown->setInsertPolicy(QComboBox::NoInsert);
repeatDropdown->setFrame(true);
gridLayout_2->addWidget(repeatDropdown, 1, 1, 1, 1);
frame_6 = new QFrame(botControlsFrame);
frame_6->setObjectName(QString::fromUtf8("frame_6"));
frame_6->setFrameShape(QFrame::StyledPanel);
frame_6->setFrameShadow(QFrame::Raised);
gridLayout_2->addWidget(frame_6, 2, 1, 1, 1);
botCommandFrame = new QFrame(botControlsFrame);
botCommandFrame->setObjectName(QString::fromUtf8("botCommandFrame"));
botCommandFrame->setMinimumSize(QSize(100, 80));
botCommandFrame->setMaximumSize(QSize(130, 16777215));
botCommandFrame->setFrameShape(QFrame::StyledPanel);
botCommandFrame->setFrameShadow(QFrame::Raised);
botCommandTextEdit = new QTextEdit(botCommandFrame);
botCommandTextEdit->setObjectName(QString::fromUtf8("botCommandTextEdit"));
botCommandTextEdit->setGeometry(QRect(0, 16, 108, 50));
QSizePolicy sizePolicy3(QSizePolicy::Expanding, QSizePolicy::Expanding);
sizePolicy3.setHorizontalStretch(130);
sizePolicy3.setVerticalStretch(50);
sizePolicy3.setHeightForWidth(botCommandTextEdit->sizePolicy().hasHeightForWidth());
botCommandTextEdit->setSizePolicy(sizePolicy3);
botCommandTextEdit->setMinimumSize(QSize(80, 30));
botCommandTextEdit->setMaximumSize(QSize(130, 50));
botCommandTextEdit->setFont(font5);
botCommandTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
botCommandTextEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
botCommandTextEdit->setLineWrapMode(QTextEdit::NoWrap);
botCommandTextEdit->setReadOnly(true);
gridLayout_2->addWidget(botCommandFrame, 3, 1, 1, 1);
gridLayout_2->setRowStretch(1, 2);
gridLayout_2->setRowStretch(2, 1);
gridLayout_2->setRowStretch(3, 1);
gridLayout->addWidget(botControlsFrame, 1, 1, 2, 1);
mapFrame = new QFrame(centralwidget);
mapFrame->setObjectName(QString::fromUtf8("mapFrame"));
QSizePolicy sizePolicy4(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
sizePolicy4.setHorizontalStretch(0);
sizePolicy4.setVerticalStretch(0);
sizePolicy4.setHeightForWidth(mapFrame->sizePolicy().hasHeightForWidth());
mapFrame->setSizePolicy(sizePolicy4);
mapFrame->setMinimumSize(QSize(120, 120));
mapFrame->setFrameShape(QFrame::StyledPanel);
mapFrame->setFrameShadow(QFrame::Raised);
gridLayout_6 = new QGridLayout(mapFrame);
gridLayout_6->setSpacing(0);
gridLayout_6->setObjectName(QString::fromUtf8("gridLayout_6"));
label = new QLabel(mapFrame);
label->setObjectName(QString::fromUtf8("label"));
sizePolicy4.setHeightForWidth(label->sizePolicy().hasHeightForWidth());
label->setSizePolicy(sizePolicy4);
label->setMinimumSize(QSize(0, 0));
label->setFrameShape(QFrame::Box);
label->setAlignment(Qt::AlignCenter);
gridLayout_6->addWidget(label, 0, 0, 1, 1);
verticalScrollBar = new QScrollBar(mapFrame);
verticalScrollBar->setObjectName(QString::fromUtf8("verticalScrollBar"));
verticalScrollBar->setOrientation(Qt::Vertical);
gridLayout_6->addWidget(verticalScrollBar, 0, 1, 1, 1);
horizontalScrollBar = new QScrollBar(mapFrame);
horizontalScrollBar->setObjectName(QString::fromUtf8("horizontalScrollBar"));
QSizePolicy sizePolicy5(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
sizePolicy5.setHorizontalStretch(0);
sizePolicy5.setVerticalStretch(0);
sizePolicy5.setHeightForWidth(horizontalScrollBar->sizePolicy().hasHeightForWidth());
horizontalScrollBar->setSizePolicy(sizePolicy5);
horizontalScrollBar->setOrientation(Qt::Horizontal);
gridLayout_6->addWidget(horizontalScrollBar, 1, 0, 1, 1);
gridLayout->addWidget(mapFrame, 1, 0, 3, 1);
gridLayout->setRowStretch(0, 1);
gridLayout->setRowStretch(1, 4);
gridLayout->setRowStretch(2, 4);
gridLayout->setColumnStretch(0, 2);
gridLayout->setColumnMinimumWidth(0, 1);
gridLayout->setColumnMinimumWidth(1, 1);
gridLayout->setRowMinimumHeight(0, 1);
gridLayout->setRowMinimumHeight(1, 1);
gridLayout->setRowMinimumHeight(2, 10);
gridLayout->setRowMinimumHeight(3, 1);
MainWindow->setCentralWidget(centralwidget);
menubar = new QMenuBar(MainWindow);
menubar->setObjectName(QString::fromUtf8("menubar"));
menubar->setGeometry(QRect(0, 0, 819, 20));
MainWindow->setMenuBar(menubar);
statusbar = new QStatusBar(MainWindow);
statusbar->setObjectName(QString::fromUtf8("statusbar"));
MainWindow->setStatusBar(statusbar);
retranslateUi(MainWindow);
abortButton->setDefault(false);
autoButton->setDefault(false);
pauseButton->setDefault(false);
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
void retranslateUi(QMainWindow *MainWindow)
{
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr));
selectColonyButton->setText(QCoreApplication::translate("MainWindow", "Andere Kolonie ...", nullptr));
currentColonyLabel->setText(QCoreApplication::translate("MainWindow", "Aktuelle Kolonie: Lithium-Stollen 13-A", nullptr));
misisonControlsLabel->setText(QCoreApplication::translate("MainWindow", "Missionsablauf", nullptr));
startButton->setText(QCoreApplication::translate("MainWindow", "Start", nullptr));
abortButton->setText(QCoreApplication::translate("MainWindow", "Abbruch", nullptr));
singleStepButton->setText(QCoreApplication::translate("MainWindow", "Einzelschritt", nullptr));
autoButton->setText(QCoreApplication::translate("MainWindow", "Automatik", nullptr));
pauseButton->setText(QCoreApplication::translate("MainWindow", "Anhalten", nullptr));
downButton->setText(QString());
upButton->setText(QString());
waitButton->setText(QCoreApplication::translate("MainWindow", "W", nullptr));
leftButton->setText(QString());
deleteButton->setText(QCoreApplication::translate("MainWindow", "<", nullptr));
rightButton->setText(QString());
botControlsLabel->setText(QCoreApplication::translate("MainWindow", "Programmieren", nullptr));
repeatDropdown->setItemText(0, QCoreApplication::translate("MainWindow", "1", nullptr));
repeatDropdown->setItemText(1, QCoreApplication::translate("MainWindow", "2", nullptr));
repeatDropdown->setItemText(2, QCoreApplication::translate("MainWindow", "3", nullptr));
repeatDropdown->setItemText(3, QCoreApplication::translate("MainWindow", "4", nullptr));
repeatDropdown->setItemText(4, QCoreApplication::translate("MainWindow", "5", nullptr));
repeatDropdown->setCurrentText(QCoreApplication::translate("MainWindow", "1", nullptr));
botCommandTextEdit->setPlaceholderText(QCoreApplication::translate("MainWindow", "Textfeld", nullptr));
label->setText(QCoreApplication::translate("MainWindow", "Umgebungskarte", nullptr));
} // retranslateUi
};
namespace Ui {
class MainWindow: public Ui_MainWindow {};
} // namespace Ui
QT_END_NAMESPACE
#endif // PATCHBOT_GUI_H
Since I'm working with VS 2019 and have the "QT VS Tools" extension installed, I could also use the new "Convert Project to Qmake generated Project" tool. The problem here is, that I don't really know how it works.
I opened my .pro file from Qt Designer with the "Open Qt project file..." funciton and then used the tool mentioned above. After the process finished, I didn't see any changes or new files. Did I miss something? Do I need to do something else?
If further information is needed, I'd be happy to help.
Basically the minimal Qt stand-alone example is two source files, one header file, and the UI XML file:
There's the "main" Source file which contains the main function
There's a "main window" source file which contains the implementation for the main window
There's the "main window" header file
And as mentioned there's the UI XML file
The "main" source file could be very simple, all it does is create a QApplication object and the MainWindow object. It shows the main window and then enters the application event loop:
#include <QApplication>
#include "MainWindow.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow window;
window.show(); // Show the main window
return app.exec(); // Run the application event loop
}
The "main window" header file start out also very simple:
#include <QMainWindow>
namespace Ui
{
class MainWindow; // This is the "main window" from the UI XML file
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent = nullptr);
~MainWindow();
private:
Ui::MainWindow* ui; // Pointer to the UI generated by the XML file and the uic program
};
And finally the "main window" implementation source file:
#include "MainWindow.h"
#include "ui_mainwindow.h" // This is the automatically generated header file created by uic
MainWindow::MainWindow(QWidget* parent /* = nullptr */)
: QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
You also need to run the uic program to generate the needed source files from the UI XML file, and then build with the source file. You also need to use the moc tool to resolve slots and signals.
All of this should really be in most tutorials you will find online.

How to rescale GraphicItems without disturbing the shape?

How to rescale GraphicItems without disturbing the shape?
I am trying to rescale QGraphicsItems from a part of a scene. When I rescale the QGraphicsItem it transforms the shape.
QList<QGraphicsItem*> items;
for (auto it : items)
{
qreal scale = 1.75;
QPointF c = it->mapToScene(it->boundingRect().center());
it->setScale(scale);
QPointF cNew = it->mapToScene((it->boundingRect()).center());
QPointF offset = c - cNew;
it->moveBy(offset.x(), offset.y());
}
Create QGraphicsItemGroup which is containing all your items and apply the transform on it.
You just have to recenter the group according to the previous position.
QList<QGraphicsItem*> makeItems()
{
return QList<QGraphicsItem*>() << new QGraphicsLineItem(0, 0, 50, 0)
<< new QGraphicsLineItem(50, 0, 50, 50)
<< new QGraphicsLineItem(50, 50, 0, 50)
<< new QGraphicsLineItem(0, 50, 0, 0)
<< new QGraphicsRectItem(24, 24, 2, 2)
<< new QGraphicsEllipseItem(0, 0, 50, 50);
}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QGraphicsScene* scene = new QGraphicsScene(-500, -500, 1000, 1000);
auto* view = new QGraphicsView;
view->setScene(scene);
QList<QGraphicsItem*> items, originalItems;
originalItems = makeItems();
items = makeItems();
QGraphicsItemGroup* itemGroup = new QGraphicsItemGroup;
for (auto* item: originalItems)
{
scene->addItem(item);
}
for (auto* item: items)
{
itemGroup->addToGroup(item);
}
scene->addItem(itemGroup);
qreal scale = 1.75;
QPointF const beforeScale = itemGroup->mapToScene(itemGroup->boundingRect().center());
itemGroup->setScale(scale);
QPointF const afterScale = itemGroup->mapToScene(itemGroup->boundingRect().center());
QPointF const offset = beforeScale - afterScale;
itemGroup->moveBy(offset.x(), offset.y()); // The center will be the same than before the scaling
view->show();
return app.exec();
}
Use QPen::setCosmetic to keep the look-and-feel before and after the transformation

Positioning a top-level object relative to another

I need to position a top-level object so that it always remains in a position relative to another top-level object. As an example, the rectangle in the image below should stick to the "front" of the ellipse:
When rotated 180 degrees, it should look like this:
Instead, the position of the rectangle is incorrect:
Please run the example below (the use of QGraphicsScene is for demonstration purposes only, as the actual use case is in physics).
#include <QtWidgets>
class Scene : public QGraphicsScene
{
Q_OBJECT
public:
Scene()
{
mEllipse = addEllipse(0, 0, 25, 25);
mEllipse->setTransformOriginPoint(QPointF(12.5, 12.5));
QGraphicsLineItem *line = new QGraphicsLineItem(QLineF(0, 0, 0, -12.5), mEllipse);
line->setPos(12.5, 12.5);
mRect = addRect(0, 0, 10, 10);
mRect->setTransformOriginPoint(QPointF(5, 5));
line = new QGraphicsLineItem(QLineF(0, 0, 0, -5), mRect);
line->setPos(5, 5);
connect(&mTimer, SIGNAL(timeout()), this, SLOT(timeout()));
mTimer.start(5);
}
public slots:
void timeout()
{
mEllipse->setRotation(mEllipse->rotation() + 0.5);
QTransform t;
t.rotate(mEllipse->rotation());
qreal relativeX = mEllipse->boundingRect().width() / 2 - mRect->boundingRect().width() / 2;
qreal relativeY = -mRect->boundingRect().height();
mRect->setPos(mEllipse->pos() + t.map(QPointF(relativeX, relativeY)));
mRect->setRotation(mEllipse->rotation());
}
public:
QTimer mTimer;
QGraphicsEllipseItem *mEllipse;
QGraphicsRectItem *mRect;
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QGraphicsView view;
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
view.setScene(new Scene);
view.resize(200, 200);
view.show();
return app.exec();
}
#include "main.moc"
Note that the position of the rectangle is not always the same, but it should always remain in the same position relative to the ellipse. For example, it may start off in this position:
But it should stay in that relative position when rotated:
If you want the two objects to keep the same relative position, they need to rotate around the same origin point.
Here your circle rotates around its center (the point 12.5, 12.5), but your rectangle rotates around another origin (5,5) instead of the circle's center (12.5, 12.5).
If you fix the origin, it'll work as you expect:
mRect->setTransformOriginPoint(QPointF(12.5, 12.5));
Even if the rectangle starts off with an offset:
mRect = addRect(-10, 0, 10, 10); // Start 10 units to the left