From d4d8287877e4f35df83f6bcf636ab631ceae3dbc Mon Sep 17 00:00:00 2001 From: Noiredd Date: Wed, 20 May 2020 17:52:04 +0200 Subject: [PATCH] a system for storing program options --- filmatyk/gui.py | 20 ++++++++++++++++--- filmatyk/options.py | 47 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 filmatyk/options.py diff --git a/filmatyk/gui.py b/filmatyk/gui.py index a2458e5..da1e787 100644 --- a/filmatyk/gui.py +++ b/filmatyk/gui.py @@ -9,6 +9,7 @@ import filters from database import Database from filmweb import FilmwebAPI +from options import Options from presenter import Presenter from updater import Updater from userdata import DataManager, UserData @@ -81,6 +82,7 @@ def __construct(self): tk.Button(master=cw, text='Zaloguj', command=self._loginClick).grid(row=4, column=1, sticky=tk.W) tk.Button(master=cw, text='Anuluj', command=self._cancelClick).grid(row=4, column=0, sticky=tk.E) self.window.withdraw() + def centerWindow(self): self.window.update() ws = self.window.winfo_screenwidth() @@ -96,6 +98,7 @@ def _setStateBad(self, event=None, connection=False): message = self.conerr_message if connection else self.logerr_message self.infoLabel['text'] = message self.stateGood = False + def _setStateGood(self, event=None): if not self.stateGood: self.infoLabel['text'] = '' @@ -103,6 +106,7 @@ def _setStateGood(self, event=None): #also, maybe the user has hit enter key, meaning to log in if event.keysym == 'Return': self._loginClick() + def _loginClick(self): #collect login data username = self.usernameEntry.get() @@ -120,6 +124,7 @@ def _loginClick(self): self.username = username self.isDone.set(True) self.window.withdraw() + def _cancelClick(self): self.passwordEntry.delete(0, tk.END) self.usernameEntry.delete(0, tk.END) @@ -127,6 +132,7 @@ def _cancelClick(self): self.isDone.set(True) self.window.withdraw() + class Main(object): filename = 'filmatyk.dat' # will be created in user documents/home directory wintitle = '{}Filmatyk' # format with debug flag @@ -136,6 +142,11 @@ def __init__(self, debugMode=False, isOnLinux=False): self.isOnLinux = isOnLinux self.root = root = tk.Tk() root.title(self.wintitle.format('[DEBUG] ' if self.debugMode else '')) + # load the savefile + self.dataManager = DataManager(self.getFilename(), VERSION) + userdata = self.dataManager.load() + # create the options manager + self.options = Options() # construct the window: first the notebook for tabbed view self.notebook = ttk.Notebook(root) self.notebook.grid(row=0, column=0, padx=5, pady=5, sticky=tk.NW) @@ -158,9 +169,6 @@ def __init__(self, debugMode=False, isOnLinux=False): self.abortUpdate = False # database can set this via callback when something goes wrong self.databases = [] self.presenters = [] - # load the savefile - self.dataManager = DataManager(self.getFilename(), VERSION) - userdata = self.dataManager.load() # instantiate Presenters and Databases self.api = FilmwebAPI(self.loginHandler.requestLogin, userdata.username) movieDatabase = Database.restoreFromString('Movie', userdata.movies_data, self.api, self._setProgress) @@ -221,6 +229,7 @@ def setStyle(self): 'configure': {'width': 20, 'anchor': 'center'} } }) + def centerWindow(self): self.root.update() ws = self.root.winfo_screenwidth() @@ -238,6 +247,7 @@ def getFilename(self): subpath = self.filename userdir = str(Path.home()) return os.path.join(userdir, subpath) + def saveUserData(self): # if for any reason the first update hasn't commenced - don't save anything if self.api.username is None: @@ -278,6 +288,7 @@ def _setProgress(self, value:int, abort:bool=False): self.progressbar.grid() self.progressVar.set(value) self.root.update() + def _updateData(self): # call softUpdate on all the databases and update all the presenters for db, ps in zip(self.databases, self.presenters): @@ -288,6 +299,7 @@ def _updateData(self): break # save data self.saveUserData() + def _reloadData(self): for db, ps in zip(self.databases, self.presenters): db.hardUpdate() @@ -295,6 +307,7 @@ def _reloadData(self): if self.abortUpdate: break self.saveUserData() + def _quit(self, restart=False): self.saveUserData() self.root.quit() @@ -312,6 +325,7 @@ def _quit(self, restart=False): command += " debug" os.system(command) + if __name__ == "__main__": debugMode = "debug" in sys.argv isOnLinux = "linux" in sys.argv diff --git a/filmatyk/options.py b/filmatyk/options.py new file mode 100644 index 0000000..6182236 --- /dev/null +++ b/filmatyk/options.py @@ -0,0 +1,47 @@ +import json +import os + +import tkinter as tk +from tkinter import ttk + + +class Options(): + """Stores program options as named Tk variables, allowing easy serialization. + + Options wraps around a simple dict of Tk vars, which enables the following: + * easy access to option values (using get), + * simple binding of option variables to Tk widgets (using variable), + * serialization and deserialization to JSON (using storeToString). + Defining a new option is done simply by adding it to the prototypes list. + """ + option_prototypes = [ + ] + + def __init__(self, json_string:str='{}'): + self.variables = {} + self.isDirty = False + saved_values = json.loads(json_string) + for name, vtype, default in self.option_prototypes: + variable = vtype() + value = saved_values[name] if name in saved_values.keys() else default + variable.set(value) + variable.trace('w', self.__touched_callback) + self.variables[name] = variable + + def storeToString(self): + """Serialize the options to a JSON string.""" + return json.dumps({ + name: variable.get() for name, variable in self.variables.items() + }) + + def get(self, name): + """Get the value of a named option.""" + return self.variables[name].get() + + def variable(self, name): + """Get Tk variable object of a named option.""" + return self.variables[name] + + def __touched_callback(self, *args): + """Set the dirty flag whenever an option changes.""" + self.isDirty = True