QGLWidget rendering area not resizing proprely - c++

I am developing an OpenGL application using Qt. Until yesterday I was subclassing QOpenGLWidget to create my custom widget but after adding It to the main application which is a QMainWindow with a few buttons and three QGraphicsView it ran really slow. I have tried using QGLWidget and the application runs the same as without the OpenGL widget.
The problem I have is that the widget that I've made subclassing QGLWidget does not resize properly (or at least the OpenGL rendering area doesn't).
I will give you an example using hellogl2 example from Qt 5.5.
Using QOpenGLWidget I get:
https://s32.postimg.org/u136s54ld/QOpen_GLWidget.png
Using QGLWidget I get:
https://s32.postimg.org/ahylis5tt/QGLWidget.png
I just changed the parent class from QOpenGLWidget to QGLWidget the rest of the code is the same. The same thing happens in my aplication.
I have tried to find a solution but I could not. May someone tell me why is this happening and how to solve it?
Thank you!

Firstly, its a good idea to stick with the up to date approach and stay away from depricated code as suggested by #ddriver.
Having said that I have written a lot of code using the QGLWidget and there is no problem with it. It does everything I need so I stick with it for now.
Here is the main issue with your approach. You took the hellogl example from qt 5.5 and simply replaced the parent from QOpenGLWidget to QGLWidget. Unfortunately they don't handle resize events similarly.
QOpenglGLWidget does change the viewport based on the width and height given by the resize event as mentioned (not so clearly with a double negative) in their documentation. Link : http://doc.qt.io/qt-5/qopenglwidget.html#resizeGL
check function resizeEvent() which in turn invokes resizeGL().
Now the QGL widget does not do this for you. All it does is makes the opengl context current which means you have to handle it old school style calling glViewport() inside the resizeGL() function. I have copied a code snippet from the hellogl example from qt4.8
void GLWidget::resizeGL(int width, int height)
{
int side = qMin(width, height);
glViewport((width - side) / 2, (height - side) / 2, side, side);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
#ifdef QT_OPENGL_ES_1
glOrthof(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
#else
glOrtho(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
#endif
glMatrixMode(GL_MODELVIEW);
}
Here is a link for qt4.8 version of the hellogl : http://doc.qt.io/qt-4.8/qt-opengl-hellogl-example.html
Notice how the glViewport() is called to resize the framebuffer inside the resizeGL() function. This is done automatically by the newer QOpenGLWidget even before reaching this function. If you choose to stick with QGLWidget, you need to handle this yourself.
There may be other subtle differences as well which you will need to figure out.

Related

QGraphicsView scaling with High DPI

I have an application with QMainWindow in which I insert my own widget, inherited from QGraphicsView. As viewport I use QGLWidget. Everything works fine but with Hidh DPI there is a problem: my widget(inherited from QGraphicsView) is very small.
Before creation of QApplication I enable High DPI support by
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
and in my widget I do following (from signal that comes from QMainWindow deep in the code):
void MyWidget::onNeedResize(QRect newGeom)
{
// some logic, that not interact with GUI stuff
setGeometry(newGeom);
setSceneRect(QRect(QPoint(0, 0), newGeom.size()));
// more logic, that not interact with GUI stuff
}
What did I missed? Where is a problem?
UPD1: I replaced QGLWidget by QOpenGLWidget and everything started work just as expected! Without any modifications/calculations/additional stuff. Setting of the flag is enough. But the problem is I can't use QOpenGLWidget instead of QGLWidget for now.
My assumption on why the dpi scaling does not work is because you use OpenGL widget as your viewport. From Qt docs:
Applications mostly work with device independent pixels. Notable exceptions are OpenGL and code that works with raster graphics.
From that document, it means that OpenGL-content widgets will not be scaled even if you use Qt::AA_EnableHighDpiScaling.
Try to use devicePixelRatio() directly in your resizing code. An example on how it can be used within your code:
void MyWidget::onNeedResize(QRect newGeom)
{
// some logic, that not interact with GUI stuff
setGeometry(QRect(newGeom.x() * Application::desktop()->devicePixelRatio(),
newGeom.y() * Application::desktop()->devicePixelRatio(),
newGeom.width() * Application::desktop()->devicePixelRatio(),
newGeom.height() * Application::desktop()->devicePixelRatio() ));
setSceneRect(QRect(QPoint(0, 0), QSize(
newGeom.width() * Application::desktop()->devicePixelRatio(),
newGeom.height() * Application::desktop()->devicePixelRatio() ) ));
// more logic, that not interact with GUI stuff
}
That is, for every sizing/position you use within your widget, use a scaling factor of Application::desktop()->devicePixelRatio(). That should fix your problem.

QOpenGLWidget without subclassing

I was just wondering if QOpenGLWidget that is on Qt (I am using version 5.4) can be used without subclassing.
In the Qt Creator, form editor, there is a Display widget that can be inserted into the form named "OpenGL Widget". The widget appears black when inserted and is QOpenGLWidget object. Compilation does not throw errors, such as the fact that the initializedGL() virtual function must be implemented.
The Qt help available and examples only show how to use this object by subclassing it. This kind of defeats the purpose of having an insertable object on the toolbar so I thought perhaps there could be a way.
I noticed that the widget has functions such as makeCurrent(), which I think could be useful.
Thanks
This kind of defeats the purpose of having an insertable object on the toolbar so I thought perhaps there could be a way.
You have to subclass, but you also want the .ui file to use the widget of the correct derived class, not of the base class.
There are two ways to go about it:
Insert the base class widget. Then promote it to the derived class. You can also do it manually by editing the .ui file.
Make a custom widget Designer plugin for your class. Note that this plugin (and the widget!) have to be compiled using the same version/configuration of Qt used to compile Qt Creator itself, so in most cases you have to compile Qt Creator itself first.
If you're serious about development in Qt, you'll probably have your own Designer and Creator plugins, and you'll build Creator yourself, as well as Qt.
You can do something like this by subclassing a shell widget
void OpenGLDisplayWidget::initializeGL()
{
m_renderer->initialise(context());
}
void OpenGLDisplayWidget::resizeGL(int w, int h)
{
m_renderer->resize(w, h);
}
void OpenGLDisplayWidget::paintGL()
{
m_renderer->draw();
}
So your rendering code isn't tied to the display widget.
I just made a few experiments and I found that the QOpenGLWidget can be used very easily, without subclassing.
Insert the "Open GL Widget" (QOpenGLWidget object)
Insert a button and go to "clicked()" slot.
Insert the following code
void MainWindow::on_pushButton_clicked()
{
ui->openGLWidget->makeCurrent();
//Paint a colorful triangle
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(-0.5, -0.5, 0);
glColor3f(0.0, 1.0, 0.0);
glVertex3f( 0.5, -0.5, 0);
glColor3f(0.0, 0.0, 1.0);
glVertex3f( 0.0, 0.5, 0);
glEnd();
}
Run and click the button and you should see this
screenshot
So no need to subclass, however it may be useful to include an initialization function that sets the projection matrix and aspect ratio.
Also these GL drawing functions must be called within the mainwindow which may not be desirable for the modular fanatics.

How do I actually use OpenGL in Qt 5?

Simply put, I cannot for the life of me figure out how to actually make use of things like QOpenGLWidget or QOpenGLWindow or anything. I want to have the rendering I do be a child widget of a window in a MDI, but nothing works.
Here's the code I have currently set up for the widget (at least, just the parts involving OpenGL):
Viewport::Viewport(QWidget * parent) : QOpenGLWidget(parent) { }
void Viewport::initializeGL() {
initializeOpenGLFunctions();
}
void Viewport::paintGL() {
// first, clear the screen
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
And here's how I use the widget:
vp = new Viewport;
vp->resize(QSize(320,240));
hbox->addWidget(vp);
And the result is that I see nothing. I just see a tiny sliver of empty space next to the other widget in the window, but that's it. No black screen like I try to clear with, not even a 320x240-sized empty space.
Like I said, I've been unable to do this in any of the ways I could find, and it's really frustrating. Am I missing something obvious? There's very little documentation as-is, so it's hard to tell if I am, or if there's some weird corner case I'm running into. (For example, none of the documentation I find uses QOpenGLWidget as part of a larger widget; is that because it can't be, or because all the examples I can find are just lazy about using the widget as its own top-level window?)
After some more fiddling around, it turns out my issue was apparently caused by the other object in the window (a QListView) by default taking up as much space as possible, making the OpenGL widget disappear since it doesn't have a minimum size.
In other words, the problem is fixed by either changing the QListView to have a QSizePolicy::Preferred sizing policy (since resizing the window will now let you see the OpenGL widget), or by giving the OpenGL widget a minimum or fixed size.
(As an aside, I really wish this could've been more obvious than just stumbling upon it by chance.)

SDL resetting glViewport

I'm testing supporting multiple resolutions in an application using SDL2 with OpenGL. To create my "letterbox" functionality I set my glViewport to an appropriate value and everything works perfectly.
However, if I create my window with the SDL_WINDOW_ALLOW_HIGHDPI flag set, whenever I move my window (after receiving the SDL_WINDOWEVENT_MOVED event) SDL modifies the viewport to the full size of the window, which can be verified by calling SDL_GL_GetDrawableSize during the event.
If I do not set SDL_WINDOW_ALLOW_HIGHDPI when creating the window, the viewport is not reset. I do believe this to be a bug, but cannot find anything through the SDL bugzilla so I thought to ask if anyone has seen similar behavior.
You may need to have a retina MacBook Pro to experience this behavior.
Just do what you should be doing anyway: Always re-/set the viewport at the beginning of drawing each frame. As soon as you want to implement a HUD, use framebuffer objects or similar things you'll have to set the viewport (several times) for drawing each frame.

Moving from QGLWidget to QWindow

I have some code using Qt 4. I want to migrate it to Qt 5 and switch to QOpenGL stuff. I can't understand where the code from void paintGL() and void initializeGL() needs to go in a QWindow. Can anyone help me with an example?
I have created a simple example of using QWindow with OpenGL.
To simplify OpenGL development I have created an abstract class GLWindow, which contains the virtual functions initializeGL(), paintGL() and resizeGL(int w, int h). I believe the source code shows the relationship between the old style and the new style.
The example is available here:
https://github.com/mortennobel/QtOpenGLChapter/tree/master/OpenGL3xAlt
Qt has at least one example (Overpainting) of putting widgets over an OpenGL scene. By looking at that, it appears the easiest way would be to inherit from QGLWidget instead of QWidget and override the appropriate virtual functions for initialize and paint.
The standard example for using QWindow with OpenGL is hellowindow in qtbase/examples/opengl.
There is no direct replacement for initializeGL and paintGL. Instead you do something like this:
Have a QWindow with surface type OpenGLSurface.
Create a QOpenGLContext with a matching format.
When the window receives an expose event, start rendering (makeCurrent, your GL calls and finally swapBuffers).