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
 

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