Commit 56415f0ed79f7b5e686f125e817273231ab1b616
Committed by
GitHub
1 parent
f30a57f6
Exists in
master
Encoding Errors in Windows (#110)
Fix the above unicode related errors: - InVesalius was not starting in Windows with users with non ascii chars. - Not opening dicom files from path with non ascii chars. - Not opening bitmap files from path with non ascii chars. - Not opening Analyze, Nifti with filename with non ascii chars. - Not exporting surface when the path has non ascii chars. - Not exporting picture when the path has non ascii chars. - Not importing surface when the path has non ascii chars. - Strange chars when showing developers and translators with non ascii chars. To fix that errors we are convert all paths and filenames to unicode. wxPythons eases this, since it gives the paths and filenames as unicode. When using VTK to save or open a file we encode the filename to sys.getfilesystemencoding. In Windows it is necessary an additional step, convert the filename to an short representation using [win32api](http://docs.activestate.com/activepython/2.6/pywin32/win32api__GetShortPathName_meth.html).
Showing
20 changed files
with
477 additions
and
205 deletions
Show diff stats
app.py
... | ... | @@ -54,6 +54,22 @@ import invesalius.i18n as i18n |
54 | 54 | import invesalius.session as ses |
55 | 55 | import invesalius.utils as utils |
56 | 56 | |
57 | +FS_ENCODE = sys.getfilesystemencoding() | |
58 | + | |
59 | +if sys.platform == 'win32': | |
60 | + from invesalius.expanduser import expand_user | |
61 | + try: | |
62 | + USER_DIR = expand_user() | |
63 | + except: | |
64 | + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) | |
65 | +else: | |
66 | + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) | |
67 | + | |
68 | +USER_INV_DIR = os.path.join(USER_DIR, u'.invesalius') | |
69 | +USER_PRESET_DIR = os.path.join(USER_INV_DIR, u'presets') | |
70 | +USER_RAYCASTING_PRESETS_DIRECTORY = os.path.join(USER_PRESET_DIR, u'raycasting') | |
71 | +USER_LOG_DIR = os.path.join(USER_INV_DIR, u'logs') | |
72 | + | |
57 | 73 | # ------------------------------------------------------------------ |
58 | 74 | |
59 | 75 | |
... | ... | @@ -157,7 +173,7 @@ class SplashScreen(wx.SplashScreen): |
157 | 173 | # Only after language was defined, splash screen will be |
158 | 174 | # shown |
159 | 175 | if lang: |
160 | - print "LANG", lang, _, wx.Locale(), wx.GetLocale() | |
176 | + # print "LANG", lang, _, wx.Locale(), wx.GetLocale() | |
161 | 177 | import locale |
162 | 178 | locale.setlocale(locale.LC_ALL, '') |
163 | 179 | # For pt_BR, splash_pt.png should be used |
... | ... | @@ -237,6 +253,8 @@ def parse_comand_line(): |
237 | 253 | """ |
238 | 254 | session = ses.Session() |
239 | 255 | |
256 | + print ">>>> stdin encoding", sys.stdin.encoding | |
257 | + | |
240 | 258 | # Parse command line arguments |
241 | 259 | parser = op.OptionParser() |
242 | 260 | |
... | ... | @@ -269,7 +287,7 @@ def parse_comand_line(): |
269 | 287 | i = len(args) |
270 | 288 | while i: |
271 | 289 | i -= 1 |
272 | - file = args[i] | |
290 | + file = args[i].decode(sys.stdin.encoding) | |
273 | 291 | if os.path.isfile(file): |
274 | 292 | path = os.path.abspath(file) |
275 | 293 | Publisher.sendMessage('Open project', path) |
... | ... | @@ -308,22 +326,16 @@ if __name__ == '__main__': |
308 | 326 | os.chdir(path) |
309 | 327 | |
310 | 328 | # Create raycasting presets' folder, if it doens't exist |
311 | - dirpath = os.path.join(os.path.expanduser('~'), | |
312 | - ".invesalius", | |
313 | - "presets") | |
314 | - if not os.path.isdir(dirpath): | |
315 | - os.makedirs(dirpath) | |
329 | + if not os.path.isdir(USER_RAYCASTING_PRESETS_DIRECTORY): | |
330 | + os.makedirs(USER_RAYCASTING_PRESETS_DIRECTORY) | |
316 | 331 | |
317 | 332 | # Create logs' folder, if it doesn't exist |
318 | - dirpath = os.path.join(os.path.expanduser('~'), | |
319 | - ".invesalius", | |
320 | - "logs") | |
321 | - if not os.path.isdir(dirpath): | |
322 | - os.makedirs(dirpath) | |
333 | + if not os.path.isdir(USER_LOG_DIR): | |
334 | + os.makedirs(USER_LOG_DIR) | |
323 | 335 | |
324 | 336 | if hasattr(sys,"frozen") and sys.frozen == "windows_exe": |
325 | 337 | # Set system standard error output to file |
326 | - path = os.path.join(dirpath, "stderr.log") | |
338 | + path = os.path.join(USER_LOG_DIR, u"stderr.log") | |
327 | 339 | sys.stderr = open(path, "w") |
328 | 340 | |
329 | 341 | # Add current directory to PYTHONPATH, so other classes can | ... | ... |
invesalius/constants.py
... | ... | @@ -320,46 +320,63 @@ WINDOW_LEVEL = {_("Abdomen"):(350,50), |
320 | 320 | |
321 | 321 | REDUCE_IMAGEDATA_QUALITY = 0 |
322 | 322 | |
323 | -FILE_PATH = os.path.split(__file__)[0] | |
323 | + | |
324 | +# PATHS | |
325 | +FS_ENCODE = sys.getfilesystemencoding() | |
326 | + | |
327 | +if sys.platform == 'win32': | |
328 | + from invesalius.expanduser import expand_user | |
329 | + try: | |
330 | + USER_DIR = expand_user() | |
331 | + except: | |
332 | + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) | |
333 | +else: | |
334 | + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) | |
335 | + | |
336 | +USER_INV_DIR = os.path.join(USER_DIR, u'.invesalius') | |
337 | +USER_PRESET_DIR = os.path.join(USER_INV_DIR, u'presets') | |
338 | +USER_LOG_DIR = os.path.join(USER_INV_DIR, u'logs') | |
339 | + | |
340 | +FILE_PATH = os.path.split(__file__)[0].decode(FS_ENCODE) | |
324 | 341 | |
325 | 342 | if hasattr(sys,"frozen") and (sys.frozen == "windows_exe"\ |
326 | 343 | or sys.frozen == "console_exe"): |
327 | - abs_path = os.path.abspath(FILE_PATH + os.sep + ".." + os.sep + ".." + os.sep + "..") | |
328 | - ICON_DIR = os.path.join(abs_path, "icons") | |
329 | - SAMPLE_DIR = os.path.join(FILE_PATH, 'samples') | |
330 | - DOC_DIR = os.path.join(FILE_PATH, 'docs') | |
331 | - folder=RAYCASTING_PRESETS_DIRECTORY= os.path.join(abs_path, "presets", "raycasting") | |
332 | - RAYCASTING_PRESETS_COLOR_DIRECTORY = os.path.join(abs_path, "presets", "raycasting", "color_list") | |
344 | + abs_path = os.path.abspath(FILE_PATH, u'..', u'..', u'..') | |
345 | + ICON_DIR = os.path.join(abs_path, u"icons") | |
346 | + SAMPLE_DIR = os.path.join(FILE_PATH, u'samples') | |
347 | + DOC_DIR = os.path.join(FILE_PATH, u'docs') | |
348 | + folder=RAYCASTING_PRESETS_DIRECTORY= os.path.join(abs_path, u"presets", u"raycasting") | |
349 | + RAYCASTING_PRESETS_COLOR_DIRECTORY = os.path.join(abs_path, u"presets", u"raycasting", u"color_list") | |
333 | 350 | |
334 | 351 | else: |
335 | - ICON_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'icons')) | |
336 | - SAMPLE_DIR = os.path.abspath(os.path.join(FILE_PATH,'..', 'samples')) | |
337 | - DOC_DIR = os.path.abspath(os.path.join(FILE_PATH,'..', 'docs')) | |
352 | + ICON_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'icons')) | |
353 | + SAMPLE_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'samples')) | |
354 | + DOC_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'docs')) | |
338 | 355 | |
339 | - folder=RAYCASTING_PRESETS_DIRECTORY= os.path.abspath(os.path.join(".", | |
340 | - "presets", | |
341 | - "raycasting")) | |
356 | + folder=RAYCASTING_PRESETS_DIRECTORY= os.path.abspath(os.path.join(u".", | |
357 | + u"presets", | |
358 | + u"raycasting")) | |
342 | 359 | |
343 | - RAYCASTING_PRESETS_COLOR_DIRECTORY = os.path.abspath(os.path.join(".", | |
344 | - "presets", | |
345 | - "raycasting", | |
346 | - "color_list")) | |
360 | + RAYCASTING_PRESETS_COLOR_DIRECTORY = os.path.abspath(os.path.join(u".", | |
361 | + u"presets", | |
362 | + u"raycasting", | |
363 | + u"color_list")) | |
347 | 364 | |
348 | 365 | |
349 | 366 | # MAC App |
350 | 367 | if not os.path.exists(ICON_DIR): |
351 | - ICON_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', '..', '..', '..', 'icons')) | |
352 | - SAMPLE_DIR = os.path.abspath(os.path.join(FILE_PATH,'..', '..', '..', '..', 'samples')) | |
353 | - DOC_DIR = os.path.abspath(os.path.join(FILE_PATH,'..', '..', '..', '..', 'docs')) | |
354 | - | |
355 | - | |
356 | -ID_TO_BMP = {VOL_FRONT: [_("Front"), os.path.join(ICON_DIR, "view_front.png")], | |
357 | - VOL_BACK: [_("Back"), os.path.join(ICON_DIR, "view_back.png")], | |
358 | - VOL_TOP: [_("Top"), os.path.join(ICON_DIR, "view_top.png")], | |
359 | - VOL_BOTTOM: [_("Bottom"), os.path.join(ICON_DIR, "view_bottom.png")], | |
360 | - VOL_RIGHT: [_("Right"), os.path.join(ICON_DIR, "view_right.png")], | |
361 | - VOL_LEFT: [_("Left"), os.path.join(ICON_DIR, "view_left.png")], | |
362 | - VOL_ISO:[_("Isometric"), os.path.join(ICON_DIR,"view_isometric.png")] | |
368 | + ICON_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'..', u'..', u'..', u'icons')) | |
369 | + SAMPLE_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'..', u'..', u'..', u'samples')) | |
370 | + DOC_DIR = os.path.abspath(os.path.join(FILE_PATH, u'..', u'..', u'..', u'..', u'docs')) | |
371 | + | |
372 | + | |
373 | +ID_TO_BMP = {VOL_FRONT: [_("Front"), os.path.join(ICON_DIR, u"view_front.png")], | |
374 | + VOL_BACK: [_("Back"), os.path.join(ICON_DIR, u"view_back.png")], | |
375 | + VOL_TOP: [_("Top"), os.path.join(ICON_DIR, u"view_top.png")], | |
376 | + VOL_BOTTOM: [_("Bottom"), os.path.join(ICON_DIR, u"view_bottom.png")], | |
377 | + VOL_RIGHT: [_("Right"), os.path.join(ICON_DIR, u"view_right.png")], | |
378 | + VOL_LEFT: [_("Left"), os.path.join(ICON_DIR, u"view_left.png")], | |
379 | + VOL_ISO:[_("Isometric"), os.path.join(ICON_DIR, u"view_isometric.png")] | |
363 | 380 | } |
364 | 381 | |
365 | 382 | # if 1, use vtkVolumeRaycastMapper, if 0, use vtkFixedPointVolumeRayCastMapper |
... | ... | @@ -405,19 +422,10 @@ RAYCASTING_FILES = {_("Airways"): "Airways.plist", |
405 | 422 | # os.path.isfile(os.path.join(folder,filename))] |
406 | 423 | |
407 | 424 | |
408 | -LOG_FOLDER = os.path.join(os.path.expanduser('~'), '.invesalius', 'logs') | |
409 | -if not os.path.isdir(LOG_FOLDER): | |
410 | - os.makedirs(LOG_FOLDER) | |
411 | - | |
412 | -folder = os.path.join(os.path.expanduser('~'), '.invesalius', 'presets') | |
413 | -if not os.path.isdir(folder): | |
414 | - os.makedirs(folder) | |
415 | - | |
416 | - | |
417 | -USER_RAYCASTING_PRESETS_DIRECTORY = folder | |
425 | +USER_RAYCASTING_PRESETS_DIRECTORY = os.path.join(USER_PRESET_DIR, u'raycasting') | |
418 | 426 | RAYCASTING_TYPES = [_(filename.split(".")[0]) for filename in |
419 | - os.listdir(folder) if | |
420 | - os.path.isfile(os.path.join(folder,filename))] | |
427 | + os.listdir(USER_RAYCASTING_PRESETS_DIRECTORY) if | |
428 | + os.path.isfile(os.path.join(USER_RAYCASTING_PRESETS_DIRECTORY, filename))] | |
421 | 429 | RAYCASTING_TYPES += RAYCASTING_FILES.keys() |
422 | 430 | RAYCASTING_TYPES.append(_(' Off')) |
423 | 431 | RAYCASTING_TYPES.sort() | ... | ... |
invesalius/control.py
... | ... | @@ -338,6 +338,9 @@ class Controller(): |
338 | 338 | else: |
339 | 339 | dirpath, filename = session.project_path |
340 | 340 | |
341 | + if isinstance(filename, str): | |
342 | + filename = filename.decode(const.FS_ENCODE) | |
343 | + | |
341 | 344 | proj = prj.Project() |
342 | 345 | prj.Project().SavePlistProject(dirpath, filename) |
343 | 346 | ... | ... |
invesalius/data/imagedata_utils.py
... | ... | @@ -227,7 +227,7 @@ def CreateImageData(filelist, zspacing, xyspacing,size, |
227 | 227 | message = _("Generating multiplanar visualization...") |
228 | 228 | |
229 | 229 | if not const.VTK_WARNING: |
230 | - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') | |
230 | + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') | |
231 | 231 | fow = vtk.vtkFileOutputWindow() |
232 | 232 | fow.SetFileName(log_path) |
233 | 233 | ow = vtk.vtkOutputWindow() |
... | ... | @@ -332,7 +332,7 @@ class ImageCreator: |
332 | 332 | message = _("Generating multiplanar visualization...") |
333 | 333 | |
334 | 334 | if not const.VTK_WARNING: |
335 | - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') | |
335 | + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') | |
336 | 336 | fow = vtk.vtkFileOutputWindow() |
337 | 337 | fow.SetFileName(log_path) |
338 | 338 | ow = vtk.vtkOutputWindow() | ... | ... |
invesalius/data/mask.py
... | ... | @@ -245,7 +245,7 @@ class Mask(): |
245 | 245 | mask['mask_shape'] = self.matrix.shape |
246 | 246 | mask['edited'] = self.was_edited |
247 | 247 | |
248 | - plist_filename = filename + '.plist' | |
248 | + plist_filename = filename + u'.plist' | |
249 | 249 | #plist_filepath = os.path.join(dir_temp, plist_filename) |
250 | 250 | |
251 | 251 | temp_plist = tempfile.mktemp() | ... | ... |
invesalius/data/polydata_utils.py
... | ... | @@ -23,7 +23,18 @@ import vtk |
23 | 23 | import wx |
24 | 24 | from wx.lib.pubsub import pub as Publisher |
25 | 25 | |
26 | +import invesalius.constants as const | |
26 | 27 | import invesalius.data.vtk_utils as vu |
28 | +from invesalius.utils import touch | |
29 | + | |
30 | +if sys.platform == 'win32': | |
31 | + try: | |
32 | + import win32api | |
33 | + _has_win32api = True | |
34 | + except ImportError: | |
35 | + _has_win32api = False | |
36 | +else: | |
37 | + _has_win32api = False | |
27 | 38 | |
28 | 39 | # Update progress value in GUI |
29 | 40 | UpdateProgress = vu.ShowProgress() |
... | ... | @@ -109,8 +120,10 @@ def Merge(polydata_list): |
109 | 120 | |
110 | 121 | def Export(polydata, filename, bin=False): |
111 | 122 | writer = vtk.vtkXMLPolyDataWriter() |
112 | - print filename, type(filename) | |
113 | - writer.SetFileName(filename.encode('utf-8')) | |
123 | + if _has_win32api: | |
124 | + touch(filename) | |
125 | + filename = win32api.GetShortPathName(filename) | |
126 | + writer.SetFileName(filename.encode(const.FS_ENCODE)) | |
114 | 127 | if bin: |
115 | 128 | writer.SetDataModeToBinary() |
116 | 129 | else: | ... | ... |
invesalius/data/surface.py
... | ... | @@ -21,6 +21,8 @@ import multiprocessing |
21 | 21 | import os |
22 | 22 | import plistlib |
23 | 23 | import random |
24 | +import shutil | |
25 | +import sys | |
24 | 26 | import tempfile |
25 | 27 | import weakref |
26 | 28 | |
... | ... | @@ -28,6 +30,15 @@ import vtk |
28 | 30 | import wx |
29 | 31 | from wx.lib.pubsub import pub as Publisher |
30 | 32 | |
33 | +if sys.platform == 'win32': | |
34 | + try: | |
35 | + import win32api | |
36 | + _has_win32api = True | |
37 | + except ImportError: | |
38 | + _has_win32api = False | |
39 | +else: | |
40 | + _has_win32api = False | |
41 | + | |
31 | 42 | import invesalius.constants as const |
32 | 43 | import invesalius.data.imagedata_utils as iu |
33 | 44 | import invesalius.data.polydata_utils as pu |
... | ... | @@ -64,8 +75,8 @@ class Surface(): |
64 | 75 | self.name = name |
65 | 76 | |
66 | 77 | def SavePlist(self, dir_temp, filelist): |
67 | - filename = 'surface_%d' % self.index | |
68 | - vtp_filename = filename + '.vtp' | |
78 | + filename = u'surface_%d' % self.index | |
79 | + vtp_filename = filename + u'.vtp' | |
69 | 80 | vtp_filepath = os.path.join(dir_temp, vtp_filename) |
70 | 81 | pu.Export(self.polydata, vtp_filepath, bin=True) |
71 | 82 | |
... | ... | @@ -80,7 +91,7 @@ class Surface(): |
80 | 91 | 'volume': self.volume, |
81 | 92 | 'area': self.area, |
82 | 93 | } |
83 | - plist_filename = filename + '.plist' | |
94 | + plist_filename = filename + u'.plist' | |
84 | 95 | #plist_filepath = os.path.join(dir_temp, filename + '.plist') |
85 | 96 | temp_plist = tempfile.mktemp() |
86 | 97 | plistlib.writePlist(surface, temp_plist) |
... | ... | @@ -263,7 +274,11 @@ class SurfaceManager(): |
263 | 274 | wx.MessageBox(_("File format not reconized by InVesalius"), _("Import surface error")) |
264 | 275 | return |
265 | 276 | |
266 | - reader.SetFileName(filename) | |
277 | + if _has_win32api: | |
278 | + reader.SetFileName(win32api.GetShortPathName(filename).encode(const.FS_ENCODE)) | |
279 | + else: | |
280 | + reader.SetFileName(filename.encode(const.FS_ENCODE)) | |
281 | + | |
267 | 282 | reader.Update() |
268 | 283 | polydata = reader.GetOutput() |
269 | 284 | |
... | ... | @@ -869,11 +884,31 @@ class SurfaceManager(): |
869 | 884 | |
870 | 885 | def OnExportSurface(self, pubsub_evt): |
871 | 886 | filename, filetype = pubsub_evt.data |
872 | - if (filetype == const.FILETYPE_STL) or\ | |
873 | - (filetype == const.FILETYPE_VTP) or\ | |
874 | - (filetype == const.FILETYPE_PLY) or\ | |
875 | - (filetype == const.FILETYPE_STL_ASCII): | |
876 | - | |
887 | + ftype_prefix = { | |
888 | + const.FILETYPE_STL: '.stl', | |
889 | + const.FILETYPE_VTP: '.vtp', | |
890 | + const.FILETYPE_PLY: '.ply', | |
891 | + const.FILETYPE_STL_ASCII: '.stl', | |
892 | + } | |
893 | + if filetype in ftype_prefix: | |
894 | + temp_file = tempfile.mktemp(suffix=ftype_prefix[filetype]) | |
895 | + | |
896 | + if _has_win32api: | |
897 | + utl.touch(temp_file) | |
898 | + _temp_file = temp_file | |
899 | + temp_file = win32api.GetShortPathName(temp_file) | |
900 | + os.remove(_temp_file) | |
901 | + | |
902 | + temp_file = temp_file.decode(const.FS_ENCODE) | |
903 | + self._export_surface(temp_file, filetype) | |
904 | + | |
905 | + shutil.move(temp_file, filename) | |
906 | + | |
907 | + def _export_surface(self, filename, filetype): | |
908 | + if filetype in (const.FILETYPE_STL, | |
909 | + const.FILETYPE_VTP, | |
910 | + const.FILETYPE_PLY, | |
911 | + const.FILETYPE_STL_ASCII): | |
877 | 912 | # First we identify all surfaces that are selected |
878 | 913 | # (if any) |
879 | 914 | proj = prj.Project() |
... | ... | @@ -912,7 +947,9 @@ class SurfaceManager(): |
912 | 947 | #writer.SetColorModeToUniformCellColor() |
913 | 948 | #writer.SetColor(255, 0, 0) |
914 | 949 | |
915 | - if filetype in (const.FILETYPE_STL, const.FILETYPE_PLY): | |
950 | + if filetype in (const.FILETYPE_STL, | |
951 | + const.FILETYPE_STL_ASCII, | |
952 | + const.FILETYPE_PLY): | |
916 | 953 | # Invert normals |
917 | 954 | normals = vtk.vtkPolyDataNormals() |
918 | 955 | normals.SetInputData(polydata) |
... | ... | @@ -923,7 +960,7 @@ class SurfaceManager(): |
923 | 960 | normals.Update() |
924 | 961 | polydata = normals.GetOutput() |
925 | 962 | |
926 | - filename = filename.encode(wx.GetDefaultPyEncoding()) | |
963 | + filename = filename.encode(const.FS_ENCODE) | |
927 | 964 | writer.SetFileName(filename) |
928 | 965 | writer.SetInputData(polydata) |
929 | 966 | writer.Write() | ... | ... |
invesalius/data/viewer_slice.py
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | |
22 | 22 | import collections |
23 | 23 | import itertools |
24 | +import os | |
24 | 25 | import tempfile |
25 | 26 | |
26 | 27 | import numpy as np |
... | ... | @@ -49,6 +50,15 @@ import invesalius.session as ses |
49 | 50 | import invesalius.data.converters as converters |
50 | 51 | import invesalius.data.measures as measures |
51 | 52 | |
53 | +if sys.platform == 'win32': | |
54 | + try: | |
55 | + import win32api | |
56 | + _has_win32api = True | |
57 | + except ImportError: | |
58 | + _has_win32api = False | |
59 | +else: | |
60 | + _has_win32api = False | |
61 | + | |
52 | 62 | ID_TO_TOOL_ITEM = {} |
53 | 63 | STR_WL = "WL: %d WW: %d" |
54 | 64 | |
... | ... | @@ -1250,12 +1260,27 @@ class Viewer(wx.Panel): |
1250 | 1260 | self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_SIZING)) |
1251 | 1261 | |
1252 | 1262 | def OnExportPicture(self, pubsub_evt): |
1253 | - Publisher.sendMessage('Begin busy cursor') | |
1263 | + id, filename, filetype = pubsub_evt.data | |
1264 | + | |
1265 | + dict = {"AXIAL": const.AXIAL, | |
1266 | + "CORONAL": const.CORONAL, | |
1267 | + "SAGITAL": const.SAGITAL} | |
1268 | + | |
1269 | + if id == dict[self.orientation]: | |
1270 | + Publisher.sendMessage('Begin busy cursor') | |
1271 | + if _has_win32api: | |
1272 | + utils.touch(filename) | |
1273 | + win_filename = win32api.GetShortPathName(filename) | |
1274 | + self._export_picture(id, win_filename, filetype) | |
1275 | + else: | |
1276 | + self._export_picture(id, filename, filetype) | |
1277 | + Publisher.sendMessage('End busy cursor') | |
1278 | + | |
1279 | + def _export_picture(self, id, filename, filetype): | |
1254 | 1280 | view_prop_list = [] |
1255 | 1281 | view_prop_list.append(self.slice_data.box_actor) |
1256 | 1282 | self.slice_data.renderer.RemoveViewProp(self.slice_data.box_actor) |
1257 | 1283 | |
1258 | - id, filename, filetype = pubsub_evt.data | |
1259 | 1284 | dict = {"AXIAL": const.AXIAL, |
1260 | 1285 | "CORONAL": const.CORONAL, |
1261 | 1286 | "SAGITAL": const.SAGITAL} |
... | ... | @@ -1294,9 +1319,12 @@ class Viewer(wx.Panel): |
1294 | 1319 | filename = "%s.tif"%filename.strip(".tif") |
1295 | 1320 | |
1296 | 1321 | writer.SetInputData(image) |
1297 | - writer.SetFileName(filename) | |
1322 | + writer.SetFileName(filename.encode(const.FS_ENCODE)) | |
1298 | 1323 | writer.Write() |
1299 | 1324 | |
1325 | + if not os.path.exists(filename): | |
1326 | + wx.MessageBox(_("InVesalius was not able to export this picture"), _("Export picture error")) | |
1327 | + | |
1300 | 1328 | for actor in view_prop_list: |
1301 | 1329 | self.slice_data.renderer.AddViewProp(actor) |
1302 | 1330 | |
... | ... | @@ -1321,9 +1349,10 @@ class Viewer(wx.Panel): |
1321 | 1349 | del self.slice_data |
1322 | 1350 | self.slice_data = None |
1323 | 1351 | |
1324 | - self.canvas.draw_list = [] | |
1325 | - self.canvas.remove_from_renderer() | |
1326 | - self.canvas = None | |
1352 | + if self.canvas: | |
1353 | + self.canvas.draw_list = [] | |
1354 | + self.canvas.remove_from_renderer() | |
1355 | + self.canvas = None | |
1327 | 1356 | |
1328 | 1357 | self.orientation_texts = [] |
1329 | 1358 | ... | ... |
invesalius/data/viewer_volume.py
... | ... | @@ -20,6 +20,7 @@ |
20 | 20 | # detalhes. |
21 | 21 | #-------------------------------------------------------------------------- |
22 | 22 | |
23 | +import os | |
23 | 24 | import sys |
24 | 25 | |
25 | 26 | import numpy as np |
... | ... | @@ -37,6 +38,15 @@ import invesalius.style as st |
37 | 38 | import invesalius.utils as utils |
38 | 39 | import invesalius.data.measures as measures |
39 | 40 | |
41 | +if sys.platform == 'win32': | |
42 | + try: | |
43 | + import win32api | |
44 | + _has_win32api = True | |
45 | + except ImportError: | |
46 | + _has_win32api = False | |
47 | +else: | |
48 | + _has_win32api = False | |
49 | + | |
40 | 50 | PROP_MEASURE = 0.8 |
41 | 51 | |
42 | 52 | class Viewer(wx.Panel): |
... | ... | @@ -314,15 +324,24 @@ class Viewer(wx.Panel): |
314 | 324 | self.seed_points) |
315 | 325 | |
316 | 326 | def OnExportPicture(self, pubsub_evt): |
317 | - Publisher.sendMessage('Begin busy cursor') | |
318 | 327 | id, filename, filetype = pubsub_evt.data |
319 | 328 | if id == const.VOLUME: |
329 | + Publisher.sendMessage('Begin busy cursor') | |
330 | + if _has_win32api: | |
331 | + utils.touch(filename) | |
332 | + win_filename = win32api.GetShortPathName(filename) | |
333 | + self._export_picture(id, win_filename, filetype) | |
334 | + else: | |
335 | + self._export_picture(id, filename, filetype) | |
336 | + Publisher.sendMessage('End busy cursor') | |
337 | + | |
338 | + def _export_picture(self, id, filename, filetype): | |
320 | 339 | if filetype == const.FILETYPE_POV: |
321 | 340 | renwin = self.interactor.GetRenderWindow() |
322 | 341 | image = vtk.vtkWindowToImageFilter() |
323 | 342 | image.SetInput(renwin) |
324 | 343 | writer = vtk.vtkPOVExporter() |
325 | - writer.SetFileName(filename) | |
344 | + writer.SetFileName(filename.encode(const.FS_ENCODE)) | |
326 | 345 | writer.SetRenderWindow(renwin) |
327 | 346 | writer.Write() |
328 | 347 | else: |
... | ... | @@ -345,12 +364,15 @@ class Viewer(wx.Panel): |
345 | 364 | writer = vtk.vtkPostScriptWriter() |
346 | 365 | elif (filetype == const.FILETYPE_TIF): |
347 | 366 | writer = vtk.vtkTIFFWriter() |
348 | - filename = "%s.tif"%filename.strip(".tif") | |
367 | + filename = u"%s.tif"%filename.strip(".tif") | |
349 | 368 | |
350 | 369 | writer.SetInputData(image) |
351 | - writer.SetFileName(filename) | |
370 | + writer.SetFileName(filename.encode(const.FS_ENCODE)) | |
352 | 371 | writer.Write() |
353 | - Publisher.sendMessage('End busy cursor') | |
372 | + | |
373 | + if not os.path.exists(filename): | |
374 | + wx.MessageBox(_("InVesalius was not able to export this picture"), _("Export picture error")) | |
375 | + | |
354 | 376 | |
355 | 377 | def OnCloseProject(self, pubsub_evt): |
356 | 378 | if self.raycasting_volume: |
... | ... | @@ -764,6 +786,18 @@ class Viewer(wx.Panel): |
764 | 786 | |
765 | 787 | def OnExportSurface(self, pubsub_evt): |
766 | 788 | filename, filetype = pubsub_evt.data |
789 | + if filetype not in (const.FILETYPE_STL, | |
790 | + const.FILETYPE_VTP, | |
791 | + const.FILETYPE_PLY, | |
792 | + const.FILETYPE_STL_ASCII): | |
793 | + if _has_win32api: | |
794 | + utils.touch(filename) | |
795 | + win_filename = win32api.GetShortPathName(filename) | |
796 | + self._export_surface(win_filename.encode(const.FS_ENCODE), filetype) | |
797 | + else: | |
798 | + self._export_surface(filename.encode(const.FS_ENCODE), filetype) | |
799 | + | |
800 | + def _export_surface(self, filename, filetype): | |
767 | 801 | fileprefix = filename.split(".")[-2] |
768 | 802 | renwin = self.interactor.GetRenderWindow() |
769 | 803 | ... | ... |
... | ... | @@ -0,0 +1,46 @@ |
1 | +#!/usr/bin/python | |
2 | +# -*- coding: utf-8 -*- | |
3 | +# From http://bugs.python.org/file23442/expanduser.py | |
4 | + | |
5 | +import ctypes | |
6 | +from ctypes import windll, wintypes | |
7 | + | |
8 | +class GUID(ctypes.Structure): | |
9 | + _fields_ = [ | |
10 | + ('Data1', wintypes.DWORD), | |
11 | + ('Data2', wintypes.WORD), | |
12 | + ('Data3', wintypes.WORD), | |
13 | + ('Data4', wintypes.BYTE * 8) | |
14 | + ] | |
15 | + | |
16 | + def __init__(self, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8): | |
17 | + """Create a new GUID.""" | |
18 | + self.Data1 = l | |
19 | + self.Data2 = w1 | |
20 | + self.Data3 = w2 | |
21 | + self.Data4[:] = (b1, b2, b3, b4, b5, b6, b7, b8) | |
22 | + | |
23 | + def __repr__(self): | |
24 | + b1, b2, b3, b4, b5, b6, b7, b8 = self.Data4 | |
25 | + return 'GUID(%x-%x-%x-%x%x%x%x%x%x%x%x)' % ( | |
26 | + self.Data1, self.Data2, self.Data3, b1, b2, b3, b4, b5, b6, b7, b8) | |
27 | + | |
28 | +# constants to be used according to the version on shell32 | |
29 | +CSIDL_PROFILE = 40 | |
30 | +FOLDERID_Profile = GUID(0x5E6C858F, 0x0E22, 0x4760, 0x9A, 0xFE, 0xEA, 0x33, 0x17, 0xB6, 0x71, 0x73) | |
31 | + | |
32 | +def expand_user(): | |
33 | + # get the function that we can find from Vista up, not the one in XP | |
34 | + get_folder_path = getattr(windll.shell32, 'SHGetKnownFolderPath', None) | |
35 | + #import pdb; pdb.set_trace() | |
36 | + if get_folder_path is not None: | |
37 | + # ok, we can use the new function which is recomended by the msdn | |
38 | + ptr = ctypes.c_wchar_p() | |
39 | + get_folder_path(ctypes.byref(FOLDERID_Profile), 0, 0, ctypes.byref(ptr)) | |
40 | + return ptr.value | |
41 | + else: | |
42 | + # use the deprecated one found in XP and on for compatibility reasons | |
43 | + get_folder_path = getattr(windll.shell32, 'SHGetSpecialFolderPathW', None) | |
44 | + buf = ctypes.create_unicode_buffer(300) | |
45 | + get_folder_path(None, buf, CSIDL_PROFILE, False) | |
46 | + return buf.value | ... | ... |
invesalius/gui/dialogs.py
... | ... | @@ -323,10 +323,7 @@ def ShowImportBitmapDirDialog(): |
323 | 323 | if dlg.ShowModal() == wx.ID_OK: |
324 | 324 | # GetPath returns in unicode, if a path has non-ascii characters a |
325 | 325 | # UnicodeEncodeError is raised. To avoid this, path is encoded in utf-8 |
326 | - if sys.platform == "win32": | |
327 | - path = dlg.GetPath() | |
328 | - else: | |
329 | - path = dlg.GetPath().encode('utf-8') | |
326 | + path = dlg.GetPath() | |
330 | 327 | |
331 | 328 | except(wx._core.PyAssertionError): #TODO: error win64 |
332 | 329 | if (dlg.GetPath()): |
... | ... | @@ -398,12 +395,7 @@ def ShowImportMeshFilesDialog(): |
398 | 395 | filename = None |
399 | 396 | try: |
400 | 397 | if dlg.ShowModal() == wx.ID_OK: |
401 | - # GetPath returns in unicode, if a path has non-ascii characters a | |
402 | - # UnicodeEncodeError is raised. To avoid this, path is encoded in utf-8 | |
403 | - if sys.platform == "win32": | |
404 | - filename = dlg.GetPath() | |
405 | - else: | |
406 | - filename = dlg.GetPath().encode('utf-8') | |
398 | + filename = dlg.GetPath() | |
407 | 399 | |
408 | 400 | except(wx._core.PyAssertionError): # TODO: error win64 |
409 | 401 | if (dlg.GetPath()): |
... | ... | @@ -1058,52 +1050,52 @@ def ShowAboutDialog(parent): |
1058 | 1050 | info.WebSite = ("https://www.cti.gov.br/invesalius") |
1059 | 1051 | info.License = _("GNU GPL (General Public License) version 2") |
1060 | 1052 | |
1061 | - info.Developers = ["Paulo Henrique Junqueira Amorim", | |
1062 | - "Thiago Franco de Moraes", | |
1063 | - "Hélio Pedrini", | |
1064 | - "Jorge Vicente Lopes da Silva", | |
1065 | - "Victor Hugo de Oliveira e Souza (navigator)", | |
1066 | - "Renan Hiroshi Matsuda (navigator)", | |
1067 | - "André Salles Cunha Peres (navigator)", | |
1068 | - "Oswaldo Baffa Filho (navigator)", | |
1069 | - "Tatiana Al-Chueyr (former)", | |
1070 | - "Guilherme Cesar Soares Ruppert (former)", | |
1071 | - "Fabio de Souza Azevedo (former)", | |
1072 | - "Bruno Lara Bottazzini (contributor)", | |
1073 | - "Olly Betts (patches to support wxPython3)"] | |
1074 | - | |
1075 | - info.Translators = ["Alex P. Natsios", | |
1076 | - "Alicia Perez", | |
1077 | - "Anderson Antonio Mamede da Silva", | |
1078 | - "Andreas Loupasakis", | |
1079 | - "Angelo Pucillo", | |
1080 | - "Annalisa Manenti", | |
1081 | - "Cheng-Chia Tseng", | |
1082 | - "Dan", | |
1083 | - "DCamer", | |
1084 | - "Dimitris Glezos", | |
1085 | - "Eugene Liscio", | |
1053 | + info.Developers = [u"Paulo Henrique Junqueira Amorim", | |
1054 | + u"Thiago Franco de Moraes", | |
1055 | + u"Hélio Pedrini", | |
1056 | + u"Jorge Vicente Lopes da Silva", | |
1057 | + u"Victor Hugo de Oliveira e Souza (navigator)", | |
1058 | + u"Renan Hiroshi Matsuda (navigator)", | |
1059 | + u"André Salles Cunha Peres (navigator)", | |
1060 | + u"Oswaldo Baffa Filho (navigator)", | |
1061 | + u"Tatiana Al-Chueyr (former)", | |
1062 | + u"Guilherme Cesar Soares Ruppert (former)", | |
1063 | + u"Fabio de Souza Azevedo (former)", | |
1064 | + u"Bruno Lara Bottazzini (contributor)", | |
1065 | + u"Olly Betts (patches to support wxPython3)"] | |
1066 | + | |
1067 | + info.Translators = [u"Alex P. Natsios", | |
1068 | + u"Alicia Perez", | |
1069 | + u"Anderson Antonio Mamede da Silva", | |
1070 | + u"Andreas Loupasakis", | |
1071 | + u"Angelo Pucillo", | |
1072 | + u"Annalisa Manenti", | |
1073 | + u"Cheng-Chia Tseng", | |
1074 | + u"Dan", | |
1075 | + u"DCamer", | |
1076 | + u"Dimitris Glezos", | |
1077 | + u"Eugene Liscio", | |
1086 | 1078 | u"Frédéric Lopez", |
1087 | - "Florin Putura", | |
1088 | - "Fri", | |
1089 | - "Jangblue", | |
1090 | - "Javier de Lima Moreno", | |
1091 | - "Kensey Okinawa", | |
1092 | - "Maki Sugimoto", | |
1093 | - "Mario Regino Moreno Guerra", | |
1094 | - "Massimo Crisantemo", | |
1095 | - "Nikos Korkakakis", | |
1096 | - "Raul Bolliger Neto", | |
1097 | - "Sebastian Hilbert", | |
1098 | - "Semarang Pari", | |
1099 | - "Silvério Santos", | |
1100 | - "Vasily Shishkin", | |
1101 | - "Yohei Sotsuka", | |
1102 | - "Yoshihiro Sato"] | |
1079 | + u"Florin Putura", | |
1080 | + u"Fri", | |
1081 | + u"Jangblue", | |
1082 | + u"Javier de Lima Moreno", | |
1083 | + u"Kensey Okinawa", | |
1084 | + u"Maki Sugimoto", | |
1085 | + u"Mario Regino Moreno Guerra", | |
1086 | + u"Massimo Crisantemo", | |
1087 | + u"Nikos Korkakakis", | |
1088 | + u"Raul Bolliger Neto", | |
1089 | + u"Sebastian Hilbert", | |
1090 | + u"Semarang Pari", | |
1091 | + u"Silvério Santos", | |
1092 | + u"Vasily Shishkin", | |
1093 | + u"Yohei Sotsuka", | |
1094 | + u"Yoshihiro Sato"] | |
1103 | 1095 | |
1104 | 1096 | #info.DocWriters = ["Fabio Francisco da Silva (PT)"] |
1105 | 1097 | |
1106 | - info.Artists = ["Otavio Henrique Junqueira Amorim"] | |
1098 | + info.Artists = [u"Otavio Henrique Junqueira Amorim"] | |
1107 | 1099 | |
1108 | 1100 | # Then we call wx.AboutBox providing its info object |
1109 | 1101 | wx.AboutBox(info) | ... | ... |
invesalius/gui/dicom_preview_panel.py
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | # -*- coding: UTF-8 -*- |
22 | 22 | |
23 | 23 | #TODO: To create a beautiful API |
24 | +import sys | |
24 | 25 | import time |
25 | 26 | import tempfile |
26 | 27 | |
... | ... | @@ -37,6 +38,14 @@ import invesalius.data.vtk_utils as vtku |
37 | 38 | import invesalius.utils as utils |
38 | 39 | import vtkgdcm |
39 | 40 | |
41 | +if sys.platform == 'win32': | |
42 | + try: | |
43 | + import win32api | |
44 | + _has_win32api = True | |
45 | + except ImportError: | |
46 | + _has_win32api = False | |
47 | +else: | |
48 | + _has_win32api = False | |
40 | 49 | |
41 | 50 | NROWS = 3 |
42 | 51 | NCOLS = 6 |
... | ... | @@ -837,7 +846,10 @@ class SingleImagePreview(wx.Panel): |
837 | 846 | self.text_acquisition.SetValue(value) |
838 | 847 | |
839 | 848 | rdicom = vtkgdcm.vtkGDCMImageReader() |
840 | - rdicom.SetFileName(dicom.image.file) | |
849 | + if _has_win32api: | |
850 | + rdicom.SetFileName(win32api.GetShortPathName(dicom.image.file).encode(const.FS_ENCODE)) | |
851 | + else: | |
852 | + rdicom.SetFileName(dicom.image.file) | |
841 | 853 | rdicom.Update() |
842 | 854 | |
843 | 855 | # ADJUST CONTRAST | ... | ... |
invesalius/gui/task_exporter.py
... | ... | @@ -331,6 +331,13 @@ class InnerTaskPanel(wx.Panel): |
331 | 331 | filename = filename + "."+ extension |
332 | 332 | Publisher.sendMessage('Export surface to file', |
333 | 333 | (filename, filetype)) |
334 | + if not os.path.exists(filename): | |
335 | + dlg = wx.MessageDialog(None, | |
336 | + _("It was not possible to save the surface."), | |
337 | + _("Error saving surface"), | |
338 | + wx.OK | wx.ICON_ERROR) | |
339 | + dlg.ShowModal() | |
340 | + dlg.Destroy() | |
334 | 341 | else: |
335 | 342 | dlg = wx.MessageDialog(None, |
336 | 343 | _("You need to create a surface and make it ") + | ... | ... |
invesalius/project.py
... | ... | @@ -22,6 +22,7 @@ import glob |
22 | 22 | import os |
23 | 23 | import plistlib |
24 | 24 | import shutil |
25 | +import sys | |
25 | 26 | import tarfile |
26 | 27 | import tempfile |
27 | 28 | |
... | ... | @@ -32,9 +33,18 @@ import vtk |
32 | 33 | import invesalius.constants as const |
33 | 34 | import invesalius.data.polydata_utils as pu |
34 | 35 | from invesalius.presets import Presets |
35 | -from invesalius.utils import Singleton, debug | |
36 | +from invesalius.utils import Singleton, debug, touch | |
36 | 37 | import invesalius.version as version |
37 | 38 | |
39 | +if sys.platform == 'win32': | |
40 | + try: | |
41 | + import win32api | |
42 | + _has_win32api = True | |
43 | + except ImportError: | |
44 | + _has_win32api = False | |
45 | +else: | |
46 | + _has_win32api = False | |
47 | + | |
38 | 48 | class Project(object): |
39 | 49 | # Only one project will be initialized per time. Therefore, we use |
40 | 50 | # Singleton design pattern for implementing it |
... | ... | @@ -193,10 +203,14 @@ class Project(object): |
193 | 203 | return measures |
194 | 204 | |
195 | 205 | def SavePlistProject(self, dir_, filename): |
196 | - dir_temp = tempfile.mkdtemp() | |
197 | - filename_tmp = os.path.join(dir_temp, 'matrix.dat') | |
206 | + dir_temp = tempfile.mkdtemp().decode(const.FS_ENCODE) | |
207 | + | |
208 | + filename_tmp = os.path.join(dir_temp, u'matrix.dat') | |
198 | 209 | filelist = {} |
199 | 210 | |
211 | + print type(dir_temp), type(filename) | |
212 | + print filename.encode('utf8'), dir_.encode('utf8'), type(filename), type(dir_) | |
213 | + | |
200 | 214 | project = { |
201 | 215 | # Format info |
202 | 216 | "format_version": 1, |
... | ... | @@ -214,10 +228,11 @@ class Project(object): |
214 | 228 | } |
215 | 229 | |
216 | 230 | # Saving the matrix containing the slices |
217 | - matrix = {'filename': u'matrix.dat', | |
218 | - 'shape': self.matrix_shape, | |
219 | - 'dtype': self.matrix_dtype, | |
220 | - } | |
231 | + matrix = { | |
232 | + 'filename': u'matrix.dat', | |
233 | + 'shape': self.matrix_shape, | |
234 | + 'dtype': self.matrix_dtype, | |
235 | + } | |
221 | 236 | project['matrix'] = matrix |
222 | 237 | filelist[self.matrix_filename] = 'matrix.dat' |
223 | 238 | #shutil.copyfile(self.matrix_filename, filename_tmp) |
... | ... | @@ -270,9 +285,9 @@ class Project(object): |
270 | 285 | import invesalius.data.surface as srf |
271 | 286 | |
272 | 287 | if not const.VTK_WARNING: |
273 | - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') | |
288 | + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') | |
274 | 289 | fow = vtk.vtkFileOutputWindow() |
275 | - fow.SetFileName(log_path) | |
290 | + fow.SetFileName(log_path.encode(const.FS_ENCODE)) | |
276 | 291 | ow = vtk.vtkOutputWindow() |
277 | 292 | ow.SetInstance(fow) |
278 | 293 | |
... | ... | @@ -328,29 +343,37 @@ class Project(object): |
328 | 343 | def Compress(folder, filename, filelist): |
329 | 344 | tmpdir, tmpdir_ = os.path.split(folder) |
330 | 345 | current_dir = os.path.abspath(".") |
346 | + temp_inv3 = tempfile.mktemp() | |
347 | + if _has_win32api: | |
348 | + touch(temp_inv3) | |
349 | + temp_inv3 = win32api.GetShortPathName(temp_inv3) | |
350 | + | |
351 | + temp_inv3 = temp_inv3.decode(const.FS_ENCODE) | |
331 | 352 | #os.chdir(tmpdir) |
332 | 353 | #file_list = glob.glob(os.path.join(tmpdir_,"*")) |
333 | - tar_filename = tmpdir_ + ".inv3" | |
334 | - tar = tarfile.open(filename.encode(wx.GetDefaultPyEncoding()), "w:gz") | |
354 | + print "Tar file", temp_inv3, type(temp_inv3), filename.encode('utf8'), type(filename) | |
355 | + tar = tarfile.open(temp_inv3, "w:gz") | |
335 | 356 | for name in filelist: |
336 | 357 | tar.add(name, arcname=os.path.join(tmpdir_, filelist[name])) |
337 | 358 | tar.close() |
338 | - #shutil.move(tmpdir_+ ".inv3", filename) | |
359 | + shutil.move(temp_inv3, filename) | |
339 | 360 | #os.chdir(current_dir) |
340 | 361 | |
362 | + | |
341 | 363 | def Extract(filename, folder): |
364 | + if _has_win32api: | |
365 | + folder = win32api.GetShortPathName(folder) | |
366 | + folder = folder.decode(const.FS_ENCODE) | |
367 | + | |
342 | 368 | tar = tarfile.open(filename, "r:gz") |
343 | - idir = os.path.split(tar.getnames()[0])[0] | |
344 | - os.mkdir(os.path.join(folder, idir.decode('utf8'))) | |
369 | + idir = os.path.split(tar.getnames()[0])[0].decode('utf8') | |
370 | + os.mkdir(os.path.join(folder, idir)) | |
345 | 371 | filelist = [] |
346 | 372 | for t in tar.getmembers(): |
347 | 373 | fsrc = tar.extractfile(t) |
348 | - | |
349 | 374 | fname = os.path.join(folder, t.name.decode('utf-8')) |
350 | 375 | fdst = file(fname, 'wb') |
351 | - | |
352 | 376 | shutil.copyfileobj(fsrc, fdst) |
353 | - | |
354 | 377 | filelist.append(fname) |
355 | 378 | fsrc.close() |
356 | 379 | fdst.close() |
... | ... | @@ -359,7 +382,7 @@ def Extract(filename, folder): |
359 | 382 | tar.close() |
360 | 383 | return filelist |
361 | 384 | |
362 | - | |
385 | + | |
363 | 386 | def Extract_(filename, folder): |
364 | 387 | tar = tarfile.open(filename, "r:gz") |
365 | 388 | #tar.list(verbose=True) | ... | ... |
invesalius/reader/bitmap_reader.py
... | ... | @@ -40,6 +40,15 @@ import invesalius.data.converters as converters |
40 | 40 | no_error = True |
41 | 41 | vtk_error = False |
42 | 42 | |
43 | +if sys.platform == 'win32': | |
44 | + try: | |
45 | + import win32api | |
46 | + _has_win32api = True | |
47 | + except ImportError: | |
48 | + _has_win32api = False | |
49 | +else: | |
50 | + _has_win32api = False | |
51 | + | |
43 | 52 | class Singleton: |
44 | 53 | |
45 | 54 | def __init__(self,klass): |
... | ... | @@ -127,10 +136,7 @@ class LoadBitmap: |
127 | 136 | |
128 | 137 | def __init__(self, bmp_file, filepath): |
129 | 138 | self.bmp_file = bmp_file |
130 | - if sys.platform == 'win32': | |
131 | - self.filepath = filepath.encode(utils.get_system_encoding()) | |
132 | - else: | |
133 | - self.filepath = filepath | |
139 | + self.filepath = filepath | |
134 | 140 | |
135 | 141 | self.run() |
136 | 142 | |
... | ... | @@ -294,9 +300,9 @@ def ScipyRead(filepath): |
294 | 300 | |
295 | 301 | def VtkRead(filepath, t): |
296 | 302 | if not const.VTK_WARNING: |
297 | - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') | |
303 | + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') | |
298 | 304 | fow = vtk.vtkFileOutputWindow() |
299 | - fow.SetFileName(log_path) | |
305 | + fow.SetFileName(log_path.encode(const.FS_ENCODE)) | |
300 | 306 | ow = vtk.vtkOutputWindow() |
301 | 307 | ow.SetInstance(fow) |
302 | 308 | |
... | ... | @@ -317,8 +323,10 @@ def VtkRead(filepath, t): |
317 | 323 | else: |
318 | 324 | return False |
319 | 325 | |
326 | + print ">>>> bmp reader", type(filepath) | |
327 | + | |
320 | 328 | reader.AddObserver("ErrorEvent", VtkErrorToPy) |
321 | - reader.SetFileName(filepath) | |
329 | + reader.SetFileName(filepath.encode(const.FS_ENCODE)) | |
322 | 330 | reader.Update() |
323 | 331 | |
324 | 332 | if no_error: |
... | ... | @@ -345,6 +353,9 @@ def VtkRead(filepath, t): |
345 | 353 | def ReadBitmap(filepath): |
346 | 354 | t = VerifyDataType(filepath) |
347 | 355 | |
356 | + if _has_win32api: | |
357 | + filepath = win32api.GetShortPathName(filepath) | |
358 | + | |
348 | 359 | if t == False: |
349 | 360 | measures_info = GetPixelSpacingFromInfoFile(filepath) |
350 | 361 | |
... | ... | @@ -368,7 +379,6 @@ def ReadBitmap(filepath): |
368 | 379 | |
369 | 380 | |
370 | 381 | def GetPixelSpacingFromInfoFile(filepath): |
371 | - | |
372 | 382 | fi = open(filepath, 'r') |
373 | 383 | lines = fi.readlines() |
374 | 384 | measure_scale = 'mm' | ... | ... |
invesalius/reader/dicom_grouper.py
... | ... | @@ -51,9 +51,23 @@ |
51 | 51 | # <dicom.image.number> and <dicom.acquisition.series_number> |
52 | 52 | # were swapped |
53 | 53 | |
54 | +import sys | |
55 | + | |
54 | 56 | import gdcm |
55 | 57 | |
58 | +if sys.platform == 'win32': | |
59 | + try: | |
60 | + import win32api | |
61 | + _has_win32api = True | |
62 | + except ImportError: | |
63 | + _has_win32api = False | |
64 | +else: | |
65 | + _has_win32api = False | |
66 | + | |
56 | 67 | import invesalius.utils as utils |
68 | +import invesalius.constants as const | |
69 | + | |
70 | + | |
57 | 71 | ORIENT_MAP = {"SAGITTAL":0, "CORONAL":1, "AXIAL":2, "OBLIQUE":2} |
58 | 72 | |
59 | 73 | |
... | ... | @@ -109,8 +123,13 @@ class DicomGroup: |
109 | 123 | # This list will be used to create the vtkImageData |
110 | 124 | # (interpolated) |
111 | 125 | |
112 | - filelist = [dicom.image.file for dicom in | |
113 | - self.slices_dict.values()] | |
126 | + if _has_win32api: | |
127 | + filelist = [win32api.GetShortPathName(dicom.image.file).encode(const.FS_ENCODE) | |
128 | + for dicom in | |
129 | + self.slices_dict.values()] | |
130 | + else: | |
131 | + filelist = [dicom.image.file for dicom in | |
132 | + self.slices_dict.values()] | |
114 | 133 | |
115 | 134 | # Sort slices using GDCM |
116 | 135 | if (self.dicom.image.orientation_label <> "CORONAL"): | ... | ... |
invesalius/reader/dicom_reader.py
... | ... | @@ -38,6 +38,15 @@ import invesalius.utils as utils |
38 | 38 | |
39 | 39 | import plistlib |
40 | 40 | |
41 | +if sys.platform == 'win32': | |
42 | + try: | |
43 | + import win32api | |
44 | + _has_win32api = True | |
45 | + except ImportError: | |
46 | + _has_win32api = False | |
47 | +else: | |
48 | + _has_win32api = False | |
49 | + | |
41 | 50 | def ReadDicomGroup(dir_): |
42 | 51 | |
43 | 52 | patient_group = GetDicomGroups(dir_) |
... | ... | @@ -88,18 +97,18 @@ class LoadDicom: |
88 | 97 | |
89 | 98 | def __init__(self, grouper, filepath): |
90 | 99 | self.grouper = grouper |
91 | - if sys.platform == 'win32': | |
92 | - self.filepath = filepath.encode(utils.get_system_encoding()) | |
93 | - else: | |
94 | - self.filepath = filepath | |
100 | + self.filepath = filepath | |
95 | 101 | |
96 | 102 | self.run() |
97 | 103 | |
98 | 104 | def run(self): |
99 | - | |
100 | 105 | grouper = self.grouper |
101 | 106 | reader = gdcm.ImageReader() |
102 | - reader.SetFileName(self.filepath) | |
107 | + if _has_win32api: | |
108 | + reader.SetFileName(win32api.GetShortPathName(self.filepath).encode(const.FS_ENCODE)) | |
109 | + else: | |
110 | + reader.SetFileName(self.filepath) | |
111 | + | |
103 | 112 | if (reader.Read()): |
104 | 113 | file = reader.GetFile() |
105 | 114 | |
... | ... | @@ -180,7 +189,12 @@ class LoadDicom: |
180 | 189 | |
181 | 190 | # -------------- To Create DICOM Thumbnail ----------- |
182 | 191 | rvtk = vtkgdcm.vtkGDCMImageReader() |
183 | - rvtk.SetFileName(self.filepath) | |
192 | + | |
193 | + if _has_win32api: | |
194 | + print 'dicom', win32api.GetShortPathName(self.filepath) | |
195 | + rvtk.SetFileName(win32api.GetShortPathName(self.filepath).encode(const.FS_ENCODE)) | |
196 | + else: | |
197 | + rvtk.SetFileName(self.filepath) | |
184 | 198 | rvtk.Update() |
185 | 199 | |
186 | 200 | try: |
... | ... | @@ -341,7 +355,7 @@ class ProgressDicomReader: |
341 | 355 | def GetDicomGroups(self, path, recursive): |
342 | 356 | |
343 | 357 | if not const.VTK_WARNING: |
344 | - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') | |
358 | + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt').encode(const.FS_ENCODE) | |
345 | 359 | fow = vtk.vtkFileOutputWindow() |
346 | 360 | fow.SetFileName(log_path) |
347 | 361 | ow = vtk.vtkOutputWindow() | ... | ... |
invesalius/reader/others_reader.py
... | ... | @@ -38,9 +38,9 @@ def ReadOthers(dir_): |
38 | 38 | """ |
39 | 39 | |
40 | 40 | if not const.VTK_WARNING: |
41 | - log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') | |
41 | + log_path = os.path.join(const.USER_LOG_DIR, 'vtkoutput.txt') | |
42 | 42 | fow = vtk.vtkFileOutputWindow() |
43 | - fow.SetFileName(log_path) | |
43 | + fow.SetFileName(log_path.encode(const.FS_ENCODE)) | |
44 | 44 | ow = vtk.vtkOutputWindow() |
45 | 45 | ow.SetInstance(fow) |
46 | 46 | ... | ... |
invesalius/session.py
... | ... | @@ -36,7 +36,24 @@ import wx |
36 | 36 | from invesalius.utils import Singleton, debug |
37 | 37 | from random import randint |
38 | 38 | |
39 | -ENCODE=wx.GetDefaultPyEncoding() | |
39 | +FS_ENCODE = sys.getfilesystemencoding() | |
40 | + | |
41 | +if sys.platform == 'win32': | |
42 | + from invesalius.expanduser import expand_user | |
43 | + try: | |
44 | + USER_DIR = expand_user() | |
45 | + except: | |
46 | + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) | |
47 | +else: | |
48 | + USER_DIR = os.path.expanduser('~').decode(FS_ENCODE) | |
49 | + | |
50 | +USER_INV_DIR = os.path.join(USER_DIR, u'.invesalius') | |
51 | +USER_PRESET_DIR = os.path.join(USER_INV_DIR, u'presets') | |
52 | +USER_LOG_DIR = os.path.join(USER_INV_DIR, u'logs') | |
53 | +USER_INV_CFG_PATH = os.path.join(USER_INV_DIR, 'config.cfg') | |
54 | + | |
55 | +SESSION_ENCODING = 'utf8' | |
56 | + | |
40 | 57 | |
41 | 58 | class Session(object): |
42 | 59 | # Only one session will be initialized per time. Therefore, we use |
... | ... | @@ -62,7 +79,7 @@ class Session(object): |
62 | 79 | # const.MODE_ODONTOLOGY |
63 | 80 | |
64 | 81 | # InVesalius default projects' directory |
65 | - homedir = self.homedir = os.path.expanduser('~').decode(ENCODE) | |
82 | + homedir = self.homedir = USER_DIR | |
66 | 83 | tempdir = os.path.join(homedir, u".invesalius", u"temp") |
67 | 84 | if not os.path.isdir(tempdir): |
68 | 85 | os.makedirs(tempdir) |
... | ... | @@ -72,10 +89,9 @@ class Session(object): |
72 | 89 | self.language = "" # "pt_BR", "es" |
73 | 90 | |
74 | 91 | self.random_id = randint(0,pow(10,16)) |
75 | - #print self.random_id | |
76 | 92 | |
77 | 93 | # Recent projects list |
78 | - self.recent_projects = [(const.SAMPLE_DIR, "Cranium.inv3")] | |
94 | + self.recent_projects = [(const.SAMPLE_DIR, u"Cranium.inv3")] | |
79 | 95 | self.last_dicom_folder = '' |
80 | 96 | self.surface_interpolation = 1 |
81 | 97 | self.slice_interpolation = 0 |
... | ... | @@ -88,18 +104,18 @@ class Session(object): |
88 | 104 | |
89 | 105 | def SaveConfigFileBackup(self): |
90 | 106 | path = os.path.join(self.homedir , |
91 | - '.invesalius', 'config.cfg') | |
107 | + u'.invesalius', u'config.cfg') | |
92 | 108 | path_dst = os.path.join(self.homedir , |
93 | - '.invesalius', 'config.backup') | |
109 | + u'.invesalius', u'config.backup') | |
94 | 110 | shutil.copy(path, path_dst) |
95 | 111 | |
96 | 112 | def RecoveryConfigFile(self): |
97 | 113 | homedir = self.homedir = os.path.expanduser('~') |
98 | 114 | try: |
99 | 115 | path = os.path.join(self.homedir , |
100 | - '.invesalius', 'config.backup') | |
116 | + u'.invesalius', u'config.backup') | |
101 | 117 | path_dst = os.path.join(self.homedir , |
102 | - '.invesalius', 'config.cfg') | |
118 | + u'.invesalius', u'config.cfg') | |
103 | 119 | shutil.copy(path, path_dst) |
104 | 120 | return True |
105 | 121 | except(IOError): |
... | ... | @@ -163,10 +179,6 @@ class Session(object): |
163 | 179 | def WriteSessionFile(self): |
164 | 180 | config = ConfigParser.RawConfigParser() |
165 | 181 | |
166 | - print ">>>", type(self.homedir), self.homedir, repr(self.homedir) | |
167 | - print ">>>", type(self.tempdir), self.tempdir, repr(self.tempdir) | |
168 | - print ">>>", type(self.language), self.language, repr(self.language) | |
169 | - | |
170 | 182 | config.add_section('session') |
171 | 183 | config.set('session', 'mode', self.mode) |
172 | 184 | config.set('session', 'status', self.project_status) |
... | ... | @@ -185,18 +197,10 @@ class Session(object): |
185 | 197 | config.set('paths','tempdir',self.tempdir) |
186 | 198 | config.set('paths','last_dicom_folder',self.last_dicom_folder) |
187 | 199 | |
188 | - print config.items('session') | |
189 | - print config.items('project') | |
190 | - print config.items('paths') | |
191 | - | |
192 | 200 | path = os.path.join(self.homedir , |
193 | 201 | '.invesalius', 'config.cfg') |
194 | 202 | |
195 | - if sys.platform == 'win32': | |
196 | - configfile = codecs.open(path, 'wb', 'utf8') | |
197 | - else: | |
198 | - configfile = open(path, 'wb') | |
199 | - | |
203 | + configfile = codecs.open(path, 'wb', SESSION_ENCODING) | |
200 | 204 | config.write(configfile) |
201 | 205 | configfile.close() |
202 | 206 | |
... | ... | @@ -238,12 +242,15 @@ class Session(object): |
238 | 242 | |
239 | 243 | def ReadLanguage(self): |
240 | 244 | config = ConfigParser.ConfigParser() |
241 | - home_path = os.path.expanduser('~') | |
242 | - path = os.path.join(home_path ,'.invesalius', 'config.cfg') | |
245 | + path = os.path.join(USER_INV_DIR, 'config.cfg') | |
243 | 246 | try: |
244 | - config.read(path) | |
247 | + f = codecs.open(path, 'rb', SESSION_ENCODING) | |
248 | + config.readfp(f) | |
249 | + f.close() | |
245 | 250 | self.language = config.get('session','language') |
246 | 251 | return self.language |
252 | + except IOError: | |
253 | + return False | |
247 | 254 | except (ConfigParser.NoSectionError, |
248 | 255 | ConfigParser.NoOptionError, |
249 | 256 | ConfigParser.MissingSectionHeaderError): |
... | ... | @@ -251,12 +258,15 @@ class Session(object): |
251 | 258 | |
252 | 259 | def ReadRandomId(self): |
253 | 260 | config = ConfigParser.ConfigParser() |
254 | - home_path = os.path.expanduser('~') | |
255 | - path = os.path.join(home_path ,'.invesalius', 'config.cfg') | |
261 | + path = os.path.join(USER_INV_DIR, 'config.cfg') | |
256 | 262 | try: |
257 | - config.read(path) | |
263 | + f = codecs.open(path, 'rb', SESSION_ENCODING) | |
264 | + config.readfp(f) | |
265 | + f.close() | |
258 | 266 | self.random_id = config.get('session','random_id') |
259 | 267 | return self.random_id |
268 | + except IOError: | |
269 | + return False | |
260 | 270 | except (ConfigParser.NoSectionError, |
261 | 271 | ConfigParser.NoOptionError, |
262 | 272 | ConfigParser.MissingSectionHeaderError): |
... | ... | @@ -264,10 +274,11 @@ class Session(object): |
264 | 274 | |
265 | 275 | def ReadSession(self): |
266 | 276 | config = ConfigParser.ConfigParser() |
267 | - home_path = os.path.expanduser('~').decode(ENCODE) | |
268 | - path = os.path.join(home_path ,'.invesalius', 'config.cfg') | |
277 | + path = USER_INV_CFG_PATH | |
269 | 278 | try: |
270 | - config.readfp(codecs.open(path, 'rb', 'utf8')) | |
279 | + f = codecs.open(path, 'rb', SESSION_ENCODING) | |
280 | + config.readfp(f) | |
281 | + f.close() | |
271 | 282 | self.mode = config.get('session', 'mode') |
272 | 283 | # Do not reading project status from the config file, since there |
273 | 284 | # isn't a recover sessession tool in InVesalius |
... | ... | @@ -295,7 +306,6 @@ class Session(object): |
295 | 306 | except(ConfigParser.NoSectionError, ConfigParser.MissingSectionHeaderError, |
296 | 307 | ConfigParser.ParsingError): |
297 | 308 | |
298 | - print 'Error 1' | |
299 | 309 | if (self.RecoveryConfigFile()): |
300 | 310 | self.ReadSession() |
301 | 311 | return True |
... | ... | @@ -303,7 +313,6 @@ class Session(object): |
303 | 313 | return False |
304 | 314 | |
305 | 315 | except(ConfigParser.NoOptionError): |
306 | - print 'Error 2' | |
307 | 316 | #Added to fix new version compatibility |
308 | 317 | self.surface_interpolation = 0 |
309 | 318 | self.slice_interpolation = 0 |
... | ... | @@ -312,6 +321,5 @@ class Session(object): |
312 | 321 | try: |
313 | 322 | self.WriteSessionFile() |
314 | 323 | except AttributeError: |
315 | - print 'Error 3' | |
316 | 324 | return False |
317 | 325 | return True | ... | ... |