Python et Qt. Pierre Puiseux



Documents pareils
Modifier les propriétés d'un widget

Modéliser ses fenêtres avec Qt Designer

Warren PAULUS Robin GODEFROID. C++ - Interface Graphique avec Visual Studio 2010

L'architecture MVC avec les widgets complexes

AUVRAY Clément (168187) HOMBERGER Alexandre (186897) GLADE. Langages, outils et méthodes pour la programmation avancée Page 1 sur 12

Avant-propos FICHES PRATIQUES EXERCICES DE PRISE EN MAIN CAS PRATIQUES

L espace de travail de Photoshop

Optimiser pour les appareils mobiles

Modéliser ses fenêtres avec Qt Designer

Atelier Le gestionnaire de fichier

L informatique en BCPST

iil est désormais courant de trouver sur Internet un document

PRISE EN MAIN D ILLUSTRATOR

EXCEL TUTORIEL 2012/2013

Installation d un ordinateur avec reprise des données

BIRT (Business Intelligence and Reporting Tools)

TP1 - Prise en main de l environnement Unix.

Guide de l utilisateur. Faites connaissance avec la nouvelle plateforme interactive de

Note de cours. Introduction à Excel 2007

L ORDINATEUR FACILE D ACCÈS!

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

Prise en main rapide

COURS WINDEV NUMERO 3

0.1 Mail & News : Thunderbird

1) Installation de Dev-C++ Téléchargez le fichier devcpp4990setup.exe dans un répertoire de votre PC, puis double-cliquez dessus :

Créer et partager des fichiers

Tutoriel. Votre site web en 30 minutes

< Atelier 1 /> Démarrer une application web

Boot Camp Guide d installation et de configuration

Introduction à la présentation graphique avec xmgrace

INSERER DES OBJETS - LE RUBAN INSERTION... 3 TABLEAUX

La programmation orientée objet et le langage C++

Installation d OpenVPN

Mon aide mémoire traitement de texte (Microsoft Word)

Notes pour l utilisation d Expression Web

Ateliers Python+Qt : Premiers pas : Comment développez ses propres interfaces graphiques sur le RaspberryPi?

Manuel de System Monitor

Préconisations Techniques & Installation de Gestimum ERP

Programmation Web. Madalina Croitoru IUT Montpellier

26 Centre de Sécurité et de

Création et utilisation de formulaire pdf

Tutoriel code::blocks

Guide de l utilisateur Mikogo Version Windows

Guide d installation UNIVERSALIS 2016

Construire des plug-ins pour SAS Management Console SAS 9.1

Tutoriel QSOS. Version /02/2013

PerSal Manuel d installation

Formation. Module WEB 4.1. Support de cours

SYSTRAN 7 Guide de démarrage

La base de données dans ArtemiS SUITE

Introduction à l informatique en BCPST

145A, avenue de Port Royal, Bonaventure (Québec) G0C 1E0 Sans frais :

Manuel d utilisation du site web de l ONRN

Ateliers Python+Qt : Premiers pas : S'installer pour PyQt... en quelques minutes sous Windows!

Service Informatique et Télématique (SITEL), Emile-Argand 11, 2009 Neuchâtel, Tél ,

Les 1 er pas sur. Guide d utilisation

The Grid 2: Manuel d utilisation

Eclipse atelier Java

GESTION DU LOGO. 1. Comment gérer votre logo? Format de l image Dimensions de l image Taille de l image 9

Manuel d utilisation 26 juin Tâche à effectuer : écrire un algorithme 2

Modélisation et Gestion des bases de données avec mysql workbench

Le logiciel de création de site internet IZISPOT est un outil très puissant et qui est assez simple après quelques temps d utilisation.

Table des matières L INTEGRATION DE SAS AVEC JMP. Les échanges de données entre SAS et JMP, en mode déconnecté. Dans JMP

Comment faire des étiquettes

NETWORK & SOFTWARE ENGINEERING MANUEL D UTILISATEUR. Logiciel TIJARA. NETWORK AND SOFTWARE ENGINEERING Manuel d'utilisateur "TIJARA" 1

Universalis Guide d installation. Sommaire

Le langage C. Séance n 4

Numérisation. Copieur-imprimante WorkCentre C2424

Importation et exportation de contenu

TP1 : Initiation à Java et Eclipse

Guide d installation UNIVERSALIS 2014

Gestion des documents avec ALFRESCO

Access et Org.Base : mêmes objectifs? Description du thème : Création de grilles d écran pour une école de conduite.

Introduction à Eclipse

Découvrez Windows NetMeeting

Guide d installation de MySQL

Introduction à Expression Web 2

Manuel de l'utilisateur d'intego VirusBarrier Express et VirusBarrier Plus

INTRODUCTION AU CMS MODX

Fiche Mémo : Options d accessibilité sous Windows et Internet Explorer 5

AIDE à l utilisation du cédérom «L athlétisme à l école» Niveau Primaire SOMMAIRE

Support pour les langues s écrivant de droite à gauche

GUIDE D UTILISATION DU LOGICIEL DE TELE-MAINTENANCE. TEAM VIEWER Version 7.

Installation et paramétrage. Accès aux modèles, autotextes et clip- art partagés

WINDOWS SHAREPOINT SERVICES 2007

Créer le schéma relationnel d une base de données ACCESS

Créer un premier document avec Pages

Fonction Memory Viewer

MO-Call pour les Ordinateurs. Guide de l utilisateur

Connected to the FP World


Utilisation de l éditeur.

2013 Pearson France Adobe Illustrator CC Adobe Press

1. Introduction Création d'une macro autonome Exécuter la macro pas à pas Modifier une macro... 5

Réalisez votre propre carte de vœux Éléctronique

Modem LG LDU-1900D. Guide d utilisateur. LG Electronics

Reporting Services - Administration

Découverte du logiciel ordinateur TI-n spire / TI-n spire CAS

PRESENTATION DE LA SOLUTION. CybEx E_Trade

VOCABULAIRE LIÉ AUX ORDINATEURS ET À INTERNET

Transcription:

Python et Qt Pierre Puiseux

Table des matières Chapitre 1. Présentation 7 1.1. Interfaces Graphiques (GUI) 8 1.2. Les bibliothèques PyQt4, PySide 11 1.3. Naviguer dans la docs Qt4 13 Chapitre 2. Première fenêtre 15 2.1. Préliminaires 15 2.2. Code minimal 16 2.3. Affichage d un widget 17 Chapitre 3. Modifier les propriétés d un widget 19 3.1. Les accesseurs avec PyQt4 20 3.2. Quelques exemples de propriétés des boutons 20 3.3. Qt4 et l héritage 22 3.4. Un widget peut en contenir un autre 23 Chapitre 4. Le principe des signaux et slots 29 4.1. Connexion d un signal à un slot simple 30 4.2. Des paramètres dans les signaux et slots 33 Chapitre 5. Boîtes de dialogue Qt4usuelles 39 5.1. Le code de base 39 5.2. QMessageBox pour afficher un message. 40 5.3. Les applications Qt4peuvent être traduites 44 5.4. Utiliser les valeurs de retour : 45 5.5. QInputDialog pour saisir une information 45 5.6. QFontDialog pour sélectionner une police 48 5.7. QColorDialog pour sélectionner une couleur 49 5.8. QFileDialog pour sélectionner un (des) fichier(s) ou un dossier 49 Chapitre 6. Les layouts 55 6.1. Positionnement absolu 55 6.2. L utilisation d un layout 57 6.3. Les layouts horizontal et vertical QHBoxLayout et QVBoxLayout 57 6.4. Le layout de grille QGridLayout 58 6.5. Le layout de formulaire QFormLayout 59 6.6. Combiner les layouts 61

4 Chapitre 0. Table des matières Chapitre 7. Les principaux widgets Qt4 63 7.1. Fenêtres et widgets 63 7.2. La classe QWidget 66 7.3. Les boutons 67 7.4. Les afficheurs 71 7.5. Les widget de saisie 71 7.6. Les conteneurs 72 Chapitre 8. La Fenêtre principale d une application 73 8.1. Présentation de QMainWindow 73 8.2. La zone centrale (SDI et MDI) 75 8.3. Les menus 76 8.4. La barre d outils 78 8.5. Les docks 79 8.6. La barre d état (statusbar) 79 Chapitre 9. Le designer Qt4 81 9.1. Présentation de designer 81 9.2. Les layouts 83 9.3. Editer les propriétés des widgets 84 9.4. Configurer les signaux et les slots 85 9.5. Inspecter les widgets 86 9.6. Exemple 86 9.7. Générer une classe Python à partir d un fichier designer-qt4 88 9.8. Utiliser un widget de designer dans notre application 88 9.9. Auto-connect 92 9.10. Compléments 92 Chapitre 10. Programmation Modèle-Vue-Contrôleur (MVC) 95 10.1. Architecture Modèle/Vue 95 10.2. Using Models and Views 98 10.3. Les classes QStandardItemModel et QStandardItem 99 10.4. Model Classes 100 10.5. Création de Nouveaux Modèles 106 10.6. View Classes 114 10.7. Handling Selections of Items 114 10.8. Les classes déléguées 115 10.9. Item View Convenience Classes 119 10.10. Using Drag and Drop with Item Views 119 10.11. Proxy Models 120 10.12. Model Subclassing Reference 120 Chapitre 11. Quelques exemples 127 11.1. Rediriger la sortie standard dans un QTextBrowser 127

11.2. Créer et utiliser menu contextuel 127 11.3. Saisir les coordonnées de la souris dans une image bitmap 127 5

CHAPITRE 1 Présentation Ce tutoriel est basé sur l excellentissime tutoriel C++ http://www.siteduzero.com/tutoriel-3-11240-introduction-a-qt.html adapté aux spécificités de Python et PyQt4. Prérequis : POO (Programmation Orientée Objet) et Python.

8 Chapitre 1. Présentation 1.1. Interfaces Graphiques (GUI) 1 #! /usr/bin/python 2 # -*- coding : utf-8 -*- 3 import sys 4 5 def main(args) : 6 print 'programme sans GUI' 7 rep = raw_input ('votre nom? ') 8 print rep 9 10 if name == " main " : 11 main(sys.argv) Fig. 1.1.1. Sans GUI

1.1 Interfaces Graphiques (GUI) 9 Fig. 1.1.2. Avec GUI Il existe pléthore de boites à outils pour le développement d interfaces graphiques : Bibliothèque OS Licence Bindings Qt4 Windows, Mac, GNU/Linux LGPL C++, Python, Java, Ruby, C# 1 FLTK Windows, Mac, GNU/Linux LGPL2 C++, Ruby, Python 2 FOX Windows, Mac, GNU/Linux LGPL C++, Python, Ruby 3 GTK+ Windows, Mac, GNU/Linux LGPL2.1 C++, C#, Java, Python, Ruby 4 Ultimate++ Windows, Mac, GNU/Linux C++ M$FC Windows Visual Studio obligatoire Tkinter Windows, Mac, GNU/Linux Python License Python VCF Windows BSD License C++, Lua WideStudio Windows, Mac, GNU/Linux très permissive C++, Java, Perl, Ruby, Python, 5 wxwidgets Windows, Mac, GNU/Linux LGPL + C++, Python, perl, java, C# 6 1.1.1. Les bibliothèques propres aux OS. Chaque OS propose au moins une bibliothèque qui permet de créer des fenêtres. Windows : API Win32. L API Win32 est un ensemble de fonctions ; bibliothèque complexe ; utilisable dans tous les langages (C, C++, Java, Python...) non orientée objet. Il existe une surcouche, MFC, orientée objet. Maintenant remplacée par la bibliothèque.net multi-plateforme. MacOS X : Cocoa. Objective C, java, Python orientée objet. Linux :

10 Chapitre 1. Présentation X = base des interfaces graphiques de Linux. Xlib, mais on programme rarement en Xlib. plus simple d utilisation et multi-plateforme : GTK+ ou Qt4. 1.1.2. Les bibliothèques multi-plateforme..net (prononcez Dot Net ) : successeur de Win32. Souvent associé à C#, langage créé par Microsoft. NET est portable car Microsoft a expliqué son fonctionnement..net porté sous Linux avec Mono. GTK+ : une des plus importantes bibliothèques utilisées sous Linux. Elle est portable : Linux, MacOS et Windows. GTK+ est utilisable en C, en C++(GTKmm). Très utilisée par Gnome, mais fonctionne aussi sous KDE. =>Firefox par exemple. Qt4 : très utilisée sous Linux, en particulier KDE. à partir de la version 4.5, licence LGPL v2.1[ wxwidgets : bibliothèque objet très complète, comparable à Qt4. Licence plus ouverte que celle de Qt4. Mais Qt4 plus facile à prendre en main au début. FLTK : légère. Dédiée à la création d interfaces graphiques multi-plateforme.

1.2 Les bibliothèques PyQt4, PySide 11 1.2. Les bibliothèques PyQt4, PySide Qt4 est une bibliothèque multi-plateforme pour créer des GUI (programme sous forme de fenêtre). Qt4 écrite en C++, mais il existe des bindings Java, Python, etc. PyQt4 est GPL, écrit par riverank : http://www.riverbankcomputing.co.uk/ Janvier 2008 : Nokia rachète Qt4 Le 18 août 2009, Nokia distribue ses propres bindings Python de Qt4 : PySide, sous licence LGPL. Les fonctions de PySide et PyQt4 sont presque rigoureusement identiques. PySide est plus jeune, mais sa licence est plus permissive. 1.2.1. Plus qu une bibliothèque : un framework. Qt4 est à la base faite pour créer des fenêtres, c est sa fonction centrale. Mais ce serait dommage de limiter Qt4 à ça. Les modules Qt4 : Module GUI : c est toute la partie création de fenêtres. Nous nous concentrerons surtout sur le module GUI dans ce cours. Module OpenGL : Qt4 peut ouvrir une fenêtre contenant de la 3D gérée par OpenGL. Module de dessin : pour dessiner dans leur fenêtre (en 2D), module de dessin très complet! Module réseau : pour accéder au réseau, Chat, client FTP, client Bittorent, lecteur de flux RSS... Module SVG : création images et animations vectorielles, à la manière de Flash. Module de script : Qt4 supporte le Javascript (ou ECMAScript), Module XML : échanger des données avec des fichiers formés à l aide de balises, un peu comme le XHTML. Module SQL : accès aux bases de données (MySQL, Oracle, PostgreSQL...). Avec l évolution de Qt4, d autres modules sont conçus : QtDBus : pour la communication inter-processus en utilisant D-Bus (uniquement sous Unix à partir de Qt4.2) ; QtSvg : pour l affichage d images aux formats SVG (à partir de Qt4.1) ; QtUiTools : pour charger dynamiquement les interfaces graphiques créées avec QtDesigner (à partir de Qt4.1) ; QtTest : pour effectuer des tests unitaires (à partir de Qt4.1) ; QtScript : pour l évaluation de scripts utilisant QtScript (à partir de Qt4.3) ; QtWebKit : portage du moteur de rendu web WebKit (à partir de Qt4.4) ; QtXmlPatterns : pour manipuler des documents XML via XQuery et XPath (à partir de Qt4.4) ; Phonon : intégration de Phonon, framework multimédia de KDE4, développé en collaboration avec la communauté KDE (à partir de Qt4.4) ; QtScriptTools : divers outils pour QtScript comme un débogueur (à partir de Qt4.5). Qt4 n est pas gros, Qt4 est énorme, sa documentation est très bien faite.

12 Chapitre 1. Présentation 1.2.2. Qt4 est multiplateforme. 1.2.3. Qui utilise Qt4? Qt4 est utilisée par de nombreuses entreprises que vous connaissez sûrement : Adobe, Boeing, Nasa, Skype, Google, AMD, Volvo, Xerox, Epson, Qt4 est utilisée pour réaliser de nombreux GUI, comme : les interfaces des portables Nokia, Adobe, Photoshop Elements, GoogleEarth, Skype, VLC media player, opera, Lyx 1.2.4. Programmes installés par Qt4. (1) Qt4 Examples and Demos $ qtdemo (2) Qt4 assistant $ assistant-qt4 (3) Qt4 linguist $ linguist-qt4 (4) Qt4 Designer $ designer-qt4

1.3 Naviguer dans la docs Qt4 13 1.2.5. Programmes installés par PyQt4 et PySide. (1) Demo : /usr/share/doc/python-qt4-doc/examples/tools/qtdemo/qtdemo.py (2) pyuic4 pour traduire les fichiers designer (.ui) en Python (3) TODO pylupdate4 - create or update Qt4Linguist translation files for PyQt4 applications pylupdate4 reads a qmake project file (.pro), finds the translatable strings in the specified sources and updates the translation files (.ts files) specified in it. The translation files are given to the translator who can use Qt4Linguist to read the files and insert the translations. The.ts file format is a simple human-readable XML format that can be used with version control systems if required. (4) pyrcc4 compile les fichiers ressources Qt4 pour les applications PyQt4 ; (5) pyside-uic (6) pyside-lupdate (7) pyside-rcc4 Lancer assistant ou bien assistant-qt4. 1.3. Naviguer dans la docs Qt4 Les classes principales. Les classes les plus courament utilisées, et pour chacune d entre elles : ses propriétés ses méthodes ses slots ses signaux ses propriétés, méthodes, slots, signaux hérités Tutoriels, exemples and démonstrations. Tutoriels : prise de contact Démonstration : plus consistantes Examples : utilisation, cas réel. Le designer-qt4. Toutes les propriétés (y compris les propriétés héritées) Tous les signaux disponibles Tous les slots prédéfinis 1.3.1. Key Technologies. Explications approfondies sur un aspect particulier 1.3.2. Tools. Description des outils autours de Qt4.

CHAPITRE 2 Première fenêtre 2.1. Préliminaires Dans ce chapitre, nous réaliserons notre premier programme utilisant PyQt4 et nous verrons comment ouvrir notre première fenêtre. Les lignes de codes sont présentées sous forme de scripts Python, mais il est possible, voir souhaitable de les adapter pour les exécuter sous forme interactive dans un shell Python. Par exemple, la première fenêtre présentée ci-dessous peut fort bien se coder dans un shell Python interactif de la manière suivante : 1 >>> from PyQt4.QtGui import QApplication 2 >>> a = QApplication([]) 3 >>> a.exec_() Le problème est que cette suite d instructions gèle le prompt, Python n est plus accessible. Un Ctrl+Z vous sortira de ce mauvais pas. Le processus Python n est pas tué, il est simplement inactif et inaccessible, il vous faut le tuer à la main : $ ps -u pierre... 8182? 00 :00 :21 soffice.bin 10070 pts/0 00 :00 :00 \Python 12004 pts/1 00 :00 :00 bash 12075 pts/1 00 :00 :00 ps $ kill -9 10070 Une autre solution, plus propre, est de lancer un shell Python de la manière suivante : $ ipython -q4thread In [1] : from PySide.QtGui import QApplication In [2] : w = QWidget() In [3] : w.show() In [4] :

16 Chapitre 2. Première fenêtre 2.2. Code minimal Voici le code qui semble ne pas fonctionner mais qui fonctionne : 1 #! /usr/bin/python 2 # -*- coding : utf-8 -*- 3 from PySide.QtGui import QApplication 4 import os,sys 5 6 def main(args) : 7 a = QApplication(args) 8 r = a.exec_() 9 return r 10 11 if name == " main " : 12 main(sys.argv) (1) On indique au shell où se trouve l exécutable en charge de faire tourner le script (On aura rendu main.py exécutable par la commande chmod +x main.py.) (2) Cette ligne indique à Python que le codage des caractères est en utf-8. A défaut l interpréteur refusera d exécuter le script s il contient des accents (même en commentaires). (3) Cette importation permet d accéder à la classe QApplication, qui est la classe de base de tout programme PyQt4. (4) Pour accéder aux arguments de la ligne de commande (5) (6) (7) Instanciation d une QApplication. 1 Cet objet est appelé a. Le constructeur de QApplication exige des arguments, ici on lui passe les paramètres sys.argv passés en ligne de commande. (8) Lancement de l application par appel à la méthode exec_ de notre objet a. Cette méthode démarre notre programme. 2 (9) retourne le résultat de app.exec_() pour dire si le programme s est bien déroulé ou pas. (10) (11) Lorsque le script est lancé de puis la ligne de commande bash, la variable globale name prend la valeur " main " donc main() est exécuté, sinon, si le fichier est utilisé comme module dans un programme (ou en ligne de commande) Python par 1 >>> import main 2 >>> main() 1 C est à dire création un nouvel objet de type QApplication. 2 la fonction s appelle exec en Qt4-C++et exec_ en PyQt4 pour la simple raison que exec est un mot clé Python.

2.3 Affichage d un widget 17 2.3. Affichage d un widget Dans la plupart des bibliothèques GUI, dont PyQt4 fait partie, tous les éléments d une fenêtre sont appelés des widgets. Les boutons, les cases à cocher, les images... sont des widgets. La fenêtre elle-même est considérée comme un widget. Pour provoquer l affichage d une fenêtre, il suffit de demander à afficher n importe quel widget. Ici par exemple, nous allons afficher un bouton. 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide import QtGui 4 import os,sys 5 6 def main(args) : 7 a = QtGui.QApplication(args) 8 bouton = QtGui.QPushButton(u"Bonjour sans accents", None) 9 bouton.show() 10 r = a.exec_() 11 return r 12 Les lignes ajoutées : 13 if name == " main " : 14 main(sys.argv) bouton=qpushbutton(u"bonjour sans accents",none) : instanciation d un QPushButton. Le paramètre est le texte affiché sur le bouton. Vous noterez que sous PyQt4 toutes les classes commencent par un Q. Afin d afficher correctement le texte avec des accents, le texte passé en argument est de type unicode. C est le type Python2.6 le plus proche du type QString. bouton.show() commande l affichage du bouton. PyQt4 l insère automatiquement dans une fenêtre.

CHAPITRE 3 Modifier les propriétés d un widget Le fichier de base (forme script) : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PyQt4.QtGui import (QWidget, QFont, 4 QPushButton, QApplication, 5 QIcon) 6 from PyQt4 import QtCore 7 import os,sys 8 9 def main(args) : 10 a = QApplication(args) 11 # Creation d'un widget qui servira de fenetre 12 fenetre = QWidget() 13 fenetre.setfixedsize(300, 150) 14 # Creation du bouton, ayant pour conteneur la "fenetre" 15 bouton = QPushButton(u"Bonjour mesdames! ",fenetre) 16 # Customisation du bouton 17 fenetre.show() 18 r = a.exec_() 19 return r 20 21 if name == " main " : 22 main(sys.argv) Comme tous les éléments d une fenêtre, on dit que le bouton est un widget. Avec PyQt4, on crée un bouton à l aide de la classe QPushButton. Une classe est constituée de 2 éléments : Des attributs : ce sont les variables internes de la classe. Des méthodes : ce sont les fonctions internes de la classe. Un dogme fondateur de la POO stipule que les utilisateurs d une classe ne doivent pas pouvoir en modifier les attributs. Ceci ne s applique pas formellement en Python, mais il est bon de le supposer.

20 Chapitre 3. Modifier les propriétés d un widget Comme utilisateurs des classes de PyQt4, nous n avons théoriquement pas accès aux attributs puisque ceux-ci sont privés. Les programmeurs doivent donc fournir un getter et un setter (get et set). 3.1. Les accesseurs avec PyQt4 Chaque propriété d un widget, correspond à un attribut privé. Exemple : text Un accesseur pour le lire (getter) : cet accesseur est une méthode qui porte le même nom que l attribut. Exemple : text() Un accesseur pour le modifier (setter) : c est une méthode qui se présente sous la forme setattribut(). Elle modifie la valeur de l attribut. Exemple : settext() 3.2. Quelques exemples de propriétés des boutons Chaque widget a de nombreuses propriétés éditables bien documentées dans trois sources : assistant-qt4 (doc C++recommandée) la doc PySide ou PyQt4 le properties-editor de designer-qt4 : Sur la doc C++(assistant), au chapitre QPushButton, le lien List of all members, including inherited members donne toutes les propriétés et méthodes disponibles, y compris celles qui sont héritées. propriété : QString text : le texte présent sur le bouton getter : QString text()const setter : void settext(const QString & text) Opérons les modifications suivantes : modifier le texte du bouton après sa création : bouton.settext(u"salut les filles!") création d un tooltip, qui apparaît lorsque la souris stationne assez longtemps sur le bouton : bouton.settooltip(u"aide à rien!") la police du bouton et ses attributs : : ce bouton ne sert strictement bouton.setfont(qfont("comic Sans MS", 20), QFont.Bold, True) l apparence du pointeur de la souris lorsqu il passe sur le bouton :

3.2 Quelques exemples de propriétés des boutons 21 bouton.setcursor(qtcore.qt.pointinghandcursor) l icône du bouton : bouton.seticon(qicon("../../images/smile.png")) Finalement, on obtient le programme suivant : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import QApplication, QPushButton, QFont, QIcon 4 from PySide.QtCore import Qt 5 import os,sys 6 7 def main(args) : 8 a = QApplication(args) 9 bouton = QPushButton(u"Hello, world!",none) 10 bouton.settext(u"salut les filles!") 11 bouton.settooltip(u"aide : ce bouton ne sert strictement à rien!") 12 bouton.setfont(qfont("comic Sans MS", 20,QFont.Bold,True)) 13 bouton.setcursor(qt.pointinghandcursor) 14 bouton.seticon(qicon("./smile.png")) 15 bouton.show() 16 r = a.exec_() 17 return r 18 19 if name == " main " : 20 main(sys.argv) avec pour résultat

22 Chapitre 3. Modifier les propriétés d un widget 3.3. Qt4 et l héritage L héritage est probablement LA notion la plus intéressante de la programmation orientée objet. Le fait de pouvoir créer une classe de base, réutilisée par des sous-classes filles, qui ont ellesmêmes leurs propres sous-classes filles, ça donne à une bibliothèque comme Qt4 une puissance infinie. En fait... quasiment toutes les classes de PyQt4 font appel à l héritage. 3.3.1. QObject : une classe de base incontournable. QObject est la classe de base de tous les objets sous Qt4. La classe QObject propose quelques fonctionnalités de base utiles à toutes les autres classes. C est dans QObject qu est implémenté le mécanisme des signaux et des slots qu on verra dans le prochain chapitre. Fig. 3.3.1. Schéma simplifié de quelques héritages de Qt4

3.4 Un widget peut en contenir un autre 23 La classe QWidget contient des propriétés communes à tous les widgets, comme : La largeur width La hauteur height La position en abscisse (x) La position en ordonnée (y) La police de caractères utilisée font Le curseur de la souris cursor L infobulle (tooltip) etc. 3.3.2. Les classes abstraites. QAbstractButton en rouge... Pourquoi? Il existe en fait un grand nombre de classes abstraites sous Qt4, qui contiennent toutes le mot Abstract dans leur nom. Les classes dites abstraites sont des classes qu on ne peut pas instancier en C++ni en PyQt4. 1 Ainsi, on ne peut pas faire : >>> bouton = QAbstractButton() On reçoit le message d erreur de Python : TypeError : QtGui.QAbstractButton represents a C++ abstract class and cannot be instantiated QAbstractButton définit un certain nombre de propriétés communes à tous les types de boutons (boutons classiques, cases à cocher, cases radio...). Par exemple, parmi les propriétés communes on trouve : text : le texte affiché ; icon : l icône affichée à côté du texte du bouton ; shortcut : le raccourci clavier pour activer le bouton ; down : indique si le bouton est enfoncé ou non etc.. Ces propriétés ne sont définies qu une fois dans QAbstractButton, et on le retrouve ensuite automatiquement dans QPushButton, QCheckBox etc., qui héritent de QAbstractButton. 3.4. Un widget peut en contenir un autre Nous attaquons maintenant une notion importante, celle des widgets conteneurs. 1 PySide permet l instanciation...

24 Chapitre 3. Modifier les propriétés d un widget 3.4.1. Contenant et contenu. Par exemple, une fenêtre (un QWidget) peut contenir 3 boutons (QPushButton), une case à cocher (QCheckBox), une barre de progression (QProgressBar ), etc. Ce n est pas là de l héritage, mais une affaire de contenant et de contenu. Dans la suite j utiliserai le terme conteneur pour désigner ce que la doc Qt4 appelle le parent Prenons un exemple : QWidget (la fenêtre) QPushButton cancel QPushButton OK QTabWidget (le conteneur à onglets) QPushButton QCheckBox QProgressBar 24% 3.4.2. Créer une fenêtre contenant un bouton. Créons une fenêtre fenetre = QWidget (), puis un bouton contenu dans la fenêtre bouton = QPushButton(u"Bonjour mesdames Le programme complet devient : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*-! ", fenetre) 3 from PySide.QtGui import QApplication, QPushButton, QFont, QIcon 4 from PySide.QtCore import Qt 5 import os,sys 6

3.4 Un widget peut en contenir un autre 25 7 def main(args) : 8 a = QApplication(args) 9 bouton = QPushButton(u"Hello, world!",none) 10 bouton.settext(u"salut les filles!") 11 bouton.settooltip(u"aide : ce bouton ne sert strictement à rien!") 12 bouton.setfont(qfont("comic Sans MS", 20,QFont.Bold,True)) 13 bouton.setcursor(qt.pointinghandcursor) 14 bouton.seticon(qicon("./smile.png")) 15 16 bouton.show() 17 r = a.exec_() 18 return r 19 20 if name == " main " : 21 main(sys.argv) A noter aussi la méthode setgeometry, qui prend 4 paramètres : bouton.setgeometry(abscisse, ordonnee, largeur, hauteur) Exercice 1. On peut placer un bouton à l intérieur du premier bouton, comme ceci :

26 Chapitre 3. Modifier les propriétés d un widget 3.4.3. Hériter un widget. Jusqu ici, nous avons : Appris à lire et modifier les propriétés d un widget, en voyant quelques exemples de propriétés des boutons. Découvert de quelle façon étaient architecturées les classes de Qt4, avec les multiples héritages. Découvert la notion de widget conteneur. Nous allons ici créer une nouvelle classe héritant de QWidget représentant notre fenêtre. Ce qui donnera une plus grande souplesse par la suite. L héritage sera donc le suivant : Pratiquement, on obtient un code comme celui-ci : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*-

3.4 Un widget peut en contenir un autre 27 3 from PySide.QtGui import (QApplication, QWidget, QPushButton) 4 from PySide.QtCore import Qt, SIGNAL 5 import os,sys 6 class MaFenetre(QWidget) : 7 """Une fenetre test""" 8 def init (self, larg, haut) : 9 QWidget. init (self) 10 self.setfixedsize(larg,haut) 11 self.bouton = QPushButton('un bouton',self) 12 13 if name == ' main ' : 14 a = QApplication(sys.argv) 15 try : larg, haut = sys.argv[],sys.argv[2] 16 except IndexError : larg, haut = 800, 600 17 mf = MaFenetre(larg, haut) 18 mf.show() 19 a.exec_() On a autorisé deux arguments sur la ligne de commande pour que l utilisateur puisse préciser la largeur et la hauteur de la fenêtre. Le programme s appellera par la commande $ python mafenetre.py 500 2007 Le contenu de la classe est très simple. (6) class MaFenetre(QWidget) : la classe MaFenetre hérite de QWidget. Nous récupérons donc automatiquement toutes les propriétés de QWidget. (9) QWidget. init (self) le constructeur ( init ()) appelle explicitement le constructeur de la classe parent (avec les paramètres par défaut). (10) self.setfixedsize(larg, haut) : on définit la taille de la fenêtre et on interdit son redimensionnement. (12) bouton = QpushButton(u"un bouton", self) : instanciation du bouton. Le second paramètre du constructeur est le widget conteneur : la fenêtre elle-même self. 3.4.4. Destructeur. Quand on supprime un widget conteneur (ici notre fenêtre), Qt4supprime automatiquement tous les widgets qui se trouvent à l intérieur (tous les widgets contenus). C est un des avantages d avoir dit que le QPushButton avait pour conteneur la fenêtre. 2 2 Au cas (improbable) où Qt4 le ferait mal, le Garbage Collector de Python se chargerait de la besogne.

CHAPITRE 4 Le principe des signaux et slots Fichier de base 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import (QPushButton, QFont, 4 QWidget, QApplication, QIcon) 5 from PySide.QtCore import QObject, SIGNAL, Qt 6 import os,sys 7 8 class MaFenetre(QWidget) : 9 def init (self) : 10 QWidget. init (self) 11 self.setfixedsize(300, 150) 12 self.bouton = QPushButton(u"Quitter", self) 13 self.bouton.move(70, 50) 14 15 def quit(self) : 16 exit(0) 17 18 def autre(self) : 19 print "autre" 20 21 if name ==" main " : 22 a = QApplication(sys.argv) 23 fenetre = MaFenetre() 24 fenetre.show() 25 r = a.exec_() C est un concept inventé par Qt4. Un signal : : c est un message envoyé par un widget lorsqu un évènement se produit. Par exemple on a cliqué sur un bouton, le bouton envoit le signal "clicked()" Un slot : : c est la fonction qui est appelée lorsqu un évènement s est produit. Concrètement, un slot est une méthode d une classe. Par exemple le slot QApplication.quit() de la classe QApplication, qui provoque l arrêt du programme.

30 Chapitre 4. Le principe des signaux et slots En PyQt4, les slots sont des simples méthodes de classes. En Python, contrairement au C++, il n y a pas lieu de distinguer slot, méthodes et fonctions. les signaux sont des objets Python. On dit que l on connecte des signaux et des slots entre eux. Sur le schéma ci-dessus, on a connecté le signal 1 de l objet 1 avec le slot 2 de l objet 2. Il est aussi possible de connecter un signal à un autre signal. 4.1. Connexion d un signal à un slot simple Voyons un cas très concret.

4.1 Connexion d un signal à un slot simple 31 Nous voudrions par exemple connecter le signal «bouton cliqué» au slot «quitter l application». Pour ce faire, nous devons utiliser la méthode connect() de la classe QObject. 4.1.1. Le principe de la méthode. connect() est une méthode statique 1 de la classe QObject, (module PyQt4.QtCore) qui supporte plusieurs syntaxes. La plus simple : (1) QObject.connect(emeteur,SIGNAL(),methode) 2 La méthode connect prend 3 arguments : l objet qui émet le signal. Le nom du signal que l on souhaite intercepter. Le nom de la méthode qui doit s exécuter lorsque le signal se produit. Il existe aussi une méthode disconnect() permettant de casser la connexion entre 2 objets, mais on n en parlera pas ici car on en a rarement besoin. 4.1.2. Utilisation de la méthode connect() pour quitter. Il suffit de connecter le signal " clicked()" émis par le bouton lorsqu il est cliqué, à la méthode MaFenetre.quit() définie ainsi : 1 def quit(self) : exit(0) 1 La méthode f de la classe A est statique si on peut l invoquer par l instruction A.f(). Elle est une méthode ordinaire si on a besoin d un objet de classe A pour l invoquer : a=a(),a.f() 2 il y a aussi la syntaxe plus délicate à utiliser : QObject.connect(emeteur,SIGNAL(),recepteur,SLOT())

32 Chapitre 4. Le principe des signaux et slots Exemple. 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import (QPushButton, QFont, 4 QWidget, QApplication, QIcon) 5 from PySide.QtCore import QObject, SIGNAL, Qt 6 import os,sys 7 8 class MaFenetre(QWidget) : 9 def init (self) : 10 QWidget. init (self) 11 self.setfixedsize(300, 150) 12 self.bouton = QPushButton(u"Quitter", self) 13 self.bouton.move(70, 50) 14 self.connect(self.bouton, SIGNAL("clicked()"), self.quit ) 15 16 def quit(self) : 17 exit(0) 18 19 def autre(self) : 20 print "autre" 21 22 if name ==" main " : 23 a = QApplication(sys.argv) 24 fenetre = MaFenetre() 25 fenetre.show() 26 r = a.exec_() 4.1.3. Exercices. Exercice 2. (1) Essayez de connecter le signal "pressed()" de QPushButton à une méthode qui se contente d afficher son nom à l écran. (2) Essayez de connecter le signal "released()" de QPushButton à une méthode qui se contente aussi d afficher son nom à l écran. Exercice 3. Créez un nouveau bouton apropos qui, lorsqu il est cliqué, appelle la méthode statique aboutqt() de QApplication.

4.2 Des paramètres dans les signaux et slots 33 Fig. 4.1.1. Bouton À propos 4.2. Des paramètres dans les signaux et slots La méthode statique connect() est une des particularités de Qt4. Les autres bibliothèques, comme wxwidgets par exemple, utilisent à la place de nombreuses macros et se servent du mécanisme un peu complexe et délicat des pointeurs de fonction. 4.2.1. Dessin de la fenêtre. Nous allons utiliser 2 nouveaux widgets : QSlider : un curseur qui permet de définir une valeur. QLCDNumber : un widget qui affiche un nombre.

34 Chapitre 4. Le principe des signaux et slots Voici le code : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import (QLCDNumber, QSlider, QWidget, 4 QApplication) 5 from PySide.QtCore import (QObject, SIGNAL, SLOT, Qt) 6 import os,sys 7 class MaFenetre(QWidget) : 8 def init (self) : 9 QWidget. init (self) 10 self.setfixedsize(200, 120) 11 self.lcd = QLCDNumber(self) 12 self.lcd.setsegmentstyle(qlcdnumber.flat) 13 self.lcd.move(60, 20) 14 15 self.slider = QSlider(Qt.Horizontal, self) 16 self.slider.setgeometry(10, 70, 150, 20) 17 self.connect(self.slider, SIGNAL("valueChanged(int)"), 18 self.lcd.display) 19 20 if name ==" main " : 21 a = QApplication(sys.argv) 22 fenetre = MaFenetre() 23 fenetre.show() 24 a.exec_() 4.2.2. Connexion avec des paramètres. On veut que l afficheur LCD change de valeur en fonction de la position du curseur du slider. Le signal QSlider.valueChanged(int) est émis dès que l on change la valeur du curseur du slider en le déplaçant. Il envoie un paramètre de type int (la nouvelle valeur du slider). Le slot QLCDNumber.display(int) affiche la valeur qui lui est passée en paramètre. La connexion se fait dans la classe MaFenetre grâce à l instruction : self.connect(slider, SIGNAL("valueChanged(int)"), self.lcd.display) Il suffit d indiquer le type du paramètre envoyé, ici un int, sans donner de nom à ce paramètre. Qt4fait automatiquement la connexion entre le signal et le slot et transmet le paramètre au slot. Il peut y avoir plusieurs paramètres dans un SIGNAL ou un SLOT.

4.2 Des paramètres dans les signaux et slots 35 ATTENTION : : en PyQt4, les appels SIGNAL (et éventuellement SLOT) doivent avoir comme paramètres des chaînes de caractères mimant la syntaxe C++. Si, par exemple, le SIGNAL émis est déclarée : textchanged( const QString & text ) dans la doc C++, alors la connexion doit être faite par l instruction du type self.connect(envoyeur, SIGNAL("textChanged(const QString &)"), methode) Si un SIGNAL envoit plus de paramètres que n en reçoit le SLOT, les paramètres surnuméraires sont ignorés. Bien sûr, la documentation Qt4vous donne toutes les précisions concernant les SLOT et SIGNAL de toutes les classes. N oubliez pas de consulter les classes parentes. N oubliez pas qu un SLOT est une simple méthode en Python. Exercice 4. Variation du code source précédent. Au lieu d afficher le nombre avec un QLCDNumber, affichez-le sous la forme d une barre de progression horizontale QProgressBar comme ceci : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import (QLCDNumber, QSlider, QWidget, 4 QApplication, QProgressBar) 5 from PySide.QtCore import (QObject, SIGNAL, SLOT, Qt) 6 import os,sys 7 class MaFenetre(QWidget) : 8 def init (self) :

36 Chapitre 4. Le principe des signaux et slots 9 QWidget. init (self) 10 self.setfixedsize(200, 200) 11 self.pb = QProgressBar(self) 12 self.pb.move(60, 60) 13 14 self.slider = QSlider(Qt.Vertical, self) 15 self.slider.setgeometry(10, 10, 20, 150)#x,y,w,h 16 self.connect(self.slider, SIGNAL("valueChanged(int)"), 17 self.display) 18 19 def display(self, i) : 20 self.pb.setvalue(i) 21 22 23 if name ==" main " : 24 a = QApplication(sys.argv) 25 fenetre = MaFenetre() 26 fenetre.show() 27 a.exec_() Exercice 5. Variation du code source précédent. La modification du slider modifie la largeur de la fenêtre. 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import QLCDNumber, QSlider, QWidget, QApplication,\ 4 QProgressBar, QPushButton 5 from PySide.QtCore import QObject, SIGNAL, SLOT, Qt 6 import os,sys 7 class MaFenetre(QWidget) : 8 def init (self) : 9 QWidget. init (self) 10 self.setfixedsize(200, 200)#w,h 11 self.slider = QSlider(Qt.Vertical, self) 12 self.slider.setrange(200,600) 13 self.slider.setgeometry(10, 10, 20, 150)#x,y,w,h 14 self.connect(self.slider, SIGNAL("valueChanged(int)"), 15 self.changerlargeur) 16 17 def changerlargeur(self, largeur) : 18 print "ch largeur"

4.2 Des paramètres dans les signaux et slots 37 19 self.setfixedsize(largeur,200) 20 21 if name ==" main " : 22 a = QApplication(sys.argv) 23 fenetre = MaFenetre() 24 fenetre.show() 25 a.exec_()

CHAPITRE 5 Boîtes de dialogue Qt4usuelles Les boites de dialogue permettent les actions suivantes : Afficher un message QMessageBox Saisir une information QInputDialog Sélectionner une police QFontDialog Sélectionner une couleur QColorDialog Sélectionner un (des) fichier(s) ou un dossier QFileDialog Partons du code Python suivant : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 5.1. Le code de base 3 from PySide.QtGui import (QPushButton, QWidget, QApplication, QMessageBox) 4 from PySide.QtCore import (QObject, SIGNAL, Qt) 5 import os,sys 6 class MaFenetre(QWidget) : 7 def init (self) : 8 QWidget. init (self) 9 self.setfixedsize(300,150) 10 self.bouton_dialogue = QPushButton(u"Ouvrir la boite de dialogue", self) 11 self.bouton_dialogue.move(50, 50) 12 self.bouton_quit = QPushButton(u"Quitter", self) 13 self.bouton_quit.move(100, 100) 14 15 self.connect(self.bouton_dialogue, SIGNAL("clicked()"), self.ouvrirdialogue) 16 self.connect(self.bouton_quit, SIGNAL("clicked()"), QApplication.quit) 17 18 def ouvrirdialogue(self) :

40 Chapitre 5. Boîtes de dialogue Qt4usuelles 19 """ Vous insererez le code d'ouverture des boites de dialogue ici""" 20 print "dialogue" 21 22 if name == " main " : 23 a = QApplication(sys.argv) 24 fenetre = MaFenetre() 25 fenetre.show() 26 r = a.exec_() 5.2. QMessageBox pour afficher un message. Pour pouvoir l utiliser, on doit importer la classe : from PySide.QtGui import QMessageBox Cette classe propose plusieurs méthodes de permettant d afficher différents types de message : information, warning, critique ou question 5.2.1. Méthodes statiques. Une méthode statique dans une classe est une méthode qui n a pas besoin d une instance de cette classe pour être appelée. Par exemple la méthode information() de la classe QmessageBox ci-dessous est invoquée par l instruction QmessageBox.information() Au contraire, une méthode qui n est pas statique doit être invoquée par une instance de la classe. Par exemple : 1 >>> l=['a','b','c'] 2 >>> l.append('d') #OK, l est une instance de list 3 >>> list.append('w') #interdit, list n'est pas une instance de list 4 >>> QmessageBox.information(...) #OK, information et une methode statique de QmessageBox

5.2 QMessageBox pour afficher un message. 41 5.2.2. La méthode statique QMessageBox.information(). elle permet d ouvrir une boîte de dialogue constituée d une icône «information». Son prototype C++est le suivant : 1 StandardButton information (QWidget * parent, 2 const QString & title, 3 const QString & text, 4 StandardButtons buttons=ok, 5 StandardButton defaultbutton =NoButton) ; Les 3 premiers paramètres sont obligatoires, les autres ont une valeur par défaut. parent : un pointeur vers la fenêtre conteneur (qui doit être de type (ou hériter de) QWidget). title : le titre de la boîte de dialogue (affiché en haut de la fenêtre). text : le texte affiché au sein de la boîte de dialogue. Dans la classe MaFenetre, nous définissons la méthode ouvrirdialogue() : 1 def ouvrirdialog(self) : 2 QMessageBox.information(self,"Titre de la fenetre", 3 "Bonjour et bienvenue a tous" ) et après exécution, on obtient :

42 Chapitre 5. Boîtes de dialogue Qt4usuelles Vous noterez que lorsque la boîte de dialogue est ouverte, on ne peut plus accéder à sa fenêtre conteneur qui est derrière. On dit que la boîte de dialogue est une fenêtre modal. A l inverse, on dit qu une fenêtre est non modale quand on peut toujours accéder à la fenêtre derrière. Par exemple les boîtes de dialogue Rechercher un texte. Il est possible de mettre en forme son message à l aide de balises (X)HTML. Par exemple, avec le code : QMessageBox.information(self,"Titre de la fenetre","bonjour et <i>bienvenue</i> a <b>tous</b>") on obtient alors :

5.2 QMessageBox pour afficher un message. 43 5.2.3. La méthode statique QMessageBox.warning(). s utilise de la même manière et on obtient une fenêtre avec une icône plus alarmante : 5.2.4. La méthode statique QMessageBox.critical(). s utilise de la même manière et on obtient une fenêtre avec une icône très anxiogène : 5.2.5. La méthode statique QMessageBox.question(). s utilise de la même manière et on obtient une fenêtre perplexe :

44 Chapitre 5. Boîtes de dialogue Qt4usuelles 5.2.6. Quelques variantes sont possibles. mettre plusieurs boutons pour la réponse : 1 QMessageBox.question(self, u"titre de la fenetre", 2 u"bonjour allez vous bien?", 3 QMessageBox.Yes QMessageBox.No) on utilise pour cela des «flags» listés dans la documentation de QMessageBox. Le flag par défaut est QMessageBox.Ok 5.3. Les applications Qt4peuvent être traduites C est un processus plutôt difficile et compliqué! Voici quelques lignes de code, à placer juste après l instanciation de l application par a = QApplication(args) qui vous permettent d obtenir des boutons Qt4standard en français. 1 locale = QLocale.system().name() 2 translator = QTranslator () 3 translator.load(u"qt_" + locale, QLibraryInfo.location( QLibraryInfo.TranslationsPath)) 4 a.installtranslator(translator)

5.5 QInputDialog pour saisir une information 45 5.4. Utiliser les valeurs de retour : les QMessageBox renvoient une valeur entière lorsqu un bouton a été cliqué. On peut utiliser cette valeur de retour. Par exemple : 1 def ouvrirdialogue(self) : 2 reponse = QMessageBox.question(self, u"inquietude", 3 u"bonjour allez vous tenir le coup?", 4 QMessageBox.Yes QMessageBox.No) 5 if (reponse == QMessageBox.Yes) : 6 QMessageBox.information(self,u"Reponse", 7 u"courage, <b>capitaine</b>, nous y sommes presque!") 8 elif (reponse == QMessageBox.No) : 9 QMessageBox.critical(self, u"reponse", 10 u"non Sapajou! Coloquinte! Tchouktchouk-Nougat!" 11 +u"<br>ingrat! Lache! Traitre! " 12 +u"<br>hors d'ici ou j'appelle <b>tintin </b>!") 5.5. QInputDialog pour saisir une information Qt4 propose quatre méthodes statiques de la classe QInputDialog pour saisir des informations.

46 Chapitre 5. Boîtes de dialogue Qt4usuelles QInputDialog.getText() pour saisir du texte. dont le prototype C++est 6 QString QInputDialog ::gettext ( QWidget * parent, 7 const QString & title, 8 const QString & label, 9 QLineEdit ::EchoMode mode =QLineEdit ::Normal, 10 const QString & text= QString(), 11 bool * ok=0, 12 Qt ::WindowFlags f=0 ) ; L équivalent Python, extrait de la doc PySide : static QInputDialog.getText(parent, title, label[, echo=qlineedit :: Normal, text=qstring(), flags=0]) Parameters : parent QWidget title QString label QString echo EchoMode text QString flags WindowFlags Return type : QString Et l équivalent dans PyQt4 4 1 (QString, bool ok) gettext (QWidget, QString, QString, 2 QLineEdit.EchoMode mode=qlineedit. Normal, 3 QString text=qstring(), 4 Qt.WindowFlags flags=0) Les paramètres signifient, dans l ordre : parent : la fenêtre conteneur. Peut être mis à None pour ne pas indiquer de fenêtre parente. title : titre de la fenêtre affiché en haut. label : texte affiché dans la fenêtre.

5.5 QInputDialog pour saisir une information 47 mode : mode d édition du texte. Permet de dire si on veut que les lettres s affichent quand on tape, ou si elles doivent être remplacées par des astérisques (pour les mots de passe) ou si aucune lettre ne doit s afficher. Toutes les options sont dans la doc. Par défaut, les lettres s affichent normalement (QLineEdit.Normal). text : le texte par défaut dans la zone de saisie. ok : un pointeur vers un booléen pour que Qt4puisse vous dire si l utilisateur a cliqué sur OK ou sur Annuler. f : quelques flags (options) permettant d indiquer si la fenêtre est modale (bloquante) ou pas. Les valeurs possibles sont détaillées par la doc. QInputDialog.getInt() pour saisir un entier. QInputDialog.getFloat() pour saisir un réel. QInputDialog.getItem() pour saisir un élément dans une liste. la classe MaFenetre, après les import nécessaires. 1 class MaFenetre(QWidget) : 2 def init (self) : 3 QWidget. init (self) 4 self.setfixedsize(230,120) 5 texte,ok = QInputDialog.getText(self, u"texte",u"entrez un texte") 6 entier,ok = QInputDialog.getInteger(self, u"entier", 7 u"entrez un nombre entier", 8 3, 0, 1000, 2) 9 decimal,ok = QInputDialog.getDouble(self, u"decimal", 10 u"entrez un nombre decimal") 11 item,ok = QInputDialog.getItem(self, u"liste", 12 u"choisissez dans la liste", 13 [u"aneto",u"palas",u"valluna",u" Faial"]) 14 msg = u"- " + texte + u"<br>- " + unicode(entier)\ 15 + u"<br>- " + unicode(decimal) + u"<br>- " + item 16 ok=qmessagebox.information(self, u"vos donnees : ", msg ) et le résultat d exécution :

48 Chapitre 5. Boîtes de dialogue Qt4usuelles images/05/capture-decimal.png images/05/capture-donnees.png 5.6. QFontDialog pour sélectionner une police Cette classe propose en gros une seule méthode statique. Le prototype est : (QFont, bool ok)getfont (QFont f, QWidget parent, QString caption) Valeurs de retour : La méthode renvoit la police sélectionnée : un objet de type QFont le booléen ok qui permet de savoir si l utilisateur a cliqué sur OK ou a annulé. Paramètres def : une police par défaut, objet de type QFont. parent : le widget conteneur de la boite de dialogue caption : la chaîne correspond au message qui sera affiché en haut de la fenêtre.

5.8 QFileDialog pour sélectionner un (des) fichier(s) ou un dossier 49 Exercice 6. Un clic sur un bouton avec le texte(u Ouvrir la boite de dialogue ) déclenche l ouverture d une boite de dialogue permettant le choix d une police de caractères. Choisir une police et appliquez-la au texte du bouton. La police sélectionnée est immédiatement appliquée au texte du bouton quitter, par l intermédiaire de la méthode QPushButton.setFont(). 1 class MaFenetre(QWidget) : 2 def init (self) : 3 QWidget. init (self) 4 self.setfixedsize(270,150) 5 self.bouton_dialogue = QPushButton(u"Ouvrir la boite de dialogue", self) 6 self.bouton_dialogue.move(20, 40) 7 self.connect(self.bouton_dialogue, SIGNAL("clicked()"), self.ouvrirdialogue) 8 self.bouton_quit = QPushButton(u"Quitter", self) 9 self.bouton_quit.setfixedsize(100,50) 10 self.bouton_quit.move(80, 80) 11 self.connect(self.bouton_quit, SIGNAL("clicked()"), quit ) 12 13 def ouvrirdialogue(self) : 14 police,ok=qfontdialog.getfont(self.bouton_dialogue.font (), 15 self, u"choisissez une police") 16 if ok : self.bouton_quit.setfont(police) 5.7. QColorDialog pour sélectionner une couleur Exercice 7. Un autre bouton permet de sélectionner une couleur et de l appliquer comme couleur de fond au bouton. Pour cela : créer une QPalette ; affecter la couleur choisie aux boutons de la palette ; affecter la palette au bouton. 5.8. QFileDialog pour sélectionner un (des) fichier(s) ou un dossier Selection d un répertoire : getexistingdirectory(). Exercice 8. Sélectionner un répertoire, et faire afficher une boîte de message avec le nom complet du répertoire choisi.

50 Chapitre 5. Boîtes de dialogue Qt4usuelles Sélection de fichier(s) existant : getopenfilename(). Exercice 9. Sélectionner un fichier, et faire afficher une boîte de message avec le nom complet du fichier choisi.

5.8 QFileDialog pour sélectionner un (des) fichier(s) ou un dossier 51 5.8.1. Enregistrement de fichier : getsavefilename(). Exercice 10. Ouvrez un fichier texte de votre choix, lisez la première ligne et affichez la dans un dialogue QInputDialog, de sorte que l utilisateur puisse la modifier, puis enregistrer le fichier avec la première ligne modifiée à un emplacement et sous un nom de votre choix. 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import (QWidget, QPushButton, QApplication, 4 QFileDialog, QMessageBox, QInputDialog) 5 from PySide.QtCore import (Qt, SIGNAL, SLOT, QString) 6 import os,sys 7 class MaFenetre(QWidget) : 8 def init (self) : 9 QWidget. init (self) 10 self.setfixedsize(270,150)

52 Chapitre 5. Boîtes de dialogue Qt4usuelles 11 self.au_boulot_push_button = QPushButton(u"Au boulot", self) 12 self.au_boulot_push_button.move(100, 40) 13 self.connect(self.au_boulot_push_button, SIGNAL("clicked ()"), 14 self.on_au_boulot_push_button_clicked) 15 self.bouton_quit = QPushButton("&Quitter", self) 16 self.bouton_quit.move(80, 80) 17 self.connect(self.bouton_quit, SIGNAL("clicked()"), quit ) 18 19 def on_au_boulot_push_button_clicked(self) : 20 """Exercice 9. Ouvrez un fichier texte de votre choix, 21 lisez la premiere ligne et affichez la dans un dialogue, 22 de sorte que l'utilisateur puisse la modifier, 23 puis enregistrer le fichier avec la première ligne modifiée 24 a un emplacement et sous un nom de votre choix.""" 25 f = QFileDialog.getOpenFileName(self, u"fichier à modifier",".") 26 try : 27 L = open(f).readlines() 28 except IOError : 29 print u"fichier impossible à ouvrir" 30 l,ok = QInputDialog.getText (self, u"à modifier",l[0]) 31 if ok : 32 L[0] = l+'\n' 33 else : 34 return 35 try : 36 f = open(qfiledialog.getsavefilename(self,u" enregistrer sous"),'w') 37 except IOError : 38 print "impossible de sauvegarder" 39 for l in L : 40 f.write(l) 41 f.close() 42 43 if name == " main " : 44 a = QApplication(sys.argv) 45 ouindo = MaFenetre() 46 ouindo.show()

5.8 QFileDialog pour sélectionner un (des) fichier(s) ou un dossier 53 47 r = a.exec_()

CHAPITRE 6 Les layouts 6.1. Positionnement absolu Placer ces widgets sur la fenêtre est un art difficile. Le positionnement absolu Le positionnement relatif Le code PyQt4 de base : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import (QPushButton, QWidget, QApplication) 4 from PySide.QtCore import (QObject, SIGNAL, Qt) 5 import os,sys 6 7 class MaFenetre(QWidget) : 8 def init (self) : 9 QWidget. init (self) 10 self.setfixedsize(300, 150) 11 self.bouton = QPushButton(u"Bonjour", self) 12 self.bouton.move(70, 50) 13 self.connect(self.bouton, SIGNAL('clicked()'), self.quit ) 14 15 def quit(self) : 16 exit() 17 18 if name ==" main " : 19 a = QApplication(sys.argv) 20 fenetre = MaFenetre() 21 fenetre.show() 22 r = a.exec_() le résultat de l exécution :

56 Chapitre 6. Les layouts Le bouton «bonjour» est placé de manière absolue ce qui crée des problèmes en cas de redimensionnement. Les layouts Qt4 sont conteneurs de widgets spécialisés dans le placement relatif des widgets, le positionnement sous forme de grille. Fig. 6.1.1. Les classes Layout Toutes les classes de layout héritent de la classe de base abstraite QLayout.

6.3 Les layouts horizontal et vertical QHBoxLayout et QVBoxLayout 57 se fait en 4 temps : 6.2. L utilisation d un layout (1) créer les widgets (2) créer le layout : QLayout() (3) y ajouter les widgets : addwidget() (4) indiquer à la fenêtre d utiliser le layout : setlayout() 6.3. Les layouts horizontal et vertical QHBoxLayout et QVBoxLayout Les layouts horizontaux et verticaux. Le fonctionnement est analogue pour les deux types de layout. (1) Créer les widgets. 1 bouton1 = QPushButton(u"Essayez") 2 bouton2 = QPushButton(u"...encore...") 3 bouton3 = QPushButton(u"ça ne marchera jamais") Les boutons n ont pas de conteneur widget, mais vont être placés dans un conteneur spécialisé : le layout. (2) Créer le layout conteneur, disons vertical : 1 layout = QVBoxLayout() (3) Placer les widgets (boutons) dans le conteneur layout 1 layout.addwidget(bouton1) 2 layout.addwidget(bouton2) 3 layout.addwidget(bouton3) (4) Indiquer à la fenêtre (ici self) d utiliser le layout 1 self.setlayout(monlayout) On obtient finalement le code de MaFenetre : 1 class MaFenetre(QWidget) : 2 def init (self) : 3 QWidget. init (self) 4 self.setfixedsize(300, 150) 5 bouton1 = QPushButton(u"Essayez",self) 6 bouton2 = QPushButton(u"...encore...",self) 7 bouton3 = QPushButton(u"ça ne marchera jamais",self)

58 Chapitre 6. Les layouts Fig. 6.3.1. Trois boutons dans un layout horizontal et un layout vertical 8 layout = QVBoxLayout() 9 layout.addwidget(bouton1) 10 layout.addwidget(bouton2) 11 layout.addwidget(bouton3) 12 13 self.setlayout(layout) Et le résultat : Essayez de redimensionner la fenêtre dans un sens ou dans l autre... Le layout fait son travail. 6.4. Le layout de grille QGridLayout Les layouts horizontaux et verticaux ne permettent pas de créer des dispositions très complexes sur votre fenêtre. Le QGridLayout peut être vu comme un assemblage de QHBoxLayout et QVBoxLayout. Il s agit d une disposition en grille, comme un tableau avec des lignes et des colonnes. 0,0 0,1 0,2... 1,0 1,1............... Comme pour les QHBoxLayout c est la méthode addwidget() qui permet d ajouter des widget dans le conteneur layout. Son prototype C++est :

6.5 Le layout de formulaire QFormLayout 59 void addwidget(qwidget* widget, int row, int column, Qt ::Alignment alignment=0) Les paramètres row et column indiquent la position du widget dans la grille. Essayer de positionner les trois boutons comme sur la figure(6.4). Fig. 6.4.1. Trois boutons dans un QGridLayout On peut demander à un widget d occuper plusieurs colonnes en largeur ou bien plusieurs lignes en hauteur. Pour ce faire, il suffit d ajouter deux paramètres rawspan et colspan à la méthode addwidget() comme ceci : mon_layout.addwidget(bouton3,1,0,rowspan,colspan) colspan=2 0,3 1,0 1,1 1,2......... Essayez de reproduire les dispositions montrées par la figure (6.4.2) 6.5. Le layout de formulaire QFormLayout Layout spécialisé pour les formulaires. Par exemple un QFormLayout est un QGridLayout à 2 colonnes et plusieurs lignes. Au lieu de la méthode addwidget(), nous allons utiliser une méthode addrow() qui prend 2 paramètres : Le texte du libellé Le champ du formulaire On peut aussi définir des raccourcis clavier pour accéder rapidement aux champs du formulaire. Pour ce faire, placez un symbole & devant la lettre du libellé que vous voulez transformer en raccourci. LayoutF.addRow("&Nom", nom) Exercice 11. Créez un formulaire analogue à celui de la figure (6.5.1)

60 Chapitre 6. Les layouts Fig. 6.4.2. Trois boutons dans un QGridLayout Fig. 6.5.1. Un simple formulaire 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PyQt4.QtGui import (QPushButton, ) 4 from PyQt4.QtCore import * 5 6 import os,sys 7 class MaFenetre(QWidget) :

6.6 Combiner les layouts 61 8 def init (self) : 9 QWidget. init (self) 10 prenom = QLineEdit() 11 nom = QLineEdit() 12 age = QSpinBox() 13 region = QComboBox() 14 region.additems([u'alsace',u'aquitaine', u'limousin', u' Corse', u'centre']) 15 date = QCalendarWidget() 16 layout_formulaire = QFormLayout() 17 layout_formulaire.addrow(u"prénom",prenom) 18 layout_formulaire.addrow(u"nom",nom) 19 layout_formulaire.addrow(u"age",age) 20 layout_formulaire.addrow(u"région",region) 21 layout_formulaire.addrow(u"date",date) 22 self.setlayout(layout_formulaire) 23 24 def quit(self) : 25 exit() 26 27 if name ==" main " : 28 a = QApplication(sys.argv) 29 fenetre = MaFenetre() 30 fenetre.show() 31 r = a.exec_() 6.6. Combiner les layouts Un layout peut en contenir un autre. Pour cela, on utilise la méthode addlayout() au lieu de addwidget(). Exercice 12. Essayez d obtenir le positionnement indique par la figure (6.6.1), en combinant un layout de grille, un layout de formulaire et un layout horizontal.

62 Chapitre 6. Les layouts Fig. 6.6.1. Layouts combinés

CHAPITRE 7 Les principaux widgets Qt4 7.1. Fenêtres et widgets Avec Qt4, tout élément de la fenêtre est appelé un widget. La fenêtre elle-même est considérée comme un widget. Dans le code, les widgets sont des classes qui héritent toujours de QWidget (directement ou indirectement). C est donc une classe de base très importante, et vous aurez probablement très souvent besoin de lire la doc de cette classe. Un widget qui n est contenu dans aucun autre widget est considéré comme une fenêtre. Donc quand on fait juste ce code très simple : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import * 4 from PySide.QtCore import * 5 import os,sys 6 def main(args) : 7 a = QApplication(args) 8 w = QWidget() 9 w.show() 10 return a.exec_() 11 if name == " main " : 12 main(sys.argv)... cela affiche une fenêtre (vide) :

64 Chapitre 7. Les principaux widgets Qt4 N importe quel widget peut servir de fenêtre. Si vous vous souvenez bien, on avait créé un bouton dans les premiers exemples du cours sur Qt4. On avait demandé à afficher ce bouton. Comme le bouton n avait pas de parent, une fenêtre avait été ouverte : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import * 4 import os,sys 5 def main(args) : 6 a = QApplication(args) 7 bouton = QPushButton(u"Hello, you! "),None) 8 bouton.show() 9 r = a.exec_() 10 return r 11 if name == " main " : 12 main(sys.argv) Dans la pratique, on ne crée pas de fenêtre-bouton comme là. On crée d abord une fenêtre, et on place ensuite des widgets à l intérieur. 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import QApplication, QWidgetQPush, Button 4 from PySide import QtCore 5 import os,sys

7.1 Fenêtres et widgets 65 6 7 def main(args) : 8 a = QApplication(args) 9 # Création d'un widget qui servira de fenêtre 10 fenetre = QWidget() 11 12 # Création du bouton, ayant pour parent la "fenetre" 13 bouton = QPushButton(u"Hello, you!"),fenetre) 14 15 fenetre.show() 16 return a.exec_() 17 18 if name ==" main " : 19 main(sys.argv) Note : je n ai pas utilisé de layouts dans ce code pour le rendre court et simple. Le bouton est donc positionné de manière absolue au coin en haut à gauche, de coordonnées (0, 0). Le QPushButton a donc pour conteneur un QWidget, car on a indiqué en second paramètre de son constructeur un pointeur vers le QWidget : fenetre. Le QWidget n a pas de conteneur car on n a pas envoyé de pointeur vers un autre widget dans son constructeur, donc c est une fenêtre. Attention dans la littérature, on trouve souvent l amalgamme entre parent et conteneur : une classe parente est une classe mère (au sens de l héritage) ; un widget «parent» désigne souvent un widget conteneur (qui en contient d autres). Ici, nous parlons de widgets conteneur en termes Qt. Deux classes de widgets ont un statut un peu particulier : QMainWindow est un widget spécial qui permet de créer la fenêtre principale de l application. Une fenêtre principale peut contenir des menus, une barre d outils, une barre d état, etc. QDialog est une classe de base utilisée par toutes les classes de boîtes de dialogue qu on a vues il y a quelques chapitres. On peut aussi s en servir directement pour ouvrir des boîtes de dialogue personnalisées. La fenêtre principale QMainWindow mérite un chapitre entier à elle toute seule.

66 Chapitre 7. Les principaux widgets Qt4 La fenêtre QDialog peut être utilisée pour ouvrir une boîte de dialogue personnalisée générique. Une boîte de dialogue est une fenêtre généralement de petite taille dans laquelle il y a peu d - informations. La classe QDialog hérite de QWidget comme tout widget. Elle y ajoute peu de choses, parmi lesquelles la gestion des fenêtres modales (une fenêtre par-dessus toutes les autres qui doit être remplie avant de pouvoir accéder aux autres fenêtres de l application). 7.2. La classe QWidget Je vous invite à ouvrir la doc de QWidget en même temps que vous lisez ce chapitre. Vous remarquerez que QWidget est la classe mère d un grand nombre d autres classes. Les QWidget disposent de beaucoup de propriétés et de méthodes. Donc tous les widgets disposent de ces propriétés et méthodes. On peut découper les propriétés en deux catégories : Celles qui valent pour tous les types de widgets et pour les fenêtres Celles qui n ont de sens que pour les fenêtres Les propriétés valables pour tous les widgets. On peut modifier une propriété en appelant une méthode du même nom commençant par set. Par exemple, si la propriété est cursor, la méthode sera setcursor(). cursor : curseur de la souris à afficher lors du survol du widget. La méthode setcursor() attend que vous lui envoyiez un objet de type QCursor. Certains curseurs classiques (comme le sablier) sont prédéfinis dans une énumération. La doc vous fait un lien vers cette énumération. enabled : indique si le widget est activé, si on peut le modifier. Un widget désactivé est généralement grisé. Si vous appliquez setenabled(false) à toute la fenêtre, c est toute la fenêtre qui deviendra inutilisable. height : hauteur du widget ; size : dimensions du widget. Vous devrez indiquer la largeur et la hauteur ; visible : contrôle la visibilité du widget ; width : largeur ; Par exemple, pour modifier la largeur d une fenêtre on écrira : mafenetre.setwidth(200) Les propriétés utilisables uniquement sur les fenêtres. Ces propriétés sont faciles à reconnaître, elles commencent toutes par window. windowflags : une série d options contrôlant le comportement de la fenêtre. Il faut consulter l énumération Qt.WindowType pour savoir les différents types disponibles. Vous pouvez aussi consulter l exemple Window Flags du programme Qt Examples and Demos. Par exemple pour afficher une fenêtre avec une transparence de 70%, et comme titre «Karakoram», on saisira :

7.3 Les boutons 67 1 fenetre.setwindowtitle("karakoram") 2 fenetre.setwindowopacity(0.7)! 7.3. Les boutons QPushButton : un bouton classique, que vous avez déjà largement eu l occasion de manipuler. QRadioButton : un bouton radio, pour un choix à faire parmi une liste. QCheckBox : une case à cocher (on considère que c est un bouton en GUI Design). Tous ces widgets héritent de QAbstractButton qui lui-même hérite de QWidget, qui finalement hérite de QObject :

68 Chapitre 7. Les principaux widgets Qt4 7.3.1. QPushButton. Le QPushButton est l élément le plus classique et le plus commun des fenêtres : Un rappel important, indiqué par la doc : QPushButton hérite de QAbstractButton. C est une information importante, car QPushButton contient peu de méthodes qui lui sont propres. C est normal, une grande partie d entre elles se trouvent dans sa classe parente QAbstractButton. Il faudra donc consulter aussi QAbstractButton, sa classe mère QWidget (et éventuellement aussi QObject mais c est plus rare), pour connaître toutes les possibilités offertes par un QPushButton. Par exemple, setenabled() permet d activer/désactiver le bouton, et cette propriété se trouve dans QWidget.

7.3 Les boutons 69 Les signaux du bouton. Un bouton émet un signal clicked() quand on l active. C est le signal le plus communément utilisé. On note aussi les signaux pressed() (bouton enfoncé) et released() (bouton relâché), mais ils sont plus rares. Les boutons à 2 états. Un bouton peut parfois avoir 2 états : enfoncé et relâché (normal). Pour activer un bouton à 2 états, utilisez setcheckable(true) : 1 bouton = QPushButton("Bouton", fenetre) 2 bouton.setcheckable(true) Désormais, un clic sur le bouton le laissera enfoncé, et un nouveau clic le rétablira dans son état normal. Utilisez les signaux pressed() et released() pour récupérer les changements d état du bouton. A gauche un bouton normal, à droite un bouton pressé 7.3.2. QCheckBox : une case à cocher. Une case à cocher QCheckBox est généralement associé à un texte de libellé comme ceci :

70 Chapitre 7. Les principaux widgets Qt4 On définit le libellé de la case lors de l appel du constructeur : 1 w = QWidget() 2 a = QCheckBox(u"Une case à cocher non cochée",w) 3 b = QCheckBox(u"Une case à cocher cochée",w) 4 b.move(0,50) 5 w.show() La case à cocher émet le signal statechanged(bool) lorsqu on modifie son état. Le booléen en paramètre nous permet de savoir si la case est maintenant cochée ou décochée. Si vous voulez vérifier à un autre moment si la case est cochée, appelez ischecked() qui renvoie un booléen. On peut aussi faire des cases à cocher à 3 états (le troisième état étant l état grisé). Renseignezvous sur la propriété tristate pour savoir faire cela. Notez que ce type de case à cocher est relativement rare. Enfin, sachez que si vous avez plusieurs cases à cocher, vous pouvez les regrouper au sein d une QGroupBox. 7.3.3. QRadioButton : les boutons radio. C est une case à cocher particulière : une seule case peut être cochée à la fois parmi une liste. Les radio buttons qui ont le même widget parent sont mutuellement exclusifs. Si vous en cochez un, les autres seront automatiquement décochés. En général, on place les radio buttons dans une QGroupBox. Utiliser des QGroupBox différentes vous permet de séparer les groupes de radio buttons.

7.5 Les widget de saisie 71 L exemple ci dessus est obtenu par les instructions : 1 from PySide.QtCore import * 2 from PySide.QtGui import * 3 w = QWidget() 4 g = QGroupBox(u"Plats",w) 5 steak = QRadioButton("steak") 6 salade = QRadioButton("salade") 7 frites = QRadioButton("frites") 8 steak.setchecked(true) 9 layout = QVBoxLayout() 10 for plat in (steak, frites, salade) : 11 layout.addwidget(plat) 12 g.setlayout(layout) 13 g.move(5,5) 14 w.show() 7.4. Les afficheurs Parmi les widgets afficheurs, on compte principalement : QLabel : le plus important, un widget permettant d afficher du texte ou une image. QProgressBar : une barre de progression. Les principaux d entre eux : 7.5. Les widget de saisie QLineEdit : champ de texte à une seule ligne. QTextEdit : champ de texte à plusieurs lignes pouvant afficher du texte mis en forme.

72 Chapitre 7. Les principaux widgets Qt4 QSpinBox : champ de texte adapté à la saisie de nombre entiers. QDoubleSpinBox : champ de texte adapté à la saisie de nombre décimaux. QSlider : un curseur qui permet de sélectionner une valeur. QComboBox : une liste déroulante. 7.6. Les conteneurs Normalement, n importe quel widget peut en contenir d autres. Cependant, certains widgets ont été vraiment créés spécialement pour pouvoir en contenir d autres : QFrame : un widget pouvant avoir une bordure. QGroupBox : un widget (que nous avons déjà utilisé) adapté à la gestion des groupes de cases à cocher et de boutons radio. QTabWidget : un widget gérant plusieurs pages d onglets.

CHAPITRE 8 La Fenêtre principale d une application 8.1. Présentation de QMainWindow La classe QMainWindow hérite directement de QWidget. C est un widget généralement utilisé une seule fois par programme, et qui sert uniquement à créer la fenêtre principale de l application. Certaines applications simples n ont pas besoin de recourir à la QMainWindow. On va supposer ici que vous vous attaquez à un programme complexe et d envergure. 8.1.1. Structure de la QMainWindow. Schéma issu de la doc de QMainWindow Une fenêtre principale peut être constituée de tout ou certains de ces éléments.

74 Chapitre 8. La Fenêtre principale d une application Menu Bar : c est la barre de menus. C est là que vous allez pouvoir créer votre menu Fichier, Edition, Affichage, Aide, etc. Toolbars : les barres d outils. Dans un éditeur de texte, on a par exemple des icônes pour créer un nouveau fichier, pour enregistrer, etc. Dock Widgets : plus complexes, ces docks sont des conteneurs que l on place autour de la fenêtre principale. Ils peuvent contenir des outils, par exemple les différents types de pinceaux que l on peut utiliser quand on fait un logiciel de dessin. Central Widget : c est le coeur de la fenêtre, là où il y aura le contenu proprement dit. Status Bar : la barre d état. Elle affiche en général l état du programme (Prêt/Enregistrement en cours, etc.). Sous MacOSX, les différentes parties (dock, central widget) sont dans des fenêtres séparées. Sous GNU-Linux, elle sont au choix de l utilisateur, amovibles ou «dockées» dans une seule et même fenêtre. 8.1.2. Un code de base. Créons un projet contenant les deux fichiers : main.py uimainwindow.py Dans main.py, le programme principal, qui se contente d instancier une fenêtre principale et de l éxécuter : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PySide.QtGui import QApplication 4 from uimainwindow02 import UiMainWindow 5 import sys 6 7 if name == " main " : 8 a = QApplication(sys.argv) 9 f = UiMainWindow() 10 f.show() 11 a.exec_() Dans uimainwindow.py, on crée une classe héritant de QMainWindow, prête à recevoir des méthodes complémentaires. 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 import PySide as PyQt4 4 from PySide.QtGui import (QMainWindow, QApplication, QKeySequence, QIcon, 5 QWidget, QLineEdit, QFormLayout) 6 from PySide.QtCore import SIGNAL

8.2 La zone centrale (SDI et MDI) 75 7 8 import os,sys 9 10 class UiMainWindow(QMainWindow) : 11 def init (self) : 12 QMainWindow. init (self) 13 zone_centrale = QWidget() L exécution de main.py affiche une fenêtre vide : 8.2. La zone centrale (SDI et MDI) La zone centrale de la fenêtre principale est prévue pour contenir un et un seul widget. On y insère un QWidget (ou une de ses classes filles) et on s en sert comme conteneur pour mettre d autres widgets à l intérieur si besoin est. Sachez tout d abord qu on distingue 2 types de QMainWindow : Les SDI (Single Document Interface) : elles ne peuvent afficher qu un document à la fois. Les MDI (Multiple Document Interface) : elles peuvent afficher plusieurs documents à la fois. Elles affichent des sous-fenêtres dans la zone centrale. C est le cas par exemple de designer. Définition de la zone centrale (type SDI). On utilise la méthode QMainWindow.setCentralWidget () pour indiquer quel widget contiendra la zone centrale. Faisons cela dans le constructeur de UiMainWindow : 1 #! /usr/bin/python 2 #-*-coding : utf-8 -*- 3 from PyQt4.QtGui import * 4 class fenprincipale(qmainwindow) : 5 def init (self) :

76 Chapitre 8. La Fenêtre principale d une application 6 QMainWindow. init (self) 7 zonecentrale = QWidget() 8 self.setcentralwidget(zonecentrale) Visuellement, rien n a changé. Par contre on a maintenant un QWidget qui sert de conteneur pour les autres widgets de la zone centrale de la fenêtre. On peut donc y insérer des widgets. Dans le constructeur on écrit : 1 nom = QLineEdit() 2 prenom = QLineEdit() 3 age = QLineEdit() 4 layout = QFormLayout() 5 layout.addrow(u"votre nom", nom) 6 layout.addrow(u"votre prénom", prenom) 7 layout.addrow(u"votre âge", age) Voici la fenêtre obtenue à l exécution : 8.3. Les menus Pour créer un menu pour la fenêtre principale. La barre de menus est accessible depuis la méthode menubar() qui renvoie un QMenuBar proposant la méthode addmenu(). dans le constructeur de UiMainWindow, on rajoute ces quelques lignes : 1 menu_fichier = self.menubar().addmenu("&fichier") 2 menu_edition = self.menubar().addmenu("&edition") 3 menu_affichage = self.menubar().addmenu("&affichage") Nous avons créé 3 menus. Vous noterez qu on utilise ici le symbole '&' pour définir des raccourcis clavier (les lettres F, E et A seront donc des raccourcis vers leurs menus respectifs). Nous avons maintenant 3 menus sur notre fenêtre :

8.3 Les menus 77 Créer des actions. On doit ensuite associer les menus à des actions. Ces actions seront également utilisées par les barres d outils à la section suivante. Pour créer une action, on utilise le constructeur QAction(action, parent), puis on la rajoute dans le menu qui convient. Par exemple, créer une action pour quitter le programme, dans le menu fichier, s écrit (toujours dans le constructeur de UiMainWindow) : 1 action_quitter = QAction("&Quitter", self) 2 menu_fichier.addaction(action_quitter) On peut associer à l action créée un raccourci clavier, une icône et un tooltip en utilisant les méthodes suivantes de QAction : 1 action_quitter.setshortcut(qkeysequence("ctrl+q")) 2 action_quitter.seticon(qicon("quitter.png")) 3 action_quitter.settooltip(u"cliquer pour quitter l'application ") Rendre les actions fonctionnelles. Pour rendre une action fonctionnelle, il faut connecter les signaux qu elle émet à des slots. Le signal le plus utile est "triggered()" (déclenchée). Le slot utilisé ici est la méthode exit() de QApplication : 1 self.connect(action_quitter, SIGNAl('triggered()'), exit) Exercice 13. Créer une méthode de UiMainWindow.lireFormulaire() qui lit les champs nom, prenom et age de notre formulaire et écrit dans le terminal (les... seront remplacés par les bonnes valeurs) : Votre nom est..., votre prénom est :..., votre âge est :... Associer cette méthode à une action que l on insèrera dans le menu Fichier

78 Chapitre 8. La Fenêtre principale d une application Les sous-menus. Ils sont gérés par la classe QMenu Imaginons que nous voulions créer un sous-menu Fichiers récents au menu Fichier. Ce sousmenu affichera une liste de fichiers récemment ouverts par le programme (des fichiers bidons pour cet exemple). Au lieu d appeler addaction() de la QMenuBar, appelez cette fois addmenu() qui renvoie un (pointeur vers un) QMenu : 1 fichiersrecents = menufichier.addmenu(u"fichiers &récents") 2 action1 = fichiersrecents.addaction("bidon-1.txt") 3 action2 = fichiersrecents.addaction("bidon-2.txt") 4 action3 = fichiersrecents.addaction("bidon-3.txt") Pour créer une barre d outils : 8.4. La barre d outils 1 tool_bar_fichier = QToolBar("Fichiers", self) Une barre d outils (QToolBar) contient des actions (par exemple celles créées dans les menus) : 1 tool_bar_fichier.addaction(action_quitter) 2 tool_bar_fichier.addaction(action1) 3 tool_bar_fichier.addaction(action2) 4 tool_bar_fichier.addaction(action3) 5 self.addtoolbar(tool_bar_fichier) Elle peut également contenir des widgets : 1 police = QFontComboBox(self)#un widget 2 tool_bar_fichier.addwidget(police) 3 self.addtoolbar(tool_bar_fichier) Il faut ensuite attacher la toolbar crée à la fenêtre (ici la fenêtre est self) 1 self.addtoolbar(tool_bar_fichier)

8.6 La barre d état (statusbar) 79 8.5. Les docks 8.6. La barre d état (statusbar) Pour faire afficher un message dans la barre d état : statusbar.showmessage("message") Exercice 14. Créer une action proposant le traitement des données, insérer cette action dans le menu Édition, connecter cette action à une méthode traiter() qui affiche le nom dans un terminal et affiche «traitement en cours» dans la barre d état.

CHAPITRE 9 Le designer Qt4 9.1.1. Choix du type de fenêtre à créer. 9.1. Présentation de designer

82 Chapitre 9. Le designer Qt4 9.1.2. Analyse de la fenêtre de Qt Designer. (1) Pour passer d un mode d édition à un autre. Qt Designer propose 4 modes d édition : Edit Widgets : le mode par défaut, que vous utiliserez le plus souvent. Il permet d insérer des widgets sur la fenêtre et de modifier leurs propriétés. Edit Signals/Slots : permet de créer des connexions entre les signaux et les slots de vos widgets. Edit Buddies : permet d associer des QLabel avec leurs champs respectifs. Lorsque vous faites un layout de type QFormLayout, ces associations sont automatiquement créées. Edit Tab Order : permet de modifier l ordre de tabulation entre les champs de la fenêtre, pour ceux qui naviguent au clavier et passent d un champ à l autre en appuyant sur la touche Tab. (2) Votre fenêtre (vide). Si vous créez une QMainWindow, vous aurez en plus une barre de menus et une barre d outils. Si vous créez une QDialog, vous aurez probablement des boutons OK et Annuler déjà disposés. (3) Widget Box : les widgets à placer sur la fenêtre ; (4) Property Editor : pour modifier toutes les propriétés du widget (y compris celles de ses ancêtres). Ce sont les «properties» de la doc Qt4 ; (5) Object Inspector : liste les widgets placés sur la fenêtre, sous forme d arbre, chacun dans son conteneur. (6) Signal/slot editor : pour connecter les signaux prédéfinis aux slots prédéfinis.

9.2 Les layouts 83 (7) Resource Browser : pour naviguer à travers les fichiers de ressources (.qrc) de votre application. (8) Action Editor : permet de créer des QAction (pour les menus et barres d outils). 9.2. Les layouts Voir le chapitre dédié (6)

84 Chapitre 9. Le designer Qt4 9.3. Editer les propriétés des widgets Fig. 9.3.1. Properties editor

9.4 Configurer les signaux et les slots 85 9.4. Configurer les signaux et les slots Fig. 9.4.1. L éditeur Signal-Slot

86 Chapitre 9. Le designer Qt4 9.5. Inspecter les widgets Fig. 9.5.1. Object Inspector 9.6. Exemple 9.6.1. Les widgets. Nous allons créer une application sommaire, mais complète. Dans designer créez un nouveau formulaire de type MainWindow Dans cette fenêtre, insérez en ligne dans l ordre : une spinbox nommé spinboxnombre1, une combobox nommée comboboxoperation, une spinbox nommée spinboxnombre2 deux labels nommés labelegal et labelresultat.

9.6 Exemple 87 Pour renommer les widget, utilisez l éditeur de propriétés, et la propriété objectname de l ancêtre QObject) : Sélectionnez, par un clic glissé, les 5 éléments puis cliquez sur l icône «layout in a grid» insérer un ressort vertical sous la calculatrice et un ressort horizontal à droite de celle-ci. Le résultat doit ressembler à ceci (au menu près que nous allons traiter ci-après) : enregistrer sous calculatricewindow.ui 9.6.2. Les actions et menus. Dans la barre de menu, ajoutez les entrées de menu Fichier/- Calculer et Fichier/Quitter Si vous tapez exactement &Fichier, puis &Calculer, &Quitter, les lettres suivant le & seront soulignées, ce qui signifie que les entrées de menu correspondantes seront accessibles par la combinaison de touches Alt-F-C et Alt-F-Q. Nous avons créé un QMenu appelé menufile et deux QActions appelées actioncalculer et actionquitter Pour affecter un raccourci clavier aux actions Quitter et et Calculer, on sélectionne l action, et on modifie sa propriété shortcut dans le «property editor» (Ctrl-C et Ctrl-Q sont de bons candidats). 9.6.3. Connexions. Dans le designer il est possible d effectuer les connections entre les signaux et slot prédéfinis de Qt4. dans View/Signal-Slot editor, connectez le SIGNAL triggered() de actionquitter au SLOT close() de la fenêtre principale.

88 Chapitre 9. Le designer Qt4 9.7. Générer une classe Python à partir d un fichier designer-qt4 designer ne fait que produire un fichier.ui, il faut le traduire en un module Python Notre fenêtre créée par designer n est qu un widget, il ne dispose d aucune fonctionnalité. De plus il n est pas lisible tel quel dans un programme Python. Comment mettre ce widget à disposition du programmeur Python? Il existe deux méthodes : au préalable ou bien à la volée Pour produire du code Python au préalable, il faut utiliser le programme pyuic ou pyuic4 ou pyuic-qt4 $ pyuic calculatricewindow.ui > uicalculatrice.py Celui-ci génèrera un fichier que je vous recommande de nommer uicalculatrice.py. Ce fichier contient une classe Python, du nom de Ui_xxx, où xxx est le nom que vous avez donné à votre widget dans designer, c est à dire la propriété QObject.objectName. Je suppose que ce nom est Calculatrice. Le programmeur n a plus qu à importer la classe uicalculatrice. Ui_calculatrice par l instruction 1 from uicalculatrice import Ui_calculatrice A la volée : le fichier.ui est chargé et traduit en Python dynamiquement. Pour ce faire, on utilise la portion de code suivante : 1 from PyQt4 import uic 2... 3 Ui_Calculatrice, Klass = uic.loaduitype(' calculatricewindow.ui') 4... Ici, Ui_Calculatrice est le nom de notre classe Python. Nous verrons un peu plus loin un code opérationnel.(9.8.1)(9.8.2) 9.8. Utiliser un widget de designer dans notre application Utilisation directe (non recommandé) Utilisation avec un héritage (recommandé)

9.8 Utiliser un widget de designer dans notre application 89 9.8.1. Utilisation directe. Le programmeur peut créer une instance de Ui_Calculatrice à sa convenance. Par exemple macalculette = Ui_Calculatrice() Il faut ensuite impérativement mettre en place les éléments dans le conteneur de macalculette par l instruction macalculette.setupui(conteneur) Concrètement, le programme suivant effectue ces opérations : 1 #! /usr/bin/python 2 # -*- coding : utf-8 -*- 3 from PyQt4.QtGui import QWidget, QApplication, QMainWindow 4 from PyQt4.QtCore import QObject, SIGNAL 5 from PyQt4 import uic 6 import os,sys 7 8 Ui_Calculatrice, Klass = uic.loaduitype('calculatricewindow.ui ') 9 10 if name ==" main " : 11 a = QApplication(sys.argv) 12 f = QMainWindow() 13 macalculette = Ui_Calculatrice() 14 macalculette.setupui(f) 15 f.show() 16 r = a.exec_() Bien sûr, il faut ensuite programmer les fonctionnalités de la calculatrice. 9.8.2. Utilisation avec un héritage multiple. Avantages : on peut personnaliser la fenêtre, écrire nos propres slots, et on n a pas besoin de mettre le préfixe ui devant chaque nom de widget. Défauts : il faut faire un héritage multiple, une technique un peu plus complexe que l héritage classique. En PyQt4, une classe peut hériter d une classe PyQt4 au maximum. Mais elle peut hériter d une classe PyQt4 et d une classe Python ordinaire Dans notre cas, nous allons créer une classe qui hérite à la fois de QMainWindow et de Ui_Calculatrice (la fenêtre créée sous designer).

90 Chapitre 9. Le designer Qt4 Tous vos widgets et programmes Qt devront suivre l agencement proposé par le fichier template 1 #! /usr/bin/python 2 # -*- coding : utf-8 -*- 3 from PyQt4.QtGui import QWidget, QApplication, QMainWindow 4 from PyQt4.QtCore import QObject, SIGNAL, QFile 5 from PyQt4 import uic 6 import os,sys 7 8 UiMaFenetre, Klass = uic.loaduitype('calculatricewindow.ui') 9 10 #class MaFenetre(QWidget,UiMaFenetre) : 11 class MaFenetre(QMainWindow,UiMaFenetre) : 12 """ 13 - Si UiMaFenetre, importe depuis le designer a ete construit comme QMainWindow, 14 alors MaFenetre doit heriter de QMainWindow

9.8 Utiliser un widget de designer dans notre application 91 15 - si UiMafFenetre est construit dans designer comme un QWidget, alors MaFenetre doit 16 heriter de QWidget""" 17 def init (self, conteneur=none) : 18 if conteneur is None : conteneur = self 19 QMainWindow. init (conteneur) 20 self.setupui(conteneur) 21 22 23 if name ==" main " : 24 a=qapplication(sys.argv) 25 f=mafenetre() #MaFenetre, inseree dans un conteneur ou non 26 f.show() #On affiche le conteneur 27 r=a.exec_() Exercice 15. Programmer les fonctionnalités de calcul dans une méthode MaFenetre.calcul, et connectez l action MaFenetre.actionCalculer à cette méthode. Testez. La méthode calcul doit : recupérer les deux valeurs l opération à effectuer, faire l opération et stocker le résultat dans labelresultat. Exercice 16. Un peu de cosmétique. Récupérer les icônes quit.png et exec.png http ://web.univ-pau.fr/ puiseux/enseignement/python/2010- Formation-UPPA/Chap6-PyQt4-PySide/scriptsPython/12/. Ecrivez une méthode finetune, que vous appellerez depuis le constructeur, et qui affecte les icônes aux deux actions actionquitter et actioncalculer.

92 Chapitre 9. Le designer Qt4 9.9. Auto-connect créer une méthode on_objet_signal ou Objet est le QObject émetteur, Signal est le SIGNAL émis, elle sera automatiquement connectée comme si l on avait écrit : connect(self.objet, SIGNAL("Signal(..)"), self.on_objet_signal) Exemple 17. la méthode def on_boutonquit_clicked (self) :pass sera automatiquement connectés comme si l on avait écrit : connect(self.boutonquit, SIGNAL("clicked()"), self.on_boutonquit_clicked ) 9.10. Compléments Exercice 18. Ajouter à la calculatrice un QTextEdit, destiner à stocker l historique des calculs. Prévoir également un bouton et une action de menu pour effacer l historique.

9.10 Compléments 93 qthelp ://com.trolltech.qt.452/qdoc/model-view-programming.html

CHAPITRE 10 Programmation Modèle-Vue-Contrôleur (MVC) 10.1. Architecture Modèle/Vue Le concept de Model-View-Controller (MVC) vient du langage Smalltalk, il est souvent utilisé pour la programmation d interfaces Homme-Machine.. In Design Patterns, Gamma et al. write : MVC consists of three kinds of objects. The Model is the application object, the View is its screen presentation, and the Controller defines the way the user interface reacts to user input. Before MVC, user interface designs tended to lump these objects together. MVC decouples them to increase flexibility and reuse. If the view and the controller objects are combined, the result is the model/view architecture. This still separates the way that data is stored from the way that it is presented to the user, but provides a simpler framework based on the same principles. This separation makes it possible to display the same data in several different views, and to implement new types of views, without changing the underlying data structures. To allow flexible handling of user input, we introduce the concept of the delegate. The advantage of having a delegate in this framework is that it allows the way items of data are rendered and edited to be customized. Generally, the model/view classes can be separated into the three groups described above : models, views, and delegates. Each of these components is defined by abstract classes that provide common interfaces and, in some cases, default implementations of features. Abstract classes are meant to be subclassed in order to provide the full set of functionality expected by other components ; this also allows specialized components to be written. Models, views, and delegates communicate with each other using signals and slots : Signals from the model inform the view about changes to the data held by the data source. Signals from the view provide information about the user s interaction with the items being displayed. Signals from the delegate are used during editing to tell the model and view about the state of the editor. Models. All item models are based on the QAbstractItemModel class. This class defines an interface that is used by views and delegates to access data. The data itself does not have to be stored in the model ; it can be held in a data structure or repository provided by a separate class, a file, a database, or some other application component. The basic concepts surrounding models are presented in the section on Model Classes.

96 Chapitre 10. Programmation Modèle-Vue-Contrôleur (MVC) The model/view architecture The model communicates with a source of data, providing an interface for the other components in the architecture. The nature of the communication depends on the type of data source, and the way the model is implemented. The view obtains model indexes from the model ; these are references to items of data. By supplying model indexes to the model, the view can retrieve items of data from the data source. In standard views, a delegate renders the items of data. When an item is edited, the delegate communicates with the model directly using model indexes. Fig. 10.1.1. Architecture Modèle/Vue QAbstractItemModel provides an interface to data that is flexible enough to handle views that represent data in the form of tables, lists, and trees. However, when implementing new models for list and table-like data structures, the QAbstractListModel and QAbstractTableModel classes are better starting points because they provide appropriate default implementations of common functions. Each of these classes can be subclassed to provide models that support specialized kinds of lists and tables. The process of subclassing models is discussed in the section on Creating New Models. Qt4provides some ready-made models that can be used to handle items of data : QStringListModel is used to store a simple list of QString items. QStandardItemModel manages more complex tree structures of items, each of which can contain arbitrary data. QDir- Model provides information about files and directories in the local filing system. QSqlQueryMo del, QSqlTableModel, and QSqlRelationalTableModel are used to access databases using model/view conventions. If these standard models do not meet your requirements, you can subclass QAbstractItemModel, QAbstractListModel, or QAbstractTableModel to create your own custom models. Views. Complete implementations are provided for different kinds of views : QListView displays a list of items, QTableView displays data from a model in a table, and QTreeView shows

10.1 Architecture Modèle/Vue 97 model items of data in a hierarchical list. Each of these classes is based on the QAbstractItemView abstract base class. Although these classes are ready-to-use implementations, they can also be subclassed to provide customized views. The available views are examined in the section on View Classes. Delegates. QAbstractItemDelegate is the abstract base class for delegates in the model/- view framework. Since Qt44.4, the default delegate implementation is provided by QStyledItemDelegate, and this is used as the default delegate by Qt4 s standard views. However, QStyledItemDelegate and QItemDelegate are independent alternatives to painting and providing editors for items in views. The difference between them is that QStyledItemDelegate uses the current style to paint its items. We therefore recommend using QStyledItemDelegate as the base class when implementing custom delegates or when working with Qt4style sheets. Delegates are described in the section on Delegate Classes. Sorting. There are two ways of approaching sorting in the model/view architecture ; which approach to choose depends on your underlying model. If your model is sortable, i.e, if it reimplements the QAbstractItemModel ::sort() function, both QTableView and QTreeView provide an API that allows you to sort your model data programmatically. In addition, you can enable interactive sorting (i.e. allowing the users to sort the data by clicking the view s headers), by connecting the QHeaderView ::sortindicatorchanged () signal to the QTableView ::sortbycolumn() slot or the QTreeView ::sortbycolumn () slot, respectively. The alternative approach, if your model do not have the required interface or if you want to use a list view to present your data, is to use a proxy model to transform the structure of your model before presenting the data in the view. This is covered in detail in the section on Proxy Models. Convenience Classes. A number of convenience classes are derived from the standard view classes for the benefit of applications that rely on Qt4 s item-based item view and table classes. They are not intended to be subclassed, but simply exist to provide a familiar interface to the equivalent classes in Qt43. Examples of such classes include QListWidget, QTreeWidget, and QTableWidget ; these provide similar behavior to the QListBox, QListView, and QTable classes in Qt4 3. These classes are less flexible than the view classes, and cannot be used with arbitrary models. We recommend that you use a model/view approach to handling data in item views unless you strongly need an item-based set of classes. If you wish to take advantage of the features provided by the model/view approach while still using an item-based interface, consider using view classes, such as QListView, QTableView, and QTreeView with QStandardItemModel.

98 Chapitre 10. Programmation Modèle-Vue-Contrôleur (MVC) 10.2. Using Models and Views Cette section décrit l implémentaion du concept modèle/vue dans Qt4. Each section provides an example of use, and is followed by a section showing how you can create new components. 10.2.1. Introduction. Two of the standard models provided by Qt4are QStandardItemMo del and QDirModel. QStandardItemModel is a multi-purpose model that can be used to represent various different data structures needed by list, table, and tree views. This model also holds the items of data. QDirModel is a model that maintains information about the contents of a directory. As a result, it does not hold any items of data itself, but simply represents files and directories on the local filing system. QDirModel provides a ready-to-use model to experiment with, and can be easily configured to use existing data. Using this model, we can show how to set up a model for use with ready-made views, and explore how to manipulate data using model indexes. 10.2.2. Using Views with an Existing Model. The QListView and QTreeView classes are the most suitable views to use with QDirModel. The example presented below displays the contents of a directory in a tree view next to the same information in a list view. The views share the user s selection so that the selected items are highlighted in both views. We set up a QDirModel so that it is ready for use, and create some views to display the contents of a directory. This shows the simplest way to use a model. The construction and use of the model is performed from within a single main() function : 1 int main(int argc, char *argv[]) 2 { QApplication app(argc, argv) ; 3 QSplitter *splitter = new QSplitter ; 4 QDirModel *model = new QDirModel ; The model is set up to use data from a default directory. We create two views so that we can examine the items held in the model in two different ways :

10.3 Les classes QStandardItemModel et QStandardItem 99 1 QTreeView *tree = new QTreeView(splitter) ; 2 tree->setmodel(model) ; 3 tree->setrootindex(model->index(qdir :: currentpath())) ; 1 QListView *list = new QListView(splitter) ; 2 list->setmodel(model) ; 3 list->setrootindex(model->index(qdir :: currentpath())) ; The views are constructed in the same way as other widgets. Setting up a view to display the items in the model is simply a matter of calling its setmodel() function with the directory model as the argument. The calls to setrootindex() tell the views which directory to display by supplying a model index that we obtain from the directory model. The index() function used in this case is unique to QDirModel ; we supply it with a directory and it returns a model index. Model indexes are discussed in the Model Classes chapter. The rest of the function just displays the views within a splitter widget, and runs the application s event loop : 1 splitter->setwindowtitle("two views onto the same directory model") ; 2 splitter->show() ; 3 return app.exec() ; \} In the above example, we neglected to mention how to handle selections of items. This subject is covered in more detail in the chapter on Handling Selections in Item Views. Before examining how selections are handled, you may find it useful to read the Model Classes chapter which describes the concepts used in the model/view framework. 10.3. Les classes QStandardItemModel et QStandardItem On peut voir un QStandardItemModel comme un ensemble de QStandardItem, dans lesquel on navigue à l aide de QModelIndex. La classe QStandardItem fournit les items de base pour le modèle QStandardItemModel Ces items contiennent usuellement du texte, des icônes ou des checkboxes. Chaque item peut avoir son propre fond setbackground(). Le texte de chaque item à ses propres police et couleur, spécifiés par les méthodes setfont() et setforeground(). Par défaut, les items sont enabled, editable, selectable, checkable, et peuvent être utilisés comme source ou destination pour des opérations drag n drop.

100 Chapitre 10. Programmation Modèle-Vue-Contrôleur (MVC) Il est possible de stocker dans un item des données spécifiques à votre application, à l aide de la méthode setdata(). Un même item peut stocker plusieurs données, chacune avec son rôle. On accède à chacune d entre elles en écriture (ou en lecture) avec la syntaxe setdata(data, role) (ou data(role )). Les valeurs possibles pour le paramètre role sont décrits ci-dessous (10.3.1). Chaque item peut avoir une table bi-dimensionnelle d items enfants. Ce qui permet la construction d hiérarchies d items. La hiérarchie typique est l arbre, auquel cas, la table des enfants est une table à une seule colonne (une liste). Les dimensions de la table des enfants sont données par setrowcount() et setcolumncount (). Les items sont positionnés dans la table des enfants par setchild(). New rows and columns of children can also be inserted with insertrow(), insertcolumn(), appendrow() et appendcolumn() permettent d ajouter des lignes et colonnes d enfants. Une ligne d enfants peut être supprimée avec removerow() ou takerow() ; Une colonne d enfants peut être supprimée avec removecolumn() ou takecolumn() ; On peut trier les enfants avec sortchildren(). 10.3.1. Les rôles essentiels. 10.4. Model Classes 10.4.1. Basic Concepts. In the model/view architecture, the model provides a standard interface that views and delegates use to access data. In Qt4, the standard interface is defined by the QAbstractItemModel class. No matter how the items of data are stored in any underlying data structure, all subclasses of QAbstractItemModel represent the data as a hierarchical structure containing tables of items. Views use this convention to access items of data in the model, but they are not restricted in the way that they present this information to the user.

10.4 Model Classes 101 Models also notify any attached views about changes to data through the signals and slots mechanism. This chapter describes some basic concepts that are central to the way item of data are accessed by other components via a model class. More advanced concepts are discussed in later chapters. 10.4.2. Model Indexes. To ensure that the representation of the data is kept separate from the way it is accessed, the concept of a model index is introduced. Each piece of information that can be obtained via a model is represented by a model index. Views and delegates use these indexes to request items of data to display. As a result, only the model needs to know how to obtain data, and the type of data managed by the model can be defined fairly generally. Model indexes contain a pointer to the model that created them, and this prevents confusion when working with more than one model. QAbstractItemModel*model= index.model() ; Model indexes provide temporary references to pieces of information, and can be used to retrieve or modify data via the model. Since models may reorganize their internal structures from time to time, model indexes may become invalid, and should not be stored. If a long-term reference to a piece of information is required, a persistent model index must be created. This provides a reference to the information that the model keeps up-to-date. Temporary model indexes are provided by the QModelIndex class, and persistent model indexes are provided by the QPersistentMo delindex class. To obtain a model index that corresponds to an item of data, three properties must be specified to the model : a row number, a column number, and the model index of a parent item. The following sections describe and explain these properties in detail.

102 Chapitre 10. Programmation Modèle-Vue-Contrôleur (MVC) The diagram shows a representation of a basic table model in which each item is located by a pair of row and column numbers. By passing the relevant row and column numbers to the model we obtain a model index that refers to an item of data. QModelIndex indexa = model->index(0, 0, QModelIndex()) ; QModelIndex indexb = model->index(1, 1, QModelIndex()) ; QModelIndex indexc = model->index(2, 1, QModelIndex()) ; Top level items in a model are always referenced by specifying QModelIndex() as their parent item. This is discussed in the next section. Fig. 10.4.1. Table model : rows and columns 10.4.3. Rows and Columns. In its most basic form, a model can be accessed as a simple table in which items are located by their row and column numbers. This does not mean that the underlying pieces of data are stored in an array structure ; the use of row and column numbers is only a convention to allow components to communicate with each other. We can retrieve information about any given item by specifying its row and column numbers to the model, and we receive an index that represents the item : QModelIndex index = model->index(row, column,...) ; Models that provide interfaces to simple, single level data structures like lists and tables do not need any other information to be provided but, as the above code indicates, we need to supply more information when obtaining a model index. 10.4.4. Parents of Items. The table-like interface to item data provided by models is ideal when using data in a table or list view ; the row and column number system maps exactly to the way the views display items. However, structures such as tree views require the model to expose a

10.4 Model Classes 103 The diagram shows a representation of a tree model in which each item is referred to by a parent, a row number, and a column number. Items A and C are represented as top-level siblings in the model : QModelIndex indexa = model->index(0, 0, QModelIndex()) ; QModelIndex indexc = model->index(2, 1, QModelIndex()) ; Item A has a number of children. A model index for item B is obtained with the following code : QModelIndex indexb = model->index(1, 0, indexa) ; Fig. 10.4.2. Tree Model : parents, rows, and columns more flexible interface to the items within. As a result, each item can also be the parent of another table of items, in much the same way that a top-level item in a tree view can contain another list of items. When requesting an index for a model item, we must provide some information about the item s parent. Outside the model, the only way to refer to an item is through a model index, so a parent model index must also be given : QModelIndex index = model->index(row, column, parent) ; 10.4.5. Item Roles. Items in a model can perform various roles for other components, allowing different kinds of data to be supplied for different situations. For example, Qt ::DisplayRole is used to access a string that can be displayed as text in a view. Typically, items contain data for a number of different roles, and the standard roles are defined by Qt ::ItemDataRole.

104 Chapitre 10. Programmation Modèle-Vue-Contrôleur (MVC) The role indicates to the model which type of data is being referred to. Views can display the roles in different ways, so it is important to supply appropriate information for each role. The Creating New Models section covers some specific uses of roles in more detail. Fig. 10.4.3. Item roles We can ask the model for the item s data by passing it the model index corresponding to the item, and by specifying a role to obtain the type of data we want : QVariant value = model->data(index, role) ; Most common uses for item data are covered by the standard roles defined in Qt ::ItemDataRole. By supplying appropriate item data for each role, models can provide hints to views and delegates about how items should be presented to the user. Different kinds of views have the freedom to interpret or ignore this information as required. It is also possible to define additional roles for application-specific purposes. 10.4.6. Summary of Concepts. Model indexes give views and delegates information about the location of items provided by models in a way that is independent of any underlying data structures. Items are referred to by their row and column numbers, and by the model index of their parent items. Model indexes are constructed by models at the request of other components, such as views and delegates. If a valid model index is specified for the parent item when an index is requested using index(), the index returned will refer to an item beneath that parent item in the model. The index obtained refers to a child of that item.