Как я могу постоянно проверять, обновляется ли файл JSON в Python? - PullRequest
0 голосов
/ 10 января 2019

Я делаю часы, используя Tkinter и Python.

Чего я хочу добиться, так это запустить часы и автоматически проверить, был ли обновлен файл JSON (который содержит визуальные настройки часов). Если файл JSON был обновлен, часы обновятся в режиме реального времени.

Однако это не так. Что мне нужно сделать, это обновить файл JSON, закрыть программу часов, а затем снова открыть программу часов. Только в этом случае измененные настройки JSON будут иметь место.

clock.py

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()

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']

settings.json

{
    "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"
    }
  }

Желаемый эффект:

Если я изменю цвет фона в своем файле JSON и сохраню его, мои часы смогут обновлять его цвет во время выполнения.

Перед изменением цвета фона

После изменения цвета фона

Что я пробовал:

  • Повторный импорт модуля settings.py с помощью importlib.reload (), но модуль settings.py не является допустимым аргументом.
  • Открываем settings.json, читаем из него, закрываем и снова открываем. Однако после закрытия файла его невозможно открыть снова.

Ответы [ 3 ]

0 голосов
/ 10 января 2019

У вас есть несколько вариантов. 1 - это вариант, предложенный Бенасом. Каждую секунду или около того проверяйте время последнего изменения файла json, чтобы увидеть, изменился ли он.

Другой вариант - использовать пакет, например PyiNotify . Этот пакет может идентифицировать события, которые происходят в файлах и папках. 1 таким событием является событие IN_MODIFY, которое срабатывает, когда файл был изменен. Список событий можно найти здесь .

Во-первых, вам нужно установить пакет с помощью pip:

pip install pyinotify

Ниже приведен пример кода (почти полностью из их документации)

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
0 голосов
/ 10 января 2019

Ваш settings модуль по сути является модулем констант, что делает его довольно сложным для повторного использования. В то время как вы могли бы сделать что-то хакерское, чтобы заставить reload, как:

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

это будет и неэффективно, и ужасно неправильно использует механизм импорта (теперь вместо чтения одного файла вы читаете два, модуль и JSON, от которых он зависит).

Вместо этого я бы порекомендовал сделать settings несколько более многократно используемыми с функцией, которая может считывать данные и кэшировать их, а не с кучей глобальных переменных:

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']

Теперь ваш clock модуль может импортировать settings, создавать Settings объект заранее и при каждом tick вызове update_settings(). Если update_settings возвращает True, также следует повторно применить конфигурацию. Код должен был бы квалифицировать различные имена, поэтому вместо того, чтобы просто сказать date_color, вы должны поставить:

mysettings = Settings()

на верхнем уровне и обратитесь к date_color с:

mysettings.date_color

но это небольшая цена за улучшение кода.

0 голосов
/ 10 января 2019

Насколько серьезен этот код? Вы можете просто проверить время последнего изменения файла через некоторый интервал с помощью os.stat(path_to_file).st_mtime и обновить интерфейс, если он позже, чем в прошлый раз, когда вы проверяли. Быстро и грязно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...