From 09e12b5312459088aa881538dfdbe22027cafe7b Mon Sep 17 00:00:00 2001 From: Noiredd Date: Thu, 21 May 2020 20:03:37 +0200 Subject: [PATCH] options as a module-level singleton This is more convenient when used in different modules, as we don't have to find hacky ways to pass a reference to the instance held by main, we just import the module and voila. Of course, the module instance has to be initialized first, but 1) this shouldn't be an issue as Main will do that among the first things, 2) get & var calls are guarded against uninitialized access, just in case. --- filmatyk/gui.py | 8 ++++---- filmatyk/options.py | 38 ++++++++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/filmatyk/gui.py b/filmatyk/gui.py index b6b442e..959ef2a 100644 --- a/filmatyk/gui.py +++ b/filmatyk/gui.py @@ -145,8 +145,8 @@ def __init__(self, debugMode=False, isOnLinux=False): # load the savefile self.dataManager = DataManager(self.getFilename(), VERSION) userdata = self.dataManager.load() - # create the options manager - self.options = Options(userdata.options_json) + # initialize the options manager + Options.init(userdata.options_json) # 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) @@ -256,7 +256,7 @@ def saveUserData(self): if not ( any([db.isDirty for db in self.databases]) or any([ps.isDirty for ps in self.presenters]) or - self.options.isDirty + Options.isDirty ): return # construct the UserData object @@ -268,7 +268,7 @@ def saveUserData(self): series_data=self.databases[1].storeToString(), games_conf=self.presenters[2].storeToString(), games_data=self.databases[2].storeToString(), - options_json=self.options.storeToString(), + options_json=Options.storeToString(), ) # request the manager to save it self.dataManager.save(serialized_data) diff --git a/filmatyk/options.py b/filmatyk/options.py index 6182236..7ca72c7 100644 --- a/filmatyk/options.py +++ b/filmatyk/options.py @@ -1,3 +1,21 @@ +"""Options is a globally accessible class holding all program options. + +The only exported object is a singleton instance of the Options class. Usage: + from options import Options +The instance has to be initialized (by passing a JSON string with serialized +initial values) or at least confirmed on assuming default values: + Options.init(json_string) +After that - and ONLY after that - it can be used in two ways. +Access to an option value: + Options.get('option_name') +Direct access to the underlying TkVar: + Options.var('option_name') + +This gives a convenient access to options from wherever in the program, +assuming the actual call to get or var happens AFTER the initialization of the +instance. +""" + import json import os @@ -5,21 +23,25 @@ from tkinter import ttk -class Options(): +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), + * simple binding of option variables to Tk widgets (using var), * 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='{}'): + def __init__(self): self.variables = {} self.isDirty = False + self.isInit = False + + def init(self, json_string:str='{}'): + """Restore the JSON-serialized values.""" saved_values = json.loads(json_string) for name, vtype, default in self.option_prototypes: variable = vtype() @@ -27,6 +49,7 @@ def __init__(self, json_string:str='{}'): variable.set(value) variable.trace('w', self.__touched_callback) self.variables[name] = variable + self.isInit = True def storeToString(self): """Serialize the options to a JSON string.""" @@ -36,12 +59,19 @@ def storeToString(self): def get(self, name): """Get the value of a named option.""" + if not self.isInit: + raise AttributeError return self.variables[name].get() - def variable(self, name): + def var(self, name): """Get Tk variable object of a named option.""" + if not self.isInit: + raise AttributeError return self.variables[name] def __touched_callback(self, *args): """Set the dirty flag whenever an option changes.""" self.isDirty = True + + +Options = _Options()