Commit 0701fca3 authored by eric pellegrini's avatar eric pellegrini
Browse files

Bug fix in PreferencesSettings dialog

Improved the dialog ergonomy
Modified a bit PLATFORM.is_writable_directory
parent 7733a751
...@@ -188,31 +188,7 @@ class Platform(object): ...@@ -188,31 +188,7 @@ class Platform(object):
# Gets an absolute version of the path to check # Gets an absolute version of the path to check
path = self.get_path(path) path = self.get_path(path)
# Case where the path to be checked does not exist return os.access(path, os.W_OK|os.X_OK)
if not os.path.exists(path):
# Try to make the directory.
try:
os.makedirs(path)
# An error occured, hence the path is not writable, return false
except OSError:
return False
# No error occured, the user has permission to create the directory, so it will be writable
else:
return True
# The directory to be checked already exists
else:
# Try to create a temporary file inside this directory
try:
tempfile.TemporaryFile("w", path)
# Failure, this directory is not writable
except OSError:
return False
# Success, this directory is writable
else:
return True
def create_directory(self, path): def create_directory(self, path):
''' '''
......
...@@ -35,6 +35,7 @@ import collections ...@@ -35,6 +35,7 @@ import collections
import ConfigParser import ConfigParser
import os import os
from MDANSE import LOGGER
from MDANSE.Core.Platform import PLATFORM, PlatformError from MDANSE.Core.Platform import PLATFORM, PlatformError
from MDANSE.Core.Error import Error from MDANSE.Core.Error import Error
from MDANSE.Core.Singleton import Singleton from MDANSE.Core.Singleton import Singleton
...@@ -167,6 +168,17 @@ class PreferencesItem(object): ...@@ -167,6 +168,17 @@ class PreferencesItem(object):
return self._value return self._value
@abc.abstractmethod
def check_value(self,value):
'''
Set the value of the preferences item.
:param value: the value of the preferences item
:type value: str
'''
pass
@abc.abstractmethod @abc.abstractmethod
def set_value(self,value): def set_value(self,value):
''' '''
...@@ -187,6 +199,25 @@ class InputDirectory(PreferencesItem): ...@@ -187,6 +199,25 @@ class InputDirectory(PreferencesItem):
type = "input_directory" type = "input_directory"
def check_value(self,value):
'''
Check the value of the preferences item.
:param value: the value of the preferences item
:type value: str
:return: True if the value is correct, False otherwise.
:rtype: bool
'''
value = PLATFORM.get_path(value)
try:
PLATFORM.create_directory(value)
except PlatformError:
return False
else:
return True
def set_value(self, value): def set_value(self, value):
''' '''
...@@ -201,17 +232,16 @@ class InputDirectory(PreferencesItem): ...@@ -201,17 +232,16 @@ class InputDirectory(PreferencesItem):
try: try:
PLATFORM.create_directory(value) PLATFORM.create_directory(value)
except PlatformError: except PlatformError:
raise PreferencesError('Error when setting input directory %r' % value) LOGGER("Invalid value for %r preferences item. Set the default value instead." % self._name,"warning")
self._value = self._default
else:
self._value = value self._value = value
def get_value(self): def get_value(self):
PLATFORM.create_directory(self._value)
return self._value return self._value
class Preferences(object): class Preferences(collections.OrderedDict):
''' '''
This class implements the MDANSE preferences. This class implements the MDANSE preferences.
...@@ -221,18 +251,19 @@ class Preferences(object): ...@@ -221,18 +251,19 @@ class Preferences(object):
__metaclass__ = Singleton __metaclass__ = Singleton
def __init__(self): def __init__(self,*args,**kwargs):
''' '''
Constructs the preferences Constructs the preferences
''' '''
self._items = collections.OrderedDict() collections.OrderedDict.__init__(self,*args,**kwargs)
self._items["working_directory"] = InputDirectory("working_directory", "paths", PLATFORM.home_directory())
self._items["macros_directory"] = InputDirectory("macros_directory", "paths", os.path.join(PLATFORM.home_directory(), "mdanse_macros")) collections.OrderedDict.__setitem__(self,"working_directory",InputDirectory("working_directory", "paths", PLATFORM.home_directory()))
collections.OrderedDict.__setitem__(self,"macros_directory",InputDirectory("macros_directory", "paths", os.path.join(PLATFORM.home_directory(), "mdanse_macros")))
self._parser = ConfigParser.ConfigParser() self._parser = ConfigParser.ConfigParser()
for s in self._items.values(): for s in self.values():
try: try:
self._parser.add_section(s.section) self._parser.add_section(s.section)
except ConfigParser.DuplicateSectionError: except ConfigParser.DuplicateSectionError:
...@@ -254,40 +285,27 @@ class Preferences(object): ...@@ -254,40 +285,27 @@ class Preferences(object):
''' '''
return self._parser return self._parser
@property def __setitem__(self,item,value):
def items(self):
return self._items
def get_preferences_item(self, item): pass
'''
Get the value of a selected preferences item.
:param item: the preferences item def clear(self):
:type item: str
:return: the values of the preferences item. pass
:rtype: ``PreferencesItem`` subclass
'''
try:
return self._items[item]
except KeyError:
raise PreferencesError("Unknown preferences item")
def set_preferences_item(self, item, value): def __getitem__(self, item):
''' '''
Set the value of a preferences item. Get the value of a selected preferences item.
:param item: the preferences item :param item: the preferences item
:type item: str :type item: str
:param value: the value to set for the preferences item. :return: the values of the preferences item.
:type value: depends on the ``PreferencesItem`` subclass :rtype: ``PreferencesItem`` subclass
''' '''
try: try:
self._items[item].set_value(value) return collections.OrderedDict.__getitem__(self,item)
return self._items[item]
except KeyError: except KeyError:
raise PreferencesError("Unknown preferences item") raise PreferencesError("Unknown preferences item")
...@@ -318,7 +336,12 @@ class Preferences(object): ...@@ -318,7 +336,12 @@ class Preferences(object):
for s in self._parser.sections(): for s in self._parser.sections():
for k, v in self._parser.items(s): for k, v in self._parser.items(s):
self.set_preferences_item(k,v) if self.has_key(k):
self[k].set_value(v)
else:
self._parser.remove_option(s,k)
if not self._parser.items(s):
self._parser.remove_section(s)
def save(self,path=None): def save(self,path=None):
''' '''
......
...@@ -474,7 +474,7 @@ class IJob(Configurable): ...@@ -474,7 +474,7 @@ class IJob(Configurable):
raise IOError("A job with %r name is already stored in the registry" % shortname) raise IOError("A job with %r name is already stored in the registry" % shortname)
from MDANSE import PREFERENCES from MDANSE import PREFERENCES
macrosDir = PREFERENCES.get_preferences_item("macros_directory").get_value() macrosDir = PREFERENCES["macros_directory"].get_value()
templateFile = os.path.join(macrosDir,"%s.py" % longname) templateFile = os.path.join(macrosDir,"%s.py" % longname)
......
...@@ -480,7 +480,7 @@ class PlotterFrame(wx.Frame): ...@@ -480,7 +480,7 @@ class PlotterFrame(wx.Frame):
mainSizer.Fit(mainPanel) mainSizer.Fit(mainPanel)
mainPanel.Layout() mainPanel.Layout()
self.SetSize((1000, 600)) self.SetSize((1000, 700))
if __name__ == "__main__": if __name__ == "__main__":
app = wx.App(False) app = wx.App(False)
......
...@@ -158,7 +158,5 @@ if __name__ == "__main__": ...@@ -158,7 +158,5 @@ if __name__ == "__main__":
p.ShowModal() p.ShowModal()
p.Destroy()
app.MainLoop() app.MainLoop()
\ No newline at end of file
...@@ -4,7 +4,7 @@ from MDANSE import PLATFORM,PREFERENCES,REGISTRY ...@@ -4,7 +4,7 @@ from MDANSE import PLATFORM,PREFERENCES,REGISTRY
directories = sorted([x[0] for x in os.walk(os.path.dirname(__file__))][1:]) directories = sorted([x[0] for x in os.walk(os.path.dirname(__file__))][1:])
macrosDir = PREFERENCES.get_preferences_item('macros_directory').get_value() macrosDir = PREFERENCES['macros_directory'].get_value()
directories.insert(0,macrosDir) directories.insert(0,macrosDir)
directories.extend(sorted([x[0] for x in os.walk(macrosDir)][1:])) directories.extend(sorted([x[0] for x in os.walk(macrosDir)][1:]))
......
...@@ -30,21 +30,32 @@ Created on May 28, 2015 ...@@ -30,21 +30,32 @@ Created on May 28, 2015
:author: Eric C. Pellegrini :author: Eric C. Pellegrini
''' '''
import abc
import collections import collections
import wx import wx
import wx.aui as wxaui import wx.aui as wxaui
import wx.lib.filebrowsebutton as wxfile import wx.lib.filebrowsebutton as wxfile
from MDANSE import LOGGER, PREFERENCES from MDANSE import PLATFORM, PREFERENCES
from MDANSE.Core.Error import Error
from MDANSE.Core.Preferences import PreferencesError
class PreferencesSettingsDialogError(Error): class WritableDirectoryValidator(wx.PyValidator):
pass
def Clone(self):
return WritableDirectoryValidator()
def TransferToWindow(self):
return True
def TransferFromWindow(self):
return True
class PreferencesItemWidget(wx.Panel): class PreferencesItemWidget(wx.Panel):
__metaclass__ = abc.ABCMeta
def __init__(self, parent, name, item, *args, **kwargs): def __init__(self, parent, name, item, *args, **kwargs):
wx.Panel.__init__(self, parent, wx.ID_ANY, *args, **kwargs) wx.Panel.__init__(self, parent, wx.ID_ANY, *args, **kwargs)
...@@ -57,6 +68,19 @@ class PreferencesItemWidget(wx.Panel): ...@@ -57,6 +68,19 @@ class PreferencesItemWidget(wx.Panel):
self.build_panel() self.build_panel()
@abc.abstractmethod
def validate(self):
pass
@abc.abstractmethod
def get_value(self):
pass
@abc.abstractmethod
def set_value(self,value):
pass
class InputDirectoryWidget(PreferencesItemWidget): class InputDirectoryWidget(PreferencesItemWidget):
type = "input_directory" type = "input_directory"
...@@ -65,7 +89,8 @@ class InputDirectoryWidget(PreferencesItemWidget): ...@@ -65,7 +89,8 @@ class InputDirectoryWidget(PreferencesItemWidget):
sb = wx.StaticBox(self, wx.ID_ANY, label=self._item.name) sb = wx.StaticBox(self, wx.ID_ANY, label=self._item.name)
self._widget = wxfile.DirBrowseButton(self, wx.ID_ANY, startDirectory=self._item.value) self._widget = wxfile.DirBrowseButton(self, wx.ID_ANY, labelText="", startDirectory=self._item.value)
self._widget.SetValidator(WritableDirectoryValidator())
self._widget.SetValue(self._item.value) self._widget.SetValue(self._item.value)
sizer = wx.StaticBoxSizer(sb, wx.HORIZONTAL) sizer = wx.StaticBoxSizer(sb, wx.HORIZONTAL)
...@@ -78,35 +103,19 @@ class InputDirectoryWidget(PreferencesItemWidget): ...@@ -78,35 +103,19 @@ class InputDirectoryWidget(PreferencesItemWidget):
return self._widget.GetValue() return self._widget.GetValue()
def set_value(self, value): def set_value(self, value):
self._widget.SetValue(value) self._widget.SetValue(value)
class LoggingLevelWidget(PreferencesItemWidget): def validate(self):
type = "logging_level"
def build_panel(self):
sb = wx.StaticBox(self, wx.ID_ANY, label=self._item.name)
self._widget = wx.Choice(self, wx.ID_ANY, choices=LOGGER.levels.keys())
self._widget.SetStringSelection(PREFERENCES[self._item.name])
sizer = wx.StaticBoxSizer(sb, wx.HORIZONTAL)
sizer.Add(self._widget, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(sizer)
def get_value(self):
return self._widget.GetStringSelection()
def set_value(self, value): path = PLATFORM.get_path(self._widget.GetValue())
return self._widget.SetStringSelection(value) if PLATFORM.is_directory_writable(path):
return True
else:
wx.MessageBox("The directory %r is not writable." % path, "Invalid input",wx.OK | wx.ICON_ERROR)
return False
WIDGETS = dict([(v.type,v) for v in PreferencesItemWidget.__subclasses__()]) WIDGETS = dict([(v.type,v) for v in PreferencesItemWidget.__subclasses__()])
...@@ -128,7 +137,7 @@ class PreferencesSettingsDialog(wx.Dialog): ...@@ -128,7 +137,7 @@ class PreferencesSettingsDialog(wx.Dialog):
self._sectionSizers = collections.OrderedDict() self._sectionSizers = collections.OrderedDict()
self._widgets = collections.OrderedDict() self._widgets = collections.OrderedDict()
for item in PREFERENCES.items.values(): for item in PREFERENCES.values():
section = item.section section = item.section
name = item.name name = item.name
typ = item.type typ = item.type
...@@ -149,11 +158,13 @@ class PreferencesSettingsDialog(wx.Dialog): ...@@ -149,11 +158,13 @@ class PreferencesSettingsDialog(wx.Dialog):
cancelButton = wx.Button(self, wx.ID_ANY, label="Cancel") cancelButton = wx.Button(self, wx.ID_ANY, label="Cancel")
defaultButton = wx.Button(self, wx.ID_ANY, label="Default") defaultButton = wx.Button(self, wx.ID_ANY, label="Default")
applyButton = wx.Button(self, wx.ID_ANY, label="Apply")
okButton = wx.Button(self, wx.ID_ANY, label="OK") okButton = wx.Button(self, wx.ID_ANY, label="OK")
sbSizer.Add(cancelButton, 0, wx.ALL, 5) sbSizer.Add(cancelButton, 0, wx.ALL, 5)
sbSizer.Add((-1,-1), 1, wx.ALL|wx.EXPAND, 5) sbSizer.Add((-1,-1), 1, wx.ALL|wx.EXPAND, 5)
sbSizer.Add(defaultButton, 0, wx.ALL, 5) sbSizer.Add(defaultButton, 0, wx.ALL, 5)
sbSizer.Add(applyButton, 0, wx.ALL, 5)
sbSizer.Add(okButton, 0, wx.ALL, 5) sbSizer.Add(okButton, 0, wx.ALL, 5)
self._sizer = wx.BoxSizer(wx.VERTICAL) self._sizer = wx.BoxSizer(wx.VERTICAL)
...@@ -164,56 +175,49 @@ class PreferencesSettingsDialog(wx.Dialog): ...@@ -164,56 +175,49 @@ class PreferencesSettingsDialog(wx.Dialog):
self.SetSizer(self._sizer) self.SetSizer(self._sizer)
self.Bind(wx.EVT_CLOSE, self.on_cancel)
self.Bind(wx.EVT_BUTTON, self.on_cancel, cancelButton) self.Bind(wx.EVT_BUTTON, self.on_cancel, cancelButton)
self.Bind(wx.EVT_BUTTON, self.on_default, defaultButton) self.Bind(wx.EVT_BUTTON, self.on_default, defaultButton)
self.Bind(wx.EVT_BUTTON, self.on_apply, applyButton)
self.Bind(wx.EVT_BUTTON, self.on_ok, okButton) self.Bind(wx.EVT_BUTTON, self.on_ok, okButton)
def on_cancel(self, event): def validate(self):
self.Close() for widget in self._widgets.values():
def on_default(self, event): if not widget.validate():
return False
for v in PREFERENCES.values(): return True
self._widgets[v.name].set_value(v.default)
def on_ok(self, event): def on_apply(self,event):
if not self.validate(): if not self.validate():
return return
d = wx.MessageDialog(None, 'Save the preferences ?', 'Question', wx.YES_NO|wx.YES_DEFAULT|wx.ICON_QUESTION) for k, widget in self._widgets.items():
if d.ShowModal() == wx.ID_YES: PREFERENCES[k].set_value(widget.get_value())
try: def on_ok(self,event):
PREFERENCES.save()
except PreferencesError as e: if not self.validate():
d = wx.MessageDialog(self, str(e), style=wx.ICON_ERROR|wx.STAY_ON_TOP|wx.CENTRE)
d.ShowModal()
return return
self.Close() for k, widget in self._widgets.items():
PREFERENCES[k].set_value(widget.get_value())
def validate(self): PREFERENCES.save()
self.Destroy()
for w in self._widgets.values(): def on_cancel(self, event):
w.SetBackgroundColour(wx.NullColour)
w.Refresh()
for k, v in self._widgets.items(): self.Destroy()
try: def on_default(self, event):
PREFERENCES[k] = v.get_value()
except PreferencesError as e:
d = wx.MessageDialog(self, str(e), style=wx.ICON_ERROR|wx.STAY_ON_TOP|wx.CENTRE)
d.ShowModal()
v.SetBackgroundColour("Pink")
v.Refresh()
v.SetFocus()
return False
return True for v in PREFERENCES.values():
self._widgets[v.name].set_value(v.default)
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -41,7 +41,7 @@ class RegistryViewer(wx.Frame): ...@@ -41,7 +41,7 @@ class RegistryViewer(wx.Frame):
def __init__(self, parent, *args, **kwargs): def __init__(self, parent, *args, **kwargs):
wx.Frame.__init__(self, parent, wx.ID_ANY, size = (800,400), title="Registry viewer", style=wx.DEFAULT_DIALOG_STYLE|wx.MINIMIZE_BOX|wx.MAXIMIZE_BOX|wx.RESIZE_BORDER) wx.Frame.__init__(self, parent, wx.ID_ANY, size = (800,400), title="Registry viewer", style=wx.DEFAULT_FRAME_STYLE|wx.MINIMIZE_BOX|wx.MAXIMIZE_BOX|wx.RESIZE_BORDER)
mainPanel = wx.Panel(self, wx.ID_ANY, size=self.GetSize()) mainPanel = wx.Panel(self, wx.ID_ANY, size=self.GetSize())
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment