Qt5:显示带有布局的 Qpainter

Qt5 : Displaying a Qpainter with layouts

我刚开始使用 Qt5,但我不知道如何在我的 window 中设置绘图的位置。

我有一个 Drawing class,它是一个 QWidget 并且包含我的 paintEvent() 函数和其他函数;和一个包含一些小部件的 MainWindow class。因此,为了显示我的 Qpainter,我必须将它包含在我的布局中,这是主要的 window 布局,但是 window 根本不适应 Qpainter;按钮、滑块的尺寸.. 适应以组织自己并占据我 window 的所有 space,但它们完全忽略了我的 Qpainter,如果我放置至少 2 个小部件,它会部分消失。

您有什么解决方案可以更好地管理这些元素的位置吗?

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

快照:

您希望 Drawing 小部件具有哪种行为? 默认情况下,它会根据布局自由调整大小,您可以使用 QWidget::sizeHint() and QWidget::sizePolicy(). You'll find detailed information in Qt documentation about custom widgets and layouts.

更改它

看完OP的快照后,我想到了可能会发生什么。

OP 的布局看起来不错。 我仍然认为布局在 OPs 问题中只起次要作用。

我试图用我的更小的 MCVE 重现 OP 问题。

我的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();
}

输出:

Qt Version: 5.15.1

我进行了一些更改,以排除可能出现的问题和可能出现的问题。

  1. 我的 Drawing 来自 QFrame。因此,很容易给它一个可见的边界。正如预期的那样,我的 Drawing _qDrawing 仅占据第一个滑块上方的 space(在我的例子中是 QSlider _qSliderT;)。

  2. 我将小部件大小的输出添加到 Drawing::paintEvent() 以查看其大小。然后我添加了一个红色矩形的绘画。为此,我想覆盖一个 space,它部分位于小部件内部,部分位于小部件的下方和右侧。

这是我的结论:

  1. 正如OPs代码中所暴露的那样,布局应该是一样的。

  2. OPs 矩形总是在相同的坐标处绘制。因此,直到 Drawing 变得足够大(主要的 window)才可见。

QPainter(即 QPoint(0, 0))的原点是小部件的左上角。这可以通过应用转换来更改,但我在 OP 代码中看不到这一点。 (滑块的影响,我暂时忽略。)

虽然,还有一些事情我不太清楚:

  1. Drawing应该剪下画。因此,我想知道 OPs 矩形如何出现在旋转滑块上。或者,OP 使用了 Drawing m_dessin 的跨度,或者小部件不会在 OP 使用的绘画引擎上剪辑绘画。 (看起来和我的很不一样。因此,它可能是一个不同的平台。)

  2. OPs快照中的布局与暴露的代码不匹配。在 OPs 快照中,Drawing 占用了所有额外的 space,这是由于主 window 的增长而产生的。这只有在使用 QGridLayout::setRowStretch()/GridLayout::setColumnStretch() 时才有可能(正如我在第一条评论中所建议的那样)。但是,公开的代码不包含它们。

为了检查这一点,我更改了 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);
}

输出:

现在,布局似乎与 OP 快照之一相匹配。

正在尝试调整大小:

这看起来和它应该的完全一样:

  • Drawing _qDrawing随主尺寸window收缩和增长
  • 如果 Drawing _qDrawing 的尺寸变得太小而无法覆盖它,那么这幅画将被剪裁。

最终结论:

OP布局没有问题

恕我直言,OP 尚不完全清楚坐标系如何应用于 QPainter

为此,我可以热烈推荐 Qt 在线文档的额外页面,专门针对该主题:

Qt Doc.: Coordinate System


续:

如何添加垂直滑块:

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

输出:

为了实现这个特定的布局,我将 _qSliderV 放入第 2 列,并给 _qSliderR(及其标签)也设置了 2 的列跨度。

为了说明这一点,我在上面的快照中添加了生成的网格草图: