• 《QT Creator快速入门》第十二章:3D绘图


      OpenGL是一套跨平台的用来渲染3D图形的API。OpenGL自身是一个巨大的状态机:一系列的变量描述OpenGL此刻应当如何运行,OpenGL的状态通常被称为OpenGL上下文(Context)。我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲,最后,我们使用当前OpenGL上下文来渲染。

      假设当我们想告诉OpenGL去画线段而不是三角形的时候,我们通过改变一些上下文变量来改变OpenGL状态,从而告诉OpenGL如何去绘图。一旦我们改变了OpenGL的状态为绘制线段,下一个绘制命令就会画出线段而不是三角形。

      当使用OpenGL的时候,我们会遇到一些状态设置函数,这类函数将会改变上下文。以及状态使用函数,这类函数会根据当前OpenGL的状态执行一些操作。比如下面例子中的glClearColor()函数是一个状态设置函数,而glClear()函数则是一个状态使用的函数,它使用了当前的状态来获取应该清除为的颜色。

      Qt中的QtOpenGL模块提供了使用OpenGL的方法,使用它需要在.pro项目文件中添加QT+=opengl。QGLWidget是QtOpenGL模块中的一个类,它是一个用来渲染OpenGL图形的部件,可以继承该类后来像使用其它QWidget部件一样使用它。QGLWidget提供了3个虚函数重写它们来实现指定操作:

        initializeGL():设置OpenGL渲染环境,定义显示列表等,只在resizeGL()、paintGL()之前被调用一次。

        resizeGL():设置OpenGL的视口,投影等,部件大小改变的时候被调用。

        paintGL():渲染OpenGL场景,当部件需要更新时被调用。

      Qt中有一个Hello GL示例程序,它在OpenGL分类中。

    1、基本绘制

      下面代码使用QGLWidget绘制了一个直线、三角形、文本,其中的MyGLWidget继承自QGLWidget:

    #include <QApplication>
    #include "myglwidget.h"
    
    int main(int argc, char**argv)
    {
        QApplication app(argc, argv);
    
        MyGLWidget w;
        w.resize(600, 400);
        w.show();
    
        return app.exec();
    }
    View Code
    #ifndef MYGLWIDGET_H
    #define MYGLWIDGET_H
    #include <QGLWidget>
    #include <GL/glu.h>
    
    class MyGLWidget: public QGLWidget
    {
        void initializeGL()
        {
            //void glClearColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha);
            glClearColor(1.0, 0.0, 0.0, 0.0); //设置清除屏幕(glClear()方法)时使用的颜色为红色
        
            glShadeModel(GL_SMOOTH); //设置阴影平滑
        
            //void glClearDepth(GLclampd depth);
            glClearDepth(1.0); //设置深度缓存(深度缓冲区中每个像素需要的值)
        
            glEnable(GL_DEPTH_TEST); //启用深度测试功能,根据坐标的远近自动隐藏被遮住的图形(材料)
        }
        void resizeGL(int w, int h)
        {
            //设置视口的大小:告诉OpenGL渲染窗口的尺寸大小
            //也可以将视口的维度设置为比窗口的维度小,这样OpenGL渲染将会在一个小窗口中显示,这样子我们也可以将一些其它元素显示在OpenGL视口之外
            glViewport(0, 0, (GLint)w, (GLint)h);
        
            glMatrixMode(GL_PROJECTION); //投影矩阵模式
            glLoadIdentity(); //重置当前指定的矩阵为单位矩阵,这样可以将矩阵恢复到初始状态。
            gluPerspective(45.0, (GLfloat)w / (GLfloat)h, 0.1, 100.0); //设置透视投影矩阵:视角为45度,纵横比为窗口的纵横比,最近和最远的位置分别为0.1和100
        
            glMatrixMode(GL_MODELVIEW); //设置模型视图矩阵
            glLoadIdentity(); //重置模型视图矩阵
        }
        void paintGL()
        {
            //在每个新的渲染迭代开始的时候我们总是希望清屏,否则我们仍能看见上一次迭代的渲染结果
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕:使用glClearColor()设置的颜色和glClearDepth()设置的深度缓存
            glLoadIdentity(); //重置模型视图矩阵,这样便将坐标原点移到了窗口中心
        
            GLfloat z_translate = -6.0;
            //void glTranslatef(GLfloat x,GLfloat y,GLfloat z);
            //移动坐标原点,是相对于当前点来移动的,对于z来说正数为向屏幕外移动(相当于在眼睛后方,所以就看不到了),负数为向屏幕内移动(绝对值越大离眼睛越远,看起来越小)
            glTranslatef(0.0, 0.0, z_translate); //将坐标原点内移6.0
        
            glBegin(GL_LINES); //开始绘制直线
            glVertex3f(0.0, 1.0, 0.0);
            glVertex3f(1.0, 1.0, 0.0);
            glEnd();
        
            glBegin(GL_TRIANGLES); //开始绘制三角形
            //逆时针绘制各顶点
            //void glVertex3f(GLfloat x,GLfloat y,GLfloat z);
            glVertex3f(0.0, 1.0, 0.0);
            glVertex3f(-1.0, -1.0, 0.0);
            glVertex3f(1.0, -1.0, 0.0);
            glEnd();
        
            //void renderText(double x, double y, double z, const QString & str,
            //                 const QFont & fnt = QFont(), int listBase = 2000);
            renderText(0.0, 2.0, 0.0, "test"); //绘制文本
        }
    };
    
    #endif // MYGLWIDGET_H
    View Code

      下面分别是z_translate为-6、-4、-2、-0.1、-0.09时的效果,可以看到其绝对值越小,即为离眼睛越来越近,所以看起来越大,而如果其绝对值小于gluPerspective()设置的最近位置则就看不见了。而如果将z_translate设为正值的话相当于是在眼睛的后方,这样更不能看见了: 

              

      void glMatrixMode(GLenum mode):将当前矩阵设置成参数所指定的模式,以满足不同绘图所需执行的矩阵变换。glMatrixMode()其实就是对接下来要做什么进行一下声明,参数mode指定哪一个矩阵堆栈是下一个矩阵操作的目标:

      GL_MODELVIEW, 对模型视图矩阵堆栈应用随后的矩阵操作。可以在执行此命令后,输出自己的物体图形了。在需要绘制出对象或要对所绘制对象进行几何变换时,需要将变换矩阵设置成模型视图模式;

      GL_PROJECTION, 对投影矩阵堆栈应用随后的矩阵操作。可以在执行此命令后,为我们的场景增加透视。当需要对绘制的对象设置某种投影方式时,则需要将变换矩阵设置成投影模式;

      GL_TEXTURE, 对纹理矩阵堆栈应用随后的矩阵操作。可以在执行此命令后,为我们的图形增加纹理贴图。在进行纹理映射时,才需要将变换矩阵设置成纹理模式。

      void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear,GLdouble zFar):设置矩阵:

      fovy是眼睛上下睁开的幅度,角度值,值越小,视野范围越狭小(眯眼),感觉物体越近,值越大,视野范围越宽阔(睁大眼),感觉物体越远;

      aspect表示裁剪面的宽w高h比,这个影响到视野的截面有多大。

      zNear表示近裁剪面到眼睛的距离,zFar表示远裁剪面到眼睛的距离,注意zNear和zFar不能设置设置为负值(你怎么看到眼睛后面的东西),离眼睛越近东西看起来就越大

      

      QGLWidget下还有一个renderPixmap()方法,使用它可以获得指定矩形的截图(QPixmap),然后可以使用QPixmap的save()来保存该截图。

      还可以设置绘制的时候各个顶点使用的颜色,如下所示:

    void MyGLWidget::paintGL()
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕:使用glClearColor()设置的颜色和glClearDepth()设置的深度缓存
        glLoadIdentity(); //重置模型视图矩阵,这样便将坐标原点移到了窗口中心
    
        GLfloat z_translate = -6.0;
        //void glTranslatef(GLfloat x,GLfloat y,GLfloat z);
        glTranslatef(-2.0, 0.0, z_translate); //将坐标原点左移2.0,内移6.0
    
        glBegin(GL_TRIANGLES); //开始绘制三角形
        //逆时针绘制各顶点
        //void glVertex3f(GLfloat x,GLfloat y,GLfloat z);
        glColor3f(1.0, 0.0, 0.0); //红色
        glVertex3f(0.0, 1.0, 0.0);
        glColor3f(0.0, 1.0, 0.0); //绿色
        glVertex3f(-1.0, -1.0, 0.0);
        glColor3f(0.0, 0.0, 1.0); //蓝色
        glVertex3f(1.0, -1.0, 0.0);
        glEnd();
    
        glTranslatef(4.0, 0.0, 0.0); //将坐标原点右移4.0
        glBegin(GL_QUADS); //开始绘制四边形
        //顺时针绘制各顶点
        glColor3f(1.0, 1.0, 0.0); //黄色
        glVertex3f(-1.0, 1.0, 0.0);
        glVertex3f(1.0, 1.0, 0.0);
        glVertex3f(1.0, -1.0, 0.0);
        glVertex3f(-1.0, -1.0, 0.0);
        glEnd();
    }
    View Code

      

     2、3D效果绘制

        下面的代码绘制了一个立方体的三面,使用上、下、左、右、4、6键分别沿X轴向上、向下、沿Y轴向左、向右、沿Z轴向左、向右,使用8和2键进行缩小和放大:

    class MyGLWidget : public QGLWidget
    {
    public:
        MyGLWidget();
    protected:
        void initializeGL()override;
        void resizeGL(int w, int h)override;
        void paintGL()override;
    
        void keyPressEvent(QKeyEvent*)override;
    private:
        GLfloat translate = -6.0, xRot = 0.0, yRot = 0.0, zRot = 0.0;
    };
    View Code
    #include "myglwidget.h"
    #include<GL/glu.h>
    #include <QKeyEvent>
    
    MyGLWidget::MyGLWidget()
    {
    
    }
    
    void MyGLWidget::initializeGL()
    {
        //void glClearColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha);
        glClearColor(1.0, 0.0, 0.0, 0.0); //设置清除屏幕时使用的颜色为红色
    
        glShadeModel(GL_SMOOTH); //设置阴影平滑
    
        //void glClearDepth(GLclampd depth);
        glClearDepth(1.0); //设置深度缓存(深度缓冲区中每个像素需要的值)
    
        glEnable(GL_DEPTH_TEST); //启用深度测试功能,根据坐标的远近自动隐藏被遮住的图形(材料)
    }
    
    void MyGLWidget::resizeGL(int w, int h)
    {
        glViewport(0, 0, (GLint)w, (GLint)h); //设置视口的大小
    
        glMatrixMode(GL_PROJECTION); //投影矩阵模式
        glLoadIdentity(); //重置当前指定的矩阵为单位矩阵,这样可以将矩阵恢复到初始状态。
        gluPerspective(45.0, (GLfloat)w / (GLfloat)h, 0.1, 100.0); //设置透视投影矩阵:视角为45度,纵横比为窗口的纵横比,最近和最远的位置分别为0.1和100
    
        glMatrixMode(GL_MODELVIEW); //设置模型视图矩阵
        glLoadIdentity(); //重置模型视图矩阵
    }
    
    void MyGLWidget::paintGL()
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();
    
        //坐标原点沿Z轴移动translate,正数为向屏幕外移,负数为向屏幕内移,向屏幕外移相当于离眼睛越来越近所以更大
        glTranslatef(0.0, 0.0, translate);
    
        //void glRotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z);
        glRotatef(xRot, 1.0, 0.0, 0.0); //绕X轴旋转到xRot度(正数为逆时针、负数为顺时针), glRotatef(xRot, -1.0, 0.0, 0.0)为绕X轴旋转到xRot度(负数为逆时针、正数为顺时针)
        glRotatef(yRot, 0.0, 1.0, 0.0); //绕Y轴旋转到yRot度
        glRotatef(zRot, 0.0, 0.0, 1.0); //绕Z轴旋转到zRot度
    
        glBegin(GL_QUADS);
        //逆时针(从上往下看)绘制正方体的上面
        glColor3f(1.0, 1.0, 0.0); //黄色
        glVertex3f(1.0, 1.0, 1.0);
        glVertex3f(1.0, 1.0, -1.0);
        glVertex3f(-1.0, 1.0, -1.0);
        glVertex3f(-1.0, 1.0, 1.0);
    
        //逆时针(从上往下看)绘制正方体的下面
        glColor3f(0.0, 1.0, 0.0); //绿色
        glVertex3f(1.0, -1.0, 1.0);
        glVertex3f(1.0, -1.0, -1.0);
        glVertex3f(-1.0, -1.0, -1.0);
        glVertex3f(-1.0, -1.0, 1.0);
    
        //逆时针绘制正方体的前面
        glColor3f(0.0, 0.0, 1.0); //蓝色
        glVertex3f(1.0, 1.0, 1.0);
        glVertex3f(-1.0, 1.0, 1.0);
        glVertex3f(-1.0, -1.0, 1.0);
        glVertex3f(1.0, -1.0, 1.0);
    
        glEnd();
    }
    
    void MyGLWidget::keyPressEvent(QKeyEvent* event)
    {
        switch (event->key()) {
            case Qt::Key_Up:
                xRot -= 10;
                break;
            case Qt::Key_Down:
                xRot += 10;
                break;
            case Qt::Key_Left:
                yRot -= 10;
                break;
            case Qt::Key_Right:
                yRot += 10;
                break;
            case Qt::Key_4:
                zRot += 10;
                break;
            case Qt::Key_6:
                zRot -= 10;
                break;
            case Qt::Key_8:
                if(--translate <= -20)
                    translate = -1;
                break;
            case Qt::Key_2:
                if(++translate >= -1)
                    translate = -20;
                break;
        }
        updateGL(); //更新绘制
    
        QGLWidget::keyPressEvent(event);
    }
    View Code

           

      3、使用纹理贴图

       使用纹理贴图可以参考Qt中的Textures示例程序。

    #include <QGLWidget>
    
    class MyGLWidget : public QGLWidget
    {
    public:
        MyGLWidget();
    protected:
        void initializeGL()override;
        void resizeGL(int w, int h)override;
        void paintGL()override;
    
        void keyPressEvent(QKeyEvent*)override;
    private:
        GLfloat translate = -6.0, xRot = 0.0, yRot = 0.0, zRot = 0.0;
        GLuint textures[3]; //存储纹理
    };
    View Code
    void MyGLWidget::initializeGL()
    {
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glShadeModel(GL_SMOOTH);
        glClearDepth(1.0);
        glEnable(GL_DEPTH_TEST);
    
        //创建并绑定纹理
        textures[0] = bindTexture(QPixmap("F://side1.png"));
        textures[1] = bindTexture(QPixmap("F://side2.png"));
        textures[2] = bindTexture(QPixmap("F://side3.png"));
    
        glEnable(GL_TEXTURE_2D); //开启2D纹理贴图功能
    }
    
    ......
    
    void MyGLWidget::paintGL()
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();
    
        glTranslatef(0.0, 0.0, translate);
        glRotatef(xRot, 1.0, 0.0, 0.0);
        glRotatef(yRot, 0.0, 1.0, 0.0);
        glRotatef(zRot, 0.0, 0.0, 1.0);
    
        //绘制正方体的上面
        glBindTexture(GL_TEXTURE_2D, textures[2]); //绑定纹理
        glBegin(GL_QUADS); //更改使用的纹理必须再次使用glBegin()来开始一个新的绘图
        //void glTexCoord2f(GLfloat s,GLfloat t); //设置纹理的坐标,应该在每次绘制顶点前调用它,纹理的四个顶点应该与四边形四个顶点正确对应
        glTexCoord2f(1.0, 0.0); //第一个参数是X坐标(0.0为纹理的最左侧,1.0为纹理的最右侧),第二个参数是Y坐标(0.0为纹理的底部,1.0为纹理的顶部)
        glVertex3f(1.0, 1.0, 1.0);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0, 1.0, -1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0, 1.0, -1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0, 1.0, 1.0);
        glEnd();
    
        //绘制正方体的下面
        glBindTexture(GL_TEXTURE_2D, textures[1]);
        glBegin(GL_QUADS);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0, -1.0, 1.0);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0, -1.0, -1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0, -1.0, -1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0, -1.0, 1.0);
        glEnd();
    
        //绘制正方体的前面
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        glBegin(GL_QUADS);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0, 1.0, 1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0, 1.0, 1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0, -1.0, 1.0);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0, -1.0, 1.0);
        glEnd();
    }
    
    ......
    View Code

      

      可以看到图形下面的2看起来是倒置的,因为我们绘制的时候是从上往下看的,将纹理的(0.0, 0.0)顶点和(0.0, 1.0)顶点对置、(1.0, 0.0)顶点和(1.0, 1.0)顶点对置即可,如下所示:

        ......
    
        //绘制正方体的下面
        glBindTexture(GL_TEXTURE_2D, textures[1]);
        glBegin(GL_QUADS);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0, -1.0, 1.0);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0, -1.0, -1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0, -1.0, -1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0, -1.0, 1.0);
        glEnd();
        
        ......
    View Code

      

     4、在3D场景中绘制2D图形 

      QGLWidget可以实现3D场景绘图,而QGLWidget也是一个QWidget部件,所以也可以进行2D绘图,可以参考Overpainting示例程序。如下所示的示例先在构造函数中调用setAutoFillBackground(),然后在keyPressEvent()中将updateGL()替换为update(),然后将initializeGL()、resizeGL()、paintGL()中代码剪切到新的方法中后在paintEvent()方法中添加对它们的调用:

    class MyGLWidget : public QGLWidget
    {
    public:
        MyGLWidget();
    protected:
        void initializeGL()override;
        void resizeGL(int w, int h)override;
        void paintGL()override;
        void keyPressEvent(QKeyEvent*)override;
    
        void paintEvent(QPaintEvent*)override;
        void initializeGL2();
        void resizeGL2(int w, int h);
        void paintGL2();
    private:
        GLfloat translate = -6.0, xRot = 0.0, yRot = 0.0, zRot = 0.0;
        GLuint textures[3];
    };
    View Code
    MyGLWidget::MyGLWidget()
    {
        setAutoFillBackground(false); //关闭自动填充背景
    }
    
    void MyGLWidget::initializeGL()
    {
    
    }
    
    void MyGLWidget::resizeGL(int w, int h)
    {
    
    }
    
    void MyGLWidget::paintGL()
    {
    
    }
    
    void MyGLWidget::initializeGL2()
    {
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glShadeModel(GL_SMOOTH);
        glClearDepth(1.0);
        glEnable(GL_DEPTH_TEST);
    
        textures[0] = bindTexture(QPixmap("F://side1.png"));
        textures[1] = bindTexture(QPixmap("F://side2.png"));
        textures[2] = bindTexture(QPixmap("F://side3.png"));
        glEnable(GL_TEXTURE_2D);
    }
    
    void MyGLWidget::resizeGL2(int w, int h)
    {
        glViewport(0, 0, (GLint)w, (GLint)h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0, (GLint)w / (GLint)h, 0.1, 100.0);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
    }
    
    void MyGLWidget::paintGL2()
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();
    
        glTranslatef(0.0, 0.0, translate);
        glRotatef(xRot, 1.0, 0.0, 0.0);
        glRotatef(yRot, 0.0, 1.0, 0.0);
        glRotatef(zRot, 0.0, 0.0, 1.0);
    
        glBindTexture(GL_TEXTURE_2D, textures[2]);
        glBegin(GL_QUADS);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0, 1.0, 1.0);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0, 1.0, -1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0, 1.0, -1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0, 1.0, 1.0);
        glEnd();
    
        glBindTexture(GL_TEXTURE_2D, textures[1]);
        glBegin(GL_QUADS);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0, -1.0, 1.0);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0, -1.0, -1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0, -1.0, -1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0, -1.0, 1.0);
        glEnd();
    
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        glBegin(GL_QUADS);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0, 1.0, 1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0, 1.0, 1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0, -1.0, 1.0);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0, -1.0, 1.0);
        glEnd();
    }
    
    void MyGLWidget::keyPressEvent(QKeyEvent* event)
    {
        switch (event->key()) {
            case Qt::Key_Up:
                xRot -= 10;
                break;
            case Qt::Key_Down:
                xRot += 10;
                break;
            case Qt::Key_Left:
                yRot -= 10;
                break;
            case Qt::Key_Right:
                yRot += 10;
                break;
            case Qt::Key_4:
                zRot += 10;
                break;
            case Qt::Key_6:
                zRot -= 10;
                break;
            case Qt::Key_8:
                if(--translate <= -20)
                    translate = -1;
                break;
            case Qt::Key_2:
                if(++translate >= -1)
                    translate = -20;
                break;
        }
        update(); //更新绘制
    
        QGLWidget::keyPressEvent(event);
    }
    
    void MyGLWidget::paintEvent(QPaintEvent*)
    {
        makeCurrent(); //在当前窗口中进行OpenGL的绘制
        glMatrixMode(GL_MODELVIEW); //将模型视图矩阵压入堆栈
        glPushMatrix();
    
        initializeGL2();
        resizeGL2(width(), height());
        paintGL2();
    
        //关闭启用的功能并弹出模型视图矩阵
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_TEXTURE_2D);
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();
    
        //进行2D绘图
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setBrush(Qt::yellow);
        painter.drawRect(0, 0, 100, 100);
        painter.end();
    }
    View Code

      

  • 相关阅读:
    一种线程安全的handle
    基于数组的无锁队列(译)
    distri.lua的web运维工具
    distri.lua重写开源手游服务器框架Survive
    99 Lisp Problems 二叉树(P54~P69)
    99 Lisp Problems 列表处理(P1~P28)
    TSPL学习笔记(4):数组相关练习
    TSPL学习笔记(3):排序算法练习
    用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 热更新Live2D
    用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 使用FairyGUI (二)
  • 原文地址:https://www.cnblogs.com/milanleon/p/12909541.html
Copyright © 2020-2023  润新知