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 @@ |
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 | 24 | import os |
25 | 25 | import sys |
26 | 26 | import shutil |
27 | +import traceback | |
28 | + | |
29 | +import re | |
27 | 30 | |
28 | 31 | if sys.platform == 'win32': |
29 | 32 | import _winreg |
... | ... | @@ -218,7 +221,8 @@ class SplashScreen(wx.SplashScreen): |
218 | 221 | self.control = Controller(self.main) |
219 | 222 | |
220 | 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 | 227 | # Check for updates |
224 | 228 | from threading import Thread |
... | ... | @@ -244,6 +248,24 @@ class SplashScreen(wx.SplashScreen): |
244 | 248 | if self.fc.IsRunning(): |
245 | 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 | 284 | action="store_true", |
263 | 285 | dest="debug") |
264 | 286 | |
287 | + parser.add_option('--no-gui', | |
288 | + action='store_true', | |
289 | + dest='no_gui') | |
290 | + | |
265 | 291 | # -i or --import: import DICOM directory |
266 | 292 | # chooses largest series |
267 | 293 | parser.add_option("-i", "--import", |
268 | 294 | action="store", |
269 | 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 | 312 | options, args = parser.parse_args() |
313 | + return options, args | |
314 | + | |
271 | 315 | |
316 | +def use_cmd_optargs(options, args): | |
272 | 317 | # If debug argument... |
273 | 318 | if options.debug: |
274 | 319 | Publisher.subscribe(print_events, Publisher.ALL_TOPICS) |
320 | + session = ses.Session() | |
275 | 321 | session.debug = 1 |
276 | 322 | |
277 | 323 | # If import DICOM argument... |
278 | 324 | if options.dicom_dir: |
279 | 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 | 342 | return True |
282 | 343 | |
283 | 344 | # Check if there is a file path somewhere in what the user wrote |
284 | 345 | # In case there is, try opening as it was a inv3 |
285 | 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 | 353 | return True |
295 | 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 | 419 | def print_events(data): |
299 | 420 | """ |
300 | 421 | Print pubsub messages |
... | ... | @@ -305,8 +426,13 @@ def main(): |
305 | 426 | """ |
306 | 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 | 437 | if __name__ == '__main__': |
312 | 438 | #Is needed because of pyinstaller | ... | ... |
invesalius/constants.py
... | ... | @@ -694,19 +694,19 @@ BTNS_IMG_MKS = {IR1: {0: 'LEI'}, |
694 | 694 | IR2: {1: 'REI'}, |
695 | 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 | 701 | BTNS_TRK = {TR1: {3: _('LET')}, |
702 | 702 | TR2: {4: _('RET')}, |
703 | 703 | TR3: {5: _('NAT')}, |
704 | 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 | 711 | CAL_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'mtc_files', 'CalibrationFiles')) |
712 | 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 | 68 | |
69 | 69 | def __bind_events(self): |
70 | 70 | Publisher.subscribe(self.OnImportMedicalImages, 'Import directory') |
71 | + Publisher.subscribe(self.OnImportGroup, 'Import group') | |
71 | 72 | Publisher.subscribe(self.OnShowDialogImportDirectory, |
72 | 73 | 'Show import directory dialog') |
73 | 74 | Publisher.subscribe(self.OnShowDialogImportOtherFiles, |
... | ... | @@ -105,6 +106,8 @@ class Controller(): |
105 | 106 | |
106 | 107 | Publisher.subscribe(self.SetBitmapSpacing, 'Set bitmap spacing') |
107 | 108 | |
109 | + Publisher.subscribe(self.OnSaveProject, 'Save project') | |
110 | + | |
108 | 111 | def SetBitmapSpacing(self, pubsub_evt): |
109 | 112 | proj = prj.Project() |
110 | 113 | proj.spacing = pubsub_evt.data |
... | ... | @@ -329,6 +332,10 @@ class Controller(): |
329 | 332 | session.OpenProject(filepath) |
330 | 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 | 339 | def SaveProject(self, path=None): |
333 | 340 | Publisher.sendMessage('Begin busy cursor') |
334 | 341 | session = ses.Session() |
... | ... | @@ -442,10 +449,11 @@ class Controller(): |
442 | 449 | #----------- to import by command line --------------------------------------------------- |
443 | 450 | |
444 | 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 | 457 | patients_groups = dcm.GetDicomGroups(directory) |
450 | 458 | name = directory.rpartition('\\')[-1].split('.') |
451 | 459 | print "patients: ", patients_groups |
... | ... | @@ -453,7 +461,7 @@ class Controller(): |
453 | 461 | if len(patients_groups): |
454 | 462 | # OPTION 1: DICOM |
455 | 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 | 465 | self.CreateDicomProject(dicom, matrix, matrix_filename) |
458 | 466 | else: |
459 | 467 | # OPTION 2: NIfTI, Analyze or PAR/REC |
... | ... | @@ -476,6 +484,18 @@ class Controller(): |
476 | 484 | self.LoadProject() |
477 | 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 | 501 | def LoadProject(self): |
... | ... | @@ -785,7 +805,7 @@ class Controller(): |
785 | 805 | n_slices = len(filelist) |
786 | 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 | 809 | re_dialog = dialog.ResizeImageDialog() |
790 | 810 | re_dialog.SetValue(int(resolution_percentage*100)) |
791 | 811 | re_dialog_value = re_dialog.ShowModal() | ... | ... |
invesalius/data/slice_.py
... | ... | @@ -359,19 +359,22 @@ class Slice(object): |
359 | 359 | |
360 | 360 | # TODO: merge this code with apply_slice_buffer_to_mask |
361 | 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 | 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 | 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 | 379 | if to_reload: |
377 | 380 | Publisher.sendMessage('Reload actual slice') |
... | ... | @@ -888,7 +891,8 @@ class Slice(object): |
888 | 891 | self.current_mask.matrix[n+1, 1:, 1:] = m |
889 | 892 | else: |
890 | 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 | 897 | # Update viewer |
894 | 898 | #Publisher.sendMessage('Update slice viewer') | ... | ... |
invesalius/data/surface.py
... | ... | @@ -191,14 +191,15 @@ class SurfaceManager(): |
191 | 191 | if selected_items: |
192 | 192 | for index in selected_items: |
193 | 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 | 203 | self.actors_dict = new_dict |
203 | 204 | |
204 | 205 | if self.last_surface_index in selected_items: |
... | ... | @@ -674,7 +675,6 @@ class SurfaceManager(): |
674 | 675 | # polydata.SetSource(None) |
675 | 676 | del decimation |
676 | 677 | |
677 | - to_measure = polydata | |
678 | 678 | #to_measure.Register(None) |
679 | 679 | # to_measure.SetSource(None) |
680 | 680 | |
... | ... | @@ -713,115 +713,133 @@ class SurfaceManager(): |
713 | 713 | # polydata.DebugOn() |
714 | 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 | 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 | 844 | def UpdateSurfaceInterpolation(self, pub_evt): |
827 | 845 | interpolation = int(ses.Session().surface_interpolation) | ... | ... |
invesalius/data/vtk_utils.py
... | ... | @@ -44,7 +44,10 @@ def ShowProgress(number_of_filters = 1, |
44 | 44 | progress = [0] |
45 | 45 | last_obj_progress = [0] |
46 | 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 | 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 | 247 | n = btns_img[k].keys()[0] |
248 | 248 | lab = btns_img[k].values()[0] |
249 | 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 | 251 | self.btns_coord[n].Bind(wx.EVT_TOGGLEBUTTON, self.OnImageFiducials) |
252 | 252 | |
253 | 253 | # Push buttons for tracker fiducials |
... | ... | @@ -258,7 +258,7 @@ class NeuronavigationPanel(wx.Panel): |
258 | 258 | n = btns_trk[k].keys()[0] |
259 | 259 | lab = btns_trk[k].values()[0] |
260 | 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 | 262 | # Excepetion for event of button that set image coordinates |
263 | 263 | if n == 6: |
264 | 264 | self.btns_coord[n].Bind(wx.EVT_BUTTON, self.OnSetImageCoordinates) | ... | ... |