À la découverte de nouveaux widgets !

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 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 ! :soleil:

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>')
Un magnifique texte - Presque de l’art

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 :

Démonstration du Gtk.Entry à travers une fenêtre-perroquet
Bravo à ceux qui trouverons la référence au film d’animation !

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 :

Un spinbutton de Gtk+

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.

Démonstration des switchs

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. :lol: 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 :

Une image importé dans GTK
So much wow !

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.

Un calendrier sous GTK

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. :p 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 ?