Solved 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.
-
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!