Skip to content

Thermadiag/qthreadopenglwidget

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Purpose

qthreadopenglwidget is a small library providing opengl related features for Qt Widgets.

The main class QThreadOpenGLWidget is a QWidget providing opengl rendering much like QOpenGLWidget, but using a dedicated rendering thread to offload the GUI thread.

Indeed, using a dedicated rendering thread with QOpenGLWidget, while probably possible (according to the documentation), is a real pain. I wasn't able to produce a reliable working class with it, which is why I rolled my own class. QThreadOpenGLWidget provides a similar API to QOpenGLWidget with the virtual members initializeGL(), resizeGL() and paintGL(). It is possible to override its paintEvent() to perform QPainter based drawing. QThreadOpenGLWidget uses a the following approach:

  • Using a QPainter on a QThreadOpenGLWidget will use its own custom QPaintEngine.
  • The paint engine serializes drawing commands in a structure similar to QPicture (see QPaintRecord class).
  • Drawing commands are periodically sent to a rendering thread.
  • The rendering thread apply the drawing commands in an internal QWindow using QOpenGLPaintDevice. If the opengl thread is slower than the time spent in recording drawing commands, it will discard older commands and only paint the last ones.

This greatly reduces the time spent in QWidget::paintEvent(), allow higher frame rates and increase the GUI responsiveness.

QThreadOpenGLWidget can be used as the viewport of a QGraphicsView, as the class was primary made for this. Indeed, from my experience, using a QOpenGLWidget as viewport does not provide much benefits: the CPU load is similar and the painting takes the same amount of time as with the raster paint engine (at least most of the times). The library provides additional classes to help working with large scenes:

  • QOpenGLItem: by inheriting this class, a QGraphicsItem can use a caching mechanism based on QPaintRecord. This caching mechanism is only enabled when using a QThreadOpenGLWidget viewport, and reduces the time spent in QGraphicsItem::paint() method. See the class documentation for more details.
  • QOpenGLGraphicsItem: convenient QGraphicsItem inheriting QOpenGLItem.
  • QOpenGLGraphicsObject: convenient QGraphicsObject inheriting QOpenGLItem.
  • QOpenGLGraphicsWidget: convenient QGraphicsWidget inheriting QOpenGLItem.

Usage

The following example shows how to use QThreadOpenGLWidget to display several static or dynamic ellipses using all possible ways:

 #include <QThreadOpenGLWidget.h>
#include <QTimer>
#include <QApplication>
#include <QSurfaceFormat>
#include <QDateTime>
 
#include <cmath>
 
 // Example of QThreadOpenGLWidget that displays static and dynamic ellipses
 // by combining all possible types of drawing mechanisms:
 //  - Overload of QThreadOpenGLWidget::paintGL()
 //  - Calling drawFunction() from within paintEvent()
 //  - Using a regular QPainter from within paintEvent()
 //
 class MyOpenGLWidget : public QThreadOpenGLWidget
 {
 	static void DrawOpenGLEllipse(const QRectF& r, int num_segments)
 	{
 		// Draw an ellipse using old school opengl, just for the use case

 		float cx = r.center().x();
 		float cy = r.center().y();
 		float rx = r.width() / 2;
 		float ry = r.height() / 2;

 		float theta = 2 * 3.1415926 / float(num_segments);
 		float c = std::cos(theta);//precalculate the sine and cosine
 		float s = std::sin(theta);
 		float t;
 		float x = 1;//we start at angle = 0
 		float y = 0;
 		glEnable(GL_MULTISAMPLE);
 		glEnable(GL_COLOR_MATERIAL);
 		glColor3f(0, 1, 0);
 		glBegin(GL_LINE_LOOP);
 		for (int ii = 0; ii < num_segments; ii++)
 		{
 			//apply radius and offset
 			glVertex2f(x * rx + cx, y * ry + cy);//output vertex

 			//apply the rotation matrix
 			t = x;
 			x = c * x - s * y;
 			y = s * t + c * y;
 		}
 		glEnd();
 	}

 public:
 	QTimer timer;

 	MyOpenGLWidget()
 		:QThreadOpenGLWidget()
 	{
 		timer.setSingleShot(false);
 		timer.setInterval(10);
 		connect(&timer, SIGNAL(timeout()), this, SLOT(update()));
 		timer.start();
 	}

 	virtual void paintGL()
 	{
 		// One way to display an ellipse: override paintGL()
 		DrawOpenGLEllipse(QRectF(0, 0, 1, 1), 100);
 	}

 	virtual void paintEvent(QPaintEvent* evt)
 	{
 		qint64 time = QDateTime::currentMSecsSinceEpoch();
 		int size = time % 500;

 		// Another (faster) way to display an ellipse:
 		// send a drawing function to the rendering thread
 		// that uses QPainter calls
 		drawFunction([size](QPainter* p)
 			{
 				p->setPen(Qt::red);
 				p->setRenderHint(QPainter::Antialiasing);
 				p->drawEllipse(QRectF(10, 10, size, size));
 			});

 		// Another (faster) way to display an ellipse:
 		// send a drawing function using raw opengl calls
 		drawFunction([size](QPainter* p)
 			{
 				p->beginNativePainting();
 				MyOpenGLWidget::DrawOpenGLEllipse(QRectF(10 / 500., 10 / 500., size / 500., size / 500.), 500);
 				p->endNativePainting();

 			});

 		// Yet another way to draw an ellipse:
 		// the regular QWidget way
 		QPainter p(this);
 		p.setPen(Qt::blue);
 		p.setRenderHint(QPainter::Antialiasing);
 		p.drawEllipse(QRectF(10, 10, 50, 50));
 	}

 };
 
 
 
 
int main(int argc, char *argv[])
{
    QSurfaceFormat format;
    format.setSamples(4);
    format.setSwapInterval(0);
    QSurfaceFormat::setDefaultFormat(format);

    QApplication app(argc, argv);
    app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);

    MyOpenGLWidget window;
    window.show();

    return app.exec();
}

Build

The qthreadopenglwidget library requires compilation using cmake, but you can just copy/paste the source files in your project.

Using qthreadopenglwidget library with CMake

When importing qthreadopenglwidget from cmake, the following variables are defined:

  • QTOW_INCLUDE_DIR: library include directory
  • QTOW_LIB_DIR: library directory
  • QTOW_QT_LIBS: Qt libraries used by the library

The following cmake file shows how to use qthreadopenglwidget library from your project (taken from the chip example):

cmake_minimum_required(VERSION 3.16)
project(chip VERSION 1.0 LANGUAGES C CXX)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

# find qthreadopenglwidget
find_package(qthreadopenglwidget REQUIRED)

# create executable
add_executable(chip main.cpp view.h view.cpp chip.h chip.cpp mainwindow.h mainwindow.cpp)

# add ressources
qt_add_resources(chip "images"
    PREFIX ""
    FILES qt4logo.png zoomin.png zoomout.png rotateleft.png rotateright.png fileprint.png)

# link to qthreadopenglwidget as well as required Qt libraries
target_link_libraries(chip PRIVATE ${QTOW_QT_LIBS} qthreadopenglwidget)

qthreadopenglwidget library and this page Copyright (c) 2025, Victor Moncada

About

Threaded Qt opengl widget

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published