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 :

Implémentation d'un menu réutilisable

Mise en oeuvre d'une barre de menu Mise en oeuvre d'une barre d'outils


Accès rapide :
La vidéo
Il faut penser « réutilisabilité »
Gestion dynamique des thèmes
Le code de notre menu de sélection de thèmes

La vidéo

Cette vidéo vous montre comment coder une nouvelle classe de menu déroulant réutilisable. A titre d'exemple, vous allez réaliser un menu déroulant permettant de choisir le thème graphique à appliquer à l'application en cours d'exécution.


Implémentation d'un menu réutilisable.

Il faut penser « réutilisabilité »

Une pratique qui me semble très saine consiste à toujours se demander si une partie du code à produire pourrait être réutilisable. Si tel est le cas, il faut alors arriver à extraire ce code dans un fichier plus facilement réutilisable.

Imaginons que pour votre application vous souhaitiez offrir un menu déroulant de changement de thème graphique. Après réflexion, une telle chose pourrait être réemployer sur d'autres développements. Du coup, autant coder une classe de menu réutilisable. C'est ce que nous allons tenter de faire dans ce chapitre.

Gestion dynamique des thèmes

Pour fonctionner, notre menu va scruter le contenu d'un dossier QSS : si des fichiers d'extension .qss y sont présents, ils seront alors considérés par notre menu déroulant.

si vous cherchez des fichiers QSS, n'oubliez pas que je vous en ai présenté quelques uns dans un précédent chapitre.

Pour localiser les fichiers QSS je vais utiliser la méthode os.listdir ainsi qu'une liste en compréhension.

 1 
 2 
 3 
 4 
# On liste les fichiers d'extension .qss présents dans le dossier qss. 
styleSheets = [file for file in os.listdir("./qss") if file.endswith(".qss")]
# On trie la liste sans tenir compte de la casse.
styleSheets.sort(key=lambda file: file.lower())
Analyse du contenu du fichier QSS.

Chaque fichier sélectionné par ce code devra produire une action dans notre menu déroulant. Pour ce faire, nous allons créer la méthode createThemeAction dans notre classe de menu déroulant.

Le code de notre menu de sélection de thèmes

 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 
import os
import platform

from PySide6.QtCore import QFile, QTextStream, Slot
from PySide6.QtGui import QAction
from PySide6.QtWidgets import QMenu, QStyleFactory, QApplication


class ThemeMenu(QMenu):

    def __init__(self, text="&Themes", parent=None):
        super().__init__(text, parent)

        # On liste les fichiers d'extension .qss présents dans le dossier qss.
        styleSheets = [file for file in os.listdir("./qss") if file.endswith(".qss")]
        # On trie la liste sans tenir compte de la casse.
        styleSheets.sort(key=lambda file: file.lower())
        # Pour chaque fichier trouvé, on crée l'action associée.
        for styleSheet in styleSheets:
            self.addAction(self.createThemeAction(styleSheet.replace(".qss", "")))

        if len(styleSheets) > 0:
            self.addSeparator()

        # On récupère le theme de démarrage
        app = QApplication.instance()
        currentTheme = app.style().name()

        actFusion = QAction("&Fusion theme", self)
        actFusion.setStatusTip("Fusion theme")
        actFusion.setData("Fusion")
        actFusion.triggered.connect(self.changeTheme)
        actFusion.setCheckable(True)
        actFusion.setChecked(currentTheme == "fusion")
        self.addAction(actFusion)

        actWindows = QAction("&Windows theme", self)
        actWindows.setStatusTip("Windows theme")
        actWindows.setData("Windows")
        actWindows.triggered.connect(self.changeTheme)
        actWindows.setCheckable(True)
        actWindows.setChecked(currentTheme == "windows")
        self.addAction(actWindows)

        if platform.system() == "Windows":
            actWindowsVista = QAction("Windows &Vista theme", self)
            actWindowsVista.setStatusTip("Windows theme")
            actWindowsVista.setData("WindowsVista")
            actWindowsVista.triggered.connect(self.changeTheme)
            actWindowsVista.setCheckable(True)
            actWindowsVista.setChecked(currentTheme == "windowsvista")
            self.addAction(actWindowsVista)

    def createThemeAction(self, themeName):
        action = QAction(f"{themeName} theme", self)
        action.setStatusTip(f"{themeName} theme")
        action.setData(f"qss/{themeName}.qss")
        action.triggered.connect(self.changeStyleSheet)
        action.setCheckable(True)
        return action

    def checkSelectedAction(self):
        for action in self.actions():
            if action != self.sender():
                action.setChecked(False)

    @Slot()
    def changeStyleSheet(self):
        self.checkSelectedAction()
        qssFileName = self.sender().data()
        app = QApplication.instance()
        file = QFile(qssFileName)
        file.open(QFile.ReadOnly | QFile.Text)
        stream = QTextStream(file)
        app.setStyle(None)
        app.setStyleSheet(stream.readAll())
        file.close()
        print(f"{qssFileName} applied")

    @Slot()
    def changeTheme(self):
        self.checkSelectedAction()
        themeName = self.sender().data()
        app = QApplication.instance()
        app.setStyleSheet(None)
        app.setStyle(QStyleFactory.create(themeName))
        print(f"{themeName} style applied")
Un menu déroulant de sélection d'un thème visuel

Il ne reste plus qu'à inclure notre menu déroulant dans une fenêtre Qt.

 1 
 2 
 3 
 4 
 5 
menuBar = self.menuBar()
            
# Injection des autres menus déroulants de la barre

menuBar.addMenu(ThemeMenu())
Ajout du menu déroulant dans la barre de menu.

Et voici le résultat produit.

Utilisation de notre menu de sélection de thèmes.


Mise en oeuvre d'une barre de menu Mise en oeuvre d'une barre d'outils