From 6e9eeb391c88e0815023573632646f4dc705e4b6 Mon Sep 17 00:00:00 2001 From: Thiago Franco de Moraes Date: Thu, 6 Dec 2018 14:54:35 -0200 Subject: [PATCH] Session as dict (#156) --- app.py | 11 ++++++----- invesalius/control.py | 1 + invesalius/data/surface.py | 22 ++++++++++++++++++++++ invesalius/gui/dialogs.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++------------------ invesalius/gui/preferences.py | 2 +- invesalius/gui/task_exporter.py | 10 +++++++++- invesalius/session.py | 285 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------------------------------------- invesalius/utils.py | 15 ++++----------- 8 files changed, 239 insertions(+), 176 deletions(-) diff --git a/app.py b/app.py index 1ec2c91..e18aa19 100644 --- a/app.py +++ b/app.py @@ -147,9 +147,8 @@ class Inv3SplashScreen(SplashScreen): create_session = True install_lang = 0 - # Check if there is a language set (if session file exists - if session.ReadLanguage(): - lang = session.GetLanguage() + lang = session.GetLanguage() + if lang: if (lang != "False"): _ = i18n.InstallLanguage(lang) install_lang = 1 @@ -179,7 +178,9 @@ class Inv3SplashScreen(SplashScreen): invdir = os.path.join(homedir, ".invesalius") shutil.rmtree(invdir) sys.exit() - + + dialog.Destroy() + # Session file should be created... So we set the recent # choosen language if (create_session): @@ -187,7 +188,7 @@ class Inv3SplashScreen(SplashScreen): session.SetLanguage(lang) session.WriteSessionFile() - session.SaveConfigFileBackup() + # session.SaveConfigFileBackup() # Only after language was defined, splash screen will be diff --git a/invesalius/control.py b/invesalius/control.py index 652dfed..9624e6e 100644 --- a/invesalius/control.py +++ b/invesalius/control.py @@ -249,6 +249,7 @@ class Controller(): def ShowDialogCloseProject(self): session = ses.Session() st = session.project_status + print('Status', st, type(st)) if st == const.PROJ_CLOSE: return -1 try: diff --git a/invesalius/data/surface.py b/invesalius/data/surface.py index 8ed387c..bce5ca7 100644 --- a/invesalius/data/surface.py +++ b/invesalius/data/surface.py @@ -159,6 +159,28 @@ class SurfaceManager(): self.last_surface_index = 0 self.__bind_events() + self._default_parameters = { + 'algorithm': 'Default', + 'quality': const.DEFAULT_SURFACE_QUALITY, + 'fill_holes': False, + 'keep_largest': False, + 'fill_border_holes': True, + } + + self._load_user_parameters() + + def _load_user_parameters(self): + session = ses.Session() + + if 'surface' in session: + self._default_parameters.update(session['surface']) + else: + session['surface'] = self._default_parameters + session.WriteSessionFile() + + print('Session', session) + + def __bind_events(self): Publisher.subscribe(self.AddNewActor, 'Create surface') Publisher.subscribe(self.SetActorTransparency, diff --git a/invesalius/gui/dialogs.py b/invesalius/gui/dialogs.py index 542c663..3f08ea6 100644 --- a/invesalius/gui/dialogs.py +++ b/invesalius/gui/dialogs.py @@ -268,8 +268,10 @@ WILDCARD_MESH_FILES = "STL File format (*.stl)|*.stl|" \ def ShowOpenProjectDialog(): # Default system path current_dir = os.path.abspath(".") + session = ses.Session() + last_directory = session.get('paths', 'last_directory_inv3', '') dlg = wx.FileDialog(None, message=_("Open InVesalius 3 project..."), - defaultDir="", + defaultDir=last_directory, defaultFile="", wildcard=WILDCARD_OPEN, style=wx.FD_OPEN|wx.FD_CHANGE_DIR) @@ -286,6 +288,10 @@ def ShowOpenProjectDialog(): except(wx._core.PyAssertionError): # FIX: win64 filepath = dlg.GetPath() + if filepath: + session['paths']['last_directory_inv3'] = os.path.split(filepath)[0] + session.WriteSessionFile() + # Destroy the dialog. Don't do this until you are done with it! # BAD things can happen otherwise! dlg.Destroy() @@ -337,17 +343,19 @@ def ShowImportDirDialog(self): def ShowImportBitmapDirDialog(self): current_dir = os.path.abspath(".") - if sys.platform == 'win32' or sys.platform.startswith('linux'): - session = ses.Session() + # if sys.platform == 'win32' or sys.platform.startswith('linux'): + # session = ses.Session() - if (session.GetLastDicomFolder()): - folder = session.GetLastDicomFolder() - else: - folder = '' - else: - folder = '' + # if (session.GetLastDicomFolder()): + # folder = session.GetLastDicomFolder() + # else: + # folder = '' + # else: + # folder = '' + session = ses.Session() + last_directory = session.get('paths', 'last_directory_bitmap', '') - dlg = wx.DirDialog(self, _("Choose a folder with TIFF, BMP, JPG or PNG:"), folder, + dlg = wx.DirDialog(self, _("Choose a folder with TIFF, BMP, JPG or PNG:"), last_directory, style=wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST | wx.DD_CHANGE_DIR) @@ -363,9 +371,13 @@ def ShowImportBitmapDirDialog(self): if (dlg.GetPath()): path = dlg.GetPath() - if (sys.platform != 'darwin'): - if (path): - session.SetLastDicomFolder(path) + # if (sys.platform != 'darwin'): + # if (path): + # session.SetLastDicomFolder(path) + + if path: + session['paths']['last_directory_bitmap'] = path + session.WriteSessionFile() # Only destroy a dialog after you're done with it. dlg.Destroy() @@ -375,9 +387,10 @@ def ShowImportBitmapDirDialog(self): def ShowImportOtherFilesDialog(id_type): # Default system path - current_dir = os.path.abspath(".") + session = ses.Session() + last_directory = session.get('paths', 'last_directory_%d' % id_type, '') dlg = wx.FileDialog(None, message=_("Import Analyze 7.5 file"), - defaultDir="", + defaultDir=last_directory, defaultFile="", wildcard=WILDCARD_ANALYZE, style=wx.FD_OPEN | wx.FD_CHANGE_DIR) @@ -407,17 +420,22 @@ def ShowImportOtherFilesDialog(id_type): if (dlg.GetPath()): filename = dlg.GetPath() + if filename: + session['paths']['last_directory_%d' % id_type] = os.path.split(dlg.GetPath())[0] + session.WriteSessionFile() # Destroy the dialog. Don't do this until you are done with it! # BAD things can happen otherwise! dlg.Destroy() - os.chdir(current_dir) return filename def ShowImportMeshFilesDialog(): # Default system path current_dir = os.path.abspath(".") + session = ses.Session() + last_directory = session.get('paths', 'last_directory_surface_import', '') dlg = wx.FileDialog(None, message=_("Import surface file"), + defaultDir=last_directory, wildcard=WILDCARD_MESH_FILES, style=wx.FD_OPEN | wx.FD_CHANGE_DIR) @@ -435,6 +453,10 @@ def ShowImportMeshFilesDialog(): if (dlg.GetPath()): filename = dlg.GetPath() + if filename: + session['paths']['last_directory_surface_import'] = os.path.split(filename)[0] + session.WriteSessionFile() + # Destroy the dialog. Don't do this until you are done with it! # BAD things can happen otherwise! dlg.Destroy() @@ -444,9 +466,11 @@ def ShowImportMeshFilesDialog(): def ShowSaveAsProjectDialog(default_filename=None): current_dir = os.path.abspath(".") + session = ses.Session() + last_directory = session.get('paths', 'last_directory_inv3', '') dlg = wx.FileDialog(None, _("Save project as..."), # title - "", # last used directory + last_directory, # last used directory default_filename, WILDCARD_INV_SAVE, wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) @@ -469,6 +493,10 @@ def ShowSaveAsProjectDialog(default_filename=None): if filename.split(".")[-1] != extension: filename = filename + "." + extension + if filename: + session['paths']['last_directory_inv3'] = os.path.split(filename)[0] + session.WriteSessionFile() + wildcard = dlg.GetFilterIndex() os.chdir(current_dir) return filename, wildcard == INV_COMPRESSED @@ -1457,13 +1485,16 @@ def ExportPicture(type_=""): utils.debug("ExportPicture") project = proj.Project() + session = ses.Session() + last_directory = session.get('paths', 'last_directory_screenshot', '') + project_name = "%s_%s" % (project.name, type_) if not sys.platform in ('win32', 'linux2', 'linux'): project_name += ".jpg" dlg = wx.FileDialog(None, "Save %s picture as..." %type_, - "", # last used directory + last_directory, # last used directory project_name, # filename WILDCARD_SAVE_PICTURE, wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) @@ -1474,6 +1505,8 @@ def ExportPicture(type_=""): filetype = INDEX_TO_TYPE[filetype_index] extension = INDEX_TO_EXTENSION[filetype_index] filename = dlg.GetPath() + session['paths']['last_directory_screenshot'] = os.path.split(filename)[0] + session.WriteSessionFile() if sys.platform != 'win32': if filename.split(".")[-1] != extension: filename = filename + "."+ extension diff --git a/invesalius/gui/preferences.py b/invesalius/gui/preferences.py index b4e9530..1841c01 100644 --- a/invesalius/gui/preferences.py +++ b/invesalius/gui/preferences.py @@ -222,7 +222,7 @@ class Language(wx.Panel): class SurfaceCreation(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - self.rb_fill_border = wx.RadioBox(self, -1, "Fill border holes", choices=[_('Yes'), _('No')], style=wx.RA_SPECIFY_COLS | wx.NO_BORDER) + self.rb_fill_border = wx.RadioBox(self, -1, _("Fill border holes"), choices=[_('Yes'), _('No')], style=wx.RA_SPECIFY_COLS | wx.NO_BORDER) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.rb_fill_border) diff --git a/invesalius/gui/task_exporter.py b/invesalius/gui/task_exporter.py index 1fbae89..8594888 100644 --- a/invesalius/gui/task_exporter.py +++ b/invesalius/gui/task_exporter.py @@ -33,6 +33,7 @@ from wx.lib.pubsub import pub as Publisher import invesalius.constants as const import invesalius.gui.dialogs as dlg import invesalius.project as proj +import invesalius.session as ses BTN_MASK = wx.NewId() BTN_PICTURE = wx.NewId() @@ -318,10 +319,12 @@ class InnerTaskPanel(wx.Panel): else: project_name = project.name+".stl" + session = ses.Session() + last_directory = session.get('paths', 'last_directory_3d_surface', '') dlg = wx.FileDialog(None, _("Save 3D surface as..."), # title - "", # last used directory + last_directory, # last used directory project_name, # filename WILDCARD_SAVE_3D, wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) @@ -335,6 +338,11 @@ class InnerTaskPanel(wx.Panel): if sys.platform != 'win32': if filename.split(".")[-1] != extension: filename = filename + "."+ extension + + if filename: + session['paths']['last_directory_3d_surface'] = os.path.split(filename)[0] + session.WriteSessionFile() + Publisher.sendMessage('Export surface to file', filename=filename, filetype=filetype) if not os.path.exists(filename): diff --git a/invesalius/session.py b/invesalius/session.py index 0948832..2e40c7c 100644 --- a/invesalius/session.py +++ b/invesalius/session.py @@ -30,6 +30,8 @@ import sys from threading import Thread import time import codecs +import collections +import json #import wx.lib.pubsub as ps from wx.lib.pubsub import pub as Publisher @@ -52,7 +54,8 @@ else: USER_INV_DIR = os.path.join(USER_DIR, u'.invesalius') USER_PRESET_DIR = os.path.join(USER_INV_DIR, u'presets') USER_LOG_DIR = os.path.join(USER_INV_DIR, u'logs') -USER_INV_CFG_PATH = os.path.join(USER_INV_DIR, 'config.cfg') +USER_INV_CFG_PATH = os.path.join(USER_INV_DIR, 'config.json') +OLD_USER_INV_CFG_PATH = os.path.join(USER_INV_DIR, 'config.cfg') SESSION_ENCODING = 'utf8' @@ -62,42 +65,98 @@ SESSION_ENCODING = 'utf8' class Session(with_metaclass(Singleton, object)): def __init__(self): + self.project_path = () self.temp_item = False - # Initializing as project status closed. - # TODO: A better way to initialize project_status as closed (3) - self.project_status = 3 - def CreateItens(self): - import invesalius.constants as const - self.project_path = () - self.debug = False - self.project_status = const.PROJ_CLOSE - # const.PROJ_NEW*, const.PROJ_OPEN, const.PROJ_CHANGE*, - # const.PROJ_CLOSE + self._values = collections.defaultdict(dict, { + 'session': { + 'status': 3, + 'language': '', + }, + 'project': { + }, + + 'paths': { + } + }) + + self._map_attrs = { + 'mode': ('session', 'mode'), + 'project_status': ('session', 'status'), + 'debug': ('session', 'debug'), + 'language': ('session', 'language'), + 'random_id': ('session', 'random_id'), + 'surface_interpolation': ('session', 'surface_interpolation'), + 'rendering': ('session', 'rendering'), + 'slice_interpolation': ('session', 'slice_interpolation'), + 'recent_projects': ('project', 'recent_projects'), + 'homedir': ('paths', 'homedir'), + 'tempdir': ('paths', 'homedir'), + 'last_dicom_folder': ('paths', 'last_dicom_folder'), + } - self.mode = const.MODE_RP - # const.MODE_RP, const.MODE_NAVIGATOR, const.MODE_RADIOLOGY, - # const.MODE_ODONTOLOGY - # InVesalius default projects' directory - homedir = self.homedir = USER_DIR - tempdir = os.path.join(homedir, u".invesalius", u"temp") + def CreateItens(self): + import invesalius.constants as const + homedir = USER_DIR + tempdir = os.path.join(USER_DIR, u".invesalius", u"temp") if not os.path.isdir(tempdir): os.makedirs(tempdir) - self.tempdir = tempdir - - # GUI language - self.language = "" # "pt_BR", "es" - self.random_id = randint(0,pow(10,16)) - - # Recent projects list - self.recent_projects = [(const.SAMPLE_DIR, u"Cranium.inv3")] - self.last_dicom_folder = '' - self.surface_interpolation = 1 - self.slice_interpolation = 0 - self.rendering = 0 - self.WriteSessionFile() + self._values = collections.defaultdict(dict, { + 'session': { + 'mode': const.MODE_RP, + 'status': const.PROJ_CLOSE, + 'debug': False, + 'language': "", + 'random_id': randint(0, pow(10,16)), + 'surface_interpolation': 1, + 'rendering': 0, + 'slice_interpolation': 0, + }, + + 'project': { + 'recent_projects': [(const.SAMPLE_DIR, u"Cranium.inv3"), ], + }, + + 'paths': { + 'homedir': USER_DIR, + 'tempdir': os.path.join(homedir, u".invesalius", u"temp"), + 'last_dicom_folder': '', + }, + }) + + def __contains__(self, key): + return key in self._values + + def __getitem__(self, key): + return self._values[key] + + def __setitem__(self, key, value): + self._values[key] = value + + def __getattr__(self, name): + map_attrs = object.__getattribute__(self, '_map_attrs') + if name not in map_attrs: + raise AttributeError(name) + session, key = map_attrs[name] + return object.__getattribute__(self, '_values')[session][key] + + def __setattr__(self, name, value): + if name in ("temp_item", "_map_attrs", "_values", "project_path"): + return object.__setattr__(self, name, value) + else: + session, key = self._map_attrs[name] + self._values[session][key] = value + + def __str__(self): + return self._values.__str__() + + def get(self, session, key, default_value): + try: + return self._values[session][key] + except KeyError: + return default_value def IsOpen(self): import invesalius.constants as const @@ -178,40 +237,17 @@ class Session(with_metaclass(Singleton, object)): self.temp_item = False def WriteSessionFile(self): - config = ConfigParser.RawConfigParser() - - config.add_section('session') - config.set('session', 'mode', self.mode) - config.set('session', 'status', self.project_status) - config.set('session','debug', self.debug) - config.set('session', 'language', self.language) - config.set('session', 'random_id', self.random_id) - config.set('session', 'surface_interpolation', self.surface_interpolation) - config.set('session', 'rendering', self.rendering) - config.set('session', 'slice_interpolation', self.slice_interpolation) - - config.add_section('project') - config.set('project', 'recent_projects', self.recent_projects) - - config.add_section('paths') - config.set('paths','homedir',self.homedir) - config.set('paths','tempdir',self.tempdir) - config.set('paths','last_dicom_folder',self.last_dicom_folder) - - path = os.path.join(self.homedir , - '.invesalius', 'config.cfg') + self._write_to_json(self._values, USER_INV_CFG_PATH) - configfile = codecs.open(path, 'wb', SESSION_ENCODING) - try: - config.write(configfile) - except UnicodeDecodeError: - pass - configfile.close() + def _write_to_json(self, cfg_dict, cfg_filename): + with open(cfg_filename, 'w') as cfg_file: + json.dump(cfg_dict, cfg_file, sort_keys=True, indent=4) def __add_to_list(self, item): import invesalius.constants as const # Last projects list l = self.recent_projects + item = list(item) # If item exists, remove it from list if l.count(item): @@ -219,11 +255,7 @@ class Session(with_metaclass(Singleton, object)): # Add new item l.insert(0, item) - - # Remove oldest projects from list - if len(l)>const.PROJ_MAX: - for i in range(len(l)-const.PROJ_MAX): - l.pop() + self.recent_projects = l[:const.PROJ_MAX] def GetLanguage(self): return self.language @@ -244,86 +276,59 @@ class Session(with_metaclass(Singleton, object)): self.last_dicom_folder = folder self.WriteSessionFile() - def ReadLanguage(self): - config = ConfigParser.ConfigParser() - path = os.path.join(USER_INV_DIR, 'config.cfg') - try: - f = codecs.open(path, 'rb', SESSION_ENCODING) - config.readfp(f) - f.close() - self.language = config.get('session','language') - return self.language - except IOError: - return False - except (ConfigParser.NoSectionError, - ConfigParser.NoOptionError, - ConfigParser.MissingSectionHeaderError): - return False - - def ReadRandomId(self): - config = ConfigParser.ConfigParser() - path = os.path.join(USER_INV_DIR, 'config.cfg') - try: - f = codecs.open(path, 'rb', SESSION_ENCODING) - config.readfp(f) - f.close() - self.random_id = config.get('session','random_id') - return self.random_id - except IOError: - return False - except (ConfigParser.NoSectionError, - ConfigParser.NoOptionError, - ConfigParser.MissingSectionHeaderError): - return False - - def ReadSession(self): - config = ConfigParser.ConfigParser() - path = USER_INV_CFG_PATH - try: - f = codecs.open(path, 'rb', SESSION_ENCODING) - config.readfp(f) - f.close() - self.mode = config.get('session', 'mode') - # Do not reading project status from the config file, since there - # isn't a recover sessession tool in InVesalius - #self.project_status = int(config.get('session', 'status')) - self.debug = config.get('session','debug') - self.language = config.get('session','language') - self.recent_projects = eval(config.get('project','recent_projects')) - self.homedir = config.get('paths','homedir') - self.tempdir = config.get('paths','tempdir') - self.last_dicom_folder = config.get('paths','last_dicom_folder') - - #if not(sys.platform == 'win32'): - # self.last_dicom_folder = self.last_dicom_folder.decode('utf-8') - - self.surface_interpolation = config.get('session', 'surface_interpolation') - self.slice_interpolation = config.get('session', 'slice_interpolation') - - self.rendering = config.get('session', 'rendering') - self.random_id = config.get('session','random_id') - return True + def _update_cfg_from_dict(self, config, cfg_dict): + for session in cfg_dict: + if cfg_dict[session] and isinstance(cfg_dict[session], dict): + config.add_section(session) + for key in cfg_dict[session]: + config.set(session, key, cfg_dict[session][key]) - except IOError: - return False + def _read_cfg_from_json(self, json_filename): + with open(json_filename, 'r') as cfg_file: + cfg_dict = json.load(cfg_file) + self._values.update(cfg_dict) - except(ConfigParser.NoSectionError, ConfigParser.MissingSectionHeaderError, - ConfigParser.ParsingError): + # Do not reading project status from the config file, since there + # isn't a recover session tool in InVesalius yet. + self.project_status = 3 - if (self.RecoveryConfigFile()): - self.ReadSession() - return True - else: - return False + def _read_cfg_from_ini(self, cfg_filename): + f = codecs.open(cfg_filename, 'rb', SESSION_ENCODING) + config = ConfigParser.ConfigParser() + config.readfp(f) + f.close() + + self.mode = config.getint('session', 'mode') + # Do not reading project status from the config file, since there + # isn't a recover sessession tool in InVesalius + #self.project_status = int(config.get('session', 'status')) + self.debug = config.getboolean('session','debug') + self.language = config.get('session','language') + recent_projects = eval(config.get('project','recent_projects')) + self.recent_projects = [list(rp) for rp in recent_projects] + self.homedir = config.get('paths','homedir') + self.tempdir = config.get('paths','tempdir') + self.last_dicom_folder = config.get('paths','last_dicom_folder') + + # if not(sys.platform == 'win32'): + # self.last_dicom_folder = self.last_dicom_folder.decode('utf-8') + + self.surface_interpolation = config.getint('session', 'surface_interpolation') + self.slice_interpolation = config.getint('session', 'slice_interpolation') + + self.rendering = config.getint('session', 'rendering') + self.random_id = config.getint('session','random_id') - except(ConfigParser.NoOptionError): - #Added to fix new version compatibility - self.surface_interpolation = 0 - self.slice_interpolation = 0 - self.rendering = 0 - self.random_id = randint(0,pow(10,16)) + def ReadSession(self): + try: + self._read_cfg_from_json(USER_INV_CFG_PATH) + except Exception as e1: + debug(e1) try: - self.WriteSessionFile() - except AttributeError: + self._read_cfg_from_ini(OLD_USER_INV_CFG_PATH) + except Exception as e2: + debug(e2) return False - return True + + self.WriteSessionFile() + return True diff --git a/invesalius/utils.py b/invesalius/utils.py index 0bdc615..d7f9391 100644 --- a/invesalius/utils.py +++ b/invesalius/utils.py @@ -394,21 +394,14 @@ def UpdateCheck(): #msgdlg.Destroy() print("Checking updates...") - + # Check if there is a language set #import invesalius.i18n as i18n import invesalius.session as ses session = ses.Session() install_lang = 0 - if session.ReadLanguage(): - lang = session.GetLanguage() - #if (lang != "False"): - #_ = i18n.InstallLanguage(lang) - #install_lang = 1 - #if (install_lang==0): - #return - if session.ReadRandomId(): - random_id = session.GetRandomId() - + lang = session.GetLanguage() + random_id = session.GetRandomId() + if lang: # Fetch update data from server import invesalius.constants as const url = "https://www.cti.gov.br/dt3d/invesalius/update/checkupdate.php" -- libgit2 0.21.2