OpenGL window as a child of a QWidget window



  • So I'm coding for a school project a simple tool to render a sine wave and have a couple of sliders to adjust parameters. I have created an OpenGL rendering context successfully but want it so that it is in the top right of my screen with another 2 below the main rendering context and then on the left hand side a tool bar that goes from top to bottom with buttons and such.

    My question is how do I make the OpenGL window a child of a main window. This is all done at a low level as to gain marks in this assignment I have to show an understanding of how it's done.

    Below is the class header and c++ file for the Sine wave window (just renders a triangle for testing):

    wavewindow.h

    /#include <QtGui/QMatrix4x4>
    #include <QtGui/QOpenGLShaderProgram>
    
    #include <QtCore/qmath.h>
    
    //Include strings, file streams and console input output streams
    #include <string>
    #include <sstream>
    #include <fstream>
    #include <iostream>
    
    //Definition for a value of PI
    #define PI 3.141592653
    
    /* Class for rendering a sine wave (will be modified for a group)
     * inherites public attribute/methods from OpenGLWindow*/
    class WaveWindow : public OpenGLWindow
    {
    public:
        //Takes the offsets for the coordinates for the sinewave
        WaveWindow(float in_x_offset, float in_y_offset, float in_z_offset);
    
        //Sets up the shader program and generates starting coordinates
        virtual void initialize() override;
    
        //Sets up view port then renders the moving sinewave
        virtual void render() override;
    
    private:
        //Postion offsets for the sinewave
        float x_offset, y_offset, z_offset;
    
        //Vector for holding coordinates of a sinewave
        std::vector<GLfloat> sine_wave_vertices;
        float sine_wave_array[1];
    
        //Vector for the colors of the sinewave
        std::vector<GLfloat> sine_wave_colors;
    
        //Position attribute of vertices
        GLuint m_posAttr;
    
        //Colour attribute of vertices
        GLuint m_colAttr;
    
        //Matrix uniform location
        GLuint m_matrixUniform;
    
        //Shader program object
        QOpenGLShaderProgram *m_program;
    
        int m_frame;
    
        //Shader code variables
        std::string vertex_shader_source;
    
        std::string fragment_shader_source;
    
        //Utility function to read a text file into a c_str for the shader compiler
        std::string loadShader(std::string shader_path);
    
        //Utility function for generating the inital coordinates of a sinewave
        void sineWaveInit();
    };
    
    #endif // WAVEWINDOW_H
    

    I have added the function to generate the coordinates for the sine wave but not using it at the moment.
    wavewindow.cpp

    #include "wavewindow.h"
    
    WaveWindow::WaveWindow(float in_x_offset, float in_y_offset, float in_z_offset)
        : m_program(0)
        , m_frame(0)
    {
        x_offset = in_x_offset;
        y_offset = in_y_offset;
        z_offset = in_z_offset;
        sineWaveInit();
    }
    
    void WaveWindow::initialize()
    {
        //Call function to load shaders
        vertex_shader_source = loadShader("C:\\Users\\SEvans\\Documents\\GitHub\\A-Level-Project\\ORPA V1\\Qt\\ORPA-Widgets-Render\\wave_vertex_shader.vert");
        fragment_shader_source = loadShader("C:\\Users\\SEvans\\Documents\\GitHub\\A-Level-Project\\ORPA V1\\Qt\\ORPA-Widgets-Render\\wave_fragment_shader.frag");
    
        m_program = new QOpenGLShaderProgram(this);
        m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertex_shader_source.c_str());
        m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragment_shader_source.c_str());
        m_program->link();
        m_posAttr = m_program->attributeLocation("posAttr");
        m_colAttr = m_program->attributeLocation("colAttr");
        m_matrixUniform = m_program->uniformLocation("matrix");
    }
    
    //Reads shader source code from a text file into a c string (cont char* in c++)
    std::string WaveWindow::loadShader(std::string shader_path)
    {
        //Retrieve the vert_shader/frag_shader source code from filePath
        std::string shader_code;
        std::ifstream shader_file;
        //Ensure ifstream objects can throw exceptions:
        shader_file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        try
        {
            // open files
            shader_file.open(shader_path);
            std::stringstream shader_stream;
            //Read file's buffer contents into streams
            shader_stream << shader_file.rdbuf();
            //Close file handlers
            shader_file.close();
            //Convert stream into string
            shader_code = shader_stream.str();
            ///std::cout << "Vert code:\n" << vert_code << "\nFrag code:\n" << frag_code << std::endl;
        }
        catch (std::ifstream::failure e)
        {
            std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ" << std::endl;
        }
    
        return shader_code;
    }
    
    void WaveWindow::sineWaveInit()
    {
        //Generates sine wave with coordinate range function_domain x 2
        int function_domain = 360;
        for (int i = -function_domain; i <= function_domain; i++)
        {
            for(int j = 0 ; j < 3; j++)
            {
                sine_wave_colors.push_back(1.0f);
            }
            sine_wave_vertices.push_back((i / function_domain) + x_offset);     //X component
            sine_wave_vertices.push_back((sin((i * PI) / 180)/2 ) + y_offset);	//Y component
            sine_wave_vertices.push_back(z_offset);                             //Z component
        }
    }
    
    void WaveWindow::render()
    {
        const qreal retinaScale = devicePixelRatio();
        glViewport(0, 0, width() * retinaScale, height() * retinaScale);
    
        glClear(GL_COLOR_BUFFER_BIT);
    
        m_program->bind();
    
        QMatrix4x4 matrix;
        matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f);
        matrix.translate(0, 0, -2);
        matrix.rotate(100.0f * m_frame / screen()->refreshRate(), 0, 1, 0);
    
        m_program->setUniformValue(m_matrixUniform, matrix);
    
        GLfloat vertices[] = {
            0.0f, 0.707f,
            -0.5f, -0.5f,
            0.5f, -0.5f
        };
    
        GLfloat colors[] = {
            1.0f, 0.0f, 0.0f,
            0.0f, 1.0f, 0.0f,
            0.0f, 0.0f, 1.0f
        };
    
        glVertexAttribPointer(m_posAttr, 2, GL_FLOAT, GL_FALSE, 0, vertices);
        glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors);
    
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
    
        glDrawArrays(GL_TRIANGLES, 0, 3);
    
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(0);
    
        m_program->release();
    
        ++m_frame;
    }
    

    openglwindow.h

    //Include Guard
    #ifndef OPENGLWINDOW_H
    #define OPENGLWINDOW_H
    
    
    //Based on code from http://doc.qt.io/qt-5/qtgui-openglwindow-example.html
    
    //Includes class for the most basic window in Qt
    #include <QtGui/QWindow>
    
    //Includes the wrapper class for OpenGl functions
    #include <QtGui/QOpenGLFunctions>
    
    //Incldues classes for porting Qt function to OpenGL
    #include <QtGui/QOpenGLPaintDevice>
    #include <QtGui/QPainter>
    
    //Includes class which gets information about device screen
    #include <QtGui/QScreen>
    
    class QPainter;
    class QOpenGLContext;
    class QOpenGLPaintDevice;
    
    /* Class for creating an OpenGL QtWindow
     * Inherites public attributes/methods from QWindow and
     * protected attributes/methods from QOpenGLFunctions */
    class OpenGLWindow : public QWindow, protected QOpenGLFunctions
    {
        Q_OBJECT
    public:
        /* Constructor for the object is explicit to prevent loss of data
         * when converting parameters */
        explicit OpenGLWindow(QWindow *parent = 0);
    
        /* Destructor for the object cleans up any rendering contexts or windows */
        ~OpenGLWindow();
    
        /* Methods for making requests to render either with the
         * QPainter renderign tool the OpenGL rendering tool */
        virtual void render(QPainter *painter);
        virtual void render();
    
        // Method to initialize OpenGL Properties
        virtual void initialize();
    
        //Set the attribute m_animating to True or False
        void setAnimating(bool animating);
    
    
    /* Attributes & methods which allow for communication between objects
     * (Qt specific) */
    public slots:
        //Same as render() but queues items to be rendered in the next frame
        void renderLater();
    
        //Same as render() but renders the items immediately
        void renderNow();
    
    protected:
    
        /* Handles all the events that happen in the window.
         * Override specifies that these method definitons
         * override the last definitions of the methods */
        bool event(QEvent *event) override;
    
        void exposeEvent(QExposeEvent *event) override;
    
    private:
        /* Bool value for storing whether the context
         *  is animating anything or not */
        bool m_animating;
    
        /* Object of a native OpenGL Rendering context */
        QOpenGLContext *m_context;
    
        /* Object which allows for the use of QPainter objects
         *  on OpenGl rendering contexts */
        QOpenGLPaintDevice *m_device;
    
    };
    
    #endif // OPENGLWINDOW_H
    

    openglwindow.cpp

    #include "openglwindow.h"
    
    OpenGLWindow::OpenGLWindow(QWindow *parent)
        /* Tell the constructor to inhherit Qt attributes from the parent
         * window and set some initial attributes */
        : QWindow(parent)
        , m_animating(false)
        , m_context(0)
        , m_device(0)
    {
        setSurfaceType(QWindow::OpenGLSurface);
    }
    
    OpenGLWindow::~OpenGLWindow()
    {
        delete m_device;
    }
    
    void OpenGLWindow::render(QPainter *painter)
    {
        //Stops QPainter from being used as the default
        Q_UNUSED(painter);
    }
    
    void OpenGLWindow::initialize()
    {
    
    }
    
    void OpenGLWindow::render()
    {
        /* Initialize a QOpenGLPaintDevice which allows painting
         * to an OpenGL Context */
        if (!m_device)
        {
                m_device = new QOpenGLPaintDevice;
        }
    
        //Clear all buffer bits for inital render
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        m_device->setSize(size());
    
        //Initialize QPainter object
        QPainter painter(m_device);
        render(&painter);
    }
    
    
    
    
    void OpenGLWindow::renderLater()
    {
        requestUpdate();
    }
    
    void OpenGLWindow::renderNow()
    {
        /* Checks if the window is exposed
         * if not then waits until the window
         * is exposed */
        if(!isExposed())
        {
            return;
        }
    
        bool needsInitialize = true;
    
        /* Checks for a rendering context, if there isn't
         * one creates one and gives ti the same QSurfaceFormat
         * as set on the OpenGLWindow. Then set the current context
         * to this context */
        if(!m_context)
        {
            m_context = new QOpenGLContext(this);
            m_context->setFormat(requestedFormat());
            m_context->create();
    
            needsInitialize = true;
        }
    
        m_context->makeCurrent(this);
    
        /* Checks the variable needsInitialize if this is true
         * initialises the OpenGLFunctions class no that the
         * super class for ^^^ is associated with the correct context */
        if(needsInitialize)
        {
            initializeOpenGLFunctions();
            initialize();
        }
    
        //Call the render function to render whats in the queue
        render();
    
        //Swap the buffers so that the next frame is visible
        m_context->swapBuffers(this);
    
        //Schedules an update request if we are animating
        if(m_animating)
        {
            renderLater();
        }
    }
    
    bool OpenGLWindow::event(QEvent *event)
    {
        switch (event->type())
        {
        case QEvent::UpdateRequest:
            renderNow();
            return true;
        default:
            return QWindow::event(event);
        }
    }
    
    /* Checks for exposure on the window (has the visibilty changed) */
    void OpenGLWindow::exposeEvent(QExposeEvent *event)
    {
        Q_UNUSED(event);
    
        if(isExposed())
        {
            renderNow();
        }
    }
    
    void OpenGLWindow::setAnimating(bool animating)
    {
        m_animating = animating;
    
        if (animating)
            renderLater();
    }
    

    And finally
    main.cpp

    #include "wavewindow.h"
    #include <QApplication>
    #include <QWidget>
    #include <QPushButton>
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        //Set format for 4x anti-aliasing
        QSurfaceFormat format;
        format.setSamples(16);
    
        WaveWindow wave_window(0,0,0);
        wave_window.setFormat(format);
        wave_window.resize(640,480);
        wave_window.show();
        wave_window.setAnimating(true);
    
        return a.exec();
    }
    

    I hope someone can help and this isnt too waffley.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Do you mean like QOpenGLWidget ?



  • Wow that's incredibly simple! Can't believe I hadn't already found that thank you so much!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.