0

I'm using Qt 5.7 with c++ on ubuntu 16.04. I'm trying to implement a class that inherits qglwidget, that renders images to the screen at a given rate (3-10 Hz).

In addition to that I want to draw a small rect somewhere on the screen that changes its color from black to white and vice-versa. It should switch from white to black when the image appears, and switch back to black some predefined time before the next image comes. Right now I'm using a texture to load the images (from QImage objects), using

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());

and this is my paintGL() overload:

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
drawTexture(QRect(0,0,1,1),texture,GL_TEXTURE_2D);
swapBuffers();
glDisable(GL_TEXTURE_2D);

and I was wondering if it possible to draw on the widget and make it render both the rect and image at the same time. I've tried using QPainter but kept getting that drawing the rect made the image disappear(the rect shouldn't be on the image, but on some corner of the widget where nothing is drawn now).

Would appreciate any help!

JLev
  • 715
  • 1
  • 8
  • 25
  • If `QPainter` is used it assumes to have exclusive access to OpenGL states. If you use OpenGL API directly, you have to restore certain states before you call `QPainter`-API. This way I solved issues to render a HUD with `QPainter` after rendering a 3d scene with OpenGL. – Scheff's Cat Feb 23 '17 at 15:10
  • Probably, you should not call `swapBuffers();` because this should be done by the `QGLWidget`. (I had to look into my source code to provide more details about this.) – Scheff's Cat Feb 23 '17 at 15:11
  • You have to be aware that usage of `QOpenGLFunctions` methods and calling GL functions (from any non-Qt library) is a difference. It is possible and considered especially in the doc. e.g. [QGLWidgetClass](http://doc.qt.io/qt-5/qglwidget.html#details). If "non-Qt" OpenGL functions are called this may bypass internal state tracking in Qt. – Scheff's Cat Feb 26 '17 at 09:38
  • 1
    Did you notice the hint about `GL_TEXTURE_MIN_FILTER` and `GL_TEXTURE_MAG_FILTER` which I added at the end of my answer? – Scheff's Cat Feb 26 '17 at 13:16

2 Answers2

2

This is a minimal sample application which mixes OpenGL code and QPainter in paint handler:

#include <QtWidgets>
#include <QOpenGLFunctions_1_1>

// manually added types (normally provided by glib)
typedef unsigned guint;
typedef unsigned char guint8;

extern const struct Image {
  guint      width;
  guint      height;
  guint      bytes_per_pixel; /* 3:RGB, 4:RGBA */
  guint8     pixel_data[1];
} fluffyCat;

class GLWidget: public QOpenGLWidget, protected QOpenGLFunctions_1_1 {
  private:
    float _step;
    GLuint _idTex;
    QTimer _qTimer;
  public:
    GLWidget(QWidget *parent = 0):
      QOpenGLWidget(parent),
      _step(0.0f), _idTex(0)
    {
      _qTimer.setInterval(100); // 100 ms -> 10 Hz
      QObject::connect(&_qTimer, &QTimer::timeout,
        this, &GLWidget::timeout);
    }
  protected:
    virtual void initializeGL();
    virtual void paintGL();
  private:
    void timeout();
};

void GLWidget::initializeGL()
{
  initializeOpenGLFunctions();
  glClearColor(0.525, 0.733f, 0.851, 1.0);
  glGenTextures(1, &_idTex);
  glBindTexture(GL_TEXTURE_2D, _idTex);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, fluffyCat.width, fluffyCat.height, 0,
    GL_RGB, GL_UNSIGNED_BYTE, fluffyCat.pixel_data);
  glBindTexture(GL_TEXTURE_2D, 0);
  _qTimer.start();
}

void GLWidget::paintGL()
{
  // prepare OpenGL rendering
  QPainter qPainter(this);
  qPainter.beginNativePainting();
  // do OpenGL rendering
  glColor3f(1.0f, 1.0f, 1.0f);
  bool tex2dOld = glIsEnabled(GL_TEXTURE_2D);
  glEnable(GL_TEXTURE_2D);
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  static GLfloat envColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
  glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, envColor);
  glBindTexture(GL_TEXTURE_2D, _idTex);
  float x = sin(_step) * 0.5f, y = cos(_step) * 0.5f;
#if 0 // does not work (no tex-coords)
  glRectf(x - 0.5f, y - 0.5f, x + 0.5f, y + 0.5f);
#else // (not) 0
  glBegin(GL_QUADS);
  glColor3f(1.0f, 1.0f, 1.0f);
  glTexCoord2i(0, 1);
  glVertex2f(x - 0.5f, y - 0.5f);
  glTexCoord2i(1, 1);
  glVertex2f(x + 0.5f, y - 0.5f);
  glTexCoord2i(1, 0);
  glVertex2f(x + 0.5f, y + 0.5f);
  glTexCoord2i(0, 0);
  glVertex2f(x - 0.5f, y + 0.5f);
  glEnd();
#endif // 0
  glBindTexture(GL_TEXTURE_2D, 0);
  //if (!tex2dOld) glDisable(GL_TEXTURE_2D);
  // prepare Qt painting
  qPainter.endNativePainting();
  // do Qt painting (HUD)
  QPen qPen;
  qPen.setWidth(1);
  qPen.setColor(QColor(Qt::black));
  qPen.setStyle(Qt::SolidLine);
  qPainter.resetMatrix();
  qPainter.setPen(qPen);
  qPainter.drawLine(0, 0, width(), height());
  qPainter.drawLine(0, height(), width(), 0);
}

void GLWidget::timeout()
{
  _step = fmod(_step + 0.1, 2 * 3.141);
  update(); // force redraw
}

int main(int argc, char **argv)
{
  QApplication app(argc, argv);
  QMainWindow win;
  GLWidget view3d;
  win.setCentralWidget(&view3d);
  win.show();
  return app.exec();
}

and the source for the texture image:

/* GIMP RGB C-Source image dump (fluffyCat.cc) */

// manually added types (normally provided by glib)
typedef unsigned guint;
typedef unsigned char guint8;

struct Image {
  guint      width;
  guint      height;
  guint      bytes_per_pixel; /* 3:RGB, 4:RGBA */ 
  guint8     pixel_data[16 * 16 * 3 + 1];
};
extern const Image fluffyCat;
const Image fluffyCat = {
  16, 16, 3,
  "x\211s\215\232\200gw`fx`at[cx^cw^fu\\itZerWn|ap~cv\204jnzedq^fr^kzfhv^Ra"
  "GRbMWdR\\jXer^qw_\311\256\226\271\253\235\275\264\252\315\277\260\304\255"
  "\231u~i\213\225\207l{fly`jx\\^nRlz_z\206nlx`t~i\221\211s\372\276\243\375"
  "\336\275\376\352\340\356\312\301\235\216\212judgwcl~f\212\226u}\206h\212"
  "\224q\231\237z\232\236{\216\225v\225\230\200\306\274\244\376\360\327\376"
  "\361\331\376\360\341\326\275\272\253\240\244{\203p\202\220xp~e{\204^\222"
  "\230n\212\217g\240\242{\234\236z\214\222r\270\271\247\360\353\340\376\370"
  "\336\376\363\334\375\357\336\310\254\262\232\223\234\\gRfrX\204\220z\212"
  "\225g\225\232j\254\255\177\252\250{\225\226u\304\302\265\374\365\351\376"
  "\375\366\376\367\341\376\361\320\374\346\324\306\241\242\237\232\235n{fj"
  "xckyfu~fUX@VZCfnT\231\231\207\374\374\371\377\372\354\376\376\374\376\376"
  "\372\376\362\332\375\340\301\341\300\264\260\253\262jvdbq\\XkVJTDNTCCG8O"
  "TE\322\321\313\377\377\375\376\376\373\376\377\376\376\376\375\376\374\362"
  "\376\360\342\344\311\306\250\244\254R_PL^HXkT<@2OP@`dP\217\220\177\374\374"
  "\370\377\377\374\376\375\371\377\377\376\376\374\360\377\367\336\376\350"
  "\316\342\303\274\246\236\245jtbXdQTdNQYGU\\KchV\317\315\302\377\376\372\377"
  "\376\367\376\373\360\377\376\367\376\366\337\376\355\312\374\331\271\323"
  "\263\251\216\214\214\\hTP^HL\\FR[LMXI^dW\355\352\342\376\375\366\377\374"
  "\360\376\374\361\376\374\361\376\356\321\374\331\264\374\330\266\330\270"
  "\260\200||Y`SLVE>K9BJ<CN?VYP\347\330\322\376\366\345\376\363\330\376\367"
  "\337\377\372\350\374\342\314\326\243\210\375\350\314\352\317\304shc^`TV`"
  "RVbT>B4IS?PTD\244\232\216\374\355\320\376\354\311\376\351\306\376\362\332"
  "\374\344\321\267\206u\375\362\337\326\274\272\\POMNBT]LNZH:<*<A*TV>OI;\242"
  "\222\207\340\304\243\375\335\262\372\336\272\376\361\334\320\241\212\374"
  "\352\322\266\233\237c\\WFH;MR>\\`F~xP\220\214[pqE\211\202\\g]=\230\214`\313"
  "\266\207\344\303\240\362\336\274\323\257\201\333\304\240\305\252\204\254"
  "\232p\216\206\\\206\203U\232\224b\234\244b\246\257m\220\232`\224\227h~\202"
  "W\206\213]\204\210W\227\227i|\177RvzNlsGrtJwtLz}N{\204RlxF",
};

(Sorry, for the low image quality. Hi-res picture whould have been too large for this site.)

The two files have to compiled and to to be linked together. (Instead of using a header, I simply (re-)declared the variable in fluffyCat.cc at the beginning of the other file.)

The Qt project file testQGLWidgetHUD.pro:

SOURCES = testQGLWidgetHUD.cc fluffyCat.cc

QT += widgets opengl

Snapshot of the testQGLWidgetHUD

By the way, I didn't realize the OP was asking for QGLWidget. The sample uses the new QOpenGLWidget which is recommended since Qt5+.

The QTimer is used for very simple kind of animation (to show that periodical painting is done).

Btw. I stumbled upon a mistake (which hit me not the first time...) It is important to set GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER because these are two of the rare OpenGL states which do not work if left with default values.

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
  • When I just create the Qpainter (with QPainter qPainter(this)), it overrides the texture I'm drawing, even if I don't call any other painting instructions. Any ideas why? I've also tried the whole block of code, and it doesn't work, just paints the diagonal lines. – JLev Feb 26 '17 at 08:24
  • @JLev I requested explicitly `QOpenGLFunctions_1_1` to use OpenGL 1.1 as you did. The default `QOpenGLFunctions` does not provide `glColor3f()` and `glRectf()` anymore. (It's deprecated in OpenGL 3+.) Maybe, the requested OpenGL 1.1 functions are not supported on your OpenGL driver (although - that's hard to believe...) – Scheff's Cat Feb 26 '17 at 09:24
  • @JLev I tried to remember what happens if `QOpenGLFunctions`xx is not able to bind the requested OpenGL functions. I couldn't find any success retrieval function in the Qt `QOpenGLFunctions` doc. I guess, it simply crashs in this case. (I once saw this when the driver update on the Windows computer of a colaborator failed and fell back to generic drivers without appropriate OpenGL support. About Linux I do not know.) – Scheff's Cat Feb 26 '17 at 10:30
  • I tried switching to QopenGlwidget, but can't init QOpenGLFunctions. I don't need it, where did you see I'm using it? – JLev Feb 26 '17 at 14:03
  • 1
    `QOpenGLFunctions` is the OpenGL API which is part of Qt. Qt recommends to use it but it's not a must. In our (companies) project, we started with `QOpenGLFunctions` but switched later to an own OpenGL C binding because we want to use our GL rendering code independently of Qt too. It does work but not without some additional efforts. As I already said, if GL states are changed these may be tracked by Qt internally and Qt code can crash if these states are not recovered after performing "non-Qt" OpenGL rendering code. Btw. Qt may use OpenGL rendering for 2d widgets also (depending on config.) – Scheff's Cat Feb 26 '17 at 14:37
1

Well, finally this worked for adding a small rect -

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
QPainter qPainter(this);
QPainterPath path;
path.addRect(10, 10, 30,30);
QPen pen(Qt::black, 1);
qPainter.setPen(pen);
qPainter.fillPath(path, Qt::black);
qPainter.drawPath(path);
qPainter.end();
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glShadeModel( GL_FLAT );
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_TEXTURE_2D);
glColor3f(0.5, 0.5, 0);
glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 1.0f); glVertex2f(0.5f, -0.5f);  // vertex 1
glTexCoord2f(0.0f, 0.0f); glVertex2f(0.5f, 0.5f); // vertex 2
glTexCoord2f(1.0f, 0.0f); glVertex2f(-0.5f, 0.5f); // vertex 3
glTexCoord2f(1.0f, 1.0f); glVertex2f(-0.5f, -0.5f); // vertex 4
glEnd();
glDisable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
JLev
  • 715
  • 1
  • 8
  • 25