I have loaded a QOpenGLWidget promoted into my Window class with QtDesigner, inside a QMainWindow.
The widget display is fine, I can see the obj model in my mainwindow.
My update() function from Window is called (I know from qDebug()).
However my OpenGLWidget can't seem to be repainted inside the mainwindow unless I interact with the mainwindow (for instance, resize it, or move it on my screen). I guess these interactions imply the repaint of the whole mainwindow.
I tryied to force the repaint of the whole mainwindow everytime the OpenGL Window is updated but I can't make this work.
update() function from window.cpp :
void Window::update()
{
qDebug() << "update" ;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_transform.rotate(5.0f, QVector3D(0.4f, 0.3f, 0.3f));
emit updated() ;
// Schedule a redraw
paintGL();
}
m_transform is my transformation matrix which rotates my obj model.
Redefinition of update() slot inside mainwindow.h, which is called :
void MainWindow::update() {
qDebug() << "update mainwindow";
this->repaint();
}
Connexion between signal and slot :
QObject::connect(ui->GLWidget, SIGNAL(updated()), this, SLOT(update()));
Edit : paintGL function in window class :
void Window::paintGL()
{
qDebug() << "paintGl" ;
// Clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.0f, 0.0f, 0.2f, 1.0f);
// Render using our shader
m_program->bind();
m_program->setUniformValue(u_worldToCamera, m_camera.toMatrix());
m_program->setUniformValue(u_cameraToView, m_projection);
{
m_program->setUniformValue(u_modelToWorld, m_transform.toMatrix());
m_model.draw(m_program) ;
}
m_program->release();
qDebug() << "end paintGL";
}
I'm using Qt5.6 on QtCreator and OpenGL 3.3 on Ubuntu.
Related
I am currently having a conflict between my paintGL() and my QPaintEvent function. I am trying to render a line that acts as a measuring tool to use on objects drawn in the paintGL(), however my current implementation renders a blank screen and I can't seem to figure out the problem. I've also tried placing the code from inside the paintEvent into the paintGL, this renders the objects inisde the paintGL but the line to be drawn from the QPainter does not work.
This is my paintEvent()
void Widget::paintEvent(QPaintEvent* e)
{
if (drawLine) {
QPainter painter(this);
QPen paintpen(Qt::red);
paintpen.setWidth(4);
QPen linepen(Qt::black);
linepen.setWidth(4);
QPoint p1;
p1.setX(xAtPress);
p1.setY(yAtPress);
painter.setPen(paintpen);
painter.drawPoint(p1);
QPoint p2;
p2.setX(xAtRelease);
p2.setY(yAtRelease);
painter.setPen(paintpen);
painter.drawPoint(p2);
painter.setPen(linepen);
painter.drawLine(p1, p2);
}
}
This is my paintGL()
void Widget::paintGL()
{
// Clear the color and depth buffer with specified clear colour.
// This will replace everything that was in the previous frame with the clear colour.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
test0->draw();
test1->draw();
test2->draw();
}
}
I apologize if this isn't exact. I'm doing the best I can to copy code by hand from one computer to another, and the destination computer doesn't have a compiler (don't ask).
Header file
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <qopenglwidget.h>
class MyOpenGlWidget : public QOpenGLWidget
{
Q_OBJECT
public:
explicit MyOpenGlWidget(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowFlags());
virtual ~MyOpenGlWidget();
protected:
// these are supposed to be overridden, so use the "override" keyword to compiler check yourself
virtual void initializeGL() override;
virtual void resizeGL(int w, int h) override;
virtual void paintGL() override;
private:
QPixmap *_foregroundPixmap;
}
#endif
Source file
QOpenGLFunctions_2_1 *f = 0;
MyOpenGlWidget::MyOpenGlWidget(QWidget *parent, Qt::WindowFlags f) :
QOpenGLWidget(parent, f)
{
_foregroundPixmap = 0;
QPixmap *p = new QPixmap("beveled_texture.tiff");
if (!p->isNull())
{
_foregroundPixmap = p;
}
}
MyOpenGlWidget::~MyOpenGlWidget()
{
delete _foregroundPixmap;
}
void MyOpenGlWidget::initializeGL()
{
// getting a deprecated set of functions because such is my work environment
// Note: Also, QOpenGLWidget doesn't support these natively.
f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
f->glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // clearing to green
f->glEnable(GL_DEPTH_TEST);
f->glEnable(GL_CULL_FACE); // implicitly culling front face
f->glEnable(GL_SCISSOR_TEST);
// it is either copy the matrix and viewport code from resizeGL or just call the method
this->resizeGL(this->width(), this->height());
}
void MyOpenGlWidget::resizeGL(int w, int h)
{
// make the viewport square
int sideLen = qMin(w, h);
int x = (w - side) / 2;
int y = (h - side) / 2;
// the widget is 400x400, so this random demonstration square will show up inside it
f->glViewport(50, 50, 100, 100);
f->glMatrixMode(GL_PROJECTION);
f->glLoadIdentity();
f->glOrtho(-2.0f, +2.0f, -2.0f, +2.0f, 1.0f, 15.0f); // magic numbers left over from a demo
f->glMatrixMode(GL_MODELVIEW);
// queue up a paint event
// Note: QGLWidget used updateGL(), but QOpenGLWidget uses update().
this->update();
}
void MyOpenGlWidget::paintGL()
{
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// I want to draw a texture with beveled edges the size of this widget, so I can't
// have the background clearing all the way to the edges
f->glScissor(50, 50, 200, 200); // more magic numbers just for demonstration
// clears to green in just scissored area (unless QPainter is created)
f->glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
// loading identity matrix, doing f->glTranslatef(...) and f->glRotatef(...)
// pixmap loaded earlier in another function
if (_foregroundPixmap != 0)
{
// QPixmap apparently draws such that culling the back face will cull the entire
// pixmap, so have to switch culling for duration of pixmap drawing
f->glCullFace(GL_FRONT);
QPainter(this);
painter.drawPixmap(0, 0, _foregroundPixmap->scaled(this->size()));
// done, so switch back to culling the front face
f->glCullFace(GL_BACK);
}
QOpenGLFunctions_2_1 *f = 0;
void MyOpenGlWidget::initializeGL()
{
// getting a deprecated set of functions because such is my work environment
// Note: Also, QOpenGLWidget doesn't support these natively.
f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
f->glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // clearing to green
f->glEnable(GL_DEPTH_TEST);
f->glEnable(GL_CULL_FACE); // implicitly culling front face
f->glEnable(GL_SCISSOR_TEST);
// it is either copy the matrix and viewport code from resizeGL or just call it directly
this->resizeGL(this->width(), this->height());
}
void MyOpenGlWidget::resizeGL(int w, int h)
{
// make the viewport square
int sideLen = qMin(w, h);
int x = (w - side) / 2;
int y = (h - side) / 2;
// the widget is 400x400, so this random demonstration square will show up inside it
f->glViewport(50, 50, 100, 100);
f->glMatrixMode(GL_PROJECTION);
f->glLoadIdentity();
f->glOrtho(-2.0f, +2.0f, -2.0f, +2.0f, 1.0f, 15.0f); // magic numbers left over from a demo
f->glMatrixMode(GL_MODELVIEW);
// queue up a paint event
// Note: QGLWidget used updateGL(), but QOpenGLWidget uses update().
this->update();
}
void MyOpenGlWidget::paintGL()
{
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// I want to draw a texture with beveled edges the size of this widget, so I can't
// have the background clearing all the way to the edges
f->glScissor(50, 50, 200, 200); // more magic numbers just for demonstration
// clears to green in just scissored area (unless QPainter is created)
f->glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
// loading identity matrix, doing f->glTranslatef(...) and f->glRotatef(...), drawing triangles
// done drawing, so now draw the beveled foreground
if (_foregroundPixmap != 0)
{
// QPixmap apparently draws such that culling the back face will cull the entire
// pixmap, so have to switch culling for duration of pixmap drawing
f->glCullFace(GL_FRONT);
QPainter(this);
painter.drawPixmap(0, 0, _foregroundPixmap->scaled(this->size()));
// done, so switch back to culling the front face
f->glCullFace(GL_BACK);
}
}
The problem is this code from paintGL():
QPainter(this);
As soon as a QPainter object is created, the glScissor(...) call that I made earlier in the function is overrun and some kind of glClearColor(...) call is made (possibly from QPainter's constructor) that clears the entire viewport to the background color that I set just after glScissor(...). Then the pixmap draws my beveled texture just fine.
I don't want QPainter to overrun my scissoring.
The closest I got to an explanation was two QPainter methods, beginNativePainting() and endNativePainting(). According to the documentation, scissor testing is disabled between these two, but in their example they re-enable it. I tried using this "native painting" code, but I couldn't stop QPainter's mere existence from ignoring GL's scissoring and clearing my entire viewport.
Why is this happening and how do I stop this?
Note: This work computer has network policies to prevent me from going to entertainment sites like imgur to upload "what I want" and "what I get" pictures, so I have to make due with text.
Why is this happening
The OpenGL context is a shared resource and you have to share it with other players.
and how do I stop this?
You can't. Just do the proper thing and set viewport, scissor rectangle and all the other drawing related state at the right moment: Right before you are going to draw something that relies on these settings. Don't set them aeons (in computer terms) before, somewhere in some "initialization" or a reshape handler. And be expected that in drawing code any function you call that makes use of OpenGL will leave some garbage behind.
I have a slight problem when calling glClearColor from various places outside of paintGL(). The aim is to enable the user to set the clear colour on the fly but this is not working as planned unless glClearColor is called each frame in paintGL.
Aim:
void GLWidget::mousePressEvent(QMouseEvent *event)
{
m_lastPos = event->pos();
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); //<-- Doesn't change clear colour
}
Non-optimal workaround:
void GLWidget::mousePressEvent(QMouseEvent *event)
{
m_lastPos = event->pos();
r = 1.0f;
g = 0.0f;
b = 0.0f;
a = 1.0f;
}
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glClearColor(r, g, b, a);
...
I assume this has something to do with how Qt swaps buffers and updates the screen but it's not clear what exactly is causing it. Any ideas would be great, thanks.
I assume this has something to do with how Qt swaps buffers and updates the screen but it's not clear what exactly is causing it. Any ideas would be great, thanks.
Wrong, it has to do with doing OpenGL calls with no OpenGL context bound. You must call makeCurrent before doing any OpenGL call.
Why does it work in paintGL then? Because Qt makes the context current automatically before calling paintGL, resizeGL and initializeGL (see their documentation).
I don't think #peppe answer correctly because
You don't need to call makeCurrent() since Qt have already to
In my opinion, what you really need is just an update() call in mousePressEvent.
If you wan't to call paintGL() to update, what you need is just to call update()
Also, you should know that in glClearColor(), you only set the clearColor attribute on OpenGL state machine. glClear(GL_COLOR_BUFFER_BIT) is the true function which actually clear the color buffer. So you should set clearColor by glClearColor() before calling glClear().
if I were you, the code would be like:
void GLWidget::mousePressEvent(QMouseEvent *event)
{
m_lastPos = event->pos();
r = 1.0f;
g = 0.0f;
b = 0.0f;
a = 1.0f;
update();
}
void GLWidget::paintGL()
{
glClearColor(r, g, b, a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
i have a MainWindow which displays some plots that come from widget promotion. Now i have decided that i want to embed a QGLWidget so that i can draw a 3D image near them.
I created a QGLWidget inside the creator design and i have promoted it to MyGLWidget class
MyGLWidget.h
#ifndef MYGLWIDGET
#define MYGLWIDGET
#include <QtOpenGL>
#include <QGLWidget>
#include <array>
class MyGLWidget : public QGLWidget{
Q_OBJECT // must include this if you use Qt signals/slots
public:
explicit MyGLWidget(QWidget *parent = 0);
std::array<GLfloat, 3> angles;
protected:
// Set up the rendering context, define display lists etc.:
void initializeGL();
// draw the scene:
void paintGL();
// setup viewport, projection etc.:
void resizeGL (int width, int height);
};
#endif // MYGLWIDGET
MyGLWidget.cpp
#include "myglwidget.h"
#include <gl/GLU.h>
#include <iostream>
MyGLWidget::MyGLWidget(QWidget *parent){
angles[0] = 50.0;
angles[1] = 15.0;
}
/*
* Sets up the OpenGL rendering context, defines display lists, etc.
* Gets called once before the first time resizeGL() or paintGL() is called.
*/
void MyGLWidget::initializeGL(){
//activate the depth buffer
glEnable(GL_DEPTH_TEST);
}
/*
* Sets up the OpenGL viewport, projection, etc. Gets called whenever the widget has been resized
* (and also when it is shown for the first time because all newly created widgets get a resize event automatically).
*/
void MyGLWidget::resizeGL (int width, int height){
glViewport( 0, 0, (GLint)width, (GLint)height );
/* create viewing cone with near and far clipping planes */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 30.0);
glMatrixMode( GL_MODELVIEW );
}
/*
* Renders the OpenGL scene. Gets called whenever the widget needs to be updated.
*/
void MyGLWidget::paintGL(){
//delete color and depth buffer
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f,0.0f,-20.0f); //move along z-axis
glRotatef(angles[0],0.0,1.0,0.0); //rotate 30 degress around y-axis
glRotatef(angles[1],1.0,0.0,0.0); //rotate 15 degress around x-axis
/* create 3D-Cube */
glBegin(GL_QUADS);
//front
glColor3f(1.0,0.0,0.0);
glVertex3f(1.0,1.0,1.0);
glVertex3f(-1.0,1.0,1.0);
glVertex3f(-1.0,-1.0,1.0);
glVertex3f(1.0,-1.0,1.0);
//back
glColor3f(0.0,1.0,0.0);
glVertex3f(1.0,1.0,-1.0);
glVertex3f(-1.0,1.0,-1.0);
glVertex3f(-1.0,-1.0,-1.0);
glVertex3f(1.0,-1.0,-1.0);
//top
glColor3f(0.0,0.0,1.0);
glVertex3f(-1.0,1.0,1.0);
glVertex3f(1.0,1.0,1.0);
glVertex3f(1.0,1.0,-1.0);
glVertex3f(-1.0,1.0,-1.0);
//bottom
glColor3f(0.0,1.0,1.0);
glVertex3f(1.0,-1.0,1.0);
glVertex3f(1.0,-1.0,-1.0);
glVertex3f(-1.0,-1.0,-1.0);
glVertex3f(-1.0,-1.0,1.0);
//right
glColor3f(1.0,0.0,1.0);
glVertex3f(1.0,1.0,1.0);
glVertex3f(1.0,-1.0,1.0);
glVertex3f(1.0,-1.0,-1.0);
glVertex3f(1.0,1.0,-1.0);
//left
glColor3f(1.0,1.0,0.0);
glVertex3f(-1.0,1.0,1.0);
glVertex3f(-1.0,-1.0,1.0);
glVertex3f(-1.0,-1.0,-1.0);
glVertex3f(-1.0,1.0,-1.0);
glEnd();
}
Now in the constructor of mainwindow.cpp i call ui->wgl->show(); where wgl is the ObjectName of the widget promoted to my class.
The cube is rendered but the widget pops out from the mainwindow instead of stay where i designed it
Your widget has no parent, but in Qt widget without parent is a separate window, so try to do this in constructor.
MyGLWidget::MyGLWidget(QWidget *parent) : QGLWidget(parent)
{
angles[0] = 50.0;
angles[1] = 15.0;
}
If you really use Qt Designer, then you have ui->setupUi(this); in your code. It does something like this (allocate memory and set parent):
MyGLWidget *wgl = new MyGLWidget(this);
It passes this as parent, but your current MyGLWidget take it but ignores it. So with code in my answer all should be fine.
I would like to draw some overlay on my image viewer : a dashed rectangle around the image, indicating the bounding box.
Here 's what I do in the paintEvent function:
void ViewerGL::paintEvent(QPaintEvent* event){
makeCurrent();
QPainter p(this);
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT); // << If I don't do this it clears out my viewer white
// and I want it black
p.setBackgroundMode(Qt::TransparentMode); // < was a test, doesn't seem to do anything
p.setBackground(QColor(0,0,0,0));
//loading the appropriate shader before texture mapping
if(rgbMode() && _shaderLoaded){
shaderRGB->bind();
}else if(!rgbMode() && _shaderLoaded){
shaderLC->bind();
}
paintGL(); // render function (texture mapping)
//drawing a rect around the texture on the viewer
QPen pen(Qt::DashLine);
pen.setColor(QColor(233,233,233));
p.setPen(pen);
QPoint btmRight=mousePosFromOpenGL(dataWindow().w(),dataWindow().h() +transY*2 + ( zoomY-dataWindow().h()/2)*2);
QPoint btmLeft=mousePosFromOpenGL(0,dataWindow().h() +transY*2 + ( zoomY-dataWindow().h()/2)*2);
QPoint topLeft=mousePosFromOpenGL(0,0 +transY*2 + ( zoomY-dataWindow().h()/2)*2);
QPoint topRight=mousePosFromOpenGL(dataWindow().w(), 0+ +transY*2 + ( zoomY-dataWindow().h()/2)*2);
p.drawLine(topLeft,topRight);
p.drawLine(topRight,btmRight);
p.drawLine(btmRight,btmLeft);
p.drawLine(btmLeft,topLeft);
QPoint pos = mousePosFromOpenGL( (dataWindow().w()) + 10 ,
(dataWindow().h()) +transY*2 + ( zoomY-dataWindow().h()/2)*2 +10); // bottom right of the texture +10
p.drawText(pos, _resolutionOverlay);
p.end();
}
And here is what I do in the paintGL function:
glPushAttrib(GL_ALL_ATTRIB_BITS);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
... my drawing code
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
And the initializeGL function :
initAndCheckGlExtensions();
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
glGenTextures (1, texId);
glGenTextures (1, texBlack);
glGenBuffersARB(1, &texBuffer);
shaderBlack=new QGLShaderProgram(context());
shaderBlack=new QGLShaderProgram(context());
if(!shaderBlack->addShaderFromSourceCode(QGLShader::Vertex,vertRGB))
cout << shaderBlack->log().toStdString().c_str() << endl;
if(!shaderBlack->addShaderFromSourceCode(QGLShader::Fragment,blackFrag))
cout << shaderBlack->log().toStdString().c_str() << endl;
if(!shaderBlack->link()){
cout << shaderBlack->log().toStdString().c_str() << endl;
}
So far it works, I have what I want, but my program is flooding stderr on exit with :
'QGLContext::makeCurrent: Cannot make invalid context current'.
I know this is coming from the QPainter and not from something else in my program.
I tried to move the code in the paintGL function to another function that is not virtual but that did not change anything.
You should first perform OpenGL calls, clear your OpenGL state, then wrap ALL QPainter draw calls between QPainter::begin(QGLWidget *) and QPainter::end(QGLWidget *).
Your program is most likely causing a problem because you interleave QPainter and OpenGL drawing calls. When you instantiate the QPainter object with QPainter(QPaintDevice *) you tell the QPainter to use the native drawing facilities of the QGLWidget to perform QPainter operations. So... QPainter uses OpenGL fixed-function calls to perform 2D drawing and it may interfere with your OpenGL rendering calls when your state has not been cleared.
I suggest following this guide:
https://doc.qt.io/archives/qt-4.8/qt-opengl-overpainting-example.html