Commit 3b526c229b4fe68b0a494dc68bc97642cb759852

Authored by Thiago Franco de Moraes
2 parents f68939cd b07a0770

Merge pull request #31 from tfmoraes/fixes_pre_beta5

Fixes some little issues before new version

Fixes:

    Only accepts boolean operations when there 2 or more masks
    Watershed and Editor new shortcuts
    Better watershed progress dialog
    Added a tool menu
    Icons to MacOSX
    Combobox problems with the surface indexes
    Added an Ok and a Cancel button to the Watershed config dialog
    Only showing editor and watershed brush when enter in the renderviewer
Showing 111 changed files with 371 additions and 251 deletions   Show diff stats
icons/3D_glasses.png

3.7 KB | W: | H:

1.16 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/3D_glasses_original.png

14.9 KB | W: | H:

12.6 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/Floppy.png

3.29 KB | W: | H:

688 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/annotation.png

43.5 KB | W: | H:

43.6 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/bool_difference.png

884 Bytes | W: | H:

739 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/bool_disjunction.png

840 Bytes | W: | H:

695 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/bool_intersection.png

820 Bytes | W: | H:

675 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/bool_union.png

843 Bytes | W: | H:

698 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/configuration.png

614 Bytes | W: | H:

469 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/connectivity_largest.png

37.6 KB | W: | H:

37.8 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/connectivity_manual.png

36.4 KB | W: | H:

36.6 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/connectivity_split_all.png

38.7 KB | W: | H:

38.8 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/cross.png

628 Bytes | W: | H:

558 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/cross_original.png

12.4 KB | W: | H:

1.37 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/data_duplicate.png

806 Bytes | W: | H:

840 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/data_new.png

767 Bytes | W: | H:

893 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/data_remove.png

643 Bytes | W: | H:

563 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/file_export.png

20.9 KB | W: | H:

15.8 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/file_from_internet.png

1.59 KB | W: | H:

1.62 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/file_from_internet_original.png

25.8 KB | W: | H:

3.04 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/file_import.png

1.04 KB | W: | H:

1.36 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/file_import_original.png

8.54 KB | W: | H:

1.96 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/file_open.png

3.78 KB | W: | H:

1.23 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/file_open_original.png

11.2 KB | W: | H:

1.93 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/file_save.png

993 Bytes | W: | H:

1.22 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/file_save_original.png

11.2 KB | W: | H:

1.71 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/it_IT.png

144 Bytes | W: | H:

196 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/layout_data_only.png

1.28 KB | W: | H:

1.43 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/layout_data_only_original.gif

1.57 KB | W: | H:

788 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/layout_data_only_original.png

952 Bytes | W: | H:

1004 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/layout_full.png

1.3 KB | W: | H:

1.46 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/layout_full_original.gif

1.57 KB | W: | H:

804 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/layout_full_original.png

943 Bytes | W: | H:

996 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/mask.png

3.75 KB | W: | H:

2.63 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/mask_small.png

1.25 KB | W: | H:

791 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/measure_angle.png

1.76 KB | W: | H:

1.79 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/measure_angle_original.png

15.8 KB | W: | H:

2.16 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/measure_line.png

1.75 KB | W: | H:

1.79 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/measure_line_original.png

14.5 KB | W: | H:

2.09 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/object_add.png

1.04 KB | W: | H:

1.23 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/object_add_original.png

8.72 KB | W: | H:

9.17 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/object_remove.png

1.56 KB | W: | H:

1.61 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/object_remove_original.png

9.04 KB | W: | H:

9.47 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/print.png

3.8 KB | W: | H:

1.27 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/print_original.png

21.8 KB | W: | H:

2.21 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/redo_menu.png

3.44 KB | W: | H:

1017 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/redo_original.png

15.3 KB | W: | H:

1.64 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/redo_small.png

4.03 KB | W: | H:

1.49 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/show_task.png

4.09 KB | W: | H:

4.31 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/show_task_min.png

1.67 KB | W: | H:

1.73 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/slice.png

1.31 KB | W: | H:

827 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/slice_original.png

6.7 KB | W: | H:

828 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/slice_plane.png

1.05 KB | W: | H:

1.23 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/slice_plane_original.png

2.58 KB | W: | H:

2.64 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_de.png

217 KB | W: | H:

224 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_de_DE.png

217 KB | W: | H:

224 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_el_GR.png

217 KB | W: | H:

224 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_en.png

217 KB | W: | H:

224 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_es.png

218 KB | W: | H:

225 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_fr.png

220 KB | W: | H:

226 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_it_IT.png

217 KB | W: | H:

224 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_ko.png

217 KB | W: | H:

224 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_pt.png

220 KB | W: | H:

226 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/splash_zh_TW.png

217 KB | W: | H:

224 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/surface_export.png

5.04 KB | W: | H:

2.67 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/surface_export_original.png

27.2 KB | W: | H:

27.4 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/text.png

572 Bytes | W: | H:

477 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/text_inverted.png

706 Bytes | W: | H:

543 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/text_inverted_original.png

2.22 KB | W: | H:

541 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/text_original.png

2.69 KB | W: | H:

567 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_annotation.png

1.7 KB | W: | H:

1.73 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_annotation_original.png

21.2 KB | W: | H:

21.6 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_contrast.png

1.01 KB | W: | H:

1.07 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_contrast_original.png

1.55 KB | W: | H:

1.65 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_photo.png

2.67 KB | W: | H:

2.71 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_photo_original.png

25.3 KB | W: | H:

2.87 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_rotate.png

4.45 KB | W: | H:

1.91 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_rotate_original.png

12.7 KB | W: | H:

2.45 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_translate.png

4.02 KB | W: | H:

1.49 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_translate_original.png

11.8 KB | W: | H:

2.21 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_zoom.png

1.14 KB | W: | H:

1.31 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_zoom_in.png

1.28 KB | W: | H:

1.38 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_zoom_in_original.png

13.1 KB | W: | H:

13.5 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_zoom_original.png

17.1 KB | W: | H:

2.11 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_zoom_out.png

1.26 KB | W: | H:

1.35 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_zoom_out_original.png

13.2 KB | W: | H:

13.6 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_zoom_select.png

1.16 KB | W: | H:

1.33 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/tool_zoom_select_original.png

15 KB | W: | H:

2.23 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/undo_menu.png

3.46 KB | W: | H:

1.02 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/undo_original.png

15.6 KB | W: | H:

1.72 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/undo_small.png

3.97 KB | W: | H:

1.42 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/view_back.png

3.34 KB | W: | H:

996 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/view_bottom.png

3.32 KB | W: | H:

938 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/view_front.png

753 Bytes | W: | H:

912 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/view_isometric.png

3.5 KB | W: | H:

1010 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/view_left.png

3.31 KB | W: | H:

929 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/view_right.png

713 Bytes | W: | H:

920 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/view_top.png

784 Bytes | W: | H:

1004 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/volume_raycasting.png

1.23 KB | W: | H:

1.21 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
icons/volume_raycasting_original.png

3.04 KB | W: | H:

3.03 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
invesalius/constants.py
... ... @@ -451,8 +451,10 @@ VTK_WARNING = 0
451 451  
452 452 [ID_DICOM_IMPORT, ID_PROJECT_OPEN, ID_PROJECT_SAVE_AS, ID_PROJECT_SAVE,
453 453 ID_PROJECT_CLOSE, ID_PROJECT_INFO, ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET,
454   -ID_PRINT_SCREENSHOT, ID_EXIT, ID_IMPORT_OTHERS_FILES, ID_ANALYZE_IMPORT, ID_PREFERENCES,
455   -ID_DICOM_NETWORK] = [wx.NewId() for number in range(14)]
  454 +ID_PRINT_SCREENSHOT, ID_IMPORT_OTHERS_FILES, ID_ANALYZE_IMPORT, ID_PREFERENCES,
  455 +ID_DICOM_NETWORK] = [wx.NewId() for number in range(13)]
  456 +ID_EXIT = wx.ID_EXIT
  457 +ID_ABOUT = wx.ID_ABOUT
456 458  
457 459  
458 460 [ID_EDIT_UNDO, ID_EDIT_REDO, ID_EDIT_LIST] =\
... ... @@ -464,7 +466,6 @@ ID_DICOM_NETWORK] = [wx.NewId() for number in range(14)]
464 466 [ID_VIEW_FULL, ID_VIEW_TEXT, ID_VIEW_3D_BACKGROUND] =\
465 467 [wx.NewId() for number in range(3)]
466 468  
467   -ID_ABOUT = wx.NewId()
468 469 ID_START = wx.NewId()
469 470  
470 471 ID_FLIP_X = wx.NewId()
... ... @@ -476,6 +477,7 @@ ID_SWAP_XZ = wx.NewId()
476 477 ID_SWAP_YZ = wx.NewId()
477 478  
478 479 ID_BOOLEAN_MASK = wx.NewId()
  480 +ID_CLEAN_MASK = wx.NewId()
479 481  
480 482 #---------------------------------------------------------
481 483 STATE_DEFAULT = 1000
... ...
invesalius/data/mask.py
... ... @@ -285,6 +285,12 @@ class Mask():
285 285 shape = shape[0] + 1, shape[1] + 1, shape[2] + 1
286 286 self.matrix = numpy.memmap(self.temp_file, mode='w+', dtype='uint8', shape=shape)
287 287  
  288 + def clean(self):
  289 + self.matrix[1:, 1:, 1:] = 0
  290 + self.matrix[0, :, :] = 1
  291 + self.matrix[:, 0, :] = 1
  292 + self.matrix[:, :, 0] = 1
  293 +
288 294 def copy(self, copy_name):
289 295 """
290 296 creates and return a copy from the mask instance.
... ...
invesalius/data/slice_.py
... ... @@ -142,6 +142,7 @@ class Slice(object):
142 142 Publisher.subscribe(self.__set_mask_name, 'Change mask name')
143 143 Publisher.subscribe(self.__show_mask, 'Show mask')
144 144 Publisher.subscribe(self.__hide_current_mask, 'Hide current mask')
  145 + Publisher.subscribe(self.__clean_current_mask, 'Clean current mask')
145 146  
146 147 Publisher.subscribe(self.__set_current_mask_threshold_limits,
147 148 'Update threshold limits')
... ... @@ -211,6 +212,7 @@ class Slice(object):
211 212 buffer_.discard_vtk_mask()
212 213 buffer_.discard_mask()
213 214  
  215 + Publisher.sendMessage('Show mask', (item, 0))
214 216 Publisher.sendMessage('Reload actual slice')
215 217  
216 218 def OnDuplicateMasks(self, pubsub_evt):
... ... @@ -385,6 +387,15 @@ class Slice(object):
385 387 value = False
386 388 Publisher.sendMessage('Show mask', (index, value))
387 389  
  390 + def __clean_current_mask(self, pubsub_evt):
  391 + if self.current_mask:
  392 + self.current_mask.clean()
  393 + for buffer_ in self.buffer_slices.values():
  394 + buffer_.discard_vtk_mask()
  395 + buffer_.discard_mask()
  396 + self.current_mask.clear_history()
  397 + self.current_mask.was_edited = True
  398 +
388 399 def create_temp_mask(self):
389 400 temp_file = tempfile.mktemp()
390 401 shape = self.matrix.shape
... ... @@ -1012,6 +1023,7 @@ class Slice(object):
1012 1023  
1013 1024 if show:
1014 1025 self.current_mask = mask
  1026 + Publisher.sendMessage('Show mask', (mask.index, 1))
1015 1027 Publisher.sendMessage('Change mask selected', mask.index)
1016 1028 Publisher.sendMessage('Update slice viewer')
1017 1029  
... ...
invesalius/data/styles.py
... ... @@ -57,7 +57,7 @@ WATERSHED_OPERATIONS = {_("Erase"): BRUSH_ERASE,
57 57 _("Background"): BRUSH_BACKGROUND,}
58 58  
59 59 def get_LUT_value(data, window, level):
60   - return np.piecewise(data,
  60 + return np.piecewise(data,
61 61 [data <= (level - 0.5 - (window-1)/2),
62 62 data > (level - 0.5 + (window-1)/2)],
63 63 [0, 255, lambda data: ((data - (level - 0.5))/(window-1) + 0.5)*(255-0)])
... ... @@ -100,8 +100,8 @@ class BaseImageInteractorStyle(vtk.vtkInteractorStyleImage):
100 100  
101 101 class DefaultInteractorStyle(BaseImageInteractorStyle):
102 102 """
103   - Interactor style responsible for Default functionalities:
104   - * Zoom moving mouse with right button pressed;
  103 + Interactor style responsible for Default functionalities:
  104 + * Zoom moving mouse with right button pressed;
105 105 * Change the slices with the scroll.
106 106 """
107 107 def __init__(self, viewer):
... ... @@ -129,10 +129,33 @@ class DefaultInteractorStyle(BaseImageInteractorStyle):
129 129 evt.StartDolly()
130 130  
131 131 def OnScrollForward(self, evt, obj):
132   - self.viewer.OnScrollForward()
  132 + iren = self.viewer.interactor
  133 + viewer = self.viewer
  134 + if iren.GetShiftKey():
  135 + opacity = viewer.slice_.opacity + 0.1
  136 + if opacity <= 1:
  137 + viewer.slice_.opacity = opacity
  138 + self.viewer.slice_.buffer_slices['AXIAL'].discard_vtk_mask()
  139 + self.viewer.slice_.buffer_slices['CORONAL'].discard_vtk_mask()
  140 + self.viewer.slice_.buffer_slices['SAGITAL'].discard_vtk_mask()
  141 + Publisher.sendMessage('Reload actual slice')
  142 + else:
  143 + self.viewer.OnScrollForward()
133 144  
134 145 def OnScrollBackward(self, evt, obj):
135   - self.viewer.OnScrollBackward()
  146 + iren = self.viewer.interactor
  147 + viewer = self.viewer
  148 +
  149 + if iren.GetShiftKey():
  150 + opacity = viewer.slice_.opacity - 0.1
  151 + if opacity >= 0.1:
  152 + viewer.slice_.opacity = opacity
  153 + self.viewer.slice_.buffer_slices['AXIAL'].discard_vtk_mask()
  154 + self.viewer.slice_.buffer_slices['CORONAL'].discard_vtk_mask()
  155 + self.viewer.slice_.buffer_slices['SAGITAL'].discard_vtk_mask()
  156 + Publisher.sendMessage('Reload actual slice')
  157 + else:
  158 + self.viewer.OnScrollBackward()
136 159  
137 160  
138 161 class CrossInteractorStyle(DefaultInteractorStyle):
... ... @@ -182,7 +205,7 @@ class CrossInteractorStyle(DefaultInteractorStyle):
182 205 # Forcing focal point to be setted in the center of the pixel.
183 206 coord_cross = self.slice_actor.GetInput().GetPoint(position)
184 207  
185   - coord = self.calcultate_scroll_position(position)
  208 + coord = self.calcultate_scroll_position(position)
186 209 self.ScrollSlice(coord)
187 210  
188 211 Publisher.sendMessage('Update cross position', coord_cross)
... ... @@ -190,7 +213,7 @@ class CrossInteractorStyle(DefaultInteractorStyle):
190 213 coord_cross)
191 214 Publisher.sendMessage('Set camera in volume', coord_cross)
192 215 Publisher.sendMessage('Render volume viewer')
193   -
  216 +
194 217 iren.Render()
195 218  
196 219  
... ... @@ -292,7 +315,7 @@ class WWWLInteractorStyle(DefaultInteractorStyle):
292 315 const.WINDOW_LEVEL['Manual'] = (self.acum_achange_window,\
293 316 self.acum_achange_level)
294 317 Publisher.sendMessage('Check window and level other')
295   - Publisher.sendMessage('Update window level value',(self.acum_achange_window,
  318 + Publisher.sendMessage('Update window level value',(self.acum_achange_window,
296 319 self.acum_achange_level))
297 320 #Necessary update the slice plane in the volume case exists
298 321 Publisher.sendMessage('Update slice viewer')
... ... @@ -339,7 +362,7 @@ class LinearMeasureInteractorStyle(DefaultInteractorStyle):
339 362 slice_number = self.slice_data.number
340 363 self.picker.Pick(x, y, 0, render)
341 364 x, y, z = self.picker.GetPickPosition()
342   - if self.picker.GetViewProp():
  365 + if self.picker.GetViewProp():
343 366 Publisher.sendMessage("Add measurement point",
344 367 ((x, y,z), const.LINEAR,
345 368 ORIENTATIONS[self.orientation],
... ... @@ -383,7 +406,7 @@ class AngularMeasureInteractorStyle(DefaultInteractorStyle):
383 406 slice_number = self.slice_data.number
384 407 self.picker.Pick(x, y, 0, render)
385 408 x, y, z = self.picker.GetPickPosition()
386   - if self.picker.GetViewProp():
  409 + if self.picker.GetViewProp():
387 410 Publisher.sendMessage("Add measurement point",
388 411 ((x, y,z), const.ANGULAR,
389 412 ORIENTATIONS[self.orientation],
... ... @@ -434,7 +457,7 @@ class SpinInteractorStyle(DefaultInteractorStyle):
434 457 ren = iren.FindPokedRenderer(mouse_x, mouse_y)
435 458 cam = ren.GetActiveCamera()
436 459 if (self.left_pressed):
437   - self.viewer.UpdateTextDirection(cam)
  460 + self.viewer.UpdateTextDirection(cam)
438 461 obj.Spin()
439 462 obj.OnRightButtonDown()
440 463  
... ... @@ -456,7 +479,7 @@ class ZoomInteractorStyle(DefaultInteractorStyle):
456 479 """
457 480 def __init__(self, viewer):
458 481 DefaultInteractorStyle.__init__(self, viewer)
459   -
  482 +
460 483 self.viewer = viewer
461 484  
462 485 self.AddObserver("MouseMoveEvent", self.OnZoomMoveLeft)
... ... @@ -532,6 +555,14 @@ class ChangeSliceInteractorStyle(DefaultInteractorStyle):
532 555 self.last_position = position[1]
533 556  
534 557  
  558 +class EditorConfig(object):
  559 + __metaclass__= utils.Singleton
  560 + def __init__(self):
  561 + self.operation = const.BRUSH_THRESH
  562 + self.cursor_type = const.BRUSH_CIRCLE
  563 + self.cursor_size = const.BRUSH_SIZE
  564 +
  565 +
535 566 class EditorInteractorStyle(DefaultInteractorStyle):
536 567 def __init__(self, viewer):
537 568 DefaultInteractorStyle.__init__(self, viewer)
... ... @@ -539,6 +570,8 @@ class EditorInteractorStyle(DefaultInteractorStyle):
539 570 self.viewer = viewer
540 571 self.orientation = self.viewer.orientation
541 572  
  573 + self.config = EditorConfig()
  574 +
542 575 self.picker = vtk.vtkWorldPointPicker()
543 576  
544 577 self.AddObserver("EnterEvent", self.OnEnterInteractor)
... ... @@ -553,13 +586,55 @@ class EditorInteractorStyle(DefaultInteractorStyle):
553 586 self.AddObserver("MouseWheelForwardEvent",self.EOnScrollForward)
554 587 self.AddObserver("MouseWheelBackwardEvent", self.EOnScrollBackward)
555 588  
  589 + Publisher.subscribe(self.set_bsize, 'Set edition brush size')
  590 + Publisher.subscribe(self.set_bformat, 'Set brush format')
  591 + Publisher.subscribe(self.set_boperation, 'Set edition operation')
  592 +
  593 + self._set_cursor()
  594 + self.viewer.slice_data.cursor.Show(0)
  595 +
  596 + def CleanUp(self):
  597 + Publisher.unsubscribe(self.set_bsize, 'Set edition brush size')
  598 + Publisher.unsubscribe(self.set_bformat, 'Set brush format')
  599 + Publisher.unsubscribe(self.set_boperation, 'Set edition operation')
  600 +
  601 + def set_bsize(self, pubsub_evt):
  602 + size = pubsub_evt.data
  603 + self.config.cursor_size = size
  604 + self.viewer.slice_data.cursor.SetSize(size)
  605 +
  606 + def set_bformat(self, pubsub_evt):
  607 + self.config.cursor_type = pubsub_evt.data
  608 + self._set_cursor()
  609 +
  610 + def set_boperation(self, pubsub_evt):
  611 + self.config.operation = pubsub_evt.data
  612 +
  613 + def _set_cursor(self):
  614 + if self.config.cursor_type == const.BRUSH_SQUARE:
  615 + cursor = ca.CursorRectangle()
  616 + elif self.config.cursor_type == const.BRUSH_CIRCLE:
  617 + cursor = ca.CursorCircle()
  618 +
  619 + cursor.SetOrientation(self.orientation)
  620 + n = self.viewer.slice_data.number
  621 + coordinates = {"SAGITAL": [n, 0, 0],
  622 + "CORONAL": [0, n, 0],
  623 + "AXIAL": [0, 0, n]}
  624 + cursor.SetPosition(coordinates[self.orientation])
  625 + spacing = self.viewer.slice_.spacing
  626 + cursor.SetSpacing(spacing)
  627 + cursor.SetColour(self.viewer._brush_cursor_colour)
  628 + cursor.SetSize(self.config.cursor_size)
  629 + self.viewer.slice_data.SetCursor(cursor)
  630 +
556 631 def OnEnterInteractor(self, obj, evt):
557 632 if (self.viewer.slice_.buffer_slices[self.orientation].mask is None):
558 633 return
559 634 self.viewer.slice_data.cursor.Show()
560 635 self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK))
561 636 self.viewer.interactor.Render()
562   -
  637 +
563 638 def OnLeaveInteractor(self, obj, evt):
564 639 self.viewer.slice_data.cursor.Show(0)
565 640 self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
... ... @@ -569,11 +644,10 @@ class EditorInteractorStyle(DefaultInteractorStyle):
569 644 if (self.viewer.slice_.buffer_slices[self.orientation].mask is None):
570 645 return
571 646  
572   -
573 647 viewer = self.viewer
574 648 iren = viewer.interactor
575 649  
576   - operation = viewer._brush_cursor_op
  650 + operation = self.config.operation
577 651 if operation == const.BRUSH_THRESH:
578 652 if iren.GetControlKey():
579 653 if iren.GetShiftKey():
... ... @@ -590,7 +664,7 @@ class EditorInteractorStyle(DefaultInteractorStyle):
590 664 operation = const.BRUSH_ERASE
591 665  
592 666 viewer._set_editor_cursor_visibility(1)
593   -
  667 +
594 668 mouse_x, mouse_y = iren.GetEventPosition()
595 669 render = iren.FindPokedRenderer(mouse_x, mouse_y)
596 670 slice_data = viewer.get_slice_data(render)
... ... @@ -601,10 +675,10 @@ class EditorInteractorStyle(DefaultInteractorStyle):
601 675 slice_data.cursor.Show()
602 676  
603 677 self.picker.Pick(mouse_x, mouse_y, 0, render)
604   -
  678 +
605 679 coord = self.get_coordinate_cursor()
606 680 position = slice_data.actor.GetInput().FindPoint(coord)
607   -
  681 +
608 682 if position != -1:
609 683 coord = slice_data.actor.GetInput().GetPoint(position)
610 684  
... ... @@ -630,12 +704,12 @@ class EditorInteractorStyle(DefaultInteractorStyle):
630 704 iren = viewer.interactor
631 705  
632 706 viewer._set_editor_cursor_visibility(1)
633   -
  707 +
634 708 mouse_x, mouse_y = iren.GetEventPosition()
635 709 render = iren.FindPokedRenderer(mouse_x, mouse_y)
636 710 slice_data = viewer.get_slice_data(render)
637 711  
638   - operation = viewer._brush_cursor_op
  712 + operation = self.config.operation
639 713 if operation == const.BRUSH_THRESH:
640 714 if iren.GetControlKey():
641 715 if iren.GetShiftKey():
... ... @@ -656,12 +730,12 @@ class EditorInteractorStyle(DefaultInteractorStyle):
656 730 #i.cursor.Show(0)
657 731  
658 732 self.picker.Pick(mouse_x, mouse_y, 0, render)
659   -
  733 +
660 734 #if (self.pick.GetViewProp()):
661 735 #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK))
662 736 #else:
663 737 #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
664   -
  738 +
665 739 coord = self.get_coordinate_cursor()
666 740 position = viewer.slice_data.actor.GetInput().FindPoint(coord)
667 741  
... ... @@ -672,7 +746,7 @@ class EditorInteractorStyle(DefaultInteractorStyle):
672 746 coord = slice_data.actor.GetInput().GetPoint(position)
673 747 slice_data.cursor.SetPosition(coord)
674 748 #self.__update_cursor_position(slice_data, coord)
675   -
  749 +
676 750 if (self.left_pressed):
677 751 cursor = slice_data.cursor
678 752 position = slice_data.actor.GetInput().FindPoint(coord)
... ... @@ -680,7 +754,7 @@ class EditorInteractorStyle(DefaultInteractorStyle):
680 754  
681 755 if position < 0:
682 756 position = viewer.calculate_matrix_position(coord)
683   -
  757 +
684 758 viewer.slice_.edit_mask_pixel(operation, cursor.GetPixels(),
685 759 position, radius, self.orientation)
686 760 # TODO: To create a new function to reload images to viewer.
... ... @@ -698,32 +772,35 @@ class EditorInteractorStyle(DefaultInteractorStyle):
698 772  
699 773 def EOnScrollForward(self, evt, obj):
700 774 iren = self.viewer.interactor
  775 + viewer = self.viewer
701 776 if iren.GetControlKey():
702 777 mouse_x, mouse_y = iren.GetEventPosition()
703 778 render = iren.FindPokedRenderer(mouse_x, mouse_y)
704 779 slice_data = self.viewer.get_slice_data(render)
705 780 cursor = slice_data.cursor
706 781 size = cursor.radius * 2
  782 + size += 1
707 783  
708   - if size < 100:
709   - Publisher.sendMessage('Set edition brush size', size + 1)
  784 + if size <= 100:
  785 + Publisher.sendMessage('Set edition brush size', size)
710 786 cursor.SetPosition(cursor.position)
711 787 self.viewer.interactor.Render()
712   -
713 788 else:
714 789 self.OnScrollForward(obj, evt)
715 790  
716 791 def EOnScrollBackward(self, evt, obj):
717 792 iren = self.viewer.interactor
  793 + viewer = self.viewer
718 794 if iren.GetControlKey():
719 795 mouse_x, mouse_y = iren.GetEventPosition()
720 796 render = iren.FindPokedRenderer(mouse_x, mouse_y)
721 797 slice_data = self.viewer.get_slice_data(render)
722 798 cursor = slice_data.cursor
723 799 size = cursor.radius * 2
  800 + size -= 1
724 801  
725 802 if size > 0:
726   - Publisher.sendMessage('Set edition brush size', size - 1)
  803 + Publisher.sendMessage('Set edition brush size', size)
727 804 cursor.SetPosition(cursor.position)
728 805 self.viewer.interactor.Render()
729 806 else:
... ... @@ -742,41 +819,29 @@ class EditorInteractorStyle(DefaultInteractorStyle):
742 819 return x, y, z
743 820  
744 821  
745   -class WatershedProgressWindow(wx.Frame):
746   - def __init__(self, process, parent=None):
747   - wx.Frame.__init__(self, parent, -1)
  822 +class WatershedProgressWindow(object):
  823 + def __init__(self, process):
748 824 self.process = process
749   - self._build_gui()
750   - self._bind_wx_events()
751   - self.timer = wx.Timer(self)
752   - self.timer.Start(1000)
753   -
754   - def _build_gui(self):
755   - self.gauge = wx.Gauge(self, -1, 100)
756   - self.btn_cancel = wx.Button(self, wx.ID_CANCEL)
757   -
758   - sizer = wx.BoxSizer(wx.VERTICAL)
759   - sizer.Add(wx.StaticText(self, -1, _("Applying watershed")))
760   - sizer.Add(self.gauge, 0, wx.EXPAND)
761   - sizer.Add(self.btn_cancel, 0, wx.ALIGN_LEFT)
762   -
763   - self.SetSizer(sizer)
764   - sizer.Fit(self)
765   - self.Layout()
766   -
767   - def __del__(self):
768   - self.timer.Stop()
769   -
770   - def _bind_wx_events(self):
771   - self.Bind(wx.EVT_TIMER, self.TimeHandler)
772   - self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_cancel)
773   -
774   - def on_cancel(self, evt):
775   - self.timer.Stop()
  825 + self.title = "InVesalius 3"
  826 + self.msg = _("Applying watershed ...")
  827 + self.style = wx.PD_APP_MODAL | wx.PD_APP_MODAL | wx.PD_CAN_ABORT
  828 +
  829 + self.dlg = wx.ProgressDialog(self.title,
  830 + self.msg,
  831 + parent = None,
  832 + style = self.style)
  833 +
  834 + self.dlg.Bind(wx.EVT_BUTTON, self.Cancel)
  835 + self.dlg.Show()
  836 +
  837 + def Cancel(self, evt):
776 838 self.process.terminate()
777 839  
778   - def TimeHandler(self, evt):
779   - self.gauge.Pulse()
  840 + def Update(self):
  841 + self.dlg.Pulse()
  842 +
  843 + def Close(self):
  844 + self.dlg.Destroy()
780 845  
781 846  
782 847 class WatershedConfig(object):
... ... @@ -851,6 +916,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
851 916 Publisher.subscribe(self.set_bformat, 'Set watershed brush format')
852 917  
853 918 self._set_cursor()
  919 + self.viewer.slice_data.cursor.Show(0)
854 920  
855 921 def SetUp(self):
856 922 mask = self.viewer.slice_.current_mask.matrix
... ... @@ -898,7 +964,6 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
898 964 cursor.SetColour(self.viewer._brush_cursor_colour)
899 965 cursor.SetSize(self.config.cursor_size)
900 966 self.viewer.slice_data.SetCursor(cursor)
901   - self.viewer.interactor.Render()
902 967  
903 968 def set_bsize(self, pubsub_evt):
904 969 size = pubsub_evt.data
... ... @@ -913,42 +978,50 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
913 978 if (self.viewer.slice_.buffer_slices[self.orientation].mask is None):
914 979 return
915 980 self.viewer.slice_data.cursor.Show()
916   - #self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK))
  981 + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK))
917 982 self.viewer.interactor.Render()
918   -
  983 +
919 984 def OnLeaveInteractor(self, obj, evt):
920 985 self.viewer.slice_data.cursor.Show(0)
921   - #self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
  986 + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
922 987 self.viewer.interactor.Render()
923 988  
924 989 def WOnScrollBackward(self, obj, evt):
  990 + iren = self.viewer.interactor
925 991 viewer = self.viewer
926   - iren = viewer.interactor
927 992 if iren.GetControlKey():
928   - if viewer.slice_.opacity > 0:
929   - viewer.slice_.opacity -= 0.1
930   - self.viewer.slice_.buffer_slices['AXIAL'].discard_vtk_mask()
931   - self.viewer.slice_.buffer_slices['CORONAL'].discard_vtk_mask()
932   - self.viewer.slice_.buffer_slices['SAGITAL'].discard_vtk_mask()
933   - viewer.OnScrollBar()
  993 + mouse_x, mouse_y = iren.GetEventPosition()
  994 + render = iren.FindPokedRenderer(mouse_x, mouse_y)
  995 + slice_data = self.viewer.get_slice_data(render)
  996 + cursor = slice_data.cursor
  997 + size = cursor.radius * 2
  998 + size -= 1
  999 +
  1000 + if size > 0:
  1001 + Publisher.sendMessage('Set watershed brush size', size)
  1002 + cursor.SetPosition(cursor.position)
  1003 + self.viewer.interactor.Render()
934 1004 else:
935 1005 self.OnScrollBackward(obj, evt)
936 1006  
937   -
938 1007 def WOnScrollForward(self, obj, evt):
  1008 + iren = self.viewer.interactor
939 1009 viewer = self.viewer
940   - iren = viewer.interactor
941 1010 if iren.GetControlKey():
942   - if viewer.slice_.opacity < 1:
943   - viewer.slice_.opacity += 0.1
944   - self.viewer.slice_.buffer_slices['AXIAL'].discard_vtk_mask()
945   - self.viewer.slice_.buffer_slices['CORONAL'].discard_vtk_mask()
946   - self.viewer.slice_.buffer_slices['SAGITAL'].discard_vtk_mask()
947   - viewer.OnScrollBar()
  1011 + mouse_x, mouse_y = iren.GetEventPosition()
  1012 + render = iren.FindPokedRenderer(mouse_x, mouse_y)
  1013 + slice_data = self.viewer.get_slice_data(render)
  1014 + cursor = slice_data.cursor
  1015 + size = cursor.radius * 2
  1016 + size += 1
  1017 +
  1018 + if size <= 100:
  1019 + Publisher.sendMessage('Set watershed brush size', size)
  1020 + cursor.SetPosition(cursor.position)
  1021 + self.viewer.interactor.Render()
948 1022 else:
949 1023 self.OnScrollForward(obj, evt)
950 1024  
951   -
952 1025 def OnBrushClick(self, obj, evt):
953 1026 if (self.viewer.slice_.buffer_slices[self.orientation].mask is None):
954 1027 return
... ... @@ -957,7 +1030,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
957 1030 iren = viewer.interactor
958 1031  
959 1032 viewer._set_editor_cursor_visibility(1)
960   -
  1033 +
961 1034 mouse_x, mouse_y = iren.GetEventPosition()
962 1035 render = iren.FindPokedRenderer(mouse_x, mouse_y)
963 1036 slice_data = viewer.get_slice_data(render)
... ... @@ -968,10 +1041,10 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
968 1041 slice_data.cursor.Show()
969 1042  
970 1043 self.picker.Pick(mouse_x, mouse_y, 0, render)
971   -
  1044 +
972 1045 coord = self.get_coordinate_cursor()
973 1046 position = slice_data.actor.GetInput().FindPoint(coord)
974   -
  1047 +
975 1048 if position != -1:
976 1049 coord = slice_data.actor.GetInput().GetPoint(position)
977 1050  
... ... @@ -1017,7 +1090,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
1017 1090 iren = viewer.interactor
1018 1091  
1019 1092 viewer._set_editor_cursor_visibility(1)
1020   -
  1093 +
1021 1094 mouse_x, mouse_y = iren.GetEventPosition()
1022 1095 render = iren.FindPokedRenderer(mouse_x, mouse_y)
1023 1096 slice_data = viewer.get_slice_data(render)
... ... @@ -1027,12 +1100,12 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
1027 1100 #i.cursor.Show(0)
1028 1101  
1029 1102 self.picker.Pick(mouse_x, mouse_y, 0, render)
1030   -
  1103 +
1031 1104 #if (self.pick.GetViewProp()):
1032 1105 #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK))
1033 1106 #else:
1034 1107 #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
1035   -
  1108 +
1036 1109 coord = self.get_coordinate_cursor()
1037 1110 position = viewer.slice_data.actor.GetInput().FindPoint(coord)
1038 1111  
... ... @@ -1043,7 +1116,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
1043 1116 coord = slice_data.actor.GetInput().GetPoint(position)
1044 1117 slice_data.cursor.SetPosition(coord)
1045 1118 #self.__update_cursor_position(slice_data, coord)
1046   -
  1119 +
1047 1120 if (self.left_pressed):
1048 1121 cursor = slice_data.cursor
1049 1122 position = slice_data.actor.GetInput().FindPoint(coord)
... ... @@ -1104,7 +1177,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
1104 1177  
1105 1178 ww = self.viewer.slice_.window_width
1106 1179 wl = self.viewer.slice_.window_level
1107   -
  1180 +
1108 1181 if BRUSH_BACKGROUND in markers and BRUSH_FOREGROUND in markers:
1109 1182 #w_algorithm = WALGORITHM[self.config.algorithm]
1110 1183 bstruct = generate_binary_structure(2, CON2D[self.config.con_2d])
... ... @@ -1146,9 +1219,7 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
1146 1219  
1147 1220 self.viewer.slice_.current_mask.was_edited = True
1148 1221 self.viewer.slice_.current_mask.clear_history()
1149   - Publisher.sendMessage('Reload actual slice')
1150   - else:
1151   - self.viewer.OnScrollBar(update3D=False)
  1222 + Publisher.sendMessage('Reload actual slice')
1152 1223  
1153 1224 def get_coordinate_cursor(self):
1154 1225 # Find position
... ... @@ -1253,18 +1324,14 @@ class WaterShedInteractorStyle(DefaultInteractorStyle):
1253 1324 self.config.use_ww_wl, wl, ww, q))
1254 1325  
1255 1326 wp = WatershedProgressWindow(p)
1256   - wp.Center(wx.BOTH)
1257   - wp.Show()
1258   - wp.MakeModal()
1259   -
1260 1327 p.start()
1261 1328  
1262 1329 while q.empty() and p.is_alive():
1263 1330 time.sleep(0.5)
  1331 + wp.Update()
1264 1332 wx.Yield()
1265 1333  
1266   - wp.MakeModal(False)
1267   - wp.Destroy()
  1334 + wp.Close()
1268 1335 del wp
1269 1336  
1270 1337 if q.empty():
... ...
invesalius/data/surface.py
... ... @@ -84,7 +84,7 @@ class Surface():
84 84 #plist_filepath = os.path.join(dir_temp, filename + '.plist')
85 85 temp_plist = tempfile.mktemp()
86 86 plistlib.writePlist(surface, temp_plist)
87   -
  87 +
88 88 filelist[temp_plist] = plist_filename
89 89  
90 90 return plist_filename
... ... @@ -145,7 +145,7 @@ class SurfaceManager():
145 145 Publisher.subscribe(self.OnDuplicate, "Duplicate surfaces")
146 146 Publisher.subscribe(self.OnRemove,"Remove surfaces")
147 147 Publisher.subscribe(self.UpdateSurfaceInterpolation, 'Update Surface Interpolation')
148   -
  148 +
149 149 def OnDuplicate(self, pubsub_evt):
150 150 selected_items = pubsub_evt.data
151 151 proj = prj.Project()
... ... @@ -155,7 +155,7 @@ class SurfaceManager():
155 155 # compute copy name
156 156 name = original_surface.name
157 157 names_list = [surface_dict[i].name for i in surface_dict.keys()]
158   - new_name = utl.next_copy_name(name, names_list)
  158 + new_name = utl.next_copy_name(name, names_list)
159 159 # create new mask
160 160 self.CreateSurfaceFromPolydata(polydata = original_surface.polydata,
161 161 overwrite = False,
... ... @@ -221,7 +221,7 @@ class SurfaceManager():
221 221 index_list.append(index)
222 222 #self.ShowActor(index, True)
223 223  
224   - Publisher.sendMessage('Show multiple surfaces', (index_list, True))
  224 + Publisher.sendMessage('Show multiple surfaces', (index_list, True))
225 225  
226 226 def OnLargestSurface(self, pubsub_evt):
227 227 """
... ... @@ -329,7 +329,8 @@ class SurfaceManager():
329 329 surface.colour, surface.volume,
330 330 surface.transparency))
331 331 self.last_surface_index = index
332   - self.ShowActor(index, True)
  332 + if surface.is_shown:
  333 + self.ShowActor(index, True)
333 334  
334 335 def OnLoadSurfaceDict(self, pubsub_evt):
335 336 surface_dict = pubsub_evt.data
... ... @@ -388,10 +389,10 @@ class SurfaceManager():
388 389 matrix = slice_.matrix
389 390 filename_img = slice_.matrix_filename
390 391 spacing = slice_.spacing
391   -
  392 +
392 393 algorithm = surface_parameters['method']['algorithm']
393 394 options = surface_parameters['method']['options']
394   -
  395 +
395 396 surface_name = surface_parameters['options']['name']
396 397 quality = surface_parameters['options']['quality']
397 398 fill_holes = surface_parameters['options']['fill']
... ... @@ -425,7 +426,7 @@ class SurfaceManager():
425 426 pipeline_size += 1
426 427 if keep_largest:
427 428 pipeline_size += 1
428   -
  429 +
429 430 ## Update progress value in GUI
430 431 UpdateProgress = vu.ShowProgress(pipeline_size)
431 432 UpdateProgress(0, _("Creating 3D surface..."))
... ... @@ -438,7 +439,7 @@ class SurfaceManager():
438 439 flip_image = True
439 440  
440 441 n_processors = multiprocessing.cpu_count()
441   -
  442 +
442 443 pipe_in, pipe_out = multiprocessing.Pipe()
443 444 o_piece = 1
444 445 piece_size = 2000
... ... @@ -455,7 +456,7 @@ class SurfaceManager():
455 456 mask.temp_file,
456 457 mask.matrix.shape,
457 458 mask.matrix.dtype,
458   - spacing,
  459 + spacing,
459 460 mode, min_value, max_value,
460 461 decimate_reduction,
461 462 smooth_relaxation_factor,
... ... @@ -642,7 +643,7 @@ class SurfaceManager():
642 643 polydata.SetSource(None)
643 644 polydata.DebugOn()
644 645 del filled_polydata
645   -
  646 +
646 647 normals = vtk.vtkPolyDataNormals()
647 648 normals.ReleaseDataFlagOn()
648 649 normals_ref = weakref.ref(normals)
... ... @@ -741,19 +742,19 @@ class SurfaceManager():
741 742 (surface.index, surface.name,
742 743 surface.colour, surface.volume,
743 744 surface.transparency))
744   -
  745 +
745 746 #When you finalize the progress. The bar is cleaned.
746 747 UpdateProgress = vu.ShowProgress(1)
747 748 UpdateProgress(0, _("Ready"))
748 749 Publisher.sendMessage('Update status text in GUI', _("Ready"))
749   -
  750 +
750 751 Publisher.sendMessage('End busy cursor')
751 752 del actor
752 753  
753 754 def UpdateSurfaceInterpolation(self, pub_evt):
754 755 interpolation = int(ses.Session().surface_interpolation)
755 756 key_actors = self.actors_dict.keys()
756   -
  757 +
757 758 for key in self.actors_dict:
758 759 self.actors_dict[key].GetProperty().SetInterpolation(interpolation)
759 760 Publisher.sendMessage('Render volume viewer')
... ...
invesalius/data/viewer_slice.py
... ... @@ -62,6 +62,7 @@ class ContourMIPConfig(wx.Panel):
62 62 wx.Panel.__init__(self, prnt)
63 63 self.mip_size_spin = wx.SpinCtrl(self, -1, min=1, max=240,
64 64 initial=const.PROJECTION_MIP_SIZE)
  65 + self.mip_size_spin.SetValue(const.PROJECTION_MIP_SIZE)
65 66 self.mip_size_spin.SetToolTip(wx.ToolTip(_("Number of slices used to compound the visualization.")))
66 67 w, h = self.mip_size_spin.GetTextExtent('M')
67 68 self.mip_size_spin.SetMinSize((5 * w + 10, -1))
... ... @@ -525,12 +526,6 @@ class Viewer(wx.Panel):
525 526 ren.GetActiveCamera().Zoom(1.0)
526 527 self.interactor.Render()
527 528  
528   - def ChangeBrushSize(self, pubsub_evt):
529   - size = pubsub_evt.data
530   - self._brush_cursor_size = size
531   - #for slice_data in self.slice_data_list:
532   - self.slice_data.cursor.SetSize(size)
533   -
534 529 def ChangeBrushColour(self, pubsub_evt):
535 530 vtk_colour = pubsub_evt.data[3]
536 531 self._brush_cursor_colour = vtk_colour
... ... @@ -545,27 +540,6 @@ class Viewer(wx.Panel):
545 540 if self.slice_data.cursor:
546 541 self.slice_data.cursor.SetColour(colour_vtk)
547 542  
548   - def ChangeBrushActor(self, pubsub_evt):
549   - brush_type = pubsub_evt.data
550   - slice_data = self.slice_data
551   - self._brush_cursor_type = brush_type
552   -
553   - if brush_type == const.BRUSH_SQUARE:
554   - cursor = ca.CursorRectangle()
555   - elif brush_type == const.BRUSH_CIRCLE:
556   - cursor = ca.CursorCircle()
557   -
558   - cursor.SetOrientation(self.orientation)
559   - coordinates = {"SAGITAL": [slice_data.number, 0, 0],
560   - "CORONAL": [0, slice_data.number, 0],
561   - "AXIAL": [0, 0, slice_data.number]}
562   - cursor.SetPosition(coordinates[self.orientation])
563   - cursor.SetSpacing(self.slice_.spacing)
564   - cursor.SetColour(self._brush_cursor_colour)
565   - cursor.SetSize(self._brush_cursor_size)
566   - slice_data.SetCursor(cursor)
567   - self.interactor.Render()
568   -
569 543 def Navigation(self, pubsub_evt):
570 544 # Get point from base change
571 545 x, y, z = pubsub_evt.data
... ... @@ -700,14 +674,8 @@ class Viewer(wx.Panel):
700 674 Publisher.subscribe(self.Navigation,
701 675 'Co-registered Points')
702 676 ###
703   - Publisher.subscribe(self.ChangeBrushSize,
704   - 'Set edition brush size')
705 677 Publisher.subscribe(self.ChangeBrushColour,
706 678 'Add mask')
707   - Publisher.subscribe(self.ChangeBrushActor,
708   - 'Set brush format')
709   - Publisher.subscribe(self.ChangeBrushOperation,
710   - 'Set edition operation')
711 679  
712 680 Publisher.subscribe(self.UpdateWindowLevelValue,
713 681 'Update window level value')
... ... @@ -833,9 +801,6 @@ class Viewer(wx.Panel):
833 801 if (state != const.SLICE_STATE_EDITOR):
834 802 Publisher.sendMessage('Set interactor default cursor')
835 803  
836   - def ChangeBrushOperation(self, pubsub_evt):
837   - self._brush_cursor_op = pubsub_evt.data
838   -
839 804 def __bind_events_wx(self):
840 805 self.scroll.Bind(wx.EVT_SCROLL, self.OnScrollBar)
841 806 self.scroll.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnScrollBarRelease)
... ...
invesalius/gui/default_tasks.py
... ... @@ -230,10 +230,6 @@ class UpperTaskPanel(wx.Panel):
230 230 wx.DefaultSize,FPB_DEFAULT_STYLE,
231 231 fpb.FPB_SINGLE_FOLD)
232 232  
233   - sizer = wx.BoxSizer(wx.VERTICAL)
234   - sizer.Add(fold_panel, 1, wx.GROW|wx.EXPAND)
235   - self.SetSizer(sizer)
236   -
237 233 image_list = wx.ImageList(16,16)
238 234 image_list.Add(GetExpandedIconBitmap())
239 235 image_list.Add(GetCollapsedIconBitmap())
... ... @@ -288,6 +284,10 @@ class UpperTaskPanel(wx.Panel):
288 284 fold_panel.Expand(fold_panel.GetFoldPanel(0))
289 285 self.fold_panel = fold_panel
290 286  
  287 + sizer = wx.BoxSizer(wx.VERTICAL)
  288 + sizer.Add(fold_panel, 1, wx.GROW|wx.EXPAND)
  289 + self.SetSizerAndFit(sizer)
  290 +
291 291 self.SetStateProjectClose()
292 292 self.__bind_events()
293 293  
... ...
invesalius/gui/dialogs.py
... ... @@ -104,7 +104,7 @@ class ResizeImageDialog(wx.Dialog):
104 104 pos=wx.DefaultPosition,
105 105 style=wx.DEFAULT_DIALOG_STYLE)
106 106 self.PostCreate(pre)
107   -
  107 +
108 108 lbl_message = wx.StaticText(self, -1, _("InVesalius is running on a 32-bit operating system or has insufficient memory. \nIf you want to work with 3D surfaces or volume rendering, \nit is recommended to reduce the medical images resolution."))
109 109 icon = wx.ArtProvider.GetBitmap(wx.ART_WARNING, wx.ART_MESSAGE_BOX, (32,32))
110 110 bmp = wx.StaticBitmap(self, -1, icon)
... ... @@ -143,7 +143,7 @@ class ResizeImageDialog(wx.Dialog):
143 143 sizer_general.Fit(self)
144 144 self.Layout()
145 145 self.Centre()
146   -
  146 +
147 147 def SetValue(self, value):
148 148 self.num_ctrl_porcent.SetValue(value)
149 149  
... ... @@ -611,7 +611,7 @@ class NewMask(wx.Dialog):
611 611 thresh_min, thresh_max = project.threshold_modes[_("Bone")]
612 612 original_colour = random.choice(const.MASK_COLOUR)
613 613 self.colour = original_colour
614   - colour = [255*i for i in original_colour]
  614 + colour = [255*i for i in original_colour]
615 615 colour.append(100)
616 616 gradient = grad.GradientSlider(self, -1, int(bound_min),
617 617 int(bound_max),
... ... @@ -673,7 +673,7 @@ class NewMask(wx.Dialog):
673 673 proj = prj.Project()
674 674 if thresh in proj.threshold_modes.values():
675 675 preset_name = proj.threshold_modes.get_key(thresh)[0]
676   - index = self.thresh_list.index(preset_name)
  676 + index = self.thresh_list.index(preset_name)
677 677 self.combo_thresh.SetSelection(index)
678 678 else:
679 679 index = self.thresh_list.index(_("Custom"))
... ... @@ -790,14 +790,14 @@ def ShowAboutDialog(parent):
790 790 "Dimitris Glezos",
791 791 "Eugene Liscio",
792 792 u"Frédéric Lopez",
793   - "Javier de Lima Moreno"
  793 + "Javier de Lima Moreno",
794 794 "Nikos Korkakakis",
795 795 "Massimo Crisantemo",
796 796 "Raul Bolliger Neto",
797 797 "Sebastian Hilbert",
798 798 "Semarang Pari"]
799 799  
800   - #info.DocWriters = ["Fabio Francisco da Silva (PT)"]
  800 + #info.DocWriters = ["Fabio Francisco da Silva (PT)"]
801 801  
802 802 info.Artists = ["Otavio Henrique Junqueira Amorim"]
803 803  
... ... @@ -1101,7 +1101,7 @@ class SurfaceCreationOptionsPanel(wx.Panel):
1101 1101 import constants as const
1102 1102 import data.surface as surface
1103 1103 import project as prj
1104   -
  1104 +
1105 1105 wx.Panel.__init__(self, parent, ID)
1106 1106  
1107 1107 # LINE 1: Surface name
... ... @@ -1151,7 +1151,7 @@ class SurfaceCreationOptionsPanel(wx.Panel):
1151 1151 flag_link = wx.EXPAND|wx.GROW|wx.ALL
1152 1152 flag_button = wx.ALL | wx.EXPAND| wx.GROW
1153 1153  
1154   - fixed_sizer = wx.FlexGridSizer(rows=2, cols=2, hgap=10, vgap=5)
  1154 + fixed_sizer = wx.FlexGridSizer(rows=3, cols=2, hgap=10, vgap=5)
1155 1155 fixed_sizer.AddGrowableCol(0, 1)
1156 1156 fixed_sizer.AddMany([ (label_surface, 1, flag_link, 0),
1157 1157 (text, 1, flag_button, 0),
... ... @@ -1208,7 +1208,7 @@ class CAOptions(wx.Panel):
1208 1208 def __init__(self, parent):
1209 1209 wx.Panel.__init__(self, parent, -1)
1210 1210 self._build_widgets()
1211   -
  1211 +
1212 1212 def _build_widgets(self):
1213 1213 sb = wx.StaticBox(self, -1, _('Options'))
1214 1214 self.angle = floatspin.FloatSpin(self, -1, value=0.7, min_val=0.0,
... ... @@ -1222,7 +1222,7 @@ class CAOptions(wx.Panel):
1222 1222 self.min_weight = floatspin.FloatSpin(self, -1, value=0.2, min_val=0.0,
1223 1223 max_val=1.0, increment=0.1,
1224 1224 digits=1)
1225   -
  1225 +
1226 1226 self.steps = wx.SpinCtrl(self, -1, value='10', min=1, max=100)
1227 1227  
1228 1228 layout_sizer = wx.FlexGridSizer(rows=4, cols=2, hgap=5, vgap=5)
... ... @@ -1285,7 +1285,7 @@ class SurfaceMethodPanel(wx.Panel):
1285 1285 self.SetSizer(self.main_sizer)
1286 1286 self.Layout()
1287 1287 self.Fit()
1288   -
  1288 +
1289 1289 if self.mask_edited:
1290 1290 self.cb_types.SetValue(_(u'Context aware smoothing'))
1291 1291 self.ca_options.Enable()
... ... @@ -1324,9 +1324,9 @@ class SurfaceMethodPanel(wx.Panel):
1324 1324 algorithm = self.GetAlgorithmSelected()
1325 1325 options = self.GetOptions()
1326 1326  
1327   - return {"algorithm": algorithm,
  1327 + return {"algorithm": algorithm,
1328 1328 "options": options}
1329   -
  1329 +
1330 1330 def ReloadMethodsOptions(self):
1331 1331 self.cb_types.Clear()
1332 1332 self.cb_types.AppendItems([i for i in sorted(self.alg_types)
... ... @@ -1388,32 +1388,37 @@ class ClutImagedataDialog(wx.Dialog):
1388 1388 if gen_evt:
1389 1389 self.clut_widget._generate_event()
1390 1390  
1391   -
1392   -class WatershedOptions(wx.Panel):
1393   - def __init__(self, parent):
  1391 +
  1392 +class WatershedOptionsPanel(wx.Panel):
  1393 + def __init__(self, parent, config):
1394 1394 wx.Panel.__init__(self, parent)
1395   -
  1395 +
1396 1396 self.algorithms = ("Watershed", "Watershed IFT")
1397 1397 self.con2d_choices = (4, 8)
1398 1398 self.con3d_choices = (6, 18, 26)
1399 1399  
  1400 + self.config = config
  1401 +
1400 1402 self._init_gui()
1401   - self._bind_events()
1402 1403  
1403 1404 def _init_gui(self):
1404 1405 self.choice_algorithm = wx.RadioBox(self, -1, "Algorithm",
1405   - choices=("Watershed", "Watershed IFT"),
  1406 + choices=self.algorithms,
1406 1407 style=wx.NO_BORDER | wx.HORIZONTAL)
  1408 + self.choice_algorithm.SetSelection(self.algorithms.index(self.config.algorithm))
1407 1409  
1408 1410 self.choice_2dcon = wx.RadioBox(self, -1, "2D",
1409 1411 choices=[str(i) for i in self.con2d_choices],
1410 1412 style=wx.NO_BORDER | wx.HORIZONTAL)
  1413 + self.choice_2dcon.SetSelection(self.con2d_choices.index(self.config.con_2d))
1411 1414  
1412 1415 self.choice_3dcon = wx.RadioBox(self, -1, "3D",
1413 1416 choices=[str(i) for i in self.con3d_choices],
1414 1417 style=wx.NO_BORDER | wx.HORIZONTAL)
  1418 + self.choice_3dcon.SetSelection(self.con3d_choices.index(self.config.con_3d))
1415 1419  
1416 1420 self.gaussian_size = wx.SpinCtrl(self, -1, "", min=1, max=10)
  1421 + self.gaussian_size.SetValue(self.config.mg_size)
1417 1422  
1418 1423 box_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Conectivity"), wx.VERTICAL)
1419 1424 box_sizer.Add(self.choice_2dcon, 0, wx.ALIGN_CENTER_VERTICAL,2)
... ... @@ -1432,47 +1437,52 @@ class WatershedOptions(wx.Panel):
1432 1437 sizer.Fit(self)
1433 1438 self.Layout()
1434 1439  
1435   - def _bind_events(self):
1436   - self.choice_algorithm.Bind(wx.EVT_RADIOBOX, self.OnSetAlgorithm)
1437   - self.gaussian_size.Bind(wx.EVT_SPINCTRL, self.OnSetGaussianSize)
1438   - self.choice_2dcon.Bind(wx.EVT_RADIOBOX, self.OnSetCon2D)
1439   - self.choice_3dcon.Bind(wx.EVT_RADIOBOX, self.OnSetCon3D)
1440   -
1441   - def OnSetAlgorithm(self, evt):
1442   - v = self.algorithms[evt.GetInt()]
1443   - Publisher.sendMessage("Set watershed algorithm", v)
1444   -
1445   - def OnSetGaussianSize(self, evt):
1446   - v = self.gaussian_size.GetValue()
1447   - Publisher.sendMessage("Set watershed gaussian size", v)
1448   -
1449   - def OnSetCon2D(self, evt):
1450   - v = self.con2d_choices[evt.GetInt()]
1451   - Publisher.sendMessage("Set watershed 2d con", v)
1452   -
1453   - def OnSetCon3D(self, evt):
1454   - v = self.con3d_choices[evt.GetInt()]
1455   - Publisher.sendMessage("Set watershed 3d con", v)
  1440 + def apply_options(self):
  1441 + self.config.algorithm = self.algorithms[self.choice_algorithm.GetSelection()]
  1442 + self.config.con_2d = self.con2d_choices[self.choice_2dcon.GetSelection()]
  1443 + self.config.con_3d = self.con3d_choices[self.choice_3dcon.GetSelection()]
  1444 + self.config.mg_size = self.gaussian_size.GetValue()
1456 1445  
1457 1446  
1458 1447 class WatershedOptionsDialog(wx.Dialog):
1459   - def __init__(self):
  1448 + def __init__(self, config):
1460 1449 pre = wx.PreDialog()
1461 1450 pre.Create(wx.GetApp().GetTopWindow(), -1, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT)
1462 1451 self.PostCreate(pre)
1463 1452  
  1453 + self.config = config
  1454 +
1464 1455 self._init_gui()
1465 1456  
1466 1457 def _init_gui(self):
1467   - wop = WatershedOptions(self)
  1458 + wop = WatershedOptionsPanel(self, self.config)
  1459 + self.wop = wop
  1460 +
  1461 + sizer = wx.BoxSizer(wx.VERTICAL)
  1462 +
  1463 + btn_ok = wx.Button(self, wx.ID_OK)
  1464 + btn_ok.SetDefault()
  1465 +
  1466 + btn_cancel = wx.Button(self, wx.ID_CANCEL)
  1467 +
  1468 + btnsizer = wx.StdDialogButtonSizer()
  1469 + btnsizer.AddButton(btn_ok)
  1470 + btnsizer.AddButton(btn_cancel)
  1471 + btnsizer.Realize()
1468 1472  
1469   - sizer = wx.BoxSizer(wx.VERTICAL)
1470 1473 sizer.Add(wop, 0, wx.EXPAND)
  1474 + sizer.Add(btnsizer, 0, wx.EXPAND)
  1475 + sizer.AddSpacer(5)
1471 1476  
1472 1477 self.SetSizer(sizer)
1473 1478 sizer.Fit(self)
1474 1479 self.Layout()
1475 1480  
  1481 + btn_ok.Bind(wx.EVT_BUTTON, self.OnOk)
  1482 +
  1483 + def OnOk(self, evt):
  1484 + self.wop.apply_options()
  1485 + evt.Skip()
1476 1486  
1477 1487 class MaskBooleanDialog(wx.Dialog):
1478 1488 def __init__(self, masks):
... ... @@ -1500,7 +1510,7 @@ class MaskBooleanDialog(wx.Dialog):
1500 1510  
1501 1511 icon_folder = '../icons/'
1502 1512 op_choices = ((_(u"Union"), const.BOOLEAN_UNION, 'bool_union.png'),
1503   - (_(u"Difference"), const.BOOLEAN_DIFF, 'bool_difference.png'),
  1513 + (_(u"Difference"), const.BOOLEAN_DIFF, 'bool_difference.png'),
1504 1514 (_(u"Intersection"), const.BOOLEAN_AND, 'bool_intersection.png'),
1505 1515 (_(u"Exclusive disjunction"), const.BOOLEAN_XOR, 'bool_disjunction.png'))
1506 1516 self.op_boolean = wx.combo.BitmapComboBox(self, -1, op_choices[0][0], choices=[])
... ...
invesalius/gui/frame.py
... ... @@ -51,7 +51,7 @@ class MessageWatershed(wx.PopupWindow):
51 51 def __init__(self, prnt, msg):
52 52 wx.PopupWindow.__init__(self, prnt, -1)
53 53 self.txt = wx.StaticText(self, -1, msg)
54   -
  54 +
55 55 self.sizer = wx.BoxSizer(wx.HORIZONTAL)
56 56 self.sizer.Add(self.txt, 1, wx.EXPAND)
57 57 self.SetSizer(self.sizer)
... ... @@ -81,10 +81,10 @@ class Frame(wx.Frame):
81 81 self.SetIcon(wx.Icon(icon_path, wx.BITMAP_TYPE_ICO))
82 82  
83 83 self.mw = None
84   -
  84 +
85 85 if sys.platform != 'darwin':
86 86 self.Maximize()
87   -
  87 +
88 88 self.sizeChanged = True
89 89 #Necessary update AUI (statusBar in special)
90 90 #when maximized in the Win 7 and XP
... ... @@ -381,12 +381,12 @@ class Frame(wx.Frame):
381 381 elif id == const.ID_START:
382 382 self.ShowGettingStarted()
383 383 elif id == const.ID_PREFERENCES:
384   - self.ShowPreferences()
  384 + self.ShowPreferences()
385 385 elif id == const.ID_DICOM_NETWORK:
386   - self.ShowRetrieveDicomPanel()
  386 + self.ShowRetrieveDicomPanel()
387 387 elif id in (const.ID_FLIP_X, const.ID_FLIP_Y, const.ID_FLIP_Z):
388   - axis = {const.ID_FLIP_X: 2,
389   - const.ID_FLIP_Y: 1,
  388 + axis = {const.ID_FLIP_X: 2,
  389 + const.ID_FLIP_Y: 1,
390 390 const.ID_FLIP_Z: 0}[id]
391 391 self.FlipVolume(axis)
392 392 elif id in (const.ID_SWAP_XY, const.ID_SWAP_XZ, const.ID_SWAP_YZ):
... ... @@ -401,6 +401,8 @@ class Frame(wx.Frame):
401 401  
402 402 elif id == const.ID_BOOLEAN_MASK:
403 403 self.OnMaskBoolean()
  404 + elif id == const.ID_CLEAN_MASK:
  405 + self.OnCleanMask()
404 406  
405 407 def OnSize(self, evt):
406 408 """
... ... @@ -429,10 +431,10 @@ class Frame(wx.Frame):
429 431 if self.preferences.ShowModal() == wx.ID_OK:
430 432 values = self.preferences.GetPreferences()
431 433 self.preferences.Close()
432   -
  434 +
433 435 ses.Session().rendering = values[const.RENDERING]
434 436 ses.Session().surface_interpolation = values[const.SURFACE_INTERPOLATION]
435   - ses.Session().language = values[const.LANGUAGE]
  437 + ses.Session().language = values[const.LANGUAGE]
436 438  
437 439 Publisher.sendMessage('Remove Volume')
438 440 Publisher.sendMessage('Reset Reaycasting')
... ... @@ -479,7 +481,7 @@ class Frame(wx.Frame):
479 481 Show save as dialog.
480 482 """
481 483 Publisher.sendMessage('Show save dialog', True)
482   -
  484 +
483 485 def ShowAnalyzeImporter(self):
484 486 """
485 487 Show save as dialog.
... ... @@ -488,12 +490,12 @@ class Frame(wx.Frame):
488 490  
489 491 def FlipVolume(self, axis):
490 492 Publisher.sendMessage('Flip volume', axis)
491   - Publisher.sendMessage('Reload actual slice')
  493 + Publisher.sendMessage('Reload actual slice')
492 494  
493 495 def SwapAxes(self, axes):
494 496 Publisher.sendMessage('Swap volume axes', axes)
495 497 Publisher.sendMessage('Update scroll')
496   - Publisher.sendMessage('Reload actual slice')
  498 + Publisher.sendMessage('Reload actual slice')
497 499  
498 500 def OnUndo(self):
499 501 print "Undo"
... ... @@ -503,11 +505,13 @@ class Frame(wx.Frame):
503 505 print "Redo"
504 506 Publisher.sendMessage('Redo edition')
505 507  
506   -
507 508 def OnMaskBoolean(self):
508 509 print "Mask boolean"
509 510 Publisher.sendMessage('Show boolean dialog')
510   -
  511 +
  512 + def OnCleanMask(self):
  513 + Publisher.sendMessage('Clean current mask')
  514 + Publisher.sendMessage('Reload actual slice')
511 515  
512 516 # ------------------------------------------------------------------
513 517 # ------------------------------------------------------------------
... ... @@ -546,12 +550,18 @@ class MenuBar(wx.MenuBar):
546 550 sub(self.OnEnableUndo, "Enable undo")
547 551 sub(self.OnEnableRedo, "Enable redo")
548 552  
  553 + sub(self.OnAddMask, "Add mask")
  554 + sub(self.OnRemoveMasks, "Remove masks")
  555 + sub(self.OnShowMask, "Show mask")
  556 +
  557 + self.num_masks = 0
  558 +
549 559 def __init_items(self):
550 560 """
551 561 Create all menu and submenus, and add them to self.
552 562 """
553 563 # TODO: This definetely needs improvements... ;)
554   -
  564 +
555 565 #Import Others Files
556 566 others_file_menu = wx.Menu()
557 567 others_file_menu.Append(const.ID_ANALYZE_IMPORT, "Analyze")
... ... @@ -594,7 +604,7 @@ class MenuBar(wx.MenuBar):
594 604 file_edit = wx.Menu()
595 605 #file_edit.AppendMenu(wx.NewId(), _('Flip'), flip_menu)
596 606 #file_edit.AppendMenu(wx.NewId(), _('Swap axes'), swap_axes_menu)
597   -
  607 +
598 608  
599 609 d = const.ICON_DIR
600 610 if not(sys.platform == 'darwin'):
... ... @@ -620,10 +630,18 @@ class MenuBar(wx.MenuBar):
620 630 #app(const.ID_EDIT_LIST, "Show Undo List...")
621 631 #################################################################
622 632  
  633 + # Tool menu
  634 + tools_menu = wx.Menu()
  635 +
623 636 # Mask Menu
624 637 mask_menu = wx.Menu()
625   - mask_menu.Append(const.ID_BOOLEAN_MASK, _(u"Boolean operations"))
626   - file_edit.AppendMenu(-1, _(u"Mask"), mask_menu)
  638 + self.bool_op_menu = mask_menu.Append(const.ID_BOOLEAN_MASK, _(u"Boolean operations"))
  639 + self.bool_op_menu.Enable(False)
  640 +
  641 + self.clean_mask_menu = mask_menu.Append(const.ID_CLEAN_MASK, _(u"Clean Mask\tCtrl+Shift+A"))
  642 + self.clean_mask_menu.Enable(False)
  643 +
  644 + tools_menu.AppendMenu(-1, _(u"Mask"), mask_menu)
627 645  
628 646  
629 647 # VIEW
... ... @@ -666,13 +684,14 @@ class MenuBar(wx.MenuBar):
666 684 help_menu.Append(const.ID_ABOUT, _("About..."))
667 685 #help_menu.Append(107, "Check For Updates Now...")
668 686  
669   - if platform.system() == 'Darwin':
670   - wx.App.SetMacAboutMenuItemId(const.ID_ABOUT)
671   - wx.App.SetMacExitMenuItemId(const.ID_EXIT)
  687 + #if platform.system() == 'Darwin':
  688 + #wx.App.SetMacAboutMenuItemId(const.ID_ABOUT)
  689 + #wx.App.SetMacExitMenuItemId(const.ID_EXIT)
672 690  
673 691 # Add all menus to menubar
674 692 self.Append(file_menu, _("File"))
675 693 self.Append(file_edit, _("Edit"))
  694 + self.Append(tools_menu, _(u"Tools"))
676 695 #self.Append(view_menu, "View")
677 696 #self.Append(tools_menu, "Tools")
678 697 self.Append(options_menu, _("Options"))
... ... @@ -716,6 +735,20 @@ class MenuBar(wx.MenuBar):
716 735 self.FindItemById(wx.ID_REDO).Enable(True)
717 736 else:
718 737 self.FindItemById(wx.ID_REDO).Enable(False)
  738 +
  739 + def OnAddMask(self, pubsub_evt):
  740 + self.num_masks += 1
  741 + self.bool_op_menu.Enable(self.num_masks >= 2)
  742 +
  743 + def OnRemoveMasks(self, pubsub_evt):
  744 + self.num_masks -= len(pubsub_evt.data)
  745 + self.bool_op_menu.Enable(self.num_masks >= 2)
  746 +
  747 + def OnShowMask(self, pubsub_evt):
  748 + index, value = pubsub_evt.data
  749 + self.clean_mask_menu.Enable(value)
  750 +
  751 +
719 752 # ------------------------------------------------------------------
720 753 # ------------------------------------------------------------------
721 754 # ------------------------------------------------------------------
... ...
invesalius/gui/task_slice.py
... ... @@ -682,7 +682,7 @@ class EditionTools(wx.Panel):
682 682 spin_brush_size = wx.SpinCtrl(self, -1, "", size=(width + 20, -1))
683 683 spin_brush_size.SetRange(1,100)
684 684 spin_brush_size.SetValue(const.BRUSH_SIZE)
685   - spin_brush_size.Bind(wx.EVT_TEXT, self.OnBrushSize)
  685 + spin_brush_size.Bind(wx.EVT_SPINCTRL, self.OnBrushSize)
686 686 self.spin = spin_brush_size
687 687  
688 688 combo_brush_op = wx.ComboBox(self, -1, "", size=(15,-1),
... ... @@ -740,6 +740,7 @@ class EditionTools(wx.Panel):
740 740 'Update threshold limits')
741 741 Publisher.subscribe(self.ChangeMaskColour, 'Change mask colour')
742 742 Publisher.subscribe(self.SetGradientColour, 'Add mask')
  743 + Publisher.subscribe(self._set_brush_size, 'Set edition brush size')
743 744  
744 745 def ChangeMaskColour(self, pubsub_evt):
745 746 colour = pubsub_evt.data
... ... @@ -792,6 +793,10 @@ class EditionTools(wx.Panel):
792 793 # Strangelly this is being called twice
793 794 Publisher.sendMessage('Set edition brush size',self.spin.GetValue())
794 795  
  796 + def _set_brush_size(self, pubsub_evt):
  797 + size = pubsub_evt.data
  798 + self.spin.SetValue(size)
  799 +
795 800 def OnComboBrushOp(self, evt):
796 801 brush_op_id = evt.GetSelection()
797 802 Publisher.sendMessage('Set edition operation', brush_op_id)
... ... @@ -837,7 +842,7 @@ class WatershedTool(EditionTools):
837 842 spin_brush_size = wx.SpinCtrl(self, -1, "", size=(width + 20, -1))
838 843 spin_brush_size.SetRange(1,100)
839 844 spin_brush_size.SetValue(const.BRUSH_SIZE)
840   - spin_brush_size.Bind(wx.EVT_TEXT, self.OnBrushSize)
  845 + spin_brush_size.Bind(wx.EVT_SPINCTRL, self.OnBrushSize)
841 846 self.spin = spin_brush_size
842 847  
843 848 combo_brush_op = wx.ComboBox(self, -1, "", size=(15,-1),
... ... @@ -897,6 +902,7 @@ class WatershedTool(EditionTools):
897 902 self.SetAutoLayout(1)
898 903  
899 904 self.__bind_events_wx()
  905 + self.__bind_pubsub_evt()
900 906  
901 907  
902 908 def __bind_events_wx(self):
... ... @@ -907,6 +913,9 @@ class WatershedTool(EditionTools):
907 913 self.btn_exp_watershed.Bind(wx.EVT_BUTTON, self.OnExpandWatershed)
908 914 self.btn_wconfig.Bind(wx.EVT_BUTTON, self.OnConfig)
909 915  
  916 + def __bind_pubsub_evt(self):
  917 + Publisher.subscribe(self._set_brush_size, 'Set watershed brush size')
  918 +
910 919 def ChangeMaskColour(self, pubsub_evt):
911 920 colour = pubsub_evt.data
912 921 self.gradient_thresh.SetColour(colour)
... ... @@ -951,6 +960,10 @@ class WatershedTool(EditionTools):
951 960 # Strangelly this is being called twice
952 961 Publisher.sendMessage('Set watershed brush size',self.spin.GetValue())
953 962  
  963 + def _set_brush_size(self, pubsub_evt):
  964 + size = pubsub_evt.data
  965 + self.spin.SetValue(size)
  966 +
954 967 def OnComboBrushOp(self, evt):
955 968 brush_op = self.combo_brush_op.GetValue()
956 969 Publisher.sendMessage('Set watershed operation', brush_op)
... ... @@ -964,7 +977,9 @@ class WatershedTool(EditionTools):
964 977 Publisher.sendMessage('Set use ww wl', value)
965 978  
966 979 def OnConfig(self, evt):
967   - dlg.WatershedOptionsDialog().Show()
  980 + from data.styles import WatershedConfig
  981 + config = WatershedConfig()
  982 + dlg.WatershedOptionsDialog(config).Show()
968 983  
969 984 def OnExpandWatershed(self, evt):
970 985 Publisher.sendMessage('Expand watershed to 3D AXIAL')
... ...
invesalius/gui/task_surface.py
... ... @@ -400,15 +400,14 @@ class SurfaceProperties(wx.Panel):
400 400 default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR)
401 401 self.SetBackgroundColour(default_colour)
402 402  
403   - self.surface_dict = utl.TwoWaysDictionary()
  403 + self.surface_list = []
404 404  
405 405 ## LINE 1
406 406  
407 407 # Combo related to mask naem
408   - combo_surface_name = wx.ComboBox(self, -1, "", choices=
409   - self.surface_dict.keys() or ["", ],
  408 + combo_surface_name = wx.ComboBox(self, -1,
410 409 style=wx.CB_DROPDOWN|wx.CB_READONLY)
411   - combo_surface_name.SetSelection(0)
  410 + #combo_surface_name.SetSelection(0)
412 411 if sys.platform != 'win32':
413 412 combo_surface_name.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
414 413 combo_surface_name.Bind(wx.EVT_COMBOBOX, self.OnComboName)
... ... @@ -476,20 +475,23 @@ class SurfaceProperties(wx.Panel):
476 475 def OnRemoveSurfaces(self, pubsub_evt):
477 476 list_index = pubsub_evt.data
478 477  
479   - old_dict = self.surface_dict
480   - new_dict = utl.TwoWaysDictionary()
481   - for index in list_index:
482   - self.combo_surface_name.Delete(index)
483   -
484   - for name in old_dict:
485   - if old_dict[name] < index:
486   - new_dict[name] = old_dict[name]
487   - if old_dict[name] > index:
488   - new_dict[name] = old_dict[name] -1
489   - old_dict = new_dict
490   - self.surface_dict = new_dict
491   -
492   -
  478 + old_dict = self.surface_list
  479 + new_dict = []
  480 + i = 0
  481 + for n, (name, index) in enumerate(old_dict):
  482 + if n not in list_index:
  483 + new_dict.append([name, i])
  484 + i+=1
  485 + self.surface_list = new_dict
  486 +
  487 + s = self.combo_surface_name.GetSelection()
  488 + self.combo_surface_name.SetItems([n[0] for n in self.surface_list])
  489 +
  490 + if self.surface_list:
  491 + if s in list_index:
  492 + self.combo_surface_name.SetSelection(0)
  493 + else:
  494 + self.combo_surface_name.SetSelection(s)
493 495  
494 496 def OnCloseProject(self, pubsub_evt):
495 497 self.CloseProject()
... ... @@ -501,32 +503,39 @@ class SurfaceProperties(wx.Panel):
501 503  
502 504 def ChangeSurfaceName(self, pubsub_evt):
503 505 index, name = pubsub_evt.data
504   - old_name = self.surface_dict.get_key(index)
505   - self.surface_dict.remove(old_name)
506   - self.surface_dict[name] = index
  506 + self.surface_list[index][0] = name
507 507 self.combo_surface_name.SetString(index, name)
508   - self.combo_surface_name.Refresh()
509 508  
510 509 def InsertNewSurface(self, pubsub_evt):
511 510 #not_update = len(pubsub_evt.data) == 5
512 511 index = pubsub_evt.data[0]
513 512 name = pubsub_evt.data[1]
514 513 colour = [value*255 for value in pubsub_evt.data[2]]
515   - overwrite = name in self.surface_dict.keys()
516   - #if index not in self.surface_dict.values():
517   - if not overwrite or not self.surface_dict:
518   - self.surface_dict[name] = index
519   - index = self.combo_surface_name.Append(name)
  514 + i = 0
  515 + print name, index, self.surface_list
  516 + try:
  517 + i = self.surface_list.index([name, index])
  518 + overwrite = True
  519 + except ValueError:
  520 + overwrite = False
  521 +
  522 + if overwrite:
  523 + self.surface_list[i] = [name, index]
  524 + else:
  525 + self.surface_list.append([name, index])
  526 + i = len(self.surface_list) - 1
  527 +
  528 + self.combo_surface_name.SetItems([n[0] for n in self.surface_list])
  529 + self.combo_surface_name.SetSelection(i)
520 530 transparency = 100*pubsub_evt.data[4]
521 531 self.button_colour.SetColour(colour)
522 532 self.slider_transparency.SetValue(transparency)
523   - self.combo_surface_name.SetSelection(index)
524 533 Publisher.sendMessage('Update surface data', (index))
525 534  
526 535 def OnComboName(self, evt):
527 536 surface_name = evt.GetString()
528 537 surface_index = evt.GetSelection()
529   - Publisher.sendMessage('Change surface selected', surface_index)
  538 + Publisher.sendMessage('Change surface selected', self.surface_list[surface_index][1])
530 539  
531 540 def OnSelectColour(self, evt):
532 541 colour = [value/255.0 for value in evt.GetValue()]
... ...