I started using Qt5 a little while ago and I don't know how to set the position of my drawing in my window.
I have a Drawing class which is a QWidget and which contains my paintEvent() function and other functions; and a MainWindow class which contains some widgets. So in order to display my Qpainter, I have to include it in my layout, which is the main window layout, but the window doesn't adapt to the Qpainter at all; the dimensions of the buttons, sliders .. adapt so as to organize themselves and occupy all the space of my window, but they totally ignore my Qpainter and it partly disappears if I put at least 2 widgets.
Do you have any solutions to better manage the position of these elements?
main.cpp :
#include <QtGui>
#include <QApplication>
#include "mywidget.h"
int main( int argc, char **argv )
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
mywidget.h :
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QtGui>
#include <QWidget>
#include <QSlider>
#include <QScrollBar>
#include <QApplication>
#include <QGridLayout>
#include <QObject>
#include <QPoint>
#include <QLabel>
#include <QPolygon>
class Drawing : public QWidget
{
Q_OBJECT
public:
Drawing();
void paintEvent(QPaintEvent* e);
public slots:
void slide(int abscisse);
void rotate();
private:
QPoint o;
QPoint a;
QPoint b;
QPoint c;
QPoint d;
};
//--------------------------------------
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow();
private:
QSlider* m_slider1;
QSlider* m_slider2;
QGridLayout* m_layout;
Drawing* m_dessin;
};
#endif // MYWIDGET_H
mywidget.cpp :
#include "mywidget.h"
#include <iostream> //POUR LES TESTS
MainWindow::MainWindow() : QWidget()
{
setGeometry(330, 140, 840, 620);
m_slider1 = new QSlider(Qt::Horizontal, this);
m_slider1->setRange(150, 650);
m_slider1->setSliderPosition(400);
m_slider2 = new QSlider(Qt::Horizontal, this);
m_slider2->setSliderPosition(50);
m_layout = new QGridLayout;
m_layout->addWidget(new QLabel("Translation Horizontale"), 1, 0);
m_layout->addWidget(m_slider1, 2, 0);
m_layout->addWidget(new QLabel("Rotation"), 0, 1);
m_layout->addWidget(m_slider2, 1, 1);
m_dessin = new Drawing;
m_layout->addWidget(m_dessin, 0, 0);
setLayout(m_layout);
QObject::connect(m_slider1, SIGNAL(valueChanged(int)), m_dessin, SLOT(slide(int)));
QObject::connect(m_slider2, SIGNAL(valueChanged(int)), m_dessin, SLOT(rotate()));
}
//--------------------------------------------------------
Drawing::Drawing() : QWidget(), o(400, 150), a(o.x()-50 , o.y()-50), b(o.x()+50 , o.y()-50), c(o.x()+50 , o.y()+50), d(o.x()-50 , o.y()+50) {}
void Drawing::paintEvent(QPaintEvent *e) {
QPolygon poly;
poly << a << b << c << d;
QWidget::paintEvent(e); // effectue le comportement standard
QPainter painter(this); // construire
painter.setPen( QPen(Qt::white, 2) ); // personnaliser
painter.drawPolygon(poly); // dessiner
}
void Drawing::slide(int abscisse) {
if (a == QPoint(o.x()-50 , o.y()-50)) {
o.setX(abscisse);
a.setX(o.x()-50);
b.setX(o.x()+50);
c.setX(o.x()+50);
d.setX(o.x()-50);
}
else {
o.setX(abscisse);
a.setX(o.x());
b.setX(o.x()+75);
c.setX(o.x());
d.setX(o.x()-75);
}
update();
}
void Drawing::rotate() {
if (a == QPoint(o.x()-50 , o.y()-50)) {
a = QPoint(o.x() , o.y()+75);
b = QPoint(o.x()+75 , o.y());
c = QPoint(o.x() , o.y()-75);
d = QPoint(o.x()-75 , o.y());
}
else {
a = QPoint(o.x()-50 , o.y()-50);
b = QPoint(o.x()+50 , o.y()-50);
c = QPoint(o.x()+50 , o.y()+50);
d = QPoint(o.x()-50 , o.y()+50);
}
update();
}
Snapshots:
After having seen the snapshots of OP, I thought about what might been happen.
The layout of OP doesn't look that wrong. I still believe that the layout plays only a minor role in OPs issue.
I tried to reproduce OPs issue with an even smaller MCVE of mine.
My testQGridLayout
:
#include <QtWidgets>
class Drawing: public QFrame {
public:
Drawing(QWidget *pQParent = nullptr);
virtual ~Drawing() = default;
Drawing(const Drawing&) = delete;
Drawing& operator=(const Drawing&) = delete;
protected:
virtual void paintEvent(QPaintEvent *pQEvent) override;
};
Drawing::Drawing(QWidget* pQParent):
QFrame(pQParent)
{
setFrameStyle(Box | Plain);
}
void Drawing::paintEvent(QPaintEvent* pQEvent)
{
{ QPainter qPainter(this);
qPainter.drawText(QPoint(40, 40),
QString("Size: %1 x %2").arg(width()).arg(height()));
qPainter.setPen(Qt::red);
qPainter.drawRect(300, 100, 200, 200);
}
// call base class paint event to keep it working
QFrame::paintEvent(pQEvent);
}
class MainWindow: public QWidget {
public:
MainWindow(QWidget *pQParent = nullptr);
virtual ~MainWindow() = default;
MainWindow(const MainWindow&) = delete;
MainWindow& operator=(const MainWindow&) = delete;
private:
QGridLayout _qGrid;
Drawing _qDrawing;
QSlider _qSliderT;
QSlider _qSliderR;
};
MainWindow::MainWindow(QWidget *pQParent):
QWidget(pQParent),
_qSliderT(Qt::Horizontal),
_qSliderR(Qt::Horizontal)
{
resize(840, 620);
_qGrid.addWidget(&_qDrawing, 0, 0);
_qGrid.addWidget(new QLabel("Translation Horizontal"), 1, 0);
_qSliderT.setRange(150, 650);
_qSliderT.setSliderPosition(400);
_qGrid.addWidget(&_qSliderT, 2, 0);
_qGrid.addWidget(new QLabel("Rotation"), 1, 1);
_qSliderR.setSliderPosition(50);
_qGrid.addWidget(&_qSliderR, 2, 1);
setLayout(&_qGrid);
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
MainWindow qWinMain;
qWinMain.setWindowTitle("Test QGridLayout");
qWinMain.show();
// runtime loop
return app.exec();
}
Output:
Qt Version: 5.15.1
I made some changes to exclude what is a possible issue and what not.
I derived my Drawing
from QFrame. Thus, it was easy to give it a visible border. As expected, my Drawing _qDrawing
occupies only the space above the first slider (QSlider _qSliderT;
in my case).
I added output of widget size to the Drawing::paintEvent()
to see its size. Then I added the painting of a red rectangle. For that, I cared to cover a space which is partly inside the widget and partly below and right of it.
This is what I conclude:
As exposed in the OPs code, the layout should be the same.
OPs rectangle is always drawn at the same coordinates. Hence, it doesn't get visible until the Drawing
grows large enough (with the main window).
The origin of the QPainter
(i.e. QPoint(0, 0)
) is the upper left corner of the widget. This can be changed by applying transformations but I couldn't see this in OPs code. (The effect of the sliders, I neglect for now.)
Though, there are still some things which are not clear to me:
The Drawing
should clip the painting. Hence, I wonder, how OPs rectangle can appear over the rotate slider. Either, the OP used a span for the Drawing m_dessin
, or the widget doesn't clip painting on the paint engine the OP uses. (The look is quite different than mine. Thus, it might be a different platform.)
The layout which can be seen in OPs snapshots doesn't match the exposed code. In OPs snapshot, the Drawing
occupies all extra space resulting from growing the main window. This is only possible when QGridLayout::setRowStretch()/GridLayout::setColumnStretch() had been used (as recommended in my first comment). However, the exposed code doesn't contain them.
To check this out, I changed the layout in MainWindow::MainWindow()
:
MainWindow::MainWindow(QWidget *pQParent):
QWidget(pQParent),
_qSliderT(Qt::Horizontal),
_qSliderR(Qt::Horizontal)
{
resize(840, 620);
_qGrid.setRowStretch(0, 1);
_qGrid.setColumnStretch(0, 1);
_qGrid.addWidget(&_qDrawing, 0, 0, 1, 2);
_qGrid.addWidget(new QLabel("Translation Horizontal"), 1, 0);
_qSliderT.setRange(150, 650);
_qSliderT.setSliderPosition(400);
_qGrid.addWidget(&_qSliderT, 2, 0);
_qGrid.addWidget(new QLabel("Rotation"), 1, 1);
_qSliderR.setSliderPosition(50);
_qGrid.addWidget(&_qSliderR, 2, 1);
setLayout(&_qGrid);
}
Output:
Now, the layout seems to match the one of OPs snapshots.
Trying resize:
This looks exactly as it should:
Drawing _qDrawing
shrinks and grows with the main window sizeDrawing _qDrawing
becomes too small to cover it.Final Conclusion:
There is nothing wrong in OPs layout.
IMHO, OP is not yet fully clear about how coordinate systems apply in QPainter
.
For this, I can warmly recommend an extra page of the Qt online doc., precisely dedicated to this topic:
Continuation:
How to add a vertical slider:
class MainWindow: public QWidget {
public:
MainWindow(QWidget *pQParent = nullptr);
virtual ~MainWindow() = default;
MainWindow(const MainWindow&) = delete;
MainWindow& operator=(const MainWindow&) = delete;
private:
QGridLayout _qGrid;
Drawing _qDrawing;
QSlider _qSliderV;
QSlider _qSliderT;
QSlider _qSliderR;
};
MainWindow::MainWindow(QWidget *pQParent):
QWidget(pQParent),
_qSliderV(Qt::Vertical),
_qSliderT(Qt::Horizontal),
_qSliderR(Qt::Horizontal)
{
resize(840, 620);
_qGrid.setRowStretch(0, 1);
_qGrid.setColumnStretch(0, 1);
_qGrid.addWidget(&_qDrawing, 0, 0, 1, 2);
_qGrid.addWidget(&_qSliderV, 0, 2);
_qGrid.addWidget(new QLabel("Translation Horizontal"), 1, 0);
_qSliderT.setRange(150, 650);
_qSliderT.setSliderPosition(400);
_qGrid.addWidget(&_qSliderT, 2, 0);
_qGrid.addWidget(new QLabel("Rotation"), 1, 1, 1, 2);
_qSliderR.setSliderPosition(50);
_qGrid.addWidget(&_qSliderR, 2, 1, 1, 2);
setLayout(&_qGrid);
}
Output:
To achieve this specific layout, I placed the _qSliderV
into column 2 and gave _qSliderR
(and its label) a column span of 2 as well.
To illustrate this, I added a sketch of the resulting grid to the above snapshot: