Vous êtes sur la page 1sur 112

C.T.

Glody BIMA LEMINDE ALI-NGAKONG

COURS DE PROJET DE PROGRAMMATION 2


A l’usage des étudiants de deuxième année de graduat en informatique
de gestion de l’Université Simon KIMBANGU.

Août 2022
2

INTRODUCTION
Ce cours de Projet de programmation 2 est destiné aux étudiants de deuxième graduat en
Informatique de Gestion. Son intérêt réside par le fait qu’il vient ajouter une touche
pratique dans la formation des étudiants futurs informaticiens en vue de leur permettre de
réaliser des applications informatiques qui tiennent compte des réalités professionnelles.
L’informatisation est le phénomène le plus important de notre époque. Elle intervient
maintenant dans tous les secteurs de l’entreprise et est devenue une nécessité.
Actuellement, l’informatique est au cœur de toutes les grandes entreprises. Les besoins
des entreprises étant dictés à la fois par la concurrence, les besoins sans cesse croissants
des utilisateurs et l’environnement en perpétuel évolution, il est tout à fait logique pour
les organisations de se doter des applications informatiques dignes de leur permettre
l’atteinte des objectifs en temps opportun et au moindre coût.
Cependant, une application informatique est un ensemble d’instructions soumises à
l’ordinateur pour résoudre un problème donné ; cette dernière peut être écrite dans un
langage exploitable ou compréhensible par l’ordinateur.
La mise en œuvre d’une application informatique (programme) exige d’une part les
respects de normes informatiques qui ne sont rien d’autres que les méthodes utilisées par
l’informaticien et d’autre part le langage de programmation lié à ces méthodes.
Pour arriver à satisfaire le client (les entreprises, organisations, etc.), il s’avère nécessaire
de faire une bonne planification et respecter les normes informatiques y relatives. Ainsi,
ce cours fait un tour d’horizon de deux langages de programmation à savoir : le langage
python en sa version 3.5 ainsi que le WinDev dans sa version 25.
3

I. OBJECTIFS DU COURS
L’objectif principal de ce cours est de renforcer la pratique des étudiants dans le domaine
de programmation en vue de leur permettre de réaliser des projets informatiques
performants qui tiennent compte des réalités professionnelles.
L’étudiant qui aura suivi ce cours avec une attention particulièrement soutenue sera en
mesure de :
 Savoir analyser et traduire les besoins fonctionnels en logiciels fonctionnels
 Bien travailler dans une équipe de développement
 Développer plus simplement des applications informatiques performantes.
Ce cours prend appui sur l’enseignement des cours ci-après : algorithme, langage python
et WinDev et en constituent une base.

II. METHODE EXPLOITEE


Le cours sera dispensé en appliquant la méthode interro-active ; donc l’étudiant sera lui-
même au centre de sa formation et aura deux (2) volets :

- L’exposé par l’enseignant : 15 heures


- La réalisation des projets par les étudiants : 30 heures
Après l’exposé magistral de chaque partie du cours par le Chef de travaux, les étudiants
réaliseront un projet pour consolider leurs savoirs.

III. STRATEGIES D’EVALUATION


L’évaluation se fera en deux temps :
1. Une partie des points (60%) sera attribuée aux projets que les étudiants réaliseront
sur base des thèmes qui seront proposés. Ces projets seront remis et défendu à la
fin du cours.
2. La seconde partie des points (40%) sera attribuée à un examen oral. Chaque
étudiant reçoit deux grandes questions recouvrant les deux parties du cours. Les
questions de précision permettent à l’étudiant d’augmenter sa côte.

IV. PRE-REQUIS
• Des connaissances minimales en informatique générale ; vous devez savoir ce que
désigne chacune des expressions ci-dessous :
- Windows
- Fenêtre, boite de dialogue
- Logiciel, application
- Menus, Barre d’outils, boutons, cases à cocher, Info-bulle, liste
déroulante (« Combo »), icône
4

• Vous devez savoir utiliser un ordinateur (clavier : Touches Ctrl, Alt, Shift ; souris
: clic, un double clic, un clic Droit), Windows, l’Explorateur.
• Un sens de la logique suffisamment développé car la programmation fait
largement appel à la logique(Algorithme).
• Vous devez savoir programmer dans l’un des langages ci-après : python ou
WinDev
• De la patience, du courage et de la volonté
• Du temps…

Bon courage
5

PREMIERE PARTIE : BASES DE LA PROGRAMMATION


Cette partie tient à clarifier quelques concepts de base en rapport avec la programmation.
Il importe de signaler que la maitrise de ces concepts permettrait aux étudiants de mieux
comprendre les notions fondamentales abordées plus loin, tout en spécifiant des
exemples d’usages de ces concepts.

1.1. Introduction

En informatique, la théorie des langages a pour objectif de décrire les langages formels.
D'un point de vue mathématique, un langage formel est un ensemble de mots, un mot
étant une suite finie de symboles. L'ensemble de ces symboles est appelé alphabet, les
symboles eux- mêmes sont des lettres. Un langage (formel) est décrit et analysé par une
méthode formelle. Plusieurs sortes de mécanismes existent pour arriver à cette fin.

Un environnement moderne de programmation comprend une interface utilisateur, des


outils (compilateur, éditeur de liens, débogueur, méthode d’analyse.) et la base de
données centrale. Des méthodes sont apparues pour formaliser les étapes de
développement. On retiendra notamment la méthode Merise et le langage de
modélisation comme UML.
En résumé, un langage de programmation se compose de quatre parties, à savoir :
 Son vocabulaire qui définit la liste des mots acceptables issus de son alphabet. Cette
couche est dite structure lexicale. Elle décrit les lexèmes ou unités lexicales du
langage, c’est-à-dire la façon dont sont écrits les mots-clés, les identificateurs, les
nombres, les opérateurs et autres symboles utilisés par le langage. Chacun de ces
lexèmes est écrit avec un ou plusieurs caractères. Par exemple, les caractères + et =
servant à représenter en C l’opérateur d’addition et l’opérateur d’affectation
définissent, lorsqu’ils sont utilisés seuls, deux lexèmes que l’on pourra par exemple
appeler PLUS et ASSIGN. Par contre, utilisés ensemble sous la forme +=, ils
correspondent à un autre lexème que l’on pourra par exemple appeler
PLUSASSIGN.
De même un nombre entier, un nombre flottant ou un identificateur correspondront
chacun à un lexème (CST_INT, CST_FLOAT ou IDENT) bien qu’ils puissent être
formés d’un nombre arbitraire de caractères. L’analyse lexicale d’un langage consiste à
regrouper de façon convenable en lexèmes la suite de caractères représentant un
programme.
 Sa grammaire qui donne la syntaxe des phrases à formuler. C’est la couche dite la
structure syntaxique. L’analyse syntaxique extrait de la suite de lexèmes produite la
structure syntaxique du programme qui est définie par une grammaire.
 Sa sémantique, c'est-à-dire la signification des instructions à produire.
 Son exécution, « l’intelligence » à répondre aux commandes de l’utilisateur.
6

1.2. Paradigme de programmation

Un paradigme de programmation fournit (et détermine) la vue qu’a le développeur de


l’exécution de son programme. Les paradigmes de programmation permettent de
"classer" les langages entre eux selon des critères centrés autour essentiellement de
l'abstraction. Si cette abstraction représente la démarche principale qui guide
l’apparition de ces paradigmes...elle cache de multiples dimensions qui se révèlent en
étudiant tant les paradigmes liés aux langages de programmation qu'au développement
lui-même.
Sans être exhaustif, la très grande majorité des langages de programmation qui sont
utilisés de nos jours peuvent avoir une bonne cinquantaine d'années d'existence, comme
dans le cas du C.

1.3 Type des programmations


1.3.1 Programmation ascendante
Dans ce type de programmation, les fonctions de plus bas niveau sont développées,
programmées et testées en premier, puis assemblées pour former les fonctions de niveau
plus élevé.

1.3.2 Programmation descendante


La méthodologie de la programmation descendante définit, en premier lieu, les
fonctionnalités du programme à son niveau le plus élevé pour subdiviser ensuite chacune
d’entre elles en tâches de niveau inférieur.

1.3.3 Programmation linéaire


La programmation linéaire correspond à l’écriture de programmes permettant de
déterminer les solutions optimales de systèmes d’équations ou d’inéquations linéaires. Si
vous utilisé un langage traditionnel de programmation Qbasic, Cobol, pascal…) vous
avez mis en œuvre une programmation linéaire. Le programme est souvent constitué de
trois phases :

Initialisation

Boucle

Finalisation

Fig. 1 Le trois phases d’une application traditionnelle


- L’initiation où l’on peut préparer l’écran ; initialiser les données, ouvrir
les fichiers ;
7

- La boucle, qui peut être l’attente d’un caractère en provenance du clavier


ou lecture d’un enregistrement du fichier. Le caractère ou
l’enregistrement est traité, puis le programme se remet en attente du
suivant ;
- La finalisation, qui effectue les opérations inverses de l’initialisation et
essentiellement une remise en état du système.

Imaginez que le déroulement du programme soit effectué par une araignée qui déroule
son fil derrière elle. Quel que soit la complexité de l’application, si l’on considère
l’exécution du programme depuis son lancement jusqu’au moment où il se termine, un
seul fil est déroulé, et on pourrait théoriquement le démêler pour n’obtenir qu’un fil
rectiligne.

Cela justifie l’appellation linéaire donnée à ce type de programmation on parle


également de programmation procédurale dans la mesure où ce déroulement linéaire
utilise en général des procédures, ensemble d’instructions pouvant être appelés à partir
des divers endroits.

La structure générale d’une programmation traditionnelle est résumée de la


manière suivante :
- Il y a un point de sortie ;
- Entre les deux, il n’y a qu’un fil pour une exécution de l’application ;
Des appels à des procédures ou fonctions peuvent être faits.
1.3.4 Programmation logique
En programmation logique, un programme se compose de faits et de relations logiques,
aboutissant à des conclusions par déduction. Le langage de programmation Prolog
permet ainsi de développer un programme selon ce type de programmation.
1.3.5 Programmation modulaire
En programmation modulaire, un programme est décomposé en plusieurs modules qui
sont compilés séparément. Chaque module exporte des éléments spécifiés (constantes,
types de données, variables, fonctions, procédures), tous les autres demeurant internes au
module.
Les modules clarifient et régularisent les interfaces entre les grandes parties du
programme, favorisant par conséquent la fiabilité de l’ensemble.

1.3.6 Programmation structurée


Un programme écrit en programmation structurée présente un déroulement net, une
conception claire et un certain degré de structure hiérarchique. Ce type de programme
offre plusieurs avantages, comme celui d’être facilement compréhensible et modifiable
par d’autres programmeurs.

1.3.7 Programmation orientée objet


En programmation orientée objet, un programme est considéré comme un ensemble
d’objets distincts, constitués eux-mêmes d’ensembles de structures de données et de
8

routines (sous-programmes) intégrées. Chaque objet appartient à une classe qui définit
les structures de données et les routines qu’il contient.

Si, par exemple, on crée une classe nommée « Cartable », l’objet « mon cartable »
faisant partie de la classe « cartable » est appelé instance de « cartable ». Toute classe
peut donc être utilisée comme variable dans ce type de programme, dont les objets ainsi
définis interagissent.

a) Objectifs :
 Lier les données et les fonctions qui les manipulent afin d’éviter des accès aux
données par des fonctions non autorisées ;
 Obtenir une meilleure abstraction en cachant l’implémentation des techniques
utilisées et en ne rendant visible que des points d’entrée. Ainsi, si
l’implémentation change, le code utilisateur n’est pas affecté ;
 Réutiliser l’existant dans un souci de productivité ;
 Traiter les erreurs localement au niveau des objets sans que cela ne perturbe les
autres parties du programme ;
 Faciliter la maintenance.
1.3.8 Programmation de macros
De nombreux programmes comprennent des macro-instructions, séquences
d’instructions prédéfinies auxquelles on accède par une combinaison de touches ou par
une commande très simple. Ces macros offrent l’avantage de supprimer les
manipulations répétitives et d’accéder plus facilement aux opérations courantes.
9

DEUXIEME PARTIE : DEVELOPPEMENT DES


APPLICATIONS GRAPHIQUES EN PYTHON
10

2.1. Introduction
Le langage de programmation Python a été créé en 1989 par Guido van Rossum, aux
Pays-Bas. Le nom Python vient d’un hommage à la série télévisée Monty Python’s
Flying Circus dont G. van Rossum est fan. La première version publique de ce langage a
été publiée en 1991. La dernière version de Python est la version 3. Plus précisément, la
version 3.10 a été publiée l’an dernier. La version 2 de Python est désormais obsolète et
cessera d’être maintenue après le 1er janvier 2020. Dans la mesure du possible évitez de
l’utiliser.
La Python Software Foundation 1 est l’association qui organise le développement de
Python et anime la communauté de développeurs et d’utilisateurs. Ce langage de
programmation présente de nombreuses caractéristiques intéressantes :
 Il est multiplateforme. C’est-à-dire qu’il fonctionne sur de nombreux systèmes
d’exploitation : Windows, Mac OS X, Linux, Android, iOS, depuis les mini-
ordinateurs Raspberry Pi jusqu’aux supercalculateurs.

 Il est gratuit. Vous pouvez l’installer sur autant d’ordinateurs que vous voulez
(même sur votre téléphone!).

 C’est un langage de haut niveau. Il demande relativement peu de connaissance sur


le fonctionnement d’un ordinateur pour être utilisé.

 C’est un langage interprété. Un script Python n’a pas besoin d’être compilé pour
être exécuté, contrairement à des langages comme le C ou le C++.

1
. https://www.python.org/psf/
11

 Il est orienté objet. C’est-à-dire qu’il est possible de concevoir en Python des
entités qui miment celles du monde réel (une cellule, une protéine, un atome, etc.)
avec un certain nombre de règles de fonctionnement et d’interactions.

 Il est relativement simple à prendre en main2.

 Enfin, il est très utilisé en analyse de données.


Toutes ces caractéristiques font que Python est désormais enseigné dans de nombreuses
universités à travers le monde entier, depuis l’enseignement secondaire jusqu’à
l’enseignement supérieur.

2.2. Origine de Tkinter

La bibliothèque Tkinter trouve son origine dans le langage de programmation TCL. Ce


langage dispose d’une extension nommée Tk et permettant de réaliser des interfaces
graphiques. Tkinter est un binding de l’extension Tk. D’ailleurs, Tkinter signifie Tk
interface. Python n’est pas le seul langage ayant réalisé un binding de Tk; c’est aussi le cas
des langages OCaml, Perl et Ruby.
Tk est lié à CPython par une extension écrite en C, dans le fichier _tkinter.c.

2.3. Quelques widgets

Le widget label
Un label est un widget constitué d’un court message textuel (en français, on
devrait dire étiquette). Par exemple :

2
. Nous sommes d’accord, cette notion est très relative.
12

Le label est créé avec le constructeur Label. Le texte est transmis avec l’option text et on
peut aussi changer la police avec l’option font.
Un label est un des widgets les plus simples et est souvent utilisé. Il est soit statique (une
description par exemple) soit dynamique (comme un compteur ou une durée qui
évoluent). Voici un exemple de labels qui indiquent le nombre de carrés générés
aléatoirement sur un canevas

75

Ici, il y a deux labels :


— un label statique, à gauche, qui porte la mention Nombre de carrés,
— un label dynamique, à droite, qui indique la valeur du nombre de carrés
présents sur le canevas.
13

HEIGHT

— Ligne 25 : création d’un label descr portant le texte "Nombre de carrés" et


placé en bas à gauche de l’interface.
— Ligne 28 : création d’un label compteur placé en bas à droite et indiquant
le nombre de carrés.
— lignes 25 et 28 : un label est créé avec le constructeur Label qui est un
widget Tkinter. Un label peut utiliser une police que l’on définit avec
l’option font.
14

— Ligne 34 : toutes les demi-secondes, le texte du label est mis à jour en


indiquant le nombre de carrés aléatoires (cf. fonction dessiner lignes 13-
23) présents sur le canevas.
— Ligne 33 : instruction pour mettre à jour le label compteur.

Créer et intégrer un widget en une seule instruction

L’interface graphique suivante :

montre un court morceau de texte et qui est placé dans un widget de type
label. Le code correspondant est :

La création du widget (ligne 4) et l’intégration du widget dans la fenêtre en utilisant un


gestionnaire de géométrie (pack ici, ligne 5) sont effectuées dans deux instructions
différentes. Toutefois, la variable lbl n’est pas réutilisé ailleurs dans le code. Dans ce cas, il
est possible de faire les deux opérations en une seule ce qui dispense de définir une variable
désignant le widget. D’où le code plus simple suivant :

— Ligne 4 : création et placement du widget en une seule instruction.


Attention, bugs en vue!

Cette méthode n’est pas toujours bien comprise et peut être source de bugs, en particulier
chez les débutants. Il est donc préférable de l’éviter, surtout que son bénéfice est réduit.
Dès qu’une interface a un peu de complexité, il est rare qu’un widget qui a été crée ne
15

soit pas référencé dans le code et donc l’astuce ci-dessus devient une nuisance. Certains
pensent alors utiliser un code comme celui-ci :

L’intention ici est d’afficher dans la console (cf. ligne 6) le contenu du label ("coucou",
ligne 4). Certes, une variable lbl a été créée sauf qu’elle ne référence pas le label mais le
retour de l’appel Label(...).pack() qui lui vaut None. Donc la ligne 6 va planter le
programme :

print(lbl["text"])
TypeError: ’NoneType’ object is not subscriptable
Pour s’en sortir, il faut découpler la création du widget et son placement avec la méthode
pack.

D’où le code correct suivant :


16

Le widget bouton
Tkinter dispose d’un widget bouton avec la classe Button. On peut donc
créer un bouton qui réagira de façon appropriée à chaque clic du bouton.
Exemple :

HEIGHT

qui produit :

Lorsque l’utilisateur clique sur le bouton, un carré coloré aléatoire est placé sur le
canevas. Décrivons le code en rapport avec le bouton :
— Ligne 19 : un bouton est créé. Il permet l’affichage du texte avec l’option text.
— Ligne 19 : on peut donner une option command au bouton : cette option doit pointer
vers la fonction qui sera exécutée lorsque l’utilisateur appuie sur le bouton. Cette
17

fonction doit être définie avant la définition du bouton (ici ligne 13, la fonction
dessiner). Ce type de fonction de commande ne doit recevoir aucun argument.
— lignes 13-17 : lorsqu’on clique sur le bouton, la fonction dessiner est appelée. Cette
fonction dessine avec une couleur aléatoire (ligne 14) à une position aléatoire (lignes
15-16) un carré sur le canevas (ligne 17).
La longueur variable de texte dans un bouton peut modifier la taille du bouton et donc de
la fenêtre parente. Pour éviter cela, on peut imposer des dimensions au bouton :
— height = nombre de lignes
— width = nombre de caractères
Si plus de caractères que la capacité permise par la largeur sont donnés dans le texte, des
caractères seront invisibles :

qui affiche après un clic sur le bouton :

On peut afficher un bouton coloré avec l’option bg (qui signifie background) :


18

Un bouton pour montrer une image

Un bouton, de même que le canevas est un widget. Le code ci-dessous place


un bouton à côté d’un canevas :

— Ligne 8 : un bouton est construit avec le constructeur Button (c’est une classe).
— Ligne 9 : comme pour tout widget, il faut l’inclure dans son environnement avec une
méthode particulière, ici la méthode pack.
— Ligne 8 : le texte passé dans l’option text est affiché sur le bouton.
— Si on clique sur le bouton, rien ne se passe de visible. Pour lier une action à un bouton,
il faudrait lui passer une option command.
19

Donnons une possibilité d’action au bouton : chaque fois qu’on clique le bouton, un logo
80x80 est dessiné sur le canevas :

— Ligne 15 : une option command a été donnée au constructeur Button :


command référence une fonction sans paramètre, ici la fonction show,
qui est exécutée à chaque pression sur le bouton.
— Lignes 11-13 : la fonction show ne peut prendre aucun paramètre; elle
dessine un logo Python aléatoire sur le canevas.

Slider basique

Un slider (en français, un curseur) est un widget permettant de modifier une


variable ou un état en faisant glisser un curseur sur un axe :
20

Dans l’exemple ci-dessus, le curseur est mobile et indique le rayon du cercle

Ligne 15 : création d’un curseur.

qui est dessiné sur le canevas.


— Ligne 15 : quelques options du curseur :
— orient : l’orientation horizontale ou verticale
— command : la fonction qui est appelée quand on déplace le curseur (ici, la fonction
rayon)
21

— from_ : la valeur (numérique) en début du curseur (par défaut à gauche pour une
orientation horizontale) transmise à la fonction de commande; le blanc souligné qui
termine from_ est utilisé car on ne peut pas utiliser la variable from puisque c’est un
mot-clé du langage Python.
— to : la valeur (numérique) en fin de curseur (par défaut à droite pour une orientation
horizontale) transmise à la fonction de commande;
— Lignes 9-18 : la fonction de commande appelée lorsque le curseur est modifié. Il
semblerait que, par défaut, cette fonction reçoive en argument une chaîne de caractères
représentant un nombre entier et qui correspond à la position du curseur dans sa
graduation.
On peut aussi changer la longueur du curseur avec l’option length. Par exemple,

curseur = Scale(root, command=tirer, from_=0, to=180, length=200)


va créer un curseur de 200 pixels de longueur.

Le widget entrée

Le widget Entry (entrée en français) est un widget analogue aux champs des formulaires
html : l’utilisateur communique avec le programme en lui transmettant dans une zone de
texte des données écrites au clavier (ou par copier-coller).
Exemple simple

Dans l’exemple ci-dessous, on montre comment une entrée transmet son contenu à une
autre partie du programme. L’utilisateur remplit l’entrée avec du texte et s’il clique sur un
bouton, cela affiche le contenu de l’entrée dans la console :

Voici le code commenté correspondant :


22

— Lignes 5-6 : définition et placement de l’entrée my_entry


— Ligne 9 : une entrée Tkinter a une la méthode get qui permet de récupérer
le contenu de l’entrée. Ici, ce contenu est affiché dans la console avec la
fonction print.
— Ligne 10-11 : définition et placement du bouton qui, lorsqu’il est cliqué,
appelle la fonction afficher.
Autre exemple

Voici un autre exemple, du même ordre mais un peu moins simple :

L’utilisateur entre au clavier un entier dans le champ en bas de la fenêtre et il valide cette
entrée par appui sur la touche ESPACE. Cela provoque immédiatement l’affichage sur le
canvas de carrés aléatoirement placés et en nombre égal à la valeur tapée au clavier.
Le code correspondant est :
23

HEIGHT

25 root.mainloop()
— Ligne 21 : création d’une entrée.
— Ligne 22 : placement de l’entrée dans sa fenêtre.
— Ligne 23 : association de l’entrée my_entry avec une touche du clavier : quand
l’entrée a le focus, si on appuie sur une certaine touche indiquée comme premier
argument de bind (dans notre exemple la touche ENTRÉE référencée par <Return>),
une fonction de commande est exécutée (ici la fonction dessiner)
— Lignes 13-19 : la fonction appelée lorsque l’entrée est validée. Cette fonction reçoit
un événement event. Cet événement correspond à l’appui sur la touche ENTRÉE mais
n’est pas véritablement utilisé par la fonction dessiner. L’objet my_entry dispose
d’une méthode get permettant de capturer le contenu textuel de l’entrée, ici un entier.
Cet entier est lu sous forme de chaîne de caractères, donc il faut le convertir avec la
fonction int (ligne 14). Le reste du code crée un carré aléatoire et le dessine sur le
canevas.
Noter que le widget Entry ne gère pas automatiquement la validation du contenu de
l’entrée par appui sur une touche (cf. ligne 23).

Options d’un widget : lecture, écriture


Un widget (par exemple un canevas, un bouton, etc) est initialisé avec son
constructeur. Par exemple, ci-dessous
24

à la ligne 4, le constructeur Label définit le texte du label, la couleur de fond


et sa longueur. Une fois le widget construit, on peut souhaiter consulter la
valeur d’une option mais aussi la modifier. C’est possible de deux façons :
— soit en utilisant le widget comme on utiliserait un dictionnaire,
— soit en utilisant la méthode configure et un ou plusieurs arguments
nommés pour accéder aux options.
Le code ci-dessous illustre ces deux possibilités :

1 from tkinter import


*
En lecture, on peut accéder, par exemple, à la valeur de l’option text du label
en utilisant :
— le label comme dictionnaire (ligne 7)
— la méthode configure (ligne 8)
— la méthode cget (ligne 9) dont le résultat est moins facilement
interprétable. Concernant les modifications (« écriture »), soit le code
suivant :
25

Le programme crée un label contenant le texte Encore! et modifie le texte du


label en le répétant 5 fois et en terminant en ajoutant le mot FIN.
Ci-dessous l’évolution du widget :

Deux méthodes sont ici utilisées :


— Ligne 8 : on accède à l’option du label L en utilisant L comme un
dictionnaire avec pour clé le nom de l’option, donc ici, on modofoe
L[’text’];
— Ligne 11 : on accède à l’option du label L en utilisant la méthode
configure du label et l’argument nommé correspondant à l’option que l’on
veut changer, ici text.
26

Image sur un bouton


Sur un bouton, il est usuel de placer du texte. Mais on peut placer une image à
la place de texte (ci-dessous, deux images sont placées) :

On suppose qu’on a placé une image python.gif à la racine du fichier source.


Le code correspondant est :
1 from tkinter import *
2
3 root = Tk()
4 logo = PhotoImage(file="python.gif")
5 btnA=Button(root, image=logo)
6 btnA.pack(padx=10, pady=10)
7 btnB=Button(root, width=50, height=50, image=logo) 8 btnB.pack(padx=10,
pady=10)
9
10 root.mainloop()
— Ligne 5 : on crée un bouton repéré par une image; aucune dimension
n’est donné donc le bouton prend la taille de l’image. Noter que l’image
doit être converti au format PhotoImage (ligne 4).
— Ligne 7 : une autre image est créée. On lui impose des dimensions. Les
dimensions sont mesurées sur l’image cible à partir de son coin supérieur
gauche.
— Lignes 5 et 7 : pour simplifier l’illustration, le clic sur les boutons n’a
aucune action visible.

Options d’un widget entrée


Le code ci-dessous montree quelques options d’une entrée :
1 from tkinter
import * 2
root = Tk()
3
27

4 my_entry = Entry(root,
5 font=’Arial 60 bold’,
6 width=’5’,
7 bg=’lavender’,
8 insertofftime=500,
9 relief=FLAT)
10 my_entry.pack(padx=20, pady=20) 11 my_entry.focus_set()
12
13
root.mainl
oop() qui
produit :

Par défaut, une entrée n’a pas le focus autrement dit, elle n’est pas en état de lire des
caractères. On l’active :
— soit avec la touche TAB
— soit en cliquant dans la zone de saisie de l’entrée.
Mais on peut forcer l’acquisition du focus par la méthode focus_set (ligne 11). Donc, dès
que l’application s’ouvre, l’entrée est réceptive aux touches de clavier.
La couleur de fond de la zone d’édition est déterminée par l’option bg (ligne 7).
Il est possible de choisir avec l’option font la police et la taille de la police de la zone de
saisie (ligne 5). On peut décider de limiter le nombre de caractères que peut accepter
l’entrée (ligne 5). La taille en pixels de la zone de saisie en sera modifiée mais cette taille
dépendra aussi la taille de la police.
Quand le focus est actif, un curseur guide la saisie. Le clignotement du curseur est
modifiable avec l’option insertofftime (ligne 500) qui désigne la durée en millisecondes
entre deux apparition du curseur (si 0 alors aucun clignotement). La couleur du curseur
est contrôlée par l’option insertbackground.
L’entrée est un entourée d’un bord dont l’épaisseur est contrôlée par l’option border.
Le style de relief de l’entrée est contrôlé par l’option relief, par défaut, on a
relief=SUNKEN (ligne 9).
On peut contrôler le texte entré par l’utilisateur grâce à l’option textvariable qui doit être
initialisée sur une variable de contrôle, par exemple StringVar. Un exemple est fourni au
28

paragraphe Exemple d’utilisation de StringVar. Noter qu’il n’y a pas d’option text valide
pour un widget Entry.
Une Entry ne permet pas d’entrer du texte sur plusieurs lignes. Utiliser plutôt le widget Text
(non décrit sur mon site) ainsi que c’est indiqué par Fredrik Lundh :
The entry widget is used to enter text strings. This widget allows the user to enter one line
of text, in a single font. To enter multiple lines of text, use the Text widget.

Effacer une entrée


Dans l’interface ci-dessous, on a écrit un entier dans une entrée :

Après validation de l’entrée (on appuie sur ENTER), des carrés ont été dessinés sur le
canevas et l’entrée a été effacée si bien que l’utilisateur peut directement réutiliser l’entrée pour
générer des carrés sur le canevas.
Pour effacer la zone d’édition d’une entrée, on utilise la méthode Entry.delete :
29

HEIGHT

— Ligne 21 : on efface l’entrée depuis le caractère d’indice 0 (le premier)


jusqu’au dernier (END).

Gestion du curseur
Initialiser le placement du curseur
30

On peut modifier dynamiquement la position du curseur, en particulier à


l’initialisation. On reprend le code d’introduction du widget Slider :

Initialement (from_ dans la ligne 15), le curseur est placé à 0. Comment


faire en sorte que le curseur soit placé conformément au rayon d’un cercle
qui serait tracé au départ? A l’exemple de la figure ci-dessous
31

où on voit que le curseur est, à l’ouverture de la fenêtre, positionné à 42 et


que le cercle a un rayon de 42.
Deux méthodes sont possibles. La plus simple consiste à utiliser la méthode
set du widget Scale :

— Ligne 18 : la position du curseur est initialisée et l’appel correspondant


(avec r=42) de la fonction de commande est effectué.
32

Une autre méthode consiste à utiliser les variables dynamiques (ou encore
appelées variables de contrôle) proposées par Tkinter. Ici, on va utiliser IntVar
(le rayon est entier). Voici le code :

— Ligne 20 : La position du curseur est contrôlée par l’objet défini par l’option variable
(ligne
20).
— ligne 16 : Initialement, variable (ligne 20) pointe vers une variable dynamique radius.
— Ligne 18 : radius est initialisée à un rayon r. La conséquence est qu’à l’ouverture, le
curseur pointera vers 42.
— Ligne 19 : L’initialisation de la variable de contrôle radius n’entraîne pas d’exécution
de la fonction de commande rayon. Pour que la dimension du cercle soit en conformité
avec la valeur indiquée par le curseur, le cercle est tracé.
Changer le sens de progression

Par défaut, le sens croissant du curseur est défini par défaut :


— de la gauche vers la droite — du haut vers le bas.
Pour changer ce sens il suffit d’inverser les valeurs donnée à from_ et to. Ci-
dessous, le générateur de cercle avec un curseur progressant vers la gauche et
non vers la droite :
33

L’état d’un curseur est déterminé par une variable state que l’on peut
modifier pour le rendre inactif. Par défaut, l’état est normal. L’état déactivé
est disabled. Par exemple, dans le code ci-dessous :
34

dès que le curseur dépasse 100, il est désactivé (lignes 11-12).

Faire un menu

déroulant Voici

un menu

déroulant :
35

Le code correspondant est le suivant

8
9 def rose():
10 cnv[’bg’]="pink"
11
12 def rouge():
13 cnv[’bg’]="red"
14
15 def orange():
16 cnv[’bg’]="orange"
17
18 def violet():
19 cnv[’bg’]="purple"
20
21 # Barre de menus
22 mon_menu = Menu(root)

23 root.config(menu=mon_menu)

24
25 # Menu légumes
26 legumes = Menu(mon_menu, tearoff=0)

27 legumes.add_command(label="Radis", command=rose)

28 legumes.add_separator()

29 legumes.add_command(label="Tomate", command=rouge)

30 mon_menu.add_cascade(label="Légumes", menu=legumes)

31
32 # Menu fruits
33 fruits = Menu(mon_menu, tearoff=0)
34 fruits.add_command(label="Raisin", command=violet)
35 fruits.add_command(label="Orange", command=orange) 36
mon_menu.add_cascade(label="Fruits", menu=fruits)
37
38 root.mainloop()
36

Prise de focus dans un canevas


Si un canevas est une surface devant capturer des événements du clavier, il
faut lui donner le focus pour qu’il puisse réagir aux touches de clavier.
Soit l’application suivante qui à l’ouverture se présente ainsi :

Comme le montre le liseré noir autour du canevas, le canevas a le focus : il


est en mesure de réceptionner des événements du clavier. En particulier ici,
si l’utilisateur appuie sur n’importe quelle touche, cette action dessine un
carré rouge aléatoire sur le canevas :
37

Pour modifier (voire faire disparaître) le liséré noir autour du canevas, il faut
modifier une option du canevas nommé highlightthickness. Par défaut, il est
de 1 pixel et de couleur noire.

Témoin de prise de focus


Les interfaces graphiques permettent de visualiser la partie de l’interface qui
a le focus. Sous Tkinter, le widget qui a le focus est entouré d’une bande
rectangulaire;
Ci-dessous une interface composée de deux widgets, un canevas et un bouton,
est présentée :
38

Le widget qui a le focus est entouré d’une bande rouge; quand il perd le focus, cette
bande devient rose. Si on appuie sur la touche TAB, le focus passe des widgets à l’autre
et les couleurs rose et rouge sont échangées à chaque changement de focus.
Le code de l’interface précédente montre qu’on peut contrôler les couleurs et la
dimension de la bande de témoin de focus :

— Lignes 8 et 15 : highlightthickness est la largeur en pixels de la bande de


focus. La largeur par défaut est de deux pixels.
— Lignes 9 et 16 : highlightbackground est la couleur de la bande lorsque le
widget n’a pas le focus, ici pink;
39

— Lignes 10 et 17 : highlightcolor est la couleur de la bande lorsque le widget a le focus,


ici red.
Dans l’interface précédente, lorsque le canevas a le focus, l’appui sur la touche ENTRÉE
(cf. ligne 24) dessine un carré aléatoire orange sur le canevas (cf. lignes 18-21). Si le
canevas n’a pas le focus, rien n’est dessiné.

Clavier et focus
Soit un jeu contenant plusieurs widgets, par exemple un canevas et une entrée (un widget
où on peut entrer du texte au clavier). On veut que le canevas réagisse à certains
événements du clavier, par exemple, si on appuie sur la barre d’espace, le joueur saute.
Comme le jeu contient plusieurs widgets, chaque widget a sa propre façon d’écouter le
clavier, un appui sur ESPACE dans le widget entrée n’aura pas même signification que
pour le canevas. Il faut donc qu’il y ait une manière de choisir l’interlocuteur entre le
clavier et les widgets. C’est ce qu’on appelle le focus.
Le focus est acquis en général par appuis successifs sur la touche TAB ou par clic de
souris (si vous cliquez sur un champ d’entrée, le champ prend le focus, comme dans un
formulaire d’une page web). Pour faire en sorte qu’un widget prenne automatiquement le
focus, on utilise la méthode focus_set du widget.
Voici un exemple avec un canevas :

quand on appuie sur la touche ENTRÉE, un carré aléatoire est dessiné sur le
canevas. Voici le
40

HEIGHT

— Ligne 21 : l’appui sur la touche ENTRÉE (si le canevas a le focus) déclenche


l’exécution de la fonction f.
— Ligne 13 : la canevas prend le focus; il pourra réagir au événement du clavier. Sans
cette ligne, le canevas serait inerte, il faudrait lui donner le focus manuellement, soit en
appuyant sur la touche TAB soit en cliquant dans le clavier.
Si la fenêtre contient le canevas comme seul widget, il est plus simple de lier la fonction f
à la fenêtre tout entière au lieu du seul canevas. Il suffit pour cela de changer la ligne 21
en :
1 root.bind(’<Return>’, f)
et on peut alors supprimer la ligne 13 de prise de focus : quand on appuiera sur la touche
ENTREE, automatiquement, le canevas capturera l’appui sur la touche.

Image dans un label


Un label peut aussi porter une image au lieu de texte :
41

Pour cela, on convertit d’abord avec le fichier image avec PhotoImage :


1 from tkinter import *
2
3 root = Tk()
4 logo = PhotoImage(file="python.gif")
5 Label(root, image=logo).pack(padx=15, pady=15)
6
7 root.mainloop() et on utilise
l’option image (ligne 5) de
Label.

Si l’option image est utilisée, l’option text sera sans effet.

Centrer un texte dans un label


Un label admet une option d’alignement (option justify) permettant le
centrage :

6 Je suis un texte long


7 qui souhaiterait
8 être centré sur
9 plusieurs lignes"""
42

10 annonce=Label(height=5, width= 50, text=mon_texte,


11 justify=CENTER, bg=’ivory’)
12 annonce.pack()
13 racine.mainloop())
— Ligne 11 : l’option justify est placée à CENTER ce qui centre le texte
ligne par ligne.
— ligne 11 : pour rendre le texte plus visible, une couleur de fond a été
utilisée dans le widget (option bg pour background).

Utilisation d’une variable de contrôle dans

un label Soit l’interface suivante :

Quand l’utilisateur clique sur le bouton, un label augmente le compteur


d’une unité. On peut effectuer la mise à jour du label en utilisant une
variable de contrôle de Tkinter, ici StringVar :

— Ligne 9 : le texte du label est couplé à une variable de contrôle cpt définie
antérieurement
(ligne 7).
— Lignes 12 et 3-4 : quand le bouton reçoit un clic, la fonction sans
paramètre plus est appelée (ligne 3-4) et son code est exécuté. Ce code
43

récupère la valeur de cpt avec la méthode get; cette valeur est une chaîne
représentant un entier. On convertit cette chaîne en entier, on incrémente
et on met à jour la variable de contrôle cpt. Cela a pour effet de mettre à
jour le label.
On peut aussi se passer de StrVar et modifier directement la valeur du label :

— Ligne 8 : l’option text accepte une entrée numérique (qui n’est pas une
chaîne de caractères).
— Lignes 8 et 3-4 : la fonction de rappel plus met directement à jour le
contenu de l’entrée
(ligne 4).

Le widget Frame
Le widget Frame (cadre en français) est un widget qui sert juste de conteneur,
un peu comme une fenêtre Tk. Ce type de widget est très utile pour
regrouper et organiser des interfaces contenant beaucoup de widgets.
44

— ligne 6 : frame est un widget comme un autre et il doit donc être placé
avec un gestionnaire de géométrie, ici pack.
— ligne 5 : on peut donner une couleur de fond à un frame (on ne peut pas
pour un fenêtre Tk).
— lignes 7 et 10 : un frame est un conteneur et ici, il est le premier
argument des constructeurs Canvas et Button. qui affiche :

On peut donner une largeur et une hauteur à un frame mais la plupart du temps, ces
dimensions sont ignorées (sauf dans certains cas où on inhibe la propagation des
dimensions des widgets).

Boutons radio

Typiquement, on utilise des boutons radio dans des situations de choix conduisant à une
unique réponse comme entre un nombre strictement positif, strictement négatif ou nul :
45

— Lignes 7-9 : un bouton radio est un widget comme un autre; on le crée avec un
constructeur, ici RadioButton et on le place comme n’importe quel widget, ici avec la
méthode pack (et des options d’alignement pour que la sortie ne soit pas trop
disgrâcieuse).
— Lignes 11-13 et 15-16 : on crée et on place de même deux autres boutons radio.
— Pour chaque bouton, on dispose d’une option de texte descriptif (text) et de police
pour le texte (font).
Dans l’exemple ci-dessus, les boutons radio sont purement factices et rien n’est prévu
pour écouter s’ils sont cochés ou pas.
Un exemple avec interaction

Les boutons radio fonctionnent, en principe, par groupes. Par exemple, dans un QCM de
10 questions, il y aura 10 groupes de boutons radio. Toutefois, le programmeur va définir
autant de boutons radio qu’il a besoin (sans tenir compte des groupes) et c’est ensuite lui
qui définera les groupes (expliqué plus loin).
Dans ce qui suit, on va examiner une interface qui montre comment le programme peut
récupérer les informations fournies par un groupe de boutons radio.
L’interface contient cinq widgets :
46

— trois boutons radio qui référencent des fruits,


— un label qui va montrer leur couleur,
— un bouton Couleur qui lorsqu’on le clique, colorie le label avec la couleur du fruit. Au
départ, aucune couleur n’apparaît et, par défaut, c’est le bouton radio de la cerise qui est
activée. Voici le code :

29 text="Orange",
47

30 variable=fruit, 31 value="orange",
32 font="arial 20")
33 orange.pack(anchor="w")
34
35 banane = Radiobutton(
36 root,
37 text="Banane",
38 variable=fruit, 39 value="banane",
40 font="arial 20")
41 banane.pack(anchor="w")
42
43 lbl_fruit = Label(root, bg="white",
width=5) 44 lbl_fruit.pack(padx=50,
pady=50)
45
46 btn = Button(root, text="Couleur",
command=colorer) 47 btn.pack(padx=50,
pady=50)
48
49 root.mainloop()
— ligne 16 : une variable de contrôle (spécificité de Tkinter) initialisée à la
chaîne cerise. Elle initialise les options variable des trois boutons radio.
— Lignes 22 et 23 : les boutons radio sont munis de 2 nouvelles options :
variable et value. Si le bouton radio est activé (donc, on a cliqué dessus),
la valeur de la variable de contrôle sera value.
— Le point qui suit est essentiel : les trois boutons fonctionnent comme un
groupe (un seul des trois doit être coché); pour le faire comprendre à
Tkinter, il faut que leur option variable pointe vers la même variable de
contrôle (lignes 22, 30 et 38), ici appelée fruit (ligne 16).
— Ligne 7 : pour savoir quel bouton est coché, il suffit de regarder le contenu
de la variable de contrôle et de le comparer aux différentes value des
boutons.

Si les boutons radio sont nombreux, on utilisera plutôt une boucle for pour
les créer et les placer. A noter que si votre interface a une certaine
complexité et contient des boutons radio, il peut être approprié d’utiliser le
placement des widgets en grid.
Cas de plusieurs groupes

Si vous avez plusieurs groupements de boutons radio, il faut associer à chaque


groupement sa propre variable de contrôle.
Par exemple, si on avait aussi un groupe de boutons radio pour des légumes :
48
49

— Lignes 17 : une première variable de contrôle pour les fruits (cf. lignes 25 et 33).
— Lignes 19 : une seconde variable de contrôle pour les légumes (cf. lignes 44 et 52).
Déclencher une action

Comme pour d’autres widgets (bouton, entrée), un bouton radio dispose d’une option
permettant de générer une action (appel d’une fonction) lorsqu’un bouton est modifié. Par
exemple, on peut modifier le code ci-dessus pour que, lorsque l’utilisateur clique sur un
bouton radio, la couleur du fruit sélectionné apparaise dans le label :
50
51

— chaque boution radio (par exemple ligne 25) contient une option command
pointant vers la fonction colorer (sans argument, comme l’impose Tkinter)
— Lorsqu’un bouton est cliqué, la valeur commune de la variable de contrôle
fruit (ligne 16) est examinée et la coloration du label est déclenchée (lignes
9-13).

Le widget case à cocher


Tkinter met à disposition le widget Checkbutton montrant du texte et une case
à cocher :

Dans l’application ci-dessus, la label affiche dynamiquement la somme des valeurs des
cases cochées.
Les widgets Checkbutton vont souvent par groupes mais il faut créer autant de widgets
Checkbutton que de cases à cocher. A la différence des boutons-radio dont un seul est
activable, on peut cocher plusieurs cases.
Ci-dessous, on crée 3 simples cases à cocher :

from tkinter import *

root=Tk()

check1=Checkbutton(root, text=2, font=’arial


30’) check1.grid() check2=Checkbutton(root,
text=3, font=’arial 30’)
52

La question qui se pose maintenant est de savoir si une case d’un


Checkbutton check a été cochée. Pour cela, il suffit d’examiner l’option
variable; c’est une variable de contrôle Tkinter qui renvoie l’entier 1 si la
case est cochée et 0 sinon. En réalité, il semble qu’il n’y ait pas d’autre
façon d’avoir accès à cette information.
Utiliser un callback par case

Par ailleurs, on peut définir, pour chaque case à cocher, une fonction
référencée par l’option command et qui sera appelée chaque fois que la case
sera modifiée. Cela fournit une première méthode pour réaliser l’application
présentée tout au début :

1
from tkinter import *
— Lignes 16-21 : on crée les 3 cases à cocher; l’état de la case (cochée ou pas) est
contrôlé par la variable de contrôle v (c’est indispensable ici). Le fonctionnement des
53

cases à cocher défini par Tkinter stipule que le contenu de cette variable est l’entier 1
si la case est cochée, 0 sinon.
— Ligne 19, option text : le texte de la case à l’indice i est i+2.
— Ligne 20 : on utilise la méthode grid pour placer les cases.
— Lignes 14 et 21 : les 3 widgets ainsi que la variable de contrôle correspondante sont
placés en tuple dans une liste.
— Ligne 19, option command : chaque bouton réagit au clic sur la case. Si le bouton est
d’indice i, la fonction qui est appelée est g=f(i) (ligne 11). Noter que f est une fonction
qui renvoie une autre fonction.
— Lorsqu’une case à cocher est modifiée, sa fonction de rappel g (ligne 7), qui dispose
de l’indice i de la case modifiée va exécuter les actions suivantes :
— elle recalcule (ligne 8) la nouvelle somme : en effet, si une case est décochée,
v.get() vaut 0 et donc la valeur du texte n’est pas comptée et sinon, v.get() vaut 1 et
donc la valeur du texte est comptée;
— elle met alors le label à jour (ligne 9).
N’utiliser que des variables de contrôle Tkinter

Une autre façon de mettre à jour le label est d’appeler une fonction chaque fois qu’une
des variables contrôlant chaque case à cocher est modifiée. Cela se fait en utilisant la
méthode trace d’une variable de contrôle. Le code suivant montre le principe de la
méthode trace :

def f(*args):
Chaque fois que la variable de contrôle v est modifiée ailleurs dans le
programme, la fonction f est automatiquement appelée; ici, cette fonction
affiche la valeur contenue dans v. L’argument "w" de trace signifie write
(modification en écriture de v).
Voici le code complet de l’application :
54

— ligne 15 : chaque variable de contrôle v appelera la fonction update


lorsque la case correspondante sera modifiée.
— Lignes 5-7 : la fonction update calcule la somme et met à jour le texte du
label.

Le widget Listbox
Le widget Listbox propose une liste dont les éléments sont sélectionnables à
la souris ou avec les flèches Haut et Bas du clavier :

On crée ce widget avec le constructeur Listbox (une seule majuscule). Voici


le code correspondant à la figure ci-dessus :
55

— Ligne 3 : la liste des chaînes qui vont


apparaître dans le widget. — Lignes 8-14 :
construction et placement du widget — Lignes 9-
13. Certaines options du widget :
— width est le nombre de caractères (pas de pixels)
— height est le nombre d’entrées devant apparaître dans la liste
— font : la police utilisée
— selectbackground : couleur du fond d’une cellule sélectionnée.
— Lignes 16-17 : insertion des éléments dans la liste du widget; END signifie
qu’on place l’item courant après le dernier placé.
En français, une listbox s’appelle une liste de sélection. On ne peut pas placer le
texte d’un item sur plusieurs lignes, comme expliqué par Bryan Oakley.
Il existe de nombreuses autres options et ce widget dispose d’un nombre
important de méthodes, en particulier de modification des items et dont je ne
parlerai pas. Il est possible de faire de la multi-sélection, voir la
documentation de selectmode. Signalons par exemple la possibilité de :
56

— placer le focus avec la méthode focus_set (et qui n’est pas propre à ce
widget) : sans toucher à la souris, on peut se déplacer dans la liste avec la
souris;
— sélectionner une cellule par son indice (à partir de 0)
— modifier la couleur de certaines cellules avec la méthode
itemconfigure. Voici un code qui utilise ces méthodes :

1
from tkinter import *
57

— Lignes 19-20 : placement du focus et sélection du 3e item.


— Lignes 22-25 : on a colorié avec des couleurs alternées les lignes
successives de la liste. Il est également possible de définir les entrées de la
liste comme des variables dynamiques Tkinter, voir le paragraphe
Modification des items d’une listbox.

Action associée

Bien entendu, le déplacement et la sélection d’éléments dans la liste sont


souvent associés à une action. Par exemple, dans l’exemple ci-dessous, le
déplacement dans la liste affiche dans un canevas une couleur adaptée à la
sélection :
58

— Ligne 4 : on crée, en respectant l’ordre, une liste des couleurs associées


aux différents fruits.
— Ligne 38 : on crée un canevas qui va afficher la couleur de l’élément
sélectionné
59

— Ligne 36 : le point essentiel : l’événement virtuel <<ListboxSelect>> qui


réagit chaque fois qu’une cellule est sélectionnée en appelant une fonction,
ici la fonction show qui va placer la bonne couleur sur le canevas.
— Ligne 32 : la méthode curselection de Listbox permet de récupérer l’indice
de la cellule sélectionnée (rappel : début à l’indice 0).
Modification des items d’une listbox

On peut aussi modifier le contenu du texte d’une ligne d’une listbox. Dans
l’exemple ci-dessous :

initialement le texte est en minuscule et au bout de deux secondes, il est placé


en majuscule. Le code correspondant est :
1 from tkinter import *
2
3 root=Tk()
4 MOIS=[’janvier’, ’février’, ’mars’, ’avril’, ’mai’, ’juin’, ’juillet’,
5 ’août’, ’septembre’, ’octobre’, ’novembre’, ’décembre’]
6 z=StringVar(value=MOIS)
60

7 lb=Listbox(root, width=16, fg=’orange’, listvar =z,


8 selectbackground=’pink’, font="Arial 16 bold")
9 lb.pack(side=LEFT, padx=10, pady=10)

— Ligne 17 : au bout de 2,5 secondes après l’ouverture de la fenêtre, la casse


des lignes de la listbox est changée.
— Ligne 6 : le texte de chaque ligne est initialement placé dans une variable
dynamique Tkinter.
— Ligne 15 : la méthode set de la variable dynamique permet une mise à jour
automatique.

Une Listbox et une barre de défilement


Si une Listbox contient beaucoup d’items, on peut parcourir son contenu avec
une barre de défilement :
61

— Ligne 1-2 : pour des raisons d’esthétique, j’ai choisi d’utiliser la barre de
Ttk.
— Ligne 4 : la liste contient 30 items.
— Ligne 19 : on place une barre (par défaut verticale); sa commande pointe
vers le déplacement vertical des items de la liste lbox.
— Ligne 20 : on place soigneusement la barre pour qu’elle recouvre tout le
côté de la liste.
— Ligne 21 : on apparie cette fois la listbox à la barre.

Le widget spinbox

Un spinbox est un widget hybride :


62

Il dispose d’une zone de texte et deux boutons de défilement. A partir d’une succession
d’éléments textuels, dans l’exemple les nombres de 2019 à 2024, ce widget permet de
faire défiler ces élements dans la zone de texte en progressant dans un sens ou dans
l’autre selon les boutons qui vont recevoir un clic. Voici le code d’une version
minimaliste :
spinbox_mini
mal.py from
tkinter import
* root=Tk()

sp= Spinbox(root, from_=2019, to=2024)


sp.pack(padx=10, pady=10)

root.mai
nloop()
qui
produit :

On peut aussi parcourir une liste de chaînes dans la zone de texte :

qui produit :
63

L’intérêt de ce widget est la compacité : il permet de circuler relativement


facilement entre tous les éléments d’une succession tout en prenant le
minimum de place.
Ce widget contient de nombreuses options. Voici un exemple de l’usage de
quelques unes :

spinbox_simple_texte.py

— Ligne 9 : l’option width est le nombre de caractères visibles dans


la zone de texte. — Ligne 10 : bg est la couleur de fond.

— Ligne 11 : justify permet de disposer le texte


dans la zone — Ligne 13 : font permet de
modifier la police et la taille du texte.

— Ligne 12 : par défaut, quand le défilement arrive en bout de succession, le


défilement est bloqué. L’option wrap permet d’aller en un clic à l’autre
extrémité de la succession.

La zone de texte est en fait une entrée (widget de la classe Entry) et est donc modifiable.
Toutefois, comme une liste de chaînes à afficher est en général prédéfinie, l’intérêt de
pouvoir modifier directement la zone de texte n’est pas immédiat. Sans compter la
possibilité d’écraser accidentellement une valeur. Et justement, il est possible de faire en
sorte que la zone de texte soit non modifiable. Il suffit pour cela d’utiliser l’option
state="readonly". Toutefois, ce changement d’état modifie la couleur de fond de la zone
de texte pour bien montrer qu’elle est désactivée, ce qui n’est pas forcément souhaité. On
peut y remédier en définissant une option readonlybackground. Voici un exemple :
64

On peut faire en sorte qu’une action soit exécutée chaque fois que la zone de texte est
modifiée par appui sur un des deux boutons. Pour cela, comme pour la plupart des
widgets de Tkinter, on utilise l’option command. Pour faire simple, on va parcourir des
nombres et on va afficher dans la console le nombre placé dans la zone de texte du
spinbox après clic :
65

— Ligne 20 : l’option command qui pointe vers une fonction f


— Ligne 4 : la fonction f utilisée lorsque un des deux boutons du spinbox est pressé.
Si on veut différencier le bouton sur lequel un clic a été reçu (le haut ou le bas), il faut
procéder autrement (trouvé sur SO) : on définit toujours une option command mais la
commande indiquée va être encapsulée avec la méthode register. Avec un exemple, ce
sera plus simple à comprendre :
66

— Ligne 22 : la commande est fournie sous forme de tuple


— Ligne 22 : le premier élément du tuple est le retour d’un appel à la méthode register,
cf. ligne 9. Cette dernière encapsule une fonction, ici f (ligne 3).
— Ligne 22 : les éléments suivants du tuple représentent le type des paramètres que la
fonction encapsulée va recevoir (ici, un seul paramètre). Ces types sont
conventionnelement définis par Tkinter. Ici par exemple, le type %d représente une
chaîne de caractère valant up si la flèche haut a été pressée et down si c’est la flèche bas.
Bien qu’on parcoure une liste ou une succession, le widget ne semble pas donner la
possibilité d’accéder à l’indice courant dans la succession de la zone de texte visible. Il
n’existe pas de version ttk du widget spinbox.
En français, la widget spinbox devrait se dire saisie rotative.
Barre de progression Ttk

Le module standard Ttk propose une barre de progression. Voici un exemple minimal :
67

— Ligne 1 : il faut impérativement importer ttk explicitement de tkinter. Une


importation de la forme from tkinter import * ne donnera pas accès à Ttk.
— Lignes 5-6 : une barre de progression se crée comme tout autre widget :
construction et placement.
— Ligne 5 : on peut définir la longueur de la barre avec l’option length.
L’option maximum sera expliquée ci-dessous.
— Ligne 8 : lancement de la progression dans la barre.
Par défaut, une barre de progression est horizontale (il existe une option pour
la placer verticale). On peut changer la couleur par défaut en créant un style.
Principe de fonctionnnement d’une barre de progression Ttk

Il existe deux modes de fonctionnement d’une barre de progression : l’option


indeterminate et l’option determinate qui est l’option par défaut. La première option ne
prend pas en compte la proportion de progression d’une tâche, elle sert à indiquer que, par
exemple, une tâche est en cours d’exécution mais pour une durée indéterminée. Cette
option ne sera pas examinée.
Quand une barre de progression évolue, une valeur interne à la barre parcourt, à un
certain rythme, des valeurs décimales entre 0 et une valeur maximum que l’on indique en
option. Lorsque cette valeur est atteinte, la barre arrive visuellement à la fin de son
rectangle de progression.
Mais, curieusement, au lieu de s’arrêter, la progression reprend automatiquement de zéro.
Ce comportement n’est pas modifiable sans utiliser la méthode after d’un widget.
On peut contrôler la valeur en cours de progression avec l’option "value", accessible par
progress["value"] où progress est le nom de la barre de progression.
Enfin, si on lance la barre de progression par progress.start(10) (cf. ligne 8), cela signifie
que la valeur de référence de la barre va évoluer d’une unité toutes les 10 ms. Dans
l’exemple ci-dessus, comme la valeur maximale est à 300, cela signifie que la barre se
remplit en 3 secondes.
On peut d’ailleurs visualiser la duréee de remplissage avec un label :
68

Un appel de start sans paramètre effectue une progression toutes les 50 ms.
Barre de progression qui s’arrête

Lorsque la barre de progression est complètement remplie, on souhaite que la


progression ne reparte pas à zero.

Pour cela, il faut utiliser after et surveiller l’évolution de la variable interne


value jusqu’au moment où elle atteint la valeur de maximum. Voici un code
possible :
69

— Ligne 14 : on surveille la variable value de la barre. Si la barre redémarre à


zéro, c’est que la valeur courante de value diminue.
— Ligne 17 : on redémarre l’animation avec l’ancienne value.
— Lignes 15-16 : on arrête la barre de progression et pour qu’elle remplisse
la zone, value est affecté à la valeur de remplissage maximum.

Modifier les options d’un widget

Ce qui suit est essentiel pour faire fonctionner une interface graphique sous
Tkinter. Il suppose que vous avez un minimum de familiarité avec les
widgets.
Pour illustrer, considérons un jeu du Pendu en version très simplifiée : le
joueur choisit des lettres en cliquant sur des boutons à la recherche de lettres
cachées dans une zone de texte.

On souhaite que :
70

— lorsqu’un clic découvre une lettre présente dans le mot inconnu, la lettre
soit rendue visible et donc que l’astérisque qui couvre la lettre disparaisse;
— tout clic (gagnant ou perdant) sur une lettre jamais encore choisie
provoque la désactivation du bouton sur lequel le joueur a cliqué (c’est
assez naturel puisqu’il n’y a aucune raison que le joueur re-clique sur le
même bouton).
Chaque lettre de la zone de texte est un label. On veut donc qu’à chaque clic :
— le texte du label bascule de * vers la
lettre trouvée; — le bouton soit modifié
(et désactivé).
On doit donc changer l’état des deux widgets. Tous les widgets d’une interface
Tkinter ont des options, souvent très nombreuses. Par exemple, un bouton a
une option state qui peut prendre trois valeurs selon l’état de réceptivité du
widget. De même, un label a une option text qui indique le contenu du texte
qu’on lit sur le label ou encore une option bg pour la couleur de fond du
label. L’état d’un widget est déterminé par l’état de ses options. Ce qui
anime une interface graphique est que ses widgets changent d’état.
Il existe essentiellement deux syntaxes pour changer une option (il existe
une 3e façon moins usuelle qui ne sera que très brièvement évoquée). Ainsi,
dans le jeu du Pendu, pour chager l’astérisque d’un label appelé disons lbl
en, par exemple, la lettre E, on pourra écrire : lbl["text"] = "E"
C’est la syntaxe la plus simple, sous forme de dictionnaire. Bien noter que lbl est
ici une référence vers le widget, le nom de l’option text est placé entre
guillement pour obtenir une chaîne de caractère. Et "E" est la nouvelle
valeur de l’option du widget.
Dans l’exemple du jeu du Pendu, pour désactiver un bouton nommé btn, on
écrira par exemple :

btn["state"]=DISABLED
Il existe une 2e syntaxe qui consiste à utiliser la méthode configure de chaque

widget : lbl.configure(text="E")

Notons que le nom de l’option est placé en argument nommé, sans


guillemet. Cette syntaxe est plus indiquée lorsqu’il y a beaucoup d’options à
changer simultanément. Par exemple, supposons que nous voulions changer
la lettre en E mais aussi la police et la couleur de fond. On pourrait alors
écrire : lbl.configure(text="E", bg="red", font="Times 20 Bold")
Enfin, il est aussi possible de changer l’état d’un widget de manière
indirecte et automatique en faisant en sorte qu’une option de ce widget soit
au départ enregistrée sous la forme d’une variable de contrôle Tkinter telle
71

que StringVar. Une telle variable est mise à jour automatiquement, sans
qu’on réaffecte comme ci-dessus. Pour voir une situation typique, consulter
Exemple d’utilisation de StringVar. Cette utilisation toutefois peut être
évitée dans de nombreuses situations.

2.4. Quelques projets


2.4.1. Réalisation d’une calculette

Codes sources de la calculette


from tkinter import *

formule = ""

def click(num):

global formule
formule = formule + str(num)
equation.set(formule)

def equalclick():
try:
global formule

result = str(eval(formule))
equation.set(result)
formule = result

except:
equation.set(" error ")
formule = ""

def effacer():
global formule
formule = ""
72

equation.set("")

if __name__ == "__main__":
master = Tk()
master.title("Calculatrice")
master.geometry("375x315")
master.config(background="#000011")
equation = StringVar()
formule_field = Entry(master, textvariable=equation)
formule_field.grid(columnspan=4, pady= 30 , padx = 20 , ipadx = 100 ,
ipady = 10)
btn_1 = Button(master, text=' 1 ', command=lambda: click(1), height=2,
width=10)
btn_1.grid(row=2, column=0)

btn_2 = Button(master, text=' 2 ', command=lambda: click(2), height=2,


width=10)
btn_2.grid(row=2, column=1)

btn_3 = Button(master, text=' 3 ', command=lambda: click(3), height=2,


width=10)
btn_3.grid(row=2, column=2)

btn_4 = Button(master, text=' 4 ', command=lambda: click(4), height=2,


width=10)
btn_4.grid(row=3, column=0)

btn_5 = Button(master, text=' 5 ', command=lambda: click(5), height=2,


width=10)
btn_5.grid(row=3, column=1)

btn_6 = Button(master, text=' 6 ', command=lambda: click(6), height=2,


width=10)
btn_6.grid(row=3, column=2)

btn_7 = Button(master, text=' 7 ', command=lambda: click(7), height=2,


width=10)
btn_7.grid(row=4, column=0)

btn_8 = Button(master, text=' 8 ', command=lambda: click(8), height=2,


width=10)
btn_8.grid(row=4, column=1)

btn_9 = Button(master, text=' 9 ', command=lambda: click(9), height=2,


width=10)
btn_9.grid(row=4, column=2)

btn_0 = Button(master, text=' 0 ', command=lambda: click(0), height=2,


width=10)
btn_0.grid(row=5, column=0)

plus = Button(master, text=' + ', command=lambda: click("+"), height=2,


width=10)
plus.grid(row=2, column=3)

minus = Button(master, text=' - ', command=lambda: click("-"), height=2,


width=10)
minus.grid(row=3, column=3)

multiply = Button(master, text=' * ', command=lambda: click("*"),


height=2, width=10)
multiply.grid(row=4, column=3)

divide = Button(master, text=' / ', command=lambda: click("/"), height=2,


width=10)
73

divide.grid(row=5, column=3)

equal = Button(master, text=' = ', command=equalclick, height=2,


width=10)
equal.grid(row=5, column=2)

effacer = Button(master, text='effacer', command=effacer, height=2,


width=10)
effacer.grid(row=6, column='0')

Decimal= Button(master, text='.', command=lambda: click('.'), height=2,


width=10)
Decimal.grid(row=5, column=1)

percent= Button(master, text='%', command=lambda: click('%'), height=2,


width=10)
percent.grid(row=6, column=1)

inverse= Button(master, text='1/X', height=2, width=10)


inverse.grid(row=6, column=2)

memo= Button(master, text='M', height=2, width=10)


memo.grid(row=6, column=3)

master.mainloop()

2.4.2. Réalisation d’un convertisseur multi-base


74

Codes sources du convertisseur


##############################
# Convertisseur Multi-bases #
# par Florian BOURGOIN #
##############################

#### Importation des modules ####


from tkinter import *

#################################
#### Fonctions de conversion ####
#################################
listehexa=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
listebinaire=["0000","0001","0010","0011","0100","0101","0110","0111","1000",
"1001","1010","1011","1100","1101","1110","1111"]

def binairedecimal(string):
x=len(string)-1
n=0
res=0
while x!=-1:
if string[x]=="1":
res=res+2**n
n=n+1
x=x-1
return res

def decimalbinaire(string):
quotient=int(string)
liste=[]
res=""
while quotient!=1:
liste=liste+[quotient%2]
quotient=quotient/2
liste=liste+[1]
while liste!=[]:
res=res+str(liste[-1])
liste=liste[:-1]
return res

def binairehexa(string):
liste=[]
res=""
while len(string)>4:
liste=liste+[string[-4:]]
string=string[:-4]
liste=liste+[string]
while len(liste[-1])!=4:
liste[-1]="0"+liste[-1]
for x in range(len(liste)):
i=listebinaire.index(liste[x])
liste[x]=listehexa[i]
while liste!=[]:
res=res+liste[-1]
liste=liste[:-1]
return res

def hexabinaire(string):
res=""
for x in range(len(string)):
i=listehexa.index(string[x])
75

res=res+listebinaire[i]
return res

def decimalhexa(string):
return binairehexa(decimalbinaire(string))

def hexadecimal(string):
return binairedecimal(hexabinaire(string))

#############################
#### Interface graphique ####
#############################

def convertir():
saisie=Esaisie.get()
x,y=basedepart.get(),basearrivee.get()
couple=(x,y)
if couple==(1,2):
res=decimalbinaire(saisie)
elif couple==(1,3):
res=decimalhexa(saisie)
elif couple==(2,1):
res=binairedecimal(saisie)
elif couple==(2,3):
res=binairehexa(saisie)
elif couple==(3,1):
res=hexadecimal(saisie)
elif couple==(3,2):
res=hexabinaire(saisie)
else :
res="Euh..... B00lay ??"
fen2=Toplevel()
Label(fen2,text="Resultat").pack()
Label(fen2,text=res).pack()
fen2.mainloop()

fen=Tk()
fen.title("Convertisseur Multi-Bases")
Label(fen,text="Saisissez la valeur\na convertir").pack(side=TOP)
Esaisie=Entry(fen)
Esaisie.pack(side=TOP)

Fgauche=Frame(fen)
Fgauche.pack(side=LEFT)
Label(Fgauche,text="Base de depart").pack()
basedepart=IntVar()
Radiobutton(Fgauche,text="Decimal",variable=basedepart,value=1,indicatoron=0,
width=15).pack()
Radiobutton(Fgauche,text="Binaire",variable=basedepart,value=2,indicatoron=0,
width=15).pack()
Radiobutton(Fgauche,text="Hexadecimal",variable=basedepart,value=3,indicatoro
n=0,width=15).pack()

Fdroite=Frame(fen)
Fdroite.pack(side=RIGHT)
Label(Fdroite,text="Base d'arrivee").pack()
basearrivee=IntVar()
Radiobutton(Fdroite,text="Decimal",variable=basearrivee,value=1,indicatoron=0
,width=15).pack()
Radiobutton(Fdroite,text="Binaire",variable=basearrivee,value=2,indicatoron=0
,width=15).pack()
Radiobutton(Fdroite,text="Hexadecimal",variable=basearrivee,value=3,indicator
on=0,width=15).pack()
76

Bconvertir=Button(fen,text="Convertir",command=convertir)
Bconvertir.pack(side=BOTTOM)

fen.mainloop()

2.4.3. Réalisation d’une horloge avec chronomètre et alarme

Codes sources de l’horloge


# horloge analogique
# -*- coding: utf_8 -*-
from tkinter import *
from math import *
from time import *
from winsound import *

global hala , mala, heuala, iala, ichro, t0, dessala, iafd


hala, mala, heuala, iala, ichro, t0, dessala, iafd = 0, 0, 0, 0, 0, 0, 0, 0

def dessine_horloge() :
can1.create_oval(25, 25, 175, 175, fill='', width=8,outline='gold')
can1.create_oval(20, 20, 180, 180, fill='', width=1)
can1.create_oval(92,92,108,108,
fill='gold',outline='black',width=1,tag="axe")
can1.tag_bind("axe","<ButtonRelease>",affich_digit)
can1.tag_bind("axe","<Enter>",curseur_main)
can1.tag_bind("axe","<Leave>",curseur_fleche)
i = 1
while i < 61 :
j = i*((2*pi)/60)
# diametre horloge = 75 centre canvas =100,100
x=(cos(j)*75)+100
y=(sin(j)*75)+100
can1.create_oval(x-1, y-1, x+1, y+1, fill='blue')
if i in (15,30,45,60):
can1.create_oval(x-4, y-4, x+4, y+4, fill='black')
if i in (5,10,20,25,35,40,50,55):
77

can1.create_oval(x-2, y-2, x+2, y+2, fill='blue')


i = i+1

def affich_digit(e) :
global iafd
if iafd == 0 :
iafd=1
else : iafd=0

def aff_heure() :
global hala, mala, heuala, indala, iafd
dateheure=localtime()
heur=dateheure[3]
minu=dateheure[4]
seco=dateheure[5]
heurd=heur
minud=minu
if str(heur) == str(hala) and str(minu) == str(mala) and iala == 1 :
Beep(800,100)
if heur >12 :
heur = heur -12
if ichro == 1 :
t1=(dateheure[3]*3600)+(dateheure[4]*60)+dateheure[5]
tchro = t1 - t0
hchro = int(tchro/3600)
mchro = int((tchro-(hchro*3600))/60)
schro=tchro-(hchro*3600)-(mchro*60)
affchro.configure(text = '%d' %hchro+'.%02d' %mchro+'.%02d'%schro,
bg='white')
# affich heures
heur=heur+(minu/60.00)
j=(heur+9)*((2*pi)/12)
x=(cos(j)*55)+100
y=(sin(j)*55)+100
can1.coords(aigheu, 100, 100, x, y)
# affich minutes
minu=minu+(seco/60.0)
j=(minu+45)*((2*pi)/60)
x=(cos(j)*65)+100
y=(sin(j)*65)+100
can1.coords(aigmin, 100, 100, x, y)
# affich secondes
j=(seco+45)*((2*pi)/60)
x=(cos(j)*70)+100
y=(sin(j)*70)+100
can1.coords(aigsec, 100, 100, x, y)
if ichro == 1 and tchro < 60 :
x=(cos(j)*84)+100
y=(sin(j)*84)+100
can1.create_oval(x-1, y-1, x+1, y+1, fill='green',
outline='darkgreen')
#afficher heure digitale
if iafd == 1 :
afdigi.configure(text = '%d' %heurd +" h " +'%02d' %minud)
else : afdigi.configure(text ='',bg='lightgrey')
can1.after(999,aff_heure)

def dessine_alarme() :
global can2 , choixala, iala, dessala
if dessala == 0 :
can2 = Canvas(fen1, width=200, height=50, bg='lightgrey')
can2.pack()
choixala = Label(can2,font=('Arial', 7),fg='darkgreen')
choixala.pack(side=BOTTOM)
curs= Scale(can2, from_=0.0, to=12.00, length=150, sliderlength=7,
78

resolution=0.01, showvalue=0, orient=HORIZONTAL,


label="Réglage de l'alarme
:",command=choix_alarme,font=('Arial', 8),fg='blue')
curs.pack(side=LEFT)
bouon=Button(can2, text="On", command=set_alarme,font=('Arial',
7),bg='grey')
bouon.pack(side=BOTTOM)
dessala = 1
bouoff=Button(can2, text="Off", command=setoff_alarme,font=('Arial',
7),bg='grey')
bouoff.pack(side=BOTTOM)

def choix_alarme(valcurs) :
global hala, mala, heuala
heuala=float(valcurs)
j=(heuala+9)*((2*pi)/12)
x=(cos(j)*70)+100
y=(sin(j)*70)+100
can1.coords(aigala, 100, 100, x, y)
can1.itemconfigure(aigala,fill="red")
hala=int(heuala)
mala=int((heuala - hala) * 60)
dateheure=localtime()
heur=dateheure[3]
if heur > 11 :
hala = hala + 12
choixala.configure(text = "Déclencher l'alarme à " +'% d' %hala +" h. "
+'%02d' %mala)

def set_alarme() :
global indala, iala, dessala
dessala = 0
if iala ==1 :
can1.delete(indala)
bouala.config(bg='red')
j=(heuala+9)*((2*pi)/12)
x=(cos(j)*75)+100
y=(sin(j)*75)+100
indala=can1.create_oval(x-2, y-2, x+2, y+2, fill='red')
iala =1
affala.configure(text = '%d' %hala +" h." +'%02d' %mala,bg='white')
can2.destroy()
Beep(1000,20)

def setoff_alarme() :
global indala, iala, dessala
if dessala == 1 :
can2.destroy()
dessala = 0
bouala.config(bg='grey')
can1.itemconfigure(aigala,fill="lightgrey")
if iala ==1 :
can1.delete(indala)
iala =0
affala.configure(text = '',bg='lightgrey')
Beep(100,50)

def chrono() :
global ichro, t0
if ichro == 0 :
ichro = 1
dateheure=localtime()
t0=(dateheure[3]*3600)+(dateheure[4]*60)+dateheure[5]
bouchro.config(bg='green')
elif ichro == 1 :
79

ichro = 2
bouchro.config(bg='lightblue')
else :
ichro = 0
bouchro.config(bg='grey')
can1.create_oval(16, 16, 184, 184, fill='', outline='lightgrey',
width=6)
affchro.configure(text='',bg='lightgrey')

def aff_infos(e) :
feninf = Toplevel()
feninf.config(bg='lightblue')
geo1=fen1.winfo_geometry()
geox=fen1.winfo_rootx()
geoy=fen1.winfo_rooty()
feninf.geometry("270x260+"+str(geox-50)+"+"+str(geoy-22))
feninf.title("À propos de Horloge")
labinf=Label(feninf, bg='lightblue',fg='black',width=50,font=('Arial',
9),
text= "\nHorloge v.1.3\n\n"
"Programme écrit en Python / Tkinter\n"
"et distribué sous licence GNU GPL.\n\n"
"© 2007 Yves Le Chevalier\n"
"( yveslechevalier@free.fr )\n\n"
"Ce programme affiche une horloge \n"
"avec une alarme, un chronomètre et \n"
"un affichage digital losque l'on clique \n"
"sur l'axe des aiguilles. ")
labinf.pack(pady=5)
bouf3=Button(feninf, text="Fermer", command=feninf.destroy,bg="orange",
fg='brown')
bouf3.pack(side=BOTTOM,pady=10)
#feninf.transient()
feninf.grab_set()
feninf.wait_window()

def curseur_main(e) :
fen1.config(cursor='hand2')

def curseur_fleche(e) :
fen1.config(cursor='arrow')

# main
fen1 = Tk(className='Horloge')
fen1.geometry("+500+400")
fen1.resizable(width=False, height=False)
can1 = Canvas(fen1, width=200, height=200, bg='lightgrey')
aigala = can1.create_line(100, 100, 100, 35, fill='', width=1,arrow=LAST)
aigheu = can1.create_line(100, 100, 100, 50, fill='blue', width=3)
aigmin = can1.create_line(100, 100, 100, 40, fill='blue', width=2)
aigsec = can1.create_line(100, 100, 100, 27, fill='yellow', width=1)
bouala=Button(can1, text="Alarme", command=dessine_alarme, font=('Arial',
7),bg='grey')
bouala.place(x=2,y=2)
affala = Label(can1, font=('Arial', 7))
affala.place(x=4,y=22)
bouchro=Button(can1, text="Chrono", command=chrono, font=('Arial',
7),bg='grey')
bouchro.place(x=163,y=2)
affchro = Label(can1, font=('Arial', 7))
affchro.place(x=164,y=22)
bquit=Button(can1, text="Quitter", command=fen1.destroy, font=('Arial',
6),bg='pink',fg='black')
bquit.place(x=2,y=183)
afdigi = Label(can1, font=('Arial',9,'bold'),fg="blue")
80

afdigi.place(x=78,y=188)
signature='YLC.gif'
signat=PhotoImage(file=signature)
sign=can1.create_image(192,195, image=signat,tag="ylc")
can1.tag_bind("ylc","<ButtonRelease>",aff_infos)
can1.tag_bind("ylc","<Enter>",curseur_main)
can1.tag_bind("ylc","<Leave>",curseur_fleche)
can1.pack()

dessine_horloge()
aff_heure()

fen1.mainloop()

2.4.4. Interface de connexion (Application de gestion des employés)

Codes sources du formulaire de connexion


#Les bibliothèques à importer
from subprocess import call
from tkinter import ttk, Tk
from tkinter import *
from tkinter import messagebox
import sqlite3

#Création de la fenètre
fen=Tk()
fen.title("FENETRE DE CONNEXION")
fen.geometry("400x300+450+200")
fen.resizable(False, False)
fen.configure(background="#091821")

#Création de la fonction Seconnecter


def Seconnecter():
username=txtuser.get()
mdp=txtmdp.get()
if(username=="" and mdp==""):
messagebox.showerror("","Il faut rentrer les données")
txtmdp.delete("0", "end")
txtuser.delete("0", "end")
elif(username=="admin" and mdp=="admin"):
messagebox.showinfo("", "Bienvenue")
txtmdp.delete("0", "end")
txtuser.delete("0", "end")
fen.destroy()
81

call(["python", "menu_principal.py"])
else:
messagebox.showwarning("", "Erreur de connexion")
txtmdp.delete("0", "end")
txtuser.delete("0", "end")

#Ajouter le titre
lbltitre=Label(fen, borderwidth=3, relief=SUNKEN, text="FORMULAIRE DE
CONNEXION", font=("Sans Serif", 15), bg="#091821", fg="white")
lbltitre.place(x=0, y=0, width=400)

#Création des labels et zones de saisie


lbluser=Label(fen, text="Nom utilisateur :", font=("Arial", 14),
bg="#091821", fg="white")
lbluser.place(x=5, y=100, width=150)
txtuser=Entry(fen, bd=4, font=("Arial", 13))
txtuser.place(x=150, y=100, width=200, height=30)

lblmdp=Label(fen, text="Mot de passe :", font=("Arial", 14), bg="#091821",


fg="white")
lblmdp.place(x=5, y=150, width=150)
txtmdp=Entry(fen, bd=4, font=("Arial", 13), show="*")
txtmdp.place(x=150, y=150, width=200, height=30)
#Création boutton
btnconnexion=Button(fen, text="Connexion", font=("Arial", 16), bg="#FF4500",
fg="white", command=Seconnecter)
btnconnexion.place(x=150, y=200, width=200)

#Exécution
fen.mainloop()

2.4.5. MENU PRINCIPAL


82

Codes sources du menu principal


#Les bibliothèques à impoerter
from subprocess import call
from tkinter import ttk, Tk
from tkinter import *
from tkinter import messagebox
import sqlite3

#Création de la fentre
fen=Tk()
fen.title("MENU PRINCIPAL")
fen.geometry("1350x700+0+0")
fen.resizable(False, False)
fen.configure(background="#091821")

#lES FONCTIONS
#Ajouter
def Ajouter():
matricule=txtmatricule.get()
nom=txtnom.get()
postnom=txtpostnom.get()
prenom=txtprenom.get()
sexe=valeursexe.get()
grade=combograde.get()
adresse=txtadresse.get()
base=sqlite3.connect("employe.db")
con=base.cursor()
if(matricule=="" and nom=="" and postnom=="" and sexe=="" and grade==""
and adresse==""):
messagebox.showerror("Attention !", "Veuillez remplir les champs
vides")
txtmatricule.delete("0", "end")
txtnom.delete("0", "end")
txtpostnom.delete("0", "end")
txtprenom.delete("0", "end")
valeursexe.delete("0", "end")
combograde.delete("0", "end")
txtadresse.delete("0", "end")
else:
con=sqlite3.connect("BDDEMPLOYE.db")
cur=con.cursor()
req1="CREATE TABLE IF NOT EXISTS T_EMPLOYE(mat TEXT PRIMARY KEY, nom
TEXT NOT NULL, postnom TEXT NOT NULL, prenom TEXT NOT NULL, sexe TEXT NOT
NULL, grade TEXT NOT NULL, adresse TEXT NOT NULL)"
cur.execute(req1)
req2="INSERT INTO T_EMPLOYE(mat, nom, postnom, prenom, sexe, grade,
adresse) VALUES(?, ?, ?, ?, ?, ?, ?)"
con.execute(req2, (matricule, nom, postnom, prenom, sexe, grade,
adresse))
con.commit()
messagebox.showinfo("Information", "Employé ajouté")
fen.destroy()
call(["python", "menu_principal.py"])

#Retour
con.close()
#Modifier
def Modifier():
matricule = txtmatricule.get()
nom = txtnom.get()
postnom = txtpostnom.get()
prenom = txtprenom.get()
sexe = valeursexe.get()
grade = combograde.get()
83

adresse = txtadresse.get()
if(matricule==""):
messagebox.showerror("Attention", "Veuillez saisir le matricule de
l'employé")
txtmatricule.delete("0", "end")
else:
con = sqlite3.connect("employe.db")
cur = con.cursor()
req1 = "CREATE TABLE IF NOT EXISTS T_EMPLOYE(mat TEXT PRIMARY KEY,
nom TEXT NOT NULL, postnom TEXT NOT NULL, prenom TEXT NOT NULL, sexe TEXT NOT
NULL, grade TEXT NOT NULL, adresse TEXT NOT NULL)"
cur.execute(req1)
cur.execute("UPDATE T_EMPLOYE SET nom=?, postnom=?, prenom=?, sexe=?,
grade=?, adresse=? where mat=?", (nom, postnom, prenom, sexe, grade, adresse,
matricule))
con.commit()
messagebox.showinfo("Information", "Information modifié avec succès")
select=cur.execute("select * from T_EMPLOYE")
select=list(select)
table.insert('', END, values=select[0])
con.close()
fen.destroy()
call(["python", "menu_principal.py"])

#Fonction supprimer
def Supprimer():
codeselectionner=table.item(table.selection())["values"][0]
con=sqlite3.connect("employe.db")
cur=con.cursor()
delete_=cur.execute("delete from T_EMPLOYE where
mat={}".format(codeselectionner))
con.commit()
table.delete(table.selection())

#Ajouter le titre
lbltitre=Label(fen, borderwidth=3, relief=SUNKEN, text="MENU PRINCIPAL",
font=("Sans Serif", 25), bg="#2F4F4F", fg="#FFFAFA")
lbltitre.place(x=0, y=0, width=1350, height=100)

#Détails des employés


#Matricule de l'employé
lblmatricule=Label(fen, text="MATRICULE :", font=("Arial", 18), bg="#091821",
fg="white")
lblmatricule.place(x=70, y=150, width=150)
txtmatricule=Entry(fen, bd=4, font=("Arial", 14))
txtmatricule.place(x=250, y=150, width=150)
#Nom de l'employé
lblnom=Label(fen, text="NOM :", font=("Arial", 18), bg="#091821", fg="white")
lblnom.place(x=70, y=200, width=80)
txtnom=Entry(fen, bd=4, font=("Arial", 14))
txtnom.place(x=250, y=200, width=300)
#Postnom de l'employé
lblpostnom=Label(fen, text="POSTNOM :", font=("Arial", 18), bg="#091821",
fg="white")
lblpostnom.place(x=70, y=250, width=150)
txtpostnom=Entry(fen, bd=4, font=("Arial", 14))
txtpostnom.place(x=250, y=250, width=300)
#Prénom de l'employé
lblprenom=Label(fen, text="PRENOM :", font=("Arial", 18), bg="#091821",
fg="white")
lblprenom.place(x=70, y=300, width=120)
txtprenom=Entry(fen, bd=4, font=("Arial", 14))
txtprenom.place(x=250, y=300, width=300)
#Sexe de l'employé
valeursexe=StringVar()
84

lblsexe=Label(fen, text="SEXE :", font=("Arial", 18), bg="#091821",


fg="white")
lblsexe.place(x=70, y=350, width=80)
lblsexeMasculin=Radiobutton(fen, text="MASCULIN", value="M",
variable=valeursexe, indicatoron=0, font=("Arial", 14), bg="#091821",
fg="#696969")
lblsexeMasculin.place(x=250, y=350, width=130)
lblsexeFeminin=Radiobutton(fen, text="FEMININ", value="F",
variable=valeursexe, indicatoron=0, font=("Arial", 14), bg="#091821",
fg="#696969")
lblsexeFeminin.place(x=420, y=350, width=130)
#Grade de l'employé
lblgrade=Label(fen, text="GRADE :", font=("Arial", 18), bg="#091821",
fg="white")
lblgrade.place(x=70, y=400, width=100)
combograde=ttk.Combobox(fen, font=("Arial", 14))
combograde["values"]=['CD', 'CB', 'ATTB1', 'ATTB2', 'AGB1', 'AGB2',
'HUISSIER']
combograde.place(x=250, y=400, width=130)
#Adresse de l'employé
#Prénom de l'employé
lbladresse=Label(fen, text="ADRESSE :", font=("Arial", 18), bg="#091821",
fg="white")
lbladresse.place(x=70, y=450, width=125)
txtadresse=Entry(fen, bd=4, font=("Arial", 14))
txtadresse.place(x=250, y=450, width=300)

#Boutton Enregistrer
btnEnregistrer=Button(fen, text="Enregistrer", font=("Arial", 16),
bg="#D2691E", fg="white", command=Ajouter)
btnEnregistrer.place(x=70, y=550, width=150)
#Boutton Modifier
btnModifier=Button(fen, text="Modifier", font=("Arial", 16), bg="#D2691E",
fg="white", command=Modifier)
btnModifier.place(x=240, y=550, width=150)
#Boutton Supprimer
btnSupprimer=Button(fen, text="Supprimer", font=("Arial", 16), bg="#D2691E",
fg="white", command=Supprimer)
btnSupprimer.place(x=410, y=550, width=150)

#Création de la table (Treeview)


table=ttk.Treeview(fen, columns=(1, 2, 3, 4, 5, 6, 7), height=5,
show="headings")
table.place(x=570, y=150, width=790, height=450)
#Entete de la table
table.heading(1, text="MAT")
table.heading(2, text="NOM")
table.heading(3, text="POSTNOM")
table.heading(4, text="PRENOM")
table.heading(5, text="SEXE")
table.heading(6, text="GRADE")
table.heading(7, text="ADRESSE")
#Définir les dimensions des colonnes
table.column(1, width=50)
table.column(2, width=150)
table.column(3, width=150)
table.column(4, width=150)
table.column(5, width=50)
table.column(6, width=50)
table.column(7, width=150)
#Afficher les informations sur la table
con=sqlite3.connect("BDDEMPLOYE.db")
cur=con.cursor()
req="CREATE TABLE IF NOT EXISTS T_EMPLOYE(mat TEXT PRIMARY KEY, nom TEXT NOT
NULL, postnom TEXT NOT NULL, prenom TEXT NOT NULL, sexe TEXT NOT NULL, grade
85

TEXT NOT NULL, adresse TEXT NOT NULL)"


cur.execute(req)
select=cur.execute("select * from T_EMPLOYE")
for row in select:
table.insert('', END, values=row)

#Exécution de la fenetre
fen.mainloop()
86

TROISIEME PARTIE : DEVELOPPEMENT DES


APPLICATIONS EN WINDEV

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
87

3.1 Présentation

Pour créer une application avec une base de données, il faut :

 Créer le projet lié à l’application. Ce projet regroupera tous les éléments de


l’application (fenêtres, code, requête, états….) ;
 Créer l’analyse liée au projet. L’analyse permet de décrire tous les fichiers de
données manipulés par l’application.

3.2. Création du projet

Pour créer le projet :

1. Lancez WinDev si ce n’est pas déjà fait ;


2. Sélectionnez l’option ‘’Fichier… Nouveau’’, cliquez sur ‘’projet’’. L’assistant de
création de projet se lance. Les différents plan de l’assistant vont vous aider à créer
votre. Tous les renseignements indiqués dans cet assistant pourront être modifiés
par la suite ;
3. Le premier plan de l’assistant permet de saisir le nom du projet, son emplacement
et sa description. Dans notre cas, ce projet va simplement s’appeler ‘’Mes
comptes’’. Par défaut, WinDev propose de créer ce projet dans le répertoire ‘’\Mes
projets\ Mes Comptes’’. Vous pouvez conserver cet emplacement ou le modifier
grâce au bouton […]. Pour le résumé du projet saisissez ‘’Le projet a pour but de
gérer des comptes bancaires’’ ;
4. Les différentes étapes de l’assistant sont indiquées à gauche de l’assistant. Ces
étapes sont directement cliquables. Les autres écrans de l’étape 1 (‘’Description’’)
n’étant pas fondamentaux, cliquez directement sur ‘’2 Chartes’’ ;
5. Cette étape permet de définir la carte graphique. Sélectionnez ‘’ActivVista’’ et
passez à l’écran suivant ;
6. Nous choisissons une taille de 800 x600 pour les écrans, car notre application
n’aura pas de grandes fenêtres, et sera ainsi adaptée à la plupart des résolutions
utilisées. Le choix serait différent pour une application de gestion d’images par
exemple ;
7. Nous allons maintenant donner les renseignements concernant la base de données.
Cliquez directement sur ‘’4 Base de données’’ ;
8. Sélectionnez l’option ‘’oui, créer une nouvelle base de données’’ et validez.
L’assistant de création de l’analyse se lance.

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
88

3.3 Création de l’analyse

Les étapes de l’assistant de création d’analyse sont les suivantes :

1. Indiquez le nom et le répertoire de l’analyse. Par défaut, le nom de l’analyse


correspond au nom du projet et le répertoire de l’analyse est un répertoire ‘’ana’’
dans le répertoire du projet nous ;

Nous allons conserver ces paramètres par défaut. Passez à l’écran suivant de
l’assistant

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
89

2. Vous pouvez ensuite choisir le ou les types de bases de données manipulées par le
projet.
Sélectionnez HyperFileSQLClassic (la base de données proposée par défaut avec
WinDev).

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
90

2. Vous pouvez ensuite choisir le ou les types de bases de données manipulées par le
projet.

Sélectionnez HyperFileSQLClassic (la base de données proposée par défaut avec


WinDev).
Passez à l’écran suivant de l’assistant.

3. Validez. L’assistant de création d’un fichier de données se lance automatiquement

3.3.1 Création de la description des fichiers de données


Les étapes de l’assistant de création d’un fichier de données sont les suivantes :
1. Sélectionnez dans l’assistant l’option "Créer une nouvelle description d’un fichier
de données". Passez à l’écran suivant de l’assistant.

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
91

2. Le fichier de données que nous allons créer est le fichier "Etudiant". Son nom est
donc
" Etudiant ". Ce nom sera utilisé :
- pour manipuler le fichier de données en programmation. La variable associée au
fichier sera
Compte.
- pour construire le nom du fichier de données physique associé (fichier Etudiant.fic).

Automatiquement, le libellé et la description des éléments représentés par les


enregistrements du fichier de données apparaissent.

3. Conservez l’option "Le fichier de données possède un identifiant automatique".


Cette
Option indique si le fichier de données doit posséder une clé unique, gérée
automatiquement par WinDev

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
92

4. Passez à l’écran suivant et sélectionnez le type de la base associée au fichier de


données.
Nous travaillons sur des fichiers de données HyperFileSQLClassic. Passez à l’écran
suivant.
5. Conservez les options par défaut et passez à l’écran suivant.
6. Cliquez sur le bouton vert pour valider. Le fichier de données est automatiquement
créé dans l’analyse. La fenêtre de description des rubriques s’ouvre.
Nous allons saisir les rubriques du fichier Etudiant. Dans la fenêtre de description du
fichier de données, vous pouvez déjà voir qu’une rubrique a été automatiquement
créée : IDEtudiant. Cette rubrique correspond à l’identifiant automatique du fichier de
données. Cette rubrique est composée des lettres "ID" et du nom du fichier. Nous
allons créer les autres rubriques de ce fichier de données.
Tout d’abord, nous allons créer la rubrique "Matricule". Cette rubrique contiendra le
numéro de compte.
1. Dans la fenêtre de description des rubriques, cliquez dans la colonne "Nom" de la
première ligne vide deux fois. Cette colonne passe automatiquement en saisie.
Saisissez "Matricule".
2. Cliquez dans la colonne "Libellé". Automatiquement, le nom de la rubrique
apparaît. Nous allons modifier le libellé de la rubrique en saisissant "Matricule de
l’étudiant". Dans la colonne "Type", le type "Texte" est automatiquement sélectionné.
Nous n’y touchons pas.

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
93

3. Nous allons modifier la taille de la rubrique. Cliquez sur la case "50" et remplacez
"50" par "10". Cliquez sur la ligne suivante. Les valeurs sont automatiquement mises
à jour.

4. La définition de la clé se fait de la manière suivante : re-sélectionnez la ligne de la


rubrique

"Matricule" pour activer les champs de description présents sur la droite de l’écran. Il
suffit alors de préciser le type de clé utilisé. Dans notre cas, le numéro de compte est
une clé uni-que.

3.3.2 Création des liaisons

Nous avons créé toutes les descriptions de fichiers nécessaires à l’application de


gestion de paiement des frais académiques.
Maintenant nous allons créer les liaisons entre les fichiers de données.
Créons tout d’abord la liaison entre le fichier Etudiant et le fichier Paiement.
1. Sélectionnez l’option "Insertion. Liaison". Le curseur de la souris se transforme en
crayon.

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
94

2. Cliquez sur le fichier "Etudiant" puis sur le fichier "Paiement".


3. L’assistant de création de liaison se lance.

4. Répondez aux questions posées par l’assistant :

5. Passez à l’écran suivant. L’assistant propose automatiquement la clé utilisée par la


liaison. Affichez l’écran suivant de l’assistant.
6. L’assistant propose de créer une nouvelle clé dans le fichier Compte. Acceptez cette
option en passant à l’écran suivant.
7. Validez les règles d’intégrité en passant à l’écran suivant.
8. Cliquez sur la flèche verte. La liaison est automatiquement créée dans l’éditeur
d’analyses.

3.5 Création d’un état

Pour créer un état :

1. Sélectionnez l’option ‘’Fichier… Nouveau ‘’. Survole la catégorie ‘’Etat’’ et


sélectionnez l’option ‘’Etat’’. L’assistant de création d’un état se lance
automatiquement ;
2. Sélectionnez un état de type ‘’Tableau’’. Passez à l’écran suivant de l’assistant ;
3. L’état va être basé sur une requête que nous allons créer : sélectionnez l’option
‘’D’une nouvelle requête’’. Passez à l’écran suivant de l’assistant ;
4. L’écran de description de la requête apparaît. Nous allons définir les rubriques qui
vont composer la requête ;
5. Nous allons trier les données ;
6. Validez la requête. L’assistant de création d’état continue ;

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
95

7. L’état va contenir une rupture (cochez ‘’Oui’’). Les ruptures servent à regrouper
les données ;
8. L’assistant propose automatiquement la rubrique la rubrique de rupture. Ce sont les
rubriques pour lesquelles nous avons défini un tri dans la requête ;
9. Cet écran est très important. En effet, il permet d’associer les différentes rubriques
associées à l’état aux différentes zones de l’état. ;
10. L’écran suivant nous propose de faire un total sur une rubrique calculée à chaque
fin de rupture. Acceptez et passer à l’écran suivant ;
11. Pour les dimensions du papier, choisissez l’affichage en mode paysage. Passez à
l’écran suivant ;
12. Choisissez un gabarit pour votre état (ActivVista par exemple) ;
13. Donnez un nom et un titre à l’état ;
14. Validez ;
15. L’état est automatiquement créé ;
16. Enregistrez l’état dans le dossier par défaut.

3.5.1 Test d’un état

Pour tester un état :

1. Lancez le test grâce à l’icône ;


2. Choisissez le mode d’impression ‘’Aperçu avant impression’’ :
3. L’état apparaît dans une fenêtre d’aperçu ;
4. Validez
5. Enregistrez l’état et testez – le.

3.5.2 Modification d’un état

Nous allons maintenant apporter une petite amélioration à l’état que nous venons de
réaliser chaque nouvel organisme bancaire va s’afficher une nouvelle page.
L’organisme bancaire est la rubrique qui sert de tri et de rupture. Nous allons donc
ajouter un saut de page après chaque rupture.

Pour ajouter un saut de page après chaque rupture :

1. Affichez l’état sous l’éditeur (si nécessaire) ;


2. Cliquez dans la zone ‘’base de rupture1’’ ;
3. Affichez le menu contextuel (clic droit) et sélectionnez l’option ‘’Description du
bloc’’ ;
4. Sélectionnez l’onglet ‘’Détail’’ de la fenêtre de description;
5. Cochez l’option ‘’Saut de page après le bloc’’
6. Validez ;
7. Enregistrez et testez l’état.

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
96

3.6 Création requête


Créez une nouvelle requête :
1. Dans le menu, sélectionnez "Fichier" puis "Nouveau".
2. Sélectionnez "Requête". L’assistant de création de requêtes se lance :

Vous avez la possibilité d’utiliser l’assistant de création de requêtes ou de saisir


directement le code SQL de la requête (option "Saisir le code SQL").

Avec l’assistant, vous pouvez réaliser des requêtes de type :


• "Sélection (SELECT)" : interrogation
• "Ajout (INSERT)" : ajout dans un fichier de données
• "Modification (UPDATE)" : modification dans un fichier de données
• "Suppression (DELETE)" : suppression dans un fichier de données
• "Union (UNION)" : combinaison de requêtes de sélection

3.7 Manipulation des données avec Windev

Dans WinDev, lors de la création d’un projet manipulant des données, vous devez tout
d’abord créer une "analyse". Une "analyse" contient la description des fichiers (ou
tables) contenant les données de l’application.

C’est seulement lors de l’exécution de l’application, que ces descriptions sont utilisées

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
97

pour créer la base de données et/ou les fichiers de données. C’est dans cette base ou
dans ces fichiers que seront stockées les données.

WinDev sait gérer différents formats de base de données (pour ne pas dire tous). Les
plus courantes sont :

• Hyper File, système de base de données intégrée à WinDev et livrée en standard.


• AS/400, Access, SyBase, ...
• Oracle, SQL Server, MySQL, xBase, …
• Toute base accessible en langage SQL sous Windows.
• Texte (fichiers ASCII).

Pour accéder aux données, il existe différentes techniques (appelées "modes d’accès")
:
• Accès Natif
• Accès OLE DB
• Accès ODBC direct
• Accès ODBC via OLE DB

Hyper File :

Il s’agit d’un SGBD Relationnel redistribuable gratuitement.

Le format Hyper File est le format de base de données fourni avec WinDev. Ce format
de base de données est commun à WinDev et WebDev.

La description d’un fichier consiste à décrire la structure de l’enregistrement qui sera


écrit dans le fichier. Les rubriques (ou "colonnes") découpent l’enregistrement.
WinDev propose différents types de rubriques :

 Texte ou Mémo texte


 Numérique ou Monétaire
 Date ou Heure
 Interrupteur ou Sélecteur
 Liste
 Image
 Mémo binaire

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
98

3.8. Notion de Migration une application HyperFileSQLClassic en mode Client-


Serveur

3.8.1 Présentation

La Migration d’une application HyperFileSQLClassic en mode Client -Serveur est


l’opération plus courante.

WinDev propose plusieurs solutions pour réaliser cette migration sur le poste de
développement :

 Réaliser la migration depuis l’éditeur d’analyses (grâce à l’option


‘’Analyse..HyperFileSQLClient/Serveur.. Passage en
HyperFileSQLClient/Serveur’’) ;
 Réaliser la migration depuis le centre de contrôle HyperFileSQL.

Pour mieux se rendre compte des différentes étapes, nous allons migrer l’application
de gestion de comptes que nous avons réalisé dans la partie 2 de ce livre, en utilisant la
première méthode.

Migration de l’exemple

Une version corrigée du projet étudié dans la partie 2 est disponible avec ce cours
d’auto-formation. Nous allons migrer ce projet pour tester en mode Client-Serveur.

Pour migrer le projet :

1. Ouvrez le projet;
2. Affichez l’analyse du projet (option ‘’Projet.. Charger l’analyse’’). L’éditeur
d’analyse s’affiche ;
3. Dans l’éditeur d’analyse, sélectionnez l’option
‘’Analyse..HyperFileSQLClient/Serveur.. Passage en
HyprFileSQLClient/Serveur’’. Un assistant s’ouvre, permettant de créer une
connexion au serveur HyperFileSQL installé sur le poste.
4. Indiquez dans les plans suivants :

 Le nom du serveur (nom de votre machine par exemple) et le numéro de port ;


 Le nom de l’utilisateur (laissez cette information vide pour utiliser
l’administrateur)
 Le nom de la base de données (‘’Mes Comptes’’ dans votre exemple) ;
 Le nom de la connexion (conservez le nom proposé) ;

5. Passez à l’écran suivant. La connexion à la base est automatiquement créée ;


6. L’assistant propose ensuite d’associer les différents fichiers présents dans l’analyse
à la connexion qui vient d’être créée ;
7. L’assistant propose ensuite de créer les fichiers de données sur le serveur. Validez

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
99

(option ‘’Copier maintenant’’) ;


8. Sélectionnez les fichiers de l’analyse à copier sur le serveur : dans notre cas, ce
sont tous les fichiers de données du répertoire EXE. Passez à l’écran suivant ;
9. Les fichiers de l’analyse sont automatiquement transformés en fichiers
HyperFileSQLClient/Serveur et associés à la connexion choisie ;
10. Générez l’analyse.

NB : Vérifiez le code de votre projet : en mode HyperFileSQLClient/Serveur, les


instructions HSubsRep, HCangeRep,… Sont inutiles.

NB : Selon les paramètres indiqués lors de la création de la connexion, il est possible


de modifier la connexion définie dans l’analyse grâce aux fonctions
HouvreConnexionetHChangeConnexion.

NB : La fonction HouvreConnexion permet toujours de repasser en mode


HyperFileSQLClassic : il suffit de lui préciser le chemin du répertoire contenant les
fichiers de données HyperFileSQLClassic.

11. Vous avez migré le projet de développement. Il peut être également nécessaire de
migrer l’application déployée (par exemple si l’application déployée utilise des
fichiers HyperFileSQLClassic). Cette opération se paramètre lors de la création du
programme d’installation de l’application.

3.8.2 Fonctionnalité disponibles en mode HyperFilSQLClient/Serveur

HyperFileSQLClient/Serveur propose de nombreuses fonctionnalités :

 Transactions ;
 Journal ;
 Procédures stockées
 Triggers.

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
100

3.8.3 Administrer

Maintenant que nous savons créer/migre une application HyperFileSQL Client/


Serveur,

Nous allons voir comment l’administrer.

En effet, une base de données Client/ Serveur nécessite :

 Une configuration spécifique des postes (installation d’un Serveur HyperFileSQL,


…) ;
 Une administration réalisée grâce au centre de contrôle HyperFileSQL.

3.8.4 Configuration des postes

Pour utiliser une base HyperFileSQLClient/Serveur, il nécessaire d’installer un serveur


HyperFileSQL sur le poste serveur. Il est possible d’utiliser plusieurs serveurs
HyperFileSQL sur le même poste, utilisant des ports différents sur chaque serveur, une
ou plusieurs bases de données peuvent être installées.

Par exemple, il est possible d’installer sur le même poste un serveur HyperFileSQL de
tests, avec une base de données de tests, et un serveur HyperFileSQL de production,
utilisant un port différent.

3.8.5 Le centre de contrôle HperFileSQL

Le centre de contrôle HyperFileSQL permet de réaliser toutes les opérations


d’administration des serveurs et des bases de données HyperFileSQLClient/Serveur.

Nous allons voir les fonctionnalités les plus importantes.

Pour lancer le centre de contrôle HyperFileSQL et accéder aux données :

1. Dans l’éditeur, sélectionnez l’option ‘’Outil.. Centre de contrôle HyperFileSQL’’ ;

2. Cliquer sur ‘’ HyperfileSQL C/S’’. La zone qui apparait peut être vide si le centre
de contrôle HyperFlieSQL n’a jamais été utilisé depuis ce poste ;

Depuis cet écran, vous pouvez :

 Rechercher les serveurs HyperFileSQL accessibles depuis le réseau (opération


relativement longue) ;
 Rechercher les serveurs HyperFileSQL accessibles depuis un ordinateur. C’est ce

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
101

que nous allons faire.

3. Cliquer sur l’icône dans l’écran qui apparait par exemple ‘’Serveur
HyperFileSQL’’ et indiquez le nom de votre poste.

Valider le poste apparait dans le centre de contrôle HyperFileSQL.


4. Sélectionnez votre poste, votre serveur HyperFileSQL et votre base ‘’
MesComptes’’. Donnez sle paramètre de connexion (Admin sans mot de passe par
défaut). La liste des fichiers s’affiche ;
5. L’organisation des données est la suivante.

A-Nom poste
B-Nom du serveur HyperFileSQL
C-Nom de la base
D-Fichier de données de la base.

Quelques exemples des informations accessibles(pour plus de détails, consultez l’aide


en ligne) :

 L’onglet ‘’ Description’’ présente les informations sur les fichiers de données


(nombre d’enregistrements, …
 Si vous sélectionnez un fichier de données, l’onglet ‘’Contenu’’ présente les
données enregistrées,…

Depuis le centre de contrôle HyperFileSQL, il est possible d’administrer toute la base


de données HyperFileSQLClient/Serveur.

3.8.6 Sauvegarde de la base

La sauvegarde de la base de données peut être réalisée directement grâce à l’onglet


‘’sauvegarde ’’ disponible lors de la sélection de la base de données dans le volet
gauche du centre de contrôle HyperFileSQL.

3.8.7 Conclusion

Le centre de contrôle HyperFileSQL est un outil complet d’administration de base de


données, permettant entre autre :

 D’arrêter ou de redémarrer un serveur en cas de problème.


 De gérer les utilisateurs et leurs droits.
 De réindexer les fichiers de données si nécessaire
 De faire des sauvegardes de la base

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
102

Le centre de contrôle HyperFileSQL est un outil redistribuable qui doit être installé
chez les clients possédant des bases de données HyperFileSQL Client/serveur. Le
centre de contrôle HyperFileSQL doit être utilisé par l’administrateur des bases
données.

Chapitre 4 : Cas pratique

Ce Chapitre fait appel à l’utilisation de la base de données HyperFileSQL. Pour cela, il


faut créer une analyse (base de données).

Procédure :

1) Menu Fichier – Nouveau : cette fenêtre apparait, pointer Données, puis


sélectionner Données.

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
103

2) Nommer le nom de l’analyse (par défaut, le nom du projet) et l’emplacement (par


défaut, le répertoire du projet) puis suivant.

3) Choisir le type de base de données, dans notre cas, HyperFileSQLClassic puis


suivant pour terminer.

Une fois l’analyse créée, il faut maintenant créer les fichiers de données (tables) : clic
droit dans l’espace de l’analyse – Nouveau fichier de données et cette fenêtre apparait :

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
104

Prendre Créer une nouvelle description d’un fichier de données, puis suivant.
Indiquer le nom du fichier puis suivant ; choisir une fois de plus le type de base de
données (dans notre cas, HyperFileSQLClassic) puis suivant – suivant – suivantpour
terminer ; une fenêtre apparait, faire la description des rubriques du fichier puis OK
pour terminer.

Formulaire mot de passe : FEN_Login

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
105

Code source :
Btn_Valider

SI login..Valeur="web" ET Mot_de_passe..Valeur="12345" ALORS


OuvreSoeur(MENU)
Ferme(login)

SINON
Info("Les données entrées sont incorrectes !")
RAZ()

FIN

Btn_Quitter
Ferme(login)

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
106

Formulaire Menu Général : FEN_Menu

Btn_Joueur
Ouvre(FEN_joueur)

Btn_Quitter
SELON Dialogue("Voulez-vous quitter cette application s.v.p ?")

CAS 1
Ferme()

FIN

Formulaire joueur : FEN_joueur

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
107

Btn_Valider
EcranVersFichier()
// S'il s'agit d'un nouvel enregistrement
SI Nom<>"" ET Postnom<>"" ALORS// Lecture des informations saisies
// On l'ajoute
HLitRecherche(jouer,code,Code)
EcranVersFichier()
SI HTrouve(jouer)
// On le modifie
Info("Cet enregistrement existe déjà !")
RAZ()
SINON
HAjoute(jouer,hVérifieIntégrité)
TableAffiche(JOUER,taRéExecuteRequete)
TableSelectMoins(JOUER)
Modifier..Grisé=Vrai
Supprimer..Grisé=Vrai
MoiMême..Grisé=Faux
RAZ()
FIN
SINON
Info("Veillez remplir le champs vides")

FIN

Btn_Modifier
SI TableSelect(JOUER)=-1 ALORS RETOUR
//1 : &Modifier
//2 : &Ne pas modifier

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
108

//1 : &Modifier
//2 : &Ne pas modifier
//1 : &Modifier
//2 : &Ne pas modifier
SELON Dialogue("Voulez-vous modifier cet enregistrement ?")
// &Supprimer
CAS 1
EcranVersFichier()
HModifie(club)
TableAffiche(JOUER,taRéExecuteRequete)
RAZ()
Valider..Grisé=Faux
MoiMême..Grisé=Vrai
Supprimer..Grisé=Vrai
TableSelectMoins(JOUER)
Info("Operationréissie !")
CAS 2
FIN
Btn_Supprimer
SI TableSelect(JOUER)>0 ALORS
//1 : &Supprimer
//2 : &Ne pas supprimer

//1 : &Supprimer
//2 : &Ne pas supprimer
//1 : &Supprimer
//2 : &Ne pas supprimer
SELON Dialogue("Voulez-vous supprimer cet enregistrement ?")
// &Supprimer
CAS 1
HSupprime(jouer)
TableAffiche(JOUER)
Valider..Grisé=Faux
Modifier..Grisé=Vrai
Supprimer..Grisé=Vrai
RAZ()
TableSelectMoins(JOUER)
Info("Operation réussie !")
// &Ne pas supprimer
CAS 2

FIN
SINON
Info("Operationréissie !")
FIN

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
109

Impression simple d’un état en sortie

iImprimeEtat(ETAT_Joeur)
Ferme(FEN_Etat1)

Formulaire d’impression :FEN_Impression

Btn_Imprimer
HAnnuleDéclaration(REQ_etat1)
HExécuteRequête(REQ_etat1,hRequêteDéfaut,test)
iAperçu(i100)
iImprimeEtat(ETAT1)

Btn_annuler
Ferme()

Création d’un état de sortie

Pour créer un état:

1) Menu Fichier – Nouveau –Etat ;

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
110

2) Type d’état : Prendre Etat vierge (pour créer vous-même un état du début à la
fin), puis suivant ;
3) Indiquer la source des données à imprimer : Prendre D’un fichier de données
ou d’une requête existante, puis suivant ;
4) Sélectionner le fichier ou la requête à partir duquel ou de laquelle on veut créer
l’état, puis suivant ;
5) Indiquer si l’état a une rupture de séquence. Dans ce cas :
1) Prendre oui, puis suivant ;
2) Indiquer la rubrique de rupture de séquence. (Il est à noter que la rupture
de séquence ne peut être faite que sur une rubrique triée), puis suivant.
6) Sélectionner par la coche les rubriques à afficher dans l’état, puis suivant ;
7) Choisir le format du papier (par défaut le A4), puis suivant ;
8) Choisir un gabarit (par défaut Aucun : à garder de préférence), puis suivant ;
9) Donner le nom de l’état, puis OK pour terminer.

Début de document : En-tête, Date d’impression et Numéro de page (voir Menu


Insertion) ;

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
111

Haut de page : Intitulé de l’état et aussi des champs des fichiers (tables) ;

Corps : Les lignes détail des enregistrements ;

Bas de page : Donnée à afficher au bas de la page quel que soit son contenu
(Page remplie ou pas. Par exemple : Note de bas de page, Numéro de page) ;

Fin de document : Total à la fin du document.

Création d’une requête

Pour créer une requête :

1) Menu Fichier – Nouveau – Requête ;


2) Type de requête : Prendre Sélection (SELECT) ;
3) Ouvrir le nœud (signe plus)du fichier (table) avec lequel on veut créer la requête,
puis pointer les rubriques souhaitées par un clic sur la petite flèche pointant vers la
droite pour la sélection (si la requête est créée à partir de plusieurs fichiers, il
faudra donc sélectionner les rubriques de tous ces fichiers.).

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)
112

Université Simon KIMBANGU/Kinshasa-Kalamu


Support du cours de projet de programmation 2 (Python et WinDev)

Vous aimerez peut-être aussi