Commit b35498fbb8ed2c0d26a9a33c631ccd4a75e67905
1 parent
0eb985ed
Exists in
master
and in
68 other branches
ADD: Remove mask feature (#156)
Showing
3 changed files
with
165 additions
and
22 deletions
Show diff stats
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) |