Commit 3d07ca3411f5f3ff484fd80ffc674478d0c7ade7

Authored by tatiana
1 parent 9076ea01

ENH: Notebook page related to measurements / pubsub evt

invesalius/data/viewer_volume.py
... ... @@ -636,8 +636,18 @@ class Viewer(wx.Panel):
636 636 m = self.measures[-1]
637 637 m.AddPoint(x, y, z)
638 638 if m.IsComplete():
639   - ps.Publisher().sendMessage("Add measure to list",
640   - (u"3D", _(u"%.3f mm" % m.GetValue())))
  639 + index = len(self.measures) - 1
  640 + name = "M"
  641 + colour = m.colour
  642 + type_ = _("Linear")
  643 + location = u"3D"
  644 + value = u"%.2f mm"% m.GetValue()
  645 +
  646 + msg = 'Update measurement info in GUI',
  647 + ps.Publisher().sendMessage(msg,
  648 + (index, name, colour,
  649 + type_, location,
  650 + value))
641 651 self.interactor.Render()
642 652  
643 653 def OnInsertAngularMeasurePoint(self, obj, evt):
... ... @@ -654,8 +664,17 @@ class Viewer(wx.Panel):
654 664 m = self.measures[-1]
655 665 m.AddPoint(x, y, z)
656 666 if m.IsComplete():
657   - ps.Publisher().sendMessage("Add measure to list",
658   - (u"3D", _(u"%.3fº" % m.GetValue())))
  667 + index = len(self.measures) - 1
  668 + name = "M"
  669 + colour = m.colour
  670 + type_ = _("Angular")
  671 + location = u"3D"
  672 + value = u"%.2f˚"% m.GetValue()
  673 + msg = 'Update measurement info in GUI',
  674 + ps.Publisher().sendMessage(msg,
  675 + (index, name, colour,
  676 + type_, location,
  677 + value))
659 678 self.interactor.Render()
660 679  
661 680  
... ...
invesalius/gui/data_notebook.py
... ... @@ -48,12 +48,10 @@ class NotebookPanel(wx.Panel):
48 48 if sys.platform != 'win32':
49 49 book.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
50 50  
51   - self.measures_list = MeasuresListCtrlPanel(book)
52   -
53 51 book.AddPage(MaskPage(book), _("Masks"))
54 52 book.AddPage(SurfacePage(book), _("Surfaces"))
55   - book.AddPage(self.measures_list, _("Measures"))
56   - book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations"))
  53 + book.AddPage(MeasurePage(book), _("Measures"))
  54 + #book.AddPage(AnnotationsListCtrlPanel(book), _("Annotations"))
57 55  
58 56 book.SetSelection(0)
59 57  
... ... @@ -63,19 +61,111 @@ class NotebookPanel(wx.Panel):
63 61  
64 62 book.Refresh()
65 63  
66   - self.__bind_events()
  64 + #def __bind_events(self):
  65 + # ps.Publisher().subscribe(self._add_measure,
  66 + # "Add measure to list")
  67 +
  68 + #def _add_measure(self, pubsub_evt):
  69 + # type = pubsub_evt.data[0]
  70 + # value = pubsub_evt.data[1]
  71 + # self.measures_list.AddMeasure(type, value)
  72 +
  73 +
  74 +
  75 +class MeasurePage(wx.Panel):
  76 + """
  77 + Page related to mask items.
  78 + """
  79 + def __init__(self, parent):
  80 + wx.Panel.__init__(self, parent, pos=wx.Point(0, 50),
  81 + size=wx.Size(256, 140))
  82 + self.__init_gui()
  83 +
  84 + def __init_gui(self):
  85 + # listctrl were existing masks will be listed
  86 + self.listctrl = MeasuresListCtrlPanel(self, size=wx.Size(256, 100))
  87 + # button control with tools (eg. remove, add new, etc)
  88 + self.buttonctrl = MeasureButtonControlPanel(self)
  89 +
  90 + sizer = wx.BoxSizer(wx.VERTICAL)
  91 + sizer.Add(self.listctrl, 0, wx.EXPAND)
  92 + sizer.Add(self.buttonctrl, 0, wx.EXPAND| wx.TOP, 2)
  93 + self.SetSizer(sizer)
  94 + self.Fit()
  95 +
  96 +
  97 +
  98 +
  99 +class MeasureButtonControlPanel(wx.Panel):
  100 + """
  101 + Button control panel that includes data notebook operations.
  102 + TODO: Enhace interface with parent class - it is really messed up
  103 + """
  104 + def __init__(self, parent):
  105 + wx.Panel.__init__(self, parent, pos=wx.Point(0, 50),
  106 + size=wx.Size(256, 22))
  107 + self.parent = parent
  108 + self.__init_gui()
  109 +
  110 + def __init_gui(self):
67 111  
68   - # TODO: insert icons bellow notebook
  112 + # Bitmaps to be used in plate buttons
  113 + BMP_NEW = wx.Bitmap("../icons/data_new.png",
  114 + wx.BITMAP_TYPE_PNG)
  115 + BMP_REMOVE = wx.Bitmap("../icons/data_remove.png",
  116 + wx.BITMAP_TYPE_PNG)
  117 + BMP_DUPLICATE = wx.Bitmap("../icons/data_duplicate.png",
  118 + wx.BITMAP_TYPE_PNG)
69 119  
70   - def __bind_events(self):
71   - ps.Publisher().subscribe(self._add_measure,
72   - "Add measure to list")
  120 + # Plate buttons based on previous bitmaps
  121 + button_style = pbtn.PB_STYLE_SQUARE | pbtn.PB_STYLE_DEFAULT
  122 + button_new = pbtn.PlateButton(self, BTN_NEW, "",
  123 + BMP_NEW,
  124 + style=button_style,
  125 + size = wx.Size(18, 18))
  126 + button_remove = pbtn.PlateButton(self, BTN_REMOVE, "",
  127 + BMP_REMOVE,
  128 + style=button_style,
  129 + size = wx.Size(18, 18))
  130 + button_duplicate = pbtn.PlateButton(self, BTN_DUPLICATE, "",
  131 + BMP_DUPLICATE,
  132 + style=button_style,
  133 + size = wx.Size(18, 18))
73 134  
74   - def _add_measure(self, pubsub_evt):
75   - type = pubsub_evt.data[0]
76   - value = pubsub_evt.data[1]
  135 + # Add all controls to gui
  136 + sizer = wx.BoxSizer(wx.HORIZONTAL)
  137 + sizer.Add(button_new, 0, wx.GROW|wx.EXPAND|wx.LEFT)
  138 + sizer.Add(button_remove, 0, wx.GROW|wx.EXPAND)
  139 + sizer.Add(button_duplicate, 0, wx.GROW|wx.EXPAND)
  140 + self.SetSizer(sizer)
  141 + self.Fit()
77 142  
78   - self.measures_list.AddMeasure(type, value)
  143 + # Bindings
  144 + self.Bind(wx.EVT_BUTTON, self.OnButton)
  145 +
  146 + def OnButton(self, evt):
  147 + id = evt.GetId()
  148 + if id == BTN_NEW:
  149 + self.OnNew()
  150 + elif id == BTN_REMOVE:
  151 + self.OnRemove()
  152 + elif id == BTN_DUPLICATE:
  153 + self.OnDuplicate()
  154 +
  155 + def OnNew(self):
  156 + mask_name = dlg.NewMask()
  157 + if mask_name:
  158 + ps.Publisher().sendMessage('Set measurement state', mask_name)
  159 +
  160 + def OnRemove(self):
  161 + self.parent.listctrl.RemoveMasks()
  162 +
  163 + def OnDuplicate(self):
  164 + selected_items = self.parent.listctrl.GetSelected()
  165 + if selected_items:
  166 + ps.Publisher().sendMessage('Duplicate measurement', selected_items)
  167 + else:
  168 + dlg.MaskSelectionRequiredForDuplication()
79 169  
80 170  
81 171  
... ... @@ -300,7 +390,8 @@ class MasksListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
300 390 self.image_gray = Image.open("../icons/object_colour.jpg")
301 391  
302 392 def OnEditLabel(self, evt):
303   - ps.Publisher().sendMessage('Change mask name', (evt.GetIndex(), evt.GetLabel()))
  393 + ps.Publisher().sendMessage('Change mask name',
  394 + (evt.GetIndex(), evt.GetLabel()))
304 395 evt.Skip()
305 396  
306 397 def OnItemActivated(self, evt):
... ... @@ -641,7 +732,6 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
641 732  
642 733 def OnItemActivated(self, evt):
643 734 self.ToggleItem(evt.m_itemIndex)
644   - #ps.Publisher().sendMessage('Change surface selected',index)
645 735 evt.Skip()
646 736  
647 737  
... ... @@ -752,7 +842,7 @@ class SurfacesListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
752 842 #-------------------------------------------------
753 843  
754 844 class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
755   - # TODO: Change edimixin to affect third column also
  845 +
756 846 def __init__(self, parent, ID=-1, pos=wx.DefaultPosition,
757 847 size=wx.DefaultSize, style=wx.LC_REPORT):
758 848  
... ... @@ -766,66 +856,231 @@ class MeasuresListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
766 856 self.__init_columns()
767 857 self.__init_image_list()
768 858 self.__init_evt()
  859 + self.__bind_events_wx()
  860 + self._list_index = {}
  861 + self._bmp_idx_to_name = {}
769 862  
770   - self._last_measure = 0
771   -
772 863 def __init_evt(self):
  864 + ps.Publisher().subscribe(self.AddItem_,
  865 + 'Update measurement info in GUI')
  866 + ps.Publisher().subscribe(self.EditItemColour,
  867 + 'Set measurement colour')
  868 + ps.Publisher().subscribe(self.OnCloseProject, 'Close project data')
  869 + ps.Publisher().subscribe(self.OnShowSingle, 'Show single measurement')
  870 + ps.Publisher().subscribe(self.OnShowMultiple, 'Show multiple measurements')
  871 +
  872 + def __bind_events_wx(self):
773 873 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
  874 + self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEditLabel)
  875 + self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected_)
  876 + self.Bind(wx.EVT_KEY_UP, self.OnKeyEvent)
  877 +
  878 +
  879 + def OnKeyEvent(self, event):
  880 + keycode = event.GetKeyCode()
  881 + # Delete key
  882 + if (sys.platform == 'darwin') and (keycode == wx.WXK_BACK):
  883 + self.RemoveMeasurements()
  884 + elif (keycode == wx.WXK_DELETE):
  885 + self.RemoveMeasurements()
  886 +
  887 + def RemoveMeasurements(self):
  888 + """
  889 + Remove item given its index.
  890 + """
  891 + # it is necessary to update internal dictionary
  892 + # that maps bitmap given item index
  893 + selected_items = self.GetSelected()
  894 + old_dict = self._list_index
  895 + new_dict = {}
  896 + if selected_items:
  897 + for index in selected_items:
  898 + self.DeleteItem(index)
  899 + for i in old_dict:
  900 + if i < index:
  901 + new_dict[i] = old_dict[i]
  902 + if i > index:
  903 + new_dict[i-1] = old_dict[i]
  904 + old_dict = new_dict
  905 + self._list_index = new_dict
  906 +
  907 + ps.Publisher().sendMessage('Remove measurements', selected_items)
  908 + else:
  909 + dlg.SurfaceSelectionRequiredForRemoval()
  910 +
  911 +
  912 + def OnCloseProject(self, pubsub_evt):
  913 + self.DeleteAllItems()
  914 + self._list_index = {}
  915 + self._bmp_idx_to_name = {}
  916 +
  917 + def OnItemSelected_(self, evt):
  918 + # Note: DON'T rename to OnItemSelected!!!
  919 + # Otherwise the parent's method will be overwritten and other
  920 + # things will stop working, e.g.: OnCheckItem
  921 +
  922 + last_index = evt.m_itemIndex
  923 + ps.Publisher().sendMessage('Change measurement selected',
  924 + last_index)
  925 + evt.Skip()
  926 +
  927 + def GetSelected(self):
  928 + """
  929 + Return all items selected (highlighted).
  930 + """
  931 + selected = []
  932 + for index in self._list_index:
  933 + if self.IsSelected(index):
  934 + selected.append(index)
  935 + # it is important to revert items order, so
  936 + # listctrl update is ok
  937 + selected.sort(reverse=True)
  938 +
  939 + return selected
774 940  
775 941 def __init_columns(self):
776   -
  942 +
777 943 self.InsertColumn(0, "", wx.LIST_FORMAT_CENTER)
778   - self.InsertColumn(1, "Value")
779   - self.InsertColumn(2, "Type", wx.LIST_FORMAT_RIGHT)
780   -
781   - self.SetColumnWidth(0, 20)
782   - self.SetColumnWidth(1, 120)
783   - self.SetColumnWidth(2, 50)
  944 + self.InsertColumn(1, _("Name"))
  945 + self.InsertColumn(2, _("Location"))
  946 + self.InsertColumn(3, _("Type"))
  947 + self.InsertColumn(4, _("Value"), wx.LIST_FORMAT_RIGHT)
784 948  
  949 + self.SetColumnWidth(0, 15)
  950 + self.SetColumnWidth(1, 70)
  951 + self.SetColumnWidth(2, 55)
  952 + self.SetColumnWidth(3, 50)
  953 + self.SetColumnWidth(4, 75)
  954 +
785 955 def __init_image_list(self):
786 956 self.imagelist = wx.ImageList(16, 16)
787 957  
788   - image = wx.Image("../icons/object_visible.jpg")
789   - bitmap = wx.BitmapFromImage(image.Scale(16, 16))
790   - bitmap.SetWidth(16)
791   - bitmap.SetHeight(16)
792   - img_check = self.imagelist.Add(bitmap)
793   -
794 958 image = wx.Image("../icons/object_invisible.jpg")
795 959 bitmap = wx.BitmapFromImage(image.Scale(16, 16))
796 960 bitmap.SetWidth(16)
797 961 bitmap.SetHeight(16)
798 962 img_null = self.imagelist.Add(bitmap)
799 963  
800   - image = wx.Image("../icons/object_colour.jpg")
  964 + image = wx.Image("../icons/object_visible.jpg")
801 965 bitmap = wx.BitmapFromImage(image.Scale(16, 16))
802 966 bitmap.SetWidth(16)
803 967 bitmap.SetHeight(16)
804   - self.img_colour = self.imagelist.Add(bitmap)
  968 + img_check = self.imagelist.Add(bitmap)
805 969  
806 970 self.SetImageList(self.imagelist,wx.IMAGE_LIST_SMALL)
807   -
  971 +
  972 + self.image_gray = Image.open("../icons/object_colour.jpg")
  973 +
  974 +
  975 + def OnEditLabel(self, evt):
  976 + ps.Publisher().sendMessage('Change measurement name', (evt.GetIndex(), evt.GetLabel()))
  977 + evt.Skip()
808 978  
809 979 def OnItemActivated(self, evt):
810 980 self.ToggleItem(evt.m_itemIndex)
  981 + evt.Skip()
  982 +
811 983  
812 984 def OnCheckItem(self, index, flag):
813   - # TODO: use pubsub to communicate to models
814   - if flag:
815   - print "checked, ", index
816   - else:
817   - print "unchecked, ", index
  985 + ps.Publisher().sendMessage('Show measurement', (index, flag))
818 986  
819   - def InsertNewItem(self, index=0, type_="", value="(1000, 4500)",
820   - colour=None):
  987 + def OnShowSingle(self, pubsub_evt):
  988 + index, visibility = pubsub_evt.data
  989 + for key in self._list_index.keys():
  990 + if key != index:
  991 + self.SetItemImage(key, not visibility)
  992 + ps.Publisher().sendMessage('Show measurement',
  993 + (key, not visibility))
  994 + self.SetItemImage(index, visibility)
  995 + ps.Publisher().sendMessage('Show measurement',
  996 + (index, visibility))
  997 +
  998 + def OnShowMultiple(self, pubsub_evt):
  999 + index_list, visibility = pubsub_evt.data
  1000 + for key in self._list_index.keys():
  1001 + if key not in index_list:
  1002 + self.SetItemImage(key, not visibility)
  1003 + ps.Publisher().sendMessage('Show measurement',
  1004 + (key, not visibility))
  1005 + for index in index_list:
  1006 + self.SetItemImage(index, visibility)
  1007 + ps.Publisher().sendMessage('Show measurement',
  1008 + (index, visibility))
  1009 +
  1010 + def AddItem_(self, pubsub_evt):
  1011 + index = pubsub_evt.data[0]
  1012 + name = pubsub_evt.data[1]
  1013 + colour = pubsub_evt.data[2]
  1014 + type_ = pubsub_evt.data[3]
  1015 + location = pubsub_evt.data[4]
  1016 + value = pubsub_evt.data[5]
  1017 +
  1018 +
  1019 + if index not in self._list_index:
  1020 + image = self.CreateColourBitmap(colour)
  1021 + image_index = self.imagelist.Add(image)
  1022 +
  1023 + index_list = self._list_index.keys()
  1024 + self._list_index[index] = image_index
  1025 +
  1026 + if (index in index_list) and index_list:
  1027 + self.UpdateItemInfo(index, name, colour, type_, location, value)
  1028 + else:
  1029 + self.InsertNewItem(index, name, colour, type_, location, value)
  1030 +
  1031 +
  1032 +
  1033 + def InsertNewItem(self, index=0, label="Measurement 1", colour=None,
  1034 + type_="LINEAR", location="SURFACE", value="0 mm"):
821 1035 self.InsertStringItem(index, "")
822   - self.SetStringItem(index, 1, type_, imageId = self.img_colour)
823   - self.SetStringItem(index, 2, value)
  1036 + self.SetStringItem(index, 1, label,
  1037 + imageId = self._list_index[index])
  1038 + self.SetStringItem(index, 2, type_)
  1039 + self.SetStringItem(index, 3, location)
  1040 + self.SetStringItem(index, 4, value)
  1041 + self.SetItemImage(index, 1)
  1042 +
  1043 + def UpdateItemInfo(self, index=0, label="Measurement 1", colour=None,
  1044 + type_="LINEAR", location="SURFACE", value="0 mm"):
  1045 + self.SetStringItem(index, 1, label,
  1046 + imageId = self._list_index[index])
  1047 + self.SetStringItem(index, 2, type_)
  1048 + self.SetStringItem(index, 3, location)
  1049 + self.SetStringItem(index, 4, value)
  1050 + self.SetItemImage(index, 1)
  1051 +
  1052 +
  1053 + def CreateColourBitmap(self, colour):
  1054 + """
  1055 + Create a wx Image with a mask colour.
  1056 + colour: colour in rgb format(0 - 1)
  1057 + """
  1058 + image = self.image_gray
  1059 + new_image = Image.new("RGB", image.size)
  1060 + for x in xrange(image.size[0]):
  1061 + for y in xrange(image.size[1]):
  1062 + pixel_colour = [int(i*image.getpixel((x,y)))
  1063 + for i in colour]
  1064 + new_image.putpixel((x,y), tuple(pixel_colour))
  1065 +
  1066 + wx_image = wx.EmptyImage(new_image.size[0],
  1067 + new_image.size[1])
  1068 + wx_image.SetData(new_image.tostring())
  1069 + return wx.BitmapFromImage(wx_image.Scale(16, 16))
  1070 +
  1071 + def EditItemColour(self, pubsub_evt):
  1072 + """
  1073 + """
  1074 + index, colour = pubsub_evt.data
  1075 + image = self.CreateColourBitmap(colour)
  1076 + image_index = self._list_index[index]
  1077 + self.imagelist.Replace(image_index, image)
  1078 + self.Refresh()
  1079 +
824 1080  
825   - def AddMeasure(self, type_, value, colour=None):
826   - self.InsertNewItem(self._last_measure, type_, value, colour)
827   - self._last_measure += 1
828 1081  
  1082 +#*******************************************************************
  1083 +#*******************************************************************
829 1084  
830 1085  
831 1086 class AnnotationsListCtrlPanel(wx.ListCtrl, listmix.TextEditMixin):
... ...