I used to cap the render framerate in GLFW using the function.
glfwSwapInterval(1);
Now i am building a Opengl project in QT using the Qt opengl features.
in the main function i set the global value.
QGLFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setSampleBuffers(true);
format.setSamples(4);
format.setSwapInterval(1);
QGLFormat::setDefaultFormat(format);
This is how the Qt opengl class header looks like.
class GLWidget : public QGLWidget
{
Q_OBJECT;
public:
explicit GLWidget(QWidget *parent = 0);
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int h) override;
QTimer timer;
};
In the constructor for the class
GLWidget::GLWidget(QWidget *parent) :QGLWidget(parent)
{
connect(&timer, &QTimer::timeout, this, [&]() {
QElapsedTimer elapsedtimer;
elapsedtimer.start();
updateGL();
qDebug() << elapsedtimer.elapsed();
});
timer.setInterval(0);
timer.start();
}
But still the render loop is not synced the Qdebug values keeps varying from 8 to 16.
Related
Problem
When QPainter is created after glClear the latter has no effect.
Description
I use Qt 5.7.1. I get same results with gcc on Linux and vc++ on Windows.
I have the following in a widget derived from QGLWidget:
void CanvasWidget::initializeGL()
{
qglClearColor(m_backgroundColor);
}
V1:
void CanvasWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
QPainter painter(this);
painter.drawLine(0, 0, 1000, 1000);
}
Produces:
V2:
void CanvasWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
}
Produces:
What I want is:
Which can be done with a hack:
void CanvasWidget::paintGL()
{
QPainter painter(this);
qglClearColor(m_backgroundColor);
glClear(GL_COLOR_BUFFER_BIT);
painter.drawLine(0, 0, 1000, 1000);
}
Question
What is going on? Why can't glClean and QPainter work together? Why can't I get it with V1?
Minimal Reproducible Example
main.cpp
#include "MainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainwindow;
mainwindow.show();
return app.exec();
}
MainWindow.h
#pragma once
#include "CanvasWidget.h"
#include <QMainWindow>
#include <memory>
class MainWindow : public QMainWindow
{
public:
explicit MainWindow(QWidget *parent = 0);
MainWindow(const MainWindow &) = delete;
MainWindow & operator= (const MainWindow &) = delete;
virtual ~MainWindow() = default;
private:
std::unique_ptr<CanvasWidget> m_canvasWidget;
};
MainWindow.cpp
#include "MainWindow.h"
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, m_canvasWidget(new CanvasWidget(parent))
{
setCentralWidget(m_canvasWidget.get());
}
CanvasWidget.h
#pragma once
#include <QGLWidget>
class CanvasWidget : public QGLWidget
{
Q_OBJECT
public:
CanvasWidget(QWidget* parent = 0, const QGLWidget* shareWidget = 0, Qt::WindowFlags f = 0);
private:
virtual void initializeGL() override;
virtual void paintGL() override;
private:
QColor m_backgroundColor;
};
CanvasWidget.cpp
#include "CanvasWidget.h"
#include <QMessageBox>
#include <QWheelEvent>
CanvasWidget::CanvasWidget(
QWidget* parent /*= 0*/,
const QGLWidget* shareWidget /*= 0*/,
Qt::WindowFlags f /*= 0 */)
: QGLWidget(parent, shareWidget, f)
, m_backgroundColor(0, 93, 196)
{}
void CanvasWidget::initializeGL()
{
qglClearColor(m_backgroundColor);
}
void CanvasWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
QPainter painter(this);
painter.drawLine(0, 0, 1000, 1000);
}
Adding setAutoFillBackground(false) in CanvasWidget constructor solves the problem.
All the credit goes to #G.M.
In Qt tutorial about overpainting OpenGL with QPainter it is stated:
When overpainting 2D content onto 3D content, we need to use a
QPainter and make OpenGL calls to achieve the desired effect. Since
QPainter itself uses OpenGL calls when used on a QGLWidget subclass,
we need to preserve the state of various OpenGL stacks when we perform
our own calls
So, looking at your code in V1 I suppose that QPainter sets its own clear color state, and if you don't set your glClearColor explicitly, you will get what QPainter object has set.
And I would also suggest you to use a tool like RenderDoc or gDebugger to trace GL commands of your app to see exactly what happens under the hood.
I have this simple example and this program crashed when I tried to call context()->moveToThread(render_thread_);. Can anyone help?
class FrameRenderer: public QThread
{
Q_OBJECT
public:
FrameRenderer(QGLCanvas *parent):m_parent(parent), QThread(){}
void run() Q_DECL_OVERRIDE;
QGLCanvas *m_parent;
};
class QGLCanvas : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
QGLCanvas(QWidget* parent = NULL);
~QGLCanvas();
virtual void initializeGL();
virtual void paintGL();
virtual void resizeGL(int width, int height);
void DrawThreadEntry();
FrameRenderer* render_thread_;
};
void FrameRenderer::run()
{
m_parent->DrawThreadEntry();
}
QGLCanvas::QGLCanvas(QWidget* parent)
: QOpenGLWidget(parent)
{
render_thread_ = new FrameRenderer(this);
doneCurrent();
context()->moveToThread(render_thread_);
render_thread_->start();
}
void QGLCanvas::DrawThreadEntry()
{
while(true)
{
makeCurrent();
QOpenGLFunctions f;
f.initializeOpenGLFunctions();
f.glClearColor(1.0, 1.0, 1.0, 1.0);
f.glFinish();
doneCurrent();
emit update();
}
}
I don't know if it's the solution but i got a similar problem on iOS, the context was available but after a short period of time.
You can try to add a timmer and delay your call to OpenGL
.h
slots:
void update();
.cpp
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);
If it's working maybe you should try look if you can do your move ToThread inside void QOpenGLWidget::initializeGL() which is responsible to set up the context
I wanted to create a simple application where there's triangle generated using OpenGL and three push buttons changing that triangle color. The triangle is generated but unfortunately buttons don't work and I get errors saying:
QObject::connect: No such slot MainWindow::redSlot(OGL) in
..\buttonwidget\mainwindow.cpp:17 QObject::connect: No such slot
MainWindow::greenSlot(OGL) in ..\buttonwidget\mainwindow.cpp:20
QObject::connect: No such slot MainWindow::blueSlot(OGL) in
..\buttonwidget\mainwindow.cpp:23
I have slots definitions:
void MainWindow::redSlot(Widget* w)
{
w->setColor(red);
}
void MainWindow::greenSlot(Widget* w)
{
w->setColor(green);
}
void MainWindow::blueSlot(Widget* w)
{
w->setColor(blue);
}
They are changing variable declared in class Widget that changes color of a generated triangle. Here's class Widget:
class Widget : public QGLWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
QSize minimumSizeHint() const;
QSize sizeHint() const;
enum color c;
void setColor(enum color color1);
protected:
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
};
And then I connect slots to buttons in class MainWindow constructor:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
layout = new QVBoxLayout();
QWidget *w = new QWidget();
setCentralWidget(w);
w->setLayout(layout);
Widget *OGL = new Widget();
//OGL->c=green; - it was a test whether changing value of enum type variable c works
//it works, changing it changes the color of a generated triangle
redButton = new QPushButton(tr("Red"));
connect(redButton, SIGNAL(clicked()), this, SLOT(redSlot(OGL)));
greenButton = new QPushButton(tr("Green"));
connect(greenButton, SIGNAL(clicked()), this, SLOT(greenSlot(OGL)));
blueButton = new QPushButton(tr("Blue"));
connect(blueButton, SIGNAL(clicked()), this, SLOT(blueSlot(OGL)));
layout->addWidget(OGL);
layout->addWidget(redButton);
layout->addWidget(greenButton);
layout->addWidget(blueButton);
}
Here's slot declaration in header:
class MainWindow : public QMainWindow
{
Q_OBJECT
QVBoxLayout *layout;
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QPushButton *redButton;
QPushButton *greenButton;
QPushButton *blueButton;
public slots:
void redSlot(Widget*);
void greenSlot(Widget*);
void blueSlot(Widget*);
};
How should I make them work?
Connects in QT are string-based, that means:
connect(redButton, SIGNAL(clicked()), this, SLOT(redSlot(OGL)));
will not work, since you havent defined a slot "redSlot(OGL)", instead you would have to use
...SLOT(redSlot(Widget*)));
Moreover, it is possible to define a SLOT with less parameter but not with more, therefore your slot/connect has to look like
void redSlot();
connect(redButton, SIGNAL(clicked()), this, SLOT(redSlot()));
if you need a pointer to the "Widget" you have to retrieve it from somewhere else.
Example: Define slot in "Widget"-class and change connect to:
connect(redButton, SIGNAL(clicked()), OGL, SLOT(redSlot()));
I've created a small QT application that redraws a circle at a random position.
What I would like to do is repeat the method a predetermined number of times that draws the circle every second using a QTimer.
I am not sure how to go about to this.
Here is my main.cpp
int main(int argc, char *argv[]) {
// initialize resources, if needed
// Q_INIT_RESOURCE(resfile);
srand (time(NULL));
QApplication app(argc, argv);
widget f;
f.show();
return app.exec();
}
widget.cpp
#include "widget.h"
widget::widget()
{
widget.setupUi(this);
}
void widget::paintEvent(QPaintEvent * p)
{
QPainter painter(this);
//**code
printcircle(& painter); //paints the circle
//**code
}
void paintcircle(QPainter* painter)
{
srand (time(NULL));
int x = rand() %200 + 1;
int y = rand() %200 + 1;
QRectF myQRect(x,y,30,30);
painter->drawEllipse(myQRect);
}
widget::~widget()
{}
widget.h
#ifndef _WIDGET_H
#define _WIDGET_H
class widget : public QWidget {
Q_OBJECT
public:
widget();
virtual ~widget();
public slots:
void paintEvent(QPaintEvent * p);
private:
Ui::widget widget;
};
#endif /* _WIDGET_H */
How would I go about creating a Qtimer to repeat the printcricle() method.
Thanks
You can create a timer in your widget class constructor as:
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);
I.e. it will call the widget's paint event every second.
Right, there are a couple of things to modify in your code in order to accomplish this:
Forward declare a QTimer
Add a QTimer member
Include the QTimer header.
Set a continuous QTimer in the constructor of your widget class.
Make sure you set the connection to the update slot so that a repaint is scheduled by the event loop.
You need to add a counter for the predetermined times as there is no such feature built into QTimer.
You need to initialise that variable to zero.
You need to increment that in each slot call.
You need to stop emitting the timeout signal for the QTimer.
In order to achieve all that above, your code would become something like this:
widget.cpp
#include "widget.h"
#include <QTimer>
// Could be any number
const static int myPredeterminedTimes = 10;
widget::widget()
: m_timer(new QTimer(this))
, m_count(0)
{
widget.setupUi(this);
connect(m_timer, SIGNAL(timeout()), SLOT(update()));
timer->start(1000);
}
void widget::paintEvent(QPaintEvent * p)
{
QPainter painter(this);
//**code
printcircle(& painter); //paints the circle
//**code
}
void widget::paintcircle(QPainter* painter)
{
srand (time(NULL));
int x = rand() %200 + 1;
int y = rand() %200 + 1;
QRectF myQRect(x,y,30,30);
painter->drawEllipse(myQRect);
}
widget::~widget()
{}
widget.h
#ifndef _WIDGET_H
#define _WIDGET_H
class QTimer;
class widget : public QWidget {
Q_OBJECT
public:
widget();
virtual ~widget();
public slots:
void paintEvent(QPaintEvent * p);
private:
Ui::widget widget;
private:
QTimer *m_timer;
int m_count;
};
#endif /* _WIDGET_H */
Although there are similar questions to mine posted on stackoverflow, none of their solutions actually respond to my problem. I have 2 independent widgets that I would like to combine (insert one widget into the other as a child): one is a UI created only with Qt Creator (drag-and-drop), and the other one an animation done in Qt with OpenGL. I am trying to add the animation in the UI and here is the code:
glwidget.h (animation):
class GLWidget : public QGLWidget
{
public:
GLWidget(QWidget *parent);
~GLWidget();
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
void drawCube(int i, GLfloat z, GLfloat ri, GLfloat jmp, GLfloat amp);
QGLFramebufferObject *fbo;
};
and glwidget.cpp:
GLWidget::GLWidget(QWidget *parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
makeCurrent();
fbo = new QGLFramebufferObject(512, 512);
timerId = startTimer(20);
}
GLWidget::~GLWidget()
{
glDeleteLists(pbufferList, 1);
delete fbo;
}
void GLWidget::initializeGL()
{....
As for the UI, I have the header file:
class ClaraTeCourseSimulator : public QMainWindow
{
Q_OBJECT
public:
explicit ClaraTeCourseSimulator(QWidget *parent = 0);
~ClaraTeCourseSimulator();
private:
Ui::ClaraTeCourseSimulator *ui;
GLWidget *defaultAnim;
protected:
void setupActions();
protected slots:
void addAnimWidget();
};
and the .cpp file:
ClaraTeCourseSimulator::ClaraTeCourseSimulator(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ClaraTeCourseSimulator)
{
ui->setupUi(this);
defaultAnim = new GLWidget(ui->centralWidget);
}
void ClaraTeCourseSimulator::setupActions()
{
connect(ui->actionCar_Modelling, SIGNAL(triggered(bool)), ui->centralWidget,
SLOT(addAnimWidget()));
}
void ClaraTeCourseSimulator::addAnimWidget()
{
ui->centralWidget->layout()->addWidget(defaultAnim);
}
ClaraTeCourseSimulator::~ClaraTeCourseSimulator()
{
delete ui;
}
But when I try to run it I get about 24 of these errors: undefined reference to `imp_ZN9QGLFormatD1Ev all pointing to the constructor and destructor in glwidget.cpp.
What am I doing wrong? How can I fix this problem?
Are you trying to change the central widget to the GL one? Because specifying the parent for the GL widget does not change them. If you'd like to change a widget to another (using the Designer), I recommend the "promote to" feature, with which you can change a widget's actual class in the designer. So add a QWidget on the UI, and than promote it to your class (GLWidget).
It seems like the problem is with the GLFormat constructor call in the GLWidget constructor.