Vous connaissez pour le moment 5 widgets :
Gtk.Window
Gtk.Button
Gtk.Label
Gtk.Box
Gtk.Grid
Or, GTK+ 3 est ne se limite pas à eux ! Je vais donc compléter cette petite liste avec 5 nouveaux types de widget qui sont pour moi les plus importants.
- Les propriétés
- Les labels
- Les champs de texte
- Les toggleButtons et les switchs
- Les images avec Pixbuf
- Les calendriers
Les propriétés
Les propriétés décrivent la configuration et l’état d’un widget. Comme pour les signaux, chaque widget à ses propres propriétés. C’est pour ça qu’il faut toujours garder la documentation sous la main. Vous pouvez spécifier ces propriétés lors de la création du widget ou après, c’est selon vos préférences. Par exemple :
1 2 3 4 | button = Gtk.Button(label='Un label') # ou alors button = Gtk.Button() button.set_label('Un label') |
Ces deux façon de faire donneront le même résultat. Il existe encore une dernière possibilité de construire un bouton même si je ne lui trouve pas d’intérêt :
1 | button = Gtk.Button.new_with_label('Un label') |
Et si je veux modifier ou avoir accès aux valeurs des propriétés ?
La première solution est celle que j’utilise la plupart du temps :
1 2 | button.set_label('Un nouveau label') # Défini un nouveau label label_button = button.get_label() # Retourne le label du boutton |
Une seconde solution :
1 2 | button.set_property('label', 'Un nouveau label') label_button = button.get_property('label') |
Enfin, il arrivera peut être qu’un jour vous ayez besoin d’afficher les différentes propriétés d’un widget :
1 2 3 | >>> button = Gtk.Button(label='Salut') >>> print(dir(button.props)) ['action_name', 'action_target', 'always_show_image', 'app_paintable', 'border_width', 'can_default', 'can_focus', 'child', 'composite_child', 'double_buffered', 'events', 'expand', 'focus_on_click', 'halign', 'has_default', 'has_focus', 'has_tooltip', 'height_request', 'hexpand', 'hexpand_set', 'image', 'image_position', 'is_focus', 'label', 'margin', 'margin_bottom', 'margin_end', 'margin_left', 'margin_right', 'margin_start', 'margin_top', 'name', 'no_show_all', 'opacity', 'parent', 'receives_default', 'related_action', 'relief', 'resize_mode', 'scale_factor', 'sensitive', 'style', 'tooltip_markup', 'tooltip_text', 'use_action_appearance', 'use_stock', 'use_underline', 'valign', 'vexpand', 'vexpand_set', 'visible', 'width_request', 'window', 'xalign', 'yalign'] |
Les labels
Si vous voulez afficher du texte, vous le ferez grâce aux labels. Ils vous offrent un nombre d’option impressionnant.
Je le rappelle encore une fois, mais gardez votre documentation ouverte à la page du widget en question. Pour les labels, c’est par ici.
Un label se construit de cette façon :
1 2 3 4 | label = Gtk.Label('Je suis un label') # Ou alors label = Gtk.Label() label.set_text('Je suis un label') |
Vous pouvez rendre ce label sélectionnable avec Gtk.Label.set_selectable()
qui prend en argument un booléen. Ça signifie que l’utilisateur pourra sélectionner votre texte et le copier avec un clique droit par exemple. Il ne pourra pas le modifier.
GTK+ impose quelques règles afin que les développeurs ne fassent pas n’importe quoi et qu’il y ai une unicité entre les différents programmes. Seuls les labels contenant une information utile — comme les messages d’erreurs — devraient être fait sélectionnable.
C’est tristounet… Si je veux lui donner du style ?
Vous voulez le pimper ? Pimpons-le alors !
On commence par l’alignement du texte. Par défaut, il est aligné à gauche. Voici les différentes justification possible :
1 2 3 4 | label.set_justify(Gtk.Justification.LEFT) # Justifié à gauche label.set_justify(Gtk.Justification.CENTER) # Justifié au centre label.set_justify(Gtk.Justification.RIGHT) # Justifié à droite label.set_justify(Gtk.Justification.FILL) # Le texte est réparti dans le label pour le remplir |
Pour changer la couleur, la taille, la police, nous allons utiliser des balises. GTK utilise Pango pour faire le rendu. Pango est également capable d’afficher tous les caractères spéciaux que vous souhaitez.
Pour indiquer à GTK que vous voulez utiliser Pango, définissez le texte de votre label grâce à la méthode Gtk.Label.set_markup()
et non plus Gtk.Label.set_text()
.
Les différentes balises disponibles :
Balises | Effet |
---|---|
<b></b> | Met le texte en gras |
<i></i> | Met le texte en italique |
<u></u> | Souligne le texte |
<s></s> | Barre le texte |
<big></big> | Incrémente la taille du texte |
<small></small> | Décrémente la taille du texte |
<tt></tt> | Met le texte en télétype |
<sup></sup> | Met le texte en exposant |
<sub></sub> | Met le texte en indice |
Pour ceux qui ne savent pas comment ça s’utilise :
1 2 3 | <b>Je suis un texte en gras !</b> <i>Et moi en italique !</i> Bah moi je suis rien... :'( |
Vous l’aurez compris, il suffit de mettre votre texte entre une balise ouvrante <>
et une balise fermante </>
.
Bien maintenant vous voulez changer la couleur ou la police ? On va utiliser la balise <span></span>
qui prend tous les attributs de votre texte. Par exemple :
1 2 | label = Gtk.Label() label.set_markup('<span color="#c0392b" weight="bold" font="FreeSerif">Je suis rouge, en gras avec une belle police !</span>') |
Il faut évidemment que la police soit présente sur l’ordinateur !
Voici les différents attributs :
Attributs | Effet |
---|---|
face/font/font_family | Définit la police d’écriture. Veillez à verifier que celle-ci soit installé sur l’ordinateur de votre utilisateur. |
size | La taille de la police en pixels. Peut aussi être xx-small, x-small, small, medium, large, x-large, xx-large |
style | Définit le style des caractères : normal, oblique ou italic |
weight | Définit l’épaisseur des caratcères : ultralight, light, normal, bold, ultrabold, heavy ou une valeur numérique |
variant | Par défaut sur la valeur normal, permet de mettre le texte en petite majuscules avec smallcaps |
stretch | Définit l’espacement entre les caratcères : ultracondensed, extracondensed, condensed, semicondensed, normal, semiexpanded, expanded, extraexpanded ou ultraexpanded. |
foreground | Définit la couleur du texte. La valeur doit être en hexadécimale (#000000 par exemple) |
background | Définit la couleur d’arrière plan du texte. La valeur doit être en hexadécimale (#000000 par exemple) |
underline | Définit le soulignement du texte: single, double, low ou none |
underline_color | Définit la couleur du soulignement. La valeur doit être en hexadécimale (#000000 par exemple) |
rise | Définit l’élévation du texte (en indice ou exposant) en valeur décimale. Les valeurs négatives sont possibles pour mettre le texte en indice |
strikethrough | Si True, barre le texte. Vaut False par défaut |
strikethrough_color | Définit la couleur de la rature. La valeur doit être en hexadécimale (#000000 par exemple) |
lang | Définit la langue du texte |
Comme vous pouvez le voir, il y en a pas mal. Vous avez de quoi vous amuser avec tout ça !
Petit bonus, je vais vous montrer comment faire des infobulles. Elles apparaissent quand vous laissez votre curseur sur un widget pendant quelques secondes. Elle vous informera de l’effet du widget auquel elle appartient.
Cette fonctionnalité appartenant à Gtk.Widget
, toutes les classes héritant possède donc cette fonctionnalité, c’est à dire tous les widgets de GTK+. Voici comment ça s’utilise :
1 2 | widget.set_tooltip_text('Ma super infobulle') # Avec un simple texte widget.set_tooltip_markup('<i>En italique</i>') # Vous l'aurez compris, vous pouvez également utiliser Pango dans les infobulles ! |
Les champs de texte
Un autre widget super important : le Gtk.Entry
. J’aborderais également le Gtk.SpinButton
ici.
Ces widgets permettent à l’utilisateur d’entrer du texte. Par exemple si vous avez un formulaire, vous allez demander le nom et le prénom de votre utilisateur à travers un Gtk.Entry
. Voici comment il s’utilise :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #!/usr/bin/env python3 # coding: utf-8 from gi.repository import Gtk def on_entry_activated(entry, label): ''' Est appellée quand entry est activé, récupère son texte et l'affiche dans label, puis nettoie l'entry ''' text = entry.get_text() # On récupère le texte label.set_text(text) # On l'affiche dans label entry.set_text('') # On vide l'entry window = Gtk.Window() window.set_title('Perroquet') window.set_border_width(10) window.connect('delete-event', Gtk.main_quit) layout = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) parrot = Gtk.Label('Entrez votre texte & appuyez sur entrée...') entry = Gtk.Entry() entry.connect('activate', on_entry_activated, parrot) # Quand l'utilisateur appuie sur <Entrée> layout.pack_start(entry, True, True, 0) layout.pack_start(parrot, True, True, 0) window.add(layout) window.show_all() Gtk.main() |
Le résultat :
Vous l’aurez compris, Gtk.Entry.get_text()
récupère le texte (toujours de type string) et Gtk.Entry.set_text(str)
définit le texte. Il existe pas mal d’options, notamment mettre un placeholder avec Gtk.Entry.set_placeholder_text(str)
.
Je vous ai également parlé d’un Gtk.SpinButton
. C’est un widget qui hérite de Gtk.Entry
et qui offre deux boutons en plus, un pour incrémenter et un autre pour décrémenter. Si vous avez des valeurs numériques à demander à votre utilisateur, c’est le meilleur moyen. Voici la tête que ça a :
Voici un code implémentant un SpinButton :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/usr/bin/env python3 # coding: utf-8 from gi.repository import Gtk window = Gtk.Window() window.set_border_width(10) window.connect('delete-event', Gtk.main_quit) adjustment = Gtk.Adjustment(0, 0, 100, 1, 10, 0) spin_button = Gtk.SpinButton() spin_button.set_adjustment(adjustment) window.add(spin_button) window.show_all() Gtk.main() |
Comme vous le voyez, on a un nouveau widget qui vient faire son trouble-fête, le Gtk.Adjustment
. En effet, votre Gtk.SpinButton
demande qu’on lui fournisse ce nouvel élément afin de savoir :
- sa valeur actuelle
- sa valeur minimale
- sa valeur maximale
- et son pas, c’est à dire de combien en combien il doit changer sa valeur (de 1 en 1, de 5 en 5…)
C’est exactement les arguments que j’ai mis quand j’ai initialisé le Gtk.Adjustement
. Les deux derniers ne nous intéressent pas pour le moment, ils vont servir au moment où l’on va s’intéresser au scroll des pages. Mettez 10
et 0
et tout se passera très bien.
La valeur de cette entrée se récupère avec Gtk.SpinButton.get_value()
, elle sera de type float
. Tout naturellement, pour assigner une valeur c’est Gtk.SpinButton.set_value()
.
Les toggleButtons et les switchs
Comme ils fonctionnent un peu de la même façon, j’ai décider de tout mettre ensemble. Ici, nous allons aborder les toggleButtons et les switchs. Commençons par le premier.
Les toggleButtons vous avez dû en voir souvent, rien que sur ce site. Ce sont en effet des petites cases que l’on peut cocher ou décocher. Ils existent aussi sous une autre forme, celle des boutons. Il y aussi les boutons de type radio, eux, vous ne pouvez en sélectionner qu’un seul parmi les autres. Pour être plus clair, voici un code que je vous recommande fortement d’essayer chez vous :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #!/usr/bin/env python3 # coding: utf-8 from gi.repository import Gtk window = Gtk.Window() window.set_title('Des boutons partout !') window.set_border_width(10) window.connect('delete-event', Gtk.main_quit) main_layout = Gtk.Grid() main_layout.set_column_spacing(6) main_layout.set_row_spacing(6) main_layout.set_row_homogeneous(True) #===================== # Bouton de type radio #===================== # On créer un premier radio avec un label radio_1 = Gtk.RadioButton.new_with_label(None, 'Pilule bleu') # On en créer un second en lui indiquant qu'il appartient au même groupe que le radio_1 radio_2 = Gtk.RadioButton.new_from_widget(radio_1) radio_2.set_label('Pilule rouge') # On lui met un label # On fait tout en même temps radio_3 = Gtk.RadioButton.new_with_label_from_widget(radio_1, 'Je n\'ai pas compris la référence...') main_layout.attach(radio_1, 0, 0, 1, 1) main_layout.attach(radio_2, 1, 0, 1, 1) main_layout.attach(radio_3, 2, 0, 1, 1) #================== # Bouton persistant #================== # Voyez ça comme des interupteurs toggle_1 = Gtk.ToggleButton('Faire un café') toggle_2 = Gtk.ToggleButton('Acheter un croissant') toggle_3 = Gtk.ToggleButton('Dormir') main_layout.attach(toggle_1, 0, 1, 1, 1) main_layout.attach(toggle_2, 1, 1, 1, 1) main_layout.attach(toggle_3, 2, 1, 1, 1) #=============== # Cases à cocher #=============== # Ils fonctionnent exactement de la même façon que les boutons persistants sauf # qu'ils rajoutent une petite case à cocher check_1 = Gtk.CheckButton('Faire un café') check_2 = Gtk.CheckButton('Acheter un croissant') check_3 = Gtk.CheckButton('Dormir') main_layout.attach(check_1, 0, 2, 1, 1) main_layout.attach(check_2, 0, 3, 1, 1) main_layout.attach(check_3, 0, 4, 1, 1) window.add(main_layout) window.show_all() Gtk.main() |
Le résultat :
Petite explication pour les boutons de type radio. Comme je vous l’ai expliqué, ils fonctionnent par groupe. J’ai créé le premier avec Gtk.RadioButton.new_with_label(None, 'Pilule bleu')
. Ce constructeur prend en premier argument un tableau contenant au moins un membre du groupe. Si vous souhaitez créer un nouveau groupe, rentrez None
. J’utilise également Gtk.RadioButton.new_from_widget(radio_1)
qui prend comme seul argument un membre du groupe (mais pas un tableau cette fois-ci) ou None
pour créer un nouveau groupe. Et enfin, la méthode Gtk.RadioButton.new_with_label_from_widget(radio_1, 'Je n\'ai pas compris la référence...')
permet de tout faire d’un coup. Elle prend en premier argument un membre du groupe ou None
pour créer un nouveau groupe puis son label.
Pour récupérer la valeur d’un de ces boutons, il vous suffit de faire un Gtk.ToggleButton.toggled()
qui vous retournera un booléen. True
si le bouton est coché/activé et False
sinon.
Passons aux switchs. Encore un bouton, mais que ne prend que deux états : On ou Off.
Le code de cette minuscule fenêtre :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #!/usr/bin/env python3 # coding: utf-8 from gi.repository import Gtk window = Gtk.Window() window.set_title('Switch me !') window.set_border_width(10) window.connect('delete-event', Gtk.main_quit) main_layout = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) switch_on = Gtk.Switch() switch_on.set_active(True) # Je force le premier switch à passer sur "On" switch_off = Gtk.Switch() # Le second est par défaut sur "Off" main_layout.pack_start(switch_on, True, True, 0) main_layout.pack_start(switch_off, True, True, 0) window.add(main_layout) window.show_all() Gtk.main() |
Pour récupérer la valeur d’un switch, faite simplement un Gtk.Switch.get_active()
qui vous retournera soit True
soit False
.
Les images avec Pixbuf
Pour toutes les images que vous voudriez mettre dans votre GUI, il faudra passer par un Pixbuf. Je vous rassure, rien de douloureux. Voici le lien de la documentation de GdkPixbuf
: http://lazka.github.io/pgi-docs/GdkPixbuf-2.0/.
Il existe deux façons de mettre des images dans votre interface. Soit, comme dit dans l’introduction, de passer par un Pixbuf, soit de l’importer directement dans le Gtk.Image
. GTK+ conseille la première méthode, je vous la recommande aussi.
Avec un Pixbuf :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env python3 # coding: utf-8 from gi.repository import Gtk, GdkPixbuf window = Gtk.Window() window.set_title('Wow une image') window.connect('delete-event', Gtk.main_quit) # On créer notre Pixbuf image = GdkPixbuf.Pixbuf.new_from_file('patrick.png') image_renderer = Gtk.Image.new_from_pixbuf(image) window.add(image_renderer) window.show_all() Gtk.main() |
N’oublier pas d’importer GdkPixbuf
! Voici le superbe rendu :
Le même rendu sera obtenu avec ce code (du coup, sans le Pixbuf) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #!/usr/bin/env python3 # coding: utf-8 from gi.repository import Gtk window = Gtk.Window() window.set_title('Wow une image') window.connect('delete-event', Gtk.main_quit) image_renderer = Gtk.Image.new_from_file('patrick.png') window.add(image_renderer) window.show_all() Gtk.main() |
L’intérêt des Pixbuf c’est que vous pouvez modifier votre image directement depuis votre fenêtre (incliner l’image, la redimensionner, etc). Je vous laisse chercher dans la documentation pour savoir comment faire.
N’oubliez pas que plus une image est lourde, plus elle met du temps à charger. Si vous trouvez que votre application est lente et que vous utilisez des Pixbuf, essayez de compresser vos images.
Autre intérêt, ce sont les GIFs. Vous avez pu en voir déjà pas mal tout au long de ce tutoriel et vous continuerez à en voir car j’adore ça ! Je parle bien sûr de ces images animées. Comment en mettre dans vos logiciels ? Pas le choix ici, vous êtes obligé de passer par un Gdk.Pixbuf
, mais pas n’importe quel Gdk.Pixbuf
non, un Gdk.PixbufAnimation
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env python3 # coding: utf-8 from gi.repository import Gtk, GdkPixbuf window = Gtk.Window() window.set_title('Wow une image') window.connect('delete-event', Gtk.main_quit) # On créer notre PixbufAnimation image = GdkPixbuf.PixbufAnimation.new_from_file('mon_super_gif.gif') image_renderer = Gtk.Image.new_from_animation(image) # ATTENTION on change de méthode! window.add(image_renderer) window.show_all() Gtk.main() |
Les calendriers
Le calendrier a été l’un des widgets qui m’a le plus impressionné par sa simplicité. Je ne pensais pas qu’il serait aussi facile de mettre un calendrier dans un logiciel. Voyons ça ensemble, j’espère que vous serez aussi impressionné que moi !
Bon, tout le monde le sait, un calendrier c’est très complexe et sûrement pas à la porté de tout le monde. Encore une fois, GTK+ est là et nous propose un calendrier tout fait et très complet. Regardez un peu ça :
1 2 3 4 5 6 7 8 9 10 11 12 | from gi.repository import Gtk window = Gtk.Window() window.set_title('Un calendrier') window.set_border_width(10) window.connect('delete-event', Gtk.main_quit) calendar = Gtk.Calendar() window.add(calendar) window.show_all() Gtk.main() |
Donnera la figure suivante.
Par défaut, il sera toujours réglé sur le jour actuel.
Pour changer le jour, ça se fera avec Gtk.Calendar.select_day()
et pour changer le mois et l’année, ce sera avec Gtk.Calendar.select_month()
. Oui je sais ça ne devrait être que les mois, mais manque de bol, ça fait les deux en même temps. D’ailleurs en parlant de mois, il faut que je vous prévienne que les indices sont déplacées d’un cran. C’est à dire que pour GTK+, juin est le cinquième mois et non le sixième, comme décembre est le onzième et non le douzième. À part ça, tout se passe très bien !
Par exemple, mettons la date au premier janvier 2000.
1 2 | calendar.select_day(1) calendar.select_month(0, 2000) |
Trop facile, non ?
Pour réagir quand l’utilisateur change la date, ce sera avec le signal Gtk.Calendar.signals.day_selected
. Petite démonstration :
1 2 3 4 5 6 | calendar.connect('day-selected', on_date_changed) def on_date_changed(calendar): year, month, day = calendar.get_date() month += 1 # On décale les indices print('Vous avez sélectionné le {}/{}/{}'.format(day, month, year)) |
Pour récupérer la date, c’est donc avec Gtk.Calendar.get_date()
qui vous renvoie 3 entiers représentant dans l’ordre :
- l’année
- le mois (attention aux indices !)
- le jour
Enfin, on peut également ajouter un marqueur sur un jour avec Gtk.Calendar.mark_day()
qui prend un entier représentant le jour. Attention, si vous le placez sur le 5 novembre, il sera placé sur tous les 5 du calendrier.
C’est bon, vous vous êtes bien reposé ? On ne va pas se le cacher, cette partie était beaucoup trop simple !
La prochaine fois on corse un peu le tout en ajoutant de la POO dans tout ça. Impatient non ?