Commit 2fade61abc95553bd4a1b9f00981ee64e1c9c734
Committed by
Thiago Franco de Moraes
1 parent
78173f1e
Exists in
master
Add command line options for saving and exporting (#85)
* Create option in app.py to import and then save the project * Add command to import and export in STL to all available presets * Create option to open InVesalius without gui * Add option --export * No-gui doing less work when creating surface * Print on debug * Fix missing session assignment in non-gui mode * Add option to import every group of DICOM images * Checking surface index exists in actors_dict before removing it
Showing
10 changed files
with
346 additions
and
141 deletions
Show diff stats
@@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
1 | +FROM ubuntu:16.04 | ||
2 | + | ||
3 | +RUN apt-get update | ||
4 | +RUN apt-get install -y \ | ||
5 | + cython \ | ||
6 | + locales \ | ||
7 | + python-concurrent.futures \ | ||
8 | + python-gdcm \ | ||
9 | + python-matplotlib \ | ||
10 | + python-nibabel \ | ||
11 | + python-numpy \ | ||
12 | + python-pil \ | ||
13 | + python-psutil \ | ||
14 | + python-scipy \ | ||
15 | + python-serial \ | ||
16 | + python-skimage \ | ||
17 | + python-vtk6 \ | ||
18 | + python-vtkgdcm \ | ||
19 | + python-wxgtk3.0 | ||
20 | + | ||
21 | +RUN locale-gen en_US.UTF-8 | ||
22 | +ENV LANG en_US.UTF-8 | ||
23 | +ENV LANGUAGE en_US:en | ||
24 | +ENV LC_ALL en_US.UTF-8 | ||
25 | + | ||
26 | +WORKDIR /usr/local/app | ||
27 | + | ||
28 | +COPY . . | ||
29 | + | ||
30 | +RUN python setup.py build_ext --inplace |
app.py
@@ -24,6 +24,9 @@ import optparse as op | @@ -24,6 +24,9 @@ import optparse as op | ||
24 | import os | 24 | import os |
25 | import sys | 25 | import sys |
26 | import shutil | 26 | import shutil |
27 | +import traceback | ||
28 | + | ||
29 | +import re | ||
27 | 30 | ||
28 | if sys.platform == 'win32': | 31 | if sys.platform == 'win32': |
29 | import _winreg | 32 | import _winreg |
@@ -218,7 +221,8 @@ class SplashScreen(wx.SplashScreen): | @@ -218,7 +221,8 @@ class SplashScreen(wx.SplashScreen): | ||
218 | self.control = Controller(self.main) | 221 | self.control = Controller(self.main) |
219 | 222 | ||
220 | self.fc = wx.FutureCall(1, self.ShowMain) | 223 | self.fc = wx.FutureCall(1, self.ShowMain) |
221 | - wx.FutureCall(1, parse_comand_line) | 224 | + options, args = parse_comand_line() |
225 | + wx.FutureCall(1, use_cmd_optargs, options, args) | ||
222 | 226 | ||
223 | # Check for updates | 227 | # Check for updates |
224 | from threading import Thread | 228 | from threading import Thread |
@@ -244,6 +248,24 @@ class SplashScreen(wx.SplashScreen): | @@ -244,6 +248,24 @@ class SplashScreen(wx.SplashScreen): | ||
244 | if self.fc.IsRunning(): | 248 | if self.fc.IsRunning(): |
245 | self.Raise() | 249 | self.Raise() |
246 | 250 | ||
251 | + | ||
252 | +def non_gui_startup(options, args): | ||
253 | + lang = 'en' | ||
254 | + _ = i18n.InstallLanguage(lang) | ||
255 | + | ||
256 | + from invesalius.control import Controller | ||
257 | + from invesalius.project import Project | ||
258 | + | ||
259 | + session = ses.Session() | ||
260 | + if not session.ReadSession(): | ||
261 | + session.CreateItens() | ||
262 | + session.SetLanguage(lang) | ||
263 | + session.WriteSessionFile() | ||
264 | + | ||
265 | + control = Controller(None) | ||
266 | + | ||
267 | + use_cmd_optargs(options, args) | ||
268 | + | ||
247 | # ------------------------------------------------------------------ | 269 | # ------------------------------------------------------------------ |
248 | 270 | ||
249 | 271 | ||
@@ -262,39 +284,138 @@ def parse_comand_line(): | @@ -262,39 +284,138 @@ def parse_comand_line(): | ||
262 | action="store_true", | 284 | action="store_true", |
263 | dest="debug") | 285 | dest="debug") |
264 | 286 | ||
287 | + parser.add_option('--no-gui', | ||
288 | + action='store_true', | ||
289 | + dest='no_gui') | ||
290 | + | ||
265 | # -i or --import: import DICOM directory | 291 | # -i or --import: import DICOM directory |
266 | # chooses largest series | 292 | # chooses largest series |
267 | parser.add_option("-i", "--import", | 293 | parser.add_option("-i", "--import", |
268 | action="store", | 294 | action="store", |
269 | dest="dicom_dir") | 295 | dest="dicom_dir") |
296 | + | ||
297 | + parser.add_option("--import-all", | ||
298 | + action="store") | ||
299 | + | ||
300 | + parser.add_option("-s", "--save", | ||
301 | + help="Save the project after an import.") | ||
302 | + | ||
303 | + parser.add_option("-t", "--threshold", | ||
304 | + help="Define the threshold for the export (e.g. 100-780).") | ||
305 | + | ||
306 | + parser.add_option("-e", "--export", | ||
307 | + help="Export to STL.") | ||
308 | + | ||
309 | + parser.add_option("-a", "--export-to-all", | ||
310 | + help="Export to STL for all mask presets.") | ||
311 | + | ||
270 | options, args = parser.parse_args() | 312 | options, args = parser.parse_args() |
313 | + return options, args | ||
314 | + | ||
271 | 315 | ||
316 | +def use_cmd_optargs(options, args): | ||
272 | # If debug argument... | 317 | # If debug argument... |
273 | if options.debug: | 318 | if options.debug: |
274 | Publisher.subscribe(print_events, Publisher.ALL_TOPICS) | 319 | Publisher.subscribe(print_events, Publisher.ALL_TOPICS) |
320 | + session = ses.Session() | ||
275 | session.debug = 1 | 321 | session.debug = 1 |
276 | 322 | ||
277 | # If import DICOM argument... | 323 | # If import DICOM argument... |
278 | if options.dicom_dir: | 324 | if options.dicom_dir: |
279 | import_dir = options.dicom_dir | 325 | import_dir = options.dicom_dir |
280 | - Publisher.sendMessage('Import directory', import_dir) | 326 | + Publisher.sendMessage('Import directory', {'directory': import_dir, 'gui': not options.no_gui}) |
327 | + | ||
328 | + if options.save: | ||
329 | + Publisher.sendMessage('Save project', os.path.abspath(options.save)) | ||
330 | + exit(0) | ||
331 | + | ||
332 | + check_for_export(options) | ||
333 | + | ||
334 | + return True | ||
335 | + elif options.import_all: | ||
336 | + import invesalius.reader.dicom_reader as dcm | ||
337 | + for patient in dcm.GetDicomGroups(options.import_all): | ||
338 | + for group in patient.GetGroups(): | ||
339 | + Publisher.sendMessage('Import group', {'group': group, 'gui': not options.no_gui}) | ||
340 | + check_for_export(options, suffix=group.title, remove_surfaces=False) | ||
341 | + Publisher.sendMessage('Remove masks', [0]) | ||
281 | return True | 342 | return True |
282 | 343 | ||
283 | # Check if there is a file path somewhere in what the user wrote | 344 | # Check if there is a file path somewhere in what the user wrote |
284 | # In case there is, try opening as it was a inv3 | 345 | # In case there is, try opening as it was a inv3 |
285 | else: | 346 | else: |
286 | - i = len(args) | ||
287 | - while i: | ||
288 | - i -= 1 | ||
289 | - file = args[i].decode(sys.stdin.encoding) | ||
290 | - if os.path.isfile(file): | ||
291 | - path = os.path.abspath(file) | ||
292 | - Publisher.sendMessage('Open project', path) | ||
293 | - i = 0 | 347 | + for arg in reversed(args): |
348 | + if os.path.isfile(arg): | ||
349 | + path_ = os.path.abspath(arg.decode(sys.stdin.encoding)) | ||
350 | + Publisher.sendMessage('Open project', path_) | ||
351 | + | ||
352 | + check_for_export(options) | ||
294 | return True | 353 | return True |
295 | return False | 354 | return False |
296 | 355 | ||
297 | 356 | ||
357 | +def sanitize(text): | ||
358 | + text = str(text).strip().replace(' ', '_') | ||
359 | + return re.sub(r'(?u)[^-\w.]', '', text) | ||
360 | + | ||
361 | + | ||
362 | +def check_for_export(options, suffix='', remove_surfaces=False): | ||
363 | + suffix = sanitize(suffix) | ||
364 | + | ||
365 | + if options.export: | ||
366 | + if not options.threshold: | ||
367 | + print("Need option --threshold when using --export.") | ||
368 | + exit(1) | ||
369 | + threshold_range = tuple([int(n) for n in options.threshold.split(',')]) | ||
370 | + | ||
371 | + if suffix: | ||
372 | + if options.export.endswith('.stl'): | ||
373 | + path_ = '{}-{}.stl'.format(options.export[:-4], suffix) | ||
374 | + else: | ||
375 | + path_ = '{}-{}.stl'.format(options.export, suffix) | ||
376 | + else: | ||
377 | + path_ = options.export | ||
378 | + | ||
379 | + export(path_, threshold_range, remove_surface=remove_surfaces) | ||
380 | + elif options.export_to_all: | ||
381 | + # noinspection PyBroadException | ||
382 | + try: | ||
383 | + from invesalius.project import Project | ||
384 | + | ||
385 | + for threshold_name, threshold_range in Project().presets.thresh_ct.iteritems(): | ||
386 | + if isinstance(threshold_range[0], int): | ||
387 | + path_ = u'{}-{}-{}.stl'.format(options.export_to_all, suffix, threshold_name) | ||
388 | + export(path_, threshold_range, remove_surface=True) | ||
389 | + except: | ||
390 | + traceback.print_exc() | ||
391 | + finally: | ||
392 | + exit(0) | ||
393 | + | ||
394 | + | ||
395 | +def export(path_, threshold_range, remove_surface=False): | ||
396 | + import invesalius.constants as const | ||
397 | + | ||
398 | + Publisher.sendMessage('Set threshold values', threshold_range) | ||
399 | + | ||
400 | + surface_options = { | ||
401 | + 'method': { | ||
402 | + 'algorithm': 'Default', | ||
403 | + 'options': {}, | ||
404 | + }, 'options': { | ||
405 | + 'index': 0, | ||
406 | + 'name': '', | ||
407 | + 'quality': _('Optimal *'), | ||
408 | + 'fill': False, | ||
409 | + 'keep_largest': False, | ||
410 | + 'overwrite': False, | ||
411 | + } | ||
412 | + } | ||
413 | + Publisher.sendMessage('Create surface from index', surface_options) | ||
414 | + Publisher.sendMessage('Export surface to file', (path_, const.FILETYPE_STL)) | ||
415 | + if remove_surface: | ||
416 | + Publisher.sendMessage('Remove surfaces', [0]) | ||
417 | + | ||
418 | + | ||
298 | def print_events(data): | 419 | def print_events(data): |
299 | """ | 420 | """ |
300 | Print pubsub messages | 421 | Print pubsub messages |
@@ -305,8 +426,13 @@ def main(): | @@ -305,8 +426,13 @@ def main(): | ||
305 | """ | 426 | """ |
306 | Initialize InVesalius GUI | 427 | Initialize InVesalius GUI |
307 | """ | 428 | """ |
308 | - application = InVesalius(0) | ||
309 | - application.MainLoop() | 429 | + options, args = parse_comand_line() |
430 | + | ||
431 | + if options.no_gui: | ||
432 | + non_gui_startup(options, args) | ||
433 | + else: | ||
434 | + application = InVesalius(0) | ||
435 | + application.MainLoop() | ||
310 | 436 | ||
311 | if __name__ == '__main__': | 437 | if __name__ == '__main__': |
312 | #Is needed because of pyinstaller | 438 | #Is needed because of pyinstaller |
invesalius/constants.py
@@ -694,19 +694,19 @@ BTNS_IMG_MKS = {IR1: {0: 'LEI'}, | @@ -694,19 +694,19 @@ BTNS_IMG_MKS = {IR1: {0: 'LEI'}, | ||
694 | IR2: {1: 'REI'}, | 694 | IR2: {1: 'REI'}, |
695 | IR3: {2: 'NAI'}} | 695 | IR3: {2: 'NAI'}} |
696 | 696 | ||
697 | -TIPS_IMG = [wx.ToolTip(_("Select left ear in image")), | ||
698 | - wx.ToolTip(_("Select right ear in image")), | ||
699 | - wx.ToolTip(_("Select nasion in image"))] | 697 | +TIPS_IMG = [_("Select left ear in image"), |
698 | + _("Select right ear in image"), | ||
699 | + _("Select nasion in image")] | ||
700 | 700 | ||
701 | BTNS_TRK = {TR1: {3: _('LET')}, | 701 | BTNS_TRK = {TR1: {3: _('LET')}, |
702 | TR2: {4: _('RET')}, | 702 | TR2: {4: _('RET')}, |
703 | TR3: {5: _('NAT')}, | 703 | TR3: {5: _('NAT')}, |
704 | SET: {6: _('SET')}} | 704 | SET: {6: _('SET')}} |
705 | 705 | ||
706 | -TIPS_TRK = [wx.ToolTip(_("Select left ear with spatial tracker")), | ||
707 | - wx.ToolTip(_("Select right ear with spatial tracker")), | ||
708 | - wx.ToolTip(_("Select nasion with spatial tracker")), | ||
709 | - wx.ToolTip(_("Show set coordinates in image"))] | 706 | +TIPS_TRK = [_("Select left ear with spatial tracker"), |
707 | + _("Select right ear with spatial tracker"), | ||
708 | + _("Select nasion with spatial tracker"), | ||
709 | + _("Show set coordinates in image")] | ||
710 | 710 | ||
711 | CAL_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'mtc_files', 'CalibrationFiles')) | 711 | CAL_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'mtc_files', 'CalibrationFiles')) |
712 | MAR_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'mtc_files', 'Markers')) | 712 | MAR_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'mtc_files', 'Markers')) |
invesalius/control.py
@@ -68,6 +68,7 @@ class Controller(): | @@ -68,6 +68,7 @@ class Controller(): | ||
68 | 68 | ||
69 | def __bind_events(self): | 69 | def __bind_events(self): |
70 | Publisher.subscribe(self.OnImportMedicalImages, 'Import directory') | 70 | Publisher.subscribe(self.OnImportMedicalImages, 'Import directory') |
71 | + Publisher.subscribe(self.OnImportGroup, 'Import group') | ||
71 | Publisher.subscribe(self.OnShowDialogImportDirectory, | 72 | Publisher.subscribe(self.OnShowDialogImportDirectory, |
72 | 'Show import directory dialog') | 73 | 'Show import directory dialog') |
73 | Publisher.subscribe(self.OnShowDialogImportOtherFiles, | 74 | Publisher.subscribe(self.OnShowDialogImportOtherFiles, |
@@ -105,6 +106,8 @@ class Controller(): | @@ -105,6 +106,8 @@ class Controller(): | ||
105 | 106 | ||
106 | Publisher.subscribe(self.SetBitmapSpacing, 'Set bitmap spacing') | 107 | Publisher.subscribe(self.SetBitmapSpacing, 'Set bitmap spacing') |
107 | 108 | ||
109 | + Publisher.subscribe(self.OnSaveProject, 'Save project') | ||
110 | + | ||
108 | def SetBitmapSpacing(self, pubsub_evt): | 111 | def SetBitmapSpacing(self, pubsub_evt): |
109 | proj = prj.Project() | 112 | proj = prj.Project() |
110 | proj.spacing = pubsub_evt.data | 113 | proj.spacing = pubsub_evt.data |
@@ -329,6 +332,10 @@ class Controller(): | @@ -329,6 +332,10 @@ class Controller(): | ||
329 | session.OpenProject(filepath) | 332 | session.OpenProject(filepath) |
330 | Publisher.sendMessage("Enable state project", True) | 333 | Publisher.sendMessage("Enable state project", True) |
331 | 334 | ||
335 | + def OnSaveProject(self, pubsub_evt): | ||
336 | + path = pubsub_evt.data | ||
337 | + self.SaveProject(path) | ||
338 | + | ||
332 | def SaveProject(self, path=None): | 339 | def SaveProject(self, path=None): |
333 | Publisher.sendMessage('Begin busy cursor') | 340 | Publisher.sendMessage('Begin busy cursor') |
334 | session = ses.Session() | 341 | session = ses.Session() |
@@ -442,10 +449,11 @@ class Controller(): | @@ -442,10 +449,11 @@ class Controller(): | ||
442 | #----------- to import by command line --------------------------------------------------- | 449 | #----------- to import by command line --------------------------------------------------- |
443 | 450 | ||
444 | def OnImportMedicalImages(self, pubsub_evt): | 451 | def OnImportMedicalImages(self, pubsub_evt): |
445 | - directory = pubsub_evt.data | ||
446 | - self.ImportMedicalImages(directory) | 452 | + directory = pubsub_evt.data['directory'] |
453 | + gui = pubsub_evt.data['gui'] | ||
454 | + self.ImportMedicalImages(directory, gui) | ||
447 | 455 | ||
448 | - def ImportMedicalImages(self, directory): | 456 | + def ImportMedicalImages(self, directory, gui=True): |
449 | patients_groups = dcm.GetDicomGroups(directory) | 457 | patients_groups = dcm.GetDicomGroups(directory) |
450 | name = directory.rpartition('\\')[-1].split('.') | 458 | name = directory.rpartition('\\')[-1].split('.') |
451 | print "patients: ", patients_groups | 459 | print "patients: ", patients_groups |
@@ -453,7 +461,7 @@ class Controller(): | @@ -453,7 +461,7 @@ class Controller(): | ||
453 | if len(patients_groups): | 461 | if len(patients_groups): |
454 | # OPTION 1: DICOM | 462 | # OPTION 1: DICOM |
455 | group = dcm.SelectLargerDicomGroup(patients_groups) | 463 | group = dcm.SelectLargerDicomGroup(patients_groups) |
456 | - matrix, matrix_filename, dicom = self.OpenDicomGroup(group, 0, [0, 0], gui=True) | 464 | + matrix, matrix_filename, dicom = self.OpenDicomGroup(group, 0, [0, 0], gui=gui) |
457 | self.CreateDicomProject(dicom, matrix, matrix_filename) | 465 | self.CreateDicomProject(dicom, matrix, matrix_filename) |
458 | else: | 466 | else: |
459 | # OPTION 2: NIfTI, Analyze or PAR/REC | 467 | # OPTION 2: NIfTI, Analyze or PAR/REC |
@@ -476,6 +484,18 @@ class Controller(): | @@ -476,6 +484,18 @@ class Controller(): | ||
476 | self.LoadProject() | 484 | self.LoadProject() |
477 | Publisher.sendMessage("Enable state project", True) | 485 | Publisher.sendMessage("Enable state project", True) |
478 | 486 | ||
487 | + def OnImportGroup(self, pubsub_evt): | ||
488 | + group = pubsub_evt.data['group'] | ||
489 | + gui = pubsub_evt.data['gui'] | ||
490 | + self.ImportGroup(group, gui) | ||
491 | + | ||
492 | + def ImportGroup(self, group, gui=True): | ||
493 | + matrix, matrix_filename, dicom = self.OpenDicomGroup(group, 0, [0, 0], gui=gui) | ||
494 | + self.CreateDicomProject(dicom, matrix, matrix_filename) | ||
495 | + | ||
496 | + self.LoadProject() | ||
497 | + Publisher.sendMessage("Enable state project", True) | ||
498 | + | ||
479 | #------------------------------------------------------------------------------------- | 499 | #------------------------------------------------------------------------------------- |
480 | 500 | ||
481 | def LoadProject(self): | 501 | def LoadProject(self): |
@@ -785,7 +805,7 @@ class Controller(): | @@ -785,7 +805,7 @@ class Controller(): | ||
785 | n_slices = len(filelist) | 805 | n_slices = len(filelist) |
786 | resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8) | 806 | resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8) |
787 | 807 | ||
788 | - if resolution_percentage < 1.0: | 808 | + if resolution_percentage < 1.0 and gui: |
789 | re_dialog = dialog.ResizeImageDialog() | 809 | re_dialog = dialog.ResizeImageDialog() |
790 | re_dialog.SetValue(int(resolution_percentage*100)) | 810 | re_dialog.SetValue(int(resolution_percentage*100)) |
791 | re_dialog_value = re_dialog.ShowModal() | 811 | re_dialog_value = re_dialog.ShowModal() |
invesalius/data/slice_.py
@@ -359,19 +359,22 @@ class Slice(object): | @@ -359,19 +359,22 @@ class Slice(object): | ||
359 | 359 | ||
360 | # TODO: merge this code with apply_slice_buffer_to_mask | 360 | # TODO: merge this code with apply_slice_buffer_to_mask |
361 | b_mask = self.buffer_slices["AXIAL"].mask | 361 | b_mask = self.buffer_slices["AXIAL"].mask |
362 | - n = self.buffer_slices["AXIAL"].index + 1 | ||
363 | - self.current_mask.matrix[n, 1:, 1:] = b_mask | ||
364 | - self.current_mask.matrix[n, 0, 0] = 1 | 362 | + if b_mask is not None: |
363 | + n = self.buffer_slices["AXIAL"].index + 1 | ||
364 | + self.current_mask.matrix[n, 1:, 1:] = b_mask | ||
365 | + self.current_mask.matrix[n, 0, 0] = 1 | ||
365 | 366 | ||
366 | b_mask = self.buffer_slices["CORONAL"].mask | 367 | b_mask = self.buffer_slices["CORONAL"].mask |
367 | - n = self.buffer_slices["CORONAL"].index + 1 | ||
368 | - self.current_mask.matrix[1:, n, 1:] = b_mask | ||
369 | - self.current_mask.matrix[0, n, 0] = 1 | 368 | + if b_mask is not None: |
369 | + n = self.buffer_slices["CORONAL"].index + 1 | ||
370 | + self.current_mask.matrix[1:, n, 1:] = b_mask | ||
371 | + self.current_mask.matrix[0, n, 0] = 1 | ||
370 | 372 | ||
371 | b_mask = self.buffer_slices["SAGITAL"].mask | 373 | b_mask = self.buffer_slices["SAGITAL"].mask |
372 | - n = self.buffer_slices["SAGITAL"].index + 1 | ||
373 | - self.current_mask.matrix[1:, 1:, n] = b_mask | ||
374 | - self.current_mask.matrix[0, 0, n] = 1 | 374 | + if b_mask is not None: |
375 | + n = self.buffer_slices["SAGITAL"].index + 1 | ||
376 | + self.current_mask.matrix[1:, 1:, n] = b_mask | ||
377 | + self.current_mask.matrix[0, 0, n] = 1 | ||
375 | 378 | ||
376 | if to_reload: | 379 | if to_reload: |
377 | Publisher.sendMessage('Reload actual slice') | 380 | Publisher.sendMessage('Reload actual slice') |
@@ -888,7 +891,8 @@ class Slice(object): | @@ -888,7 +891,8 @@ class Slice(object): | ||
888 | self.current_mask.matrix[n+1, 1:, 1:] = m | 891 | self.current_mask.matrix[n+1, 1:, 1:] = m |
889 | else: | 892 | else: |
890 | slice_ = self.buffer_slices[orientation].image | 893 | slice_ = self.buffer_slices[orientation].image |
891 | - self.buffer_slices[orientation].mask = (255 * ((slice_ >= thresh_min) & (slice_ <= thresh_max))).astype('uint8') | 894 | + if slice_ is not None: |
895 | + self.buffer_slices[orientation].mask = (255 * ((slice_ >= thresh_min) & (slice_ <= thresh_max))).astype('uint8') | ||
892 | 896 | ||
893 | # Update viewer | 897 | # Update viewer |
894 | #Publisher.sendMessage('Update slice viewer') | 898 | #Publisher.sendMessage('Update slice viewer') |
invesalius/data/surface.py
@@ -191,14 +191,15 @@ class SurfaceManager(): | @@ -191,14 +191,15 @@ class SurfaceManager(): | ||
191 | if selected_items: | 191 | if selected_items: |
192 | for index in selected_items: | 192 | for index in selected_items: |
193 | proj.RemoveSurface(index) | 193 | proj.RemoveSurface(index) |
194 | - actor = old_dict[index] | ||
195 | - for i in old_dict: | ||
196 | - if i < index: | ||
197 | - new_dict[i] = old_dict[i] | ||
198 | - if i > index: | ||
199 | - new_dict[i-1] = old_dict[i] | ||
200 | - old_dict = new_dict | ||
201 | - Publisher.sendMessage('Remove surface actor from viewer', actor) | 194 | + if index in old_dict: |
195 | + actor = old_dict[index] | ||
196 | + for i in old_dict: | ||
197 | + if i < index: | ||
198 | + new_dict[i] = old_dict[i] | ||
199 | + if i > index: | ||
200 | + new_dict[i-1] = old_dict[i] | ||
201 | + old_dict = new_dict | ||
202 | + Publisher.sendMessage('Remove surface actor from viewer', actor) | ||
202 | self.actors_dict = new_dict | 203 | self.actors_dict = new_dict |
203 | 204 | ||
204 | if self.last_surface_index in selected_items: | 205 | if self.last_surface_index in selected_items: |
@@ -674,7 +675,6 @@ class SurfaceManager(): | @@ -674,7 +675,6 @@ class SurfaceManager(): | ||
674 | # polydata.SetSource(None) | 675 | # polydata.SetSource(None) |
675 | del decimation | 676 | del decimation |
676 | 677 | ||
677 | - to_measure = polydata | ||
678 | #to_measure.Register(None) | 678 | #to_measure.Register(None) |
679 | # to_measure.SetSource(None) | 679 | # to_measure.SetSource(None) |
680 | 680 | ||
@@ -713,115 +713,133 @@ class SurfaceManager(): | @@ -713,115 +713,133 @@ class SurfaceManager(): | ||
713 | # polydata.DebugOn() | 713 | # polydata.DebugOn() |
714 | del filled_polydata | 714 | del filled_polydata |
715 | 715 | ||
716 | - normals = vtk.vtkPolyDataNormals() | ||
717 | - # normals.ReleaseDataFlagOn() | ||
718 | - normals_ref = weakref.ref(normals) | ||
719 | - normals_ref().AddObserver("ProgressEvent", lambda obj,evt: | ||
720 | - UpdateProgress(normals_ref(), _("Creating 3D surface..."))) | ||
721 | - normals.SetInputData(polydata) | ||
722 | - normals.SetFeatureAngle(80) | ||
723 | - normals.AutoOrientNormalsOn() | ||
724 | - # normals.GetOutput().ReleaseDataFlagOn() | ||
725 | - normals.Update() | ||
726 | - del polydata | ||
727 | - polydata = normals.GetOutput() | ||
728 | - #polydata.Register(None) | ||
729 | - # polydata.SetSource(None) | ||
730 | - del normals | ||
731 | - | ||
732 | - # Improve performance | ||
733 | - stripper = vtk.vtkStripper() | ||
734 | - # stripper.ReleaseDataFlagOn() | ||
735 | - stripper_ref = weakref.ref(stripper) | ||
736 | - stripper_ref().AddObserver("ProgressEvent", lambda obj,evt: | ||
737 | - UpdateProgress(stripper_ref(), _("Creating 3D surface..."))) | ||
738 | - stripper.SetInputData(polydata) | ||
739 | - stripper.PassThroughCellIdsOn() | ||
740 | - stripper.PassThroughPointIdsOn() | ||
741 | - # stripper.GetOutput().ReleaseDataFlagOn() | ||
742 | - stripper.Update() | ||
743 | - del polydata | ||
744 | - polydata = stripper.GetOutput() | ||
745 | - #polydata.Register(None) | ||
746 | - # polydata.SetSource(None) | ||
747 | - del stripper | 716 | + to_measure = polydata |
748 | 717 | ||
749 | - # Map polygonal data (vtkPolyData) to graphics primitives. | ||
750 | - mapper = vtk.vtkPolyDataMapper() | ||
751 | - mapper.SetInputData(polydata) | ||
752 | - mapper.ScalarVisibilityOff() | ||
753 | - # mapper.ReleaseDataFlagOn() | ||
754 | - mapper.ImmediateModeRenderingOn() # improve performance | 718 | + # If InVesalius is running without GUI |
719 | + if wx.GetApp() is None: | ||
720 | + proj = prj.Project() | ||
721 | + #Create Surface instance | ||
722 | + if overwrite: | ||
723 | + surface = Surface(index = self.last_surface_index) | ||
724 | + proj.ChangeSurface(surface) | ||
725 | + else: | ||
726 | + surface = Surface(name=surface_name) | ||
727 | + index = proj.AddSurface(surface) | ||
728 | + surface.index = index | ||
729 | + self.last_surface_index = index | ||
730 | + surface.colour = colour | ||
731 | + surface.polydata = polydata | ||
755 | 732 | ||
756 | - # Represent an object (geometry & properties) in the rendered scene | ||
757 | - actor = vtk.vtkActor() | ||
758 | - actor.SetMapper(mapper) | ||
759 | - del mapper | ||
760 | - #Create Surface instance | ||
761 | - if overwrite: | ||
762 | - surface = Surface(index = self.last_surface_index) | 733 | + # With GUI |
763 | else: | 734 | else: |
764 | - surface = Surface(name=surface_name) | ||
765 | - surface.colour = colour | ||
766 | - surface.polydata = polydata | ||
767 | - del polydata | 735 | + normals = vtk.vtkPolyDataNormals() |
736 | + # normals.ReleaseDataFlagOn() | ||
737 | + normals_ref = weakref.ref(normals) | ||
738 | + normals_ref().AddObserver("ProgressEvent", lambda obj,evt: | ||
739 | + UpdateProgress(normals_ref(), _("Creating 3D surface..."))) | ||
740 | + normals.SetInputData(polydata) | ||
741 | + normals.SetFeatureAngle(80) | ||
742 | + normals.AutoOrientNormalsOn() | ||
743 | + # normals.GetOutput().ReleaseDataFlagOn() | ||
744 | + normals.Update() | ||
745 | + del polydata | ||
746 | + polydata = normals.GetOutput() | ||
747 | + #polydata.Register(None) | ||
748 | + # polydata.SetSource(None) | ||
749 | + del normals | ||
768 | 750 | ||
769 | - # Set actor colour and transparency | ||
770 | - actor.GetProperty().SetColor(colour) | ||
771 | - actor.GetProperty().SetOpacity(1-surface.transparency) | 751 | + # Improve performance |
752 | + stripper = vtk.vtkStripper() | ||
753 | + # stripper.ReleaseDataFlagOn() | ||
754 | + stripper_ref = weakref.ref(stripper) | ||
755 | + stripper_ref().AddObserver("ProgressEvent", lambda obj,evt: | ||
756 | + UpdateProgress(stripper_ref(), _("Creating 3D surface..."))) | ||
757 | + stripper.SetInputData(polydata) | ||
758 | + stripper.PassThroughCellIdsOn() | ||
759 | + stripper.PassThroughPointIdsOn() | ||
760 | + # stripper.GetOutput().ReleaseDataFlagOn() | ||
761 | + stripper.Update() | ||
762 | + del polydata | ||
763 | + polydata = stripper.GetOutput() | ||
764 | + #polydata.Register(None) | ||
765 | + # polydata.SetSource(None) | ||
766 | + del stripper | ||
772 | 767 | ||
773 | - prop = actor.GetProperty() | 768 | + # Map polygonal data (vtkPolyData) to graphics primitives. |
769 | + mapper = vtk.vtkPolyDataMapper() | ||
770 | + mapper.SetInputData(polydata) | ||
771 | + mapper.ScalarVisibilityOff() | ||
772 | + # mapper.ReleaseDataFlagOn() | ||
773 | + mapper.ImmediateModeRenderingOn() # improve performance | ||
774 | 774 | ||
775 | - interpolation = int(ses.Session().surface_interpolation) | 775 | + # Represent an object (geometry & properties) in the rendered scene |
776 | + actor = vtk.vtkActor() | ||
777 | + actor.SetMapper(mapper) | ||
778 | + del mapper | ||
779 | + #Create Surface instance | ||
780 | + if overwrite: | ||
781 | + surface = Surface(index = self.last_surface_index) | ||
782 | + else: | ||
783 | + surface = Surface(name=surface_name) | ||
784 | + surface.colour = colour | ||
785 | + surface.polydata = polydata | ||
786 | + del polydata | ||
776 | 787 | ||
777 | - prop.SetInterpolation(interpolation) | 788 | + # Set actor colour and transparency |
789 | + actor.GetProperty().SetColor(colour) | ||
790 | + actor.GetProperty().SetOpacity(1-surface.transparency) | ||
778 | 791 | ||
779 | - proj = prj.Project() | ||
780 | - if overwrite: | ||
781 | - proj.ChangeSurface(surface) | ||
782 | - else: | ||
783 | - index = proj.AddSurface(surface) | ||
784 | - surface.index = index | ||
785 | - self.last_surface_index = index | 792 | + prop = actor.GetProperty() |
786 | 793 | ||
787 | - session = ses.Session() | ||
788 | - session.ChangeProject() | 794 | + interpolation = int(ses.Session().surface_interpolation) |
789 | 795 | ||
790 | - # The following lines have to be here, otherwise all volumes disappear | ||
791 | - measured_polydata = vtk.vtkMassProperties() | ||
792 | - # measured_polydata.ReleaseDataFlagOn() | ||
793 | - measured_polydata.SetInputData(to_measure) | ||
794 | - volume = float(measured_polydata.GetVolume()) | ||
795 | - area = float(measured_polydata.GetSurfaceArea()) | ||
796 | - surface.volume = volume | ||
797 | - surface.area = area | ||
798 | - self.last_surface_index = surface.index | ||
799 | - del measured_polydata | ||
800 | - del to_measure | 796 | + prop.SetInterpolation(interpolation) |
801 | 797 | ||
802 | - Publisher.sendMessage('Load surface actor into viewer', actor) | 798 | + proj = prj.Project() |
799 | + if overwrite: | ||
800 | + proj.ChangeSurface(surface) | ||
801 | + else: | ||
802 | + index = proj.AddSurface(surface) | ||
803 | + surface.index = index | ||
804 | + self.last_surface_index = index | ||
803 | 805 | ||
804 | - # Send actor by pubsub to viewer's render | ||
805 | - if overwrite and self.actors_dict.keys(): | ||
806 | - old_actor = self.actors_dict[self.last_surface_index] | ||
807 | - Publisher.sendMessage('Remove surface actor from viewer', old_actor) | 806 | + session = ses.Session() |
807 | + session.ChangeProject() | ||
808 | 808 | ||
809 | - # Save actor for future management tasks | ||
810 | - self.actors_dict[surface.index] = actor | 809 | + measured_polydata = vtk.vtkMassProperties() |
810 | + # measured_polydata.ReleaseDataFlagOn() | ||
811 | + measured_polydata.SetInputData(to_measure) | ||
812 | + volume = float(measured_polydata.GetVolume()) | ||
813 | + area = float(measured_polydata.GetSurfaceArea()) | ||
814 | + surface.volume = volume | ||
815 | + surface.area = area | ||
816 | + self.last_surface_index = surface.index | ||
817 | + del measured_polydata | ||
818 | + del to_measure | ||
811 | 819 | ||
812 | - Publisher.sendMessage('Update surface info in GUI', | ||
813 | - (surface.index, surface.name, | ||
814 | - surface.colour, surface.volume, | ||
815 | - surface.area, | ||
816 | - surface.transparency)) | ||
817 | - | ||
818 | - #When you finalize the progress. The bar is cleaned. | ||
819 | - UpdateProgress = vu.ShowProgress(1) | ||
820 | - UpdateProgress(0, _("Ready")) | ||
821 | - Publisher.sendMessage('Update status text in GUI', _("Ready")) | ||
822 | - | ||
823 | - Publisher.sendMessage('End busy cursor') | ||
824 | - del actor | 820 | + Publisher.sendMessage('Load surface actor into viewer', actor) |
821 | + | ||
822 | + # Send actor by pubsub to viewer's render | ||
823 | + if overwrite and self.actors_dict.keys(): | ||
824 | + old_actor = self.actors_dict[self.last_surface_index] | ||
825 | + Publisher.sendMessage('Remove surface actor from viewer', old_actor) | ||
826 | + | ||
827 | + # Save actor for future management tasks | ||
828 | + self.actors_dict[surface.index] = actor | ||
829 | + | ||
830 | + Publisher.sendMessage('Update surface info in GUI', | ||
831 | + (surface.index, surface.name, | ||
832 | + surface.colour, surface.volume, | ||
833 | + surface.area, | ||
834 | + surface.transparency)) | ||
835 | + | ||
836 | + #When you finalize the progress. The bar is cleaned. | ||
837 | + UpdateProgress = vu.ShowProgress(1) | ||
838 | + UpdateProgress(0, _("Ready")) | ||
839 | + Publisher.sendMessage('Update status text in GUI', _("Ready")) | ||
840 | + | ||
841 | + Publisher.sendMessage('End busy cursor') | ||
842 | + del actor | ||
825 | 843 | ||
826 | def UpdateSurfaceInterpolation(self, pub_evt): | 844 | def UpdateSurfaceInterpolation(self, pub_evt): |
827 | interpolation = int(ses.Session().surface_interpolation) | 845 | interpolation = int(ses.Session().surface_interpolation) |
invesalius/data/vtk_utils.py
@@ -44,7 +44,10 @@ def ShowProgress(number_of_filters = 1, | @@ -44,7 +44,10 @@ def ShowProgress(number_of_filters = 1, | ||
44 | progress = [0] | 44 | progress = [0] |
45 | last_obj_progress = [0] | 45 | last_obj_progress = [0] |
46 | if (dialog_type == "ProgressDialog"): | 46 | if (dialog_type == "ProgressDialog"): |
47 | - dlg = ProgressDialog(100) | 47 | + try: |
48 | + dlg = ProgressDialog(100) | ||
49 | + except wx._core.PyNoAppError: | ||
50 | + return lambda obj, label: 0 | ||
48 | 51 | ||
49 | 52 | ||
50 | # when the pipeline is larger than 1, we have to consider this object | 53 | # when the pipeline is larger than 1, we have to consider this object |
invesalius/gui/task_navigator.py
@@ -247,7 +247,7 @@ class NeuronavigationPanel(wx.Panel): | @@ -247,7 +247,7 @@ class NeuronavigationPanel(wx.Panel): | ||
247 | n = btns_img[k].keys()[0] | 247 | n = btns_img[k].keys()[0] |
248 | lab = btns_img[k].values()[0] | 248 | lab = btns_img[k].values()[0] |
249 | self.btns_coord[n] = wx.ToggleButton(self, k, label=lab, size=wx.Size(45, 23)) | 249 | self.btns_coord[n] = wx.ToggleButton(self, k, label=lab, size=wx.Size(45, 23)) |
250 | - self.btns_coord[n].SetToolTip(tips_img[n]) | 250 | + self.btns_coord[n].SetToolTip(wx.ToolTip(tips_img[n])) |
251 | self.btns_coord[n].Bind(wx.EVT_TOGGLEBUTTON, self.OnImageFiducials) | 251 | self.btns_coord[n].Bind(wx.EVT_TOGGLEBUTTON, self.OnImageFiducials) |
252 | 252 | ||
253 | # Push buttons for tracker fiducials | 253 | # Push buttons for tracker fiducials |
@@ -258,7 +258,7 @@ class NeuronavigationPanel(wx.Panel): | @@ -258,7 +258,7 @@ class NeuronavigationPanel(wx.Panel): | ||
258 | n = btns_trk[k].keys()[0] | 258 | n = btns_trk[k].keys()[0] |
259 | lab = btns_trk[k].values()[0] | 259 | lab = btns_trk[k].values()[0] |
260 | self.btns_coord[n] = wx.Button(self, k, label=lab, size=wx.Size(45, 23)) | 260 | self.btns_coord[n] = wx.Button(self, k, label=lab, size=wx.Size(45, 23)) |
261 | - self.btns_coord[n].SetToolTip(tips_trk[n-3]) | 261 | + self.btns_coord[n].SetToolTip(wx.ToolTip(tips_trk[n-3])) |
262 | # Excepetion for event of button that set image coordinates | 262 | # Excepetion for event of button that set image coordinates |
263 | if n == 6: | 263 | if n == 6: |
264 | self.btns_coord[n].Bind(wx.EVT_BUTTON, self.OnSetImageCoordinates) | 264 | self.btns_coord[n].Bind(wx.EVT_BUTTON, self.OnSetImageCoordinates) |
invesalius/utils.py
@@ -76,6 +76,7 @@ def debug(error_str): | @@ -76,6 +76,7 @@ def debug(error_str): | ||
76 | from invesalius.session import Session | 76 | from invesalius.session import Session |
77 | session = Session() | 77 | session = Session() |
78 | #if session.debug: | 78 | #if session.debug: |
79 | + print(error_str) | ||
79 | 80 | ||
80 | def next_copy_name(original_name, names_list): | 81 | def next_copy_name(original_name, names_list): |
81 | """ | 82 | """ |