Search code examples
pythonqtopenglpyqtpyqt6

Drawing a triangle using PyQt6 and OpenGL (rewriting the Qt6 C++ example)


My current PyQt6 example does not show a window and does not give me any error messages. This PyQt6 example is just a copy of Qt C++. The following Qt C++ example works without problems even in the browser and on Android:

enter image description here

pro

QT += core gui openglwidgets

win32: LIBS += -lopengl32

CONFIG += c++17

SOURCES += \
    main.cpp

main.cpp

#include <QtGui/QOpenGLFunctions>
#include <QtOpenGL/QOpenGLBuffer>
#include <QtOpenGL/QOpenGLShader>
#include <QtOpenGL/QOpenGLShaderProgram>
#include <QtOpenGLWidgets/QOpenGLWidget>
#include <QtWidgets/QApplication>

class OpenGLWindow : public QOpenGLWidget, private QOpenGLFunctions
{
public:
    OpenGLWindow()
    {
        setWindowTitle("OpenGL ES 2.0, Qt6, C++");
        resize(350, 350);
    }

    void initializeGL() override
    {
        initializeOpenGLFunctions();
        glClearColor(48.f / 255.f, 56.f / 255.f, 65.f / 255.f, 1.f);

        QString vertShaderSrc =
            "attribute vec2 aPosition;\n"
            "void main()\n"
            "{\n"
            "    gl_Position = vec4(aPosition, 0.0, 1.0);\n"
            "}\n";

        QString fragShaderSrc =
            "#ifdef GL_ES\n"
            "precision mediump float;\n"
            "#endif\n"
            "void main()\n"
            "{\n"
            "    gl_FragColor = vec4(0.2, 0.7, 0.3, 1.0);\n"
            "}\n";

        m_program.create();
        m_program.addShaderFromSourceCode(QOpenGLShader::ShaderTypeBit::Vertex, vertShaderSrc);
        m_program.addShaderFromSourceCode(QOpenGLShader::ShaderTypeBit::Fragment, fragShaderSrc);
        m_program.link();
        m_program.bind();

        float vertPositions[] = {
            -0.5f, -0.5f,
            0.5f, -0.5f,
            0.f, 0.5f
        };
        m_vertPosBuffer.create();
        m_vertPosBuffer.bind();
        m_vertPosBuffer.allocate(vertPositions, sizeof(vertPositions));
    }

    void paintGL() override
    {
        glClear(GL_COLOR_BUFFER_BIT);
        m_program.bind();
        m_vertPosBuffer.bind();
        m_program.setAttributeBuffer("aPosition", GL_FLOAT, 0, 2);
        m_program.enableAttributeArray("aPosition");
        glDrawArrays(GL_TRIANGLES, 0, 3);
    }

private:
    QOpenGLShaderProgram m_program;
    QOpenGLBuffer m_vertPosBuffer;
};

int main(int argc, char *argv[])
{
    QApplication::setAttribute(Qt::ApplicationAttribute::AA_UseDesktopOpenGL);
    QApplication app(argc, argv);
    OpenGLWindow w;
    w.show();
    return app.exec();
}

This is my trying to rewrite the code above to PyQt6. As you can see I want to rewrite it very close to Qt C++. I want to have two very similar example. But this code does not show a window and does not show any error messages.

Note. I have fixed the problem after eyllanesc's answer in the code below:

main.py

import sys

import numpy as np
from OpenGL.GL import (GL_COLOR_BUFFER_BIT, GL_FLOAT, GL_TRIANGLES, glClear,
                       glClearColor, glDrawArrays)
from PyQt6.QtCore import Qt
from PyQt6.QtOpenGL import QOpenGLBuffer, QOpenGLShader, QOpenGLShaderProgram
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
from PyQt6.QtWidgets import QApplication


class OpenGLWindow(QOpenGLWidget):

    def __init__(self):
        super().__init__()

        self.setWindowTitle("OpenGL ES 2.0, PyQt6, Python")
        self.resize(350, 350)

    def initializeGL(self):
        glClearColor(48 / 255, 56 / 255, 65 / 255, 1)

        vertShaderSrc = """
            attribute vec2 aPosition;
            void main()
            {
                gl_Position = vec4(aPosition, 0.0, 1.0);
            }
        """

        fragShaderSrc = """
            #ifdef GL_ES
            precision mediump float;
            #endif
            void main()
            {
                gl_FragColor = vec4(0.2, 0.7, 0.3, 1.0);
            }
        """

        self.program = QOpenGLShaderProgram(self)
        self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, vertShaderSrc)
        self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, fragShaderSrc)
        self.program.link()
        self.program.bind()

        vertPositions = np.array([
            -0.5, -0.5,
            0.5, -0.5,
            0, 0.5], dtype=np.float32)
        self.vertPosBuffer = QOpenGLBuffer()
        self.vertPosBuffer.create()
        self.vertPosBuffer.bind()
        self.vertPosBuffer.allocate(vertPositions, len(vertPositions) * 4)

    def resizeGL(self, w, h):
        pass

    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT)
        self.program.bind()
        self.vertPosBuffer.bind()
        self.program.setAttributeBuffer("aPosition", GL_FLOAT, 0, 2)
        self.program.enableAttributeArray("aPosition")
        glDrawArrays(GL_TRIANGLES, 0, 3)

if __name__ == "__main__":
    QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
    app = QApplication(sys.argv)
    w = OpenGLWindow()
    w.show()
    sys.exit(app.exec())

Solution

  • The problem is that the QOpenGLBuffer is a local variable that is destroyed instantly, whereas in C++ QOpenGLBuffer is an attribute of the class. The solution is to change vertPosBuffer to self.vertPosBuffer