Accueil

Traduction

Tutoriel Python - sommaire

Tutoriel Python - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Créer et diffuser des applications locales

J'entends par "application" un programme proposant une ou des interfaces utilisateurs (plus d'utilisation du shell).
Dans ce chapitre je n'évoque que les applications locales (à l'opposé des applications en ligne) : des programmes qui fonctionnent sans connexion internet car elles sont installées sur le poste de l'utilisateur.

Les applications python en ligne (ou applications web) seront traitées dans le chapitre en lien (et les suivants): Python et le web

Gestion des exceptions

Une application ne doit pas "planter" à la moindre erreur de saisie de l'utilisateur ; il faut "gérer les exceptions".
Une application destinée être diffusée doit bien sûr intégrer cette gestion.

Les différents messages d'erreur

Testez dans la console :

>>> a= 20
>>> b=0
>>> a/b
...
ZeroDivisionError: division by zero
>>> c="10"
>>> d=10
>>> c+d
...
TypeError: can only concatenate str (not "int") to str
>>> d + e
...
NameError: name 'e' is not defined
>>>

Exemple de script

Je reprends le thème de la calculatrice évoqué dans le chapitre 19 : Interfaces utilisateur avec tkinter
Il n'y avait pas de gestion des exceptions dans cette version.

Rappel du script

Les saisies et affichages sont gérées par des fonctions.

def saisies() :
    a = eval(zt1.get())
    b = eval(zt2.get())
    return a,b
def fadd() :
    a,b = saisies()
    resultat = a+b
    zt3.delete(0,END)
    zt3.insert(0,resultat)
...
def fdivi():
    a,b = saisies()
    resultat = a / b
    zt3.delete(0,END)
    zt3.insert(0,resultat)

Exécution du programme

Dans le widget Entry légendé "saisir B" je saisis "2O" puis 0 puis je demande à chaque fois une division.
Affichage dans le shell :

...
SyntaxError: invalid decimal literal
...
ZeroDivisionError: division by zero

Heureusement, comme les entrées et sorties se déroulent désormais dans l'interface graphique ces erreurs ne sont pas bloquantes (le programme ne "plante pas"). Mais elles restent pertubantes pour l'utilisateur.

Le programme amélioré

Ajout dans la routine principale

Il faut importer le sous module tkinter.messagebox.

...
from tkinter import *
import tkinter.messagebox as msg
...
Modifications de certaines fonctions

Il faut modifier les fonctions "saisies()" & "fdivi()" en intégrant la "gestion des exeptions".

def saisies() :
    try:
        a = eval(zt1.get())
        b = eval(zt2.get())
        return a,b
    except : 
        msg.showinfo("Erreur : ", "il faut saisir des nombres !")
...
    
def fdivi():
    try : 
        a,b = saisies()
        resultat = a / b
        zt3.delete(0,END)
        zt3.insert(0,resultat)
    except :
        msg.showinfo("Erreur : ","Division par zéro !")

Le rendu

Dans le widget Entry légendé "saisir B" tapez 1O puis 0 et demandez à chaque fois une division.
À chaque fois un message d'erreur apparait dans une boite de dialogue.

Deux applications distribuables

Dans cette partie je vous propose deux applications locales comprenant chacune plusieurs fichiers.

Application "appli_temperatures"

Le script

def ecrire():
    celsius = float(scale.get())
    message = f"Votre température est {celsius} °C"
    msg.showinfo("Bilan de santé : ", message)
    cejour = time.strftime("%A %d %B %Y")
    heure = time.strftime("%H %M")
    contenu = f"Votre température le {cejour} à {heure}  est de {celsius} °C \n"

    with open("temperatures.txt", "a") as fichier:
        fichier.write(contenu)
#----------------------------------------
# nom programme : tkinter_temperatures.py
# objet : programme connecté à un fichier texte

import tkinter as tk
import tkinter.messagebox as msg
import time
import tkinter.ttk as ttk

# Création d'un objet "fenêtre
fenetre = tk.Tk()  
fenetre.title("Suivi des paramètres vitaux")
fenetre.geometry("400x400")
fenetre.config(bg ="ivory")
fonte1 = "{courier new} 14"

with open('temperatures.txt', 'r') as fichier:
        contenu = fichier.readlines()
tk.Label(fenetre, text = "Historique", font = fonte1).pack(padx =0, pady=10) 
deroulante = ttk.Combobox(fenetre, values=contenu, width = 150, font ="font1")
deroulante.pack(padx =0, pady=10)

#scale
tk.Label(fenetre, text = "Votre température ? ", font = fonte1).pack(padx =0, pady=10)
scale = tk.Scale(fenetre, from_=35, to=40, resolution=0.1, orient='horizontal',font =fonte1)
scale.pack(padx =0, pady=10)
tk.Button(fenetre, text ="Validez", command =ecrire, font =fonte1).pack(padx =0, pady=10)

fenetre.mainloop()

Il s'agit d'une bonne révision du traitement des fichiers TXT par Python mais aussi un rappel sur le traitement des dates.

Le rendu

J'ai déroulé la liste pour afficher le contenu du fichier "temperatures.txt".

Deuxième application : "appli_faune_marine"

Le dossier "appli_faune_marine" comprend plusieurs fichiers d'extension .py et une base de données SQLite nommée "animaux.db".
Le programme "parent" est "tkinter_menu.py" ; les autres scripts sont importés dans "tkinter_menu.py".

le script "tkinter_menu.py"

#-------------------fonctions-------------
def fonction1():
    import tkinter_lire_especes2
def fonction2():
    import tkinter_recherche_especes
def fonction3():
    messagebox.showinfo("Ajout espèces", "Fonctionnalité pas disponbile")
def fonction4():
     messagebox.showinfo("Ajout de taxons","Fonctionnalité pas disponible")
def fonction5():
    messagebox.showinfo("Imprimer fenêtre active","Fonctionnalité pas disponible")
def fonction6():
     reponse =messagebox.askquestion("Quitter application", "Voulez vous quitter application ? ")
     if reponse == "yes":
         quit()
#----------------------routine principale
# nom programme :  tkinter_menu.py
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox

fen = tk.Tk()
fen.title("Gestion de la BD 'animaux.db' ")
fen.geometry("600x400")
fen.resizable(False,False)
fen.config(bg ="skyblue")

ma_barre = tk.Menu(fen)
ma_barre.config(font=('Arial', 20))

menu1 = tk.Menu(ma_barre, tearoff=0)
menu1.add_command(label="Liste des espèces",command =fonction1)
menu1.add_command(label="Recherche d'une espèce", command =fonction2)
ma_barre.add_cascade(label="Requêtes sur la base", menu=menu1,)

menu2=tk.Menu(ma_barre,tearoff=0)
menu2.add_command(label="Ajouts d'espèces",command =fonction3)
menu2.add_command(label="Ajouts de taxons", command =fonction4)
ma_barre.add_cascade(label="Modifications de la base", menu=menu2)

menu3=tk.Menu(ma_barre,tearoff=0)
menu3.add_command(label="Imprimer fenêtre active ",command =fonction5)
menu3.add_command(label="Quitter application",command =fonction6)
ma_barre.add_cascade(label="Travaux divers", menu=menu3)

fen.config(menu=ma_barre)
fen.mainloop()

Les fonctions

import tkinter_lire_especes2: le script "tkinter_lire_especes2.py" exécute une requête paramétrée et affiche le résultat de la requête dans un Treeview.
import tkinter_recherche_especes : le script "tkinter_recherche_especes.py permet une recherche par nom ou taxon.
Retrouvez ces deux scripts dans le chapitre Le module tkinter.ttk.

Le dossier "appli_faune_marine"

La capture d'écran ci-dessus vous montre que Python a créé un sous dossier "_pycache_".
Ce dosssier est créé par Python pour stocker des fichiers d'extension .pyc qui sont des versions compilées des scripts importés afin d'accélérer leur exécution.

Le sous dossier "_py_cache"

On retrouve les versions compilées des scripts importés dans le programme parent.

Distribuer vos programmes

Vous voulez faire profiter les internautes de votre créativité en matière de développement Python.

Distribuer des applications locales aux "dégourdis"

Je désignerai désormais le premier public par le terme "dégourdis".

Concrétement

Les deux applications présentées ci-dessus n'utilisent que des modules faisant partie de la bibliothèque de base.

Pour "appli_faune_marine" c'est un peu plus délicat. En effet à la racine du dossier il y a trois fichiers d'extension .py.
À partir du dossier ouvert, il faut double cliquer sur "tkinter_menu.py" !

Télécharger les fichiers compressés

Distribution pour les novices

Pour ce type de public, il est préférable de leur adresser des "exécutables". Ainsi il n'auront rien à installer sur leur PC (sauf l'exécutable bien sûr).

Mais c'est quoi un exécutable et comment le créer ?

La bibliothèque "Pyinstaller"

C'est une bibliothèque Python externe qui permet de produire un programme "standalone" c'est à dire un programme qui fonctionne sans qu'il soit nécessaire d'installer Python sur le poste de celui qui va l'utiliser.
Une version "standalone" d'un programme comprend en plus du code, l'exécutable Python et les dépendances.
Concrétement vous obtennez un exécutable (fichier. exe).

Le développeur Python doit éventuellement installer l'extension "Pyinstaller" avec l'utilitaire pip.
Mais auparavant il vérifira si cette extension n'est pas déjà installée.

Rappels de commandes dans la console windows

pip list #afficher toutes les extensions Python installées.
pip install pyinstaller # installer la bibliothèque citée. 

Produire et publier un exécutable

Nous voulons créer un exécutable à partir du script "tkinter_calculatrice.py" qui est stocké dans "c:\python_interfaces"
Je rappelle que désormais ce programme "gère les exeptions" : voir début du chapitre.

Créer l'exécutable

Il n'y a que deux commandes à saisir dans la console windows :

 
cd c:\python_interfaces  #accès au dossier 
pyinstaller --onefile  tkinter_calculatrice.py #création d'un exécutable à partir du script cité

Il faut sélectionner le répertoire qui contient le programme source.
Attention l'exécution de la deuxième commande peut demander un certain temps ... Soyez patient.

Message final :
...
13137 INFO: Build complete! The results are available in: c:\python_interfaces\dist

Le système indique enfin que la fabrication du fichier EXE s'est réalisé avec succès et qu'il est disponible dans le sous dossier "dist".

Vérifions !
Ouvrons le dossier "C:\python_interfaces".
Nous observons l'existence d'un nouveau sous-dossier "dist" et à l'intérieur de ce répertoire un fichier d'extension .exe nommé "tkinter_calculatrice.exe" qui pèse plus de 10 MO. C'est l'inconvénient des exécutables : leur poids. Un exécutable est lourd car il inclut l'interpréteur Python et les dépendances nécessaires.

Tester cet exécutable avant diffusion

À partir de l'explorateur de fichiers double-cliquez sur le fichier .exe.
La console Python affiche une fenêtre tkinter :

Diffuser cet exécutable

Cher visiteur de mon site, il vous suffit de cliquer sur le lien ci-dessous pour "uploader" l'exécutable : Une calculatrice basique
Il vous suffira de le ranger dans un dossier puis à partir de l'explorateur de fichiers, de double cliquer dessus pour lancer l'application : la console python s'affiche puis la fenêtre tkinter.

Lors du "upload" il est fort possible que windows defender et votre programme anti-virus signale un danger ...
Je vous garantis que ce programme est sans virus.

Créer des exécutables à partir d'applications multifichiers

L'application "calculatrice", c'était un seul fichier.
Mais il n'en n'est pas de même pour les applications "appli_temperatures" et "appli_faune_marine). Ces applications correspondent à chaque fois à un dossier incluant plusieurs fichiers de nature différente (.py, .txt .db, etc. )

Exécutable pour "appli_temperatures"

Le dossier comprend : un script et un fichier TXT.

Commandes dans la console windows pour générer un exécutable
cd c:\appli_temperature # changer de répertoire 
pyinstaller --onefile --add-data "temperatures.txt;." tkinter_temperatures.py

La deuxième commmande est un peu complexe ; il faut inclure le fichier TXT avec l'option --add-data. Le point indique que le fichier TXT est dans le même dossier que le script.
Retournez à l'explorateur de fichiers : un sous dossier "dist" a été créé et contient l'exécutable.
Maintenant il faut déplacer le fichier texte dans le sous dossier "dist".

Test

Ouvrez le sous dossier "dist" puis double cliquez sur l'exécutable. La console Python affiche une fenêtre tkinter.
La liste "historique" vous indique que l'exécutable est bien relié au fichier texte.

Distribution

Sélectionnez les deux fichiers de "dist" puis compressez les (dans un ZIP).
Via un lien hypertexte proposez le fichier ZIP aux internautes.

Exécutable pour "appli_faune_marine"

Attention il y a plusieurs scripts dans l'application.
La procédure est identique à l'exemple précédent sinon qu'ici il faut inclure la base de données.

cd c:\appli_faune_marine
pyinstaller --onefile --add-data "animaux.db;." tkinter_menu.py

Il faut donc générer l'exécutable à partir du script principal.
Les autres scripts importés (dépendances) seront prises en compte pour la génération de l'éxécutable.

Le sous dossier "dist" a été créé avec l'exécutable.
Maintenant il faut déplacer la base "animaux.db" dans le répertoire "dist".

Test

Ouvrez le sous dossier "dist" puis double cliquez sur l'exécutable. Une fenêtre tkinter avec une barre de menus s'affiche !

Distribution

Sélectionnez les deux fichiers de "dist" puis compressez les.
Via un lien proposez le fichier compressé aux internautes.

Téléchargez les exécutables des deux applications

Vous n'avez pas besoin d'installer Python sur votre PC ; les exécutables en lien intègrent l'interpréteur Python.

Fichiers garantis sans virus :
Exécutable de l'application "températures"
Exécutable de l'application "faune marine"