Construire une calculatrice graphique de bureau – Real Python

By | octobre 23, 2019

Expert Python

Même si les applications Web et mobiles semblent dépasser le marché du développement de logiciels, il existe toujours une demande pour des applications traditionnelles. Interface utilisateur graphique (GUI) applications de bureau. Les développeurs souhaitant créer ce type d’applications en Python disposent d’un large éventail de bibliothèques, notamment Tkinter, wxPython, PyQt, PySide2, etc. Dans ce tutoriel, vous allez développer des applications de bureau à interface graphique avec Python et PyQt.

Vous allez apprendre à:

  • Créer des interfaces utilisateur graphiques avec Python et PyQt
  • Donnez vie à vos applications en reliant les événements utilisateur à des actions concrètes
  • Créez des applications graphiques entièrement fonctionnelles pour résoudre les problèmes du monde réel

Pour ce tutoriel, vous allez créer une application de calculatrice avec Python et PyQt. Cela vous aidera à comprendre les principes fondamentaux et à vous familiariser avec la bibliothèque. Vous pouvez télécharger le code source du projet et tous les exemples de ce tutoriel en cliquant sur le lien ci-dessous:

Comprendre PyQt

PyQt est une liaison Python pour Qt, ensemble de bibliothèques C ++ et d’outils de développement comprenant des abstractions indépendantes de la plate-forme pour les interfaces graphiques, ainsi que la mise en réseau, les threads, les expressions régulières, les bases de données SQL, SVG, OpenGL, XML et de nombreuses autres fonctionnalités puissantes. Développé par RiverBank Computing Ltd, PyQt est disponible en deux éditions:

  1. PyQt4: une édition construite contre Qt 4.x et 5.x
  2. PyQt5: une édition construite uniquement avec Qt 5.x

Même si PyQt4 peut être construit avec Qt 5.x, seul un petit sous-ensemble également compatible avec Qt 4.x sera pris en charge. Cela signifie que si vous décidez d'utiliser PyQt4, vous manquerez probablement certaines des nouvelles fonctionnalités et améliorations de PyQt5. Consultez la documentation PyQt4 pour plus d'informations sur ce sujet.

Vous allez couvrir PyQt5 dans ce tutoriel, car cela semble être l'avenir de la bibliothèque. A partir de maintenant, assurez-vous de considérer toute mention de PyQt en tant que référence à PyQt5.

PyQt5 est basé sur Qt v5 et inclut des classes couvrant les interfaces utilisateur graphiques, la manipulation XML, la communication réseau, les expressions régulières, les threads, les bases de données SQL, le multimédia, la navigation sur le Web et d'autres technologies disponibles dans Qt. PyQt5 implémente plus d'un millier de ces classes Qt dans un ensemble de modules Python, qui sont tous contenus dans un paquet Python de niveau supérieur appelé PyQt5.

PyQt5 est compatible avec Windows, Unix, Linux, MacOS, iOS et Android. Cela peut constituer une fonctionnalité intéressante si vous recherchez une bibliothèque ou un framework pour développer des applications multiplates-formes avec une apparence native sur chaque plate-forme.

PyQt5 est disponible sous deux licences:

  1. La licence commerciale Riverbank
  2. La licence publique générale (GPL), version 3

Votre licence PyQt5 doit être compatible avec votre licence Qt. Si vous utilisez la version GPL, votre code doit également utiliser une licence compatible avec la GPL. Si vous souhaitez utiliser PyQt5 pour créer des applications commerciales, vous aurez besoin d’une licence commerciale pour votre installation.

Installation de PyQt

Vous avez le choix entre plusieurs options lorsque vous installez PyQt sur votre système ou votre environnement de développement. La première option est de construire à partir des sources. Cela peut être un peu compliqué, vous voudrez peut-être l'éviter si possible. Si vous avez vraiment besoin de compiler à partir des sources, vous pouvez alors consulter les recommandations de la documentation de la bibliothèque dans ces cas.

Une autre option serait d'utiliser des roues binaires. Les roues sont un moyen très populaire de gérer l’installation de paquets Python. Cependant, vous devez considérer que les roues pour PyQt5 ne sont disponibles que pour Python 3.5 et versions ultérieures. Il y a des roues pour:

  • Linux (64 bits)
  • macOS
  • Windows (32 bits et 64 bits)

Toutes ces roues contiennent des copies des bibliothèques Qt correspondantes, vous n’avez donc pas besoin de les installer séparément.

Votre troisième option consiste à utiliser des gestionnaires de paquets sur les distributions Linux et macOS. Pour Windows, vous pouvez utiliser un binaire .EXE fichier. Votre quatrième et dernière option consiste à utiliser la distribution Anaconda pour installer PyQt sur votre système. Les sections suivantes vous expliqueront certaines des options permettant d’installer correctement PyQt5 à partir de différentes sources et sur différentes plates-formes.

Installation à l'échelle du système avec pépin

Si vous utilisez Python 3.5 ou une version ultérieure, vous pouvez installer PyQt5 à partir de PyPI en exécutant la commande suivante:

Avec cette commande, vous installerez PyQt5 dans votre système de base. Vous pouvez commencer à utiliser la bibliothèque immédiatement après la fin de l'installation. Selon votre système d'exploitation, vous aurez peut-être besoin de privilèges root ou d'administrateur pour que cette installation fonctionne.

Installation d'environnement virtuel avec pépin

Vous pouvez décider de ne pas installer PyQt directement sur votre système de base pour ne pas gâcher votre configuration. Dans ce cas, vous pouvez utiliser un environnement virtuel Python. Utilisez les commandes suivantes pour en créer une et installer PyQt5:

$ python3 -m venv pyqtvenv
$ la source pyqtvenv / bin / activate
(pyqtvenv) $ pip installer pyqt5
Collecte de pyqt5
...
PyQt5-sip-4.19.17 installé avec succès pyqt5-5.12.2

Ici, vous créez un environnement virtuel avec venv. Une fois que vous l'activez, vous installez pyqt5 dans cet environnement avec pip installer pyqt5. Cette option d'installation est l'option la plus recommandée si vous souhaitez que votre système de base reste propre.

Installation spécifique à la plateforme

Dans l'écosystème Linux, plusieurs distributions incluent des packages binaires pour PyQt dans leurs référentiels. Si cela est vrai pour votre distribution, vous pouvez l’installer à l’aide du gestionnaire de paquets de la distribution. Sur Ubuntu 18.04 par exemple, vous pouvez utiliser la commande suivante:

$ sudo apt installer python3-pyqt5

Avec cette commande, vous installerez PyQt5 et toutes ses dépendances dans votre système de base afin de pouvoir utiliser la bibliothèque dans n’importe lequel de vos projets graphiques. Notez que vous avez besoin des privilèges root, que vous appelez ici avec sudo.

Si vous êtes un utilisateur Mac, vous pouvez installer PyQt5 à l’aide du gestionnaire de paquets Homebrew. Pour ce faire, ouvrez un terminal et tapez la commande suivante:

Si tout se passe bien, PyQt5 sera installé sur votre système de base et prêt à être utilisé.

Si vous préférez utiliser Windows, mais que vous décidez de ne pas utiliser les roues binaires, votre chemin à travers l'installation de PyQt peut être pénible. C’est parce que la page de téléchargement PyQt5 ne semble plus fournir les fichiers binaires Windows (.EXE fichiers) pour les versions ultérieures. Néanmoins, vous pouvez trouver des packages binaires Windows pour les anciennes versions de la bibliothèque sur la page du projet. Le dernier fichier binaire a été créé pour PyQt v5.6.

Si vous devez vraiment installer PyQt de cette façon, vous devrez:

  1. Déterminez la version de Python que vous utilisez et si vous disposez de Python 32 bits ou 64 bits.
  2. Téléchargez la bonne version pour votre installation Python
  3. Installez PyQt en lançant le .EXE déposer et suivre les instructions à l'écran

Installation d'Anaconda

Une autre alternative que vous pouvez utiliser pour installer PyQt est Anaconda, une distribution Python pour la science des données. Anaconda est un gestionnaire d’emballage et d’environnement gratuit et multi-plateforme, qui comprend un ensemble de plus de 1 500 packages open source.

Anaconda fournit un assistant d'installation convivial que vous pouvez utiliser pour installer PyQt sur votre système. Vous pouvez télécharger la version appropriée pour votre plate-forme actuelle et suivre les instructions à l'écran. Si vous installez la dernière version d’Anaconda, vous disposerez des packages suivants:

  • pyqt: une liaison Python pour la boîte à outils d'interface graphique multi-plateforme Qt (licences commerciales, GPL-2.0, GPL-3.0)
  • n'importe quoi: une couche de compatibilité pour PyQt4 / PyQt5 (licence GPL-3.0)
  • qtpy: une couche d'abstraction PyQt5 / PyQt4 / PySide (licence MIT)
  • pyqtgraph: une bibliothèque Python pour Scientific Graphics (licence MIT)

Avec cet ensemble de packages, vous aurez tout ce dont vous avez besoin pour développer des applications de bureau à interface graphique avec Python et PyQt.

Création de votre première application PyQt

Maintenant que votre installation PyQt est opérationnelle, vous êtes prêt à commencer à coder. Vous allez créer une application «Hello, World!» Avec Python et PyQt. Voici les étapes à suivre:

  1. Importation QApplication et tous les widgets requis de PyQt5.QtWidgets.
  2. Créer une instance de QApplication.
  3. Créez une instance de l'interface graphique de votre application.
  4. Affichez l'interface graphique de votre application.
  5. Exécutez la boucle d’événements de votre application (ou la boucle principale).

Vous pouvez télécharger le code source des exemples que vous allez couvrir dans cette section en cliquant sur le lien ci-dessous:

Vous allez commencer avec un fichier appelé bonjour.py dans votre répertoire de travail actuel:

# Nom du fichier: hello.py

"" "Exemple simple de Hello World avec PyQt5." ""

importation sys

# 1. Importer `QApplication` et tous les widgets requis
de PyQt5.QtWidgets importation QApplication
de PyQt5.QtWidgets importation QLabel
de PyQt5.QtWidgets importation QWidget

Tout d'abord, vous importez sys, ce qui vous permettra de gérer l’état de sortie de l’application. Ensuite, vous importez QApplication, QWidget, et QLabel de QtWidgets, qui fait partie du paquet nommé PyQt5. Vous utiliserez ces importations ultérieurement, mais pour l'instant, vous en avez terminé avec la première étape.

Pour la deuxième étape, vous devez créer une instance de QApplication comme suit:

# 2. Créer une instance de QApplication
app = QApplication(sys.argv)

Ici, vous créez l'instance de QApplication. Depuis le QApplication objet (app) fait tellement d'initialisation, vous devriez le créer avant de créer tout autre objet lié à l'interface graphique. le QApplication object traite également des arguments de ligne de commande courants, vous devez donc également passer sys.argv comme argument lorsque vous créez app.

La troisième étape consiste à créer l'interface graphique de l'application. Pour cet exemple, votre interface graphique sera basée sur QWidget, qui est la classe de base de tous les objets d'interface utilisateur dans PyQt. Créons l’interface graphique:

# 3. Créez une instance de l'interface graphique de votre application
fenêtre = QWidget()
fenêtre.setWindowTitle('App PyQt5')
fenêtre.setGeometry(100, 100, 280, 80)
fenêtre.bouge toi(60, 15)
bonjour = QLabel('

Bonjour le monde!

'
, parent=fenêtre) bonjour.bouge toi(60, 15)

Dans ce code, fenêtre est un exemple de QWidget, qui fournit toutes les fonctionnalités nécessaires à la création de la fenêtre (ou du formulaire) de l’application. Avec .setWindowTitle (), vous pouvez ajouter un titre à la fenêtre de votre application. Dans ce cas, le titre à afficher est PyQt5 App.

Vous pouvez utiliser .setGeometry () pour définir la taille de la fenêtre et où la placer sur votre écran. Les deux premiers paramètres sont les X et y coordonnées auxquelles la fenêtre sera placée à l'écran. Les troisième et quatrième paramètres sont les largeur et la taille de la fenêtre.

Toute application graphique fonctionnelle a besoin de widgets! Ici, vous utilisez un QLabel objet (bonjour) pour afficher le message Bonjour le monde! dans la fenêtre de votre application. QLabel les objets peuvent accepter du texte HTML, vous pouvez donc utiliser l'élément HTML '

Bonjour le monde!

' formater le texte en tant que h1 entête. Enfin, vous utilisez .bouge toi() placer bonjour aux coordonnées (60, 15) dans la fenêtre de votre application.

La troisième étape étant terminée, codons les deux dernières étapes et préparons votre première application d'interface graphique PyQt:

# 4. Affichez l'interface graphique de votre application
fenêtre.spectacle()

# 5. Exécutez la boucle d'événements de votre application (ou la boucle principale)
sys.sortie(app.exec_())

Ici, vous appelez .spectacle() sur fenêtre. L'appel à .spectacle() programme un événement de peinture. En d’autres termes, il ajoute un nouvel événement à la file d’événements de l’application. Vous couvrez la boucle d'événement dans une section ultérieure.

Enfin, vous démarrez la boucle d’événements de l’application en appelant app.exec_ (). L'appel à .exec_ () est enveloppé dans un appel à sys.exit (), ce qui vous permet de quitter proprement Python et de libérer des ressources de mémoire lorsque l'application se termine. Tu peux courir bonjour.py avec la commande suivante:

Lorsque vous exécutez ce script, vous devriez voir une fenêtre comme celle-ci:

Application d'interface graphique Hello World PyQt

Ici, votre application affiche une fenêtre (basée sur QWidget) avec le message Bonjour le monde! dessus. Pour afficher le message, vous utilisez un QLabel qui contient le message au format HTML.

Félicitations! Vous avez créé votre première application de bureau PyQt GUI!

Considérer les styles de code

Si vous examinez de plus près le code de votre première application, vous remarquerez que PyQt ne respecte pas le style de codage PEP 8 et les conventions de dénomination. PyQt est construit sur Qt, qui est écrit en C ++ et utilise un style de nommage camelCase pour les fonctions, les méthodes et les variables. Cela dit, vous devrez choisir le style de nommage que vous utiliserez pour vos propres applications d'interface graphique PyQt.

Le PEP 8 précise à ce sujet que:

Les nouveaux modules et packages (y compris les frameworks tiers) doivent être écrits dans ces normes, mais lorsqu'une bibliothèque existante a un style différent, la cohérence interne est préférable. (La source)

En outre, le zen de Python dit:

… La praticité bat la pureté. (La source)

Si vous souhaitez écrire un code cohérent, vous pouvez ignorer le style de nommage PEP 8 et vous en tenir au style de nommage PyQt. C'est une décision que vous devez prendre. Dans ce tutoriel, vous suivrez le style de nommage PyQt pour plus de cohérence.

Apprendre les bases de PyQt

Vous devrez maîtriser les concepts de base de la logique PyQt pour pouvoir utiliser efficacement la bibliothèque afin de développer des applications à interface graphique. Certains de ces concepts incluent:

  • Widgets
  • Gestionnaires de mise en page
  • Dialogues
  • Fenêtres principales
  • Applications
  • Boucles d'événement
  • Signaux et slots

Ces éléments constitueront les blocs de construction de vos applications d'interface graphique PyQt. La plupart d'entre eux sont représentés sous forme de classes Python. PyQt5.QtWidgets est le module qui fournit toutes ces classes. Ces éléments sont extrêmement importants, vous en couvrirez donc les prochaines sections.

Widgets

QWidget est la classe de base pour tous les objets d'interface utilisateur, ou widgets. Il s’agit de composants graphiques de forme rectangulaire que vous pouvez placer dans les fenêtres de votre application pour créer l’interface graphique. Les widgets contiennent une série d'attributs et de méthodes qui vous permettent de modéliser leur apparence et leur comportement. Ils peuvent également peindre une représentation d'eux-mêmes à l'écran.

Les widgets reçoivent également des clics de souris, des touches du clavier et d'autres événements de la part de l'utilisateur, du système de fenêtre et de nombreuses autres sources. Chaque fois qu'un widget attrape un événement, il émet un signal pour annoncer son changement d'état. PyQt5 a une collection riche et moderne de widgets qui servent à plusieurs fins. Certains des widgets les plus courants et utiles sont:

  • Boutons
  • Étiquettes
  • Modifications de ligne
  • Boites Combo
  • Boutons radio

Examinons de plus près chacun de ces widgets. Le premier est le bouton. Vous pouvez créer un bouton en instanciant QPushButton, une classe qui fournit un bouton de commande classique. Les boutons typiques sont D'accord, Annuler, Appliquer, Oui, Non, et proche. Voici à quoi ils ressemblent sur un système Linux:

Exemple PyQt QPushButton

Des boutons comme ceux-ci sont peut-être le widget le plus couramment utilisé dans une interface graphique. Lorsque vous cliquez dessus, vous pouvez demander à l'ordinateur d'effectuer des actions. Vous pouvez même effectuer des actions en réponse à un utilisateur qui clique sur un bouton.

Up next sont Étiquettes, que vous pouvez créer avec QLabel. Les étiquettes vous permettent d'afficher des informations utiles sous forme de texte ou d'images:

Exemple PyQt QLabel

Vous pouvez utiliser des étiquettes comme celles-ci pour mieux expliquer l'objectif ou l'utilisation de votre interface graphique. Vous pouvez modifier leur apparence de plusieurs manières et ils peuvent même accepter du texte HTML, comme vous l'avez vu précédemment. Les étiquettes peuvent également être utilisées pour spécifier une clé mnémonique de focus pour un autre widget.

Un autre widget commun est le édition en ligne, une zone de texte d’une seule ligne que vous pouvez créer avec QLineEdit. Les modifications de ligne sont utiles lorsque vous avez besoin que l'utilisateur saisisse ou modifie des données au format texte brut. Voici à quoi ils ressemblent sur un système Linux:

Exemple PyQt QLineEdit

Les modifications de ligne comme celles-ci fournissent des opérations de modification de base telles que copie, pâte, annuler, refaire, faites glisser, déposez, etc. Dans la figure ci-dessus, vous pouvez également constater que les objets de la première ligne affichent un texte fictif pour informer l'utilisateur du type de saisie requis.

Boites Combo sont un autre widget utile que vous pouvez créer avec QComboBox. Une liste déroulante présentera à votre utilisateur une liste d’options d’une manière qui occupe un minimum d’espace à l’écran. Voici un exemple de liste déroulante sur un système Linux:

Exemple PyQt QComboBox

Cette combo est lecture seulementCela signifie que l’utilisateur peut sélectionner l’une des options mais ne peut pas en ajouter. Les combos peuvent aussi être éditable, permettant à l'utilisateur d'ajouter de nouvelles options. Ils peuvent contenir des pixmaps, des chaînes ou les deux.

Le dernier widget que vous allez couvrir ici est le bouton radio, que vous pouvez créer avec QRadioButton. UNE QRadioButton objet est un bouton d'option qui peut être activé (coché) ou désactivé (non coché). Les boutons radio sont utiles lorsque l'utilisateur doit sélectionner l'une des nombreuses options. Dans ce cas, toutes les options sont visibles à l’écran en même temps:

Exemple PyQt QRadioButton

Dans ce groupe de boutons radio, un seul bouton peut être coché à la fois. Si l'utilisateur sélectionne un autre bouton radio, le bouton précédemment sélectionné s'éteindra automatiquement.

PyQt5 a une grande collection de widgets. Au moment d'écrire ces lignes, vous en avez plus de quarante à utiliser pour créer l'interface graphique de votre application. Ceux que vous avez couverts jusqu’à présent ne constituent qu’un petit échantillon, mais ils vous montrent la puissance et la flexibilité de PyQt5. Dans la section suivante, vous découvrirez comment mettre en forme différents widgets pour créer des interfaces graphiques modernes et fonctionnelles pour vos applications.

Gestionnaires de mise en page

Maintenant, vous savez ce que sont les widgets et comment les utiliser pour construire des interfaces graphiques. Mais comment pouvez-vous organiser un ensemble de widgets pour créer une interface graphique cohérente et fonctionnelle? Vous pouvez utiliser différentes techniques pour organiser les widgets sur un formulaire ou une fenêtre. Par exemple, vous pouvez utiliser .resize () et .bouge toi() pour donner aux widgets des tailles et des positions absolues. Cependant, cela peut avoir des inconvénients:

  • Vous devrez effectuer de nombreux calculs manuels pour déterminer la taille et la position correctes de chaque widget dans vos formulaires.
  • Vous devrez effectuer des calculs supplémentaires pour répondre correctement aux modifications de la taille du formulaire (redimensionner l'événement).
  • Vous devrez refaire tous les calculs chaque fois que vous modifiez la disposition de vos formulaires, ajoutez ou supprimez des widgets.

Une alternative consiste à utiliser .resizeEvent () calculer la taille et la position du widget de manière dynamique. Cependant, l'alternative la plus efficace est peut-être d'utiliser gestionnaires de mise en page, ce qui augmentera votre productivité et améliorera la maintenabilité de votre code.

Les gestionnaires de disposition sont des classes qui vous permettent de dimensionner et de positionner vos widgets aux emplacements souhaités sur le formulaire de l'application. Les gestionnaires de disposition s'adaptent automatiquement pour redimensionner les événements et les modifications de contenu. Ils contrôlent également la taille des widgets qu'ils contiennent. Cela signifie que les widgets d'une mise en page sont automatiquement redimensionnés chaque fois que le formulaire est redimensionné.

PyQt fournit quatre classes de base de gestionnaire de disposition:

  1. QHBoxLayout
  2. QVBoxLayout
  3. QGridLayout
  4. QFormLayout

La première classe de gestionnaire de disposition est QHBoxLayout, qui organise les widgets horizontalement de gauche à droite:

PyQt QHBoxLayout schéma

Les widgets apparaîtront côte à côte, en commençant par la gauche.

Cet exemple de code vous montre comment utiliser QHBoxLayout pour organiser les boutons horizontalement:

    1 # Nom du fichier: h_layout.py
    2 
    3 "" "Exemple de disposition horizontale." ""
    4 
    5 importation sys
    6 
    sept de PyQt5.QtWidgets importation QApplication
    8 de PyQt5.QtWidgets importation QHBoxLayout
    9 de PyQt5.QtWidgets importation QPushButton
dix de PyQt5.QtWidgets importation QWidget
11 
12 app = QApplication(sys.argv)
13 fenêtre = QWidget()
14 fenêtre.setWindowTitle('QHBoxLayout')
15 disposition = QHBoxLayout()
16 disposition.ajouter un voyage(QPushButton('La gauche'))
17 disposition.ajouter un voyage(QPushButton('Centre'))
18 disposition.ajouter un voyage(QPushButton('Droite'))
19 fenêtre.setLayout(disposition)
20 fenêtre.spectacle()
21 sys.sortie(app.exec_())

Les lignes en surbrillance font la magie ici:

  • Ligne 15 crée un QHBoxLayout objet appelé disposition.
  • Lignes 16 à 18 ajouter trois boutons à disposition avec .addWidget ()
  • Ligne 19 ensembles disposition comme la disposition de votre fenêtre avec .setLayout ().

Quand tu cours python3 h_layout.py à partir de votre ligne de commande, vous obtiendrez le résultat suivant:

Exemple PyQt QHBoxLayout

Dans la figure ci-dessus, vous avez ajouté trois boutons horizontaux. Notez que les boutons sont affichés de gauche à droite dans le même ordre que vous les avez ajoutés dans votre code.

La prochaine classe de gestionnaire de disposition est QVBoxLayout, qui organise les widgets verticalement, de haut en bas:

PyQt QVBoxLayout schéma

Chaque nouveau widget apparaîtra sous le précédent. Vous pouvez utiliser cette classe pour construire des objets de disposition de boîte verticale et organiser votre widget de haut en bas.

Voici comment créer et utiliser un QVBoxLayout objet:

    1 # Nom du fichier: v_layout.py
    2 
    3 "" "Exemple de disposition verticale." ""
    4 
    5 importation sys
    6 
    sept de PyQt5.QtWidgets importation QApplication
    8 de PyQt5.QtWidgets importation QPushButton
    9 de PyQt5.QtWidgets importation QVBoxLayout
dix de PyQt5.QtWidgets importation QWidget
11 
12 app = QApplication(sys.argv)
13 fenêtre = QWidget()
14 fenêtre.setWindowTitle('QVBoxLayout')
15 disposition = QVBoxLayout()
16 disposition.ajouter un voyage(QPushButton('Haut'))
17 disposition.ajouter un voyage(QPushButton('Centre'))
18 disposition.ajouter un voyage(QPushButton('Bas'))
19 fenêtre.setLayout(disposition)
20 fenêtre.spectacle()
21 sys.sortie(app.exec_())

À la ligne 15, vous créez une instance de QVBoxLayout. Dans les trois lignes suivantes, vous ajoutez trois boutons à disposition. Enfin, vous utilisez disposition organiser le widget dans une mise en page verticale.

Lorsque vous exécuterez cette application, vous obtiendrez une sortie comme celle-ci:

Exemple PyQt QVBoxLayout

Cette application affiche trois boutons disposés verticalement, l'un en dessous de l'autre. Les boutons apparaissent dans le même ordre que vous les avez ajoutés dans votre code, de haut en bas.

La troisième classe de gestionnaire de disposition est QGridLayout, qui organise les widgets dans une grille de lignes et de colonnes. Chaque widget aura une position relative sur la grille. Vous pouvez définir la position d’un widget en lui transmettant une paire de coordonnées sous la forme (rangée, colonne). Ces coordonnées doivent être valides int Nombres. Ils définissent la cellule de la grille sur laquelle vous allez placer le widget. La disposition de la grille fonctionne comme suit:

PyQt QGridLayout schéma

QGridLayout prend l'espace mis à sa disposition par son parent, le divise en Lignes et Colonneset place chaque widget dans sa propre cellule.

Voici comment utiliser QGridLayout dans votre interface graphique:

    1 # Nom du fichier: g_layout.py
    2 
    3 "" "Exemple de disposition de grille." ""
    4 
    5 importation sys
    6 
    sept de PyQt5.QtWidgets importation QApplication
    8 de PyQt5.QtWidgets importation QGridLayout
    9 de PyQt5.QtWidgets importation QPushButton
dix de PyQt5.QtWidgets importation QWidget
11 
12 app = QApplication(sys.argv)
13 fenêtre = QWidget()
14 fenêtre.setWindowTitle('QGridLayout')
15 disposition = QGridLayout()
16 disposition.ajouter un voyage(QPushButton('Bouton (0, 0)'), 0, 0)
17 disposition.ajouter un voyage(QPushButton("Bouton (0, 1)"), 0, 1)
18 disposition.ajouter un voyage(QPushButton("Bouton (0, 2)"), 0, 2)
19 disposition.ajouter un voyage(QPushButton('Bouton (1, 0)'), 1, 0)
20 disposition.ajouter un voyage(QPushButton("Bouton (1, 1)"), 1, 1)
21 disposition.ajouter un voyage(QPushButton("Bouton (1, 2)"), 1, 2)
22 disposition.ajouter un voyage(QPushButton('Bouton (2, 0)'), 2, 0)
23 disposition.ajouter un voyage(QPushButton('Bouton (2, 1) + 2 colonnes'), 2, 1, 1, 2)
24 fenêtre.setLayout(disposition)
25 fenêtre.spectacle()
26 sys.sortie(app.exec_())

Dans cet exemple, vous créez une application qui utilise un QGridLayout objet pour organiser ses widgets. Notez que, dans ce cas, les deuxième et troisième arguments que vous transmettez à .addWidget () sont int arguments qui définissent la position de chaque widget.

A la ligne 23, vous ajoutez deux arguments supplémentaires à .addWidget (). Ces arguments sont appelés rowSpan et columnSpan, et ce sont les quatrième et cinquième arguments passés à la fonction. Vous pouvez les utiliser pour faire en sorte qu'un widget occupe plus d'une ligne ou d'une colonne, comme vous l'avez fait avec QPushButton ('Button (2, 1) + 2 Columns Span') ici.

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

Exemple PyQt QGridLayout

Vous pouvez voir vos widgets organisés dans une grille de lignes et de colonnes. Le dernier widget occupe plusieurs cellules, comme indiqué à la ligne 23.

La dernière classe de gestionnaire de disposition est QFormLayout, qui organise les widgets dans une disposition en deux colonnes. La première colonne affiche généralement les messages sous forme d'étiquettes. La deuxième colonne contient généralement des widgets tels que QLineEdit, QComboBox, QSpinBox, etc. Celles-ci permettent à l'utilisateur d'entrer ou de modifier des données concernant les informations de la première colonne. Le diagramme suivant montre le fonctionnement pratique des dispositions de formulaire:

PyQt QFormLayout schéma

La colonne de gauche est composée d'étiquettes et la colonne de droite est composée de widgets de champs. Si vous utilisez une application de base de données, ce type de présentation peut être une option attrayante pour une productivité accrue lorsque vous créez vos formulaires.

L’exemple suivant montre comment créer une application utilisant un QFormLayout objet pour organiser ses widgets:

    1 # Nom du fichier: f_layout.py
    2 
    3 "" "Exemple de mise en page." ""
    4 
    5 importation sys
    6 
    sept de PyQt5.QtWidgets importation QApplication
    8 de PyQt5.QtWidgets importation QFormLayout
    9 de PyQt5.QtWidgets importation QLineEdit
dix de PyQt5.QtWidgets importation QWidget
11 
12 app = QApplication(sys.argv)
13 fenêtre = QWidget()
14 fenêtre.setWindowTitle('QFormLayout')
15 disposition = QFormLayout()
16 disposition.ajouter une rangée('Nom:', QLineEdit())
17 disposition.ajouter une rangée('Âge:', QLineEdit())
18 disposition.ajouter une rangée('Emploi:', QLineEdit())
19 disposition.ajouter une rangée('Loisirs:', QLineEdit())
20 fenêtre.setLayout(disposition)
21 fenêtre.spectacle()
22 sys.sortie(app.exec_())

Les lignes 15 à 20 font le travail difficile dans cet exemple. Remarquerez que QFormLayout a une méthode pratique appelée .ajouter une rangée(). Vous pouvez utiliser cette méthode pour ajouter une ligne à deux widgets à la présentation. Le premier argument de .ajouter une rangée() devrait être une étiquette, et le deuxième argument devrait être tout autre widget permettant à l'utilisateur d'entrer ou de modifier des données.

Si vous exécutez ce code, vous obtiendrez le résultat suivant:

Exemple PyQt QFormLayout

La figure ci-dessus montre une interface graphique qui utilise une disposition de formulaire. La première colonne contient des étiquettes pour demander des informations à l'utilisateur. La deuxième colonne affiche les widgets qui permettent à l'utilisateur de saisir ou de modifier les informations que vous leur avez demandées.

Dialogues

Avec PyQt, vous pouvez développer deux types d’applications de bureau à interface graphique. En fonction de la classe utilisée pour créer le formulaire ou la fenêtre principale, vous obtiendrez l’un des éléments suivants:

  1. Une application de type fenêtre principale: La fenêtre principale de l'application hérite de QMainWindow.
  2. Une application de style dialogue: La fenêtre principale de l'application hérite de QDialog.

Vous commencerez par les applications de style dialogue. Dans la section suivante, vous aborderez les applications de type fenêtre principale.

Pour développer une application de style dialogue, vous devez créer une classe graphique qui hérite de QDialog, qui est la classe de base de toutes les fenêtres de dialogue. UNE fenêtre de dialogue est toujours une fenêtre de niveau supérieur que vous pouvez utiliser comme fenêtre principale pour votre application Dialog-Style.

Une boîte de dialogue est toujours un widget de niveau supérieur. Si elle a un parent, son emplacement par défaut est alors centré sur le widget de niveau supérieur du parent. Ce type de dialogue partagera également l’entrée de la barre des tâches du parent. Si vous ne définissez pas un parent pour une boîte de dialogue donnée, celle-ci aura sa propre entrée dans la barre des tâches du système.

Voici un exemple de la façon dont vous utiliseriez QDialog développer une application de type dialogue:

    1 # Nom du fichier: dialog.py
    2 
    3 "" "Application de style dialogue." ""
    4 
    5 importation sys
    6 
    sept de PyQt5.QtWidgets importation QApplication
    8 de PyQt5.QtWidgets importation QDialog
    9 de PyQt5.QtWidgets importation QDialogButtonBox
dix de PyQt5.QtWidgets importation QFormLayout
11 de PyQt5.QtWidgets importation QLineEdit
12 de PyQt5.QtWidgets importation QVBoxLayout
13 
14 classe Dialogue(QDialog):
15     """Dialogue."""
16     def __init__(soi, parent=Aucun):
17         "" "Initialiseur." ""
18         super().__init__(parent)
19         soi.setWindowTitle('QDialog')
20         dlgLayout = QVBoxLayout()
21         formLayout = QFormLayout()
22         formLayout.ajouter une rangée('Nom:', QLineEdit())
23         formLayout.ajouter une rangée('Âge:', QLineEdit())
24         formLayout.ajouter une rangée('Emploi:', QLineEdit())
25         formLayout.ajouter une rangée('Loisirs:', QLineEdit())
26         dlgLayout.addLayout(formLayout)
27         btns = QDialogButtonBox()
28         btns.setStandardButtons(
29             QDialogButtonBox.Annuler | QDialogButtonBox.D'accord)
30         dlgLayout.ajouter un voyage(btns)
31         soi.setLayout(dlgLayout)
32 
33 si __Nom__ == '__principale__':
34     app = QApplication(sys.argv)
35     dlg = Dialogue()
36     dlg.spectacle()
37     sys.sortie(app.exec_())

Cette application est un peu plus élaborée. Voici ce qui se passe:

  • Ligne 14 crée une classe complète Dialogue pour l'interface graphique, qui hérite de QDialog.
  • Ligne 20 assigne un QVBoxLayout objecter à dlgLaout.
  • Ligne 21 assigne un QVFormLayout objecter à formLayout.
  • Ligne 22 à 25 ajouter des widgets à formLayout.
  • Ligne 26 les usages dlgLayout pour organiser tous les widgets sur le formulaire.
  • Ligne 27 fournit un objet pratique pour placer les boutons de dialogue.
  • Lignes 28 et 29 ajoutez deux boutons standard: D'accord et Annuler.
  • Lignes 33 à 37 wrap the boilerplate code in an if __name__ == '__main__': idiom. This is considered a best practice for Pythonistas.

The code block above displays the following window:

PyQt Dialog-Style application example

This is the GUI that you created using QFormLayout for the widgets and QVBoxLayout for the general application’s layout (dlgLayout in line 20).

Main Windows

Most of the time, your GUI applications will be Main Window-Style. This means that they’ll have a menu bar, some toolbars, a status bar, and a central widget that will be the GUI’s main element. It’s also common that your apps will have several dialog windows to accomplish secondary actions that depend on user input.

You’ll use the class QMainWindow to develop Main Window-Style applications. You need to inherit from QMainWindow to create your main GUI class. An instance of a class that derives from QMainWindow is considered to be a main window. QMainWindow provides a framework for building your application’s GUI. The class has its own built-in layout, which you can use to place the following:

  • One menu bar is at the top of the window. The menu bar holds the application’s main menu.

  • Several toolbars are on the sides of the window. Toolbars are suitable for holding tool buttons and other kinds of widgets such as QComboBox, QSpinBox, et plus.

  • One central widget is in the center of the window. The central widget can be of any type, or it can be a composite widget.

  • Several dock widgets are around the central widget. Dock widgets are small, movable windows.

  • One status bar is at the bottom of the window. The status bar shows information on the application’s general status.

You can’t create a main window without first setting a central widget. Toi doit have a central widget, even if it’s just a placeholder. When this is the case, you can use a QWidget object as your central widget. You can set the main window’s central widget with .setCentralWidget(). The main window’s layout will allow you to have only one central widget, but it can be a single or a composite widget.

The following code example shows you how to use QMainWindow to create a Main Window-Style application:

    1 # Filename: main_window.py
    2 
    3 """Main Window-Style application."""
    4 
    5 import sys
    6 
    sept de PyQt5.QtWidgets import QApplication
    8 de PyQt5.QtWidgets import QLabel
    9 de PyQt5.QtWidgets import QMainWindow
dix de PyQt5.QtWidgets import QStatusBar
11 de PyQt5.QtWidgets import QToolBar
12 
13 class Window(QMainWindow):
14     """Main Window."""
15     def __init__(self, parent=Aucun):
16         """Initializer."""
17         super().__init__(parent)
18         self.setWindowTitle('QMainWindow')
19         self.setCentralWidget(QLabel("I'm the Central Widget"))
20         self._createMenu()
21         self._createToolBar()
22         self._createStatusBar()
23 
24     def _createMenu(self):
25         self.menu = self.menuBar().addMenu("&Menu")
26         self.menu.addAction('&Exit', self.close)
27 
28     def _createToolBar(self):
29         tools = QToolBar()
30         self.addToolBar(tools)
31         tools.addAction('Exit', self.close)
32 
33     def _createStatusBar(self):
34         status = QStatusBar()
35         status.showMessage("I'm the Status Bar")
36         self.setStatusBar(status)
37 
38 if __name__ == '__main__':
39     app = QApplication(sys.argv)
40     win = Window()
41     win.spectacle()
42     sys.exit(app.exec_())

Here’s how this code works:

  • Line 13 creates a class Window that inherits from QMainWindow.
  • Line 18 sets the window’s title.
  • Line 19 sets a QLabel as the central widget.
  • Lines 20 to 22 call private methods in the lines that follow in order to create different GUI elements:
    • Lines 24 to 26 create the main menu.
    • Lines 28 to 31 create the toolbar.
    • Lines 33 to 36 create the status bar.

When you run the above code, you’ll see a window like the following:

PyQt Main Window-Style application example

You can see that your Main Window-Style application has the following components:

  • One main menu called Menu
  • One toolbar with a functional Sortie tool button
  • One central widget (a QLabel object)
  • One status bar at the bottom of the window

So far, you’ve covered some of the more important graphical components of PyQt5’s set of widgets. In the next two sections, you’ll cover some other important concepts related to PyQt GUI applications.

Applications

The most basic class you’ll use when developing PyQt GUI applications is QApplication. This class is at the core of any PyQt application. It manages the application’s control flow as well as its main settings. In PyQt, any instance of QApplication is considered to be an application. Every PyQt GUI application must have one QApplication object. Some of the application’s responsibilities include:

  • Handling initialization and finalization
  • Providing the event loop and event handling
  • Handling most of the system-wide and application-wide settings
  • Providing access to global information, such as the application’s directory, screen size, and so on
  • Parsing common command-line arguments
  • Defining the application’s look and feel
  • Providing localization capabilities

These are just some of the core responsibilities of QApplication. As you can see, this is a fundamental class when it comes to developing PyQt GUI applications!

One of the most important responsibilities of QApplication is to provide the event loop and the entire event handling mechanism. Let’s take a closer look at the event loop now.

Event Loops

GUI applications are event-driven. This means that functions and methods are executed in response to user actions like clicking on a button, selecting an item from a combo box, entering or updating the text in a text edit, pressing a key on the keyboard, and so on. These user actions are generally called events.

Events are commonly handled by an event loop (also called the main loop). An event loop is an infinite loop in which all events from the user, the window system, and any other sources are processed and dispatched. The event loop waits for an event to occur and then dispatches it to perform some task. The event loop continues to work until the application is terminated.

Event loops are used by all GUI applications. The event loop is kind of an infinite loop that waits for the occurrence of events. If an event happens, then the loop checks if the event is a Terminate un événement. In that case, the loop is terminated and the application exits. Otherwise, the event is sent to the application’s event queue for further processing, and the loop starts again.

In PyQt, you can run the application’s event loop by calling .exec_() sur le QApplication object.

For an event to trigger a response action, you need to connect the event with the action you want to be executed. In PyQt5, you can establish that connection by using the signals and slots mechanism. You’ll cover these in the next section.

Signals and Slots

PyQt widgets act as event-catchers. This means that every widget can catch a specific number of events, like mouse clicks, keypresses, and so on. In response to these events, widgets always emit a signal, which is a kind of message that announces a change in its state.

The signal on its own doesn’t perform any action. If you want a signal to trigger an action, then you need to connect it to a slot. This is the function or method that will perform an action whenever the connecting signal is emitted. You can use any Python callable (or callback) as a slot.

If a signal is connected to a slot, then the slot is called whenever the signal is emitted. If a signal isn’t connected to any slot, then nothing happens and the signal is ignored. Here are some of the most useful features of this mechanism:

  • A signal can be connected to one or many slots.
  • A signal may also be connected to another signal.
  • A slot may be connected to one or many signals.

You can use the following syntax to connect a signal to a slot:

widget.signal.connect(slot_function)

This will connect slot_function à widget.signal. Whenever signal is emitted, slot_function() will be called.

This code shows you how to use the signals and slots mechanism:

    1 # Filename: signals_slots.py
    2 
    3 """Signals and slots example."""
    4 
    5 import sys
    6 
    sept de PyQt5.QtWidgets import QApplication
    8 de PyQt5.QtWidgets import QLabel
    9 de PyQt5.QtWidgets import QPushButton
dix de PyQt5.QtWidgets import QVBoxLayout
11 de PyQt5.QtWidgets import QWidget
12 
13 def greeting():
14     """Slot function."""
15     if msg.text():
16         msg.setText("")
17     else:
18         msg.setText("Hello World!")
19 
20 app = QApplication(sys.argv)
21 window = QWidget()
22 window.setWindowTitle('Signals and slots')
23 layout = QVBoxLayout()
24 
25 btn = QPushButton('Greet')
26 btn.clicked.connect(greeting)  # Connect clicked to greeting()
27 
28 layout.addWidget(btn)
29 msg = QLabel('')
30 layout.addWidget(msg)
31 window.setLayout(layout)
32 window.spectacle()
33 sys.exit(app.exec_())

In line 13, you create greeting(), which you’ll use as a slot. Then in line 26, you connect the button’s clicked signal to greeting(). This way, whenever the user clicks on the button, greeting() is called and msg will alternate between the message Bonjour le monde! and an empty string:

PyQt signals and slots example

When you click on Greet, le Bonjour le monde! message will appear and disappear on your screen.

If your slot function needs to receive extra arguments, then you can pass them in by using functools.partial. For example, you can modify greeting() as follows:

def greeting(qui):
    """Slot function."""
    if msg.text():
        msg.setText('')
    else:
        msg.setText(f'Hello who')

Maintenant, greeting() needs to receive an argument called qui. If you want to connect this new version of greeting() à la btn.clicked signal, then you can do something like this:

btn.clicked.connect(functools.partial(greeting, 'World!'))

For this code to work, you need to import functools first. The call to functools.partial() returns an object that behaves similarly to calling greeting() avec who='World!'. Now, when the user clicks on the button, the message 'Hello World!' will be shown in the label as well as before.

The signals and slots mechanism is what you’ll use to give life to your PyQt5 GUI applications. This mechanism will allow you to turn user events into concrete actions. You can dive deeper into the signals and slots mechanism by taking a look at the PyQt5 documentation.

Now you’ve finished covering the most important concepts of PyQt5. With this knowledge and the library’s documentation at hand, you’re ready to start developing your own GUI applications. In the next section, you’ll build your first fully-functional GUI application.

Let’s go for it!

Creating a Calculator With Python and PyQt

In this section, you’re going to develop a calculator using the Model-View-Controller (MVC) design pattern. This pattern has three layers of code, each with different roles:

  1. The model takes care of your app’s business logic. It contains the core functionality and data. For your calculator, the model will handle the calculations.

  2. The view implements your app’s GUI. It hosts all the widgets the end-user would need to interact with the application. The view also receives user actions and events. For your calculator, the view will be the window you’ll see on your screen.

  3. The controller connects the model and the view to make the application work. User events (or requests) are sent to the controller, which puts the model to work. When the model delivers the requested result (or data) in the right format, the controller forwards it to the view. For your calculator, the controller will receive user events from the GUI, ask the model to perform calculations, and update the GUI with the result.

Here’s a step-by-step MVC pattern for a GUI desktop application:

  1. The user performs an action or request (event) on the view (GUI).
  2. The view notifies the controller about the user’s action.
  3. The controller gets the user’s request and queries the model for a response.
  4. The model processes the controller query, performs the required operations, and returns an answer or result.
  5. The controller receives the model’s answer and updates the view accordingly.
  6. The user finally sees the requested result on the view.

You’ll use this MVC design pattern to build your calculator.

Creating the Skeleton

You’ll start by implementing a basic skeleton for your application, called pycalc.py. You can find this script and the rest of the source code at the link below:

If you’d prefer to code the project on your own, then go ahead and create pycalc.py in your current working directory. Then, open the file in your code editor and type the following code:

#!/usr/bin/env python3

# Filename: pycalc.py

"""PyCalc is a simple calculator built using Python and PyQt5."""

import sys

# Import QApplication and the required widgets from PyQt5.QtWidgets
de PyQt5.QtWidgets import QApplication
de PyQt5.QtWidgets import QMainWindow
de PyQt5.QtWidgets import QWidget

__version__ = '0.1'
__author__ = 'Leodanis Pozo Ramos'

# Create a subclass of QMainWindow to setup the calculator's GUI
class PyCalcUi(QMainWindow):
    """PyCalc's View (GUI)."""
    def __init__(self):
        """View initializer."""
        super().__init__()
        # Set some main window's properties
        self.setWindowTitle('PyCalc')
        self.setFixedSize(235, 235)
        # Set the central widget
        self._centralWidget = QWidget(self)
        self.setCentralWidget(self._centralWidget)

# Client code
def principale():
    """Main function."""
    # Create an instance of QApplication
    pycalc = QApplication(sys.argv)
    # Show the calculator's GUI
    vue = PyCalcUi()
    vue.spectacle()
    # Execute the calculator's main loop
    sys.exit(pycalc.exec_())

if __name__ == '__main__':
    principale()

This script implements all the code you’ll need to run a basic GUI application. You’ll use this skeleton to build your calculator. Here’s how it works:

  • Lines 10 to 12 import the required modules and classes from PyQt5.QtWidgets.

  • Line 18 creates the GUI with the class PyCalcUi. Note that this class inherits from QMainWindow.

  • Line 24 sets the window’s title to PyCalc.

  • Line 25 les usages .setFixedSize() to give the window a fixed size. This ensures that the user won’t be able to resize the window.

  • Line 27 creates a QWidget object to play the role of a central widget. Remember that since your GUI class inherits from QMainWindow, you need a central widget. This object will be the parent for the rest of the GUI component.

  • Line 31 defines your calculator’s main function, which is considered a best practice. This function will be the entry point to the application. Inside main(), your program does the following:

    • Line 34 creates a QApplication object pycalc.
    • Line 37 shows the GUI with view.show().
    • Line 39 runs the application’s event loop with pycalc.exec_().

When you run the script, the following window will appear on your screen:

PyCalc's skeleton

This is your GUI application skeleton.

Completing the View

The GUI you have at this point doesn’t really look like a calculator. Let’s finish the GUI by adding the display and buttons for the numbers. You’ll also add buttons for basic math operations and for clearing the display.

First, you’ll need to add the following imports to the top of your file:

de PyQt5.QtCore import Qt
de PyQt5.QtWidgets import QGridLayout
de PyQt5.QtWidgets import QLineEdit
de PyQt5.QtWidgets import QPushButton
de PyQt5.QtWidgets import QVBoxLayout

You’re going to use a QVBoxLayout for the calculator’s general layout. You’ll also use a QGridLayout object to arrange the buttons. Finally, you import QLineEdit for the display and QPushButton for the buttons. There should now be eight import statements at the top of your file.

Now you can update the initializer for PyCalcUi:

# Create a subclass of QMainWindow to setup the calculator's GUI
class PyCalcUi(QMainWindow):
    """PyCalc's View (GUI)."""
    def __init__(self):
        """View initializer."""
        super().__init__()
        # Set some main window's properties
        self.setWindowTitle('PyCalc')
        self.setFixedSize(235, 235)
        # Set the central widget and the general layout
        self.generalLayout = QVBoxLayout()
        self._centralWidget = QWidget(self)
        self.setCentralWidget(self._centralWidget)
        self._centralWidget.setLayout(self.generalLayout)
        # Create the display and the buttons
        self._createDisplay()
        self._createButtons()

Here, you’ve added the highlighted lines of code. You’ll use a QVBoxLayout to place the display at the top and the buttons in a grid layout at the bottom.

The calls to ._createDisplay() et ._createButtons() won’t work, because you haven’t yet implemented those methods. Let’s fix that by coding ._createDisplay():

class PyCalcUi(QMainWindow):
    # Snip
    def _createDisplay(self):
        """Create the display."""
        # Create the display widget
        self.afficher = QLineEdit()
        # Set some display's properties
        self.afficher.setFixedHeight(35)
        self.afficher.setAlignment(Qt.AlignRight)
        self.afficher.setReadOnly(True)
        # Add the display to the general layout
        self.generalLayout.addWidget(self.afficher)

To create the display widget, you use a QLineEdit object. Then you set the following display properties:

  • The display has a fixed height of 35 pixels.
  • The display shows the text as left-aligned.
  • The display is set to read-only to avoid direct editing.

The last line adds the display to the calculator’s general layout with generalLayout.addWidget().

Next, you’ll implement ._createButtons() to create buttons for your calculator. You’ll use a dictionary to hold each button’s text and position on the grid. You’ll also use QGridLayout to arrange the buttons on the calculator’s window. The final code will look like this:

class PyCalcUi(QMainWindow):
    # Snip
    def _createButtons(self):
        """Create the buttons."""
        self.buttons = 
        buttonsLayout = QGridLayout()
        # Button text | position on the QGridLayout
        buttons = '7': (0, 0),
                   '8': (0, 1),
                   '9': (0, 2),
                   '/': (0, 3),
                   'C': (0, 4),
                   '4': (1, 0),
                   '5': (1, 1),
                   '6': (1, 2),
                   '*': (1, 3),
                   '(': (1, 4),
                   '1': (2, 0),
                   '2': (2, 1),
                   '3': (2, 2),
                   '-': (2, 3),
                   ')': (2, 4),
                   '0': (3, 0),
                   '00': (3, 1),
                   '.': (3, 2),
                   '+': (3, 3),
                   '=': (3, 4),
                  
        # Create the buttons and add them to the grid layout
        pour btnText, pos dans buttons.items():
            self.buttons[[[[btnText] = QPushButton(btnText)
            self.buttons[[[[btnText].setFixedSize(40, 40)
            buttonsLayout.addWidget(self.buttons[[[[btnText], pos[[[[0], pos[[[[1])
            # Add buttonsLayout to the general layout
            self.generalLayout.addLayout(buttonsLayout)

You first create an empty dictionary self.buttons to hold the calculator buttons. Then, you create a temporary dictionary to store their labels and relative positions on the grid layout (buttonsLayout). Inside the pour loop, you create the buttons and add them to both self.buttons et buttonsLayout. Every button will have a fixed size of 40x40 pixels, which you set with .setFixedSize(40, 40).

Now, the calculator’s GUI (or view) can show the display and the buttons. But there’s still no way to update the information shown in the display. You can fix this by adding a few extra methods:

  1. .setDisplayText() to set and update the display’s text
  2. .displayText() to get the current display’s text
  3. .clearDisplay() to clear the display’s text

These methods will form the GUI public interface and complete the view class for your Python calculator. Here’s a possible implementation:

class PyCalcUi(QMainWindow):
    # Snip
    def setDisplayText(self, text):
        """Set display's text."""
        self.afficher.setText(text)
        self.afficher.setFocus()

    def displayText(self):
        """Get display's text."""
        return self.afficher.text()

    def clearDisplay(self):
        """Clear the display."""
        self.setDisplayText('')

Here’s what each function does:

  • .setDisplayText() les usages .setText() to set and update the display’s text, and .setFocus() to set the cursor’s focus on the display.

  • .displayText() is a getter method that returns the display’s current text. When the user clicks on the equals sign (=), the program will use the return value of .displayText() as the math expression to be evaluated.

  • .clearDisplay() sets the display’s text to an empty string ('') so the user can introduce a new math expression.

Now your calculator’s GUI is ready! When you run the application, you’ll see a window like this one:

PyCalc's Graphical User Interface

You’ve completed the calculator’s GUI interface. However, if you try to do some calculations, then you’ll notice that the calculator doesn’t do anything just yet. That’s because you haven’t implemented the model or the controller. Next, you’ll add a basic controller class to start giving life to your calculator.

Creating a Basic Controller

In this section, you’re going to code the calculator’s controller class. This class will connect the view to the model. You’ll use the controller class to make the calculator perform actions in response to user events. You’ll start with the following import:

de functools import partial

At the top of pycalc.py, you import partial() to connect signals with methods that need to take extra arguments.

Your controller class needs to perform three main tasks:

  1. Access the GUI’s public interface
  2. Handle the creation of math expressions
  3. Connect button clicked signals with the appropriate slots

This will ensure that your calculator is working correctly. Here’s how you code the controller class:

# Create a Controller class to connect the GUI and the model
class PyCalcCtrl:
    """PyCalc Controller class."""
    def __init__(self, vue):
        """Controller initializer."""
        self._view = vue
        # Connect signals and slots
        self._connectSignals()

    def _buildExpression(self, sub_exp):
        """Build expression."""
        expression = self._view.displayText() + sub_exp
        self._view.setDisplayText(expression)

    def _connectSignals(self):
        """Connect signals and slots."""
        pour btnText, btn dans self._view.buttons.items():
            if btnText ne pas dans '=', 'C':
                btn.clicked.connect(partial(self._buildExpression, btnText))

        self._view.buttons[[[['C'].clicked.connect(self._view.clearDisplay)

The first thing you do is give PyCalcCtrl an instance of the view PyCalcUi. You’ll use this instance to gain full access to the view’s public interface. Next, you create ._buildExpression() to handle the creation of math expressions. This method also updates the calculator’s display in response to user input.

Finally, you use ._connectSignals() to connect the imprimable buttons with ._buildExpression(). This allows your users to create math expressions by clicking on the calculator’s buttons. In the last line, you connect the clear button (C) to ._view.clearDisplay(). This method will clear up the text on the display.

For this new controller class to work, you need to update main():

# Client code
def principale():
    """Main function."""
    # Create an instance of QApplication
    pycalc = QApplication(sys.argv)
    # Show the calculator's GUI
    vue = PyCalcUi()
    vue.spectacle()
    # Create instances of the model and the controller
    PyCalcCtrl(vue=vue)
    # Execute calculator's main loop
    sys.exit(pycalc.exec_())

This code creates an instance of PyCalcCtrl(view=view) avec le vue passed in as an argument. This will initialize the controller and connect the signals and slots to give your calculator some functionality.

If you run the application, then you’ll see something like the following:

PyCalc's basic controller

As you can see, the calculator already has some useful functionalities! Now, you can build math expressions by clicking on the buttons. Notice that the equals sign (=) doesn’t work yet. To fix this, you need to implement the calculator’s model.

Implementing the Model

The model is the layer of code that takes care of the business logic. In this case, the business logic is all about basic math calculations. Your model will evaluate the math expressions introduced by your users. Since the model needs to handle errors, you’re going to define the following global constant:

This is the message the user will see if they introduce an invalid math expression.

Your model will be a single function:

# Create a Model to handle the calculator's operation
def evaluateExpression(expression):
    """Evaluate an expression."""
    try:
        result = str(eval(expression, , ))
    sauf Exception:
        result = ERROR_MSG

    return result

Here, you use eval() to evaluate a string as an expression. If this is successful, then you’ll return the result. Otherwise, you’ll return the error message. Note that this function isn’t perfect. It has a couple of important issues:

  • le try...except block doesn’t catch any specific exception, which is not a best practice in Python.
  • The function is based on the use of eval(), which can lead to some serious security issues. The general advice is to only use eval() on trusted input.

You’re free to rework the function to make it more reliable and secure. For this tutorial, you’ll use the function as-is.

Completing the Controller

Once you’ve completed the calculator’s model, you can finish the controller. The final version of PyCalcCtrl will include logic to process the calculations and to make sure the equals sign (=) works correctly:

# Create a Controller class to connect the GUI and the model
class PyCalcCtrl:
    """PyCalc's Controller."""
    def __init__(self, model, vue):
        """Controller initializer."""
        self._evaluate = model
        self._view = vue
        # Connect signals and slots
        self._connectSignals()

    def _calculateResult(self):
        """Evaluate expressions."""
        result = self._evaluate(expression=self._view.displayText())
        self._view.setDisplayText(result)

    def _buildExpression(self, sub_exp):
        """Build expression."""
        if self._view.displayText() == ERROR_MSG:
            self._view.clearDisplay()

        expression = self._view.displayText() + sub_exp
        self._view.setDisplayText(expression)

    def _connectSignals(self):
        """Connect signals and slots."""
        pour btnText, btn dans self._view.buttons.items():
            if btnText ne pas dans '=', 'C':
                btn.clicked.connect(partial(self._buildExpression, btnText))

        self._view.buttons[[[['='].clicked.connect(self._calculateResult)
        self._view.afficher.returnPressed.connect(self._calculateResult)
        self._view.buttons[[[['C'].clicked.connect(self._view.clearDisplay)

The new lines of code are highlighted. First, you add a new parameter to the init function. Now the class receives instances from both the model and the view. Then in ._calculateResult(), you take the display’s content, evaluate it as a math expression, and finally show the result in the display.

You also add an if statement to ._buildExpression() to check if an error has occurred. If so, then you clear the display and start over with a new expression. Finally, you add two more connections inside ._connectSignals(). The first enables the equals sign (=). The second ensures that when the user hits Entrer, the calculator will process the expression as expected.

For all this code to work, you need to update main():

# Client code
def principale():
    """Main function."""
    # Create an instance of `QApplication`
    pycalc = QApplication(sys.argv)
    # Show the calculator's GUI
    vue = PyCalcUi()
    vue.spectacle()
    # Create instances of the model and the controller
    model = evaluateExpression
    PyCalcCtrl(model=model, vue=vue)
    # Execute calculator's main loop
    sys.exit(pycalc.exec_())

Here, your model holds a reference to evaluateExpression(). In addition, PyCalcCtrl() now receives two arguments: the model et le vue.

Running the Calculator

Now that you’ve finished the code, it’s time for a test! If you run the application, then you’ll see something like this:

PyCalc, a calculator with Python and PyQt

To use PyCalc, enter a valid math expression with your mouse. Then, press Entrer or click on the equals sign (=) to see the result on the calculator’s display.

Félicitations! You’ve developed your first fully-functional GUI desktop application with Python and PyQt!

PyQt5 offers quite a useful set of additional tools to help you build solid, modern, and full-featured GUI applications. Here are some of the most remarkable tools you can use:

Qt Designer is the Qt tool for designing and building graphical user interfaces. You can use it to design widgets, dialogs, or complete main windows by using on-screen forms and a drag-and-drop mechanism. The following figure shows some of the Qt Designer’s features:

Qt-Designer example

Qt Designer uses XML .ui files to store your GUI designs. You can load them with QUiLoader. PyQt includes a module called uic to help with this. You can also convert the .ui file content into Python code with a command-line tool called pyuic5.

PyQt5 also provides a comprehensive set of tools for the internationalization of apps into local languages. pylupdate5 creates and updates translation (.ts) files. Then, Qt Linguist updates the generated .ts files with translations of the strings. It also releases the .ts files as .qm files. Celles-ci .qm files are compact binary equivalents that can be used directly by the application.

Finally, you can use the PyQt5 resource system, which is a facility for embedding resources such as icons and translation files. To use this tool, you need to generate a .qrc fichier. This is an XML file that’s used to specify which resource files are to be embedded. Once you have this file ready, you can use pyrcc5 to generate a Python module that contains the embedded resources.

Conclusion

Graphical User Interface (GUI) desktop applications still hold a substantial share of the software development market. Python offers a handful of frameworks and libraries that can help you develop modern and robust GUI applications.

In this tutorial, you learned how to use PyQt, which is arguably one of the most popular and solid libraries for GUI desktop application development in Python. Now you know how to effectively use both Python and PyQt to build modern GUI desktop applications.

You’re now able to:

  • Create Graphical User Interfaces with Python and PyQt
  • Connect user events to concrete actions in your application
  • Create fully-functional GUI desktop applications to solve real-world problems

Now you can use Python and PyQt to give life to your desktop GUI applications! You can get the source code for the calculator project and all code examples at the link below:

Further Reading

If you want to dive deeper into PyQt and its related tools, then you can take a look at some of these resources:

Although the PyQt5 Documentation is the first resource listed here, some important parts are still missing or incomplete. Fortunately, you can use the PyQt4 Documentation to fill in the blanks. The Class Reference is particularly useful for gaining a complete understanding of widgets and classes.

If you’re using the PyQt4 documentation as a reference for PyQt5 classes, then bear in mind that the classes will be slightly different and may behave differently, too. Another option would be to use the original Qt v5 Documentation and its Class Reference instead. In this case, you may need some background in C++ to properly understand the code samples.