I have a problem if I want to display a video using QGLWidget
. With one instance it works, but it doesn't and widgets are black with multi-occurrence (usage in QGridLayout
for example).
I subclass QGLWidget
that way to play a video (by refreshing the QPixmap
I want to show using my setCurrentImage
method):
class GLWidget : public QGLWidget
{
Q_OBJECT
public:
GLWidget(QGLWidget* shareWidget = 0, QWidget* parent = 0)
: QGLWidget(parent, shareWidget)
{
//...
// I do this because the QPixmap to refresh is produced in a different thread than UI thread.
connect(this, SIGNAL(updated()), this SLOT(update()));
}
//...
void setCurrentImage(const QPixmap& pixmap)
{
m_mutex.lock();
m_pixmap = pixmap;
m_mutex.unlock();
emit updated();
}
protected:
void initializeGL()
{
static const int coords[4][2] = {{ +1, -1 }, { -1, -1 }, { -1, +1 }, { +1, +1 }};
for(int j = 0; j < 4; ++j){
m_texCoords.append(QVector2D(j == 0 || j == 3, j == 0 || j == 1));
m_vertices.append(QVector2D(0.5 * coords[j][0], 0.5 * coords[j][1]));
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
QGLShader* vShader = new QGLShader(QGLShader::Vertex, this);
const char* szVShaderCode = /*...*/;
vShader->compileSourceCode(szVShaderCode);
QGLShader *fShader = new QGLShader(QGLShader::Fragment, this);
const char* szFShaderCode = /*...*/;
fShader->compileSourceCode(szFShaderCode);
m_pProgram = new QGLShaderProgram(this);
m_pProgram->addShader(vShader);
m_pProgram->addShader(fShader);
m_pProgram->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE);
m_pProgram->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
m_pProgram->link();
m_pProgram->bind();
m_pProgram->setUniformValue("texture", 0);
}
void paintGL()
{
qglClearColor(Qt::black);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// I use some uniform values to "modify" image
m_pProgram->setUniformValue(/*...*/);
//...
QMatrix4x4 m;
m.ortho(-0.5f, +0.5f, +0.5f, -0.5f, 4.0f, 15.0f);
m.translate(0.0f, 0.0f, -10.0f);
m_pProgram->setUniformValue("matrix", m);
m_pProgram->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
m_pProgram->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
m_pProgram->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, m_vertices.constData());
m_pProgram->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, m_texCoords.constData());
glBindTexture(GL_TEXTURE_2D, m_texture);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
void resizeGL(int iWidth, int iHeight)
{
glViewport(0, 0, iWidth, iHeight);
}
private slots:
void update()
{
QPixmap pixmap;
m_mutex.lock();
pixmap = m_pixmap;
m_mutex.unlock();
m_texture = bindTexture(pixmap, GL_TEXTURE_2D);
updateGL();
}
private:
QMutex m_mutex;
QPixmap m_pixmap;
QVector<QVector2D> m_vertices;
QVector<QVector2D> m_texCoords;
QGLShaderProgram* m_pProgram;
};
Then, to simulate a multi thread rendering (each thread produces its own video images), I wrote these little classes.
The image producer class:
class ImageProducer : public QThread
{
Q_OBJECT
public:
ImageProducer(QGLWidget* pGLWidget)
: QThread(pGLWidget), m_pGLWidget(pGLWidget)
{
m_pixmap = QPixmap("fileName.jpg");
m_bMustStop = false;
}
protected:
void run()
{
while(!m_bMustStop){
static_cast<GLWidget*>(m_pGLWidget)->setCurrentImage(m_pixmap);
// Simulate a frame rate
msleep(1000 / /*FRAME_RATE*/);
}
}
private:
QGLWidget* m_pGLWidget;
QPixmap m_pixmap;
bool m_bMustStop;
};
And the rendering class:
#define ROWS 1
#define COLS 1
class Window : public QWidget
{
public:
Window()
{
QGridLayout* pMainLayout = new QGridLayout(this);
setLayout(pMainLayout);
for(int i = 0; i < ROWS; ++i){
for(int j = 0; j < COLS; ++j){
QGLWidget* pGLWidget = new GLWidget();
pMainLayout->addWidget(pGLWidget, i, j);
ImageProducer* pImageProducer = new ImageProducer(pGLWidget);
pImageProducer->start();
}
}
}
};
Ok stop with code samples ^^ Problem is with ROWS = 1 and COLS = 1 (see Window
class) it works, but I have black widgets with other values... I'm lost, what do I miss?
Thanks!
EDIT: (context is always multi-GLWidget instance, all works fine with only one)
Strange thing I just discover: I overrode QGLWidget
's mouseMoveEvent
(so, in my GLWidget
class) which simply calls updateGL();
. And fact is when I press and move the mouse with the current code nothing happens. But if I replace (in my ImageProducer
's run()
method):
static_cast<GLWidget*>(m_pGLWidget)->setCurrentImage(m_pixmap);
By
static_cast<GLWidget*>(m_pGLWidget)->setCurrentImage(QPixmap("fileName.jpg"));
The image is refreshed in the current component as long as I move the mouse. When I release it, or do it in an other components, the background become black again.