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 | 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="raycasting"): |
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