summaryrefslogtreecommitdiff
path: root/lib/gui/presets.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gui/presets.py')
-rw-r--r--lib/gui/presets.py183
1 files changed, 183 insertions, 0 deletions
diff --git a/lib/gui/presets.py b/lib/gui/presets.py
new file mode 100644
index 00000000..e6000718
--- /dev/null
+++ b/lib/gui/presets.py
@@ -0,0 +1,183 @@
+import json
+import os
+import re
+
+import wx
+
+from ..i18n import _
+from ..utils import cache
+from .dialogs import info_dialog
+
+
+class PresetsPanel(wx.Panel):
+ """A wx.Panel for loading, saving, and applying presets.
+
+ A preset is a named collection of settings. From the perspective of this
+ class, a preset is an opaque JSON-serializable object.
+
+ The PresetsPanel will handle interaction with the user and inform the
+ instantiator of events such as a preset being loaded. Presets starting
+ and ending with "__" will not be shown to the user. This allows for the
+ instantiator to manage hidden presets such as "__LAST__".
+ """
+
+ HIDDEN_PRESET_RE = re.compile('^__.*__$')
+
+ def __init__(self, parent, *args, **kwargs):
+ """Construct a PresetsPanel.
+
+ The parent is the parent window for this wx.Panel. The parent is
+ expected to implement the following methods:
+
+ def get_preset_data(self)
+ returns a JSON object representing the current state as a preset
+
+ def apply_preset_data(self, preset_data):
+ apply the preset data to the GUI, updating GUI elements as necessary
+
+ def get_preset_suite_name(self):
+ Return a string used in the presets filename, e.g. "lettering" -> "lettering_presets.json".
+ If not defined, "presets.json" will be used.
+ """
+
+ kwargs.setdefault('style', wx.BORDER_NONE)
+ wx.Panel.__init__(self, parent, wx.ID_ANY, *args, **kwargs)
+ self.parent = parent
+
+ self.presets_box = wx.StaticBox(self, wx.ID_ANY, label=_("Presets"))
+
+ self.preset_chooser = wx.ComboBox(self, wx.ID_ANY)
+ self.update_preset_list()
+ self.preset_chooser.SetSelection(-1)
+
+ self.load_preset_button = wx.Button(self, wx.ID_ANY, _("Load"))
+ self.load_preset_button.Bind(wx.EVT_BUTTON, self.load_selected_preset)
+
+ self.add_preset_button = wx.Button(self, wx.ID_ANY, _("Add"))
+ self.add_preset_button.Bind(wx.EVT_BUTTON, self.add_preset)
+
+ self.overwrite_preset_button = wx.Button(self, wx.ID_ANY, _("Overwrite"))
+ self.overwrite_preset_button.Bind(wx.EVT_BUTTON, self.overwrite_preset)
+
+ self.delete_preset_button = wx.Button(self, wx.ID_ANY, _("Delete"))
+ self.delete_preset_button.Bind(wx.EVT_BUTTON, self.delete_preset)
+
+ presets_sizer = wx.StaticBoxSizer(self.presets_box, wx.HORIZONTAL)
+ presets_sizer.Add(self.preset_chooser, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
+ presets_sizer.Add(self.load_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
+ presets_sizer.Add(self.add_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
+ presets_sizer.Add(self.overwrite_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
+ presets_sizer.Add(self.delete_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
+
+ self.SetSizerAndFit(presets_sizer)
+ self.Layout()
+
+ @property
+ @cache
+ def suite_name(self):
+ try:
+ return self.parent.get_preset_suite_name() + "_presets"
+ except AttributeError:
+ return "presets"
+
+ @cache
+ def presets_path(self):
+ try:
+ import appdirs
+ config_path = appdirs.user_config_dir('inkstitch')
+ except ImportError:
+ config_path = os.path.expanduser('~/.inkstitch')
+
+ if not os.path.exists(config_path):
+ os.makedirs(config_path)
+ return os.path.join(config_path, '%s.json' % self.suite_name)
+
+ def _load_presets(self):
+ try:
+ with open(self.presets_path(), 'r') as presets:
+ presets = json.load(presets)
+ return presets
+ except IOError:
+ return {}
+
+ def _save_presets(self, presets):
+ with open(self.presets_path(), 'w') as presets_file:
+ json.dump(presets, presets_file)
+
+ def update_preset_list(self):
+ preset_names = self._load_presets().keys()
+ preset_names = [preset for preset in preset_names if not self.is_hidden(preset)]
+ self.preset_chooser.SetItems(sorted(preset_names))
+
+ def is_hidden(self, preset_name):
+ return self.HIDDEN_PRESET_RE.match(preset_name)
+
+ def get_preset_name(self):
+ preset_name = self.preset_chooser.GetValue().strip()
+ if preset_name:
+ return preset_name
+ else:
+ info_dialog(self, _("Please enter or select a preset name first."), caption=_('Preset'))
+ return
+
+ def check_and_load_preset(self, preset_name):
+ preset = self._load_presets().get(preset_name)
+ if not preset:
+ info_dialog(self, _('Preset "%s" not found.') % preset_name, caption=_('Preset'))
+
+ return preset
+
+ def store_preset(self, preset_name, data):
+ presets = self._load_presets()
+ presets[preset_name] = data
+ self._save_presets(presets)
+ self.update_preset_list()
+
+ def add_preset(self, event, overwrite=False):
+ preset_name = self.get_preset_name()
+ if not preset_name:
+ return
+
+ if not overwrite and preset_name in self._load_presets():
+ info_dialog(self, _('Preset "%s" already exists. Please use another name or press "Overwrite"') % preset_name, caption=_('Preset'))
+
+ self.store_preset(self, preset_name, self.parent.get_preset_data())
+
+ event.Skip()
+
+ def overwrite_preset(self, event):
+ self.add_preset(event, overwrite=True)
+
+ def load_preset(self, preset_name):
+ preset = self.check_and_load_preset(preset_name)
+ if not preset:
+ return
+
+ self.parent.apply_preset_data(preset)
+
+ def load_selected_preset(self, event):
+ preset_name = self.get_preset_name()
+ if not preset_name:
+ return
+
+ self.load_preset(preset_name)
+
+ event.Skip()
+
+ def delete_preset(self, event):
+ preset_name = self.get_preset_name()
+ if not preset_name:
+ return
+
+ preset = self.check_and_load_preset(preset_name)
+ if not preset:
+ return
+
+ presets = self._load_presets()
+ presets.pop(preset_name, None)
+ self._save_presets(presets)
+
+ self.update_preset_list()
+ self.preset_chooser.SetValue("")
+
+ event.Skip()