2

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:

Snapshot 1

Snapshot 2

Snapshot 3

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
Arcane
  • 33
  • 4
  • Try `m_layout->setRowStretch(0, 1); m_layout->setColumnStretch(0, 1);` to tell your `QGridLayout` that cell 0, 0 is intended to grow. ([QGridLayout::setRowStretch()](https://doc.qt.io/qt-5/qgridlayout.html#setRowStretch), [QGridLayout::setColumnStretch()](https://doc.qt.io/qt-5/qgridlayout.html#setColumnStretch)) – Scheff's Cat Feb 05 '21 at 18:53
  • Maybe, I overlooked something - or I misunderstood something. Is it the `Drawing` _widget_ which doesn't grow while resizing the window, or is it the _drawing_ in the `Drawing` widget that doesn't grow? As far as I see (debugging by eyes) width of `Drawing` _should_ grow with `m_slider1`. Could you add snapshots to your question? (I'd be glad to help to embed the images visibly.) – Scheff's Cat Feb 06 '21 at 08:10
  • Here is some pictures, the 2nd slider is tiny because I added the row and column stretch. https://ibb.co/XVtbB2K https://ibb.co/vVQFTTK https://ibb.co/C5hyctf – Arcane Feb 06 '21 at 10:05

2 Answers2

1

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

Snapshot of testQGridLayout.exe

I made some changes to exclude what is a possible issue and what not.

  1. 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).

  2. 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:

  1. As exposed in the OPs code, the layout should be the same.

  2. 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:

  1. 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.)

  2. 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:

Snapshot of testQGridLayout.exe after modification

Now, the layout seems to match the one of OPs snapshots.

Trying resize:

Video of testQGridLayout.exe while resizing

This looks exactly as it should:

  • the Drawing _qDrawing shrinks and grows with the main window size
  • the painting is clipped if the size of Drawing _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:

Qt Doc.: Coordinate System


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:

Snapshot of testQGridLayout.exe with add. vertical slider

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:

Snapshot of testQGridLayout.exe with grid sketch

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • Thank you for your answers, I have still a lot to learn ! I just wonder one more thing; I want to add a vertical slider on the right side of the window, but when I place it at coordinates (0, 1), it goes on the left side of the (0, 1) "box". Do you know how to move it to the right ? https://ibb.co/XYMRntD – Arcane Feb 06 '21 at 15:07
  • @Arcane I extended my answer. Please, keep in mind that the grid cells are like rubber bands. First, the `QGridLayout` tries to tighten the rows and columns as close as possible according to all attached widgets. If the resulting space is less than the available space, the rest is distributed according to the stretch factors. (Higher stretch factors get more of that.) – Scheff's Cat Feb 06 '21 at 15:52
  • @Arcane Beside of this, there is also an alignment parameter in the `attach` functions. I need/use it rarely but it could be used to align a widget inside the cells it is attached to. – Scheff's Cat Feb 06 '21 at 15:57
0

Which behavior would you like for your Drawing widget? By default it will be resized freely by the layout, you can change this by using QWidget::sizeHint() and QWidget::sizePolicy(). You'll find detailed information in Qt documentation about custom widgets and layouts.

Kusa
  • 11
  • 3
  • (First, sorry if my english isn't clear) So, when I resize my window manually, the widgets adapt themselves continuously, thanks to layout. But the Drawing doesn't, it's like when you put a button without a layout to contain it. I'll try that solution though. – Arcane Feb 05 '21 at 20:18