Commit c79f0659a38f58644ae9feda327f828b704f2f2c

Authored by Thiago Franco de Moraes
Committed by GitHub
1 parent ad7294a7
Exists in master

Select mask part tool (#50)

* Starting to select part to new mask

* Showing the selected part

* Improvement

* Better create_new_mask method and doc

* Removed the old method to add new mask

* Better method to get the position of the clicked voxel

* Option to deselect part

* Not setting new mask as current in task_slice

* Strip white spaces and better doc in Mask create_mask class

* Showing dialog

* Appending the mask only after the proccess is over

* Better gui
invesalius/constants.py
@@ -483,6 +483,7 @@ ID_CLEAN_MASK = wx.NewId() @@ -483,6 +483,7 @@ ID_CLEAN_MASK = wx.NewId()
483 ID_REORIENT_IMG = wx.NewId() 483 ID_REORIENT_IMG = wx.NewId()
484 ID_FLOODFILL_MASK = wx.NewId() 484 ID_FLOODFILL_MASK = wx.NewId()
485 ID_REMOVE_MASK_PART = wx.NewId() 485 ID_REMOVE_MASK_PART = wx.NewId()
  486 +ID_SELECT_MASK_PART = wx.NewId()
486 487
487 #--------------------------------------------------------- 488 #---------------------------------------------------------
488 STATE_DEFAULT = 1000 489 STATE_DEFAULT = 1000
@@ -502,6 +503,7 @@ SLICE_STATE_WATERSHED = 3009 @@ -502,6 +503,7 @@ SLICE_STATE_WATERSHED = 3009
502 SLICE_STATE_REORIENT = 3010 503 SLICE_STATE_REORIENT = 3010
503 SLICE_STATE_MASK_FFILL = 3011 504 SLICE_STATE_MASK_FFILL = 3011
504 SLICE_STATE_REMOVE_MASK_PARTS = 3012 505 SLICE_STATE_REMOVE_MASK_PARTS = 3012
  506 +SLICE_STATE_SELECT_MASK_PARTS = 3013
505 507
506 VOLUME_STATE_SEED = 2001 508 VOLUME_STATE_SEED = 2001
507 # STATE_LINEAR_MEASURE = 3001 509 # STATE_LINEAR_MEASURE = 3001
@@ -521,6 +523,7 @@ SLICE_STYLES.append(SLICE_STATE_EDITOR) @@ -521,6 +523,7 @@ SLICE_STYLES.append(SLICE_STATE_EDITOR)
521 SLICE_STYLES.append(SLICE_STATE_WATERSHED) 523 SLICE_STYLES.append(SLICE_STATE_WATERSHED)
522 SLICE_STYLES.append(SLICE_STATE_MASK_FFILL) 524 SLICE_STYLES.append(SLICE_STATE_MASK_FFILL)
523 SLICE_STYLES.append(SLICE_STATE_REMOVE_MASK_PARTS) 525 SLICE_STYLES.append(SLICE_STATE_REMOVE_MASK_PARTS)
  526 +SLICE_STYLES.append(SLICE_STATE_SELECT_MASK_PARTS)
524 527
525 VOLUME_STYLES = TOOL_STATES + [VOLUME_STATE_SEED, STATE_MEASURE_DISTANCE, 528 VOLUME_STYLES = TOOL_STATES + [VOLUME_STATE_SEED, STATE_MEASURE_DISTANCE,
526 STATE_MEASURE_ANGLE] 529 STATE_MEASURE_ANGLE]
@@ -531,6 +534,7 @@ STYLE_LEVEL = {SLICE_STATE_EDITOR: 1, @@ -531,6 +534,7 @@ STYLE_LEVEL = {SLICE_STATE_EDITOR: 1,
531 SLICE_STATE_WATERSHED: 1, 534 SLICE_STATE_WATERSHED: 1,
532 SLICE_STATE_MASK_FFILL: 2, 535 SLICE_STATE_MASK_FFILL: 2,
533 SLICE_STATE_REMOVE_MASK_PARTS: 2, 536 SLICE_STATE_REMOVE_MASK_PARTS: 2,
  537 + SLICE_STATE_SELECT_MASK_PARTS: 2,
534 SLICE_STATE_CROSS: 2, 538 SLICE_STATE_CROSS: 2,
535 SLICE_STATE_SCROLL: 2, 539 SLICE_STATE_SCROLL: 2,
536 SLICE_STATE_REORIENT: 2, 540 SLICE_STATE_REORIENT: 2,
invesalius/data/mask.py
@@ -126,7 +126,7 @@ class EditionHistory(object): @@ -126,7 +126,7 @@ class EditionHistory(object):
126 h[self.index].commit_history(mvolume) 126 h[self.index].commit_history(mvolume)
127 self._reload_slice(self.index) 127 self._reload_slice(self.index)
128 Publisher.sendMessage("Enable redo", True) 128 Publisher.sendMessage("Enable redo", True)
129 - 129 +
130 if self.index == 0: 130 if self.index == 0:
131 Publisher.sendMessage("Enable undo", False) 131 Publisher.sendMessage("Enable undo", False)
132 print "AT", self.index, len(self.history), self.history[self.index].filename 132 print "AT", self.index, len(self.history), self.history[self.index].filename
@@ -154,7 +154,7 @@ class EditionHistory(object): @@ -154,7 +154,7 @@ class EditionHistory(object):
154 h[self.index].commit_history(mvolume) 154 h[self.index].commit_history(mvolume)
155 self._reload_slice(self.index) 155 self._reload_slice(self.index)
156 Publisher.sendMessage("Enable undo", True) 156 Publisher.sendMessage("Enable undo", True)
157 - 157 +
158 if self.index == len(h) - 1: 158 if self.index == len(h) - 1:
159 Publisher.sendMessage("Enable redo", False) 159 Publisher.sendMessage("Enable redo", False)
160 print "AT", self.index, len(h), h[self.index].filename 160 print "AT", self.index, len(h), h[self.index].filename
@@ -174,7 +174,7 @@ class EditionHistory(object): @@ -174,7 +174,7 @@ class EditionHistory(object):
174 v_undo = False 174 v_undo = False
175 elif self.index == len(self.history) - 1: 175 elif self.index == len(self.history) - 1:
176 v_redo = False 176 v_redo = False
177 - 177 +
178 Publisher.sendMessage("Enable undo", v_undo) 178 Publisher.sendMessage("Enable undo", v_undo)
179 Publisher.sendMessage("Enable redo", v_redo) 179 Publisher.sendMessage("Enable redo", v_redo)
180 180
@@ -229,7 +229,7 @@ class Mask(): @@ -229,7 +229,7 @@ class Mask():
229 229
230 def SavePlist(self, dir_temp, filelist): 230 def SavePlist(self, dir_temp, filelist):
231 mask = {} 231 mask = {}
232 - filename = u'mask_%d' % self.index 232 + filename = u'mask_%d' % self.index
233 mask_filename = u'%s.dat' % filename 233 mask_filename = u'%s.dat' % filename
234 mask_filepath = os.path.join(dir_temp, mask_filename) 234 mask_filepath = os.path.join(dir_temp, mask_filename)
235 filelist[self.temp_file] = mask_filename 235 filelist[self.temp_file] = mask_filename
@@ -304,7 +304,12 @@ class Mask(): @@ -304,7 +304,12 @@ class Mask():
304 Mask.general_index = index 304 Mask.general_index = index
305 305
306 def create_mask(self, shape): 306 def create_mask(self, shape):
307 - print "Creating a mask" 307 + """
  308 + Creates a new mask object. This method do not append this new mask into the project.
  309 +
  310 + Parameters:
  311 + shape(int, int, int): The shape of the new mask.
  312 + """
308 self.temp_file = tempfile.mktemp() 313 self.temp_file = tempfile.mktemp()
309 shape = shape[0] + 1, shape[1] + 1, shape[2] + 1 314 shape = shape[0] + 1, shape[1] + 1, shape[2] + 1
310 self.matrix = numpy.memmap(self.temp_file, mode='w+', dtype='uint8', shape=shape) 315 self.matrix = numpy.memmap(self.temp_file, mode='w+', dtype='uint8', shape=shape)
@@ -329,7 +334,7 @@ class Mask(): @@ -329,7 +334,7 @@ class Mask():
329 new_mask.threshold_range = self.threshold_range 334 new_mask.threshold_range = self.threshold_range
330 new_mask.edition_threshold_range = self.edition_threshold_range 335 new_mask.edition_threshold_range = self.edition_threshold_range
331 new_mask.is_shown = self.is_shown 336 new_mask.is_shown = self.is_shown
332 - 337 +
333 new_mask.create_mask(shape=[i-1 for i in self.matrix.shape]) 338 new_mask.create_mask(shape=[i-1 for i in self.matrix.shape])
334 new_mask.matrix[:] = self.matrix[:] 339 new_mask.matrix[:] = self.matrix[:]
335 340
invesalius/data/slice_.py
@@ -302,14 +302,14 @@ class Slice(object): @@ -302,14 +302,14 @@ class Slice(object):
302 302
303 def __add_mask(self, pubsub_evt): 303 def __add_mask(self, pubsub_evt):
304 mask_name = pubsub_evt.data 304 mask_name = pubsub_evt.data
305 - self.CreateMask(name=mask_name) 305 + self.create_new_mask(name=mask_name)
306 self.SetMaskColour(self.current_mask.index, self.current_mask.colour) 306 self.SetMaskColour(self.current_mask.index, self.current_mask.colour)
307 307
308 def __add_mask_thresh(self, pubsub_evt): 308 def __add_mask_thresh(self, pubsub_evt):
309 mask_name = pubsub_evt.data[0] 309 mask_name = pubsub_evt.data[0]
310 thresh = pubsub_evt.data[1] 310 thresh = pubsub_evt.data[1]
311 colour = pubsub_evt.data[2] 311 colour = pubsub_evt.data[2]
312 - self.CreateMask(name=mask_name, threshold_range=thresh, colour =colour) 312 + self.create_new_mask(name=mask_name, threshold_range=thresh, colour=colour)
313 self.SetMaskColour(self.current_mask.index, self.current_mask.colour) 313 self.SetMaskColour(self.current_mask.index, self.current_mask.colour)
314 self.SelectCurrentMask(self.current_mask.index) 314 self.SelectCurrentMask(self.current_mask.index)
315 Publisher.sendMessage('Reload actual slice') 315 Publisher.sendMessage('Reload actual slice')
@@ -574,6 +574,14 @@ class Slice(object): @@ -574,6 +574,14 @@ class Slice(object):
574 1: (0.0, 1.0, 0.0, 1.0), 574 1: (0.0, 1.0, 0.0, 1.0),
575 2: (1.0, 0.0, 0.0, 1.0)}) 575 2: (1.0, 0.0, 0.0, 1.0)})
576 final_image = self.do_blend(final_image, cimage) 576 final_image = self.do_blend(final_image, cimage)
  577 + elif self.to_show_aux and self.current_mask:
  578 + m = self.get_aux_slice(self.to_show_aux, orientation, slice_number)
  579 + tmp_vimage = converters.to_vtk(m, self.spacing, slice_number, orientation)
  580 + aux_image = self.do_custom_colour(tmp_vimage, {0: (0.0, 0.0, 0.0, 0.0),
  581 + 1: (0.0, 0.0, 0.0, 0.0),
  582 + 254: (1.0, 0.0, 0.0, 1.0),
  583 + 255: (1.0, 0.0, 0.0, 1.0)})
  584 + final_image = self.do_blend(final_image, aux_image)
577 return final_image 585 return final_image
578 586
579 def get_image_slice(self, orientation, slice_number, number_slices=1, 587 def get_image_slice(self, orientation, slice_number, number_slices=1,
@@ -1026,14 +1034,32 @@ class Slice(object): @@ -1026,14 +1034,32 @@ class Slice(object):
1026 #else: 1034 #else:
1027 #widget.SetInput(cast.GetOutput()) 1035 #widget.SetInput(cast.GetOutput())
1028 1036
1029 -  
1030 -  
1031 - def CreateMask(self, imagedata=None, name=None, colour=None,  
1032 - opacity=None, threshold_range=None,  
1033 - edition_threshold_range = None,  
1034 - edited_points=None):  
1035 -  
1036 - # TODO: mask system to new system. 1037 + def create_new_mask(self, name=None,
  1038 + colour=None,
  1039 + opacity=None,
  1040 + threshold_range=None,
  1041 + edition_threshold_range=None,
  1042 + add_to_project=True,
  1043 + show=True):
  1044 + """
  1045 + Creates a new mask and add it to project.
  1046 +
  1047 + Parameters:
  1048 + name (string): name of the new mask. If name is None a automatic
  1049 + name will be used.
  1050 + colour (R, G, B): a RGB tuple of float number.
  1051 + opacity (float): a float number, from 0 to 1. If opacity is None
  1052 + the default one will be used.
  1053 + threshold_range (int, int): a 2-tuple indicating threshold range.
  1054 + If None the default one will be used.
  1055 + edition_threshold_range (int, int): a 2-tuple indicating threshold
  1056 + range. If None the default one will be used.
  1057 + show (bool): if this new mask will be showed and set as current
  1058 + mask.
  1059 +
  1060 + Returns:
  1061 + new_mask: The new mask object.
  1062 + """
1037 future_mask = Mask() 1063 future_mask = Mask()
1038 future_mask.create_mask(self.matrix.shape) 1064 future_mask.create_mask(self.matrix.shape)
1039 1065
@@ -1045,12 +1071,13 @@ class Slice(object): @@ -1045,12 +1071,13 @@ class Slice(object):
1045 future_mask.opacity = opacity 1071 future_mask.opacity = opacity
1046 if edition_threshold_range: 1072 if edition_threshold_range:
1047 future_mask.edition_threshold_range = edition_threshold_range 1073 future_mask.edition_threshold_range = edition_threshold_range
1048 - if edited_points:  
1049 - future_mask.edited_points = edited_points  
1050 if threshold_range: 1074 if threshold_range:
1051 future_mask.threshold_range = threshold_range 1075 future_mask.threshold_range = threshold_range
1052 1076
1053 - self._add_mask_into_proj(future_mask) 1077 + if add_to_project:
  1078 + self._add_mask_into_proj(future_mask, show=show)
  1079 +
  1080 + return future_mask
1054 1081
1055 1082
1056 def _add_mask_into_proj(self, mask, show=True): 1083 def _add_mask_into_proj(self, mask, show=True):
@@ -1282,6 +1309,7 @@ class Slice(object): @@ -1282,6 +1309,7 @@ class Slice(object):
1282 op, m1, m2 = pubsub_evt.data 1309 op, m1, m2 = pubsub_evt.data
1283 self.do_boolean_op(op, m1, m2) 1310 self.do_boolean_op(op, m1, m2)
1284 1311
  1312 +
1285 def do_boolean_op(self, op, m1, m2): 1313 def do_boolean_op(self, op, m1, m2):
1286 name_ops = {const.BOOLEAN_UNION: _(u"Union"), 1314 name_ops = {const.BOOLEAN_UNION: _(u"Union"),
1287 const.BOOLEAN_DIFF: _(u"Diff"), 1315 const.BOOLEAN_DIFF: _(u"Diff"),
invesalius/data/styles.py
@@ -1928,6 +1928,98 @@ class RemoveMaskPartsInteractorStyle(FloodFillMaskInteractorStyle): @@ -1928,6 +1928,98 @@ class RemoveMaskPartsInteractorStyle(FloodFillMaskInteractorStyle):
1928 self._progr_msg = _(u"Removing part ...") 1928 self._progr_msg = _(u"Removing part ...")
1929 1929
1930 1930
  1931 +class SelectPartConfig(object):
  1932 + __metaclass__= utils.Singleton
  1933 + def __init__(self):
  1934 + self.mask = None
  1935 + self.con_3d = 6
  1936 + self.dlg_visible = False
  1937 + self.mask_name = ''
  1938 +
  1939 +
  1940 +class SelectMaskPartsInteractorStyle(DefaultInteractorStyle):
  1941 + def __init__(self, viewer):
  1942 + DefaultInteractorStyle.__init__(self, viewer)
  1943 +
  1944 + self.viewer = viewer
  1945 + self.orientation = self.viewer.orientation
  1946 +
  1947 + self.picker = vtk.vtkWorldPointPicker()
  1948 + self.slice_actor = viewer.slice_data.actor
  1949 + self.slice_data = viewer.slice_data
  1950 +
  1951 + self.config = SelectPartConfig()
  1952 + self.dlg = None
  1953 +
  1954 + self.t0 = 254
  1955 + self.t1 = 255
  1956 + self.fill_value = 254
  1957 +
  1958 + self.AddObserver("LeftButtonPressEvent", self.OnSelect)
  1959 +
  1960 + def SetUp(self):
  1961 + if not self.config.dlg_visible:
  1962 + import data.mask as mask
  1963 + default_name = const.MASK_NAME_PATTERN %(mask.Mask.general_index+2)
  1964 +
  1965 + self.config.mask_name = default_name
  1966 + self.config.dlg_visible = True
  1967 + self.dlg= dialogs.SelectPartsOptionsDialog(self.config)
  1968 + self.dlg.Show()
  1969 +
  1970 + def CleanUp(self):
  1971 + if (self.dlg is not None) and (self.config.dlg_visible):
  1972 + self.config.dlg_visible = False
  1973 + self.dlg.Destroy()
  1974 + self.dlg = None
  1975 +
  1976 + if self.config.mask:
  1977 + self.config.mask.name = self.config.mask_name
  1978 + self.viewer.slice_._add_mask_into_proj(self.config.mask)
  1979 + self.viewer.slice_.SelectCurrentMask(self.config.mask.index)
  1980 + Publisher.sendMessage('Change mask selected', self.config.mask.index)
  1981 + self.config.mask = None
  1982 + del self.viewer.slice_.aux_matrices['SELECT']
  1983 + self.viewer.slice_.to_show_aux = ''
  1984 + Publisher.sendMessage('Reload actual slice')
  1985 +
  1986 + def OnSelect(self, obj, evt):
  1987 + if (self.viewer.slice_.buffer_slices[self.orientation].mask is None):
  1988 + return
  1989 +
  1990 + iren = self.viewer.interactor
  1991 + mouse_x, mouse_y = iren.GetEventPosition()
  1992 + x, y, z = self.viewer.get_voxel_clicked(mouse_x, mouse_y, self.picker)
  1993 +
  1994 + mask = self.viewer.slice_.current_mask.matrix[1:, 1:, 1:]
  1995 +
  1996 + bstruct = np.array(generate_binary_structure(3, CON3D[self.config.con_3d]), dtype='uint8')
  1997 + self.viewer.slice_.do_threshold_to_all_slices()
  1998 +
  1999 + if self.config.mask is None:
  2000 + self._create_new_mask()
  2001 +
  2002 + if iren.GetControlKey():
  2003 + floodfill.floodfill_threshold(self.config.mask.matrix[1:, 1:, 1:], [[x, y, z]], 254, 255, 0, bstruct, self.config.mask.matrix[1:, 1:, 1:])
  2004 + else:
  2005 + floodfill.floodfill_threshold(mask, [[x, y, z]], self.t0, self.t1, self.fill_value, bstruct, self.config.mask.matrix[1:, 1:, 1:])
  2006 +
  2007 + self.viewer.slice_.aux_matrices['SELECT'] = self.config.mask.matrix[1:, 1:, 1:]
  2008 + self.viewer.slice_.to_show_aux = 'SELECT'
  2009 +
  2010 + self.config.mask.was_edited = True
  2011 + Publisher.sendMessage('Reload actual slice')
  2012 +
  2013 + def _create_new_mask(self):
  2014 + mask = self.viewer.slice_.create_new_mask(show=False, add_to_project=False)
  2015 + mask.was_edited = True
  2016 + mask.matrix[0, :, :] = 1
  2017 + mask.matrix[:, 0, :] = 1
  2018 + mask.matrix[:, :, 0] = 1
  2019 +
  2020 + self.config.mask = mask
  2021 +
  2022 +
1931 def get_style(style): 2023 def get_style(style):
1932 STYLES = { 2024 STYLES = {
1933 const.STATE_DEFAULT: DefaultInteractorStyle, 2025 const.STATE_DEFAULT: DefaultInteractorStyle,
@@ -1945,6 +2037,7 @@ def get_style(style): @@ -1945,6 +2037,7 @@ def get_style(style):
1945 const.SLICE_STATE_REORIENT: ReorientImageInteractorStyle, 2037 const.SLICE_STATE_REORIENT: ReorientImageInteractorStyle,
1946 const.SLICE_STATE_MASK_FFILL: FloodFillMaskInteractorStyle, 2038 const.SLICE_STATE_MASK_FFILL: FloodFillMaskInteractorStyle,
1947 const.SLICE_STATE_REMOVE_MASK_PARTS: RemoveMaskPartsInteractorStyle, 2039 const.SLICE_STATE_REMOVE_MASK_PARTS: RemoveMaskPartsInteractorStyle,
  2040 + const.SLICE_STATE_SELECT_MASK_PARTS: SelectMaskPartsInteractorStyle,
1948 } 2041 }
1949 return STYLES[style] 2042 return STYLES[style]
1950 2043
invesalius/data/viewer_slice.py
@@ -977,9 +977,12 @@ class Viewer(wx.Panel): @@ -977,9 +977,12 @@ class Viewer(wx.Panel):
977 my = round((z - zi)/self.slice_.spacing[2], 0) 977 my = round((z - zi)/self.slice_.spacing[2], 0)
978 return my, mx 978 return my, mx
979 979
980 - def get_coordinate_cursor(self): 980 + def get_coordinate_cursor(self, picker=None):
981 # Find position 981 # Find position
982 - x, y, z = self.pick.GetPickPosition() 982 + if picker is None:
  983 + picker = self.pick
  984 +
  985 + x, y, z = picker.GetPickPosition()
983 bounds = self.slice_data.actor.GetBounds() 986 bounds = self.slice_data.actor.GetBounds()
984 if bounds[0] == bounds[1]: 987 if bounds[0] == bounds[1]:
985 x = bounds[0] 988 x = bounds[0]
@@ -989,11 +992,14 @@ class Viewer(wx.Panel): @@ -989,11 +992,14 @@ class Viewer(wx.Panel):
989 z = bounds[4] 992 z = bounds[4]
990 return x, y, z 993 return x, y, z
991 994
992 - def get_coordinate_cursor_edition(self, slice_data): 995 + def get_coordinate_cursor_edition(self, slice_data, picker=None):
993 # Find position 996 # Find position
994 actor = slice_data.actor 997 actor = slice_data.actor
995 slice_number = slice_data.number 998 slice_number = slice_data.number
996 - x, y, z = self.pick.GetPickPosition() 999 + if picker is None:
  1000 + picker = self.pick
  1001 +
  1002 + x, y, z = picker.GetPickPosition()
997 1003
998 # First we fix the position origin, based on vtkActor bounds 1004 # First we fix the position origin, based on vtkActor bounds
999 bounds = actor.GetBounds() 1005 bounds = actor.GetBounds()
@@ -1023,6 +1029,41 @@ class Viewer(wx.Panel): @@ -1023,6 +1029,41 @@ class Viewer(wx.Panel):
1023 1029
1024 return x, y, z 1030 return x, y, z
1025 1031
  1032 + def get_voxel_clicked(self, mx, my, picker=None):
  1033 + """
  1034 + Given the (mx, my) mouse clicked position returns the voxel coordinate
  1035 + of the voxel at (that mx, my) position.
  1036 +
  1037 + Parameters:
  1038 + mx (int): x position.
  1039 + my (int): y position
  1040 + picker: the picker used to get calculate the voxel coordinate.
  1041 +
  1042 + Returns:
  1043 + voxel_coordinate (x, y, z): voxel coordinate inside the matrix. Can
  1044 + be used to access the voxel value inside the matrix.
  1045 + """
  1046 + if picker is None:
  1047 + picker = self.pick
  1048 +
  1049 + slice_data = self.slice_data
  1050 + renderer = slice_data.renderer
  1051 +
  1052 + picker.Pick(mx, my, 0, renderer)
  1053 +
  1054 + coord = self.get_coordinate_cursor(picker)
  1055 + position = slice_data.actor.GetInput().FindPoint(coord)
  1056 +
  1057 + if position != -1:
  1058 + coord = slice_data.actor.GetInput().GetPoint(position)
  1059 +
  1060 + if position < 0:
  1061 + position = viewer.calculate_matrix_position(coord)
  1062 +
  1063 + x, y, z = self.calcultate_scroll_position(position)
  1064 +
  1065 + return (x, y, z)
  1066 +
1026 def __bind_events(self): 1067 def __bind_events(self):
1027 Publisher.subscribe(self.LoadImagedata, 1068 Publisher.subscribe(self.LoadImagedata,
1028 'Load slice to viewer') 1069 'Load slice to viewer')
@@ -1044,8 +1085,8 @@ class Viewer(wx.Panel): @@ -1044,8 +1085,8 @@ class Viewer(wx.Panel):
1044 Publisher.subscribe(self.Navigation, 1085 Publisher.subscribe(self.Navigation,
1045 'Co-registered Points') 1086 'Co-registered Points')
1046 ### 1087 ###
1047 - Publisher.subscribe(self.ChangeBrushColour,  
1048 - 'Add mask') 1088 + # Publisher.subscribe(self.ChangeBrushColour,
  1089 + # 'Add mask')
1049 1090
1050 Publisher.subscribe(self.UpdateWindowLevelValue, 1091 Publisher.subscribe(self.UpdateWindowLevelValue,
1051 'Update window level value') 1092 'Update window level value')
invesalius/gui/data_notebook.py
@@ -512,11 +512,11 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): @@ -512,11 +512,11 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
512 self.SetStringItem(index, 1, label, 512 self.SetStringItem(index, 1, label,
513 imageId=self.mask_list_index[index]) 513 imageId=self.mask_list_index[index])
514 self.SetStringItem(index, 2, threshold) 514 self.SetStringItem(index, 2, threshold)
515 - self.SetItemImage(index, 1)  
516 - for key in self.mask_list_index.keys():  
517 - if key != index:  
518 - self.SetItemImage(key, 0)  
519 - self.current_index = index 515 + # self.SetItemImage(index, 1)
  516 + # for key in self.mask_list_index.keys():
  517 + # if key != index:
  518 + # self.SetItemImage(key, 0)
  519 + # self.current_index = index
520 520
521 def AddMask(self, pubsub_evt): 521 def AddMask(self, pubsub_evt):
522 index, mask_name, threshold_range, colour = pubsub_evt.data 522 index, mask_name, threshold_range, colour = pubsub_evt.data
invesalius/gui/dialogs.py
@@ -1940,3 +1940,94 @@ class FFillOptionsDialog(wx.Dialog): @@ -1940,3 +1940,94 @@ class FFillOptionsDialog(wx.Dialog):
1940 Publisher.sendMessage('Disable style', const.SLICE_STATE_MASK_FFILL) 1940 Publisher.sendMessage('Disable style', const.SLICE_STATE_MASK_FFILL)
1941 evt.Skip() 1941 evt.Skip()
1942 self.Destroy() 1942 self.Destroy()
  1943 +
  1944 +
  1945 +class SelectPartsOptionsDialog(wx.Dialog):
  1946 + def __init__(self, config):
  1947 + pre = wx.PreDialog()
  1948 + pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Select mask parts"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT)
  1949 + self.PostCreate(pre)
  1950 +
  1951 + self.config = config
  1952 +
  1953 + self._init_gui()
  1954 +
  1955 + def _init_gui(self):
  1956 + self.target_name = wx.TextCtrl(self, -1)
  1957 + self.target_name.SetValue(self.config.mask_name)
  1958 +
  1959 + # Connectivity 3D
  1960 + self.conect3D_6 = wx.RadioButton(self, -1, "6", style=wx.RB_GROUP)
  1961 + self.conect3D_18 = wx.RadioButton(self, -1, "18")
  1962 + self.conect3D_26 = wx.RadioButton(self, -1, "26")
  1963 +
  1964 + if self.config.con_3d == 18:
  1965 + self.conect3D_18.SetValue(1)
  1966 + elif self.config.con_3d == 26:
  1967 + self.conect3D_26.SetValue(1)
  1968 + else:
  1969 + self.conect3D_6.SetValue(1)
  1970 +
  1971 + sizer_t = wx.BoxSizer(wx.HORIZONTAL)
  1972 + sizer_t.AddSpacer(7)
  1973 + sizer_t.Add(wx.StaticText(self, -1, _(u"Target mask name")), 1, wx.ALIGN_CENTRE_VERTICAL)
  1974 + sizer_t.AddSpacer(7)
  1975 + sizer_t.Add(self.target_name, 1, wx.EXPAND)
  1976 + sizer_t.AddSpacer(7)
  1977 +
  1978 + sizer_c = wx.BoxSizer(wx.HORIZONTAL)
  1979 + sizer_c.AddSpacer(7)
  1980 + sizer_c.Add(self.conect3D_6)
  1981 + sizer_c.AddSpacer(7)
  1982 + sizer_c.Add(self.conect3D_18)
  1983 + sizer_c.AddSpacer(7)
  1984 + sizer_c.Add(self.conect3D_26)
  1985 +
  1986 + sizer = wx.BoxSizer(wx.VERTICAL)
  1987 + sizer.AddSpacer(7)
  1988 + sizer.Add(sizer_t, 1, wx.EXPAND)
  1989 + sizer.AddSpacer(7)
  1990 + sizer.Add(wx.StaticText(self, -1, _(u"3D Connectivity")), 0, wx.LEFT, 7)
  1991 + sizer.AddSpacer(5)
  1992 + sizer.Add(sizer_c)
  1993 + sizer.AddSpacer(7)
  1994 +
  1995 + # sizer = wx.GridBagSizer(11, 6)
  1996 + # sizer.AddStretchSpacer((0, 0))
  1997 +
  1998 + # sizer.Add(wx.StaticText(self, -1, _(u"Target mask name")), (1, 0), (1, 6), flag=wx.LEFT|wx.ALIGN_BOTTOM|wx.EXPAND, border=7)
  1999 + # sizer.Add(self.target_name, (2, 0), (1, 6), flag=wx.LEFT|wx.EXPAND|wx.RIGHT|wx.ALIGN_TOP, border=9)
  2000 +
  2001 + # # sizer.AddStretchSpacer((3, 0))
  2002 +
  2003 + # sizer.Add(wx.StaticText(self, -1, _(u"3D Connectivity")), (3, 0), (1, 6), flag=wx.LEFT, border=7)
  2004 + # sizer.Add(self.conect3D_6, (4, 0), flag=wx.LEFT, border=9)
  2005 + # sizer.Add(self.conect3D_18, (4, 1), flag=wx.LEFT, border=9)
  2006 + # sizer.Add(self.conect3D_26, (4, 2), flag=wx.LEFT, border=9)
  2007 + # sizer.AddStretchSpacer((5, 0))
  2008 +
  2009 + self.SetSizer(sizer)
  2010 + sizer.Fit(self)
  2011 + self.Layout()
  2012 +
  2013 + self.target_name.Bind(wx.EVT_CHAR, self.OnChar)
  2014 + self.Bind(wx.EVT_RADIOBUTTON, self.OnSetRadio)
  2015 + self.Bind(wx.EVT_CLOSE, self.OnClose)
  2016 +
  2017 + def OnChar(self, evt):
  2018 + evt.Skip()
  2019 + self.config.mask_name = self.target_name.GetValue()
  2020 +
  2021 + def OnSetRadio(self, evt):
  2022 + if self.conect3D_6.GetValue():
  2023 + self.config.con_3d = 6
  2024 + elif self.conect3D_18.GetValue():
  2025 + self.config.con_3d = 18
  2026 + elif self.conect3D_26.GetValue():
  2027 + self.config.con_3d = 26
  2028 +
  2029 + def OnClose(self, evt):
  2030 + if self.config.dlg_visible:
  2031 + Publisher.sendMessage('Disable style', const.SLICE_STATE_SELECT_MASK_PARTS)
  2032 + evt.Skip()
  2033 + self.Destroy()
invesalius/gui/frame.py
@@ -451,20 +451,16 @@ class Frame(wx.Frame): @@ -451,20 +451,16 @@ class Frame(wx.Frame):
451 elif id == const.ID_REMOVE_MASK_PART: 451 elif id == const.ID_REMOVE_MASK_PART:
452 self.OnRemoveMaskParts() 452 self.OnRemoveMaskParts()
453 453
  454 + elif id == const.ID_SELECT_MASK_PART:
  455 + self.OnSelectMaskParts()
  456 +
454 elif id == const.ID_VIEW_INTERPOLATED: 457 elif id == const.ID_VIEW_INTERPOLATED:
455 -  
456 st = self.actived_interpolated_slices.IsChecked(const.ID_VIEW_INTERPOLATED) 458 st = self.actived_interpolated_slices.IsChecked(const.ID_VIEW_INTERPOLATED)
457 -  
458 if st: 459 if st:
459 self.OnInterpolatedSlices(True) 460 self.OnInterpolatedSlices(True)
460 else: 461 else:
461 self.OnInterpolatedSlices(False) 462 self.OnInterpolatedSlices(False)
462 463
463 - def OnInterpolatedSlices(self, status):  
464 -  
465 - Publisher.sendMessage('Set interpolated slices', status)  
466 -  
467 -  
468 def OnSize(self, evt): 464 def OnSize(self, evt):
469 """ 465 """
470 Refresh GUI when frame is resized. 466 Refresh GUI when frame is resized.
@@ -594,6 +590,12 @@ class Frame(wx.Frame): @@ -594,6 +590,12 @@ class Frame(wx.Frame):
594 def OnRemoveMaskParts(self): 590 def OnRemoveMaskParts(self):
595 Publisher.sendMessage('Enable style', const.SLICE_STATE_REMOVE_MASK_PARTS) 591 Publisher.sendMessage('Enable style', const.SLICE_STATE_REMOVE_MASK_PARTS)
596 592
  593 + def OnSelectMaskParts(self):
  594 + Publisher.sendMessage('Enable style', const.SLICE_STATE_SELECT_MASK_PARTS)
  595 +
  596 + def OnInterpolatedSlices(self, status):
  597 + Publisher.sendMessage('Set interpolated slices', status)
  598 +
597 # ------------------------------------------------------------------ 599 # ------------------------------------------------------------------
598 # ------------------------------------------------------------------ 600 # ------------------------------------------------------------------
599 # ------------------------------------------------------------------ 601 # ------------------------------------------------------------------
@@ -615,7 +617,8 @@ class MenuBar(wx.MenuBar): @@ -615,7 +617,8 @@ class MenuBar(wx.MenuBar):
615 const.ID_PROJECT_CLOSE, 617 const.ID_PROJECT_CLOSE,
616 const.ID_REORIENT_IMG, 618 const.ID_REORIENT_IMG,
617 const.ID_FLOODFILL_MASK, 619 const.ID_FLOODFILL_MASK,
618 - const.ID_REMOVE_MASK_PART,] 620 + const.ID_REMOVE_MASK_PART,
  621 + const.ID_SELECT_MASK_PART,]
619 self.__init_items() 622 self.__init_items()
620 self.__bind_events() 623 self.__bind_events()
621 624
@@ -733,6 +736,9 @@ class MenuBar(wx.MenuBar): @@ -733,6 +736,9 @@ class MenuBar(wx.MenuBar):
733 self.remove_mask_part_menu = mask_menu.Append(const.ID_REMOVE_MASK_PART, _(u"Remove parts")) 736 self.remove_mask_part_menu = mask_menu.Append(const.ID_REMOVE_MASK_PART, _(u"Remove parts"))
734 self.remove_mask_part_menu.Enable(False) 737 self.remove_mask_part_menu.Enable(False)
735 738
  739 + self.select_mask_part_menu = mask_menu.Append(const.ID_SELECT_MASK_PART, _(u"Select parts"))
  740 + self.select_mask_part_menu.Enable(False)
  741 +
736 tools_menu.AppendMenu(-1, _(u"Mask"), mask_menu) 742 tools_menu.AppendMenu(-1, _(u"Mask"), mask_menu)
737 743
738 # Image menu 744 # Image menu
invesalius/gui/task_slice.py
@@ -562,10 +562,10 @@ class MaskProperties(wx.Panel): @@ -562,10 +562,10 @@ class MaskProperties(wx.Panel):
562 mask_thresh = evt_pubsub.data[2] 562 mask_thresh = evt_pubsub.data[2]
563 mask_colour = [int(c*255) for c in evt_pubsub.data[3]] 563 mask_colour = [int(c*255) for c in evt_pubsub.data[3]]
564 index = self.combo_mask_name.Append(mask_name) 564 index = self.combo_mask_name.Append(mask_name)
565 - self.combo_mask_name.SetSelection(index)  
566 - self.button_colour.SetColour(mask_colour)  
567 - self.gradient.SetColour(mask_colour)  
568 - self.combo_mask_name.SetSelection(index) 565 + # self.combo_mask_name.SetSelection(index)
  566 + # self.button_colour.SetColour(mask_colour)
  567 + # self.gradient.SetColour(mask_colour)
  568 + # self.combo_mask_name.SetSelection(index)
569 569
570 def GetMaskSelected(self): 570 def GetMaskSelected(self):
571 x = self.combo_mask_name.GetSelection() 571 x = self.combo_mask_name.GetSelection()