Commit b35498fbb8ed2c0d26a9a33c631ccd4a75e67905

Authored by tatiana
1 parent 0eb985ed

ADD: Remove mask feature (#156)

invesalius/data/slice_.py
@@ -93,6 +93,22 @@ class Slice(object): @@ -93,6 +93,22 @@ class Slice(object):
93 93
94 ps.Publisher().subscribe(self.OnEnableStyle, 'Enable style') 94 ps.Publisher().subscribe(self.OnEnableStyle, 'Enable style')
95 ps.Publisher().subscribe(self.OnDisableStyle, 'Disable style') 95 ps.Publisher().subscribe(self.OnDisableStyle, 'Disable style')
  96 + ps.Publisher().subscribe(self.OnRemoveMasks, 'Remove masks')
  97 +
  98 +
  99 + def OnRemoveMasks(self, pubsub_evt):
  100 + selected_items = pubsub_evt.data
  101 + proj = Project()
  102 + for item in selected_items:
  103 + proj.RemoveMask(item)
  104 +
  105 + index = self.current_mask.index
  106 + if (proj.mask_dict) and (index in selected_items):
  107 + self.SelectCurrentMask(0)
  108 + elif not proj.mask_dict:
  109 + self.blend_filter.SetOpacity(1, 0)
  110 + self.blend_filter.Update()
  111 + ps.Publisher().sendMessage('Update slice viewer')
96 112
97 113
98 def OnEnableStyle(self, pubsub_evt): 114 def OnEnableStyle(self, pubsub_evt):
@@ -157,8 +173,6 @@ class Slice(object): @@ -157,8 +173,6 @@ class Slice(object):
157 173
158 def __set_current_mask_threshold(self, evt_pubsub): 174 def __set_current_mask_threshold(self, evt_pubsub):
159 session = ses.Session() 175 session = ses.Session()
160 - print session.project_status != const.PROJ_OPEN  
161 - print session.project_status  
162 #FIXME: find a better way to implement this 176 #FIXME: find a better way to implement this
163 if (self.num_gradient >= 2) or \ 177 if (self.num_gradient >= 2) or \
164 (session.project_status != const.PROJ_OPEN): 178 (session.project_status != const.PROJ_OPEN):
invesalius/gui/data_notebook.py
@@ -25,14 +25,21 @@ import Image @@ -25,14 +25,21 @@ import Image
25 import wx 25 import wx
26 import wx.grid 26 import wx.grid
27 import wx.lib.flatnotebook as fnb 27 import wx.lib.flatnotebook as fnb
  28 +import wx.lib.platebtn as pbtn
28 import wx.lib.pubsub as ps 29 import wx.lib.pubsub as ps
29 30
  31 +import gui.dialogs as dlg
30 import gui.widgets.listctrl as listmix 32 import gui.widgets.listctrl as listmix
31 33
  34 +
  35 +
  36 +BTN_NEW, BTN_REMOVE, BTN_DUPLICATE = [wx.NewId() for i in xrange(3)]
  37 +
32 # Panel that initializes notebook and related tabs 38 # Panel that initializes notebook and related tabs
33 class NotebookPanel(wx.Panel): 39 class NotebookPanel(wx.Panel):
34 def __init__(self, parent): 40 def __init__(self, parent):
35 - wx.Panel.__init__(self, parent, pos=wx.Point(0, 50), size=wx.Size(256, 140)) 41 + wx.Panel.__init__(self, parent, pos=wx.Point(0, 50),
  42 + size=wx.Size(256, 160))
36 43
37 book = wx.Notebook(self, -1,style= wx.BK_DEFAULT) 44 book = wx.Notebook(self, -1,style= wx.BK_DEFAULT)
38 # TODO: check under Windows and Linux 45 # TODO: check under Windows and Linux
@@ -41,7 +48,7 @@ class NotebookPanel(wx.Panel): @@ -41,7 +48,7 @@ class NotebookPanel(wx.Panel):
41 if sys.platform != 'win32': 48 if sys.platform != 'win32':
42 book.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) 49 book.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
43 50
44 - book.AddPage(MasksListCtrlPanel(book), _("Masks")) 51 + book.AddPage(MaskPage(book), _("Masks"))
45 book.AddPage(SurfacesListCtrlPanel(book), _("Surfaces")) 52 book.AddPage(SurfacesListCtrlPanel(book), _("Surfaces"))
46 #book.AddPage(MeasuresListCtrlPanel(book), _("Measures")) 53 #book.AddPage(MeasuresListCtrlPanel(book), _("Measures"))
47 #book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations")) 54 #book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations"))
@@ -56,6 +63,103 @@ class NotebookPanel(wx.Panel): @@ -56,6 +63,103 @@ class NotebookPanel(wx.Panel):
56 63
57 # TODO: insert icons bellow notebook 64 # TODO: insert icons bellow notebook
58 65
  66 +
  67 +class MaskPage(wx.Panel):
  68 + """
  69 + Page related to mask items.
  70 + """
  71 + def __init__(self, parent):
  72 + wx.Panel.__init__(self, parent, pos=wx.Point(0, 50),
  73 + size=wx.Size(256, 120))
  74 + self.__init_gui()
  75 +
  76 + def __init_gui(self):
  77 + # listctrl were existing masks will be listed
  78 + self.listctrl = MasksListCtrlPanel(self, size=wx.Size(256, 80))
  79 + # button control with tools (eg. remove, add new, etc)
  80 + self.buttonctrl = ButtonControlPanel(self)
  81 +
  82 + sizer = wx.BoxSizer(wx.VERTICAL)
  83 + sizer.Add(self.listctrl, 0, wx.EXPAND)
  84 + sizer.Add(self.buttonctrl, 0, wx.EXPAND)
  85 + self.SetSizer(sizer)
  86 + self.Fit()
  87 +
  88 +
  89 +class ButtonControlPanel(wx.Panel):
  90 + """
  91 + Button control panel that includes data notebook operations.
  92 + TODO: Enhace interface with parent class - it is really messed up
  93 + """
  94 + def __init__(self, parent):
  95 + wx.Panel.__init__(self, parent, pos=wx.Point(0, 50),
  96 + size=wx.Size(256, 20))
  97 + self.parent = parent
  98 + self.__init_gui()
  99 +
  100 + def __init_gui(self):
  101 +
  102 + # Bitmaps
  103 + BMP_NEW = wx.Bitmap("../icons/data_new.png",
  104 + wx.BITMAP_TYPE_PNG)
  105 + BMP_REMOVE = wx.Bitmap("../icons/data_remove.png",
  106 + wx.BITMAP_TYPE_PNG)
  107 + BMP_DUPLICATE = wx.Bitmap("../icons/data_duplicate.png",
  108 + wx.BITMAP_TYPE_PNG)
  109 +
  110 + # Plate buttons based on previous bitmaps
  111 + button_style = pbtn.PB_STYLE_SQUARE | pbtn.PB_STYLE_DEFAULT
  112 + button_new = pbtn.PlateButton(self, BTN_NEW, "",
  113 + BMP_NEW,
  114 + style=button_style)
  115 + button_remove = pbtn.PlateButton(self, BTN_REMOVE, "",
  116 + BMP_REMOVE,
  117 + style=button_style)
  118 + button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "",
  119 + BMP_DUPLICATE,
  120 + style=button_style)
  121 +
  122 + # Add all controls to gui
  123 + sizer = wx.BoxSizer(wx.HORIZONTAL)
  124 + sizer.Add(button_new, 0, wx.GROW|wx.EXPAND|wx.LEFT, 10)
  125 + sizer.Add(button_remove, 0, wx.GROW|wx.EXPAND)
  126 + sizer.Add(button_duplicate, 0, wx.GROW|wx.EXPAND)
  127 + self.SetSizer(sizer)
  128 + self.Fit()
  129 +
  130 + self.Bind(wx.EVT_BUTTON, self.OnButton)
  131 +
  132 + def OnButton(self, evt):
  133 + id = evt.GetId()
  134 + if id == BTN_NEW:
  135 + self.OnNew()
  136 + elif id == BTN_REMOVE:
  137 + self.OnRemove()
  138 + elif id == BTN_DUPLICATE:
  139 + self.OnDuplicate()
  140 +
  141 + def OnNew(self):
  142 + print "New"
  143 + #print self.parent.listctrl.GetSelected()
  144 +
  145 + def OnRemove(self):
  146 + print "Remove"
  147 + selected_items = self.parent.listctrl.GetSelected()
  148 + if selected_items:
  149 + for item in selected_items:
  150 + self.parent.listctrl.RemoveMask(item)
  151 + ps.Publisher().sendMessage('Remove masks', selected_items)
  152 + else:
  153 + dlg.MaskSelectionRequiredForRemoval()
  154 +
  155 + def OnDuplicate(self):
  156 + print "Duplicate"
  157 + selected_items = self.parent.listctrl.GetSelected()
  158 + if selected_items:
  159 + ps.Publisher().sendMessage('Duplicate masks', selected_items)
  160 + else:
  161 + dlg.MaskSelectionRequiredForDuplication()
  162 +
59 class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): 163 class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
60 164
61 def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, 165 def __init__(self, parent, ID=-1, pos=wx.DefaultPosition,
@@ -68,7 +172,6 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): @@ -68,7 +172,6 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
68 wx.ListCtrl.__init__(self, parent, ID, pos, size, style=wx.LC_REPORT) 172 wx.ListCtrl.__init__(self, parent, ID, pos, size, style=wx.LC_REPORT)
69 listmix.TextEditMixin.__init__(self) 173 listmix.TextEditMixin.__init__(self)
70 self.mask_list_index = {} 174 self.mask_list_index = {}
71 - self.mask_bmp_idx_to_name = {}  
72 self.__init_columns() 175 self.__init_columns()
73 self.__init_image_list() 176 self.__init_image_list()
74 self.__bind_events_wx() 177 self.__bind_events_wx()
@@ -91,7 +194,6 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): @@ -91,7 +194,6 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
91 def OnCloseProject(self, pubsub_evt): 194 def OnCloseProject(self, pubsub_evt):
92 self.DeleteAllItems() 195 self.DeleteAllItems()
93 self.mask_list_index = {} 196 self.mask_list_index = {}
94 - self.mask_bmp_idx_to_name = {}  
95 197
96 def OnChangeCurrentMask(self, pubsub_evt): 198 def OnChangeCurrentMask(self, pubsub_evt):
97 199
@@ -195,6 +297,35 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): @@ -195,6 +297,35 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
195 image_index = self.mask_list_index[index] 297 image_index = self.mask_list_index[index]
196 self.imagelist.Replace(image_index, image) 298 self.imagelist.Replace(image_index, image)
197 self.Refresh() 299 self.Refresh()
  300 +
  301 + def GetSelected(self):
  302 + """
  303 + Return all items selected (highlighted).
  304 + """
  305 + selected = []
  306 + for index in self.mask_list_index:
  307 + if self.IsSelected(index):
  308 + selected.append(index)
  309 + # it is important to revert items order, so
  310 + # listctrl update is ok
  311 + selected.sort(reverse=True)
  312 + return selected
  313 +
  314 + def RemoveMask(self, index):
  315 + """
  316 + Remove item given its index.
  317 + """
  318 + # it is necessary to update internal dictionary
  319 + # that maps bitmap given item index
  320 + old_dict = self.mask_list_index
  321 + new_dict = {}
  322 + for i in old_dict:
  323 + if i < index:
  324 + new_dict[i] = old_dict[i]
  325 + if i > index:
  326 + new_dict[i-1] = old_dict[i]
  327 + self.mask_list_index = new_dict
  328 + self.DeleteItem(index)
198 329
199 class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): 330 class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
200 331
invesalius/gui/task_slice.py
@@ -26,6 +26,7 @@ import wx.lib.pubsub as ps @@ -26,6 +26,7 @@ import wx.lib.pubsub as ps
26 26
27 import data.mask as mask 27 import data.mask as mask
28 import constants as const 28 import constants as const
  29 +import gui.dialogs as dlg
29 import gui.widgets.gradient as grad 30 import gui.widgets.gradient as grad
30 import gui.widgets.foldpanelbar as fpb 31 import gui.widgets.foldpanelbar as fpb
31 import widgets.colourselect as csel 32 import widgets.colourselect as csel
@@ -142,25 +143,16 @@ class InnerTaskPanel(wx.Panel): @@ -142,25 +143,16 @@ class InnerTaskPanel(wx.Panel):
142 143
143 def OnButtonNextTask(self, evt): 144 def OnButtonNextTask(self, evt):
144 overwrite = self.check_box.IsChecked() 145 overwrite = self.check_box.IsChecked()
145 - ps.Publisher().sendMessage('Create surface from index', 146 + if self.GetMaskSelected() != -1:
  147 + ps.Publisher().sendMessage('Create surface from index',
146 (self.GetMaskSelected(), 148 (self.GetMaskSelected(),
147 overwrite)) 149 overwrite))
  150 + else:
  151 + dlg.InexistentMask()
148 152
149 def OnLinkNewMask(self, evt=None): 153 def OnLinkNewMask(self, evt=None):
150 - dlg = wx.TextEntryDialog(self, _('Name of new mask:'),  
151 - _('InVesalius 3 - New mask'))  
152 - dlg.CenterOnScreen()  
153 - default_mask_name = const.MASK_NAME_PATTERN %(mask.Mask.general_index+2)  
154 - dlg.SetValue(default_mask_name)  
155 -  
156 - try:  
157 - op = dlg.ShowModal() == wx.ID_OK  
158 - except(wx._core.PyAssertionError):  
159 - print "win64 - wx._core.PyAssertionError"  
160 - op = True  
161 -  
162 - if (op):  
163 - mask_name = dlg.GetValue() 154 + mask_name = dlg.NewMask()
  155 + if mask_name:
164 ps.Publisher().sendMessage('Create new mask', mask_name) 156 ps.Publisher().sendMessage('Create new mask', mask_name)
165 157
166 if evt: 158 if evt:
@@ -353,6 +345,7 @@ class MaskProperties(wx.Panel): @@ -353,6 +345,7 @@ class MaskProperties(wx.Panel):
353 'Set threshold values in gradient') 345 'Set threshold values in gradient')
354 ps.Publisher().subscribe(self.SelectMaskName, 'Select mask name in combo') 346 ps.Publisher().subscribe(self.SelectMaskName, 'Select mask name in combo')
355 ps.Publisher().subscribe(self.ChangeMaskName, 'Change mask name') 347 ps.Publisher().subscribe(self.ChangeMaskName, 'Change mask name')
  348 + ps.Publisher().subscribe(self.OnRemoveMasks, 'Remove masks')
356 ps.Publisher().subscribe(self.OnCloseProject, 'Close project data') 349 ps.Publisher().subscribe(self.OnCloseProject, 'Close project data')
357 ps.Publisher().subscribe(self.SetThresholdValues2, 'Set threshold values') 350 ps.Publisher().subscribe(self.SetThresholdValues2, 'Set threshold values')
358 351
@@ -366,7 +359,12 @@ class MaskProperties(wx.Panel): @@ -366,7 +359,12 @@ class MaskProperties(wx.Panel):
366 n = self.combo_thresh.GetCount() 359 n = self.combo_thresh.GetCount()
367 for i in xrange(n-1, -1, -1): 360 for i in xrange(n-1, -1, -1):
368 self.combo_thresh.Delete(i) 361 self.combo_thresh.Delete(i)
369 - 362 +
  363 + def OnRemoveMasks(self, pubsub_evt):
  364 + print "OnRemoveMasks"
  365 + list_index = pubsub_evt.data
  366 + for i in list_index:
  367 + self.combo_mask_name.Delete(i)
370 368
371 def __bind_events_wx(self): 369 def __bind_events_wx(self):
372 self.Bind(grad.EVT_THRESHOLD_CHANGE, self.OnSlideChanged, self.gradient) 370 self.Bind(grad.EVT_THRESHOLD_CHANGE, self.OnSlideChanged, self.gradient)