Créez des applications GUI d'aspect professionnel – Real Python

By | novembre 25, 2020

trouver un expert Python

Les gestionnaires de mise en page de PyQt offrent un moyen convivial et productif d’organiser les composants graphiques, ou widgets, sur une interface graphique. La disposition correcte des widgets donnera à vos applications GUI un aspect soigné et professionnel. Apprendre à le faire de manière efficace et efficiente est une compétence fondamentale pour vous familiariser avec le développement d'applications GUI à l'aide de Python et PyQt.

Dans ce didacticiel, vous apprendrez:

  • Quels sont les avantages de l’utilisation de PyQt gestionnaires de mise en page
  • Comment procéder par programmation mettre en page des widgets sur une interface graphique utilisant les gestionnaires de disposition de PyQt
  • Comment sélectionnez le bon gestionnaire de mise en page pour votre application GUI
  • Comment disposer les widgets dans basé sur la fenêtre principale et basé sur le dialogue applications

Grâce à ces connaissances et à ces compétences, vous pourrez utiliser Python et PyQt pour créer des applications GUI d’aspect professionnel.

Pour une meilleure compréhension de l'utilisation des gestionnaires de disposition, des connaissances préalables sur la création d'applications d'interface graphique PyQt et l'utilisation des widgets PyQt seraient utiles.

Disposition des éléments graphiques sur une interface graphique

Lorsque vous créez des applications d'interface utilisateur graphique (GUI), un problème courant est de savoir comment disposer vos composants graphiques (boutons, menus, barres d'outils, étiquettes, etc.) de manière cohérente sur vos formulaires et fenêtres. Ce processus est connu sous le nom de Disposition GUI, et c'est une étape importante dans la création d'applications GUI.

Dans le passé, si vous vouliez disposer des composants graphiques, ou des widgets, sur une fenêtre, vous suiviez l'une des approches suivantes:

  1. Décidez et définissez manuellement une taille et une position statiques pour chaque widget dans la fenêtre.
  2. Calculez et définissez dynamiquement la taille et la position de chaque widget.

La première approche est assez directe, mais elle présente au moins les inconvénients suivants:

  • Vos fenêtres seront non redimensionnable, ce qui pourrait poser des problèmes lors de leur affichage sur différentes résolutions d'écran.
  • Vos libellés peuvent ne pas prendre en charge localisation correctement parce que la longueur d'un texte donné change d'une langue à l'autre.
  • Vos widgets s'afficheront différemment sur différentes plates-formes, ce qui rend l'écriture difficile multi plateforme des applications qui ont l'air bien.

La deuxième approche est plus flexible. Cependant, il présente également des inconvénients:

  • Vous devez faire beaucoup de calculs manuels pour déterminer le bon Taille et position de chaque widget.
  • Vous devez faire quelques calculs supplémentaires pour répondre correctement à redimensionnement de la fenêtre.
  • Vous devez refaire tous les calculs à tout moment modifier la mise en page de votre fenêtre.

Même si vous pouvez toujours utiliser l’une de ces deux approches pour mettre en page vos interfaces graphiques, la plupart du temps, vous souhaiterez utiliser une troisième approche plus pratique mise en œuvre par la plupart des cadres d’interface graphique ou des boîtes à outils modernes: les gestionnaires de disposition.

Les gestionnaires de mise en page organisent automatiquement les widgets sur une interface graphique en fonction de vos besoins spécifiques. Ils évitent les inconvénients de compatibilité de la première approche ainsi que les calculs ennuyeux et compliqués de la seconde approche.

Dans les sections suivantes, vous découvrirez les gestionnaires de disposition intégrés de PyQt et comment les utiliser pour mettre en page efficacement les composants graphiques de vos applications GUI.

Utilisation des gestionnaires de mise en page à usage général

Lors de la création d'applications GUI avec PyQt, vous utiliserez souvent une ou plusieurs des quatre mises en page à usage général que vous avez vues à la fin de la section précédente pour que votre widget soit disposé sur vos fenêtres et formulaires.

Dans les prochaines sections, vous apprendrez à créer et à utiliser les quatre gestionnaires de mise en page à usage général à l'aide de quelques exemples.

Création de dispositions horizontales: QHBoxLayout

Gestionnaires de disposition de boîte prendre l'espace qu'ils obtiennent de leur mise en page ou widget parent, le diviser en plusieurs cases, ou celluleset faites en sorte que chaque widget de la mise en page remplisse une case.

QHBoxLayout est l'une des deux mises en page de boîte disponibles dans PyQt. Ce gestionnaire de mise en page vous permet d'organiser des widgets horizontalement, l'un à côté de l'autre. Les widgets sont ajoutés à la mise en page de gauche à droite. Cela signifie que le widget que vous ajoutez en premier dans votre code sera le widget le plus à gauche de la mise en page.

Pour ajouter des widgets à un QHBoxLayout objet, vous appelez .addWidget (widget, étirement, alignement) sur l'objet de mise en page. Cette méthode prend un argument obligatoire et deux arguments facultatifs:

  1. widget est un argument obligatoire qui contient le widget spécifique que vous souhaitez ajouter à la mise en page.

  2. étendue est un argument facultatif contenant un nombre entier représentant le facteur d'étirement à appliquer widget. Les widgets avec des facteurs d'étirement plus élevés se développent davantage lors du redimensionnement de la fenêtre. Il est par défaut 0, ce qui signifie que le widget n'a pas de facteur d'étirement attribué.

  3. alignement est un argument facultatif qui contient des indicateurs horizontaux et verticaux. Vous pouvez combiner ces indicateurs pour produire l'alignement souhaité du widget à l'intérieur de sa cellule contenant. Il est par défaut 0, ce qui signifie que le widget remplira toute la cellule.

Voici une petite application qui montre comment créer une mise en page horizontale à l'aide de QHBoxLayout. Dans cet exemple, vous utiliserez QPushButton des objets pour mieux visualiser où chaque widget sera placé dans la mise en page selon l'ordre dans lequel vous ajoutez les widgets à votre code:

    1importer sys
    2
    3de PyQt5.QtWidgets importer (
    4    QApplication,
    5    QHBoxLayout,
    6    QPushButton,
    sept    QWidget,
    8)
    9
dixclasse La fenêtre(QWidget):
11    def __init__(soi):
12        super().__init__()
13        soi.setWindowTitle("Exemple de QHBoxLayout")
14        # Créer une instance QHBoxLayout
15        disposition = QHBoxLayout()
16        # Ajouter des widgets à la mise en page
17        disposition.addWidget(QPushButton("Le plus à gauche"))
18        disposition.addWidget(QPushButton("Centre"), 1)
19        disposition.addWidget(QPushButton("Le plus à droite"), 2)
20        # Définir la mise en page sur la fenêtre de l'application
21        soi.setLayout(disposition)
22        impression(soi.les enfants())
23
24si __Nom__ == "__principale__":
25    app = QApplication(sys.argv)
26    la fenêtre = La fenêtre()
27    la fenêtre.montrer()
28    sys.sortie(app.exec_())

À la ligne 15, vous créez un QHBoxLayout objet appelé disposition. Aux lignes 17 à 19, vous ajoutez trois boutons à disposition en utilisant .addWidget (). Notez que vous passez 1 et 2 à la étendue paramètre dans le Centre et Tout à droite boutons, respectivement. À la ligne 21, vous définissez disposition comme disposition de premier niveau de votre fenêtre en utilisant .setLayout ().

Si vous exécutez cette application, la fenêtre suivante s’affiche à l’écran:

Exemple de QHBoxLayout

Cette fenêtre contient trois boutons disposés horizontalement. Notez que le Le plus à gauche bouton correspond au premier bouton que vous ajoutez dans votre code. Ainsi, les boutons sont affichés dans le même ordre (de gauche à droite) que vous les ajoutez dans votre code (de haut en bas).

le Centre et Tout à droite les boutons ont des facteurs d'étirement différents, de sorte qu'ils s'étendent proportionnellement à ces facteurs lorsque vous redimensionnez la fenêtre.

De plus, tous les boutons de disposition et la mise en page elle-même est définie comme enfants de La fenêtre. Cette opération est effectuée automatiquement par l'objet layout, qui appelle en interne .setParent () sur chaque widget. L'appel à impression() à la ligne 17 imprime une liste des enfants de La fenêtre sur votre terminal comme preuve de ce comportement.

Création de dispositions verticales: QVBoxLayout

QVBoxLayout organise les widgets verticalement, l'un en dessous de l'autre. Vous pouvez utiliser cette classe pour créer des dispositions verticales et organiser vos widgets de haut en bas. Depuis QVBoxLayout est une autre disposition de boîte, son .addWidget () la méthode fonctionne de la même manière que dans QHBoxLayout.

Voici une application PyQt qui montre comment créer et utiliser un QVBoxLayout objet pour créer des arrangements verticaux de widgets dans vos interfaces graphiques:

    1importer sys
    2
    3de PyQt5.QtWidgets importer (
    4    QApplication,
    5    QPushButton,
    6    QVBoxLayout,
    sept    QWidget,
    8)
    9
dixclasse La fenêtre(QWidget):
11    def __init__(soi):
12        super().__init__()
13        soi.setWindowTitle("Exemple de QVBoxLayout")
14        soi.redimensionner(270, 110)
15        # Créer une instance QVBoxLayout
16        disposition = QVBoxLayout()
17        # Ajouter des widgets à la mise en page
18        disposition.addWidget(QPushButton("Haut"))
19        disposition.addWidget(QPushButton("Centre"))
20        disposition.addWidget(QPushButton("Bas"))
21        # Définir la mise en page sur la fenêtre de l'application
22        soi.setLayout(disposition)
23
24si __Nom__ == "__principale__":
25    app = QApplication(sys.argv)
26    la fenêtre = La fenêtre()
27    la fenêtre.montrer()
28    sys.sortie(app.exec_())

À la ligne 16, vous créez une instance de QVBoxLayout. Aux lignes 18 à 20, vous ajoutez trois boutons à disposition. Enfin, vous définissez disposition comme disposition de premier niveau de votre fenêtre.

Si vous exécutez cette application, vous obtiendrez la fenêtre suivante:

Exemple de QVBoxLayout

Votre fenêtre affiche trois boutons dans une disposition verticale, l'un en dessous de l'autre. Les boutons apparaissent dans le même ordre (de haut en bas) que vous les ajoutez dans votre code (de haut en bas).

Organisation des widgets dans une grille: QGridLayout

Vous pouvez utiliser QGridLayout pour organiser les widgets dans un la grille de lignes et de colonnes. Chaque widget aura une position relative dans la grille. Pour définir la position d'un widget ou d'une cellule dans la grille, vous utilisez une paire de coordonnées du formulaire (ligne, colonne). Ces coordonnées doivent être des nombres entiers de base zéro.

QGridLayout prend l'espace disponible sur son parent, le divise en lignes et colonnes, et place chaque widget dans sa propre cellule ou boîte. QGridLayout détermine automatiquement le nombre de lignes et de colonnes de la mise en page finale en fonction du nombre de widgets et de leurs coordonnées. Si vous n'ajoutez pas de widget à une cellule donnée, alors QGridLayout laissera cette cellule vide.

Pour ajouter des widgets à une disposition de grille, vous appelez .addWidget () sur la mise en page. Cette méthode a deux implémentations surchargées différentes:

  1. addWidget (widget, ligne, colonne, alignement) ajoute widget à la cellule à (rangée, colonne).
  2. addWidget (widget, fromRow, fromColumn, rowSpan, columnSpan, alignement) ajoute widget à la cellule, couvrant plusieurs lignes, colonnes ou les deux.

La première implémentation prend les arguments suivants:

  1. widget est un argument obligatoire qui contient le widget spécifique que vous devez ajouter à la mise en page.
  2. rangée est un argument obligatoire contenant un entier représentant la coordonnée d'une ligne dans la grille.
  3. colonne est un argument obligatoire contenant un entier représentant la coordonnée d'une colonne dans la grille.
  4. alignement est un argument facultatif qui contient l'alignement du widget à l'intérieur de sa cellule contenant. Il est par défaut 0, ce qui signifie que le widget remplira toute la cellule.

Voici un exemple d'utilisation QGridLayout pour créer une grille de widgets:

    1importer sys
    2
    3de PyQt5.QtWidgets importer (
    4    QApplication,
    5    QGridLayout,
    6    QPushButton,
    sept    QWidget,
    8)
    9
dixclasse La fenêtre(QWidget):
11    def __init__(soi):
12        super().__init__()
13        soi.setWindowTitle(«Exemple QGridLayout»)
14        # Créer une instance QGridLayout
15        disposition = QGridLayout()
16        # Ajouter des widgets à la mise en page
17        disposition.addWidget(QPushButton("Bouton à (0, 0)"), 0, 0)
18        disposition.addWidget(QPushButton("Bouton à (0, 1)"), 0, 1)
19        disposition.addWidget(QPushButton("Bouton à (0, 2)"), 0, 2)
20        disposition.addWidget(QPushButton("Bouton à (1, 0)"), 1, 0)
21        disposition.addWidget(QPushButton("Bouton sur (1, 1)"), 1, 1)
22        disposition.addWidget(QPushButton("Bouton en (1, 2)"), 1, 2)
23        disposition.addWidget(QPushButton("Bouton à (2, 0)"), 2, 0)
24        disposition.addWidget(QPushButton("Bouton en (2, 1)"), 2, 1)
25        disposition.addWidget(QPushButton("Bouton en (2, 2)"), 2, 2)
26        # Définir la mise en page sur la fenêtre de l'application
27        soi.setLayout(disposition)
28
29si __Nom__ == "__principale__":
30    app = QApplication(sys.argv)
31    la fenêtre = La fenêtre()
32    la fenêtre.montrer()
33    sys.sortie(app.exec_())

À la ligne 15, vous créez le QGridLayout objet. Ensuite, aux lignes 17 à 25, vous ajoutez des widgets à la mise en page en utilisant .addWidget (). Pour voir comment les mises en page de grille gèrent les cellules sans widget attribué, commentez une ou plusieurs de ces lignes et exécutez à nouveau l'application.

Si vous exécutez ce code à partir de votre ligne de commande, vous obtiendrez une fenêtre comme celle-ci:

Exemple de QGridLayout

Chaque widget dans le QGridLayout objet occupe la cellule définie par la paire de coordonnées que vous fournissez dans .addWidget (). Le texte de chaque bouton reflète ces coordonnées. Les coordonnées sont basées sur zéro, donc la première cellule est à (0, 0).

Dans la deuxième implémentation de .addWidget (), les arguments widget et alignement restez le même, et vous avez quatre arguments supplémentaires qui vous permettent de placer le widget sur plusieurs lignes ou colonnes:

  1. fromRow prend un nombre entier qui représente la ligne dans laquelle le widget commencera.
  2. fromColumn prend un nombre entier qui représente la colonne dans laquelle le widget démarrera.
  3. rowSpan prend un nombre entier qui représente le nombre de lignes que le widget occupera dans la grille.
  4. columnSpan prend un nombre entier qui représente le nombre de colonnes que le widget occupera dans la grille.

Voici une application qui montre comment cette variante de .addWidget () travaux:

    1importer sys
    2
    3de PyQt5.QtWidgets importer (
    4    QApplication,
    5    QGridLayout,
    6    QPushButton,
    sept    QWidget,
    8)
    9
dixclasse La fenêtre(QWidget):
11    def __init__(soi):
12        super().__init__()
13        soi.setWindowTitle(«Exemple QGridLayout»)
14        # Créer une instance QGridLayout
15        disposition = QGridLayout()
16        # Ajouter des widgets à la mise en page
17        disposition.addWidget(QPushButton("Bouton à (0, 0)"), 0, 0)
18        disposition.addWidget(QPushButton("Bouton sur (0, 1)"), 0, 1)
19        disposition.addWidget(QPushButton("Le bouton s'étend sur deux colonnes"), 1, 0, 1, 2)
20        # Définir la mise en page sur la fenêtre de l'application
21        soi.setLayout(disposition)
22
23si __Nom__ == "__principale__":
24    app = QApplication(sys.argv)
25    la fenêtre = La fenêtre()
26    la fenêtre.montrer()
27    sys.sortie(app.exec_())

À la ligne 19, vous utilisez la deuxième implémentation de .addWidget () pour ajouter un bouton qui occupe deux colonnes dans la grille. Le bouton commence à la deuxième ligne (fromRow = 1) et à la première colonne (fromColumn = 0). Enfin, le bouton occupe une ligne (rowSpan = 1) et deux colonnes (columnSpan = 2).

Voici la fenêtre que vous verrez sur votre écran si vous exécutez cette application:

Exemple de plage de QGridLayout

Dans ce type de mise en page, vous pouvez faire en sorte qu'un widget occupe plus d'une cellule, comme vous l'avez fait avec le Le bouton s'étend sur deux cols bouton.

Créer rapidement des formulaires: QFormLayout

Si vous créez constamment des formulaires pour effectuer des actions telles que la saisie de données dans une base de données, alors QFormLayout est pour toi. Cette classe organise les widgets dans un deux colonnes disposition. La première colonne affiche généralement un étiquette décrivant l'entrée prévue, et la deuxième colonne contient généralement widgets d'entrée tel que QLineEdit, QComboBox, ou QSpinBox qui permettent à l'utilisateur de saisir ou de modifier des données.

Pour ajouter des widgets à une mise en page de formulaire, vous utilisez .ajouter une rangée(). Cette méthode comporte plusieurs variantes mais, la plupart du temps, vous aurez le choix entre les deux suivantes:

  1. .addRow (étiquette, champ) ajoute une nouvelle ligne au bas d'une mise en page de formulaire. La ligne doit contenir un QLabel objet (étiquette) et un widget d'entrée (champ).

  2. .addRow (labelText, champ) crée et ajoute automatiquement un nouveau QLabel objet avec labelText comme son texte. champ contient un widget d'entrée.

Voici un exemple d'application utilisant un QFormLayout objet pour organiser les widgets:

    1importer sys
    2
    3de PyQt5.QtWidgets importer (
    4    QApplication,
    5    QFormLayout,
    6    QLabel,
    sept    QLineEdit,
    8    QWidget,
    9)
dix
11classe La fenêtre(QWidget):
12    def __init__(soi):
13        super().__init__()
14        soi.setWindowTitle("Exemple QFormLayout")
15        soi.redimensionner(270, 110)
16        # Créer une instance QHBoxLayout
17        disposition = QFormLayout()
18        # Ajouter des widgets à la mise en page
19        disposition.ajouter une rangée("Nom:", QLineEdit())
20        disposition.ajouter une rangée("Emploi:", QLineEdit())
21        emailLabel = QLabel("Email:")
22        disposition.ajouter une rangée(emailLabel, QLineEdit())
23        # Définir la mise en page sur la fenêtre de l'application
24        soi.setLayout(disposition)
25
26si __Nom__ == "__principale__":
27    app = QApplication(sys.argv)
28    la fenêtre = La fenêtre()
29    la fenêtre.montrer()
30    sys.sortie(app.exec_())

À la ligne 17, vous créez un QFormLayout objet. Ensuite, aux lignes 19 à 22, vous ajoutez quelques lignes à la mise en page. Notez qu'aux lignes 19 et 20, vous utilisez la deuxième variante de la méthode, et à la ligne 22, vous utilisez la première variante, en passant un QLabel objet comme premier argument de .ajouter une rangée().

Si vous exécutez ce code, vous obtiendrez la fenêtre suivante sur votre écran:

Exemple de QFormLayout

Avec un QFormLayout, vous pouvez organiser vos widgets en deux colonnes. La première colonne contient des étiquettes qui demandent à l'utilisateur des informations. La deuxième colonne affiche des widgets qui permettent à l'utilisateur de saisir ou de modifier ces informations.

Imbrication de dispositions pour créer des interfaces graphiques complexes

Vous pouvez utiliser imbriqué mises en page pour créer des interfaces graphiques complexes qu’il serait difficile de créer à l’aide de l’un des gestionnaires de mise en page à usage général de PyQt. Pour ce faire, vous devez appeler .addLayout () sur un disposition extérieure. De cette façon, le disposition intérieure devient un enfant de la disposition extérieure.

Supposons que vous deviez créer une boîte de dialogue qui affiche une étiquette et une modification de ligne dans une mise en page de formulaire, et sous ces widgets, vous souhaitez placer plusieurs cases à cocher dans une mise en page verticale. Voici une maquette de ce à quoi votre boîte de dialogue devrait ressembler:

Diagramme de dispositions imbriquées

Le rectangle bleu représente votre disposition extérieure. Le rectangle vert est la mise en page du formulaire qui contiendra l'étiquette et l'édition de ligne. Le rectangle rouge est la disposition verticale pour contenir les cases à cocher des options. La disposition verte et la disposition rouge sont imbriquées dans la disposition bleue, qui est une disposition verticale.

Voici un exemple de création de cette mise en page à l'aide de PyQt:

    1importer sys
    2
    3de PyQt5.QtWidgets importer (
    4    QApplication,
    5    QCheckBox,
    6    QFormLayout,
    sept    QLineEdit,
    8    QVBoxLayout,
    9    QWidget,
dix)
11
12classe La fenêtre(QWidget):
13    def __init__(soi):
14        super().__init__()
15        soi.setWindowTitle("Exemple de dispositions imbriquées")
16        # Créer une mise en page externe
17        externalLayout = QVBoxLayout()
18        # Créer une mise en page de formulaire pour le libellé et l'édition de ligne
19        topLayout = QFormLayout()
20        # Ajouter une étiquette et une modification de ligne à la mise en page du formulaire
21        topLayout.ajouter une rangée("Du texte:", QLineEdit())
22        # Créer une mise en page pour les cases à cocher
23        optionsMise en page = QVBoxLayout()
24        # Ajoutez des cases à cocher à la mise en page
25        optionsMise en page.addWidget(QCheckBox("Option un"))
26        optionsMise en page.addWidget(QCheckBox("Option deux"))
27        optionsMise en page.addWidget(QCheckBox("Option trois"))
28        # Imbriquez les dispositions intérieures dans la disposition extérieure
29        externalLayout.addLayout(topLayout)
30        externalLayout.addLayout(optionsMise en page)
31        # Définir la disposition principale de la fenêtre
32        soi.setLayout(externalLayout)
33
34si __Nom__ == "__principale__":
35    app = QApplication(sys.argv)
36    la fenêtre = La fenêtre()
37    la fenêtre.montrer()
38    sys.sortie(app.exec_())

Voici ce que vous faites dans ce code:

  • En ligne 17, vous créez la mise en page externe, ou de niveau supérieur, que vous utiliserez comme mise en page parente et comme mise en page principale de votre fenêtre. Dans ce cas, vous utilisez QVBoxLayout car vous souhaitez que vos widgets soient disposés verticalement sur votre formulaire. Dans votre maquette, il s'agit de la mise en page bleue.
  • En ligne 19, vous créez une mise en page de formulaire pour contenir une étiquette et une modification de ligne.
  • En ligne 21, vous ajoutez les widgets requis à la mise en page. Cela équivaut à votre mise en page verte.
  • En ligne 23, vous créez une disposition verticale pour contenir les cases à cocher.
  • Aux lignes 25 à 27, vous ajoutez les cases à cocher requises. Ceci est votre mise en page rouge.
  • Aux lignes 29 et 30, vous nidifiez topLayout et optionsMise en page sous le externalLayout.

C'est ça! Si vous exécutez l'application, une fenêtre semblable à celle-ci s'affiche:

Exemple de dispositions imbriquées

Dans cette application, vous imbriquez deux mises en page différentes sous une mise en page externe pour créer une mise en page générale pour votre fenêtre. En haut de la fenêtre, vous utilisez une disposition horizontale pour placer une étiquette et une ligne d'édition. Ensuite, vous placez des cases à cocher en dessous de cela en utilisant une disposition verticale.

Utilisation de mises en page et de widgets multipages

Jusqu'à présent, vous avez vu comment utiliser des gestionnaires de mise en page traditionnels ou à usage général pour organiser les widgets dans les fenêtres de votre application. Ces gestionnaires de disposition organiseront les widgets sur un mise en page sur une seule page. En d'autres termes, votre interface graphique affichera toujours le même ensemble de widgets à l'utilisateur.

Parfois, vous devez créer une mise en page qui affiche un ensemble différent de widgets en réponse à certaines actions de l'utilisateur sur l'interface graphique. Par exemple, si vous créez une boîte de dialogue de préférences pour une application donnée, vous souhaiterez peut-être présenter à l'utilisateur un par tabulation, ou plusieurs pages, mise en page dans laquelle chaque onglet ou page contient un ensemble différent d'options étroitement liées. Chaque fois que l'utilisateur clique sur un onglet ou une page, l'application affiche un ensemble différent de widgets.

PyQt fournit une disposition intégrée appelée QStackedLayout et quelques widgets pratiques comme QTabWidget cela vous permettra de créer ce type de mise en page multipage. Les prochaines sections vous guideront à travers certains de ces outils.

Créer une pile de widgets

QStackedLayout fournit un gestionnaire de mise en page qui vous permettra d'organiser vos widgets sur un empiler, les uns sur les autres. Dans ce type de mise en page, un seul widget est visible à la fois.

Pour remplir une mise en page empilée avec des widgets, vous devez appeler .addWidget () sur l'objet de mise en page. Cela ajoutera chaque widget à la fin de la liste interne des widgets de la mise en page. Vous pouvez également insérer ou supprimer un widget à une position donnée dans la liste des widgets en utilisant .insertWidget (index) ou .removeWidget (widget), respectivement.

Chaque widget de la liste des widgets est affiché comme une page indépendante. Si vous souhaitez afficher plusieurs widgets sur une page, utilisez un QWidget objet pour chaque page et définissez une disposition appropriée des widgets pour le widget de page. Si vous avez besoin d'obtenir le nombre total de widgets (pages) dans la mise en page, vous pouvez appeler .compter().

Un point important à garder à l'esprit lorsque vous travaillez avec QStackedLayout objets est que vous devez fournir explicitement un mécanisme pour basculer entre les pages. Sinon, votre mise en page affichera toujours la même page à l'utilisateur. Pour basculer entre les pages, vous devez appeler .setCurrentIndex () sur l'objet de mise en page.

Voici un exemple qui montre comment utiliser une mise en page empilée avec une zone de liste déroulante pour basculer entre les pages:

    1importer sys
    2
    3de PyQt5.QtWidgets importer (
    4    QApplication,
    5    QComboBox,
    6    QFormLayout,
    sept    QLineEdit,
    8    QStackedLayout,
    9    QVBoxLayout,
dix    QWidget,
11)
12
13classe La fenêtre(QWidget):
14    def __init__(soi):
15        super().__init__()
16        soi.setWindowTitle("Exemple de QStackedLayout")
17        # Créer une mise en page de premier niveau
18        disposition = QVBoxLayout()
19        soi.setLayout(disposition)
20        # Créez et connectez la liste déroulante pour basculer entre les pages
21        soi.pageCombo = QComboBox()
22        soi.pageCombo.Ajouter des articles([[[["Page 1", "Page 2"])
23        soi.pageCombo.activé.relier(soi.switchPage)
24        # Créer la mise en page empilée
25        soi.stackedLayout = QStackedLayout()
26        # Créer la première page
27        soi.Page 1 = QWidget()
28        soi.page1Mise en page = QFormLayout()
29        soi.page1Mise en page.ajouter une rangée("Nom:", QLineEdit())
30        soi.page1Mise en page.ajouter une rangée("Adresse:", QLineEdit())
31        soi.Page 1.setLayout(soi.page1Mise en page)
32        soi.stackedLayout.addWidget(soi.Page 1)
33        # Créer la deuxième page
34        soi.page 2 = QWidget()
35        soi.page2Mise en page = QFormLayout()
36        soi.page2Mise en page.ajouter une rangée("Emploi:", QLineEdit())
37        soi.page2Mise en page.ajouter une rangée("Département:", QLineEdit())
38        soi.page 2.setLayout(soi.page2Mise en page)
39        soi.stackedLayout.addWidget(soi.page 2)
40        # Ajouter la liste déroulante et la disposition empilée à la disposition de niveau supérieur
41        disposition.addWidget(soi.pageCombo)
42        disposition.addLayout(soi.stackedLayout)
43
44    def switchPage(soi):
45        soi.stackedLayout.setCurrentIndex(soi.pageCombo.currentIndex())
46
47if __name__ == "__main__":
48    app = QApplication(sys.argv)
49    la fenêtre = Window()
50    la fenêtre.montrer()
51    sys.exit(app.exec_())

On lines 21 to 23, you create a QComboBox object that will allow you to switch between the pages in the layout. Then you add two options to the combo box in a list and connect it to .switchPage(), which is intended to handle page switching.

Inside .switchPage(), you call .setCurrentIndex() on the layout object, passing the current index of the combo box as an argument. This way, when the user changes the option in the combo box, the page on the stacked layout will change accordingly.

On line 25, you create the QStackedLayout object. On lines 27 to 32, you add the first page to the layout, and on lines 34 to 39, you add the second page. Each page is represented by a QWidget object that contains several widgets in a convenient layout.

The final step to get everything working is to add the combo box and the layout to the application’s main layout.

Here’s how your application behaves now:

QStackedLayout Example

In this case, you have two pages in your application’s layout. Each page is represented by a QWidget object. When you select a new page in the combo box on the top of the window, the layout changes to show the selected page.

Besides stacked layout and stacked widget, you can use QTabWidget to create a multipage user interface. You’ll learn how in the next section.

Using PyQt’s Tab Widgets

Another popular way of creating multipage arrangements in PyQt is by using a class called QTabWidget. This class provides a tab bar and a page area. You use the tab bar to switch between pages and the page area to display the page associated with the selected tab.

The tab bar is located at the top of the page area by default. However, you can change this behavior using .setTabPosition() and one of four possible tab positions:

Tab Position Tab Bar Location
QTabWidget.North Top of the pages
QTabWidget.South Bottom of the pages
QTabWidget.West Left of the pages
QTabWidget.East Right of the pages

To add tabs to a tab widget, you use .addTab(). This method has two variations, or overloaded implementations:

  1. .addTab(page, label)
  2. .addTab(page, icon, label)

In both cases, the method adds a new tab, with label as the tab’s title. page needs to be a widget representing the page associated with the tab at hand.

In the second variation of the method, icon needs to be a QIcon object. If you pass an icon to .addTab(), then that icon will be shown to the left of the tab’s title.

A common practice when creating tab widgets is to use a QWidget object for each page. This way, you’ll be able to add extra widgets to the page using a layout containing the required widgets.

Most of the time, you’ll use tab widgets to create dialogs for your GUI applications. This kind of layout allows you to present the user with several options in a relatively small space. You can also take advantage of the tab system to organize your options according to some classification criteria.

Here’s a sample application that shows the basics of how to create and use a QTabWidget object:

    1import sys
    2
    3de PyQt5.QtWidgets import (
    4    QApplication,
    5    QCheckBox,
    6    QTabWidget,
    sept    QVBoxLayout,
    8    QWidget,
    9)
dix
11class Window(QWidget):
12    def __init__(self):
13        super().__init__()
14        self.setWindowTitle("QTabWidget Example")
15        self.resize(270, 110)
16        # Create a top-level layout
17        layout = QVBoxLayout()
18        self.setLayout(layout)
19        # Create the tab widget with two tabs
20        tabs = QTabWidget()
21        tabs.addTab(self.generalTabUI(), "General")
22        tabs.addTab(self.networkTabUI(), "Network")
23        layout.addWidget(tabs)
24
25    def generalTabUI(self):
26        """Create the General page UI."""
27        generalTab = QWidget()
28        layout = QVBoxLayout()
29        layout.addWidget(QCheckBox("General Option 1"))
30        layout.addWidget(QCheckBox("General Option 2"))
31        generalTab.setLayout(layout)
32        return generalTab
33
34    def networkTabUI(self):
35        """Create the Network page UI."""
36        networkTab = QWidget()
37        layout = QVBoxLayout()
38        layout.addWidget(QCheckBox("Network Option 1"))
39        layout.addWidget(QCheckBox("Network Option 2"))
40        networkTab.setLayout(layout)
41        return networkTab
42
43if __name__ == "__main__":
44    app = QApplication(sys.argv)
45    la fenêtre = Window()
46    la fenêtre.montrer()
47    sys.exit(app.exec_())

In this example, you use a tab widget to present the user with a concise dialog that shows options related to the General et Network sections of a hypothetical preferences menu. On line 20, you create the QTabWidget object. Then you add two tabs to the tab widget using .addTab().

Dans .generalTabUI() et networkTabUI(), you create the specific GUI for each tab. To do this, you use a QWidget object, a QVBoxLayout object, and some checkboxes to hold the options.

If you run the application now, then you’ll get the following dialog on your screen:

QTabWidget Example

That’s it! You have a fully functional tab-based GUI. Note that to switch between pages, you just need to click the corresponding tab.

Laying Out the Application’s Main Window

If you’re using PyQt to create your GUI applications, then most of the time you’ll use QMainWindow to create a GUI on top of it. This class allows you to create main window–style applications. QMainWindow ships with its own predefined layout. This layout will allow you to add the following graphical components to your main window:

  • A menu bar at the top of the window
  • One or more toolbars at any of the four sides of the window
  • A status bar at the bottom of the window
  • One or more dock widgets at any of the four sides of the window (but without occupying the toolbars area)
  • A central widget at the very center of the window

For most applications, all these graphical components are optional except for the central widget, which is required to make your application work.

Some applications use a unique and fully functional widget as their central widget. For example, if you’re coding a text editor, then you’ll likely use a QTextEdit object as your editor’s central widget.

Other kinds of GUI applications might require a more elaborate central widget. In that case, you can use a QWidget object as your central widget and then create a layout containing the specific widget arrangement that you need for your application’s GUI. The final step is to set that layout as your central widget’s layout.

Most of the time, the layout that QMainWindow offers is enough to create any kind of GUI application. This layout will effectively manage the behavior of the widgets on the window, so you don’t have to worry about that.

Laying Out the Application’s Dialogs

GUI applications are commonly built using a main window and one or more dialogs. Dialogs are small windows that allow you to communicate with your users. PyQt provides QDialog to handle the creation of dialogs.

Unlike QMainWindow, QDialog doesn’t have a predefined or default top-level layout. That’s because dialogs can be quite varied and include a wide range of widget arrangements and combinations.

Once you place all the widgets on a dialog’s GUI, you need to set a top-level layout on that dialog. To do this, you have to call .setLayout() on the dialog object just like you’d do with any other widget.

Here’s a dialog-style application that shows how to set a top-level layout to a QDialog object:

    1import sys
    2
    3de PyQt5.QtWidgets import (
    4    QApplication,
    5    QDialog,
    6    QDialogButtonBox,
    sept    QFormLayout,
    8    QLineEdit,
    9    QVBoxLayout,
dix)
11
12class Dialog(QDialog):
13    def __init__(self):
14        super().__init__()
15        self.setWindowTitle("QDialog's Top-Level Layout Example")
16        dlgLayout = QVBoxLayout()
17        # Create a form layout and add widgets
18        formLayout = QFormLayout()
19        formLayout.addRow("Name:", QLineEdit())
20        formLayout.addRow("Job:", QLineEdit())
21        formLayout.addRow("Email:", QLineEdit())
22        # Add a button box
23        btnBox = QDialogButtonBox()
24        btnBox.setStandardButtons(
25            QDialogButtonBox.D'accord | QDialogButtonBox.Cancel
26        )
27        # Set the layout on the dialog
28        dlgLayout.addLayout(formLayout)
29        dlgLayout.addWidget(btnBox)
30        self.setLayout(dlgLayout)
31
32if __name__ == "__main__":
33    app = QApplication(sys.argv)
34    dlg = Dialog()
35    dlg.montrer()
36    sys.exit(app.exec_())

In this case, the application’s window inherits from QDialog, so you have a dialog-style application. On line 16, you create the layout that you’ll use as the dialog’s top-level layout. On lines 18 to 21, you create a form layout to arrange some widgets in a form.

On line 24, you add a QDialogButtonBox object. You’ll often use QDialogButtonBox to handle the buttons on a dialog. In this example, you use two buttons, an D'accord button and a Cancel bouton. These buttons won’t have any functionality—they’re just intended to make the dialog more realistic.

Once you have all the widgets and layouts in place, you can add them to the top-level layout. That’s what you do on lines 28 and 29. The final step, on line 30, is to set the top-level layout as your dialog’s layout using .setLayout().

If you run this application, then you’ll see the following window on your screen:

QDialog Top-Level Layout Example

It’s a best practice to set a top-level layout for all your dialogs. This ensures that the dialog’s GUI will behave coherently when the user resizes the underlying window. Otherwise, your dialogs could appear disorganized and unpolished in the user’s eyes.

Managing Space in a PyQt Layout

When it comes to using PyQt’s layout managers to arrange the widgets on a window or form, managing space—empty space, space between widgets, and so on—is a common issue. Being able to manage this space is an important skill to have.

Internally, layouts manage the available space on a window using some of the following widget properties:

Layouts use these properties to automatically position and resize widgets, assigning a given amount of space to each widget according to the available space. This ensures that widgets are consistently arranged and remain usable.

In the next three sections, you’ll learn how the different types of layouts manage space in PyQt.

Managing Space in Box Layouts

Box layouts do a great job when it comes to distributing available space between widgets. However, sometime their default behavior isn’t enough, and you need to manually handle the available space. To help you out in this situation, PyQt provides QSpacerItem. This class allows you to add blank space (or empty boxes) to a box layout.

Normally, you don’t need to use QSpacerItem directement. Instead, you call some of the following methods on your box layout objects:

  • .addSpacing(i) adds a non-stretchable space (or empty box) of fixed size je to the layout. je must be an integer representing the size of the space in pixels.

  • .addStretch(i) adds a stretchable space with a minimum size of 0 and a stretch factor je to a box layout. je must be an integer.

  • .insertSpacing(index, size) inserts a non-stretchable space at position index, with size size. Si index is negative, then the space is added at the end of the box layout.

  • insertStretch(index, stretch) inserts a stretchable space at position index, with a minimum size of 0 and a stretch factor of stretch. Si index is negative, then the space is added at the end of the box layout.

Stretchable spacers will expand or shrink to fill empty space when the user resizes the underlying window. Non-stretchable spacers will remain the same size regardless of the changes in the size of the underlying window.

Go back to the example of how to use vertical layouts and run that application again. If you pull down the border of the window, then you’ll notice that more space appears between the buttons the further down you pull:

Non Stretchable Spacer Example

This happens because the layout handles the newly available space by automatically expanding its boxes. You can change this behavior by adding a stretchable QSpacerItem object to the end of the layout.

In your example’s code, update the initializer of Window as follows:

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QVBoxLayout Example")
        self.resize(270, 110)
        # Create a QVBoxLayout instance
        layout = QVBoxLayout()
        # Add widgets to the layout
        layout.addWidget(QPushButton("Top"))
        layout.addWidget(QPushButton("Center"))
        layout.addWidget(QPushButton("Bottom"))
        layout.addStretch()
        # Set the layout on the application's window
        self.setLayout(layout)

In the highlighted line, you add a stretchable QSpacerItem object to the end of the layout by calling .addStretch() on the layout. If you run the application again, then you’ll get the following behavior:

Stretchable Spacer Example

Now all the extra space is automatically assigned to the stretchable QSpacerItem object at the bottom of the layout without affecting the position or size of the rest of the widgets. You can use this and other space management techniques to make your GUI applications look good and polished.

Managing Space in Grid and Form Layouts

Grid and form layouts handle available space in a different way. In these types of layouts, you can handle only the vertical and horizontal space between widgets. These layouts provide three methods to manage these spaces:

  1. setSpacing(spacing) sets both the vertical and the horizontal spacing between widgets to spacing.
  2. setVerticalSpacing(spacing) sets only the vertical spacing between widgets in the layout to spacing.
  3. setHorizontalSpacing(spacing) sets only the horizontal spacing between widgets in the layout to spacing.

In all cases, spacing is an integer representing pixels. Now go back to the example on how to create a form layout and update the initializer of Window like this:

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QFormLayout Example")
        self.resize(270, 110)
        # Create a QHBoxLayout instance
        layout = QFormLayout()
        # Add widgets to the layout
        layout.setVerticalSpacing(30)
        layout.addRow("Name:", QLineEdit())
        layout.addRow("Job:", QLineEdit())
        emailLabel = QLabel("Email:")
        layout.addRow(emailLabel, QLineEdit())
        # Set the layout on the application's window
        self.setLayout(layout)

In the highlighted line, you set the vertical space between widgets to 30 pixels. If you run the application again, then you’ll see the following window:

QFormLayout Spacing Example

Now there’s more space between the rows of widgets. You can also try modifying the example of how to use a grid layout by adding some vertical or horizontal space just to see how all these spacing mechanisms work.

Conclusion

Creating high-quality GUI applications requires laying out all the graphical components in a coherent and polished arrangement. In PyQt, an effective way of doing that is to use PyQt’s layout managers, which provide a user-friendly and productive way of approaching this task.

In this tutorial, you’ve learned:

  • What the benefits are of properly laying out the widgets on a GUI
  • How to programmatically arrange widgets using PyQt’s built-in layout managers
  • Which layout manager to use for your specific use case
  • How to lay out main window–style et dialog-style applications in PyQt

With this knowledge, you’ll be able to create good-looking and professional GUI applications using PyQt’s built-in layouts.