diff --git a/app.py b/app.py index 41beab7..79fe62e 100755 --- a/app.py +++ b/app.py @@ -54,6 +54,22 @@ import invesalius.i18n as i18n import invesalius.session as ses import invesalius.utils as utils +FS_ENCODE = sys.getfilesystemencoding() + +if sys.platform == 'win32': + from invesalius.expanduser import expand_user + try: + USER_DIR = expand_user() + except: + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) +else: + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) + +USER_INV_DIR = os.path.join(USER_DIR, u'.invesalius') +USER_PRESET_DIR = os.path.join(USER_INV_DIR, u'presets') +USER_RAYCASTING_PRESETS_DIRECTORY = os.path.join(USER_PRESET_DIR, u'raycasting') +USER_LOG_DIR = os.path.join(USER_INV_DIR, u'logs') + # ------------------------------------------------------------------ @@ -157,7 +173,7 @@ class SplashScreen(wx.SplashScreen): # Only after language was defined, splash screen will be # shown if lang: - print "LANG", lang, _, wx.Locale(), wx.GetLocale() + # print "LANG", lang, _, wx.Locale(), wx.GetLocale() import locale locale.setlocale(locale.LC_ALL, '') # For pt_BR, splash_pt.png should be used @@ -237,6 +253,8 @@ def parse_comand_line(): """ session = ses.Session() + print ">>>> stdin encoding", sys.stdin.encoding + # Parse command line arguments parser = op.OptionParser() @@ -269,7 +287,7 @@ def parse_comand_line(): i = len(args) while i: i -= 1 - file = args[i] + file = args[i].decode(sys.stdin.encoding) if os.path.isfile(file): path = os.path.abspath(file) Publisher.sendMessage('Open project', path) @@ -308,22 +326,16 @@ if __name__ == '__main__': os.chdir(path) # Create raycasting presets' folder, if it doens't exist - dirpath = os.path.join(os.path.expanduser('~'), - ".invesalius", - "presets") - if not os.path.isdir(dirpath): - os.makedirs(dirpath) + if not os.path.isdir(USER_RAYCASTING_PRESETS_DIRECTORY): + os.makedirs(USER_RAYCASTING_PRESETS_DIRECTORY) # Create logs' folder, if it doesn't exist - dirpath = os.path.join(os.path.expanduser('~'), - ".invesalius", - "logs") - if not os.path.isdir(dirpath): - os.makedirs(dirpath) + if not os.path.isdir(USER_LOG_DIR): + os.makedirs(USER_LOG_DIR) if hasattr(sys,"frozen") and sys.frozen == "windows_exe": # Set system standard error output to file - path = os.path.join(dirpath, "stderr.log") + path = os.path.join(USER_LOG_DIR, u"stderr.log") sys.stderr = open(path, "w") # Add current directory to PYTHONPATH, so other classes can diff --git a/invesalius/constants.py b/invesalius/constants.py index 7a99472..05cbc6b 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -320,46 +320,63 @@ WINDOW_LEVEL = {_("Abdomen"):(350,50), REDUCE_IMAGEDATA_QUALITY = 0 -FILE_PATH = os.path.split(__file__)[0] + +# PATHS +FS_ENCODE = sys.getfilesystemencoding() + +if sys.platform == 'win32': + from invesalius.expanduser import expand_user + try: + USER_DIR = expand_user() + except: + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) +else: + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) + +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') + +FILE_PATH = os.path.split(__file__)[0].decode(FS_ENCODE) if hasattr(sys,"frozen") and (sys.frozen == "windows_exe"\ or sys.frozen == "console_exe"): - abs_path = os.path.abspath(FILE_PATH + os.sep + ".." + os.sep + ".." + os.sep + "..") - ICON_DIR = os.path.join(abs_path, "icons") - SAMPLE_DIR = os.path.join(FILE_PATH, 'samples') - DOC_DIR = os.path.join(FILE_PATH, 'docs') - folder=RAYCASTING_PRESETS_DIRECTORY= os.path.join(abs_path, "presets", "raycasting") - RAYCASTING_PRESETS_COLOR_DIRECTORY = os.path.join(abs_path, "presets", "raycasting", "color_list") + abs_path = os.path.abspath(FILE_PATH, u'..', u'..', u'..') + ICON_DIR = os.path.join(abs_path, u"icons") + SAMPLE_DIR = os.path.join(FILE_PATH, u'samples') + DOC_DIR = os.path.join(FILE_PATH, u'docs') + folder=RAYCASTING_PRESETS_DIRECTORY= os.path.join(abs_path, u"presets", u"raycasting") + RAYCASTING_PRESETS_COLOR_DIRECTORY = os.path.join(abs_path, u"presets", u"raycasting", u"color_list") else: - ICON_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'icons')) - SAMPLE_DIR = os.path.abspath(os.path.join(FILE_PATH,'..', 'samples')) - DOC_DIR = os.path.abspath(os.path.join(FILE_PATH,'..', 'docs')) + ICON_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'icons')) + SAMPLE_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'samples')) + DOC_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'docs')) - folder=RAYCASTING_PRESETS_DIRECTORY= os.path.abspath(os.path.join(".", - "presets", - "raycasting")) + folder=RAYCASTING_PRESETS_DIRECTORY= os.path.abspath(os.path.join(u".", + u"presets", + u"raycasting")) - RAYCASTING_PRESETS_COLOR_DIRECTORY = os.path.abspath(os.path.join(".", - "presets", - "raycasting", - "color_list")) + RAYCASTING_PRESETS_COLOR_DIRECTORY = os.path.abspath(os.path.join(u".", + u"presets", + u"raycasting", + u"color_list")) # MAC App if not os.path.exists(ICON_DIR): - ICON_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', '..', '..', '..', 'icons')) - SAMPLE_DIR = os.path.abspath(os.path.join(FILE_PATH,'..', '..', '..', '..', 'samples')) - DOC_DIR = os.path.abspath(os.path.join(FILE_PATH,'..', '..', '..', '..', 'docs')) - - -ID_TO_BMP = {VOL_FRONT: [_("Front"), os.path.join(ICON_DIR, "view_front.png")], - VOL_BACK: [_("Back"), os.path.join(ICON_DIR, "view_back.png")], - VOL_TOP: [_("Top"), os.path.join(ICON_DIR, "view_top.png")], - VOL_BOTTOM: [_("Bottom"), os.path.join(ICON_DIR, "view_bottom.png")], - VOL_RIGHT: [_("Right"), os.path.join(ICON_DIR, "view_right.png")], - VOL_LEFT: [_("Left"), os.path.join(ICON_DIR, "view_left.png")], - VOL_ISO:[_("Isometric"), os.path.join(ICON_DIR,"view_isometric.png")] + ICON_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'..', u'..', u'..', u'icons')) + SAMPLE_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'..', u'..', u'..', u'samples')) + DOC_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'..', u'..', u'..', u'docs')) + + +ID_TO_BMP = {VOL_FRONT: [_("Front"), os.path.join(ICON_DIR, u"view_front.png")], + VOL_BACK: [_("Back"), os.path.join(ICON_DIR, u"view_back.png")], + VOL_TOP: [_("Top"), os.path.join(ICON_DIR, u"view_top.png")], + VOL_BOTTOM: [_("Bottom"), os.path.join(ICON_DIR, u"view_bottom.png")], + VOL_RIGHT: [_("Right"), os.path.join(ICON_DIR, u"view_right.png")], + VOL_LEFT: [_("Left"), os.path.join(ICON_DIR, u"view_left.png")], + VOL_ISO:[_("Isometric"), os.path.join(ICON_DIR, u"view_isometric.png")] } # if 1, use vtkVolumeRaycastMapper, if 0, use vtkFixedPointVolumeRayCastMapper @@ -405,19 +422,10 @@ RAYCASTING_FILES = {_("Airways"): "Airways.plist", # os.path.isfile(os.path.join(folder,filename))] -LOG_FOLDER = os.path.join(os.path.expanduser('~'), '.invesalius', 'logs') -if not os.path.isdir(LOG_FOLDER): - os.makedirs(LOG_FOLDER) - -folder = os.path.join(os.path.expanduser('~'), '.invesalius', 'presets') -if not os.path.isdir(folder): - os.makedirs(folder) - - -USER_RAYCASTING_PRESETS_DIRECTORY = folder +USER_RAYCASTING_PRESETS_DIRECTORY = os.path.join(USER_PRESET_DIR, u'raycasting') RAYCASTING_TYPES = [_(filename.split(".")[0]) for filename in - os.listdir(folder) if - os.path.isfile(os.path.join(folder,filename))] + os.listdir(USER_RAYCASTING_PRESETS_DIRECTORY) if + os.path.isfile(os.path.join(USER_RAYCASTING_PRESETS_DIRECTORY, filename))] RAYCASTING_TYPES += RAYCASTING_FILES.keys() RAYCASTING_TYPES.append(_(' Off')) RAYCASTING_TYPES.sort() diff --git a/invesalius/control.py b/invesalius/control.py index 7d3bf44..e7b04c2 100644 --- a/invesalius/control.py +++ b/invesalius/control.py @@ -338,6 +338,9 @@ class Controller(): else: dirpath, filename = session.project_path + if isinstance(filename, str): + filename = filename.decode(const.FS_ENCODE) + proj = prj.Project() prj.Project().SavePlistProject(dirpath, filename) diff --git a/invesalius/data/imagedata_utils.py b/invesalius/data/imagedata_utils.py index 473a62d..55e3a90 100644 --- a/invesalius/data/imagedata_utils.py +++ b/invesalius/data/imagedata_utils.py @@ -227,7 +227,7 @@ def CreateImageData(filelist, zspacing, xyspacing,size, message = _("Generating multiplanar visualization...") if not const.VTK_WARNING: - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') fow = vtk.vtkFileOutputWindow() fow.SetFileName(log_path) ow = vtk.vtkOutputWindow() @@ -332,7 +332,7 @@ class ImageCreator: message = _("Generating multiplanar visualization...") if not const.VTK_WARNING: - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') fow = vtk.vtkFileOutputWindow() fow.SetFileName(log_path) ow = vtk.vtkOutputWindow() diff --git a/invesalius/data/mask.py b/invesalius/data/mask.py index d00d22e..b636061 100644 --- a/invesalius/data/mask.py +++ b/invesalius/data/mask.py @@ -245,7 +245,7 @@ class Mask(): mask['mask_shape'] = self.matrix.shape mask['edited'] = self.was_edited - plist_filename = filename + '.plist' + plist_filename = filename + u'.plist' #plist_filepath = os.path.join(dir_temp, plist_filename) temp_plist = tempfile.mktemp() diff --git a/invesalius/data/polydata_utils.py b/invesalius/data/polydata_utils.py index 05cd59f..45ce644 100644 --- a/invesalius/data/polydata_utils.py +++ b/invesalius/data/polydata_utils.py @@ -23,7 +23,18 @@ import vtk import wx from wx.lib.pubsub import pub as Publisher +import invesalius.constants as const import invesalius.data.vtk_utils as vu +from invesalius.utils import touch + +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False # Update progress value in GUI UpdateProgress = vu.ShowProgress() @@ -109,8 +120,10 @@ def Merge(polydata_list): def Export(polydata, filename, bin=False): writer = vtk.vtkXMLPolyDataWriter() - print filename, type(filename) - writer.SetFileName(filename.encode('utf-8')) + if _has_win32api: + touch(filename) + filename = win32api.GetShortPathName(filename) + writer.SetFileName(filename.encode(const.FS_ENCODE)) if bin: writer.SetDataModeToBinary() else: diff --git a/invesalius/data/surface.py b/invesalius/data/surface.py index 4bdd243..5cd3e4f 100644 --- a/invesalius/data/surface.py +++ b/invesalius/data/surface.py @@ -21,6 +21,8 @@ import multiprocessing import os import plistlib import random +import shutil +import sys import tempfile import weakref @@ -28,6 +30,15 @@ import vtk import wx from wx.lib.pubsub import pub as Publisher +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False + import invesalius.constants as const import invesalius.data.imagedata_utils as iu import invesalius.data.polydata_utils as pu @@ -64,8 +75,8 @@ class Surface(): self.name = name def SavePlist(self, dir_temp, filelist): - filename = 'surface_%d' % self.index - vtp_filename = filename + '.vtp' + filename = u'surface_%d' % self.index + vtp_filename = filename + u'.vtp' vtp_filepath = os.path.join(dir_temp, vtp_filename) pu.Export(self.polydata, vtp_filepath, bin=True) @@ -80,7 +91,7 @@ class Surface(): 'volume': self.volume, 'area': self.area, } - plist_filename = filename + '.plist' + plist_filename = filename + u'.plist' #plist_filepath = os.path.join(dir_temp, filename + '.plist') temp_plist = tempfile.mktemp() plistlib.writePlist(surface, temp_plist) @@ -263,7 +274,11 @@ class SurfaceManager(): wx.MessageBox(_("File format not reconized by InVesalius"), _("Import surface error")) return - reader.SetFileName(filename) + if _has_win32api: + reader.SetFileName(win32api.GetShortPathName(filename).encode(const.FS_ENCODE)) + else: + reader.SetFileName(filename.encode(const.FS_ENCODE)) + reader.Update() polydata = reader.GetOutput() @@ -869,11 +884,31 @@ class SurfaceManager(): def OnExportSurface(self, pubsub_evt): filename, filetype = pubsub_evt.data - if (filetype == const.FILETYPE_STL) or\ - (filetype == const.FILETYPE_VTP) or\ - (filetype == const.FILETYPE_PLY) or\ - (filetype == const.FILETYPE_STL_ASCII): - + ftype_prefix = { + const.FILETYPE_STL: '.stl', + const.FILETYPE_VTP: '.vtp', + const.FILETYPE_PLY: '.ply', + const.FILETYPE_STL_ASCII: '.stl', + } + if filetype in ftype_prefix: + temp_file = tempfile.mktemp(suffix=ftype_prefix[filetype]) + + if _has_win32api: + utl.touch(temp_file) + _temp_file = temp_file + temp_file = win32api.GetShortPathName(temp_file) + os.remove(_temp_file) + + temp_file = temp_file.decode(const.FS_ENCODE) + self._export_surface(temp_file, filetype) + + shutil.move(temp_file, filename) + + def _export_surface(self, filename, filetype): + if filetype in (const.FILETYPE_STL, + const.FILETYPE_VTP, + const.FILETYPE_PLY, + const.FILETYPE_STL_ASCII): # First we identify all surfaces that are selected # (if any) proj = prj.Project() @@ -912,7 +947,9 @@ class SurfaceManager(): #writer.SetColorModeToUniformCellColor() #writer.SetColor(255, 0, 0) - if filetype in (const.FILETYPE_STL, const.FILETYPE_PLY): + if filetype in (const.FILETYPE_STL, + const.FILETYPE_STL_ASCII, + const.FILETYPE_PLY): # Invert normals normals = vtk.vtkPolyDataNormals() normals.SetInputData(polydata) @@ -923,7 +960,7 @@ class SurfaceManager(): normals.Update() polydata = normals.GetOutput() - filename = filename.encode(wx.GetDefaultPyEncoding()) + filename = filename.encode(const.FS_ENCODE) writer.SetFileName(filename) writer.SetInputData(polydata) writer.Write() diff --git a/invesalius/data/viewer_slice.py b/invesalius/data/viewer_slice.py index aed8bcf..a44d288 100755 --- a/invesalius/data/viewer_slice.py +++ b/invesalius/data/viewer_slice.py @@ -21,6 +21,7 @@ import collections import itertools +import os import tempfile import numpy as np @@ -49,6 +50,15 @@ import invesalius.session as ses import invesalius.data.converters as converters import invesalius.data.measures as measures +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False + ID_TO_TOOL_ITEM = {} STR_WL = "WL: %d WW: %d" @@ -1250,12 +1260,27 @@ class Viewer(wx.Panel): self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_SIZING)) def OnExportPicture(self, pubsub_evt): - Publisher.sendMessage('Begin busy cursor') + id, filename, filetype = pubsub_evt.data + + dict = {"AXIAL": const.AXIAL, + "CORONAL": const.CORONAL, + "SAGITAL": const.SAGITAL} + + if id == dict[self.orientation]: + Publisher.sendMessage('Begin busy cursor') + if _has_win32api: + utils.touch(filename) + win_filename = win32api.GetShortPathName(filename) + self._export_picture(id, win_filename, filetype) + else: + self._export_picture(id, filename, filetype) + Publisher.sendMessage('End busy cursor') + + def _export_picture(self, id, filename, filetype): view_prop_list = [] view_prop_list.append(self.slice_data.box_actor) self.slice_data.renderer.RemoveViewProp(self.slice_data.box_actor) - id, filename, filetype = pubsub_evt.data dict = {"AXIAL": const.AXIAL, "CORONAL": const.CORONAL, "SAGITAL": const.SAGITAL} @@ -1294,9 +1319,12 @@ class Viewer(wx.Panel): filename = "%s.tif"%filename.strip(".tif") writer.SetInputData(image) - writer.SetFileName(filename) + writer.SetFileName(filename.encode(const.FS_ENCODE)) writer.Write() + if not os.path.exists(filename): + wx.MessageBox(_("InVesalius was not able to export this picture"), _("Export picture error")) + for actor in view_prop_list: self.slice_data.renderer.AddViewProp(actor) @@ -1321,9 +1349,10 @@ class Viewer(wx.Panel): del self.slice_data self.slice_data = None - self.canvas.draw_list = [] - self.canvas.remove_from_renderer() - self.canvas = None + if self.canvas: + self.canvas.draw_list = [] + self.canvas.remove_from_renderer() + self.canvas = None self.orientation_texts = [] diff --git a/invesalius/data/viewer_volume.py b/invesalius/data/viewer_volume.py index 4464b9d..b077e1b 100755 --- a/invesalius/data/viewer_volume.py +++ b/invesalius/data/viewer_volume.py @@ -20,6 +20,7 @@ # detalhes. #-------------------------------------------------------------------------- +import os import sys import numpy as np @@ -37,6 +38,15 @@ import invesalius.style as st import invesalius.utils as utils import invesalius.data.measures as measures +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False + PROP_MEASURE = 0.8 class Viewer(wx.Panel): @@ -314,15 +324,24 @@ class Viewer(wx.Panel): self.seed_points) def OnExportPicture(self, pubsub_evt): - Publisher.sendMessage('Begin busy cursor') id, filename, filetype = pubsub_evt.data if id == const.VOLUME: + Publisher.sendMessage('Begin busy cursor') + if _has_win32api: + utils.touch(filename) + win_filename = win32api.GetShortPathName(filename) + self._export_picture(id, win_filename, filetype) + else: + self._export_picture(id, filename, filetype) + Publisher.sendMessage('End busy cursor') + + def _export_picture(self, id, filename, filetype): if filetype == const.FILETYPE_POV: renwin = self.interactor.GetRenderWindow() image = vtk.vtkWindowToImageFilter() image.SetInput(renwin) writer = vtk.vtkPOVExporter() - writer.SetFileName(filename) + writer.SetFileName(filename.encode(const.FS_ENCODE)) writer.SetRenderWindow(renwin) writer.Write() else: @@ -345,12 +364,15 @@ class Viewer(wx.Panel): writer = vtk.vtkPostScriptWriter() elif (filetype == const.FILETYPE_TIF): writer = vtk.vtkTIFFWriter() - filename = "%s.tif"%filename.strip(".tif") + filename = u"%s.tif"%filename.strip(".tif") writer.SetInputData(image) - writer.SetFileName(filename) + writer.SetFileName(filename.encode(const.FS_ENCODE)) writer.Write() - Publisher.sendMessage('End busy cursor') + + if not os.path.exists(filename): + wx.MessageBox(_("InVesalius was not able to export this picture"), _("Export picture error")) + def OnCloseProject(self, pubsub_evt): if self.raycasting_volume: @@ -764,6 +786,18 @@ class Viewer(wx.Panel): def OnExportSurface(self, pubsub_evt): filename, filetype = pubsub_evt.data + if filetype not in (const.FILETYPE_STL, + const.FILETYPE_VTP, + const.FILETYPE_PLY, + const.FILETYPE_STL_ASCII): + if _has_win32api: + utils.touch(filename) + win_filename = win32api.GetShortPathName(filename) + self._export_surface(win_filename.encode(const.FS_ENCODE), filetype) + else: + self._export_surface(filename.encode(const.FS_ENCODE), filetype) + + def _export_surface(self, filename, filetype): fileprefix = filename.split(".")[-2] renwin = self.interactor.GetRenderWindow() diff --git a/invesalius/expanduser.py b/invesalius/expanduser.py new file mode 100644 index 0000000..8aac381 --- /dev/null +++ b/invesalius/expanduser.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# From http://bugs.python.org/file23442/expanduser.py + +import ctypes +from ctypes import windll, wintypes + +class GUID(ctypes.Structure): + _fields_ = [ + ('Data1', wintypes.DWORD), + ('Data2', wintypes.WORD), + ('Data3', wintypes.WORD), + ('Data4', wintypes.BYTE * 8) + ] + + def __init__(self, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8): + """Create a new GUID.""" + self.Data1 = l + self.Data2 = w1 + self.Data3 = w2 + self.Data4[:] = (b1, b2, b3, b4, b5, b6, b7, b8) + + def __repr__(self): + b1, b2, b3, b4, b5, b6, b7, b8 = self.Data4 + return 'GUID(%x-%x-%x-%x%x%x%x%x%x%x%x)' % ( + self.Data1, self.Data2, self.Data3, b1, b2, b3, b4, b5, b6, b7, b8) + +# constants to be used according to the version on shell32 +CSIDL_PROFILE = 40 +FOLDERID_Profile = GUID(0x5E6C858F, 0x0E22, 0x4760, 0x9A, 0xFE, 0xEA, 0x33, 0x17, 0xB6, 0x71, 0x73) + +def expand_user(): + # get the function that we can find from Vista up, not the one in XP + get_folder_path = getattr(windll.shell32, 'SHGetKnownFolderPath', None) + #import pdb; pdb.set_trace() + if get_folder_path is not None: + # ok, we can use the new function which is recomended by the msdn + ptr = ctypes.c_wchar_p() + get_folder_path(ctypes.byref(FOLDERID_Profile), 0, 0, ctypes.byref(ptr)) + return ptr.value + else: + # use the deprecated one found in XP and on for compatibility reasons + get_folder_path = getattr(windll.shell32, 'SHGetSpecialFolderPathW', None) + buf = ctypes.create_unicode_buffer(300) + get_folder_path(None, buf, CSIDL_PROFILE, False) + return buf.value diff --git a/invesalius/gui/dialogs.py b/invesalius/gui/dialogs.py index 337a739..79bc7b7 100644 --- a/invesalius/gui/dialogs.py +++ b/invesalius/gui/dialogs.py @@ -323,10 +323,7 @@ def ShowImportBitmapDirDialog(): if dlg.ShowModal() == wx.ID_OK: # GetPath returns in unicode, if a path has non-ascii characters a # UnicodeEncodeError is raised. To avoid this, path is encoded in utf-8 - if sys.platform == "win32": - path = dlg.GetPath() - else: - path = dlg.GetPath().encode('utf-8') + path = dlg.GetPath() except(wx._core.PyAssertionError): #TODO: error win64 if (dlg.GetPath()): @@ -398,12 +395,7 @@ def ShowImportMeshFilesDialog(): filename = None try: if dlg.ShowModal() == wx.ID_OK: - # GetPath returns in unicode, if a path has non-ascii characters a - # UnicodeEncodeError is raised. To avoid this, path is encoded in utf-8 - if sys.platform == "win32": - filename = dlg.GetPath() - else: - filename = dlg.GetPath().encode('utf-8') + filename = dlg.GetPath() except(wx._core.PyAssertionError): # TODO: error win64 if (dlg.GetPath()): @@ -1058,52 +1050,52 @@ def ShowAboutDialog(parent): info.WebSite = ("https://www.cti.gov.br/invesalius") info.License = _("GNU GPL (General Public License) version 2") - info.Developers = ["Paulo Henrique Junqueira Amorim", - "Thiago Franco de Moraes", - "Hélio Pedrini", - "Jorge Vicente Lopes da Silva", - "Victor Hugo de Oliveira e Souza (navigator)", - "Renan Hiroshi Matsuda (navigator)", - "André Salles Cunha Peres (navigator)", - "Oswaldo Baffa Filho (navigator)", - "Tatiana Al-Chueyr (former)", - "Guilherme Cesar Soares Ruppert (former)", - "Fabio de Souza Azevedo (former)", - "Bruno Lara Bottazzini (contributor)", - "Olly Betts (patches to support wxPython3)"] - - info.Translators = ["Alex P. Natsios", - "Alicia Perez", - "Anderson Antonio Mamede da Silva", - "Andreas Loupasakis", - "Angelo Pucillo", - "Annalisa Manenti", - "Cheng-Chia Tseng", - "Dan", - "DCamer", - "Dimitris Glezos", - "Eugene Liscio", + info.Developers = [u"Paulo Henrique Junqueira Amorim", + u"Thiago Franco de Moraes", + u"Hélio Pedrini", + u"Jorge Vicente Lopes da Silva", + u"Victor Hugo de Oliveira e Souza (navigator)", + u"Renan Hiroshi Matsuda (navigator)", + u"André Salles Cunha Peres (navigator)", + u"Oswaldo Baffa Filho (navigator)", + u"Tatiana Al-Chueyr (former)", + u"Guilherme Cesar Soares Ruppert (former)", + u"Fabio de Souza Azevedo (former)", + u"Bruno Lara Bottazzini (contributor)", + u"Olly Betts (patches to support wxPython3)"] + + info.Translators = [u"Alex P. Natsios", + u"Alicia Perez", + u"Anderson Antonio Mamede da Silva", + u"Andreas Loupasakis", + u"Angelo Pucillo", + u"Annalisa Manenti", + u"Cheng-Chia Tseng", + u"Dan", + u"DCamer", + u"Dimitris Glezos", + u"Eugene Liscio", u"Frédéric Lopez", - "Florin Putura", - "Fri", - "Jangblue", - "Javier de Lima Moreno", - "Kensey Okinawa", - "Maki Sugimoto", - "Mario Regino Moreno Guerra", - "Massimo Crisantemo", - "Nikos Korkakakis", - "Raul Bolliger Neto", - "Sebastian Hilbert", - "Semarang Pari", - "Silvério Santos", - "Vasily Shishkin", - "Yohei Sotsuka", - "Yoshihiro Sato"] + u"Florin Putura", + u"Fri", + u"Jangblue", + u"Javier de Lima Moreno", + u"Kensey Okinawa", + u"Maki Sugimoto", + u"Mario Regino Moreno Guerra", + u"Massimo Crisantemo", + u"Nikos Korkakakis", + u"Raul Bolliger Neto", + u"Sebastian Hilbert", + u"Semarang Pari", + u"Silvério Santos", + u"Vasily Shishkin", + u"Yohei Sotsuka", + u"Yoshihiro Sato"] #info.DocWriters = ["Fabio Francisco da Silva (PT)"] - info.Artists = ["Otavio Henrique Junqueira Amorim"] + info.Artists = [u"Otavio Henrique Junqueira Amorim"] # Then we call wx.AboutBox providing its info object wx.AboutBox(info) diff --git a/invesalius/gui/dicom_preview_panel.py b/invesalius/gui/dicom_preview_panel.py index ce12820..52d2326 100644 --- a/invesalius/gui/dicom_preview_panel.py +++ b/invesalius/gui/dicom_preview_panel.py @@ -21,6 +21,7 @@ # -*- coding: UTF-8 -*- #TODO: To create a beautiful API +import sys import time import tempfile @@ -37,6 +38,14 @@ import invesalius.data.vtk_utils as vtku import invesalius.utils as utils import vtkgdcm +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False NROWS = 3 NCOLS = 6 @@ -837,7 +846,10 @@ class SingleImagePreview(wx.Panel): self.text_acquisition.SetValue(value) rdicom = vtkgdcm.vtkGDCMImageReader() - rdicom.SetFileName(dicom.image.file) + if _has_win32api: + rdicom.SetFileName(win32api.GetShortPathName(dicom.image.file).encode(const.FS_ENCODE)) + else: + rdicom.SetFileName(dicom.image.file) rdicom.Update() # ADJUST CONTRAST diff --git a/invesalius/gui/task_exporter.py b/invesalius/gui/task_exporter.py index 5fa28cc..3b62522 100644 --- a/invesalius/gui/task_exporter.py +++ b/invesalius/gui/task_exporter.py @@ -331,6 +331,13 @@ class InnerTaskPanel(wx.Panel): filename = filename + "."+ extension Publisher.sendMessage('Export surface to file', (filename, filetype)) + if not os.path.exists(filename): + dlg = wx.MessageDialog(None, + _("It was not possible to save the surface."), + _("Error saving surface"), + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() else: dlg = wx.MessageDialog(None, _("You need to create a surface and make it ") + diff --git a/invesalius/project.py b/invesalius/project.py index 7949dde..ba21e22 100644 --- a/invesalius/project.py +++ b/invesalius/project.py @@ -22,6 +22,7 @@ import glob import os import plistlib import shutil +import sys import tarfile import tempfile @@ -32,9 +33,18 @@ import vtk import invesalius.constants as const import invesalius.data.polydata_utils as pu from invesalius.presets import Presets -from invesalius.utils import Singleton, debug +from invesalius.utils import Singleton, debug, touch import invesalius.version as version +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False + class Project(object): # Only one project will be initialized per time. Therefore, we use # Singleton design pattern for implementing it @@ -193,10 +203,14 @@ class Project(object): return measures def SavePlistProject(self, dir_, filename): - dir_temp = tempfile.mkdtemp() - filename_tmp = os.path.join(dir_temp, 'matrix.dat') + dir_temp = tempfile.mkdtemp().decode(const.FS_ENCODE) + + filename_tmp = os.path.join(dir_temp, u'matrix.dat') filelist = {} + print type(dir_temp), type(filename) + print filename.encode('utf8'), dir_.encode('utf8'), type(filename), type(dir_) + project = { # Format info "format_version": 1, @@ -214,10 +228,11 @@ class Project(object): } # Saving the matrix containing the slices - matrix = {'filename': u'matrix.dat', - 'shape': self.matrix_shape, - 'dtype': self.matrix_dtype, - } + matrix = { + 'filename': u'matrix.dat', + 'shape': self.matrix_shape, + 'dtype': self.matrix_dtype, + } project['matrix'] = matrix filelist[self.matrix_filename] = 'matrix.dat' #shutil.copyfile(self.matrix_filename, filename_tmp) @@ -270,9 +285,9 @@ class Project(object): import invesalius.data.surface as srf if not const.VTK_WARNING: - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') fow = vtk.vtkFileOutputWindow() - fow.SetFileName(log_path) + fow.SetFileName(log_path.encode(const.FS_ENCODE)) ow = vtk.vtkOutputWindow() ow.SetInstance(fow) @@ -328,29 +343,37 @@ class Project(object): def Compress(folder, filename, filelist): tmpdir, tmpdir_ = os.path.split(folder) current_dir = os.path.abspath(".") + temp_inv3 = tempfile.mktemp() + if _has_win32api: + touch(temp_inv3) + temp_inv3 = win32api.GetShortPathName(temp_inv3) + + temp_inv3 = temp_inv3.decode(const.FS_ENCODE) #os.chdir(tmpdir) #file_list = glob.glob(os.path.join(tmpdir_,"*")) - tar_filename = tmpdir_ + ".inv3" - tar = tarfile.open(filename.encode(wx.GetDefaultPyEncoding()), "w:gz") + print "Tar file", temp_inv3, type(temp_inv3), filename.encode('utf8'), type(filename) + tar = tarfile.open(temp_inv3, "w:gz") for name in filelist: tar.add(name, arcname=os.path.join(tmpdir_, filelist[name])) tar.close() - #shutil.move(tmpdir_+ ".inv3", filename) + shutil.move(temp_inv3, filename) #os.chdir(current_dir) + def Extract(filename, folder): + if _has_win32api: + folder = win32api.GetShortPathName(folder) + folder = folder.decode(const.FS_ENCODE) + tar = tarfile.open(filename, "r:gz") - idir = os.path.split(tar.getnames()[0])[0] - os.mkdir(os.path.join(folder, idir.decode('utf8'))) + idir = os.path.split(tar.getnames()[0])[0].decode('utf8') + os.mkdir(os.path.join(folder, idir)) filelist = [] for t in tar.getmembers(): fsrc = tar.extractfile(t) - fname = os.path.join(folder, t.name.decode('utf-8')) fdst = file(fname, 'wb') - shutil.copyfileobj(fsrc, fdst) - filelist.append(fname) fsrc.close() fdst.close() @@ -359,7 +382,7 @@ def Extract(filename, folder): tar.close() return filelist - + def Extract_(filename, folder): tar = tarfile.open(filename, "r:gz") #tar.list(verbose=True) diff --git a/invesalius/reader/bitmap_reader.py b/invesalius/reader/bitmap_reader.py index af30557..cdb352e 100644 --- a/invesalius/reader/bitmap_reader.py +++ b/invesalius/reader/bitmap_reader.py @@ -40,6 +40,15 @@ import invesalius.data.converters as converters no_error = True vtk_error = False +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False + class Singleton: def __init__(self,klass): @@ -127,10 +136,7 @@ class LoadBitmap: def __init__(self, bmp_file, filepath): self.bmp_file = bmp_file - if sys.platform == 'win32': - self.filepath = filepath.encode(utils.get_system_encoding()) - else: - self.filepath = filepath + self.filepath = filepath self.run() @@ -294,9 +300,9 @@ def ScipyRead(filepath): def VtkRead(filepath, t): if not const.VTK_WARNING: - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') fow = vtk.vtkFileOutputWindow() - fow.SetFileName(log_path) + fow.SetFileName(log_path.encode(const.FS_ENCODE)) ow = vtk.vtkOutputWindow() ow.SetInstance(fow) @@ -317,8 +323,10 @@ def VtkRead(filepath, t): else: return False + print ">>>> bmp reader", type(filepath) + reader.AddObserver("ErrorEvent", VtkErrorToPy) - reader.SetFileName(filepath) + reader.SetFileName(filepath.encode(const.FS_ENCODE)) reader.Update() if no_error: @@ -345,6 +353,9 @@ def VtkRead(filepath, t): def ReadBitmap(filepath): t = VerifyDataType(filepath) + if _has_win32api: + filepath = win32api.GetShortPathName(filepath) + if t == False: measures_info = GetPixelSpacingFromInfoFile(filepath) @@ -368,7 +379,6 @@ def ReadBitmap(filepath): def GetPixelSpacingFromInfoFile(filepath): - fi = open(filepath, 'r') lines = fi.readlines() measure_scale = 'mm' diff --git a/invesalius/reader/dicom_grouper.py b/invesalius/reader/dicom_grouper.py index ad68d0e..f92d012 100644 --- a/invesalius/reader/dicom_grouper.py +++ b/invesalius/reader/dicom_grouper.py @@ -51,9 +51,23 @@ # and # were swapped +import sys + import gdcm +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False + import invesalius.utils as utils +import invesalius.constants as const + + ORIENT_MAP = {"SAGITTAL":0, "CORONAL":1, "AXIAL":2, "OBLIQUE":2} @@ -109,8 +123,13 @@ class DicomGroup: # This list will be used to create the vtkImageData # (interpolated) - filelist = [dicom.image.file for dicom in - self.slices_dict.values()] + if _has_win32api: + filelist = [win32api.GetShortPathName(dicom.image.file).encode(const.FS_ENCODE) + for dicom in + self.slices_dict.values()] + else: + filelist = [dicom.image.file for dicom in + self.slices_dict.values()] # Sort slices using GDCM if (self.dicom.image.orientation_label <> "CORONAL"): diff --git a/invesalius/reader/dicom_reader.py b/invesalius/reader/dicom_reader.py index 6e0a793..f569ca2 100644 --- a/invesalius/reader/dicom_reader.py +++ b/invesalius/reader/dicom_reader.py @@ -38,6 +38,15 @@ import invesalius.utils as utils import plistlib +if sys.platform == 'win32': + try: + import win32api + _has_win32api = True + except ImportError: + _has_win32api = False +else: + _has_win32api = False + def ReadDicomGroup(dir_): patient_group = GetDicomGroups(dir_) @@ -88,18 +97,18 @@ class LoadDicom: def __init__(self, grouper, filepath): self.grouper = grouper - if sys.platform == 'win32': - self.filepath = filepath.encode(utils.get_system_encoding()) - else: - self.filepath = filepath + self.filepath = filepath self.run() def run(self): - grouper = self.grouper reader = gdcm.ImageReader() - reader.SetFileName(self.filepath) + if _has_win32api: + reader.SetFileName(win32api.GetShortPathName(self.filepath).encode(const.FS_ENCODE)) + else: + reader.SetFileName(self.filepath) + if (reader.Read()): file = reader.GetFile() @@ -180,7 +189,12 @@ class LoadDicom: # -------------- To Create DICOM Thumbnail ----------- rvtk = vtkgdcm.vtkGDCMImageReader() - rvtk.SetFileName(self.filepath) + + if _has_win32api: + print 'dicom', win32api.GetShortPathName(self.filepath) + rvtk.SetFileName(win32api.GetShortPathName(self.filepath).encode(const.FS_ENCODE)) + else: + rvtk.SetFileName(self.filepath) rvtk.Update() try: @@ -341,7 +355,7 @@ class ProgressDicomReader: def GetDicomGroups(self, path, recursive): if not const.VTK_WARNING: - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt').encode(const.FS_ENCODE) fow = vtk.vtkFileOutputWindow() fow.SetFileName(log_path) ow = vtk.vtkOutputWindow() diff --git a/invesalius/reader/others_reader.py b/invesalius/reader/others_reader.py index fa6407f..2cb4e3f 100644 --- a/invesalius/reader/others_reader.py +++ b/invesalius/reader/others_reader.py @@ -38,9 +38,9 @@ def ReadOthers(dir_): """ if not const.VTK_WARNING: - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') fow = vtk.vtkFileOutputWindow() - fow.SetFileName(log_path) + fow.SetFileName(log_path.encode(const.FS_ENCODE)) ow = vtk.vtkOutputWindow() ow.SetInstance(fow) diff --git a/invesalius/session.py b/invesalius/session.py index b5512b4..139199e 100644 --- a/invesalius/session.py +++ b/invesalius/session.py @@ -36,7 +36,24 @@ import wx from invesalius.utils import Singleton, debug from random import randint -ENCODE=wx.GetDefaultPyEncoding() +FS_ENCODE = sys.getfilesystemencoding() + +if sys.platform == 'win32': + from invesalius.expanduser import expand_user + try: + USER_DIR = expand_user() + except: + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) +else: + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) + +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') + +SESSION_ENCODING = 'utf8' + class Session(object): # Only one session will be initialized per time. Therefore, we use @@ -62,7 +79,7 @@ class Session(object): # const.MODE_ODONTOLOGY # InVesalius default projects' directory - homedir = self.homedir = os.path.expanduser('~').decode(ENCODE) + homedir = self.homedir = USER_DIR tempdir = os.path.join(homedir, u".invesalius", u"temp") if not os.path.isdir(tempdir): os.makedirs(tempdir) @@ -72,10 +89,9 @@ class Session(object): self.language = "" # "pt_BR", "es" self.random_id = randint(0,pow(10,16)) - #print self.random_id # Recent projects list - self.recent_projects = [(const.SAMPLE_DIR, "Cranium.inv3")] + self.recent_projects = [(const.SAMPLE_DIR, u"Cranium.inv3")] self.last_dicom_folder = '' self.surface_interpolation = 1 self.slice_interpolation = 0 @@ -88,18 +104,18 @@ class Session(object): def SaveConfigFileBackup(self): path = os.path.join(self.homedir , - '.invesalius', 'config.cfg') + u'.invesalius', u'config.cfg') path_dst = os.path.join(self.homedir , - '.invesalius', 'config.backup') + u'.invesalius', u'config.backup') shutil.copy(path, path_dst) def RecoveryConfigFile(self): homedir = self.homedir = os.path.expanduser('~') try: path = os.path.join(self.homedir , - '.invesalius', 'config.backup') + u'.invesalius', u'config.backup') path_dst = os.path.join(self.homedir , - '.invesalius', 'config.cfg') + u'.invesalius', u'config.cfg') shutil.copy(path, path_dst) return True except(IOError): @@ -163,10 +179,6 @@ class Session(object): def WriteSessionFile(self): config = ConfigParser.RawConfigParser() - print ">>>", type(self.homedir), self.homedir, repr(self.homedir) - print ">>>", type(self.tempdir), self.tempdir, repr(self.tempdir) - print ">>>", type(self.language), self.language, repr(self.language) - config.add_section('session') config.set('session', 'mode', self.mode) config.set('session', 'status', self.project_status) @@ -185,18 +197,10 @@ class Session(object): config.set('paths','tempdir',self.tempdir) config.set('paths','last_dicom_folder',self.last_dicom_folder) - print config.items('session') - print config.items('project') - print config.items('paths') - path = os.path.join(self.homedir , '.invesalius', 'config.cfg') - if sys.platform == 'win32': - configfile = codecs.open(path, 'wb', 'utf8') - else: - configfile = open(path, 'wb') - + configfile = codecs.open(path, 'wb', SESSION_ENCODING) config.write(configfile) configfile.close() @@ -238,12 +242,15 @@ class Session(object): def ReadLanguage(self): config = ConfigParser.ConfigParser() - home_path = os.path.expanduser('~') - path = os.path.join(home_path ,'.invesalius', 'config.cfg') + path = os.path.join(USER_INV_DIR, 'config.cfg') try: - config.read(path) + 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): @@ -251,12 +258,15 @@ class Session(object): def ReadRandomId(self): config = ConfigParser.ConfigParser() - home_path = os.path.expanduser('~') - path = os.path.join(home_path ,'.invesalius', 'config.cfg') + path = os.path.join(USER_INV_DIR, 'config.cfg') try: - config.read(path) + 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): @@ -264,10 +274,11 @@ class Session(object): def ReadSession(self): config = ConfigParser.ConfigParser() - home_path = os.path.expanduser('~').decode(ENCODE) - path = os.path.join(home_path ,'.invesalius', 'config.cfg') + path = USER_INV_CFG_PATH try: - config.readfp(codecs.open(path, 'rb', 'utf8')) + 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 @@ -295,7 +306,6 @@ class Session(object): except(ConfigParser.NoSectionError, ConfigParser.MissingSectionHeaderError, ConfigParser.ParsingError): - print 'Error 1' if (self.RecoveryConfigFile()): self.ReadSession() return True @@ -303,7 +313,6 @@ class Session(object): return False except(ConfigParser.NoOptionError): - print 'Error 2' #Added to fix new version compatibility self.surface_interpolation = 0 self.slice_interpolation = 0 @@ -312,6 +321,5 @@ class Session(object): try: self.WriteSessionFile() except AttributeError: - print 'Error 3' return False return True diff --git a/invesalius/utils.py b/invesalius/utils.py index f675788..eb329c9 100644 --- a/invesalius/utils.py +++ b/invesalius/utils.py @@ -436,3 +436,8 @@ def vtkarray_to_numpy(m): for j in xrange(4): nm[i, j] = m.GetElement(i, j) return nm + + +def touch(fname): + with open(fname, 'a'): + pass -- libgit2 0.21.2