<<

graphical interfaces in

Eric Lecolinet - Télécom ParisTech www.telecom-parist.fr/~elc 2020

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif

Filière IGR and IGD Master

http://www.telecom-paris.fr/~elc/igr/

http://www.telecom-paristech.fr/~elc/

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif http://www.telecom-paris.fr/~elc/igr 3 Filière IGR (2a + 3a)

comporte des informations sur la 3e année et les séjours http://www.telecom-paristech.fr/~elc/igr/ à l'étranger !

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 4 IGR201b: Interactive 2D/Mobile/Web development

IGR201a: Interactive 3D application development - Basic digital imaging - OpenGL

IGR201b - Qt GUI toolkit - Android - Web interface

http://www.telecom-paristech.fr/~elc/igr201/

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 5 Qt ("cute")

A versatile environnement including - A powerful cross-platform graphical toolkit (including iOS and Android) - A large set of libraries - Used in various open-source or commercial products

Dual-licensed - LGPL licence (free): open-, student/academic/hobby projects, etc. - Commercial licence: for companies

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 6 Qt WebEngine WidgetsThe Qt WebEngine Widgets module provides a web browser engine as well as ++ classes to render and interact with web content. Qt WebKit Widgets The Qt WebKit Widgets module provides a web browser engine as well as C++ classes to render and interact with web content. Qt3DCore Qt3D Core module contains functionality to support near-realtime simulation systems Qt3DInput Qt3D Input module provides classes for handling user input in applications using Qt3D Qt3DRenderer Qt3D Renderer module contains functionality to support 2D and 3D rendering using Qt3D QtBluetooth Enables Bluetooth operations like scanning for devices and connecting them QtConcurrent Qt Concurrent module contains functionality to support concurrent execution of program code QtCore Provides core non-GUI functionality QtDBus Qt D-Bus module is a Unix-only library that you can use to perform Inter-Process Communication using the D-Bus protocol QtDesigner Provides classes to create your own custom widget plugins for Qt Designer and classes to access Qt Designer components QtGui Qt GUI module provides the basic enablers for graphical applications written with Qt QtHelp Provides classes for integrating online documentation in applications QtLocation Provides C++ interfaces to retrieve location and navigational information QtMultimedia Qt Multimedia module provides audio, video, radio and camera functionality QtNetwork Provides classes to make network programming easier and portable QtNfc An API for accessing NFC Forum Tags QtOpenGL Qt OpenGL module offers classes that make it easy to use OpenGL in Qt applications QtPositioning Positioning module provides positioning information via QML and C++ interfaces QtPrintSupport Qt PrintSupport module provides classes to make printing easier and portable QtQml C++ API provided by the Qt QML module QtQuick module provides classes for embedding Qt Quick in Qt/C++ applications QtQuickWidgets C++ API provided by the Qt Quick Widgets module QtScript Qt Script module provides classes for making Qt applications scriptable QtScriptTools Provides additional components for applications that use Qt Script QtSensors Provides classes for reading sensor data QtSerialPort List of C++ classes that enable access to a serial port QtSql Provides a driver layer, SQL API layer, and a user interface layer for SQL databases QtSvg Qt SVG module provides functionality for handling SVG images QtTest Provides classes for Qt applications and libraries QtUiTools Provides classes to handle forms created with Qt Designer QtWebChannel List of C++ classes that provide the Qt WebChannel functionality QtWebSockets List of C++ classes that enable WebSocket-based communication QtWidgets Qt Widgets module extends Qt GUI with C++ widget functionality QtXml Qt XML module provides C++ implementations of the SAX and DOM standards for XML QtXmlPatterns Qt XML Patterns module provides support for XPath, XQuery, XSLT and XML Schema validat Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 7 Tools & computer languages

Tools - IDE: QtCreator (QtDesigner) - Internationalization: Qt Linguist - Documentation: Qt Assistant - , etc.

Computer languages - Natively written in C++

- Various bindings, especially PyQt (Python)

- Qt Quick: based on the QML declarative langage (similar to CSS & JSON)

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 8 Useful Links

- Télécom Labs: http://www.telecom-paristech.fr/~elc/qt

- Qt Web site: http://www.qt.io/

- GUI Toolkit: • Main page: http://doc.qt.io/qt-5/ • All modules: http://doc.qt.io/qt-5/modules-cpp.html

- Qt Widgets: • Documentation : http://doc.qt.io/qt-5/qtwidgets-index.html

• List of classes: http://doc.qt.io/qt-5/qtwidgets-module.html

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 9 Main widgets

Inheritance graph

QObject: root class

QWidget: base class of all graphical objects

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 10 Interactors

QLabel

QPushButton

QSlider

QLineEdit

QTextEdit

etc.

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 11 Containers

A QWidget can contain other widgets QToolbox

There as also special purpose containers

QFrame

QStackedWidget QGroupBox

QTabWidget

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 12 Main window and dialog boxes

QFileDialog

QMainWidget

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif QMessageBox 13 Note: C++ vs

MyClass.java MyClass.h class MyClass { class MyClass { public void whatever(int i) { public: Zombat z = new Zombat(i); void whatever(int i); z.foo(); } ; } }

MyClass.cpp Some Differences in C++ void MyClass::whatever(int i) { Zombat * z = new Zombat(); • header + implementation files (not mandatory) z->foo(); } • final ; for classes • don't forget the * • -> instead of . for accessing members

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 14 Note: C++ vs Java

MyClass2.cpp MyClass.h void MyClass::whatever(int i) { class MyClass { Zombat z = new Zombat(); public: z.foo(); void whatever(int i); } };

Correct? MyClass.cpp MyClass3.cpp void MyClass::whatever(int i) { void MyClass::whatever(int i) { Zombat * z = new Zombat(); Zombat z = Zombat(); z->foo(); z.foo(); } }

Correct?

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 15 Note: C++ vs Java

MyClass2.cpp MyClass.h void MyClass::whatever(int i) { class MyClass { Zombat z = new Zombat(); public: z.foo(); void whatever(int i); }; };

NOT Correct! MyClass.cpp MyClass3.cpp void MyClass::whatever(int i) { void MyClass::whatever(int i) { Zombat * z = new Zombat(); Zombat z = Zombat(); z->foo(); z.foo(); } };

Means something different!

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 16 Hello Word!

#include #include int main( int argc, char **argv ) { QApplication * app = new QApplication(argc, argv); QLabel * hello = new QLabel("Hello Qt!"); hello->show( ); return app->exec( ); }

• show() : the widget is displayed • hello becomes the top-level widget because it has no parent (see later) • exec() : starts the event loop

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 17 Event loop

Calls callback methods (slots)

Event Updates widgets

Event loop User

The event loop • Takes control : exec() blocks the program while (true) { event = getNextEvent() • Indefinitely: processEvent(event); • waits for the next event } • processes the event basic event loop

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 18 Instance graph

A QWidget can contain QWidgets that contain QWidgets...

= instance graph (more precisely it's a tree)

parenthood means:

• superimposition: children on top of parents

• clipping : children parts outside parent area not visible

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 19 Instance graph

#include // include corresponding headers #include #include box #include

int main( int argc, char **argv ) { auto * app = new QApplication(argc, argv);

auto * box = new QWidget(); quitBtn box->resize(200, 120);

auto * quitBtn = new QPushButton("Quit", box); // box is the parent of quitBtn quitBtn->resize(100, 50); quitBtn->move(50, 35); quitBtn->setFont( QFont("Times", 18, QFont::Bold) );

box->show( ); The parent is given as an argument of the constructor return app->exec( ); } Except for "top-level" widgets (no parent)

Note auto keyword Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 20

#include #include #include #include

int main( int argc, char **argv ) { auto * app = new QApplication(argc, argv);

auto * box = new QWidget(); box->resize(200, 120);

auto * quitBtn = new QPushButton("Quit", box); // etc....

QObject::connect( quitBtn, SIGNAL(clicked( )), app, SLOT(quit( )) ); // connects a signal to a slot

box->show( ); return app->exec( ); }

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 21 Signals and slots

A signal is emitted by an object – e.g. when clicking a Push Button

A slot is a method – that can be connected to a signal

When a signal is emitted, the connected slots are executed

– A signal can be connected to several slots – Several signals can be connected to a slot

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 22 Signals and slots

#include #include #include #include

int main( int argc, char **argv ) { auto * app = new QApplication(argc, argv);

auto * box = new QWidget(); box->resize(200, 120);

auto * quitBtn = new QPushButton("Quit", box); // etc....

QObject::connect( quitBtn, SIGNAL(clicked( )), app, SLOT(quit( )) );

box->show( ); return app->exec( ); }

standard signal slot (method) of QPushButton of QApplication

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 23 Signals and slots

Key aspect of Qt - differs from callback functions or Java listeners - require a pre-compilation step

Signals and slots can have arguments - which must correspond (same order, same types) - previous example: connect( quitBtn, SIGNAL(clicked( )), app, SLOT(quit( )) );

- here stateChanged() holds an int that is received by mySlot(): connect( checkBox, SIGNAL(stateChanged(int)), y, SLOT(mySlot(int)) );

- OK because a slot can have less arguments than a signal: connect( checkBox, SIGNAL(stateChanged(int)), app, SLOT(quit()) );

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 24 Declaring a slot

In the header file (.h)

• MyClass must derive from QObject class MyClass : public QWidget { • or any widget class Q_OBJECT public: • keywords Q_OBJECT and slots MyClass( );

• needed by the moc pre-compiler public slots: void mySlot( int value ); • slots must be implemented }; • usually, in the .cpp file

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 25 Alternate syntax

QObject::connect(quitBtn, &QPushButton::clicked, app, &QApplication::quit);

- relies on C++ method pointers

- benefits : the C++ compiler checks there is no error

- drawbacks : less powerful, not compatible with QtQuick

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 26 Using lambdas

void MyClass::foo() { int val = 10; QObject::connect(quitBtn, &QPushButton::clicked, [=] {myObj->setBalance (val);} ); }

[=] {myObj->setBalance(value);} is a lambda

[=] means that the lambda captures this and the value of local variables

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 27 Using lambdas

void MyClass::foo() { int val = 10; QObject::connect(quitBtn, &QPushButton::clicked, [=] {myObj->setBalance (val);} ); }

[=] {myObj->setBalance(value);} is a lambda

[=] means that the lambda captures this and the value of local variables

[&] means it captures variables by reference => variables' values can be changed in the lambda... => as they are not copied they must still exist (program crash otherwise!)

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 28 Misc.

Disconnection

disconnect(x, SIGNAL(balanceChanged(int)), y, SLOT(setBalance(int)) );

Propagates the signal

connect(x, SIGNAL(balanceChanged(int)), controller, SIGNAL(changed(int)) );

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 29 Signals and slots compilation

Meta Object Compiler (MOC) - pre-processor - generates additional code (signal / slots table) - caution: do not forget the Q_OBJECT keyword

Use QtCreator or the qmake command - in the directory containing the source files:

• qmake -project // creates xxx.pro • qmake // creates the Makefile (or equivalent) • make // creates .moc and binary files

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 30 Main Window (QMainWindow)

Predefined areas for: - a menubar - a toolbar - a statusbar - a central area (working area)

Principle - Create a subclass QMainWindow

- Its constructor must create the widgets

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 31 Main Window

In the constructor of a class deriving from de QMainWindow

QMenuBar * menubar = this->menuBar( ); // adds/returns the menu bar QMenu * menu = menubar->addMenu( tr("&File") ); // adds a pulldown menu

// new.png is specified in a resource file (see later) QAction * action = new QAction( QIcon(":/new.png"), tr("&New..."), this ); menu->addAction(action); // adds the action to the menu connect(action, SIGNAL(triggered( )), this, SLOT(open( ))); // connection

action->setShortcut( tr("Ctrl+N") ); // hot key action->setToolTip( tr("New file") ); // tool tip action->setStatusTip( tr("New file") ); // info on status bar

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 32 Main Window

Actions – can both be added to menus and toolbars

QToolBar * toolbar = this->addToolBar( tr("File") ); toolbar->addAction(action);

Central area

QTextEdit * text = new QTextEdit(this); this->setCentralWidget(text);

Internationalization

– tr( ) translates the text

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 33 Dialog boxes

QMessageBox

QFileDialog

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 34 Modal dialog box

QString fileName = QFileDialog::getOpenFileName(this, tr("Open Image"), // title "/home/jana", // directory tr("Image Files (*.png *.jpg *.bmp)") // filter );

A modal dialog box blocks the interaction: => it forces the user to answer • Advantage: easy to program! • Drawback: not always very convenient for the user

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 35 Modal dialog box

More generally

QFileDialog dialog = new QFileDialog(parent); dialog->setFilter("Text files (*.txt)"); QStringList fileNames;

if (dialog->exec( ) == QDialog::Accepted) { name1 = dialog->selectedFiles(); QString name1 = fileNames[0]; ... }

dialog.exec (or getOpenFileName) start a secondary event loop

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 36 Internationalized text

Problem - so many languages, so many symbols! - solution: UNICODE

QString: Unicode strings • made of QChars (16 bits each) • one symbol = 1 QChar (usual case) or 2 QChar (more exotic symbols, e.g. Emojis)

To convert a QString • qPrintable(string) : converts to local encoding • toAscii( ) : ASCII 8 bits • toUtf8( ) : UTF-8 Unicode multibyte (1 char = 1 à 4 octets) • toLocal8Bit( ) : local encoding 8 bits • etc.

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 37 Internationalized text

QFile: for opening files

QFile file( fileName ); if ( file.open( QIODevice::ReadOnly | QIODevice::Text) ) { .... } if ( file.open( QIODevice::WriteOnly ) ) { ... }

QTextStream: for reading or writing text from a QFile

QFile file("output.txt"); if (file.open( QIODevice::WriteOnly )) { QTextStream out( &file ); out << "Result: " << 10 << " " << 3.14; // writes on out }

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 38 Debugging qDebug: for displaying debugging messages

#include ......

int val; QString s; qDebug( ) << "value: " << s << " / " << val;

// or, using standard C++ IO std::cout << "value: " << qPrintable(s) << " / " << val << std::endl;

don't forget endl !

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 39 Reading text

• To read a word : stream >> arg1 >> arg2; // stops reading before a space

• To read a line : stream.readLine( size ); // returns the line, no size limit if size = 0

• To read the whole text file : stream.readAll( ); // only for small files!

• codecs : • setCodec(codec), setAutoDetectUnicode(bool);

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 40 Ressources: internationalization

QAction * action = new QAction( tr("&New..."), this );

• tr() sert à translates the text if there is a dictionary in the resource files

• see QLinguist

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 41 Ressources : Icons

QAction * action = new QAction( QIcon(":/images/new.png"), tr("&New..."), this );

: filename that is relative to the application here new.png is in sub-directory images

Caution!

The Prefix in QtCreator must be empty or /

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 42 Ressource file

.qrc file - created by hand or by QtCreator images/copy.png images/cut.png - images are compiled images/new.png and included in the binary images/open.png images/paste.png images/save.png

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 43 Styles & Style Sheets

QStyle: Emulates the native "look and feel" - Look and feel is simulated and can be changed (as with Swing)

QStyleSheet: for customizing the "look" - Using CSS-like files

myTextEdit->setStyleSheet("color: blue;" "background-color: yellow;" "selection-color: yellow;" "selection-background-color: blue;");

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 44 Layout

Goals • Text size depends on language => internationalization • Make it possible to resize interactively properly • Avoid making complex calculations

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 45 Layout

QHBoxLayout, QVBoxLayout, QGridLayout

QFormLayout

QMainWindow Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 46 Layout example

QVBoxLayout * v_layout = new QVBoxLayout( ); v_layout->addWidget( new QPushButton( "OK" ) ); v_layout->addWidget( new QPushButton( "Cancel" ) ); v_layout->addStretch( ); v_layout->addWidget( new QPushButton( "Help" ) );

Layout objects

- can be nested - are independent from the widget tree (contrary to Java Swing)

- note addStretch

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 47 Layout example

QVBoxLayout * v_layout = new QVBoxLayout( ); v_layout->addWidget( new QPushButton( "OK" ) ); v_layout->addWidget( new QPushButton( "Cancel" ) ); v_layout->addStretch( ); v_layout->addWidget( new QPushButton( "Help" ) );

QListBox * country_list = new QListBox( this ); countryList->insertItem( "Canada" ); ...etc...

QHBoxLayout * h_layout = new QHBoxLayout( ); h_layout->addWidget( country_list ); h_layout->addLayout( v_layout );

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 48 Layout example

QVBoxLayout * v_layout = new QVBoxLayout( ); v_layout->addWidget( new QPushButton( "OK" ) ); v_layout->addWidget( new QPushButton( "Cancel" ) ); v_layout->addStretch( ); v_layout->addWidget( new QPushButton( "Help" ) );

QListBox * country_list = new QListBox( this ); countryList->insertItem( "Canada" ); ...etc...

QHBoxLayout * h_layout = new QHBoxLayout( ); h_layout->addWidget( country_list ); h_layout->addLayout( v_layout );

QVBoxLayout * top_layout = new QVBoxLayout( ); top_layout->addWidget( new QLabel( "Select a country", this ) ); top_layout->addLayout( h_layout );

window->setLayout( top_layout ); window->show( ); Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 49 Advanced layout control

For each QWidget • min and max sizes • a Size Policy

Methods • setMinimumSize(), setMaximumSize()

• setSizePolicy(QSizePolicy)

• setSizePolicy(QSizePolicy::Policy horizontal, QSizePolicy::Policy vertical)

• setHorizontalPolicy() • setVerticalPolicy()

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 50 Advanced layout control

Size Policy = size constraints • sizeHint() = recommended size for the widget • Existing Size Policies • Fixed: = sizeHint() • Minimum: >= sizeHint() • Maximum: <= sizeHint() • Preferred: ~ sizeHint(), can be shrunk, can be expanded • Expanding: get as much space as possible, can be shrunk • MinimumExpanding: idem, but can't be shrunk • Ignored: idem, ignoring sizeHint()

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 51 More on signal & slots

Slot executed by several objects Action 1 - signal / slot model => le slot does not know the emitter Slot doIt()

- but this can be useful!

=> shared slots Action 2

Solutions ?

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 52 Solution 1: Lambdas

// instance variable of MyClass QAction * action1, * action2, …;

Action // in the .cpp file void MyClass::createGUI( ) { action1 = new QAction(tr("Action 1"), this); Slot connect( action1, &QAction::triggered, [=] {doIt(action1);} );

action2 = new QAction(tr("Action 2"), this); Action connect( action2, &QAction::triggered, [=] {doIt(action2);} ); …. } void MyClass::doIt(QAction * sender ) { if (sender == action1) ....; else if (sender == action2) .... ; …. }

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 53 Solution 2: QObject::sender()

// instance variable of MyClass QAction * action1, * action2, …;

Action // in the .cpp file void MyClass::createGUI( ) { action1 = new QAction(tr("Action 1"), this); Slot connect( action1, SIGNAL(triggered()), this, SLOT(doIt()) );

action2 = new QAction(tr("Action 2"), this); Action connect( action2, SIGNAL(triggered()), this, SLOT(doIt()) ); ..… } void MyClass::doIt( ) { auto * sender = QObject::sender( ); // similar to getSource() in Java/Swing if (sender == action1) ....; else if (sender == action2) .... ; …. } Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 54 Solution 3: QActionGroup

// instance variable of MyClass QAction * action1, * action2, …;

// in the .cpp file void MyClass::createGUI( ) { Action 1 auto *group = new QActionGroup(this);

// only one connect ! connect(group, SIGNAL(triggered(QAction *)), Slot doIt() this, SLOT(doIt(QAction *)));

action1 = group->addAction(tr("Action 1")); action2 = group->addAction(tr("Action 2")); Action 2 … } void MaClasse::doIt(QAction * sender ) { ActionGroup if (sender == action1) ....; else if (sender == action2) .... ; …. Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 55 Solution 3: QActionGroup

By default, the group implements exclusive selection otherwise:

group->setExclusive(false);

Action 1

Same principle with buttons (QPushButton, QRadioButton, QCheckBox …) Slot doIt()

QButtonGroup * group = new QButtonGroup(this);

Action 2 QButtonGroup signals:

buttonClicked(QAbstractButton * button) ActionGroup buttonClicked(int id)

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 56 Solution 4: QSignalMapper

void MyClass::createGUI( ) { QSignalMapper* mapper = new QSignalMapper (this) ; connect(mapper, SIGNAL(mapped(int)), this, SLOT(doIt(int))) ;

QPushButton * btn1 = new QPushButton("Action 1"), this); connect (btn1, SIGNAL(clicked( )), mapper, SLOT(map( ))) ; mapper->setMapping (btn1, 1) ;

QPushButton * btn2 = new QPushButton("Action 1"), this); connect (btn2, SIGNAL(clicked( )), mapper, SLOT(map( ))) ; mapper->setMapping (btn2, 2) ; … } void MaClasse::doIt(int value) { …. } Possible for : int, QString&, QWidget* et QObject*

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 57 Creating one's own signals & slots

In the header file (.h) class BankAccount : public QObject { Q_OBJECT • Must be a subclass of QObject private: int curBalance; (or QWidget) public: BankAccount( ) { curBalance = 0; } • Note Q_OBJECT, slots int getBalance( ) const { return curBalance; } and signals keywords public slots: (needed by the moc pre-compiler) void setBalance( int newBalance ); signals: void balanceChanged( int newBalance ); • Implement slots };

• Don't implement signals (this is done by the moc)

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 58 class BankAccount : public QObject { Q_OBJECT private: Implementing a slot int curBalance; public: BankAccount( ) { curBalance = 0; } int getBalance( ) const { return curBalance; } In the implémentation file (.cpp) public slots: void setBalance( int newBalance ); signals: void BankAccount::setBalance(int newBalance) void balanceChanged( int newBalance ); { }; if (newBalance != curBalance) { curBalance = newBalance; emit balanceChanged(curBalance); } }

emit • emits the balanceChanged() signal • the signal "holds" the curBalance value

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 59 class BankAccount : public QObject { Q_OBJECT private: One way connection int curBalance; public: BankAccount( ) { curBalance = 0; } int getBalance( ) const { return curBalance; } public slots: void setBalance( int newBalance ); signals: void balanceChanged( int newBalance ); };

BankAccount * x = new BankAccount; BankAccount * y = new BankAccount; connect(x, SIGNAL(balanceChanged(int)), y, SLOT(setBalance(int))); x->setBalance(10);

void BankAccount::setBalance(int newBalance) { if (newBalance != curBalance) { curBalance = newBalance; emit balanceChanged(curBalance); } }

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 60 class BankAccount : public QObject { Q_OBJECT private: One way connection int curBalance; public: BankAccount( ) { curBalance = 0; } int getBalance( ) const { return curBalance; } public slots: void setBalance( int newBalance ); signals: void balanceChanged( int newBalance ); };

BankAccount * x = new BankAccount; BankAccount * y = new BankAccount; connect(x, SIGNAL(balanceChanged(int)), y, SLOT(setBalance(int))); x->setBalance(10);

- x is set to 10 ➜ the balanceChanged() signal is emitted and holds this value

- setBalance() is executed on object y ➜ y is set to 10

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 61 Two-way connection

connect(x, SIGNAL(balanceChanged(int)), y, SLOT(setBalance(int))); connect(y, SIGNAL(balanceChanged(int)), x, SLOT(setBalance(int))); x->setBalance(10);

- OK? void BankAccount::setBalance(int newBalance) { if (newBalance != curBalance) { curBalance = newBalance; emit balanceChanged(curBalance); } }

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 62 Two-way connection

connect(x, SIGNAL(balanceChanged(int)), y, SLOT(setBalance(int))); connect(y, SIGNAL(balanceChanged(int)), x, SLOT(setBalance(int))); x->setBalance(10);

• OK : because emit is called void BankAccount::setBalance(int newBalance) only if newBalance has changed { if (newBalance != curBalance) { • infinite loop otherwise! curBalance = newBalance; emit balanceChanged(curBalance); } }

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 63 Ignoring events

To ignore events

• QEvent::ignore() => The receptor ignores the event

Typical example: Custom behavior when quitting the application

• When clicking on the window bar close button

• The QEvent::closeEvent() method is called

Custom behavior void MainWindow::closeEvent(QCloseEvent *e) { • Redefine closeEvent() so that it calls ignore() e->ignore(); myMethod(); }

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 64 2D Graphics

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 65 Drawing in a widget

A widget is repainted "when needed" - When the application is started or restored (deiconified)

- When a window that was hiding a widget is moved

- When a window is moved (depending on settings)

- When requesting it by calling update( )

damaged / repaint model - Differs from 3D applications (usually, the scene is constantly repainted)

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 66 Damaged / repaint model

void mySlot(...) ...... update(); ...... update(x, y, w, h); Event ...... update(region1); Event loop update(region2); User ...... }

void paintEvent(QPaintEvent* e) { ...... }

update() tells the system that an area has been damaged paintEvent() is then automatically called after processing slots

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 67 Damaged / repaint model

Caution! • Draw only in paintEvent() • Don't call paintEvent() directly

Stores requests in a waiting list The area can be specified (whole widget area by default) called once whatever the number of update() calls

update() tells the system that an area has been damaged paintEvent() is then automatically called after processing slots

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 68 Damaged / repaint model

In addition:

• repaint() redraw immediately (contrary to update()) • don't use it except in special cases (animations)

Java works in the same way but: • update() Qt == repaint() Java • paintEvent() Qt == paint() Java

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 69 Drawing

Principle: create a subclass of QWidget that redefines paintEvent()

Canvas.h Canvas.cpp

#include #include < QPainter > #include "Canvas.h" class Canvas : public QWidget { public: void Canvas::paintEvent(QPaintEvent* e) { Canvas(QWidget* parent) : QWidget(parent) {} // standard behavior (draws the background) QWidget::paintEvent(e); protected: virtual void paintEvent(QPaintEvent*); // creates a painter for this widget }; QPainter painter(this); painter.drawLine(50, 10, 100, 20); }

Use QPainter only in paintEvent() (because of double buffering)

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 70 Drawing

Important! Canvas.cpp QPainter is allocated on the stack! #include => auto-deleted when #include "Canvas.h" the function returns void Canvas::paintEvent(QPaintEvent* e) { => avoid creating it using new // standard behavior (draws the background) (if you do don't forget to delete it!) QWidget::paintEvent(e); // creates a painter for this widget QPainter painter(this); painter.drawLine(50, 10, 100, 20); }

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 71 Detecting events

Principle: create a subclass of QWidget that redefines these functions No signals / slots in this case!

void mousePressEvent(QMouseEvent*);

void mouseReleaseEvent(QMouseEvent*);

void mouseDoubleClickEvent(QMouseEvent*); Note: detects drag events void mouseMoveEvent(QMouseEvent*); if setMouseTracking(true) was called

void setMouseTracking(bool) Drag event = move with mouse button pressed bool hasMouseTracking()

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 72 Detecting events

Canvas.h Canvas.cpp #include #include "Canvas.h" #include void Canvas::mousePressEvent(QMouseEvent* e) { class Canvas : public QWidget { if (e->button() == Qt::LeftButton) { public: ...... Canvas(QWidget* p) : QWidget(p) {} ….. protected: update(); // dont forget! void mousePressEvent(QMouseEvent*); } }; }

QMouseEvent methods • button() Mouse bouton that triggered the event. ex: Qt::LeftButton

• buttons() State of the other buttons (ORed mask). ex: Qt::LeftButton | Qt::MidButton

• modifiers() Keyboard modifiers. ex: Qt::ControlModifier | Qt:: ShiftModifier

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 73 Cursor position

QMouseEvent methods Canvas.cpp #include "Canvas.h" • Local position of the event (relative to widget): void Canvas::mousePressEvent(QMouseEvent* e) { pos( ) if (e->button() == Qt::LeftButton) { localPos( ) ...... ….. • Position relative to screen or window: update(); // dont forget! screenPos( ) } windowPos( ) } • When moving a widget interactively: globalPos( ) Changing coordinates • QWidget::mapToGlobal( ) Alternate way • QCursor::pos( ) : position of the cursor • QWidget::mapFromGlobal( )

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 74 QPainter

Attributes • setPen( ) : how lines and outlines are drawn • setBrush( ) : how space if filled inside line • setFont( ) : text fonts • setTransform( ) : geometrical transformations • setClipRect/Path/Region( ) : clipping • setCompositionMode( ) : composing (e.g. alpha blending)

Drawing • drawPoint(), drawPoints(), drawLine(), drawLines() • drawRect(), drawRects(), fillRect(), • drawArc(), drawEllipse() • drawPolygon(), drawPolyline() Classes • drawPath(), fillPath() : combination of graphical primitives • QPoint, QLine, QRect... • drawText() • QPointF, QLineF • drawPixmap(), drawImage(), drawPicture(), • QPainterPath • etc. • QRegion • etc. Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 75 QPen

Attributes // in paintEvent() • style default value is 0 QPen pen; // default pen • width = "cosmetic pen" pen.setStyle(Qt::DashDotLine); • brush pen.setWidth(3); • capStyle pen.setBrush(Qt::green); • joinStyle pen.setCapStyle(Qt::RoundCap); pen.setJoinStyle(Qt::RoundJoin);

QPainter painter(this); Qt::PenStyle painter.setPen(pen); // etc....

Qt::PenCapStyle

Qt::PenJoinStyle

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 76 QBrush

Attributes • style • color • gradient • texture Qt::GlobalColor (predefined colors)

QLinearGradient

Qt::BrushStyle QRadialGradient QLinearGradient gradient( QPointF(0, 0), QPointF(100, 100) );

gradient.setColorAt(0, Qt::white); QConicalGradient gradient.setColorAt(1, Qt::blue);

QBrush brush(gradient);

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 77 Compositing

Porter Duff operators

• Specify: F(source, destination) • QPainter::setCompositionMode()

• Default: alpha blending (SourceOver mode)

• dest <= asrc * source + (1-asrc) * adest * dest

Limitations • hardware • implementation • what your are drawing on

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 78 Clipping

According to a rectangle, a region, a path - QPainter::setClipping(), setClipRect(), setClipRegion(), setClipPath()

QRegion operations - united(), intersected(), subtracted(), xored()

QRegion r1(QRect(100, 100, 200, 80), QRegion::Ellipse); // r1: elliptic region QRegion r2(QRect(100, 120, 90, 30)); // r2: rectangular region QRegion r3 = r1.intersected(r2); // r3: intersection

QPainter painter(this); painter.setClipRegion(r3); ...etc...

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 79 Geometrical transformations

• translate() • rotate() See Qt demos • scale() • shear() • setTransform()

Clock example

QPainter painter( this ); painter.translate( width( )/2, height( )/2 ); painter.scale( side/200.0, side/200.0 );

painter.save( ); // empile l’état courant painter.rotate( 30.0 * ((time.hour() + time.minute() / 60.0)) ); painter.drawConvexPolygon( hourHand, 3 ); painter.restore( ); // dépile

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 80 Picking / Interaction

Example • Clicking on the rectangle • Centers the rectangle

QRect rect;

void mousePressEvent(QMouseEvent* e) { if (rect.contains( e->pos() )) { Tests rect.moveCenter( e->pos() ); • intersects() update( ); } • contains() • Methods of QRect, QRectF, void paintEvent(QPaintEvent* e ) { QPainterPath, etc. QPainter painter(this); • return true or false ..... painter.drawRect(rect); Returns the intersection } • intersected()

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 81 Antialiasing

Anti-aliasing • To display "smooth contours" • Consequence of pixel discrete geometry • Especially useful for rendering fonts (e.g. ClearType)

what you see oeil.jpg what is actually displayed

subpixel rendering aliased antialiased Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 82 Paths

QPainterPath • Arbitrary combinations of lines and curves • displayed using QPainter::drawPath() • various graphical operations (e.g. clipping)

Methods - Moving the pen: moveTo(), arcMoveTo() - Drawing: lineTo(), arcTo() - Bezier curves: quadTo(), cubicTo() - addRect(), addEllipse(), addPolygon(), addPath() ... - addText() - translate(), etc.

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 83 Path examples

QPointF center, startPoint; QPainterPath myPath;

myPath.moveTo( center ); myPath.arcTo( boundingRect, startAngle, sweepAngle );

QPainter painter( this ); painter.setBrush( myGradient ); painter.setPen( myPen ); painter.drawPath( myPath );

QPointF baseline(x, y); QPainterPath myPath;

myPath.addText( baseline, myFont, "Qt" );

QPainter painter( this ); ... etc.... painter.drawPath( myPath );

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 84 Path examples

QPainterPath path; path.addRect(20, 20, 60, 60); path.moveTo(0, 0); path.cubicTo(99, 0, 50, 50, 99, 99); path.cubicTo(0, 99, 50, 50, 0, 0);

QPainter painter(this); painter.fillRect(0, 0, 100, 100, Qt::white); painter.setPen(QPen(QColor(79, 106, 25), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); painter.setBrush(QColor(122, 163, 39)); painter.drawPath(path);

Qt::OddEvenFill Qt::WindingFill (default)

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 85 Images

RGB colors

QImage image(3, 3, QImage::Format_RGB32); QRgb value; value = qRgb(122, 163, 39); // 0xff7aa327 image.setPixel(0, 1, value); image.setPixel(1, 0, value)

Indexed colors

QImage image(3, 3, QImage::Format_Indexed8); QRgb value; value = qRgb(122, 163, 39); // 0xff7aa327 image.setColor(0, value); value = qRgb(237, 187, 51); // 0xffedba31 image.setColor(1, value); image.setPixel(0, 1, 0); image.setPixel(1, 0, 1);

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 86 QPicture

To record and replay graphical primitives

Record: QPicture picture; QPainter painter; painter.begin( &picture ); // paint in picture painter.drawEllipse( 10,20, 80,70 ); // draw an ellipse painter.end( ); // painting done picture.save( "drawing.pic" ); // save picture

Replay : QPicture picture; picture.load( "drawing.pic" ); // load picture QPainter painter; painter.begin( &myImage ); // paint in myImage painter.drawPicture( 0, 0, picture ); // draw the picture at (0,0) painter.end( ); // painting done

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 87 Graphics View Framework

Goals and benefits • Item-based approach (scene graph) • Model-view programming • => graphical objects automatically refreshed, automatic picking, etc. • => multiple views

Main classes QGraphicsScene scene; QGraphicsRectItem * rect = • QGraphicsScene scene.addRect( QRectF(0,0,100,100) ); • QGraphicsView QGraphicsItem *item = scene.itemAt(50, 50); • QGraphicsItem ..... • etc. QGraphicsView view( &scene ); view.show( );

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 88 3D graphics with OpenGL

Principle

- Create a subclass o QOpenGLWidget and redefinine : • virtual void initializeGL( ) • virtual void resizeGL(int w, int h) • virtual void paintGL( )

- Call update( ) to ask the widget to be repainted

- Note : QtGraphicsView is also compatible with OpenGL

- See example on the course Qt Web site

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 89 OpenGL : Box3D.h

#include #include

class Box3D : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: Box3D(QWidget * parent); public slots: void setXRot(int angle); void setYRot(int angle); void setZRot(int angle); signals: void xRotChanged(int angle); void yRotChanged(int angle); void zRotChanged(int angle); private: void initializeGL( ) override; void paintGL( ) override; void resizeGL(int w, int h) override;

int xRot, yRot, zRot; GLfloat red, green, blue; Eric}; Lecolinet - Télécom ParisTech - Qt et graphique interactif 90 OpenGL : Box3D.cpp

#include "Box3D.h" void Box3D::setXRot(int rot) { if (rot != xRot) { Box3D::Box3D(QWidget * parent) : QOpenGLWidget(parent) { xRot = rot; xRrot = yRot = zRot = 0.; emit xRotChanged(rot); red = green = blue = 0.; update( ); } } } void Box3D::initializeGL( ) { void Box3D::setYRot(int rot) { initializeOpenGLFunctions( ); // ne pas oublier ! if (rot != yRot) { glClearColor(0.0f, 0.0f, 0.0f, 0.0f); yRot = rot; glEnable(GL_DEPTH_TEST); emit yRotChanged(rot); glEnable(GL_CULL_FACE); update( ); glShadeModel(GL_SMOOTH); } glEnable(GL_LIGHTING); } glEnable(GL_LIGHT0); void Box3D::setZRot(int rot) { GLfloat lightPosition[4] = {0, 0, 10, 1}; if (rot != zRot) { glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); zRot = rot; GLfloat lightColor[4] = {red, green, blue, 1}; glLightfv(GL_LIGHT0, GL_AMBIENT, lightColor); emit zRotChanged(rot); } update( ); } } Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 91 OpenGL : Box3D.cpp

void Box3D::paintGL( ) { glBegin(GL_TRIANGLES); glNormal3f(1,0, 0.707); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glVertex3f(1,-1,0); glVertex3f(1,1,0); glLoadIdentity(); glVertex3f(0,0,1.2); glTranslatef(0.0, 0.0, -10.0); glEnd(); glRotatef(xRot, 1.0, 0.0, 0.0); glRotatef(yRot, 0.0, 1.0, 0.0); glBegin(GL_TRIANGLES); glRotatef(zRot, 0.0, 0.0, 1.0); glNormal3f(0,1,0.707); glVertex3f(1,1,0); glBegin(GL_QUADS); glVertex3f(-1,1,0); glNormal3f(0,0,-1); glVertex3f(0,0,1.2); glVertex3f(-1,-1,0); glEnd(); glVertex3f(-1,1,0); glBegin(GL_TRIANGLES); glVertex3f(1,1,0); glNormal3f(-1,0,0.707); glVertex3f(1,-1,0); glVertex3f(-1,1,0); glEnd(); glVertex3f(-1,-1,0); glVertex3f(0,0,1.2); glBegin(GL_TRIANGLES); glEnd(); glNormal3f(0,-1,0.707); } glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(-1,-1,0); void Box3D::resizeGL(int w, int h) { glColor3f(1.0f, 1.0f, 0.0f); int side = qMin(w, h); glVertex3f(1,-1,0); glViewport((w - side) / 2, (h - side) / 2, side, side); glColor3f(1.0f, 0.0f, 1.0f); glMatrixMode(GL_PROJECTION); glVertex3f(0,0,1.2); glLoadIdentity(); glEnd(); glOrtho(-2, +2, -2, +2, 1.0, 15.0); …. glMatrixMode(GL_MODELVIEW); Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif } 92 OpenGL : MainWindow

MainWindow::MainWindow(QWidget *parent) : int main(int argc, char **argv) { QMainWindow(parent) QApplication app(argc, argv); { … QSurfaceFormat format; Box3D * box3d = new Box3D(parent); format.setDepthBufferSize(24); setCentralWidget(box3d); format.setStencilBufferSize(8); createSlider(box3d, SLOT(setXRot(int))); format.setProfile(QSurfaceFormat::CoreProfile); createSlider(box3d, SLOT(setYRot(int))); QSurfaceFormat::setDefaultFormat(format); createSlider(box3d, SLOT(setZRot(int))); … MainWindow mainwin; } mainwin.setWindowTitle("OpenGL Box"); mainwin.show( ); return app.exec( ); void MainWindow::createSlider(Box3D * box3d, const char * slot) } { QSlider * slider = new QSlider(QSlider::Horizontal, this); connect(slider, SIGNAL(valueChanged(int)), box3d, slot); Type of a slot }

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 93 Performance issues

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 94 Flicker and double buffering

Flicker - example : https://www.youtube.com/watch?v=QoZ8L1quWnc

Problem - Drawing directly on the screen - Makes the eye to perceive changes

Solution: double buffering

- 1) Draw in the back buffer (hidden)

- 2) Copy in the front buffer (on the screen)

AnandTech Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 95 Page flipping

Alternate solution

- Changes buffers instead of copying them

Oracle/JavaSE

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 96 Tearing

Tearing • example : • https://www.youtube.com/watch?v=1nFBtTq0DHo

Problem • The back buffer is copied before the drawing is complete • Two different frames appear on the screen!

Solution - VSync (Vertical synchronization) - Drawback: slower!

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 97 Triple buffering

Faster

• One of the 2 back buffers is always ready • The font buffer can be updated without waiting

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 98 Latency

Problem - Interactive drawing - The drawing does not "follow" the mouse

Reason - Too many elements must be drawn!

Solution - Draw less elements! - Double buffering can't help!

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 99 Latency

Solution 1: Skip some intermediary images - Drawback: jerky display

void mouseMoveEvent(QMouseEvent* e) ...... Events if (delay < MAX_DELAY) update(); }

Don't redisplay if delay is too long

QTimer can be used for this purpose void paintEvent(QPaintEvent* e) { ...... }

100

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif Latency

Solution 2: Backing store - Don't redraw all elements • Store the part that does not change in an image • Display the image + the part that changed

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 101 Qt Designer

Files • calculator.pro • calculator.ui • calculator.h • calculator.cpp • main.cpp calculator.pro TEMPLATE = app FORMS = calculator.ui SOURCES = main.cpp HEADERS = calculator.h calculator.ui • fichier XML 102 Qt Designer

calculator.h calculator.cpp

#include "ui_calculator.h" #include "calculator.h"

class Calculator : public QMainWindow { Calculator::Calculator(QWidget * parent) : Q_OBJECT QMainWindow(parent), ui (new Ui::Calculator) public: { Calculator(QWidget * parent = 0); ui->setupUi(this); private : ...... Ui::Calculator * ui; } };

ui points to an object that contains the widgets created by Qt Designer

103 Linking with source code

How can we display the result of a calculation in a gadget created using Designer?

spinbox1 spinbox2 result

names given to these widgets in Designer 104 #include "ui_calculator.h" class Calculator : public QMainWindow { Hidden file Q_OBJECT ui_calculator.h public: Calculator(QWidget *parent =0); private : Ui::Calculator * ui; private slots : void on_spinbox1_valueChanged(int value); "auto-connect" slots void on_spinbox2_valueChanged(int value); };

#include "calculator.h void Calculator::on_spinbox1_valueChanged(int value) { result variable: QString res = QString::number(value); // or: QString res = QString::number(ui->spinbox1->value()); •its name is the name of the widget in Designer ui->result->setText(res); } •it is declared in ui_calculator.h

105 Auto-connection

spinbox1 spinbox2 result

•Name of the gadget in Designer: spinbox1 •Name of the signal: valueChanged •=> the slot on_spinbox1_valueChanged() is auto-connected

106 Dynamic loading

class Calculator : public QWidget { #include Q_OBJECT #include "calculator.h public: Calculator::Calculator(QWidget *parent) : QWidget(parent) { Calculator(QWidget *parent = 0); QUiLoader loader; private: QFile file(":/forms/calculator.ui"); QSpinBox * spinbox1; QSpinBox * spinbox2; file.open(QFile::ReadOnly); QLabel * result; QWidget * widget = loader.load(&file, this); }; file.close();

spinbox1 = qFindChild(this, "spinbox1"); spinbox2 = qFindChild(this, "spinbox2"); result = qFindChild(this, "result"); // etc. }

107 « Promotion »

Promote - Allows including instances of client classes in Designer

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 108 Examples

QtCreator > Accueil

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 109 Concurrency

Events are processed sequentially => The UI freezes if a slot or a callback function :

• Takes long time to execute void mySlot( ) ...... • Never returns update(); } Events or

void xxxEvent(QMouseEvent* e) Event Loop ...... update(); }

void paintEvent(QPaintEvent* e) { QWidget::paintEvent(e); QPainter.painter(this); ...... } Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 110 Concurrency

Events are processed sequentially => The UI freezes if a slot or a callback function : • Takes long time to execute • Never returns

Typical examples • Heavy calculations • Network applications (using sockets, etc.) • ex: Web browser • External sensors • ex: Kinect

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 111 Threads

void mySlot() { starts a thread Events }

void myFunction() { executes in the thread sends back data and returns }

1st case: A slot launches a thread • mySlot returns immediately

• myFunction terminates later • ex : load a Web page

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 112 Threads

void mySlot() { Events }

void myFunction() { executes in the thread while (true) { send data continuously } }

2nd case : A thread is constantly running • A server, a device, etc. sends data continuously • myFunction runs a loop • ex : retrieve Kinect data

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 113 Threads

void mySlot() { Events }

void myFunction() { executes in the thread while (true) { send data continuously } }

In both cases : How can we display this data? • myFunction must not directly change widgets • because of the event loop the graphical interfaces "lives" in the main thread

• Same problem with most (all?) GUI toolkits

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 114 QThread

void mySlot() { Events }

void QThread::run() { while (true) { getData(données); emit mysignal(données) } } QThread • Its run() method is executed in a new thread

• It can send signals to slots that are executed in the main loop

• connect() provides a queuing mechanism • 5th argument: Qt::ConnectionType

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 115 QThread class Tracker : public QThread { class MyWindow : public QMainWindow { Q_OBJECT Q_OBJECT public: Tracker * tracker{}; Tracker(const QString& host, int port); public slots: signals: void showData(const QString& data); void dataReady(const QString& data); public: private: MyWindow(QWidget* par) : QMainWindow(par) { void run( ) { // executed in the thread tracker = new DTracker(hostname, port); bool ok = true; QString data; connect(tracker, SIGNAL(dataReady(QString)), this, SLOT(showData(QString)) while (ok) { Qt::QueuedConnection); data = .....; // retrieve the data emit dataReady(data); tracker->start(); // starts the thread } .... } } }; dataReady() and ShowData() live in different threads ! Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 116 QThread

Qt::ConnectionType • AutoConnection connect(tracker, SIGNAL(dataReady(QString)), • DirectConnection this, SLOT(showData(QString)) • QueuedConnection Qt::QueuedConnection); • etc.

See also • QtConcurrent framework • QSocketNotifier

Eric Lecolinet - Télécom ParisTech - Qt et graphique interactif 117