2
votes

Comment puis-je vérifier en permanence si un fichier JSON est mis à jour en Python?

Je crée une horloge en utilisant Tkinter et Python.

Ce que je veux, c'est faire fonctionner l'horloge et vérifier automatiquement si le fichier JSON (qui contient les paramètres visuels de l'horloge) a été mis à jour. Si le fichier JSON a été mis à jour, l'horloge sera mise à jour en direct.

Cependant, ce n'est pas le cas. Ce que je dois faire, c'est mettre à jour le fichier JSON, fermer le programme d'horloge, puis rouvrir le programme d'horloge. Ce n'est que dans ce cas que les paramètres JSON modifiés seront appliqués.

clock.py

{
    "window": {
      "full_screen": false,
      "window_title" : "chronoberry"
    },
    "background": {
      "color": "black"
    },
    "time": {
      "font": "arial",
      "size": 70,
      "weight": "bold",
      "slant": "roman",
      "color": "white",
      "format": "%-I:%M:%S %p"
    },
    "date": {
      "font": "arial",
      "size": 20,
      "weight": "normal",
      "slant": "roman",
      "color": "white",
      "format": "%A, %B %-d %Y"
    }
  }

settings.py

import os
import json

with open('settings.json') as json_settings:
    settings = json.load(json_settings)

    # Window
    full_screen = settings['window']['full_screen']
    window_title = settings['window']['window_title']

    # Background
    background_color = settings['background']['color']

    # Time
    time_font = settings['time']['font']
    time_size = settings['time']['size']
    time_weight = settings['time']['weight'] 
    time_slant = settings['time']['slant']
    time_color = settings['time']['color']    
    time_format = settings['time']['format']

    # Date
    date_font = settings['date']['font']
    date_size = settings['date']['size']
    date_weight = settings['date']['weight']
    date_slant = settings['date']['slant']
    date_color = settings['date']['color']
    date_format = settings['date']['format']

from tkinter import *
from datetime import datetime
from settings import *

# Updates the program and clock settings
def tick():
    time_string = datetime.now().strftime(time_format)
    date_string = datetime.now().strftime(date_format)

    root.config(bg=background_color)

    container.configure(bg=background_color)

    current_time.configure(text=time_string,
                           font=(time_font, time_size, time_weight, time_slant),
                           fg=time_color,
                           bg=background_color)

    current_date.configure(text=date_string,
                           font=(date_font, date_size, date_weight, date_slant),
                           fg=date_color,
                           bg=background_color)

    current_time.after(1, tick)


# TKInterface
root = Tk()
root.title(window_title)

# Binds 'Esc' key to exit program
root.bind('<Escape>', exit)

# Runs program in full-screen
if full_screen:
    root.attributes('-fullscreen', True)
    root.config(cursor='none')

# Creates container to hold time and date
container = Frame(root)
current_time = Label(container)
current_date = Label(container)

container.pack(expand=True)
current_time.pack()
current_date.pack()

tick()
root.mainloop()

Effet souhaité:

Si je change la couleur d'arrière-plan de mon fichier JSON et que je l'enregistre, mon horloge devrait pouvoir mettre à jour sa couleur pendant l'exécution.

Avant de changer l'arrière-plan color

Après avoir changé la couleur d'arrière-plan

Ce que j'ai essayé:

  • Réimportation du module settings.py à l'aide de importlib.reload (), mais le module settings.py n'est pas un argument valide.
  • Ouverture de settings.json, lecture de celui-ci, fermeture, puis ouverture à nouveau. Cependant, une fois qu'un fichier est fermé, il est impossible de le rouvrir.

  • 2 commentaires

    une fois qu'un fichier est fermé, il est impossible de l'ouvrir à nouveau Non, ce n'est pas le cas. Pourquoi dites vous cela? Obtenez-vous une erreur lorsque vous essayez?


    Savez-vous que after (1, ...) s'exécute après 1 milliseconde, pas une seconde? Étant donné que votre horloge n'a qu'une seconde de résolution, vous faites travailler le processeur beaucoup plus que nécessaire.


    3 Réponses :


    0
    votes

    Quelle est la gravité de ce code? Vous pouvez simplement vérifier la dernière heure de modification du fichier à un certain intervalle avec os.stat (path_to_file) .st_mtime et actualiser votre interface si elle est postérieure à la dernière vérification. Rapide et sale.


    0 commentaires

    0
    votes

    Vous avez plusieurs options. 1 est l'option que benas a suggérée. Toutes les secondes environ, vérifiez la dernière heure de modification du fichier json pour voir si elle a changé.

    Une autre option consiste à utiliser un package tel que PyiNotify . Ce package peut identifier les événements qui se produisent sur les fichiers et les dossiers. Un de ces événements est l'événement IN_MODIFY qui se déclenche lorsqu'un fichier a été modifié. Une liste d'événements est disponible ici .

    Tout d'abord, vous devez installer le package en utilisant pip:

    import pyinotify
    
    # The watch manager stores the watches and provides operations on watches
    wm = pyinotify.WatchManager()
    wm.add_watch('/path/to/file', mask, rec=True)
    
    mask = pyinotify.IN_MODIFY  # watched events
    
    class EventHandler(pyinotify.ProcessEvent):
        def process_IN_MODIFY(self, event):
            print "Modifying:", event.pathname
    

    Ce qui suit est un exemple de code (presque entièrement tiré de leur documentation)

    pip install pyinotify
    


    1 commentaires

    Je crois comprendre que pyinotify n'est pas bien pris en charge (aucun changement depuis des années). La plupart des gens semblent recommander inotify .



    2
    votes

    Votre module settings est essentiellement un module de constantes, ce qui le rend assez difficile à réutiliser. Bien que vous puissiez faire quelque chose de piraté pour forcer le recharger comme:

    mysettings.date_color
    

    , cela va être à la fois inefficace et une mauvaise utilisation du mécanisme d'importation (maintenant au lieu d'en lire un fichier, vous en lisez deux, le module et le JSON dont il dépend).

    Au lieu de cela, je recommanderais de rendre les paramètres un peu plus réutilisables avec une fonction qui peut lire les données et les mettre en cache, plutôt qu'un tas de globaux:

    mysettings = Settings()
    

    Maintenant, votre module clock peut importer des paramètres , Construisez un objet Settings à l'avant, et à chaque tick appelez update_settings () dessus. Si update_settings renvoie True , il doit également réappliquer la configuration. Le code aurait besoin de qualifier les différents noms, donc au lieu de simplement dire date_color , vous mettriez:

    import os
    import json
    
    class Settings:
        SETTINGS_FILE = 'settings.json'
    
        def __init__(self):
            self._load_settings()
    
        def update_settings(self):
            if self._last_update != os.stat(self.SETTINGS_FILE).st_mtime:
                self._load_settings()
                return True
            return False
    
        def _load_settings(self):
            with open(self.SETTINGS_FILE) as json_settings:
                settings = json.load(json_settings)
                self._last_update = os.fstat(json_settings.fileno()).st_mtime
    
                # Window
                self.full_screen = settings['window']['full_screen']
                self.window_title = settings['window']['window_title']
    
                # Background
                self.background_color = settings['background']['color']
    
                # Time
                self.time_font = settings['time']['font']
                self.time_size = settings['time']['size']
                self.time_weight = settings['time']['weight'] 
                self.time_slant = settings['time']['slant']
                self.time_color = settings['time']['color']    
                self.time_format = settings['time']['format']
    
                # Date
                self.date_font = settings['date']['font']
                self.date_size = settings['date']['size']
                self.date_weight = settings['date']['weight']
                self.date_slant = settings['date']['slant']
                self.date_color = settings['date']['color']
                self.date_format = settings['date']['format']
    

    au niveau supérieur, et se référer à date_color avec:

    def tick():
        import settings, importlib
        importlib.reload(settings)
        from settings import *
        # ... rest of tick code ...
    

    mais c'est un petit prix à payer pour améliorer le code.


    4 commentaires

    Pour mémoire, je conviens que les interfaces inotify seraient une amélioration ici et devraient être prises en compte, mais elles (IMO) sont trop avancées pour votre niveau de compétence en ce moment.


    C'est une approche élégante de mon problème! J'apprécie le temps que vous avez pris pour expliquer cela à un programmeur novice. Merci!


    Pour ma compréhension de base, c'est ce que fait le fichier settings.py modifié: Lors de la création d'un objet Settings (), la méthode _load_settings () est appelée. Ici, la dernière heure de modification enregistrée du fichier JSON est stockée à l'intérieur de _last_update.


    @leecharles_: Ouais. Ensuite, chaque fois que vous appelez update_settings , il vérifie si le fichier JSON a été modifié depuis son dernier chargement, et si tel est le cas, le recharge et renvoie True (afin que vous sachiez à reconfigurer avec les nouveaux paramètres), sinon il renvoie False (vous savez donc que vous pouvez simplement mettre à jour l'heure et laisser la configuration Tk inchangée)