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
我进行了一些更改,以排除可能出现的问题和可能出现的问题。
我的 Drawing
来自 QFrame。因此,很容易给它一个可见的边界。正如预期的那样,我的 Drawing _qDrawing
仅占据第一个滑块上方的 space(在我的例子中是 QSlider _qSliderT;
)。
我将小部件大小的输出添加到 Drawing::paintEvent()
以查看其大小。然后我添加了一个红色矩形的绘画。为此,我想覆盖一个 space,它部分位于小部件内部,部分位于小部件的下方和右侧。
这是我的结论:
正如OPs代码中所暴露的那样,布局应该是一样的。
OPs 矩形总是在相同的坐标处绘制。因此,直到 Drawing
变得足够大(主要的 window)才可见。
QPainter
(即 QPoint(0, 0)
)的原点是小部件的左上角。这可以通过应用转换来更改,但我在 OP 代码中看不到这一点。 (滑块的影响,我暂时忽略。)
虽然,还有一些事情我不太清楚:
Drawing
应该剪下画。因此,我想知道 OPs 矩形如何出现在旋转滑块上。或者,OP 使用了 Drawing m_dessin
的跨度,或者小部件不会在 OP 使用的绘画引擎上剪辑绘画。 (看起来和我的很不一样。因此,它可能是一个不同的平台。)
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 在线文档的额外页面,专门针对该主题:
续:
如何添加垂直滑块:
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 的列跨度。
为了说明这一点,我在上面的快照中添加了生成的网格草图:
我刚开始使用 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
我进行了一些更改,以排除可能出现的问题和可能出现的问题。
我的
Drawing
来自 QFrame。因此,很容易给它一个可见的边界。正如预期的那样,我的Drawing _qDrawing
仅占据第一个滑块上方的 space(在我的例子中是QSlider _qSliderT;
)。我将小部件大小的输出添加到
Drawing::paintEvent()
以查看其大小。然后我添加了一个红色矩形的绘画。为此,我想覆盖一个 space,它部分位于小部件内部,部分位于小部件的下方和右侧。
这是我的结论:
正如OPs代码中所暴露的那样,布局应该是一样的。
OPs 矩形总是在相同的坐标处绘制。因此,直到
Drawing
变得足够大(主要的 window)才可见。
QPainter
(即 QPoint(0, 0)
)的原点是小部件的左上角。这可以通过应用转换来更改,但我在 OP 代码中看不到这一点。 (滑块的影响,我暂时忽略。)
虽然,还有一些事情我不太清楚:
Drawing
应该剪下画。因此,我想知道 OPs 矩形如何出现在旋转滑块上。或者,OP 使用了Drawing m_dessin
的跨度,或者小部件不会在 OP 使用的绘画引擎上剪辑绘画。 (看起来和我的很不一样。因此,它可能是一个不同的平台。)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 在线文档的额外页面,专门针对该主题:
续:
如何添加垂直滑块:
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 的列跨度。
为了说明这一点,我在上面的快照中添加了生成的网格草图: