PyQt-intro-2h

advertisement
Introduction à Qt
Gilles Bailly
gilles.bailly@upmc.fr
Crédits
• Eric Lecolinet
2
• hci.isir.upmc.fr/ihm-androide/
3
http://pyqt.sourceforge.net/Docs/
PyQt5/index.html
4
Introduction
1
What ?
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ù
7
Historique
• 1988 : Haavard & Eirik ont l’idée de créer une librairie
graphique orientée objet
• 1993 : Le noyau est terminé et ont pour objectif « The world’s
best C++ GUI framework »
– Nom Qt, signaux et slots
• 2001 : Qt 3.0, 500 000 lignes de codes, Linux, Windows, Mac.
• 2008 : Qt 4.5 (racheté par Nokia; 250 employés)
– la plateforme Symbian
• 2009 : Qt 4.6 : animation; GraphicScene; machine à état;
gestures
• 2011 : Qt est racheté par Digia
– objectif : Android, iOS et Windows 8
• 2012 : Qt 5.0 : Qt Quick (javascript)
• 2016 : Qt compagny
Why?
•
•
•
•
Performance (C++)
Relativement Simple
Gratuit (GPL) et code source
Nombreux outils
–
–
–
–
–
Générateur d’interface : Qt Designer
Internationalisation : Qt Linguist
Documentation : Qt Assistant
Examples : Qt Examples
Programmation : Qt Creator (eclipse)
Qt creator
• Multi-Plateformes
– Look and feel simulé
Designer
10
Aujourd'hui
• Utilisateurs de Qt :
– Adobe Photoshop, Autodesk Maya, Google Earth, Skype, Spotify, VLC
media player
• Bindings (java, python, c#)
12
13
14
Aujourd'hui
Qt Widgets
Qt Graphics view
Qt quick /QML
15
Aujourd'hui
v.s.
Qt Widgets
Qt Graphics view
▪ Widgets cannot be scaled or rotated,
▪ Widgets can only appear once on the desktop, but several views can observe one graphicsitem.
▪ Widgets express their geometries in pixels, graphics items use logical units.
(…int vs. double)
▪ Widgets support tons of features that graphics items don’t understand.
▪ Widgets understand layouts,
▪ 4000000 widgets don’t work that well, but 4000000 items works perfectly. 16
17
Aujourd'hui
v.s.
Qt Widgets
Qt quick/QML
QML is based on JSON (Language); QtQuick (library)
Widgets are more mature, flexible and provide rich features Language déclaratif
Qt Quick focuses on animation and transition
Qt Quick is mainly for mobile devices (today)
Qt Quick will (maybe) remplace Widgets tomorrow
Qt Quick is maybe easier to use for designers
18
19
20
Aujourd'hui
v.s.
QGraphicsView
Qt quick/QML
Qt Quick Graphics engine only works with OpenGL
Drawing complex shapes easier with QGraphicView
Qt Quick: QML & Javascript
QML is more compatible with Widget
Language déclaratif
21
Qt (QML) vs. HTML 5
22
Objectifs
Introduction
– Signaux et slots
– Les principales classes Qt
Graphisme avancé
– Création de vos propres widgets
– Programmation événementielle
– Notion avancée de graphisme avec Qt
Quelques autres notions avancées
–
–
–
–
Machine à états
Gestes
Animation
Qt Designer
Au delà de Qt …
Syntaxe
Syntaxe C
int factorielle(int n) {
if (n < 2) {
return 1;
} else {
return n * factorielle(n - 1);
}
}
Syntaxe Python
def factorielle(n):
if n < 2:
return 1
else:
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'>
Classes
class Voiture:
#commentaire
nbRoues=4
➡
Déclaration d’une classe
def __init__(self,marque,couleur):
➡ Déclaration d’un constructeur
self.couleur=couleur
➡ Déclaration d’une variable d’instance
self.marque=marque
➡ Déclaration d’une variable d’instance
def main():
➡ Déclaration conventionnelle d’une méthode main()
audirouge = Voiture('audi','rouge')
print(audirouge.couleur)
if __name__ == "__main__":
main(sys.argv)
➡
Appel automatique Main
Mes premiers pas avec Qt
2
"Hello world"
28
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
def main(args) :
app = QApplication(args)
button = QPushButton("Hello World !", None)
button.resize(100,30)
button.show()
app.exec_()
if __name__ == "__main__" :
main(sys.argv)
29
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
def main(args) :
app = QApplication(args)
widget = QWidget(None)
widget.resize(150,90)
button = QPushButton("Hello World !", widget)
button.resize(100,30)
widget.show()
app.exec_()
if __name__ == "__main__" :
main(sys.argv)
30
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
def main(args) :
app = QApplication(args)
widget = QWidget(None)
widget.resize(150,90)
button = QPushButton("Hello World !", widget)
button.resize(100,30)
widget.show()
button.clicked.connect(app.quit)
app.exec_()
if __name__ == "__main__" :
main(sys.argv)
31
Signaux et Slots
3
Problématique
• Comment
– à partir d’un « clic sur un bouton »
– Je peux éxé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)
Connecter Signaux et Slots
class QSlider(QObject):
….
def mousePressEvent(self):
self.valueChanged.emit(value)
…
class QLCDNumber(QObject):
def display(num):
m_value = num;
…
lcd = QLCDNumber(none)
slider = QSlider(None)
42
Signal émis
connexion
42
Slot implémenté
slider.valueChanged.connect(lcd.display)
Une classe avec des signaux et des slots
class MyClass(QObject):
mySignal = pyqtSignal(int)
def mySlot( self, num ):
blabla
• Sous class de QObject
• Les signaux 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)
def mySlot( num ):
blabla
• Sous class de QObject
• Les signaux 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 (en C++)
– Les types des paramètres doivent être les mêmes
– Un slot peut avoir moins de paramètres qu’un signal
transfert d’argent entre deux banques
BANK 1
BANK 2
BANK 1
BANK 2
38
transfert d’argent entre deux banques
class BankAccount (QObject):
balance = 0
name = “"
balanceChanged = pyqtSignal(int)
def setBalance(self, newValue):
self.balance = newValue
self.balanceChanged.emit( self.balance )
Signal
Slot
def main(args):
account1 = BankAccount()
account1.setName( “account1” )
account2 = BankAccount()
account2.setName( “account2” )
account1.balancedChanged.connect( account2.setBalance )
account2.balancedChanged.connect( account1.setBalance )
if __name__ == “__main__”:
main(sys.argv)
Connexion
Problème?
transfert d’argent entre deux banques
def setBalance(self, newValue):
if self.balance != newValue:
self.balance = newValue
self.balanceChanged.emit( self.balance )
account1 <- 25
account1 emit balanceChanged(25)
account2 <- 25
account2 emit balanceChanged(25)
account1 <-25
…
Un peu plus loin…
class MyClass(QObject):
mySignal = pyqtSignal(int)
def __init__(self, parent =None):
super(MyClass, self).__init__(parent)
@pyqtSlot(int)
def mySlot( num ):
blabla
parfois nécessaire
• Sous class de QObject
• Les signaux sont pas implémentés
• Les slots doivent êtres implémentés
• peuvent être décorés de @pyqtSlot
réduit la quantité de mémoire / est plus rapide
parfois utile pour les “auto-connect"
42
Encore un peu plus loin
• les auto-connect
class MyClass(QObject):
def __init__(self, parent =None):
self.buttonA = QPushButton(‘button 1’, self)
self.buttonA.setObjectName('buttonA)
self.spinBox = QSpinBox(self)
self.spinBox.setObjectName('mySpinBox')
….
QMetaObject.connectSlotsByName(self)
def on_buttonA_clicked(self):
blabla
@pyqtSlot(int)
def on_mySpinBox_valueChanged(self, arg):
blabla
43
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()
Ce qui se cache derrière
• en C++ c’est plus compliqué
45
Les principaux widgets
Modules
•
•
•
•
•
•
•
•
•
•
•
•
QtCore
QtWidgets
QtBluetooth
QtOpenGL
QtSript/QtScriptTools
QtSql
QtSvg
QtWebKit
QtXml/QtXmlPatterns
QtMultimedia
QtSensors
…
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, Qsemaphore, …
• Autres : QTimer, QTimeLine, …
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
- qPrintable ( const QString & str )
• équivalent à : str.toLocal8Bit( ).constData( )
QFile ouverture/fermeture de fichiers
exemples :
• file = QFile( fileName )
• if file.open( QFile.ReadOnly | QFile.Text) :
• if file.open( QFile.WriteOnly ):
• file.write( “fdsafsdf”)
• file.flush()
• file.read()
• file.close()
QTextStream lecture, écriture dans fichiers
exemples :
• stream = QTextStream( file )
• txtAll = stream.readAll() ou txtLine = stream.readLine()
• stream.write
QTextStream
QTextStream
- lecture ou écriture de texte depuis un QFile :
• stream = QTextStream( file );
- méthodes utiles :
• QString readLine( taillemax = 0 );
• QString readAll( );
// pas de limite de taille si = 0
// pratique mais à n’utiliser que pour des petits fichiers
QtGUI
QStyle
• main(args)
app = QApplication( args)
./python monAppli –style plastique
Ex: windows, motif, platinum,…
QMainWindow
- Méthode 1: créer une instance de QMainWindow
win = QMainWindow()
win.resize(200,300)
- Méthode 2: créer une sous-classe de QMainWindow
class Win(QMainWindow):
def __init__(self): self.resize(200,300)
QMainWindow :
Menus
•
bar = self.menuBar()
•
fileMenu = bar.addMenu( "File" )
•
•
•
•
newAct = QAction(QIcon("path/images/new.png"), “New…”, self )
newAct.setShortcut( “Ctrl+N” )
newACT.setToolTip(self.tr(“New File”))
newAct.setStatusTip(self.tr(“New file”))
•
fileMenu.addAction(newAct)
•
newAct.triggered.connect( self.new )
si sous-classe (methode 2)
sinon win.menuBar() (methode 1)
QMainWindow
• QMenuBar, QMenu, QAction
• QToolBar
–
–
–
–
fileToolBar = QToolBar("File")
fileToolBar.addAction(newAct)
mainWindow.addToolBar(fileToolBar)
newAct.setEnabled( false )
• QToolTip, QWhatsThis
• Composant central
textEdit = TextEdit( self );
self.setCentralWidget( textEdit );
grise la commande dans les menus et la toolbar
Buttons
57
Input Widgets
58
Containers
QMidArea
QScrollArea
QTabWidget
QGroupBox
QToolBox
QWidget; QFrame; QDockWidget; QStackedWidget
59
Views
QCompleter
60
standard widgets use data that is part of the widget
View classes operate on external data (the model)
61
class MyModel(QAbstractTableModel):
def __init__(self):
QAbstractTableModel.__init__(self)
self.myData = <dataBase>
def rowCount( self, parent ):
return 2
#Type (parent) == QModelIndex
def columnCount( self, parent ):
return 2
def data( self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return self.myData(index.row() + 1, index.column()+1)
def main(args):
app = QApplication(args)
tableView = QTableView()
myModel = MyModel()
tableView.setModel( myModel )
tableView.show()
app.exec()
62
Display Widgets
63
Boite de dialogue
QProgressDialog
QFileDialog
QMessageBox
QColorDialog
QFontDialog
Boîte de dialogue modale
Solution simplifiée
fileName = QFileDialog.getOpenFileName( self,
"Open Image”,
"/home/jana",
"*.txt")
fileName = QFileDialog.getSaveFileName(…)
//parent
// titre
// répertoire initial
// filtre
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( );
countryList.insertItem( "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()
container.setLayout( top_layout )
win.setCentralWidget(container)
win.show( )
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
Arbre d’héritage
Principaux widgets : Arbre d’héritage
QCheckBox
QWidget
QAbstractButton
QRadioButton
QToolButton
QPushButton
Hiérarchie de classes (type)
- chaque sous-classe hérite des variables et méthodes de sa superclasse
- du plus général au plus particulier
- héritage simple
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’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
Modules
•
•
•
•
•
•
•
•
•
•
•
QtCore
QtWidgets
QtBluetooth
QtOpenGL
QtSript/QtScriptTools
QtSql
QtSvg
QtWebKit
QtXml/QtXmlPatterns
QtMultimedia
QtSensors
QtNetwork
• QFtp, QHttp, QTcpSocket, QUdpSocket
OpenGL : Box3D
#include <QGLWidget>
class Box3D : public QGLWidget {
Q_OBJECT
GLuint object;
GLfloat rotX, rotY, rotZ;
public:
Box3D( QWidget *parent = 0);
virtual ~Box3D();
protected:
virtual void initializeGL();
virtual void paintGL();
virtual void resizeGL( int w, int h );
virtual GLuint makeObject();
public slots:
void setRotationX(int deg) { rotX = deg; updateGL( ); }
void setRotationY(int deg) { rotY = deg; updateGL( ); }
void setRotationZ(int deg) { rotZ = deg; updateGL( ); }
};
modules
•
•
•
•
•
•
•
•
•
•
•
QtCore
QtGui
QtNetwork
QtOpenGL
QtSript/QtScriptTools
QtSql
QtSvg
QtWebKit
QtXml/QtXmlPatterns
Phonon
Qt3Support
Questions
• Quelle est la différence entre l’arbre d’héritage et l’arbre
d’instanciation?
• héritage de classe (A hérite de B)
• héritage d’instances (A contient B)
• Quelle classe utiliser pour créer une fenêtre / Comment ajouter le
widget central?
• QMainWindow
• win.setCentralWidget( myWidget )
• Quels sont les différents layouts?
• QVBoxLayout; QHBoxLayout; QGridLayout; QFormLayout
• Quelle classe pour un bouton?
• QPushButton
Outils Qt
Les outils Qt
•
•
•
•
•
•
Qt Creator
Qt Assistant
Qt Examples
QtDemo
Qt resources
Qt Designer
The Qt Resource System
• Mechanism for storing binary files in the application’s
executable
• Useful if your application needs a certain set of files
(icons, translation files) and you don’t want to run the risk
of losing the files
resources.qrc
• How to use:
• pyrcc5 - o resources.py resources.qrc
• import resources
83
Qt Designer
84
Qt Designer
• sauvegarder le projet en dialog.ui (fichier xml)
• transformer en fichier python:
pyuic5 dialog.ui -o dialog.py
• Insérer la boite de dialogue dans votre code
import dialog
class Dialog(QDialog, dialog.UI_Dialog):
def __init__(self, parent = None):
super(Dialog, self).__init__(parent)
self.setupUI(self)
85
Download