# Qt tools
# QPainter
To use painter create a virtual void paintEvent(QPaintEvent *event);
on the header file of the widget or mainwindow.
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPainter>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
virtual void paintEvent(QPaintEvent *event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Under the widget.cpp file include the implementation of the void Widget::paintEvent(QPaintEvent *event)
.
#include "widget.h"
#include "./ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// painter.setBrush(Qt::BDiagPattern);
// painter.setBrush(Qt::SolidPattern);
painter.setBrush(Qt::Dense7Pattern);
QPen pen;
pen.setColor(Qt::green);
pen.setWidth(5);
painter.setPen(pen);
painter.drawRect(QRect(80, 120, 200, 100));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# QWidget::mousePressEvent(), QWidget::mouseReleaseEvent() and QWidget::mouseMoveEvent()
To react to mouse events like press, release and move we can use the QWidget class.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPoint>
#include <QMouseEvent>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
signals:
void mousePressed(const QPoint&);
void mouseReleased(const QPoint&);
void mouseMoved(const QPoint&);
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
const QPoint p = event->pos();
emit mousePressed(p);
QString x = QString::number(event->x());
QString y = QString::number(event->y());
qDebug() << x << "," << y;
ui->label_x1->setText(x);
ui->label_y1->setText(y);
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
const QPoint p = event->pos();
emit mouseReleased(p);
QString x = QString::number(event->x());
QString y = QString::number(event->y());
qDebug() << x << "," << y;
ui->label_x2->setText(x);
ui->label_y2->setText(y);
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
const QPoint p = event->pos();
emit mouseMoved(p);
QString x = QString::number(event->x());
QString y = QString::number(event->y());
qDebug() << x << "," << y;
ui->label_xc->setText(x);
ui->label_yc->setText(y);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# QtQuick and QML
# OpenGL
First include the openGLWidget on the ui.
Adjust the Cmake file:
cmake_minimum_required(VERSION 3.5)
project(OpenGlLines LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.
#if(ANDROID)
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
# if (ANDROID_ABI STREQUAL "armeabi-v7a")
# set(ANDROID_EXTRA_LIBS
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
# endif()
#endif()
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
find_package(Qt5OpenGL)
find_package(OpenGL)
if(ANDROID)
add_library(OpenGlLines SHARED
main.cpp
widget.cpp
widget.h
widget.ui
myglwidget.cpp
myglwidget.h
)
else()
add_executable(OpenGlLines
main.cpp
widget.cpp
widget.h
widget.ui
myglwidget.cpp
myglwidget.h
)
endif()
target_link_libraries(OpenGlLines PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${OPENGL_LIBRARIES})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Then create a class QOpenGLWidget with the name you want.
On the header file include:
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QWidget>
class MyGLWidget : public QOpenGLWidget
{
Q_OBJECT
public:
MyGLWidget(QWidget *parent=nullptr);
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
and on the cpp file:
#include "myglwidget.h"
MyGLWidget::MyGLWidget(QWidget* parent):
QOpenGLWidget(parent)
{
}
2
3
4
5
6
7
Then add QOpenGLFunctions
to the header file and public QOpenGLFunctions
:
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
Q_OBJECT
public:
MyGLWidget(QWidget *parent=nullptr);
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Now add the void initializeGL() override;
and void paintGL() override;
in a protected:
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
Q_OBJECT
public:
MyGLWidget(QWidget *parent=nullptr);
protected:
// These are virtual functions
void initializeGL() override;
void paintGL() override;
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Add the functions to the cpp and:
#include "myglwidget.h"
MyGLWidget::MyGLWidget(QWidget* parent):
QOpenGLWidget(parent)
{
}
void MyGLWidget::initializeGL()
{
// This is compulsory
this->initializeOpenGLFunctions();
}
void MyGLWidget::paintGL()
{
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Then add this:
#include "myglwidget.h"
MyGLWidget::MyGLWidget(QWidget* parent):
QOpenGLWidget(parent)
{
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
}
void MyGLWidget::initializeGL()
{
// This is compulsory
this->initializeOpenGLFunctions();
}
void MyGLWidget::paintGL()
{
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
and include the QSurfaceFormat in the header file:
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
#include <QSurfaceFormat>
class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
Q_OBJECT
public:
MyGLWidget(QWidget *parent=nullptr);
protected:
// These are virtual functions
void initializeGL() override;
void paintGL() override;
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Now we Promote the OpenGLWidget to the class we created.
# Scaling Factors and Translation Factors
# For 2D Graphics
In the OpenGL view port the range goes from -1 to +1 in the and axis.
Generate points for the Graph
Find ranges of x and y
Find two scaling factors and two translation factors
are coordinates on the opengl Viewport, are scaling factors and are the translation factors.
The question is how to find the factors. For this we use the following equation:
Let
and
then we have:
, substituting
, subtracting we have
,
, so
For we have:
For we have:
For we have:
For we have:
So with the translation and scaling factors we can compute the points on the OpenGL ViewPort using:
TIP
Find the math explanation here
# Creating 2D Graphics
Now, from the previous example we have the following, with some changes:
cmake_minimum_required(VERSION 3.5)
project(OpenGlLines LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.
#if(ANDROID)
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
# if (ANDROID_ABI STREQUAL "armeabi-v7a")
# set(ANDROID_EXTRA_LIBS
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
# endif()
#endif()
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
find_package(Qt5OpenGL)
find_package(OpenGL)
if(ANDROID)
add_library(OpenGlLines SHARED
main.cpp
widget.cpp
widget.h
widget.ui
myglwidget.cpp
myglwidget.h
)
else()
add_executable(OpenGlLines
main.cpp
widget.cpp
widget.h
widget.ui
myglwidget.cpp
myglwidget.h
)
endif()
target_link_libraries(OpenGlLines PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${OPENGL_LIBRARIES})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Header
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
Q_OBJECT
public:
MyGLWidget(QWidget *parent=nullptr);
protected:
// These are virtual functions
void initializeGL() override;
void paintGL() override;
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Cpp
#include "myglwidget.h"
MyGLWidget::MyGLWidget(QWidget* parent):
QOpenGLWidget(parent)
{
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
}
void MyGLWidget::initializeGL()
{
// This is compulsory
this->initializeOpenGLFunctions();
}
void MyGLWidget::paintGL()
{
// We will clear the background with color white
// 1.0f for alpha channel means opaque opaque or NOT transparent"
//(R,G,B, Alpha)
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
With this we have a blank canvas:
Now Let's draw some Primitives:
Let's start with the GL_LINE_LOOP
:
void MyGLWidget::paintGL()
{
// We will clear the background with color white
// 1.0f for alpha channel means opaque opaque or NOT transparent"
//(R,G,B, Alpha)
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glLineWidth(4);
glColor3f(0.0f, 0.0f, 0.0f);
glBegin(GL_LINE_LOOP);
float _1 = 1.0f;
glVertex2f(-_1, _1); // left top vertex
glVertex2f( _1, _1); // left top vertex
glVertex2f( _1, -_1); // left top vertex
glVertex2f(-_1, -_1); // left top vertex
glEnd();
glFlush();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
With this code we draw a black rectangle
Now, Lets draw a quadratic function:
First we make a struct
for the point x y:
For this we include #include <vector>
at the header file and #include <numeric>
.
struct PointXY
{
float x, y;
};
2
3
4
and we define the type of point_vector_t
using point_vector_t = std::vector<PointXY>;
Then on STEP 0 we Generate the points
// STEP 0 - Generate Points
// Quadratic function
auto func = [](auto x)
{
return (x -1) * (x - 3);
};
int count = 200;
float d = (5.0f - 0.0f) / count;
point_vector_t points;
for (float x = 0; x <= 5.0f; x += d)
{
points.emplace_back(PointXY{x, func(x)});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
emplace_back
emplace_back reference.
Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container. The arguments args... are forwarded to the constructor as std::forward<Args>(args)
....
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
We also make a struct for the range and the scaling and translation factors:
struct Range
{
float x_min, x_max;
float y_min, y_max;
};
struct ScalingTranslation
{
float Sx, Tx;
float Sy, Ty;
};
2
3
4
5
6
7
8
9
10
11
And in STEP 1 we find the ranges of X and Y:
// ovp = OpenGL view port
// grp = graph
// Here the range receives {x_min, x_max, y_min, y_max}
// OpenGL Viewport Range
Range ovp{-1.0f, 1.0f, -1.0f, 1.0f,};
//graph's x_min, x_max, y_min, y_max
2
3
4
5
6
7
8
now we find the graph's x_min, x_max, y_min, y_max:
//graph's x_min, x_max, y_min, y_max
Range grp
{
std::numeric_limits<float>::max(), // for x_min initial value
std::numeric_limits<float>::min(), // for x_max initial value
std::numeric_limits<float>::max(), // for y_min initial value
std::numeric_limits<float>::max(), // for y_max initial value
};
for (auto pt : points)
{
if (grp.x_min > pt.x) grp.x_min = pt.x;
if (grp.x_max < pt.x) grp.x_max = pt.x;
if (grp.y_min > pt.y) grp.y_min = pt.y;
if (grp.y_max < pt.y) grp.y_max = pt.y;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Now we find the OpenGL viewport x_min, x_max, y_min and y_max and also the graph's x_min, x_max, y_min and y_max.
And we can compute the scaling and translation factors like this:
// STEP 2 - Find two scaling and translation factors
//Scaling and translation factors
ScalingTranslation stf;
stf.Sx = (ovp.x_max - ovp.x_min) / (grp.x_max - grp.x_min);
stf.Tx = ovp.x_min - stf.Sx * grp.x_min;
stf.Sy = (ovp.y_max - ovp.y_min) / (grp.y_max - grp.y_min);
stf.Ty = ovp.y_min - stf.Sy * grp.y_min;
2
3
4
5
6
7
8
9
10
11
Now we create the GL_LINE_STRIP
// STEP 3 - Draw the points
glBegin(GL_LINE_STRIP);
for (auto pt : points)
{
auto xo = stf.Sx * pt.x + stf.Tx;
auto yo = stf.Sy * pt.y + stf.Ty;
glVertex2f(xo, yo);
}
glEnd();
glFlush();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Now the while program working will be:
Header
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
#include <QSurfaceFormat>
#include <QDebug>
#include <vector>
#include <numeric>
class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
Q_OBJECT
public:
MyGLWidget(QWidget *parent=nullptr);
protected:
// These are virtual functions
void initializeGL() override;
void paintGL() override;
};
#endif // MYGLWIDGET_H
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
myglwidget.cpp
#include "myglwidget.h"
MyGLWidget::MyGLWidget(QWidget* parent):
QOpenGLWidget(parent)
{
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
}
void MyGLWidget::initializeGL()
{
// This is compulsory
this->initializeOpenGLFunctions();
}
struct PointXY
{
float x, y;
};
// Here we define the type of point_vector_t
using point_vector_t = std::vector<PointXY>;
struct Range
{
float x_min, x_max;
float y_min, y_max;
};
struct ScalingTranslation
{
float Sx, Tx;
float Sy, Ty;
};
void MyGLWidget::paintGL()
{
// We will clear the background with color white
// 1.0f for alpha channel means opaque opaque or NOT transparent"
//(R,G,B, Alpha)
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glLineWidth(4);
glColor3f(0.0f, 0.0f, 0.0f);
glBegin(GL_LINE_LOOP);
float _1 = 1.0f;
glVertex2f(-_1, _1); // left top vertex
glVertex2f( _1, _1); // left top vertex
glVertex2f( _1, -_1); // left top vertex
glVertex2f(-_1, -_1); // left top vertex
glEnd();
// STEP 0 - Generate Points
// Quadratic function
auto func = [](auto x)
{
return (x - 1) * (x - 3);
};
int count = 200;
float d = (5.0f - 0.0f) / count;
point_vector_t points;
for (float x = 0; x <= 5.0f; x += d)
{
points.emplace_back(PointXY{ x, func(x) });
}
// STEP 1 - Find ranges of X and Y
// ovp = OpenGL view port
// grp = graph
// Here the range receives {x_min, x_max, y_min, y_max}
// OpenGL Viewport Range
Range ovp{-1.0f, 1.0f, -1.0f, 1.0f};
//graph's x_min, x_max, y_min, y_max
Range grp
{
std::numeric_limits<float>::max(), // for x_min initial value
std::numeric_limits<float>::min(), // for x_max initial value
std::numeric_limits<float>::max(), // for y_min initial value
std::numeric_limits<float>::min() // for y_max initial value
};
for (auto pt : points)
{
if (grp.x_min > pt.x) grp.x_min = pt.x;
if (grp.x_max < pt.x) grp.x_max = pt.x;
if (grp.y_min > pt.y) grp.y_min = pt.y;
if (grp.y_max < pt.y) grp.y_max = pt.y;
}
// ovp.x_min, ovp.x_max, ovp.y_min, ovp.y_max
// grp.x_min, grp.x_max, grp.y_min, grp.y_max
// STEP 2 - Find two scaling and translation factors
//Scaling and translation factors
ScalingTranslation stf;
stf.Sx = (ovp.x_max - ovp.x_min) / (grp.x_max - grp.x_min);
stf.Tx = ovp.x_min - stf.Sx * grp.x_min;
stf.Sy = (ovp.y_max - ovp.y_min) / (grp.y_max - grp.y_min);
stf.Ty = ovp.y_min - stf.Sy * grp.y_min;
// STEP 3 - Draw the points
glBegin(GL_LINE_STRIP);
for (auto pt : points)
{
auto xo = stf.Sx * pt.x + stf.Tx;
auto yo = stf.Sy * pt.y + stf.Ty;
glVertex2f(xo, yo);
}
glEnd();
glFlush();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
and the result:
If you know how to draw a 2D graph on the computer monitor mathematically, you can easily understand how to draw a 3D graph on the computer monitor mathematically.
Once you understand the mathematical logic behind the computer graphics, you can easily understand DirectX3D or OpenGL 3D Library
# For 3D Graphics
For 3D Graphics we will need the Projection factor, not just the translation and scaling.