Commit 683702208d3cdfc945400f93acd306c17f3c6442
1 parent
9bea8413
Exists in
master
and in
6 other branches
ADD: Surface removal / new / duplicate options (under devel)
Showing
4 changed files
with
215 additions
and
6 deletions
Show diff stats
invesalius/data/surface.py
@@ -125,6 +125,19 @@ class SurfaceManager(): | @@ -125,6 +125,19 @@ class SurfaceManager(): | ||
125 | 'Create surface from largest region') | 125 | 'Create surface from largest region') |
126 | ps.Publisher().subscribe(self.OnSeedSurface, "Create surface from seeds") | 126 | ps.Publisher().subscribe(self.OnSeedSurface, "Create surface from seeds") |
127 | 127 | ||
128 | + | ||
129 | + ps.Publisher().subscribe(self.OnRemove,"Remove surfaces") | ||
130 | + | ||
131 | + | ||
132 | + def OnRemove(self, pubsub_evt): | ||
133 | + selected_items = pubsub_evt.data | ||
134 | + proj = prj.Project() | ||
135 | + for item in selected_items: | ||
136 | + proj.RemoveSurface(item) | ||
137 | + actor = self.actors_dict[item] | ||
138 | + self.actors_dict.pop(item) | ||
139 | + ps.Publisher().sendMessage('Remove surface actor from viewer', actor) | ||
140 | + | ||
128 | def OnSeedSurface(self, pubsub_evt): | 141 | def OnSeedSurface(self, pubsub_evt): |
129 | """ | 142 | """ |
130 | Create a new surface, based on the last selected surface, | 143 | Create a new surface, based on the last selected surface, |
invesalius/gui/data_notebook.py
@@ -49,7 +49,7 @@ class NotebookPanel(wx.Panel): | @@ -49,7 +49,7 @@ class NotebookPanel(wx.Panel): | ||
49 | book.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) | 49 | book.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) |
50 | 50 | ||
51 | book.AddPage(MaskPage(book), _("Masks")) | 51 | book.AddPage(MaskPage(book), _("Masks")) |
52 | - book.AddPage(SurfacesListCtrlPanel(book), _("Surfaces")) | 52 | + book.AddPage(SurfacePage(book), _("Surfaces")) |
53 | #book.AddPage(MeasuresListCtrlPanel(book), _("Measures")) | 53 | #book.AddPage(MeasuresListCtrlPanel(book), _("Measures")) |
54 | #book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations")) | 54 | #book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations")) |
55 | 55 | ||
@@ -343,12 +343,141 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -343,12 +343,141 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
343 | self.mask_list_index = new_dict | 343 | self.mask_list_index = new_dict |
344 | 344 | ||
345 | if new_dict: | 345 | if new_dict: |
346 | - self.SetItemImage(0, 1) | ||
347 | - ps.Publisher().sendMessage('Show mask', (0, 1)) | 346 | + for key in new_dict: |
347 | + if key == 0: | ||
348 | + self.SetItemImage(key, 1) | ||
349 | + ps.Publisher().sendMessage('Show mask', (key, 1)) | ||
348 | 350 | ||
349 | self.DeleteItem(index) | 351 | self.DeleteItem(index) |
350 | 352 | ||
351 | #------------------------------------------------- | 353 | #------------------------------------------------- |
354 | +class SurfacePage(wx.Panel): | ||
355 | + """ | ||
356 | + Page related to mask items. | ||
357 | + """ | ||
358 | + def __init__(self, parent): | ||
359 | + wx.Panel.__init__(self, parent, pos=wx.Point(0, 50), | ||
360 | + size=wx.Size(256, 140)) | ||
361 | + self.__init_gui() | ||
362 | + | ||
363 | + def __init_gui(self): | ||
364 | + # listctrl were existing masks will be listed | ||
365 | + self.listctrl = SurfacesListCtrlPanel(self, size=wx.Size(256, 100)) | ||
366 | + # button control with tools (eg. remove, add new, etc) | ||
367 | + self.buttonctrl = SurfaceButtonControlPanel(self) | ||
368 | + | ||
369 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
370 | + sizer.Add(self.listctrl, 0, wx.EXPAND) | ||
371 | + sizer.Add(self.buttonctrl, 0, wx.EXPAND| wx.TOP, 2) | ||
372 | + self.SetSizer(sizer) | ||
373 | + self.Fit() | ||
374 | + | ||
375 | +class SurfaceButtonControlPanel(wx.Panel): | ||
376 | + """ | ||
377 | + Button control panel that includes data notebook operations. | ||
378 | + TODO: Enhace interface with parent class - it is really messed up | ||
379 | + """ | ||
380 | + def __init__(self, parent): | ||
381 | + wx.Panel.__init__(self, parent, pos=wx.Point(0, 50), | ||
382 | + size=wx.Size(256, 22)) | ||
383 | + self.parent = parent | ||
384 | + self.__init_gui() | ||
385 | + | ||
386 | + def __init_gui(self): | ||
387 | + | ||
388 | + # Bitmaps to be used in plate buttons | ||
389 | + BMP_NEW = wx.Bitmap("../icons/data_new.png", | ||
390 | + wx.BITMAP_TYPE_PNG) | ||
391 | + BMP_REMOVE = wx.Bitmap("../icons/data_remove.png", | ||
392 | + wx.BITMAP_TYPE_PNG) | ||
393 | + BMP_DUPLICATE = wx.Bitmap("../icons/data_duplicate.png", | ||
394 | + wx.BITMAP_TYPE_PNG) | ||
395 | + | ||
396 | + # Plate buttons based on previous bitmaps | ||
397 | + button_style = pbtn.PB_STYLE_SQUARE | pbtn.PB_STYLE_DEFAULT | ||
398 | + button_new = pbtn.PlateButton(self, BTN_NEW, "", | ||
399 | + BMP_NEW, | ||
400 | + style=button_style, | ||
401 | + size = wx.Size(18, 18)) | ||
402 | + button_remove = pbtn.PlateButton(self, BTN_REMOVE, "", | ||
403 | + BMP_REMOVE, | ||
404 | + style=button_style, | ||
405 | + size = wx.Size(18, 18)) | ||
406 | + button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "", | ||
407 | + BMP_DUPLICATE, | ||
408 | + style=button_style, | ||
409 | + size = wx.Size(18, 18)) | ||
410 | + | ||
411 | + # Add all controls to gui | ||
412 | + sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
413 | + sizer.Add(button_new, 0, wx.GROW|wx.EXPAND|wx.LEFT) | ||
414 | + sizer.Add(button_remove, 0, wx.GROW|wx.EXPAND) | ||
415 | + sizer.Add(button_duplicate, 0, wx.GROW|wx.EXPAND) | ||
416 | + self.SetSizer(sizer) | ||
417 | + self.Fit() | ||
418 | + | ||
419 | + # Bindings | ||
420 | + self.Bind(wx.EVT_BUTTON, self.OnButton) | ||
421 | + | ||
422 | + def OnButton(self, evt): | ||
423 | + id = evt.GetId() | ||
424 | + if id == BTN_NEW: | ||
425 | + self.OnNew() | ||
426 | + elif id == BTN_REMOVE: | ||
427 | + self.OnRemove() | ||
428 | + elif id == BTN_DUPLICATE: | ||
429 | + self.OnDuplicate() | ||
430 | + | ||
431 | + def OnNew(self): | ||
432 | + import project as prj | ||
433 | + | ||
434 | + dialog = dlg.NewSurfaceDialog() | ||
435 | + try: | ||
436 | + if dialog.ShowModal() == wx.ID_OK: | ||
437 | + ok = 1 | ||
438 | + else: | ||
439 | + ok = 0 | ||
440 | + except(wx._core.PyAssertionError): #TODO FIX: win64 | ||
441 | + ok = 1 | ||
442 | + | ||
443 | + if ok: | ||
444 | + (mask_index, surface_name, surface_quality, fill_holes,\ | ||
445 | + keep_largest) = dialog.GetValue() | ||
446 | + | ||
447 | + # Retrieve information from mask | ||
448 | + proj = prj.Project() | ||
449 | + mask = proj.mask_dict[mask_index] | ||
450 | + | ||
451 | + # Send all information so surface can be created | ||
452 | + surface_data = [proj.imagedata, | ||
453 | + mask.colour, | ||
454 | + mask.threshold_range, | ||
455 | + mask.edited_points, | ||
456 | + False, # overwrite | ||
457 | + surface_name, | ||
458 | + surface_quality, | ||
459 | + fill_holes, | ||
460 | + keep_largest] | ||
461 | + | ||
462 | + ps.Publisher().sendMessage('Create surface', surface_data) | ||
463 | + | ||
464 | + def OnRemove(self): | ||
465 | + selected_items = self.parent.listctrl.GetSelected() | ||
466 | + if selected_items: | ||
467 | + for item in selected_items: | ||
468 | + self.parent.listctrl.RemoveSurface(item) | ||
469 | + ps.Publisher().sendMessage('Remove surfaces', selected_items) | ||
470 | + else: | ||
471 | + dlg.SurfaceSelectionRequiredForRemoval() | ||
472 | + | ||
473 | + def OnDuplicate(self): | ||
474 | + selected_items = self.parent.listctrl.GetSelected() | ||
475 | + if selected_items: | ||
476 | + ps.Publisher().sendMessage('Duplicate surfaces', selected_items) | ||
477 | + else: | ||
478 | + dlg.SurfaceSelectionRequiredForDuplication() | ||
479 | + | ||
480 | + | ||
352 | class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | 481 | class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
353 | 482 | ||
354 | def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, | 483 | def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, |
@@ -385,6 +514,21 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -385,6 +514,21 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
385 | self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) | 514 | self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) |
386 | self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEditLabel) | 515 | self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEditLabel) |
387 | self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected_) | 516 | self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected_) |
517 | + self.Bind(wx.EVT_KEY_UP, self.OnKeyEvent) | ||
518 | + | ||
519 | + | ||
520 | + def OnKeyEvent(self, event): | ||
521 | + keycode = event.GetKeyCode() | ||
522 | + # Delete key | ||
523 | + if (sys.platform == 'darwin') and (keycode == wx.WXK_BACK): | ||
524 | + selected = self.GetSelected() | ||
525 | + for item in selected: | ||
526 | + self.RemoveSurface(item) | ||
527 | + elif (keycode == wx.WXK_DELETE): | ||
528 | + selected = self.GetSelected() | ||
529 | + for item in selected: | ||
530 | + self.RemoveSurface(item) | ||
531 | + | ||
388 | 532 | ||
389 | def OnCloseProject(self, pubsub_evt): | 533 | def OnCloseProject(self, pubsub_evt): |
390 | self.DeleteAllItems() | 534 | self.DeleteAllItems() |
@@ -400,6 +544,19 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -400,6 +544,19 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
400 | last_surface_index) | 544 | last_surface_index) |
401 | evt.Skip() | 545 | evt.Skip() |
402 | 546 | ||
547 | + def GetSelected(self): | ||
548 | + """ | ||
549 | + Return all items selected (highlighted). | ||
550 | + """ | ||
551 | + selected = [] | ||
552 | + for index in self.surface_list_index: | ||
553 | + if self.IsSelected(index): | ||
554 | + selected.append(index) | ||
555 | + # it is important to revert items order, so | ||
556 | + # listctrl update is ok | ||
557 | + selected.sort(reverse=True) | ||
558 | + return selected | ||
559 | + | ||
403 | def __init_columns(self): | 560 | def __init_columns(self): |
404 | 561 | ||
405 | self.InsertColumn(0, "", wx.LIST_FORMAT_CENTER) | 562 | self.InsertColumn(0, "", wx.LIST_FORMAT_CENTER) |
@@ -535,7 +692,6 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -535,7 +692,6 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
535 | index and value. | 692 | index and value. |
536 | """ | 693 | """ |
537 | index, value = pubsub_evt.data | 694 | index, value = pubsub_evt.data |
538 | - print "EditSurfaceTransparency", index, value | ||
539 | self.SetStringItem(index, 3, "%d%%"%(int(value*100))) | 695 | self.SetStringItem(index, 3, "%d%%"%(int(value*100))) |
540 | 696 | ||
541 | def EditSurfaceColour(self, pubsub_evt): | 697 | def EditSurfaceColour(self, pubsub_evt): |
@@ -547,6 +703,22 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | @@ -547,6 +703,22 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | ||
547 | self.imagelist.Replace(image_index, image) | 703 | self.imagelist.Replace(image_index, image) |
548 | self.Refresh() | 704 | self.Refresh() |
549 | 705 | ||
706 | + def RemoveSurface(self, index): | ||
707 | + """ | ||
708 | + Remove item given its index. | ||
709 | + """ | ||
710 | + # it is necessary to update internal dictionary | ||
711 | + # that maps bitmap given item index | ||
712 | + old_dict = self.surface_list_index | ||
713 | + new_dict = {} | ||
714 | + for i in old_dict: | ||
715 | + if i < index: | ||
716 | + new_dict[i] = old_dict[i] | ||
717 | + if i > index: | ||
718 | + new_dict[i-1] = old_dict[i] | ||
719 | + self.surface_list_index = new_dict | ||
720 | + self.DeleteItem(index) | ||
721 | + | ||
550 | #------------------------------------------------- | 722 | #------------------------------------------------- |
551 | 723 | ||
552 | class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): | 724 | class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): |
invesalius/gui/dialogs.py
@@ -336,6 +336,17 @@ def MaskSelectionRequiredForRemoval(): | @@ -336,6 +336,17 @@ def MaskSelectionRequiredForRemoval(): | ||
336 | dlg.ShowModal() | 336 | dlg.ShowModal() |
337 | dlg.Destroy() | 337 | dlg.Destroy() |
338 | 338 | ||
339 | +def SurfaceSelectionRequiredForRemoval(): | ||
340 | + msg = _("No surfaces were selected for removal.") | ||
341 | + if sys.platform == 'darwin': | ||
342 | + dlg = wx.MessageDialog(None, "", msg, | ||
343 | + wx.ICON_INFORMATION | wx.OK) | ||
344 | + else: | ||
345 | + dlg = wx.MessageDialog(None, msg, "InVesalius 3", | ||
346 | + wx.ICON_INFORMATION | wx.OK) | ||
347 | + dlg.ShowModal() | ||
348 | + dlg.Destroy() | ||
349 | + | ||
339 | 350 | ||
340 | def MaskSelectionRequiredForDuplication(): | 351 | def MaskSelectionRequiredForDuplication(): |
341 | msg = _("No masks were selected for duplication.") | 352 | msg = _("No masks were selected for duplication.") |
@@ -349,6 +360,20 @@ def MaskSelectionRequiredForDuplication(): | @@ -349,6 +360,20 @@ def MaskSelectionRequiredForDuplication(): | ||
349 | dlg.Destroy() | 360 | dlg.Destroy() |
350 | 361 | ||
351 | 362 | ||
363 | + | ||
364 | +def SurfaceSelectionRequiredForDuplication(): | ||
365 | + msg = _("No surfaces were selected for duplication.") | ||
366 | + if sys.platform == 'darwin': | ||
367 | + dlg = wx.MessageDialog(None, "", msg, | ||
368 | + wx.ICON_INFORMATION | wx.OK) | ||
369 | + else: | ||
370 | + dlg = wx.MessageDialog(None, msg, "InVesalius 3", | ||
371 | + wx.ICON_INFORMATION | wx.OK) | ||
372 | + dlg.ShowModal() | ||
373 | + dlg.Destroy() | ||
374 | + | ||
375 | + | ||
376 | + | ||
352 | def NewMask(): | 377 | def NewMask(): |
353 | import data.mask as mask | 378 | import data.mask as mask |
354 | dlg = wx.TextEntryDialog(None, _('Name of new mask:'), | 379 | dlg = wx.TextEntryDialog(None, _('Name of new mask:'), |
@@ -467,7 +492,7 @@ def ShowSavePresetDialog(default_filename="raycasting"): | @@ -467,7 +492,7 @@ def ShowSavePresetDialog(default_filename="raycasting"): | ||
467 | return filename | 492 | return filename |
468 | 493 | ||
469 | class NewSurfaceDialog(wx.Dialog): | 494 | class NewSurfaceDialog(wx.Dialog): |
470 | - def __init__(self, parent, ID, title, size=wx.DefaultSize, | 495 | + def __init__(self, parent=None, ID=-1, title="InVesalius 3", size=wx.DefaultSize, |
471 | pos=wx.DefaultPosition, style=wx.DEFAULT_DIALOG_STYLE, | 496 | pos=wx.DefaultPosition, style=wx.DEFAULT_DIALOG_STYLE, |
472 | useMetal=False): | 497 | useMetal=False): |
473 | import constants as const | 498 | import constants as const |
invesalius/gui/task_surface.py
@@ -162,7 +162,6 @@ class InnerTaskPanel(wx.Panel): | @@ -162,7 +162,6 @@ class InnerTaskPanel(wx.Panel): | ||
162 | keep_largest] | 162 | keep_largest] |
163 | 163 | ||
164 | ps.Publisher().sendMessage('Create surface', surface_data) | 164 | ps.Publisher().sendMessage('Create surface', surface_data) |
165 | - print "TODO: Send Signal - Create 3d surface %s \n" % surface_data | ||
166 | dialog.Destroy() | 165 | dialog.Destroy() |
167 | if evt: | 166 | if evt: |
168 | evt.Skip() | 167 | evt.Skip() |