Positionner ses widgets avec les layouts



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

Modéliser ses fenêtres avec Qt Designer

Freeway 7. Nouvelles fonctionnalités

L'architecture MVC avec les widgets complexes

Gestion d Active Directory à distance : MMC & Délégation

Chapitre 4 Pierre, papier, ciseaux

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

les Formulaires / Sous-Formulaires Présentation Créer un formulaire à partir d une table...3

COURS WINDEV NUMERO 3

Systèmes d'exploitation virtuels

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

Modéliser ses fenêtres avec Qt Designer

Traitement de texte : Quelques rappels de quelques notions de base

Navigation dans Windows

Petit guide à l'usage des profs pour la rédaction de pages pour le site Drupal du département

Création d'un questionnaire (sondage)

Bien travailler sur plusieurs écrans

Guide pour la réalisation d'un document avec Open Office Writer 2.2

Créer des étiquettes avec les adresses d'un tableau Calc

1. Introduction Création d'une requête...2

Comment faire des étiquettes

Réaliser un PUBLIPOSTAGE

Classer et partager ses photographies numériques

Guide Draw. Chapitre 5 Combiner plusieurs objets

Publication Assistée par Ordinateur

L ORDINATEUR FACILE D ACCÈS!

Automatisation d'une Facture 4. Liste Déroulante Remises Case à cocher Calculs

Vous y trouverez notamment les dernières versions Windows, MAC OS X et Linux de Thunderbird.

1 sur 5 10/06/14 13:10

Dans la série. présentés par le site FRAMASOFT

Je communique par

Xubuntu Une alternative à Windows et à Ubuntu, (pour ceux qui ne veulent pas d'unity) : installer Xubuntu.

Création d'un site dynamique en PHP avec Dreamweaver et MySQL

Tutorial et Guide TeamViewer

Créer un site Internet dynamique

TBI-DIRECT. Bridgit. Pour le partage de votre bureau. Écrit par : TBI Direct.

Installation et utilisation du client FirstClass 11

LibreOffice Calc : introduction aux tableaux croisés dynamiques

Publier dans la Base Documentaire

Système clients serveur Kwartz Vulgarisation, identification, dossier personnel

ContactForm et ContactFormLight - Gestionnaires de formulaire pour Prestashop Edité par ARETMIC S.A.

Un exemple avec WORKSPACE d'interwrite

HTML5, CSS3 et JavaScript Développez vos sites pour les terminaux mobiles

Introduction à Expression Web 2

Prise en main du logiciel. Smart BOARD Notebook 10

Projet ISN - dossier réalisé par Randrianarimanana Stéphanie. Titre du projet : Site de rencontre. le nom de notre site de rencontre : Linkymeet

Créer une base de données

@telier d'initiation

Le modèle de données

Formation > Développement > Internet > Réseaux > Matériel > Maintenance

1. Création d'un état Création d'un état Instantané Colonnes Création d'un état Instantané Tableau... 4

I. Introduction aux fonctions : les fonctions standards

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

Débuter avec OOo Base

FICHIERS ET DOSSIERS

Utilisation de l éditeur.

Assistance à distance sous Windows

Notre projet est de réaliser un document

Stopack : logiciel pour l entrepôt

Dessiner dans Galaad FRANÇOIS PALLUT

MAILING KOMPOZER... 2 CREEZ UNE PAGE... 2 FORMAT DE LA PAGE... 2 AJOUTER DU TEXTE SUR UNE PAGE... 4

TUTORIEL IMPRESS. Ouvrir Impress cocher «présentation vierge», «suivant» cocher «écran», «suivant» cocher «standard», «créer»

TRUECRYPT SUR CLEF USB ( Par Sébastien Maisse 09/12/2007 )

Votre site Internet avec FrontPage Express en 1 heure chrono

Gérer ses fichiers et ses dossiers avec l'explorateur Windows. Février 2013

Un serveur web, difficile?

Le poste de travail, les dossiers et les fichiers

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

LECON 2 : PROPRIETES DE L'AFFICHAGE Version aout 2011

Acer erecovery Management

1. Installation du Module

Manuel d'utilisation de l'administration du site Japo.ch - 1

Louer et utiliser un Hébergement Mutualisé OVH (Version 1.0)


WINDOWS 8. Windows 8 se distingue par la présence de 2 interfaces complémentaires :

Utilisation de Sarbacane 3 Sarbacane Software

I Pourquoi une messagerie?

Publipostage avec Calc

Tutorial Ophcrack. I) Ophcrack en API. (ou comment utiliser Ophcrack pour recouvrir un mot de passe sous Windows XP et Windows Vista)

FAIRE SES COMPTES AVEC GRISBI

PHPWEBSITE -Tutoriel image

AGASC / BUREAU INFORMATION JEUNESSE Saint Laurent du Var Tel : bij@agasc.fr Word: Les tableaux.

Créer son Blog! Une fois votre compte blogger ouvert, vous allez pouvoir cliquer sur «Nouveau Blog» Une nouvelle fenêtre apparaît

Qu est ce qu une bibliothèque?

Publier un Carnet Blanc

Comment autoriser un programme à communiquer avec Internet sous Vista?

Les calques supplémentaires. avec Magix Designer 10 et autres versions

Virtualisation de Windows dans Ubuntu Linux

Affectation standard Affectation modifiée (exemple)

Date M.P Libellé Catégorie S.Catégorie Crédit Débit Solde S.B

Cours Excel : les bases (bases, texte)

VOCABULAIRE LIÉ AUX ORDINATEURS ET À INTERNET

DA MOTA Anthony - Comparaison de technologies : PhoneGap VS Cordova

TRUCS & ASTUCES SYSTEME. 1-Raccourcis Programme sur le Bureau (7)

Tout savoir sur le clavier

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

Transcription:

Positionner ses widgets avec les layouts Comme vous le savez, une fenêtre peut contenir toutes sortes de widgets : des boutons, des champs de texte, des cases à cocher... Placer ces widgets sur la fenêtre est une science à part entière. Je veux dire par là qu'il faut vraiment y aller avec méthode, si on ne veut pas que la fenêtre ressemble rapidement à un champ de bataille Comment bien placer les widgets sur la fenêtre? Comment gérer les redimensionnements de la fenêtre? Comment s'adapter automatiquement à toutes les résolutions d'écran? On distingue 2 techniques différentes pour positionner des widgets : Le positionnement absolu : c'est celui que nous avons vu jusqu'ici, avec l'appel à la méthode setgeometry (ou move)... Ce positionnement est très précis, car on place les widgets au pixel près, mais cela comporte un certain nombre de défauts comme nous allons le voir. Le positionnement relatif : c'est le plus flexible et c'est celui que je vous recommande d'utiliser autant que possible. Nous allons l'étudier dans ce chapitre. Sommaire Le positionnement absolu et ses défauts L'architecture des classes de layout Les layouts horizontaux et verticaux Le layout de grille Le layout de formulaire Combiner les layouts

1. Le positionnement absolu et ses défauts Nous allons commencer par voir le code Qt de base que nous allons utiliser dans ce chapitre, puis nous ferons quelques rappels sur le positionnement absolu que vous avez déjà utilisé sans savoir exactement ce que c'était

1.1 - Le code Qt de base #! /usr/bin/python #-*-coding: utf-8 -*- from PyQt4.QtGui import * from PyQt4.QtCore import * import os,sys def main(args): a=qapplication(args) fenetre=qwidget() bouton=qpushbutton("bonjour", fenetre) bouton.move(70, 60) fenetre.show() r=a.exec_() return r if name ==" main ": main(sys.argv) C'est très simple : nous créons une fenêtre, et nous affichons un bouton que nous plaçons aux coordonnées (70, 60) sur la fenêtre. Le résultat est le suivant :

Dans le code précédent, nous avons positionné notre bouton de manière absolue en faisant bouton.move(70, 60); Le bouton a été très précisément placé 70 pixels sur la droite et 60 pixels plus bas. Imaginez que l'utilisateur s'amuse à redimensionner la fenêtre : Empêcher l'utilisateur de redimensionner la fenêtre? setfixedsize Le cas des résolutions d'écran plus petites que la vôtre : votre résolution (1600 x 1200). Vous placiez un bouton 1200 pixels sur la droite. L'utilisateur a une résolution plus petite (1024 x 768). Il ne pourra jamais voir le bouton! Préférez l'autre méthode : le positionnement relatif. Par exemple, "Le bouton 1 est en-dessous du bouton 2, qui est à gauche du bouton 3". Ce problème est géré par appelle les layouts avec Qt. Ce sont des conteneurs de widgets.

1.2 - L'architecture des classes de layout positionnement horizontal et vertical des widgets, ou encore le positionnement sous forme de grille. Ce sont les classes gérant les layouts de Qt. Toutes les classes héritent de la classe de base QLayout.

On compte donc en gros les classes : QBoxLayout QHBoxLayout QVBoxLayout QGridLayout QFormLayout QStackedLayout QLayout est ce qu'on appelle une classe abstraite : une classe "de base" qu'on ne peut pas instancier. C'est-à-dire qu'on ne peut pas créer d'objets de type QLayout. QLayout sert de "modèle" de base pour les autres classes

2. Les layouts horizontaux et verticaux Nous allons travailler sur 2 classes : QHBoxLayout QVBoxLayout QHBoxLayout et QVBoxLayout héritent de QBoxLayout. Ce sont des classes très similaires (la doc Qt parle de "convenience classes", des classes qui sont là pour vous aider à aller plus vite mais qui sont en fait quasiment identiques à QBoxLayout). Nous n'allons pas utiliser QBoxLayout, mais juste ses classes filles QHBoxLayout et QVBoxLayout (ça revient au même).

2.1 - Le layout horizontal L'utilisation d'un layout se fait en 3 temps : 1. On crée les widgets 2. On crée le layout et on place les widgets dedans 3. On dit à la fenêtre d'utiliser le layout qu'on a créé 1 ) Créer les widgets Pour les besoins de ce tutoriel, nous allons créer plusieurs boutons de type QPushButton : bouton1 = QPushButton("Bonjour") bouton2 = QPushButton("les") bouton3 = QPushButton(qApp.trUtf8("Zéros")) 3 boutons, pas de fenêtre parente comme précédemment bouton1 = QPushButton("Bonjour", fenetre) On ne place pas les boutons dans la fenêtre, mais dans un conteneur : le layout.

2 ) Créer le layout et placer les widgets dedans monlayout = QHBoxLayout() rajoutons nos widgets à l'intérieur : monlayout.addwidget(bouton1) monlayout.addwidget(bouton2) monlayout.addwidget(bouton3) La méthode addwidget du layout attend que vous lui donniez en paramètre un (pointeur vers le) widget à ajouter au conteneur.

3 ) Indiquer à la fenêtre d'utiliser le layout fenetre.setlayout(monlayout) La méthode setlayout de la fenêtre attend (un pointeur vers) le layout à utiliser. Et voilà, notre fenêtre contient maintenant notre layout, qui contient les widgets. La layout se chargera d'organiser les widgets horizontalement tout seul.

4 ) Résumé du code Voici le code complet de notre fichier main.py : #! /usr/bin/python #-*-coding: utf-8 -*- from PyQt4.QtGui import * from PyQt4.QtCore import * import os,sys def main(args): a=qapplication(args) fenetre = QWidget() bouton1 = QPushButton("Bonjour") bouton2 = QPushButton("les") bouton3 = QPushButton(qApp.trUtf8("Zéros")) monlayout = QHBoxLayout() monlayout.addwidget(bouton1) monlayout.addwidget(bouton2) monlayout.addwidget(bouton3) fenetre.setlayout(monlayout) fenetre.show() r=a.exec_() return r if name ==" main ": main(sys.argv) J'ai surligné les principales nouveautés.

5 ) Résultat Voilà à quoi ressemble la fenêtre maintenant que l'on utilise un layout horizontal : Les boutons sont automatiquement disposés de manière horizontale! L'intérêt principal du layout, c'est son comportement face aux redimensionnements de la fenêtre. Essayons de l'élargir : Les boutons continuent de prendre l'espace en largeur. On peut aussi l'agrandir en hauteur :

On remarque que les widgets restent centrés verticalement. Vous pouvez aussi essayer de réduire la taille de la fenêtre. On vous interdira de la réduire si les boutons ne peuvent plus être affichés, ce qui vous garantit que les boutons ne risquent plus de disparaître comme avant! 6 ) Schéma des conteneurs En résumé, la fenêtre contient le layout qui contient les widgets. Le layout se charge d'organiser les widgets. Schématiquement, ça se passe donc comme ça : Le layout est invisible à l'affichage On vient de voir le layout QHBoxLayout qui organise les widgets horizontalement. Il y en a un autre qui les organise verticalement (c'est quasiment la même chose) : QVBoxLayout.

2.2 - Le layout vertical Pour utiliser un layout vertical, il suffit de remplacer QHBoxLayout par QVBoxLayout dans le code précédent. Oui oui, c'est aussi simple que ça #! /usr/bin/python # * coding: utf 8 * from PyQt4.QtGui import * from PyQt4.QtCore import * import os,sys def main(args): a=qapplication(args) fenetre = QWidget() bouton1 = QPushButton("Bonjour") bouton2 = QPushButton("les") bouton3 = QPushButton(qApp.trUtf8("Zéros")) monlayout = QVBoxLayout() monlayout.addwidget(bouton1) monlayout.addwidget(bouton2) monlayout.addwidget(bouton3) fenetre.setlayout(monlayout) fenetre.show() r=a.exec_() return r if name ==" main ": main(sys.argv)

Compilez et exécutez ce code, et admirez le résultat : Amusez-vous à redimensionner la fenêtre. Vous voyez là encore que la layout adapte les widgets qu'il contient à toutes les dimensions. Il empêche en particulier la fenêtre de devenir trop petite, ce qui aurait empêché l'affichage des boutons.

3. Le layout de grille Les layouts horizontaux et verticaux ne permettent pas de créer des dispositions très complexes sur votre fenêtre. C'est là qu'entre en jeu QGridLayout, qui est en fait un peu un assemblage de QHBoxLayout et QVBoxLayout. Il s'agit d'une disposition en grille, comme un tableau avec des lignes et des colonnes.

3.1 - Schéma de la grille Il faut imaginer que votre fenêtre peut être découpée sous la forme d'une grille avec une infinité de cases, comme ceci : Si on veut placer un widget en haut à gauche, il faudra le placer à la case de coordonnées (0, 0). Si on veut en placer un autre en-dessous, il faudra utiliser les coordonnées (1, 0). Ainsi de suite

3.2 - Utilisation basique de la grille Essayons d'utiliser un QGridLayout simplement pour commencer. Nous allons placer un bouton en haut à gauche, un à sa droite et un en-dessous. La seule différence réside en fait dans l'appel à la méthode addwidget. Celle-ci accepte 2 paramètres supplémentaires : les coordonnées où placer le widget sur la grille. #! /usr/bin/python #-*-coding: utf-8 -*- from PyQt4.QtGui import * from PyQt4.QtCore import * import os,sys def main(args): a=qapplication(args) fenetre = QWidget() bouton1 = QPushButton("Bonjour") bouton2 = QPushButton("les") bouton3 = QPushButton(qApp.trUtf8("Zéros")) monlayout = QGridLayout() monlayout.addwidget(bouton1,0,0) monlayout.addwidget(bouton2,0,1) monlayout.addwidget(bouton3,1,0) fenetre.setlayout(monlayout) fenetre.show() r=a.exec_() return r if name ==" main ": main(sys.argv)

Résultat : Si vous comparez avec le schéma de la grille que j'ai fait plus haut, vous voyez que les boutons ont bien été disposés selon les bonnes coordonnées. D'ailleurs en parlant du schéma plus haut, il y a un truc que je comprends pas, c'est tous ces points de suspension "..." là. Ca veut dire que la taille de la grille est infinie? Dans ce cas, comment je fais pour placer un bouton en bas à droite? Qt "sait" quel est le widget à mettre en bas à droite en fonction des coordonnées des autres widgets. Le widget qui a les coordonnées les plus élevées sera placé en bas à droite. Petit test, rajoutons un bouton aux coordonnées (1, 1) :

#! /usr/bin/python #-*-coding: utf-8 -*- from PyQt4.QtGui import * from PyQt4.QtCore import * import os,sys def main(args): a=qapplication(args) fenetre = QWidget() bouton1 = QPushButton("Bonjour") bouton2 = QPushButton("les") bouton3 = QPushButton(qApp.trUtf8("Zéros")) bouton4 = QPushButton(qApp.trUtf8("Ça va?")) monlayout = QGridLayout() monlayout.addwidget(bouton1,0,0) monlayout.addwidget(bouton2,0,1) monlayout.addwidget(bouton3,1,0) monlayout.addwidget(bouton4,1,1) fenetre.setlayout(monlayout) fenetre.show() r=a.exec_() return r if name ==" main ": main(sys.argv) Résultat :

Si on veut, on peut aussi décaler le bouton encore plus en bas à droite dans une nouvelle ligne et une nouvelle colonne : monlayout.addwidget(bouton4, 2, 30) On voit au passage, que puisqu'on a demandé 30 colonnes, pour 3 boutons en largeur, les 27 colonnes vides sont de largeur nulles, car elles sont vides.

3.3 - Un widget qui occupe plusieurs cases L'avantage de la disposition en grille, c'est qu'on peut faire en sorte qu'un widget occupe plusieurs cases à la fois. On parle de spanning (ceux qui font du HTML doivent avoir entendu parler des attributs rowspan et colspan sur les tableaux). Pour faire cela, il faut appeler une version surchargée de addwidget qui accepte 2 paramètres supplémentaires : le rowspan et le columnspan. rowspan : nombre de lignes qu'occupe le widget (par défaut 1) columnspan : nombre de colonnes qu'occupe le widget (par défaut 1) Imaginons un widget placé en haut à gauche, aux coordonnées (0, 0). Si on lui donne un rowspan de 2, il occupera alors l'espace suivant : Si on lui donne un columnspan de 3, il occupera cet espace :

L'espace pris par le widget au final dépend de la nature du widget (les boutons s'agrandissent en largeur mais pas en hauteur par exemple), et dépend du nombre de widgets sur la grille. En pratiquant vous allez rapidement comprendre comment ça fonctionne. La méthode addwidget que nous utilisons a pour prototype C++ : void addwidget (QWidget* widget, int row, int column, int rowspan, int columnspan, Qt::Alignment alignment = 0 ) Essayons de faire en sorte que le bouton "Zéros" prenne 2 colonnes de largeur : monlayout.addwidget(bouton3,1,0,1,2) Les 2 derniers paramètres correspondent respectivement au rowspan et au columnspan. Le rowspan est ici de 1, c'est la valeur par défaut on ne change donc rien, mais le columnspan est de 2.

Le bouton va donc "occuper" 2 colonnes : Essayez le columnspan à 3 : vous ne verrez a priori aucun changement... Sauf si vous redimensionnez la fenêtre (en largeur). Dans ce cas, le troisième bouton occupera effectivement 3 colonnes de largeur.

4. Le layout de formulaire QFormLayout Le layout de formulaire QFormLayout est un layout assez récent spécialement fait pour les fenêtres qui contiennent des formulaires. Un formulaire est en général une suite de libellés ("Votre prénom :") associés à des champs de formulaire (zone de texte par exemple) : Normalement, pour écrire du texte dans la fenêtre, on utilise le widget QLabel (libellé), et QLineEdit, dont on parlera plus en détail dans le prochain chapitre. L'avantage du layout que nous allons utiliser, c'est qu'il simplifie notre travail en créant automatiquement des QLabel pour nous. QFormLayout = QGridLayout à 2 colonnes et plusieurs lignes. En effet, le QFormLayout n'est en fait rien d'autre qu'une version spéciale du QGridLayout pour les formulaires, avec quelques particularités : il s'adapte en fonction des habitudes des OS, pour certains les libellés sont alignés à gauche, pour d'autres ils sont alignés à droite, etc. L'utilisation d'un QFormLayout est très simple : au lieu de la méthode addwidget, nous allons utiliser une méthode addrow qui prend 2 paramètres : Le texte du libellé Un pointeur vers le champ du formulaire Pour faire simple, nous allons créer 3 champs de formulaire de type "Zone de texte à une ligne" (QLineEdit), puis nous allons les placer dans un QFormLayout au moyen de la méthode addrow :

#! /usr/bin/python #-*-coding: utf-8 -*- from PyQt4.QtGui import * from PyQt4.QtCore import * import os,sys def main(args): a=qapplication(args) fenetre = QWidget() prenom = QLineEdit() nom = QLineEdit() age = QLineEdit() layoutf = QFormLayout() layoutf.addrow(qapp.trutf8("prénom"),prenom) layoutf.addrow(qapp.trutf8("nom"),nom) layoutf.addrow(qapp.trutf8("age"),age) fenetre.setlayout(layoutf) fenetre.show() r=a.exec_() return r if name ==" main ": main(sys.argv) Résultat :

On peut aussi définir des raccourcis clavier pour accéder rapidement aux champs du formulaire. Pour ce faire, placez un symbole "&" devant la lettre du libellé que vous voulez transformer en raccourci. Explication en image (euh, en code) : LayoutF.addRow("&Nom", nom) layoutf.addrow("&prénom", prenom) layoutf.addrow("&age", age) La lettre "p" est désormais un raccourci vers le champ du prénom. "n" pour le champ nom. "g" pour le champ âge. L'utilisation du raccourci dépend de votre système d'exploitation. Sous Windows, sous Linux, il faut faire Alt puis la touche raccourci. Les lettres raccourcis apparaissent soulignées : Faites Alt + N pour accéder directement au champ du nom! Souvenez-vous de ce symbole &, il est très souvent utilisé en GUI Design (design de fenêtre) pour indiquer quelle lettre sert de raccourci. On le réutilisera notamment pour avoir des raccourcis dans les menus de la fenêtre. Ah, et si vous voulez par contre vraiment afficher un symbole & dans un libellé, tapez-en deux : "&&". Exemple : "Bonnie && Clyde".

5. Combiner les layouts Avant de terminer ce chapitre, il me semble important que nous jetions un oeil aux layouts combinés, une fonctionnalité qui va vous faire comprendre toute la puissance des layouts.

5.1 - Un cas concret Prenons par exemple notre formulaire. Supposons que l'on veuille ajouter un bouton "Quitter". Si vous voulez placer ce bouton en bas du formulaire, comment faire? Il va falloir d'abord créer un layout vertical (QVBoxLayout), et placer à l'intérieur notre layout de formulaire puis notre bouton "Quitter". Cela donne le schéma suivant : On voit que notre QVBoxLayout contient 2 choses, dans l'ordre : 1. Un QFormLayout (qui contient lui-même d'autres widgets) 2. Un QPushButton Un layout peut donc contenir aussi bien des layouts que des widgets.

5.2 - Utilisation de addlayout Pour insérer un layout dans un autre, on utilise addlayout au lieu de addwidget. #! /usr/bin/python #-*-coding: utf-8 -*- from PyQt4.QtGui import * from PyQt4.QtCore import * import os,sys def main(args): a=qapplication(args) fenetre = QWidget() prenom = QLineEdit() nom = QLineEdit() age = QLineEdit() #objet 1, formulaire layoutf = QFormLayout() layoutf.addrow(qapp.trutf8("&prénom"),prenom) layoutf.addrow(qapp.trutf8("&nom"),nom) layoutf.addrow(qapp.trutf8("&age"),age) #objet2, bouton quitter bouttonquitter=qpushbutton("&quitter") QWidget.connect(bouttonQuitter,SIGNAL("clicked()"),a,SLOT("quit()")) #Layout principal : création et peuplement layoutv=qvboxlayout() layoutv.addlayout(layoutf) layoutv.addwidget(bouttonquitter) fenetre.setlayout(layoutv) fenetre.show() r=a.exec_() return r if name ==" main ": main(sys.argv)

J'ai surligné les ajouts au layout vertical principal : L'ajout du sous-layout de formulaire (addlayout) L'ajout du bouton (addwidget) Vous remarquerez que je fais les choses un peu dans l'ordre inverse : d'abord je crée les widgets et layouts "enfants" (le QFormLayout), et ensuite je crée le layout principal (le QVBoxLayout) et j'y ajoute le layout enfant que j'ai créé. Au final, la fenêtre qui apparaît est la suivante : On ne le voit pas, mais la fenêtre contient d'abord un QVBoxLayout, qui contient lui-même un layout de formulaire et un bouton.

5.3 - Exercice Essayez d'obtenir le rendu suivant :