Participer au site avec un Tip
Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Mise en oeuvre d'une barre d'outils

Définition d'un menu réutilisable Enrichissement de la barre de statuts


Accès rapide :
Quelques précisions sur la notion de barres d'outils
Création d'une barre d'outils
Configurer le placement de votre barre d'outils
Configurer la flottaison
Configurer la possibilité de ré-ancrer une barre d'outils
Configurer les côtés sur lesquels une barre peut s'ancrer
Interconnexion avec la barre de statuts
Ajouter des séparateurs dans une barre d'outils
Connecter les slots à votre barre d'outils
Cas de l'utilisation d'une action QT (QAction)
Cas de l'utilisation d'un widget
Injecter plusieurs barres d'outils dans votre application
Travaux pratiques
L'énnoncé de l'exercice
La correction de l'exercice

Quelques précisions sur la notion de barres d'outils

Par abus de langage, on parle de la barre d'outils d'une application. Mais en fait, cela est faux : une application peut proposer plusieurs barres d'outils. A titre d'exemple, voici une capture d'écran présentant une application avec trois barres d'outils (notez la présence du « gripper » devant chaque barre : il permet de les déplacer).

Une fenêtre graphique avec trois barres d'outils.

Ces barres sont souvent injectées ensembles juste en dessous de la barre de menu, d'où l'abus de langage. Pour rappel, voici un diagramme montrant la topologie classique d'une application graphique en mode « Bureau » (en mode « Desktop »).

Topographie d'une fenêtre graphique Qt.

Mais là aussi, il y a encore un petit mensonge. En réalité chaque barre d'outils peut être ancrée sur n'importe quel côté de la zone centrale, comme le montre la capture d'écran suivante. La fenêtre contient en interne une instance de la classe QDockWidget : c'est elle qui définit les quatre points d'ancrage.

Une fenêtre graphique avec trois barres d'outils ancrées à des positions différentes.

A l'instar d'une barre de menu, une barre d'outils peut se définir à partir d'actions. Ces actions, sont d'ailleurs souvent partagées entre la barre de menu et les barres d'outils. Mais cela n'est pas non plus une obligation : dans la capture d'écran précédente, vous pouvez observer une barre d'outils contenant directement des widgets Qt (un bouton et une case à cocher).

si la notion d'action Qt ne vous est pas encore familière, commencez par lire la page dédiée à ce concept.

Création d'une barre d'outils

Le conteneur de barre d'outils est déjà instancié dans une QMainWindow. Par contre, vous devez demander la production d'une nouvelle barre d'outils via la méthode addToolBar. Par défaut, la nouvelle barre d'outils sera ajoutée juste en dessous de la barre de menu et au-dessus de la zone centrale. Une barre d'outils possède un nom : il sera utilisé dans le menu contextuel permettant de cacher/afficher la barre.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
# Création de la barre d'outils avec son nom
toolbar = self.addToolBar("First tool bar")     # self représente la fenêtre de type QMainWindow

# Ajout d'une action dans la barre d'outils.
actNew = QAction(QIcon("icons/new.png"), "&New", self)
toolbar.addAction(actNew)

# Ajout d'un widget quelconque dans la barre d'outils.
toolbar.addWidget(QPushButton("A button"))
Creation d'une barre de menu

La capture d'écran ci-dessous affiche le menu contextuel pour cache/afficher une barre d'outils. Vous pouvez l'obtenir en cliquant avec le bouton droit de la souris sur la barre.

Votre première barre d'outils avec Python/Qt.

Si vous garder le bouton gauche de la souris appuyé sur le gripper à gauche de la barre d'outils (voir capture ci-dessous), vous devriez pouvoir l'ancrer sur un autre côté ou carrément détacher la barre de la fenêtre (la rendre flottante).

Vous pouvez déplacer votre barre d'outils.

Configurer le placement de votre barre d'outils

Configurer la flottaison

Si vous souhaitez interdire, ou non, la flottaison de votre barre d'outils, vous devez invoquer la méthode setFloatable (la valeur par défaut est True).

 1 
toolbar.setFloatable(False)
Interdire la possibilité de flottaison de la barre d'outils.

Configurer la possibilité de ré-ancrer une barre d'outils

Si vous souhaitez interdire, ou non, la possibilité d'ancrer la barre d'outils sur un autre côté de la fenêtre, vous devez utiliser la méthode setMovable (la valeur par défaut est True).

 1 
toolbar.setMovable(False)
Interdire la possibilité de déplacer la barre d'outils d'un côté à un autre.

Configurer les côtés sur lesquels une barre peut s'ancrer

Vous pouvez aussi directement créer et positionner votre barre d'outils sur un côté précis. Pour ce faire, il faut légèrement changer le code : vous devez d'abord produire l'instance de la barre d'outils. Elle sera de type QToolBar. Puis il vous faudra invoquer la méthode addToolBar en spécifiant en premier paramètre la position de la barre et en second paramètre l'instance de la barre d'outils.

il n'est pas possible d'invoquer addToolBar avec la position et le nom de la barre d'outils à produire (c'est bien dommage).
 1 
 2 
toolbar = QToolBar("First tool bar")
self.addToolBar(Qt.LeftToolBarArea, toolbar)
Positionner la barre d'outils sur un côté précis.

Enfin, si votre barre d'outils peut être déplacée (si elle est « movable »), alors vous pouvez limiter les côtés sur lesquels elle pourra s'accrocher. L'exemple suivant propose une barre d'outils positionnée à gauche par défaut, mais qui pourra être ancrée que latéralement (à gauche ou à droite).

 1 
 2 
 3 
toolbar = QToolBar("First tool bar")
self.addToolBar(Qt.LeftToolBarArea, toolbar)
toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea)
Limiter les emplacements de la barre d'outils.

Interconnexion avec la barre de statuts

Nous l'avons déjà vu : si votre barre d'outils utilise des actions, alors en cas de survol de la souris sur les éléments de la barre, le statusTip associé sera affiché dans la barre de statuts. A titre de rappel, voici comment spécifier le statusTip d'une action Qt.

 1 
 2 
 3 
actNew = QAction(QIcon("icons/new.png"), "&New", self)
actNew.setStatusTip("New file")
toolbar.addAction(actNew)
Définition du statusTip d'une action Qt.

Et voici le résultat produit en cas de survol de la souris sur l'action associée.

Affichage du statusTip dans la barre de statuts.

Si vous placez des widgets dans vos barres d'outils, il est quand même possible d'avoir le même comportement. Pour ce faire, il faut invoquer la méthode setStatusTip sur votre widget.

 1 
 2 
 3 
button = QPushButton("A button")
button.setStatusTip("It also works")
toolbar.addWidget(button)
Association d'un statusTip pour un widget.

Ajouter des séparateurs dans une barre d'outils

A l'instar d'une barre de menu, il est possible d'ajouter des séparateurs dans une barre d'outils. Pour ce faire, vous devez invoquer la méthode addSeparator sur votre barre d'outils.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
# Ajout d'une action dans la barre d'outils.
actNew = QAction(QIcon("icons/new.png"), "&New", self)
actNew.setStatusTip("New file")
toolbar.addAction(actNew)

# Ajout d'un séparateur
toolbar.addSeparator()

# Ajout d'un widget quelconque dans la barre d'outils.
button = QPushButton("A button")
button.setStatusTip("It also works")
toolbar.addWidget(button)
Ajout d'un séparateur dans la barre d'outils

Connecter les slots à votre barre d'outils

Pour connecter un slot aux éléments graphiques de votre barre d'outils, on procède de manière classique et habituelle. Deux situations sont à considérer : soit vous avez une action placée dans la barre d'outils, soit vous avez un widget.

Cas de l'utilisation d'une action QT (QAction)

Pour rappel, vous devez connecter le signal triggered de votre action sur votre slot pour réagir à son l'activation (par le clavier ou la souris).

 1 
 2 
 3 
 4 
actNew = QAction(QIcon("icons/new.png"), "&New", self)
actNew.setStatusTip("New file")
actNew.triggered.connect(self.newFile)
toolbar.addAction(actNew)
Connexion d'un slot sur le signal triggered d'une de vos actions

Cas de l'utilisation d'un widget

Pour ce qui est de vos widgets, il faudra en premier lieu déterminer sur quel signal vous souhaitez réagir (widget par widget). Ensuite, utilisez la méthode connect du signal pour le lier à votre slot. Voici un exemple réagissant au clic sur un bouton.

 1 
 2 
 3 
 4 
button = QPushButton("A button")
button.setStatusTip("It also works")
button.clicked.connect(self.buttonSlot)
toolbar.addWidget(button)
Connexion d'un slot sur le signal clicked d'un bouton

En synthèse, voici un exemple de code montrant un bon nombre de possibilités présentées depuis le début de ce document.

 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 
import sys

from PySide6.QtCore import Slot
from PySide6.QtGui import QIcon, QAction, Qt
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QCheckBox


class MyWindow(QMainWindow):

    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle('ToolBars with Python/Qt')
        self.resize(400, 150)

        # Création de la barre d'outils avec son nom
        toolbar = self.addToolBar("First tool bar")
        toolbar.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
        toolbar.setFloatable(False)
        # toolbar.setMovable(False)

        # Ajout d'une action dans la barre d'outils.
        actNew = QAction(QIcon("icons/new.png"), "&New", self)
        actNew.setStatusTip("New file")
        actNew.triggered.connect(self.newFile)
        toolbar.addAction(actNew)
        
        # Ajout d'un séparateur
        toolbar.addSeparator()
        
        # Ajout d'un widget quelconque dans la barre d'outils.
        button = QPushButton("A button")
        button.setStatusTip("It also works")
        button.clicked.connect(self.buttonSlot)
        toolbar.addWidget(button)

        # On demande l'instanciation de la barre de status
        self.statusBar().showMessage("ToolBar sample")

    @Slot()
    def newFile(self):
        print("New file requested")

    @Slot()
    def buttonSlot(self):
        print("Button clicked")


if __name__ == "__main__":
    app = QApplication(sys.argv)

    myWindow = MyWindow()
    myWindow.show()

    sys.exit(app.exec())
On mixe le tout

Injecter plusieurs barres d'outils dans votre application

Vous pouvez ajouter autant de barres d'outils que nécessaire dans votre application. Pour ce faire, il vous suffit de répéter autant d'appels que souhaité à la méthode addToolBar. Ensuite veuillez remplir chaque barre d'outils avec les éléments requis.

Travaux pratiques

L'énnoncé de l'exercice

Voici une capture d'écran montrant une fenêtre avec une barre de menu, trois barres d'outils et une barre de status. En vous basant sur les explications de ce chapitre, mais aussi celles des chapitres précédents (en notamment ceux sur les actions et les barre de menu), veuillez développer un programme affichant, à peu près, les mêmes éléments.

bien entendu, vous avez toutes liberté sur le choix des icônes. Mais si vous ne voulez pas trop perdre de temps, sachez que vous retrouverez un ZIP avec les icônes utilisées dans la capture d'écran en activant ce lien.
La fenêtre à reproduire durant cet exercice

Comme toujours, essayez de réaliser l'exercice sans directement copier/coller le code ci-dessous. ;-)

La correction de l'exercice

Voici ma proposition de correction.

 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 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
 78 
 79 
 80 
 81 
 82 
 83 
 84 
 85 
 86 
 87 
 88 
 89 
 90 
 91 
 92 
 93 
 94 
 95 
 96 
 97 
 98 
 99 
 100 
 101 
 102 
 103 
 104 
 105 
 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 
 115 
 116 
 117 
 118 
 119 
 120 
 121 
 122 
 123 
 124 
 125 
import sys

from PySide6.QtCore import Slot
from PySide6.QtGui import QIcon, QAction, Qt
from PySide6.QtWidgets import QApplication, QMainWindow, QToolBar, QPushButton, QCheckBox, QComboBox


class MyWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Exemple de définition de barres d'outils Qt")
        self.setWindowIcon(QIcon("icons/file.png"))
        self.resize(600, 150)

        self.createActions()
        self.createMenuBar()
        self.createToolBars()

        statusBar = self.statusBar()
        statusBar.showMessage(self.windowTitle())   # Définition du message initial

    # Les actions seront communes à la barre de menu et aux barres d'outils
    # C'est pour cette raison qu'elles sont stockées sous forme d'attributs :
    # on pourra les retrouver plus rapidement.
    def createActions(self):
        self.actNew = QAction(QIcon("icons/new.png"), "&New", self)
        self.actNew.setShortcut("Ctrl+N")
        self.actNew.setStatusTip("New document")
        self.actNew.triggered.connect(self.newDocument)

        self.actOpen = QAction(QIcon("icons/open.png"), "&Open...", self)
        self.actOpen.setShortcut("Ctrl+O")
        self.actOpen.setStatusTip("Open file")

        self.actSave = QAction(QIcon("icons/save.png"), "&Save", self)
        self.actSave.setShortcut("Ctrl+S")
        self.actSave.setStatusTip("Save File")

        self.actExit = QAction(QIcon("icons/exit.png"), "Exit", self)
        self.actExit.setShortcut("Alt+F4")
        self.actExit.setStatusTip("Exit")
        # La méthode close est directement fournie par la classe QMainWindow.
        self.actExit.triggered.connect(self.close)

        self.actUndo = QAction(QIcon("icons/undo.png"), "&Undo", self)
        self.actUndo.setShortcut("Ctrl+Z")
        self.actUndo.setStatusTip("Undo")

        self.actRedo = QAction(QIcon("icons/redo.png"), "&Redo", self)
        self.actRedo.setShortcut("Ctrl+Y")
        self.actRedo.setStatusTip("Redo")

        self.actCopy = QAction(QIcon("icons/copy.png"), "&Copy", self)
        self.actCopy.setShortcut("Ctrl+C")
        self.actCopy.setStatusTip("Copy selection")

        self.actCut = QAction(QIcon("icons/cut.png"), "Cu&t", self)
        self.actCut.setShortcut("Ctrl+X")
        self.actCut.setStatusTip("Cut selection")

        self.actPaste = QAction(QIcon("icons/paste.png"), "&Paste", self)
        self.actPaste.setShortcut("Ctrl+V")
        self.actPaste.setStatusTip("Paste")

        self.actAbout = QAction(QIcon("icons/about.png"), "&About...", self)
        self.actAbout.setStatusTip("About this application")

    def createMenuBar(self):
        menuBar = self.menuBar()

        file = menuBar.addMenu("&File")
        file.addAction(self.actNew)
        file.addSeparator()
        file.addAction(self.actOpen)
        file.addAction(self.actSave)
        file.addSeparator()
        file.addAction(self.actExit)

        edit = menuBar.addMenu("&Edit")
        edit.addAction(self.actUndo)
        edit.addAction(self.actRedo)
        edit.addSeparator()
        edit.addAction(self.actCopy)
        edit.addAction(self.actCut)
        edit.addAction(self.actPaste)

        help = menuBar.addMenu("&Help")
        help.addAction(self.actAbout)

    def createToolBars(self):
        file = self.addToolBar("File tool bar")
        file.addAction(self.actNew)
        file.addSeparator()
        file.addAction(self.actOpen)
        file.addAction(self.actSave)
        file.addSeparator()
        file.addAction(self.actExit)

        edit = self.addToolBar("Edit tool bar")
        edit.addAction(self.actUndo)
        edit.addAction(self.actRedo)
        edit.addSeparator()
        edit.addAction(self.actCopy)
        edit.addAction(self.actCut)
        edit.addAction(self.actPaste)

        bottomToolbar = QToolBar("Bottom tool bar")
        self.addToolBar(Qt.BottomToolBarArea, bottomToolbar)
        bottomToolbar.addWidget(QPushButton("A button"))
        bottomToolbar.addWidget(QCheckBox("A check box"))
        comboBox = QComboBox()
        comboBox.addItems(["A combo box with some choices", "Other choice"])
        bottomToolbar.addWidget(comboBox)

    @Slot()
    def newDocument(self):
        print("New document is requested")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    sys.exit(app.exec())
La correction de l'exercice


Définition d'un menu réutilisable Enrichissement de la barre de statuts