• QT OpenGL (1)2D Painting Example


    2D Painting Example

    为方便查阅,此文是原网站文档翻译与整理,如有侵权,请与本人联系。
    官网



    2D Painting示例展示了QPainter和QOpenGLWidget如何一起使用,以在支持的硬件上显示加速的2D图形。

    在这里插入图片描述

    QPainter类用于在QPaintDevice子类(如QWidget和QImage)提供的绘制设备上绘制2D图形基元。
    由于QOpenGLWidget是QWidget的子类,因此可以重新实现其paintEvent()并使用QPainter在设备上绘制,就像使用QWidget一样。唯一的区别是,如果系统的OpenGL驱动程序支持,绘制操作将在硬件中加速。
    在本例中,我们对QWidget和QOpenGLWidget执行相同的绘制操作。QWidget显示为启用了抗锯齿,如果系统的OpenGL驱动程序支持所需的扩展,QOpenGLWidget也将使用抗锯齿。

    概述

    为了能够将在QOpenGLWidget子类上绘制的结果与在QWidget子类中的本地绘制进行比较,我们希望并排显示这两种小部件。为此,我们派生了QWidget和QOpenGLWidget的子类,使用一个单独的Helper类为每个子类执行相同的绘制操作,并将它们放在一个顶级小部件中,该小部件本身提供了一个Window类。

    Helper类定义

    在本例中,绘制操作由辅助类执行。我们这样做是因为我们希望对QWidget子类和QOpenGLWidget子类执行相同的绘制操作。
    Helper类是最小的:

    class Helper
    {
    public:
        Helper();
    
    public:
        void paint(QPainter *painter, QPaintEvent *event, int elapsed);
    
    private:
        QBrush background;
        QBrush circleBrush;
        QFont textFont;
        QPen circlePen;
        QPen textPen;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    除了构造函数之外,它只提供了一个paint()函数来使用我们的一个小部件子类提供的painter进行绘制。

    Helper类实现

    类的构造函数设置将内容绘制到小部件上所需的资源:

    Helper::Helper()
    {
        QLinearGradient gradient(QPointF(50, -20), QPointF(80, 20));
        gradient.setColorAt(0.0, Qt::white);
        gradient.setColorAt(1.0, QColor(0xa6, 0xce, 0x39));
    
        background = QBrush(QColor(64, 32, 64));
        circleBrush = QBrush(gradient);
        circlePen = QPen(Qt::black);
        circlePen.setWidth(1);
        textPen = QPen(Qt::white);
        textFont.setPixelSize(50);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    实际绘制是在paint()函数中执行的。这需要一个已经设置为在绘制设备(QWidget或QOpenGLWidget)上绘制的QPainter,一个提供要绘制区域信息的QPaintEvent,以及自上次更新绘制设备以来经过的时间(以毫秒为单位)

    void Helper::paint(QPainter *painter, QPaintEvent *event, int elapsed)
    {
        painter->fillRect(event->rect(), background);
        painter->translate(100, 100);
    /*
    *在平移坐标系的原点之前,我们通过填充绘制事件中包含的区域来开始绘制,以便将其余的绘制操作移向绘制设备的中心。
    *我们绘制了一个圆形的螺旋图案,使用指定的运行时间为它们设置动画,使它们看起来向外移动并围绕坐标系的原点移动:
    */
    	painter->save();
        painter->setBrush(circleBrush);
        painter->setPen(circlePen);
        painter->rotate(elapsed * 0.030);
    
        qreal r = elapsed / 1000.0;
        int n = 30;
        for (int i = 0; i < n; ++i) {
            painter->rotate(30);
            qreal factor = (i + r) / n;
            qreal radius = 0 + 120.0 * factor;
            qreal circleRadius = 1 + factor * 20;
            painter->drawEllipse(QRectF(radius, -circleRadius,circleRadius * 2, circleRadius * 2));
        }
        painter->restore();
    /*
    *由于坐标系在这个过程中旋转了很多次,我们事先save() QPainter的状态,然后restore()。
    */
    	painter->setPen(textPen);
        painter->setFont(textFont);
        painter->drawText(QRect(-50, -50, 100, 100), Qt::AlignCenter, QStringLiteral("Qt"));
    /*
    *我们在原点绘制一些文本来完成效果。
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    Widget类定义

    Widget类提供了一个基本的自定义小部件,用于显示Helper类绘制的简单动画

    class Helper;
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(Helper *helper, QWidget *parent);
    
    public slots:
        void animate();
    
    protected:
        void paintEvent(QPaintEvent *event) override;
    
    private:
        Helper *helper;
        int elapsed;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    除了构造函数之外,它只包含一个paintEvent()函数,该函数允许我们绘制自定义内容,以及一个用于为其内容设置动画的槽。一个成员变量跟踪小部件用来绘制其内容的Helper,另一个记录自上次更新以来所用的时间。

    Widget类实现

    构造函数只初始化成员变量,存储提供的Helper对象并调用基类的构造函数,并强制小部件的固定大小:

    Widget::Widget(Helper *helper, QWidget *parent)
        : QWidget(parent), helper(helper)
    {
        elapsed = 0;
        setFixedSize(200, 200);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    每当我们稍后定义的计时器超时时,就会调用animate()槽:

    void Widget::animate()
    {
        elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
        update();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里,我们确定自计时器上次超时以来所经过的时间间隔,并在重新绘制小部件之前将其添加到任何现有值中。由于Helper类中使用的动画每秒循环一次,因此我们可以使用模运算符来确保经过的变量始终小于1000。
    由于Helper类完成了所有实际绘制,我们只需要实现一个绘制事件,该事件为小部件设置一个QPainter并调用Helper的paint()函数:

    void Widget::paintEvent(QPaintEvent *event)
    {
        QPainter painter;
        painter.begin(this);
        painter.setRenderHint(QPainter::Antialiasing);
        helper->paint(&painter, event, elapsed);
        painter.end();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    GLWidget类定义

    GLWidget类的定义与Widget类基本相同,只是它是从QOpenGLWidget派生的。

    class Helper;
    
    class GLWidget : public QOpenGLWidget
    {
        Q_OBJECT
    
    public:
        GLWidget(Helper *helper, QWidget *parent);
    
    public slots:
        void animate();
    
    protected:
        void paintEvent(QPaintEvent *event) override;
    
    private:
        Helper *helper;
        int elapsed;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    同样,成员变量记录用于绘制小部件的Helper以及自上次更新以来经过的时间。

    GLWidget类实现

    构造函数与Widget类的构造函数略有不同:

    GLWidget::GLWidget(Helper *helper, QWidget *parent)
        : QOpenGLWidget(parent), helper(helper)
    {
        elapsed = 0;
        setFixedSize(200, 200);
        setAutoFillBackground(false);// 多了这个设置
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    除了初始化逝去的成员变量和存储用于绘制widget的Helper对象外,还使用指定QGL::SampleBuffers标志的格式调用基类的构造函数。如果系统的OpenGL驱动程序支持,则可以启用抗锯齿功能。
    animate()插槽与Widget类提供的插槽完全相同:

    void GLWidget::animate()
    {
        elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
        update();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    paintEvent()与Widget类中的几乎相同:

    void GLWidget::paintEvent(QPaintEvent *event)
    {
        QPainter painter;
        painter.begin(this);
        painter.setRenderHint(QPainter::Antialiasing);
        helper->paint(&painter, event, elapsed);
        painter.end();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    由于如果可用,将启用抗锯齿,我们只需要在小部件上设置一个QPainter,并调用助手的paint()函数来显示小部件的内容。

    Window 类定义

    Window类有一个基本的、最小的定义:

    class Window : public QWidget
    {
        Q_OBJECT
    
    public:
        Window();
    
    private:
        Helper helper;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    它包含一个将在所有widgets之间共享的Helper对象。

    Window 类实现

    构造函数完成所有的工作,创建每种类型的widget ,并将它们与标签一起插入到布局中:

    Window::Window()
    {
        setWindowTitle(tr("2D Painting on Native and OpenGL Widgets"));
    
        Widget *native = new Widget(&helper, this);
        GLWidget *openGL = new GLWidget(&helper, this);
        QLabel *nativeLabel = new QLabel(tr("Native"));
        nativeLabel->setAlignment(Qt::AlignHCenter);
        QLabel *openGLLabel = new QLabel(tr("OpenGL"));
        openGLLabel->setAlignment(Qt::AlignHCenter);
    
        QGridLayout *layout = new QGridLayout;
        layout->addWidget(native, 0, 0);
        layout->addWidget(openGL, 0, 1);
        layout->addWidget(nativeLabel, 1, 0);
        layout->addWidget(openGLLabel, 1, 1);
        setLayout(layout);
    
        QTimer *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, native, &Widget::animate);
        connect(timer, &QTimer::timeout, openGL, &GLWidget::animate);
        timer->start(50);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    一个超时50毫秒的计时器是为动画目的而构建的,并连接到Widget和GLWidget对象的animate()插槽。一旦启动,小部件应该以每秒20帧左右的速度更新。

    运行示例

    该示例显示了在Widget和GLWidget中同时执行的相同绘制操作。GLWidget中渲染的质量和速度取决于系统的OpenGL驱动程序对多采样和硬件加速的支持程度。如果缺乏对其中任何一个的支持,驱动程序可能会依赖于软件渲染器,该软件渲染器可能会以质量换取速度。

  • 相关阅读:
    CNN详解——反向传播过程
    007 OpenCV霍夫变换
    C++---多态
    51单片机可调幅度频率波形信号发生器( proteus仿真+程序+原理图+报告+讲解视频)
    一、初识Docker
    Allavsoft Video Downloader Converter Mac(视频下载工具)
    EfficientNet-V1论文阅读笔记
    复盘:图像有哪些基本属性?关于图像的知识你知道哪些?图像的参数有哪些
    KVM 虚机镜像操作, 扩容和压缩
    css_易忘点总结
  • 原文地址:https://blog.csdn.net/gb11235/article/details/134075531