From 24f3763a27bbd2738d552b0e32c1accce7d4742b Mon Sep 17 00:00:00 2001 From: Pandaroux007 Date: Sun, 8 Sep 2024 12:32:09 +0200 Subject: [PATCH] =?UTF-8?q?Mise=20=C3=A0=20jour=20du=20fichier=20README.md?= =?UTF-8?q?,=20correction=20de=20la=20liste=20des=20d=C3=A9pendances=20(et?= =?UTF-8?q?=20donc=20du=20fichier=20requirements.txt).=20Correction=20du?= =?UTF-8?q?=20bug=20qui=20emp=C3=AAchait=20l'application=20de=20d=C3=A9mar?= =?UTF-8?q?rer=20gr=C3=A2ce=20au=20d=C3=A9bugger=20python=20de=20VScode=20?= =?UTF-8?q?-=20c'=C3=A9tait=20d=C3=BB=20=C3=A0=20un=20oublie=20apr=C3=A8s?= =?UTF-8?q?=20un=20test=20pour=20ajouter=20une=20ic=C3=B4ne=20=C3=A0=20une?= =?UTF-8?q?=20entr=C3=A9e=20du=20menu=20'Source'.=20Ajout=20d'un=20syst?= =?UTF-8?q?=C3=A8me=20de=20v=C3=A9rification=20des=20mises=20=C3=A0=20jour?= =?UTF-8?q?=20dans=20la=20fen=C3=AAtre=20'A=20propos'=20(fichier=20Fenetre?= =?UTF-8?q?Info.py)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +-- README.md | 64 +++++++++++++++++++++++++------------------ requirements.txt | 1 - src/Application.py | 12 ++++---- src/Definitions.py | 11 ++++---- src/FenetreInfo.py | 52 ++++++++++++++++++++++++++++------- src/FenetreLicence.py | 1 + src/runApp.py | 4 +-- 8 files changed, 93 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index 3aa74c0..9006b43 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,4 @@ runApp.onefile-build # dossier contenant des éléments de développement dev # dossier d'environnement python au cas où -gotimeEnv -# fichier contenant les screenshots de l'appli -screenshots/ \ No newline at end of file +gotimeEnv \ No newline at end of file diff --git a/README.md b/README.md index 9b3efd2..7193d96 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ ___ 5. Merci à ma professeur d'anglais, qui a lancer l'idée et qui m'a fait confiance pour réaliser ce projet; 6. Merci à tous les développeurs des modules utilisés ici pour leurs travaux et leurs contributions à la communauté open-source; 7. Enfin, merci à tous les créateurs de contenus techniques sur internet qui m'ont permis de trouver de la documentation pour chaque éléments des modules. - +___ # Apparence globale ## Interface ### Barre de Menu @@ -146,7 +146,7 @@ Comme indiqué plus haut, sur la fenêtre se trouve un bouton permettant de "dé une nouvelle fenêtre avec affiché seulement le temps restant, sur un fond de couleur. Cette fenêtre à l'avantage d'être beaucoup plus visible que l'affichage de l'application, mais aussi le fait qu'elle reste toujours au premier plan (même si vous cliquez à côté, elle restera apparente). - +___ # Développement ## Fonctionnement des versions > **Base : *x.y.z*** @@ -175,7 +175,7 @@ Voici une petite liste non exhaustive des futurs améliorations. - [ ] Si un jour, une fois l'application terminée completement avec toutes les améliorations présentées ci-haut, j'ai envie de perdre mon temps, je passerai probablement sur une version 4 cross-platform, avec wxWidgets (**wxPython**) ou Toga (?). Mais comme cela nécessite de refaire toute l'interface, je ne le ferai probablement pas avant longtemps (j'en profiterai pour changer quelques détails pour rendre l'application plus conviviale). -- [ ] Terminer la section "À propos" du menu "Source" +- [x] Terminer la section "À propos" du menu "Source" ## Installation des dépendances Attention, **certains des modules utilisés par le projet ne sont pas inclus par défauts dans python**. Pour les installer, il vous suffit de @@ -184,24 +184,30 @@ lancer la commande suivante (après vous être déplacé dans le répertoire du pip install -r requirements.txt ``` Cette commande installera les modules listés dans le fichier *requirements.txt*, utilisés par l'application mais qui ne sont pas inclue par défaut dans python3. Tous les modules utilisés par l'application sont listé ci-dessous : -1. tkinter -2. datetime -3. webbrowser -4. sys -5. subprocess -6. **darkdetect** -7. **PIL (pillow)** -8. os -9. json -10. platform -11. getpass -12. socket -13. **pygame** +1. `tkinter`, `ttk`, `messagebox` +2. `datetime` +3. `webbrowser` +4. `sys` +5. `subprocess` +6. **`darkdetect`** +7. `os` +8. `json` +9. `platform` +10. `getpass` +11. `socket` +12. **`pygame`** +13. `packaging` +14. `re` +15. `urllib` +16. `threading` + +> [!CAUTION] +> Notez qu'il peut y avoir des problème lors de l'utilisation de `darkdetect` sous Windows avec le fichier `setup.py`. Je réfléchie à placer directement le code du module dans [src](src/), je dois encore regarder les permissions accordées par la licence *BSD License*. ## Fonctionnement GoTime fonctionne avec la fonction `after` de `tkinter`. L'affichage de l'heure fonctionne de cette manière et le minuteur également. Cette méthode permet d'appeller une fonction un certain temps plus tard, temps défini en ms. Pour modifier chaque seconde l'heure, par exemple, -on utilise cette commande (l.130) +on utilise cette commande (l.138) ```py self.after(1000, self.update_time) # Met à jour toutes les secondes ``` @@ -210,16 +216,11 @@ Pour ce qui est des paramètres, l'application fonctionne grâce à une lecture/ Tout le reste de l'application n'est qu'une question d'apparence et de widgets, la base fonctionne comme ceci. ## Bugs -Il y a deux bug actuellement découverts. -1. Bien entendu, le bug des antivirus sur Windows persiste et c'est l'un des plus gros problèmes de l'application. Cependant il est aussi très simple à contourner : il suffit de désactiver son antivirus le temps d'installer le logiciel et le réactiver ensuite (ce qui prouve leur inutilité sur ce point). Je conseille d'utiliser la fonction "disable for 10mn" pour ne pas oublier ensuite de le réactiver. +Il y a un bug actuellement découvert. +1. Le bug des antivirus sur Windows persiste et c'est l'un des plus gros problèmes de l'application. Cependant il est aussi très simple à contourner : il suffit de désactiver son antivirus le temps d'installer le logiciel et le réactiver ensuite (ce qui prouve leur inutilité sur ce point). Je conseille d'utiliser la fonction "disable for 10mn" pour ne pas oublier ensuite de le réactiver. -2. Actuellement l'application ne démarre pas (enfin la fenêtre commence à s'ouvrir - elle est juste noire, il n'y a aucun widget dedant - avec une boite de dialogue indiquant le message d'erreur). Le message d'erreur indique que le fichier [icon.png](dep/icon.png) n'existe pas (Cette erreur n'était jamais apparue avant que je change le système de paramètres dans la branche git "correctionParam"). Voici le message : -```txt -Hmm...Quelque chose semble s'être mal passé... -L'erreur est : image "/home/pandaroux007/Bureau/Programmation/Projets python/gui/GoTime/dep/icon.png" doesn't exist -``` - -## Compilation +## Compilation & distribution +### Compilation avec Nuitka Pour compiler et distribuer l'application, j'utilise [`nuitka`](https://github.com/Nuitka/Nuitka), avec cette commande : ```sh python3 -m nuitka --run --onefile --output-filename="GoTime" --windows-console-mode=disable --follow-imports --enable-plugin=tk-inter --nofollow-import-to=pygame.tests --linux-icon="dep/icon.ico" --macos-app-icon="dep/icon.ico" --windows-icon-from-ico="dep/icon.ico" runApp.py @@ -232,4 +233,13 @@ python3 -m nuitka --run --onefile --output-filename="GoTime" --windows-console-m **EDIT** : Après tests, il se trouve que l'ajout de `--mingw64` lors de la compilation sous Windows ne change rien, l'application et son installation se font toujours flaguer. Je conseille donc à ceux qui installent l'application ou qui la compile **de désactiver pour quelques minutes leurs antivirus**. > [!NOTE] -> Installez nuitka avec `pip install nuitka`, puis installez l'utilitaire de compression d'exécutables si il n'est pas installé (`pip install zstandard`), configurez le cache des fichiers `C` (ccache), puis lancez la commande indiquez plus haut. \ No newline at end of file +> Installez nuitka avec `pip install nuitka`, puis installez l'utilitaire de compression d'exécutables si il n'est pas installé (`pip install zstandard`), configurez le cache des fichiers `C` (ccache), puis lancez la commande indiquez plus haut. + +### Distribution du logiciel +Comme indiqué dans [le chapitre installation](#installation), l'application est distribuée de trois façon: +#### Sous Linux +- Utilisation de scripts shell bash ([install.sh](install.sh) et [uninstall.sh](#désinstallation)) +#### Sous Microsoft Windows +- Utilisation d'un installateur créé *via* `Inno Setup` +#### Sous MacOS-X +- Pas de programme compilé ni de script d'installation : les utilisateurs de machines Apple devrons utiliser la version python directement pendant une durée indéterminée. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 928ecd9..f70c005 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -pillow darkdetect pygame \ No newline at end of file diff --git a/src/Application.py b/src/Application.py index 5a20a0d..ceccdd7 100644 --- a/src/Application.py +++ b/src/Application.py @@ -4,10 +4,10 @@ from tkinter import messagebox # pour les erreur et les validations import darkdetect # pour la détection du thème de l'OS # ------------------------ fichiers de l'application -from Definitions import * # fichier contenant toutes les variables +from Definitions import * from FenetreLicence import FenetreLicence -from FenetreParametres import FenetreParametres # fichier contenant la fenêtre d'affichage des paramètres -from FenetreInfo import FenetreInfo # fichier contenant la fenêtre d'affichage du lien du dépôt GitHub +from FenetreParametres import FenetreParametres +from FenetreInfo import FenetreInfo class Application(tk.Tk): def __init__(self): @@ -32,7 +32,7 @@ def __init__(self): barre_de_menu = tk.Menu(self) # ------------------------ Création d'un menu 'Fenêtre' fenetre_menu = tk.Menu(barre_de_menu, tearoff=0) - fenetre_menu.add_command(label="Paramètres", command=lambda: FenetreParametres()) + fenetre_menu.add_command(label="Paramètres", command=lambda: FenetreParametres(callback_theme=self.gestion_theme_par_defaut)) fenetre_menu.add_separator() fenetre_menu.add_command(label="Quitter", command=self.quit) barre_de_menu.add_cascade(label="Fenêtre", menu=fenetre_menu) @@ -43,11 +43,9 @@ def __init__(self): barre_de_menu.add_cascade(label="Commandes", menu=commandes_menu) # ------------------------ Création d'un troisième menu 'Source' source_menu = tk.Menu(barre_de_menu, tearoff=0) - # source_menu.add_command(label="Ouvrir GitHub", command=lambda: webbrowser.open_new_tab(lien_du_github)) - # source_menu.add_command(label="Afficher GitHub", command=lambda: FenetreInfoAffichageLienGitHub()) source_menu.add_command(label="Afficher LICENCE", command=lambda: FenetreLicence()) source_menu.add_command(label="Signaler un bug", command=lambda: webbrowser.open_new_tab(lien_du_github + "/issues")) - source_menu.add_command(label="À propos", image=chemin_image_application, command=lambda: FenetreInfo()) + source_menu.add_command(label="À propos", command=lambda: FenetreInfo()) barre_de_menu.add_cascade(label="Source", menu=source_menu) # ------------------------ Ajout de la barre de menu à la fenêtre self.config(menu=barre_de_menu) diff --git a/src/Definitions.py b/src/Definitions.py index 10ff1b7..ed09012 100644 --- a/src/Definitions.py +++ b/src/Definitions.py @@ -8,7 +8,7 @@ # https://github.com/Nuitka/Nuitka/issues/1737#issuecomment-1224488673 # https://stackoverflow.com/questions/59427353/how-to-get-the-current-path-of-compiled-binary-from-python-using-nuitka repertoire_courant = os.path.dirname(os.path.abspath(os.path.realpath(sys.argv[0]))) -""" # variables définie par nuitka lors de la compilation (UNIQUEMENT Nuitka, pas un autre compilateur!) +""" # chemins lors de la compilation chemin_fichier_parametres = os.path.join(repertoire_courant, "dep", "settings.json") chemin_fichier_logs = os.path.join(repertoire_courant, 'log', "error_log.csv") chemin_image_application = os.path.join(repertoire_courant, 'dep', 'icon.png') @@ -18,7 +18,6 @@ chemin_fichier_parametres = os.path.join(os.path.dirname(repertoire_courant), "dep", "settings.json") chemin_fichier_logs = os.path.join(os.path.dirname(repertoire_courant), 'log', "error_log.csv") chemin_image_application = os.path.join(os.path.dirname(repertoire_courant), 'dep', 'icon.png') -chemin_image_checkmark = os.path.join(os.path.dirname(repertoire_courant), 'dep', 'checkmark.png') chemin_fichier_wav_fin_temps = os.path.join(os.path.dirname(repertoire_courant), 'sons', 'digital-clock-alarm.wav') chemin_fichier_licence = os.path.join(os.path.dirname(repertoire_courant), "LICENCE.txt") @@ -77,7 +76,7 @@ def jouer_sonnerie(etat_jouer_son): sonnerie_actuelle = mixer.Sound(chemin_fichier_wav_fin_temps) if etat_jouer_son: sonnerie_actuelle.play() else: sonnerie_actuelle.stop() - except Exception as e: log_error(f"Exception dans la fonction jouer_sonnerie : {e}"); pass + except Exception as e: log_error(f"Exception dans la fonction jouer_sonnerie : {str(e)}"); pass # ------------------------ Variables utiles systeme_exploitation = system() @@ -89,7 +88,7 @@ def jouer_sonnerie(etat_jouer_son): temps_max = 10800 # 3 heures en secondes # ------------------------ Infos App -lien_du_github = "https://github.com/pandaroux007/GoTime" nom_application = "GoTime" -version_application = "1.0.3-bêta" -developpeur_application = "Pandaroux007" \ No newline at end of file +version_application = "1.1.0-bêta" +developpeur_application = "pandaroux007" +lien_du_github = "https://github.com/" + developpeur_application + "/" + nom_application \ No newline at end of file diff --git a/src/FenetreInfo.py b/src/FenetreInfo.py index 71a8312..7be895d 100644 --- a/src/FenetreInfo.py +++ b/src/FenetreInfo.py @@ -1,6 +1,10 @@ import tkinter as tk from tkinter import messagebox import webbrowser +from threading import Thread +from urllib import request +from packaging import version +import re # ------------------------ fichiers de l'application from Definitions import * @@ -13,8 +17,9 @@ def __init__(self, master, text, url, *args, **kwargs): self.bind("", lambda action: self.config(font=("TkDefaultFont", 10))) class FenetreInfo(tk.Toplevel): - def __init__(self, parent): - super().__init__(parent) + def __init__(self): + super().__init__() + # ------------------------ Parametrge fenêtre self.title(f"À propos de {nom_application}") self.geometry("400x300") self.resizable(False, False) @@ -29,14 +34,41 @@ def __init__(self, parent): # ------------------------ lien du GitHub LienHypertexte(self.frame_infos, text="GitHub du projet", url=lien_du_github).pack(pady=10) # ------------------------ Frame pour les boutons - button_frame = tk.Frame(self.frame_infos) - button_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=(20, 0)) + self.frame_boutons = tk.Frame(self.frame_infos) + self.frame_boutons.pack(side=tk.BOTTOM, fill=tk.X, pady=(20, 0)) # ------------------------ Bouton verif les mises à jours - update_button = tk.Button(button_frame, text="Vérifier les mises à jour maintenant", command=self.check_updates) - update_button.pack(side=tk.LEFT, expand=True) + self.bouton_verif_mises_a_jour = tk.Button(self.frame_boutons, text="Vérifier les mises à jour maintenant", command=self.lancer_verif_mise_a_jour) + self.bouton_verif_mises_a_jour.pack(side=tk.LEFT, expand=True) # ------------------------ Bouton "OK" - ok_button = tk.Button(button_frame, text="OK", command=self.destroy) - ok_button.pack(side=tk.RIGHT, expand=True) + self.bouton_ok = tk.Button(self.frame_boutons, text="OK", command=self.destroy) + self.bouton_ok.pack(side=tk.RIGHT, expand=True) - def check_updates(self): - messagebox.showwarning("Mise à jour", "Cette partie de l'application est encore en développement!") # "Vérification des mises à jour en cours..." \ No newline at end of file + def lancer_verif_mise_a_jour(self): + self.bouton_verif_mises_a_jour.config(state="disabled") + Thread(target=self.verif_mise_a_jour, daemon=True).start() + + # utilisation de self.after(0, ...) pour modifier la gui depuis le thread car tkinter n'est pas thread-safe + def verif_mise_a_jour(self): + try: + # ------------------------ Requête à l'api de github pour obtenir la dernière version + url = "https://api.github.com/repos/" + developpeur_application + "/" + nom_application + "/releases/latest" + with request.urlopen(url) as response: + data = json.loads(response.read().decode()) + derniere_version_app_sur_github = data['tag_name'] + # ------------------------ enlever les éléments inutiles dans le numéro de version + version_app_local = re.sub(r"[^\d.]", "", version_application) + version_app_github = re.sub(r"[^\d.]", "", derniere_version_app_sur_github) + # ------------------------ comparer les deux versions + if version.parse(version_app_local) < version.parse(version_app_github): + def afficher_message_mise_a_jour_dispo(): + reponse = messagebox.askyesno( + title="Mise à jour disponible", icon="info", + message=f"Une nouvelle version {version_app_github} de {nom_application} est disponible!\n\nVoulez-vous ouvrir la page de téléchargement ?") + if reponse == tk.YES: webbrowser.open_new_tab(url=lien_du_github + "/releases/latest") + self.after(0, afficher_message_mise_a_jour_dispo) + else: self.after(0, lambda: messagebox.showinfo(title="Aucune mise à jour disponible", message=f"Aucune nouvelle version de {nom_application} n'est disponible!")) + # ------------------------ Si une erreur est levée afficher un message d'avertissement + except Exception as e: + self.after(0, lambda: messagebox.showwarning(title="Avertissement", message=f"Erreur lors de la vérification des mises à jour!\n{e}")) + # ------------------------ réactiver le bouton de verif des mises à jours + finally: self.after(0, lambda: self.bouton_verif_mises_a_jour.config(state="normal")) \ No newline at end of file diff --git a/src/FenetreLicence.py b/src/FenetreLicence.py index 113c079..42f4073 100644 --- a/src/FenetreLicence.py +++ b/src/FenetreLicence.py @@ -1,5 +1,6 @@ import tkinter as tk from tkinter import messagebox +# ------------------------ fichiers de l'application from Definitions import * class FenetreLicence(tk.Toplevel): diff --git a/src/runApp.py b/src/runApp.py index 63ec3ed..f2d51b6 100644 --- a/src/runApp.py +++ b/src/runApp.py @@ -9,6 +9,6 @@ root = Application() root.mainloop() except Exception as e: - messagebox.showerror(title=f"Erreur", message=f"Hmm...Quelque chose semble s'être mal passé...\nL'erreur est : {e}") - log_error(e) + messagebox.showerror(title=f"Erreur", message=f"Hmm...Quelque chose semble s'être mal passé...\nL'erreur est : {str(e)}") + log_error(str(e)) exit() \ No newline at end of file