From 3d07ca3411f5f3ff484fd80ffc674478d0c7ade7 Mon Sep 17 00:00:00 2001 From: tatiana Date: Thu, 4 Mar 2010 13:45:51 +0000 Subject: [PATCH] ENH: Notebook page related to measurements / pubsub evt --- invesalius/data/viewer_volume.py | 27 +++++++++++++++++++++++---- invesalius/gui/data_notebook.py | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------- 2 files changed, 324 insertions(+), 50 deletions(-) diff --git a/invesalius/data/viewer_volume.py b/invesalius/data/viewer_volume.py index a2d9066..fc8ba0e 100755 --- a/invesalius/data/viewer_volume.py +++ b/invesalius/data/viewer_volume.py @@ -636,8 +636,18 @@ class Viewer(wx.Panel): m = self.measures[-1] m.AddPoint(x, y, z) if m.IsComplete(): - ps.Publisher().sendMessage("Add measure to list", - (u"3D", _(u"%.3f mm" % m.GetValue()))) + index = len(self.measures) - 1 + name = "M" + colour = m.colour + type_ = _("Linear") + location = u"3D" + value = u"%.2f mm"% m.GetValue() + + msg = 'Update measurement info in GUI', + ps.Publisher().sendMessage(msg, + (index, name, colour, + type_, location, + value)) self.interactor.Render() def OnInsertAngularMeasurePoint(self, obj, evt): @@ -654,8 +664,17 @@ class Viewer(wx.Panel): m = self.measures[-1] m.AddPoint(x, y, z) if m.IsComplete(): - ps.Publisher().sendMessage("Add measure to list", - (u"3D", _(u"%.3fº" % m.GetValue()))) + index = len(self.measures) - 1 + name = "M" + colour = m.colour + type_ = _("Angular") + location = u"3D" + value = u"%.2f˚"% m.GetValue() + msg = 'Update measurement info in GUI', + ps.Publisher().sendMessage(msg, + (index, name, colour, + type_, location, + value)) self.interactor.Render() diff --git a/invesalius/gui/data_notebook.py b/invesalius/gui/data_notebook.py index 04affc2..81332b6 100644 --- a/invesalius/gui/data_notebook.py +++ b/invesalius/gui/data_notebook.py @@ -48,12 +48,10 @@ class NotebookPanel(wx.Panel): if sys.platform != 'win32': book.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) - self.measures_list = MeasuresListCtrlPanel(book) - book.AddPage(MaskPage(book), _("Masks")) book.AddPage(SurfacePage(book), _("Surfaces")) - book.AddPage(self.measures_list, _("Measures")) - book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations")) + book.AddPage(MeasurePage(book), _("Measures")) + #book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations")) book.SetSelection(0) @@ -63,19 +61,111 @@ class NotebookPanel(wx.Panel): book.Refresh() - self.__bind_events() + #def __bind_events(self): + # ps.Publisher().subscribe(self._add_measure, + # "Add measure to list") + + #def _add_measure(self, pubsub_evt): + # type = pubsub_evt.data[0] + # value = pubsub_evt.data[1] + # self.measures_list.AddMeasure(type, value) + + + +class MeasurePage(wx.Panel): + """ + Page related to mask items. + """ + def __init__(self, parent): + wx.Panel.__init__(self, parent, pos=wx.Point(0, 50), + size=wx.Size(256, 140)) + self.__init_gui() + + def __init_gui(self): + # listctrl were existing masks will be listed + self.listctrl = MeasuresListCtrlPanel(self, size=wx.Size(256, 100)) + # button control with tools (eg. remove, add new, etc) + self.buttonctrl = MeasureButtonControlPanel(self) + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self.listctrl, 0, wx.EXPAND) + sizer.Add(self.buttonctrl, 0, wx.EXPAND| wx.TOP, 2) + self.SetSizer(sizer) + self.Fit() + + + + +class MeasureButtonControlPanel(wx.Panel): + """ + Button control panel that includes data notebook operations. + TODO: Enhace interface with parent class - it is really messed up + """ + def __init__(self, parent): + wx.Panel.__init__(self, parent, pos=wx.Point(0, 50), + size=wx.Size(256, 22)) + self.parent = parent + self.__init_gui() + + def __init_gui(self): - # TODO: insert icons bellow notebook + # Bitmaps to be used in plate buttons + BMP_NEW = wx.Bitmap("../icons/data_new.png", + wx.BITMAP_TYPE_PNG) + BMP_REMOVE = wx.Bitmap("../icons/data_remove.png", + wx.BITMAP_TYPE_PNG) + BMP_DUPLICATE = wx.Bitmap("../icons/data_duplicate.png", + wx.BITMAP_TYPE_PNG) - def __bind_events(self): - ps.Publisher().subscribe(self._add_measure, - "Add measure to list") + # Plate buttons based on previous bitmaps + button_style = pbtn.PB_STYLE_SQUARE | pbtn.PB_STYLE_DEFAULT + button_new = pbtn.PlateButton(self, BTN_NEW, "", + BMP_NEW, + style=button_style, + size = wx.Size(18, 18)) + button_remove = pbtn.PlateButton(self, BTN_REMOVE, "", + BMP_REMOVE, + style=button_style, + size = wx.Size(18, 18)) + button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "", + BMP_DUPLICATE, + style=button_style, + size = wx.Size(18, 18)) - def _add_measure(self, pubsub_evt): - type = pubsub_evt.data[0] - value = pubsub_evt.data[1] + # Add all controls to gui + sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(button_new, 0, wx.GROW|wx.EXPAND|wx.LEFT) + sizer.Add(button_remove, 0, wx.GROW|wx.EXPAND) + sizer.Add(button_duplicate, 0, wx.GROW|wx.EXPAND) + self.SetSizer(sizer) + self.Fit() - self.measures_list.AddMeasure(type, value) + # Bindings + self.Bind(wx.EVT_BUTTON, self.OnButton) + + def OnButton(self, evt): + id = evt.GetId() + if id == BTN_NEW: + self.OnNew() + elif id == BTN_REMOVE: + self.OnRemove() + elif id == BTN_DUPLICATE: + self.OnDuplicate() + + def OnNew(self): + mask_name = dlg.NewMask() + if mask_name: + ps.Publisher().sendMessage('Set measurement state', mask_name) + + def OnRemove(self): + self.parent.listctrl.RemoveMasks() + + def OnDuplicate(self): + selected_items = self.parent.listctrl.GetSelected() + if selected_items: + ps.Publisher().sendMessage('Duplicate measurement', selected_items) + else: + dlg.MaskSelectionRequiredForDuplication() @@ -300,7 +390,8 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): self.image_gray = Image.open("../icons/object_colour.jpg") def OnEditLabel(self, evt): - ps.Publisher().sendMessage('Change mask name', (evt.GetIndex(), evt.GetLabel())) + ps.Publisher().sendMessage('Change mask name', + (evt.GetIndex(), evt.GetLabel())) evt.Skip() def OnItemActivated(self, evt): @@ -641,7 +732,6 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): def OnItemActivated(self, evt): self.ToggleItem(evt.m_itemIndex) - #ps.Publisher().sendMessage('Change surface selected',index) evt.Skip() @@ -752,7 +842,7 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): #------------------------------------------------- class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): - # TODO: Change edimixin to affect third column also + def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.LC_REPORT): @@ -766,66 +856,231 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): self.__init_columns() self.__init_image_list() self.__init_evt() + self.__bind_events_wx() + self._list_index = {} + self._bmp_idx_to_name = {} - self._last_measure = 0 - def __init_evt(self): + ps.Publisher().subscribe(self.AddItem_, + 'Update measurement info in GUI') + ps.Publisher().subscribe(self.EditItemColour, + 'Set measurement colour') + ps.Publisher().subscribe(self.OnCloseProject, 'Close project data') + ps.Publisher().subscribe(self.OnShowSingle, 'Show single measurement') + ps.Publisher().subscribe(self.OnShowMultiple, 'Show multiple measurements') + + def __bind_events_wx(self): self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated) + self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEditLabel) + self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected_) + self.Bind(wx.EVT_KEY_UP, self.OnKeyEvent) + + + def OnKeyEvent(self, event): + keycode = event.GetKeyCode() + # Delete key + if (sys.platform == 'darwin') and (keycode == wx.WXK_BACK): + self.RemoveMeasurements() + elif (keycode == wx.WXK_DELETE): + self.RemoveMeasurements() + + def RemoveMeasurements(self): + """ + Remove item given its index. + """ + # it is necessary to update internal dictionary + # that maps bitmap given item index + selected_items = self.GetSelected() + old_dict = self._list_index + new_dict = {} + if selected_items: + for index in selected_items: + self.DeleteItem(index) + for i in old_dict: + if i < index: + new_dict[i] = old_dict[i] + if i > index: + new_dict[i-1] = old_dict[i] + old_dict = new_dict + self._list_index = new_dict + + ps.Publisher().sendMessage('Remove measurements', selected_items) + else: + dlg.SurfaceSelectionRequiredForRemoval() + + + def OnCloseProject(self, pubsub_evt): + self.DeleteAllItems() + self._list_index = {} + self._bmp_idx_to_name = {} + + def OnItemSelected_(self, evt): + # Note: DON'T rename to OnItemSelected!!! + # Otherwise the parent's method will be overwritten and other + # things will stop working, e.g.: OnCheckItem + + last_index = evt.m_itemIndex + ps.Publisher().sendMessage('Change measurement selected', + last_index) + evt.Skip() + + def GetSelected(self): + """ + Return all items selected (highlighted). + """ + selected = [] + for index in self._list_index: + if self.IsSelected(index): + selected.append(index) + # it is important to revert items order, so + # listctrl update is ok + selected.sort(reverse=True) + + return selected def __init_columns(self): - + self.InsertColumn(0, "", wx.LIST_FORMAT_CENTER) - self.InsertColumn(1, "Value") - self.InsertColumn(2, "Type", wx.LIST_FORMAT_RIGHT) - - self.SetColumnWidth(0, 20) - self.SetColumnWidth(1, 120) - self.SetColumnWidth(2, 50) + self.InsertColumn(1, _("Name")) + self.InsertColumn(2, _("Location")) + self.InsertColumn(3, _("Type")) + self.InsertColumn(4, _("Value"), wx.LIST_FORMAT_RIGHT) + self.SetColumnWidth(0, 15) + self.SetColumnWidth(1, 70) + self.SetColumnWidth(2, 55) + self.SetColumnWidth(3, 50) + self.SetColumnWidth(4, 75) + def __init_image_list(self): self.imagelist = wx.ImageList(16, 16) - image = wx.Image("../icons/object_visible.jpg") - bitmap = wx.BitmapFromImage(image.Scale(16, 16)) - bitmap.SetWidth(16) - bitmap.SetHeight(16) - img_check = self.imagelist.Add(bitmap) - image = wx.Image("../icons/object_invisible.jpg") bitmap = wx.BitmapFromImage(image.Scale(16, 16)) bitmap.SetWidth(16) bitmap.SetHeight(16) img_null = self.imagelist.Add(bitmap) - image = wx.Image("../icons/object_colour.jpg") + image = wx.Image("../icons/object_visible.jpg") bitmap = wx.BitmapFromImage(image.Scale(16, 16)) bitmap.SetWidth(16) bitmap.SetHeight(16) - self.img_colour = self.imagelist.Add(bitmap) + img_check = self.imagelist.Add(bitmap) self.SetImageList(self.imagelist,wx.IMAGE_LIST_SMALL) - + + self.image_gray = Image.open("../icons/object_colour.jpg") + + + def OnEditLabel(self, evt): + ps.Publisher().sendMessage('Change measurement name', (evt.GetIndex(), evt.GetLabel())) + evt.Skip() def OnItemActivated(self, evt): self.ToggleItem(evt.m_itemIndex) + evt.Skip() + def OnCheckItem(self, index, flag): - # TODO: use pubsub to communicate to models - if flag: - print "checked, ", index - else: - print "unchecked, ", index + ps.Publisher().sendMessage('Show measurement', (index, flag)) - def InsertNewItem(self, index=0, type_="", value="(1000, 4500)", - colour=None): + def OnShowSingle(self, pubsub_evt): + index, visibility = pubsub_evt.data + for key in self._list_index.keys(): + if key != index: + self.SetItemImage(key, not visibility) + ps.Publisher().sendMessage('Show measurement', + (key, not visibility)) + self.SetItemImage(index, visibility) + ps.Publisher().sendMessage('Show measurement', + (index, visibility)) + + def OnShowMultiple(self, pubsub_evt): + index_list, visibility = pubsub_evt.data + for key in self._list_index.keys(): + if key not in index_list: + self.SetItemImage(key, not visibility) + ps.Publisher().sendMessage('Show measurement', + (key, not visibility)) + for index in index_list: + self.SetItemImage(index, visibility) + ps.Publisher().sendMessage('Show measurement', + (index, visibility)) + + def AddItem_(self, pubsub_evt): + index = pubsub_evt.data[0] + name = pubsub_evt.data[1] + colour = pubsub_evt.data[2] + type_ = pubsub_evt.data[3] + location = pubsub_evt.data[4] + value = pubsub_evt.data[5] + + + if index not in self._list_index: + image = self.CreateColourBitmap(colour) + image_index = self.imagelist.Add(image) + + index_list = self._list_index.keys() + self._list_index[index] = image_index + + if (index in index_list) and index_list: + self.UpdateItemInfo(index, name, colour, type_, location, value) + else: + self.InsertNewItem(index, name, colour, type_, location, value) + + + + def InsertNewItem(self, index=0, label="Measurement 1", colour=None, + type_="LINEAR", location="SURFACE", value="0 mm"): self.InsertStringItem(index, "") - self.SetStringItem(index, 1, type_, imageId = self.img_colour) - self.SetStringItem(index, 2, value) + self.SetStringItem(index, 1, label, + imageId = self._list_index[index]) + self.SetStringItem(index, 2, type_) + self.SetStringItem(index, 3, location) + self.SetStringItem(index, 4, value) + self.SetItemImage(index, 1) + + def UpdateItemInfo(self, index=0, label="Measurement 1", colour=None, + type_="LINEAR", location="SURFACE", value="0 mm"): + self.SetStringItem(index, 1, label, + imageId = self._list_index[index]) + self.SetStringItem(index, 2, type_) + self.SetStringItem(index, 3, location) + self.SetStringItem(index, 4, value) + self.SetItemImage(index, 1) + + + def CreateColourBitmap(self, colour): + """ + Create a wx Image with a mask colour. + colour: colour in rgb format(0 - 1) + """ + image = self.image_gray + new_image = Image.new("RGB", image.size) + for x in xrange(image.size[0]): + for y in xrange(image.size[1]): + pixel_colour = [int(i*image.getpixel((x,y))) + for i in colour] + new_image.putpixel((x,y), tuple(pixel_colour)) + + wx_image = wx.EmptyImage(new_image.size[0], + new_image.size[1]) + wx_image.SetData(new_image.tostring()) + return wx.BitmapFromImage(wx_image.Scale(16, 16)) + + def EditItemColour(self, pubsub_evt): + """ + """ + index, colour = pubsub_evt.data + image = self.CreateColourBitmap(colour) + image_index = self._list_index[index] + self.imagelist.Replace(image_index, image) + self.Refresh() + - def AddMeasure(self, type_, value, colour=None): - self.InsertNewItem(self._last_measure, type_, value, colour) - self._last_measure += 1 +#******************************************************************* +#******************************************************************* class AnnotationsListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin): -- libgit2 0.21.2