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 | 93 | |
94 | 94 | ps.Publisher().subscribe(self.OnEnableStyle, 'Enable style') |
95 | 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 | 114 | def OnEnableStyle(self, pubsub_evt): |
... | ... | @@ -157,8 +173,6 @@ class Slice(object): |
157 | 173 | |
158 | 174 | def __set_current_mask_threshold(self, evt_pubsub): |
159 | 175 | session = ses.Session() |
160 | - print session.project_status != const.PROJ_OPEN | |
161 | - print session.project_status | |
162 | 176 | #FIXME: find a better way to implement this |
163 | 177 | if (self.num_gradient >= 2) or \ |
164 | 178 | (session.project_status != const.PROJ_OPEN): | ... | ... |
invesalius/gui/data_notebook.py
... | ... | @@ -25,14 +25,21 @@ import Image |
25 | 25 | import wx |
26 | 26 | import wx.grid |
27 | 27 | import wx.lib.flatnotebook as fnb |
28 | +import wx.lib.platebtn as pbtn | |
28 | 29 | import wx.lib.pubsub as ps |
29 | 30 | |
31 | +import gui.dialogs as dlg | |
30 | 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 | 38 | # Panel that initializes notebook and related tabs |
33 | 39 | class NotebookPanel(wx.Panel): |
34 | 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 | 44 | book = wx.Notebook(self, -1,style= wx.BK_DEFAULT) |
38 | 45 | # TODO: check under Windows and Linux |
... | ... | @@ -41,7 +48,7 @@ class NotebookPanel(wx.Panel): |
41 | 48 | if sys.platform != 'win32': |
42 | 49 | book.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) |
43 | 50 | |
44 | - book.AddPage(MasksListCtrlPanel(book), _("Masks")) | |
51 | + book.AddPage(MaskPage(book), _("Masks")) | |
45 | 52 | book.AddPage(SurfacesListCtrlPanel(book), _("Surfaces")) |
46 | 53 | #book.AddPage(MeasuresListCtrlPanel(book), _("Measures")) |
47 | 54 | #book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations")) |
... | ... | @@ -56,6 +63,103 @@ class NotebookPanel(wx.Panel): |
56 | 63 | |
57 | 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 | 163 | class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
60 | 164 | |
61 | 165 | def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, |
... | ... | @@ -68,7 +172,6 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
68 | 172 | wx.ListCtrl.__init__(self, parent, ID, pos, size, style=wx.LC_REPORT) |
69 | 173 | listmix.TextEditMixin.__init__(self) |
70 | 174 | self.mask_list_index = {} |
71 | - self.mask_bmp_idx_to_name = {} | |
72 | 175 | self.__init_columns() |
73 | 176 | self.__init_image_list() |
74 | 177 | self.__bind_events_wx() |
... | ... | @@ -91,7 +194,6 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
91 | 194 | def OnCloseProject(self, pubsub_evt): |
92 | 195 | self.DeleteAllItems() |
93 | 196 | self.mask_list_index = {} |
94 | - self.mask_bmp_idx_to_name = {} | |
95 | 197 | |
96 | 198 | def OnChangeCurrentMask(self, pubsub_evt): |
97 | 199 | |
... | ... | @@ -195,6 +297,35 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
195 | 297 | image_index = self.mask_list_index[index] |
196 | 298 | self.imagelist.Replace(image_index, image) |
197 | 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 | 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 | 26 | |
27 | 27 | import data.mask as mask |
28 | 28 | import constants as const |
29 | +import gui.dialogs as dlg | |
29 | 30 | import gui.widgets.gradient as grad |
30 | 31 | import gui.widgets.foldpanelbar as fpb |
31 | 32 | import widgets.colourselect as csel |
... | ... | @@ -142,25 +143,16 @@ class InnerTaskPanel(wx.Panel): |
142 | 143 | |
143 | 144 | def OnButtonNextTask(self, evt): |
144 | 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 | 148 | (self.GetMaskSelected(), |
147 | 149 | overwrite)) |
150 | + else: | |
151 | + dlg.InexistentMask() | |
148 | 152 | |
149 | 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 | 156 | ps.Publisher().sendMessage('Create new mask', mask_name) |
165 | 157 | |
166 | 158 | if evt: |
... | ... | @@ -353,6 +345,7 @@ class MaskProperties(wx.Panel): |
353 | 345 | 'Set threshold values in gradient') |
354 | 346 | ps.Publisher().subscribe(self.SelectMaskName, 'Select mask name in combo') |
355 | 347 | ps.Publisher().subscribe(self.ChangeMaskName, 'Change mask name') |
348 | + ps.Publisher().subscribe(self.OnRemoveMasks, 'Remove masks') | |
356 | 349 | ps.Publisher().subscribe(self.OnCloseProject, 'Close project data') |
357 | 350 | ps.Publisher().subscribe(self.SetThresholdValues2, 'Set threshold values') |
358 | 351 | |
... | ... | @@ -366,7 +359,12 @@ class MaskProperties(wx.Panel): |
366 | 359 | n = self.combo_thresh.GetCount() |
367 | 360 | for i in xrange(n-1, -1, -1): |
368 | 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 | 369 | def __bind_events_wx(self): |
372 | 370 | self.Bind(grad.EVT_THRESHOLD_CHANGE, self.OnSlideChanged, self.gradient) | ... | ... |