Commit 683702208d3cdfc945400f93acd306c17f3c6442

Authored by tatiana
1 parent 9bea8413

ADD: Surface removal / new / duplicate options (under devel)

invesalius/data/surface.py
... ... @@ -125,6 +125,19 @@ class SurfaceManager():
125 125 'Create surface from largest region')
126 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 141 def OnSeedSurface(self, pubsub_evt):
129 142 """
130 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 49 book.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
50 50  
51 51 book.AddPage(MaskPage(book), _("Masks"))
52   - book.AddPage(SurfacesListCtrlPanel(book), _("Surfaces"))
  52 + book.AddPage(SurfacePage(book), _("Surfaces"))
53 53 #book.AddPage(MeasuresListCtrlPanel(book), _("Measures"))
54 54 #book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations"))
55 55  
... ... @@ -343,12 +343,141 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
343 343 self.mask_list_index = new_dict
344 344  
345 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 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 481 class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
353 482  
354 483 def __init__(self, parent, ID=-1, pos=wx.DefaultPosition,
... ... @@ -385,6 +514,21 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
385 514 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
386 515 self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEditLabel)
387 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 533 def OnCloseProject(self, pubsub_evt):
390 534 self.DeleteAllItems()
... ... @@ -400,6 +544,19 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
400 544 last_surface_index)
401 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 560 def __init_columns(self):
404 561  
405 562 self.InsertColumn(0, "", wx.LIST_FORMAT_CENTER)
... ... @@ -535,7 +692,6 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
535 692 index and value.
536 693 """
537 694 index, value = pubsub_evt.data
538   - print "EditSurfaceTransparency", index, value
539 695 self.SetStringItem(index, 3, "%d%%"%(int(value*100)))
540 696  
541 697 def EditSurfaceColour(self, pubsub_evt):
... ... @@ -547,6 +703,22 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
547 703 self.imagelist.Replace(image_index, image)
548 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 724 class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
... ...
invesalius/gui/dialogs.py
... ... @@ -336,6 +336,17 @@ def MaskSelectionRequiredForRemoval():
336 336 dlg.ShowModal()
337 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 351 def MaskSelectionRequiredForDuplication():
341 352 msg = _("No masks were selected for duplication.")
... ... @@ -349,6 +360,20 @@ def MaskSelectionRequiredForDuplication():
349 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 377 def NewMask():
353 378 import data.mask as mask
354 379 dlg = wx.TextEntryDialog(None, _('Name of new mask:'),
... ... @@ -467,7 +492,7 @@ def ShowSavePresetDialog(default_filename=&quot;raycasting&quot;):
467 492 return filename
468 493  
469 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 496 pos=wx.DefaultPosition, style=wx.DEFAULT_DIALOG_STYLE,
472 497 useMetal=False):
473 498 import constants as const
... ...
invesalius/gui/task_surface.py
... ... @@ -162,7 +162,6 @@ class InnerTaskPanel(wx.Panel):
162 162 keep_largest]
163 163  
164 164 ps.Publisher().sendMessage('Create surface', surface_data)
165   - print "TODO: Send Signal - Create 3d surface %s \n" % surface_data
166 165 dialog.Destroy()
167 166 if evt:
168 167 evt.Skip()
... ...