Introduction à PyQt
Sylvain Malacria
[Link]
[Link]
Diapositives inspirées de Gilles Bailly
Documentation
[Link]
Qu’est ce que Qt ?
Librairie graphique écrite en C++ par la société TrollTech
‣ Mécanisme pour interagir :
- avec l’utilisateur (bouton, liste déroulante, ..)
- avec le système (OpenGl, Xml, SQL, sockets, plugin…)
Multi-Plateforme
‣ Windows | Mac | Linux
‣ Android | iOS | WinRT | BlackBerry* | SailFish
‣ Embedded Linux | Embedded Android | Windows Embedded
Gratuit (GPL), mais dispose aussi d’une licence commerciale
Approche : Ecrire une fois, compiler n’importe où
Historique
1990 Haavard & Eirik ont l’idée de créer une librairie graphique
1995 Qt 0.9 Première distribution publique pour X11/Linux
1996 Qt 1.0 (licences commerciales et open source)
1999 Qt 2.0 en Open Source (Licence QPL)
2001 Qt 3.0 support Mac et Qt designer
2005 Qt 4.0 (licence GPL 2.0 pour toutes plateforme)
2008 Nokia rachète Trolltech (société mère de Qt) et ses 250 employés
2009 Distribution Qt 4.5 avec QtCreator
2008 Qt 4.5
2010 Qt 4.6 animation; GraphicScene; machine à état; gestes
2011 Qt est racheté par Digia (objectif Android, iOS et Windows 8)
2012 Qt 5.0 Qt Quick (création d’interfaces dynamiques)
2015 20ème anniversaire de la première distribution publique
2016 Support Qt Mobile (iOS, Android)
Pourquoi Qt?
Performance (C++)
Relativement Simple
Gratuit (GPL) et code source
Nombreux outils
‣ Générateur d’interface : Qt Designer
‣ Internationalisation : Qt Linguist
‣ Documentation : Qt Assistant Qt creator
‣ Examples : Qt Examples
‣ Programmation : Qt Creator (eclipse)
Multi-Plateformes
‣ Look and feel simulé
Designer
Aujourd’hui
Augmentation de 250% des téléchargements de la GPL
Qt 5 : 10 000 téléchargement par jour
Aujourd'hui
Utilisateurs de Qt :
‣ ESA, Nokia, Nasa, Adobe, Motorola, Google, …
Bindings (java, python, c#)
8
9
Aujourd'hui
Qt Widgets
Aujourd'hui
Qt Widgets Qt Graphics view Qt quick /QML
Aujourd'hui
v.s.
Qt Widgets Qt Graphics view
‣ Widgets ne peuvent pas être transformés
‣ Widgets utilisent des coordonnées en pixels ; GraphicsItems en unités logiques
(int vs doubles)
‣ Widgets peuvent être utilisés dans des layouts
‣ 4000000 widgets rament, mais 4000000 items fonctionnent très bien
[Link]
Aujourd'hui
v.s.
Qt Widgets Qt quick/QML Language déclaratif
‣ QML est basé sur JSON (Language); QtQuick (library)
‣ QWidgets sont plus matures, flexibles and ont plus de fonctionnalités
‣ Qt Quick se concentre sur les animation and transition
‣ Qt Quick est (pour l’instant) plutôt pour dispositifs mobiles
‣ Qt Quick va (peut-être) remplacer QWidgets un jour
‣ Qt Quick est (peut-être) mieux pour les designers (non-informaticiens)
Aujourd'hui
v.s.
QGraphicsView Qt quick/QML Language déclaratif
‣ Qt Quick Graphics engine only works with OpenGL
‣ Drawing complex shapes easier with QGraphicView
‣ Qt Quick: QML & Javascript
‣ QML is more compatible with Widget
PyQt
Bindings Python v2 et v3 pour Qt
Développé par Riverbank Computing Limited
Existe pour les mêmes plateformes que Qt
Binding le plus populaire avec PySide
PyQt5 pour Qt5, PyQt4 pour Qt4
Licences différentes de Qt (GNU GPL 3 et license commerciale)
PyQt peut générer du code python depuis Qt Designer
Possibilité d’ajouter des widgets écrits en PyQt à Qt Designer
PyQt n’est pas QUE pour la programmation de GUI !!!
Objectifs de ce cours
Rappels programmation Python
Premiers pas avec PyQt
Introduction aux Signaux et Slots de Qt
Aperçu des principales classes de Qt
Python
Langage de programmation objet
Typage dynamique fort
Placé sous licence libre
Enormément de bibliothèques
Rapide d’utilisation
Nombreuses extensions destinées au calcul numérique
Syntaxe
Syntaxe C Syntaxe Python
int factorielle(int n) { def factorielle(n):
if (n < 2) { if n < 2:
return 1; return 1
} else { else:
return n * factorielle(n - 1); return n * factorielle(n - 1)
}
}
Syntaxe
Syntaxe C Syntaxe Python
int factorielle(int n) { def factorielle(n):
if (n < 2) { if n < 2:
return 1; return 1
} else { else:
return n * factorielle(n - 1); return n * factorielle(n - 1)
}
}
Attention aux tabulations !!!
Typage dynamique fort
int a = 4
a=4
type(a) <class 'int'>
a = 4.1
type(a) <class 'float'>
Quelques types de base
Booléen
Numériques
‣ int
‣ long
‣ float
‣ complex
Collections
‣ list
‣ tuple
‣ set
‣ dict
‣ etc.
Quelques types de base
Booléen
Numériques
list1 = ['physics','chemistry',1200]
‣ int print(list1[0])
‣ long
‣ float >>> ‘physics’
‣ complex
[Link]("bla")
Collections print(list1[3])
‣ list
‣ tuple >>> ‘bla’
‣ set
‣ dict
print(list1[1:3])
‣ etc.
>>> ['chemistry', 1200]
Quelques types de base
Booléen
Numériques
list1 = ['physics','chemistry',1200]
‣ int print(list1[0])
‣ long
‣ float >>> ‘physics’
‣ complex
[Link]("bla")
Collections print(list1[3])
‣ list
‣ tuple >>> ‘bla’
‣ set
‣ dict
print(list1[1:3])
‣ etc.
>>> ['chemistry', 1200]
Opérations de base sur collections (List, Tuple)
alphabetT = ('a','b','c','d','e','f','g','h','i','j','k') ➡ Tuple
alphabetL = [‘a','b','c','d','e','f','g','h','i','j','k'] ➡ List
alphabetT2 = ‘a’,’b','c','d',('e','é','è'),'f','g','h','i','j','k'
len(alphabetT) ➡ Nombre d’éléments
>>> 11
len(alphabetT2[4])
>>> 3
alphabetT[-2] ➡ Accès depuis la fin
>>> ‘j’
for char in alphabetT: ➡ Boucle for each
print(char)
for i in range(len(alphabetT)): ➡ Boucle itérative
print(alphabetT[i])
for i in range(2,len(alphabetT)):
print(alphabetT[i])
Entrées/Sorties fichiers
file = open("[Link]", "r+") ➡ (r,w,a,r+) read, write, append, read+write
text = [Link]()
[Link]("blabla\n")
[Link]()
Classes
class Voiture(Vehicule): ➡ Déclaration d’une classe Voiture qui hérite de véhicule
#commentaire
nbRoues=4
def __init__(self,marque,couleur): ➡ Déclaration d’un constructeur
super().__init__() ➡ Appel du constructeur de la classe mère
[Link]=couleur ➡ Déclaration d’une variable d’instance
[Link]=marque ➡ Déclaration d’une variable d’instance
➡ Déclaration conventionnelle d’une méthode main()
def main():
audirouge = Voiture('audi','rouge')
print([Link])
➡ Appel automatique Main
if __name__ == "__main__":
main([Link])
Documentation PyQt
[Link]
Les principaux modules
QtCore
QtWidgets
QtGUI
QtBluetooth
QtOpenGL
QtSript/QtScriptTools
QtSql
QtSvg
QtWebKit
QtXml/QtXmlPatterns
QtMultimedia
QtSensors
Module QtCore
QObject
Type de base :
‣ QChar, QDate, QString, QStringList, Qtime,…
File systems :
‣ QDir, QFile,…
Container :
‣ QList, Qmap, Qpair, QSet, QVector,…
Graphique :
‣ QLine, Qpoint, QRect, QSize …
Thread :
‣ QThread, Qmutex, …
Autres :
‣ QTimer, …
QString
Codage Unicode 16 bits
‣ Suite de QChars
‣ 1 caractère = 1 QChar de 16 bits (cas usuel)
‣ 1 caractère = 2 QChars de 16 bits (pour valeurs > 65535)
Conversions d’une QString :
‣ toAscii( ) : ASCII 8 bits
‣ toLatin1( ) : Latin-1 (ISO 8859-1) 8 bits
‣ toUtf8( ) : UTF-8 Unicode multibyte (1 caractère = 1 à 4 octets)
‣ toLocal8Bit( ) : codage local 8 bits
Principaux widgets
QObject
QTimer QWidget
QDialog QFrame QAbstractButton
QLabel QLineEdit
Module QtWidgets
Qwidget QComboBox
QPushButton QSlider
QLabel QProgressBar
QCheckBox QTabBar
QRadioButton QMenu
QTableView QTreeView
Simple window
from [Link] import *
from [Link] import *
import sys
def main(args) :
app = QApplication(args)
button = QPushButton("Hello World !", None)
[Link](100,30)
[Link]()
app.exec_()
if __name__ == "__main__":
main([Link])
Simple window with button in widget
from [Link] import *
from [Link] import *
import sys
def main(args) :
app = QApplication(args)
widget = QWidget(None)
[Link](400,90)
button = QPushButton("Hello World !", widget)
[Link](100,30)
widget().show()
app.exec_()
if __name__ == "__main__":
main([Link])
Signaux et slots
Comment, à partir d’un « clic sur un bouton », je peux exécuter la partie
correspondant à la logique de mon application ? (ex: fermer l’application) ??
Solutions:
‣ MFC (introduit un langage au dessus de C++)
‣ Java (utilise des listeners)
‣ Qt (utilise principalement des signaux et slots)
Application algorithmique
Utilisation de procédures (fonctions) appelées de manière séquentielle
Série d’étapes à réaliser dans un certain ordre
Execution 1 Execution 2 Execution 3 Stop
Entrées - sorties utilisateur
Programmation “classique” :
‣ Programme principal initialise et appelle des fonctions dans un ordre pre-déterminé
‣ Les éventuels évènements utilisateurs sont « demandés » (programme en pause)
Programmation “événementielle” :
‣ Programme principal initialise des variables et les fonctions réagissent aux
événements
‣ Le déroulement est contrôlé par la survenue d’événements
(dont les actions de l’utilisateur)
‣ Boucle principale qui traite les événements (enfouie dans la bibliothèque)
Quels évènements?
Actions utilisateurs
Notifications de processus (applications, OS, MAJ)
Capteurs sensorielles (info ubiquitaire)
while (true){
if(![Link]()){
event = [Link]();
source = findSourceForEvent(event);
[Link](event);
}
}
File d’attente (queue FIFO)
Connecter signaux et slots
Connecter signaux et slots
42
Signal émis
Connecter signaux et slots
42 42
Signal émis
Slot implémenté
Connecter signaux et slots
42 connexion 42
Signal émis
Slot implémenté
[Link]([Link])
Connecter signaux et slots
class QSlider(QObject):
….
def mousePressEvent(self)
[Link](value)
…
slider = QSlider(None)
42 connexion 42
Signal émis
Slot implémenté
[Link]([Link])
Connecter signaux et slots
class QLCDNumber(QObject):
def display(num)
m_value = num;
…
lcd = QLCDNumber(None)
42 connexion 42
Signal émis
Slot implémenté
[Link]([Link])
Une classe avec des signaux et des slots
class MyClass(QObject):
mySignal = pyqtSignal(int)
void mySlot( self, num ):
blabla
- Sous class de QObject
- Les signaux ne sont pas implémentés
- Les slots doivent êtres implémentés
Une classe avec des signaux et des slots
class MyClass(QObject):
mySignal = pyqtSignal(int)
def __init__(self, parent =None):
super(MyClass, self).__init__(parent)
@pyqtSlot(int)
void mySlot( num ): parfois nécessaire
blabla
- Sous class de QObject
- Les signaux ne sont pas implémentés
- Les slots doivent êtres implémentés
Signaux et slots
Modularité, flexibilité
‣ Connecter plusieurs signaux à un slot
‣ Connecter un signal à plusieurs slots
Philosophie
‣ L’émetteur n’a pas besoin de connaître le(s) récepteur(s)
‣ L’émetteur ne sait pas si le signal a été reçu
‣ Le récepteur ne connaît pas non plus l’émetteur
‣ Programmation par composant (indépendant, réutilisable)
Sécurité, typage fort
‣ Les types des paramètres doivent être les mêmes
‣ Un slot peut avoir moins de paramètres qu’un signal
Exemple : transfert d’argent entre banques
class PunchingBag(QObject):
punched = pyqtSignal() Signal
def __init__(self):
# Initialize the PunchingBag as a QObject
QObject.__init__(self)
def punch(self): Slot
[Link]()
@pyqtSlot()
def say_punched():
print('Bag was punched.')
def main(args):
bag = PunchingBag()
# Connect the bag's punched signal to the say_punched slot
[Link](say_punched) Connexion
# Punch the bag 10 times
for i in range(10):
[Link]()
if __name__ == "__main__":
main([Link])
Questions
Comment connecter un signal à un slot ?
‣ EmetteurObj.<nameSignal>.connect ( Recepteur.<nameSlot> )
Quel code pour déclarer / implémenter un slot ?
‣ rien de particulier (mais on peut rajouter @pyqtSlot()
Est ce qu’un slot peut retourner une valeur ?
‣ Oui
Quel code pour déclarer / implémenter un signal ?
‣ mySignal = pyqtSignal()
Simple window with button in widget
from [Link] import *
from [Link] import *
import sys
def main(args) :
app = QApplication(args)
widget = QWidget(None)
[Link](400,90)
button = QPushButton("Hello World !", widget)
[Link](100,30)
[Link]([Link])
widget().show()
app.exec_()
if __name__ == "__main__":
main([Link])
Les principaux modules
QtCore
QtWidgets
QtGUI
QtBluetooth
QtOpenGL
QtSript/QtScriptTools
QtSql
QtSvg
QtWebKit
QtXml/QtXmlPatterns
QtMultimedia
QtSensors
QStyle
Peut être passé en argument à l’execution du programme
Ex: python3 [Link] -style Windows
QMainWindow
Méthode 1: créer une instance de QMainWindow
win = QMainWindow()
[Link](200,300)
Méthode 2: créer une sous-classe de QMainWindow
class Win(QMainWindow):
def __init__(self):
[Link](200,300)
QMainWindow
Menus
bar = [Link]() si sous-classe (methode 2)
sinon [Link]() (methode 1)
fileMenu = [Link]( "File" )
newAct = QAction(QIcon("path/images/[Link]"), “New…”, None )
[Link]( “Ctrl+N” )
[Link](tr(“New File”))
[Link](tr(“New file”))
[Link](newAct)
[Link]( [Link] )
QMainWindow
QMenuBar, QMenu, QAction
QToolBar
‣ fileToolBar = QToolBar("File")
‣ [Link](newAct)
‣ [Link]( false ) desactive (grise) la commande dans
les menus et la toolbar
QToolTip, QWhatsThis
Composant central
textEdit = TextEdit( self );
[Link]( textEdit );
Buttons
Input Widgets
Containers
QMidArea QTabWidget QGroupBox
QToolBox
QScrollArea
QWidget; QFrame; QDockWidget; QStackedWidget
Views
QCompleter
standard widgets use data that is part of the widget
View classes operate on external data (the model)
def main(args):
app = QApplication(args)
tableView = QTableView()
myModel = MyModel()
[Link]( myModel )
[Link]()
[Link]()
class MyModel(QAbstractTableModel):
def __init__(self):
QAbstractTableModel.__init__(self)
[Link] = <dataBase>
def rowCount( self, parent ): #Type (parent) ==
QModelIndex
return 2
def columnCount( self, parent ):
return 2
def data( self, index, role=[Link]):
if role == [Link]:
return [Link]([Link]() + 1, [Link]()+1)
def main(args):
app = QApplication(args)
tableView = QTableView()
myModel = MyModel()
[Link]( myModel )
[Link]()
[Link]()
Display Widgets
Boites de dialogue
QProgressDialog
QFileDialog
QMessageBox
QColorDialog QFontDialog
Boîte de dialogue modale
Solution simplifiée
fileName = [Link]( self, //parent
"Open Image”, // titre
"/home/jana", // répertoire initial
"*.txt") // filtre
fileName = [Link](…)
Layout
Problèmes
‣ internationalisation
‣ redimensionnement
‣ complexité du code
Layout
QFormLayout
QHBoxLayout
QVBoxLayout QGridLayout
Layout
Exemple
v_layout = QVBoxLayout( )
v_layout.addWidget( QPushButton( "OK" ) )
v_layout.addWidget( QPushButton( "Cancel" ) )
v_layout.addStretch( )
v_layout.addWidget( QPushButton( "Help" ) )
country_list = QListBox( );
[Link]( "Canada" );
...etc...
h_layout = QHBoxLayout( )
h_layout.addWidget( country_list )
h_layout.addLayout( v_layout )
top_layout = QVBoxLayout( )
top_layout.addWidget( QLabel( "Select a country" ) )
top_layout.addLayout( h_layout );
container = QWidget()
[Link]( top_layout )
[Link](container)
[Link]( ) Notes sur layouts :
- peuvent être emboîtés
- pas liés à une hiérarchie
de conteneurs comme Java
- cf. le « stretch »
Layout
Exemple
v_layout = QVBoxLayout( )
v_layout.addWidget( QPushButton( "OK" ) )
v_layout.addWidget( QPushButton( "Cancel" ) )
v_layout.addStretch( )
v_layout.addWidget( QPushButton( "Help" ) )
country_list = QListBox( );
[Link]( "Canada" );
...etc...
h_layout = QHBoxLayout( )
h_layout.addWidget( country_list )
h_layout.addLayout( v_layout )
top_layout = QVBoxLayout( )
top_layout.addWidget( QLabel( "Select a country" ) )
top_layout.addLayout( h_layout );
container = QWidget()
[Link]( top_layout )
[Link](container)
[Link]( ) Notes sur layouts :
- peuvent être emboîtés
- pas liés à une hiérarchie
de conteneurs comme Java
- cf. le « stretch »
Layout
Exemple
v_layout = QVBoxLayout( )
v_layout.addWidget( QPushButton( "OK" ) )
v_layout.addWidget( QPushButton( "Cancel" ) )
v_layout.addStretch( )
v_layout.addWidget( QPushButton( "Help" ) )
country_list = QListBox( );
[Link]( "Canada" );
...etc...
h_layout = QHBoxLayout( )
h_layout.addWidget( country_list )
h_layout.addLayout( v_layout )
top_layout = QVBoxLayout( )
top_layout.addWidget( QLabel( "Select a country" ) )
top_layout.addLayout( h_layout );
container = QWidget()
[Link]( top_layout )
[Link](container)
[Link]( ) Notes sur layouts :
- peuvent être emboîtés
- pas liés à une hiérarchie
de conteneurs comme Java
- cf. le « stretch »
Layout
Exemple
v_layout = QVBoxLayout( )
v_layout.addWidget( QPushButton( "OK" ) )
v_layout.addWidget( QPushButton( "Cancel" ) )
v_layout.addStretch( )
v_layout.addWidget( QPushButton( "Help" ) )
country_list = QListBox( );
[Link]( "Canada" );
...etc...
h_layout = QHBoxLayout( )
h_layout.addWidget( country_list )
h_layout.addLayout( v_layout )
top_layout = QVBoxLayout( )
top_layout.addWidget( QLabel( "Select a country" ) )
top_layout.addLayout( h_layout );
container = QWidget()
[Link]( top_layout )
[Link](container)
[Link]( ) Notes sur layouts :
- peuvent être emboîtés
- pas liés à une hiérarchie
de conteneurs comme Java
- cf. le « stretch »
Arbre d’héritage
vs.
arbre d’instanciation
Arbres d’heritage
QObject
QTimer QWidget
QDialog QFrame QAbstractButton
QLabel QLineEdit
Principaux widgets
Arbre d’heritage
QCheckBox
QWidget QAbstractButton QRadioButton
QToolButton
QPushButton
Arbre d’instanciation
Hiérarchie d’instance (=objets)
‣ Arbre de filiation des objets
Arbre d’instanciation
Hiérarchie d’instance (=objets)
‣ Arbre de filiation des objets
Arbre d’insanciation
Les enfants se déclarent auprès de son parent (≠ java)
‣ label = Qlabel("Hello", parent);
‣ Exceptions
- QFile, QApplication…
Si le parent d’un Widget est nul, le Widget est une fenêtre (Window).
Que font les parents ?
‣ Ils ont une liste des enfants
‣ Ils détruisent automatiquement les enfants quand ils sont détruits
‣ Enable/disable les enfants quand ils enable/disable eux memes
‣ Pareil pour Show/Hide
Arbre d’instanciation
Hiérarchie d’instance (=objets)
‣ Arbre de filiation des objets
Chaque objet contient ses enfants
‣ Clipping : enfants inclus dans parents
‣ Superposition : enfants au dessus des parents
Un objet n’a qu’un seul parent
Arbre d’instanciation
• Les enfants se déclarent auprès de son parent (≠ java)
– label = Qlabel("Hello", parent);
– Execptions
• QFIle, QApplication…
• Si le parent d’un Widget est nul, le Widget est une fenêtre (Window).
• Que font les parents ?
– Ils ont une liste des enfants
– Ils détruisent automatiquement les enfants quand ils sont détruits
– Enable/disable les enfants quand ils enable/disable eux memes
– Pareil pour Show/Hide
Arbre d’instanciation
• Hiérarchie d’instance (=objets)
– Arbre de filiation des objets
• Chaque objet contient ses enfants
– Clipping : enfants inclus dans parents
– Superposition : enfants au
dessus des parents
• Un objet n’a qu’un seul parent