Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QGraphicsView backed with QOpenGLWidget



  • Hi, I'm trying to set up a QGraphicsView with a QOpenGLWidget set to the viewport but the GL widget renders only white. I know this normally means that the texture is incorrect but that doesn't seem to be the case. I'm sure I am just missing some elementary step.

    I wrote a modified version of the Textures example (https://doc.qt.io/qt-5/qtopengl-textures-example.html) and then added a QGraphicsView and QGraphicsScene.

    My code is below; you can switch off the view/scene code using the define USE_VIEW. You will need to supply your own image.

    gview.pro:

    HEADERS       = window.h \
                    simpleglwidget.h
    
    SOURCES       = main.cc \
                    window.cc \
                    simpleglwidget.cc
    
    DEFINES       = USE_VIEW=1
    
    QT           += core gui widgets opengl
    CONFIG       += qt debug warn_all c++11
    
    QMAKE_MAC_SDK = macosx10.12
    

    main.cc:

    #include <QApplication>
    #include <QSurfaceFormat>
    
    #include "window.h"
    
    int main(int argc, char *argv[])
    {
      QApplication app(argc, argv);
    
      QSurfaceFormat format;
      format.setDepthBufferSize(24);
      QSurfaceFormat::setDefaultFormat(format);
    
      Window window;
      window.show();
    
      window.resize(720,480);
      window.SetTestImage();
    
      return app.exec();
    }
    

    window.h:

    #ifndef WINDOW_H
    #define WINDOW_H
    
    #include <QGraphicsView>
    #include <QGraphicsScene>
    
    class SimpleGLWidget;
    
    class Window : public QWidget
    {
      Q_OBJECT
    
    public:
      Window();
    
      void SetTestImage();
    
    private:
      QGraphicsView *view;
      QGraphicsScene *scene;
    
      SimpleGLWidget *myGLWidget;
    };
    
    #endif
    

    window.cc:

    #include <QtWidgets>
    
    #include "window.h"
    #include "simpleglwidget.h"
    
    Window::Window()
    {
      QVBoxLayout *mainLayout = new QVBoxLayout;
    
      myGLWidget = new SimpleGLWidget;
      myGLWidget->setClearColor(Qt::black);
    
    #if USE_VIEW
      view  = new QGraphicsView(this);
      scene = new QGraphicsScene(view);
      view->setViewport(myGLWidget);
      view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
      view->setScene(scene);
      mainLayout->addWidget(view);
    #else
      mainLayout->addWidget(myGLWidget);
    #endif
    
      setLayout(mainLayout);
      setWindowTitle(tr("QGraphicsView Test"));
    }
    
    void Window::SetTestImage()
    {
      QImage input_image(QString("speedway17.png"));
      myGLWidget->SetImage(input_image);
    }
    

    simpleglwidget.h:

    #ifndef simpleglwidget_h
    #define simpleglwidget_h
    
    #include <QOpenGLWidget>
    #include <QOpenGLFunctions>
    #include <QOpenGLBuffer>
    #include <QMatrix4x4>
    
    QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram);
    QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
    
    class SimpleGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
    {
      Q_OBJECT
    
    public:
      explicit SimpleGLWidget(QWidget *parent = 0);
    
      ~SimpleGLWidget();
    
      void SetImage(const QImage &img);
    
      void setClearColor(const QColor &color);
      QSize minimumSizeHint() const Q_DECL_OVERRIDE;
      QSize sizeHint() const Q_DECL_OVERRIDE;
    
      void rotateBy(int xAngle, int yAngle, int zAngle);
    
    signals:
      void clicked();
    
    protected:
      void initializeGL() Q_DECL_OVERRIDE;
      void paintGL() Q_DECL_OVERRIDE;
      void resizeGL(int width, int height) Q_DECL_OVERRIDE;
    
      void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
      void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
      void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    
    private:
      void makeObject();
    
      void setView(int w, int h);
    
      QColor clearColour;
    
      /// Rotation angles (used in testing)
      int xRot;
      int yRot;
      int zRot;
    
      QPoint lastPos;
    
      QOpenGLShaderProgram *program;
      QOpenGLBuffer vbo;
    
      QOpenGLTexture *frame_texture;
    
      bool hasTexture;
    
      /// Compensating projection for origin position
      QMatrix4x4 comp;
    
      QMatrix4x4 projection;
    
      int scaledWidth, scaledHeight;
      float yAspect;
      float yAspectActual;
    
      int textureWidth, textureHeight;
    };
    
    #endif
    

    simpleglwidget.cc:

    #include "simpleglwidget.h"
    
    #include <QOpenGLShaderProgram>
    #include <QOpenGLTexture>
    #include <QMouseEvent>
    
    #include <math.h>
    
    #define PI 3.14159265358979323846
    
    SimpleGLWidget::SimpleGLWidget(QWidget *parent)
        : QOpenGLWidget(parent),
          clearColour(Qt::black),
          xRot(0),
          yRot(0),
          zRot(0),
          program(0),
          frame_texture(NULL)
    {
      comp = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
                        0.0f, -1.0f, 0.0f, 0.0f,
                        0.0f, 0.0f, 1.0f, 0.0f,
                        0.0f, 0.0f, 0.0f, 1.0f);
    
      yAspect          = 0.75;
      yAspectActual    = 0.75;
      textureWidth     = 1;
      textureHeight    = 1;
      hasTexture       = false;
    }
    
    SimpleGLWidget::~SimpleGLWidget()
    {
      makeCurrent();
    
      if(vbo.isCreated())
        vbo.destroy();
    
      if(hasTexture)
        delete frame_texture;
    
      delete program;
      doneCurrent();
    }
    
    // Set an image in the widget
    void SimpleGLWidget::SetImage(const QImage &img)
    {
      makeCurrent();
    
      if(frame_texture) delete frame_texture;
      frame_texture = new QOpenGLTexture(img);
      textureWidth  = img.width();
      textureHeight = img.height();
      yAspect       = (float)(textureHeight)/textureWidth;
      hasTexture    = true;
    
      makeObject();
      doneCurrent();
    
      setView(width(), height());
      update();
    }
    
    QSize SimpleGLWidget::minimumSizeHint() const
    {
      return(QSize(160, 90));
    }
    
    QSize SimpleGLWidget::sizeHint() const
    {
      return(QSize(800, 450));
    }
    
    // Rotation
    void SimpleGLWidget::rotateBy(int xAngle, int yAngle, int zAngle)
    {
      xRot += xAngle;
      yRot += yAngle;
      zRot += zAngle;
      update();
    }
    
    void SimpleGLWidget::setClearColor(const QColor &color)
    {
      clearColour = color;
      update();
    }
    
    void SimpleGLWidget::initializeGL()
    {
      initializeOpenGLFunctions();
    
      glEnable(GL_DEPTH_TEST);
      // glEnable(GL_CULL_FACE);
    
    #define PROGRAM_VERTEX_ATTRIBUTE 0
    #define PROGRAM_TEXCOORD_ATTRIBUTE 1
    
      QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
      const char *vsrc =
          "attribute highp vec4 vertex;\n"
          "attribute mediump vec4 texCoord;\n"
          "varying mediump vec4 texc;\n"
          "uniform mediump mat4 matrix;\n"
          "void main(void)\n"
          "{\n"
          "    gl_Position = matrix * vertex;\n"
          "    texc = texCoord;\n"
          "}\n";
      vshader->compileSourceCode(vsrc);
    
      QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
      const char *fsrc =
          "uniform sampler2D texture;\n"
          "varying mediump vec4 texc;\n"
          "void main(void)\n"
          "{\n"
          "    gl_FragColor = texture2D(texture, texc.st);\n"
          "}\n";
      fshader->compileSourceCode(fsrc);
    
      program = new QOpenGLShaderProgram;
      program->addShader(vshader);
      program->addShader(fshader);
      program->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE);
      program->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
      program->link();
    
      program->bind();
      program->setUniformValue("texture", 0);
    }
    
    void SimpleGLWidget::paintGL()
    {
      glClearColor(clearColour.redF(), clearColour.greenF(), clearColour.blueF(), clearColour.alphaF());
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
      QMatrix4x4 m;
    
      m = comp*projection;
    
      m.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f);
      m.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);
      m.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);
    
      program->setUniformValue("matrix", m);
      program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
      program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
      program->setAttributeBuffer(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat));
      program->setAttributeBuffer(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat));
    
      if(hasTexture)
      {
        frame_texture->bind();
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
      }
    }
    
    void SimpleGLWidget::resizeGL(int width, int height)
    {
      setView(width, height);
    }
    
    // Set up view port. (w, h) = current size of the widget
    void SimpleGLWidget::setView(int w, int h)
    {
      glViewport(0, 0, GLsizei(w), GLsizei(h));
    
      // Set perspective projection
    
      if(h == 0) h = 1;
    
      float half_viewing_angle = atan(yAspect*0.25);
    
      if(((float)(h)/w) <= yAspect)
      {
        half_viewing_angle = atan(yAspect*0.25);
    
        if(h >= textureHeight)
        {
          half_viewing_angle = atan((yAspect*0.25)*(float)(h)/textureHeight);
        }
      }
      else
      {
        half_viewing_angle = atan(((float)(h)/w)*0.25);
    
        if(w >= textureWidth)
        {
          half_viewing_angle = atan((yAspect*0.25)*(float)(h)/textureHeight);
        }
      }
    
      float fov    = 2.0*half_viewing_angle;
      float aspect = (float)(w)/(float)(h);
      float znear  = 1.0f;
      float zfar   = 1000.0f;
    
      projection = QMatrix4x4();
      projection.perspective(fov * (180.0/PI), aspect, znear, zfar); // Qt uses degrees
      projection.translate(-0.5, -yAspect/2, -2.0);
    }
    
    void SimpleGLWidget::mousePressEvent(QMouseEvent *event)
    {
      lastPos = event->pos();
    }
    
    void SimpleGLWidget::mouseMoveEvent(QMouseEvent *event)
    {
      int dx = event->x() - lastPos.x();
      int dy = event->y() - lastPos.y();
    
      if (event->buttons() & Qt::LeftButton) {
          rotateBy(8 * dy, 8 * dx, 0);
      } else if (event->buttons() & Qt::RightButton) {
          rotateBy(8 * dy, 0, 8 * dx);
      }
      lastPos = event->pos();
    }
    
    void SimpleGLWidget::mouseReleaseEvent(QMouseEvent * /* event */)
    {
      emit clicked();
    }
    
    void SimpleGLWidget::makeObject()
    {
      static const float coords[4][3] = {
        { 1.0f, yAspect,  0.0f },
        { 0.0f, yAspect,  0.0f },
        { 0.0f, 0.0f,     0.0f },
        { 1.0f, 0.0f,     0.0f }
      };
    
      QVector<GLfloat> vertData;
      for(int j = 0; j < 4; ++j)
      {
        // vertex position
        vertData.append(1.0 * coords[j][0]);
        vertData.append(1.0 * coords[j][1]);
        vertData.append(1.0 * coords[j][2]);
    
        // texture coordinate
        vertData.append(j == 0 || j == 3);
        vertData.append(j == 0 || j == 1);
      }
    
      vbo.create();
      vbo.bind();
      vbo.allocate(vertData.constData(), vertData.count() * sizeof(GLfloat));
    }
    

    I am developing on OSX El Capitan, Qt 5.5.1 but also experience the same behaviour in Debian Jessie, Qt 5.7



  • It seems that the issue is that paintGL() and resizeGL() are never called.


Log in to reply