Commit 3d07ca3411f5f3ff484fd80ffc674478d0c7ade7
1 parent
9076ea01
Exists in
master
and in
68 other branches
ENH: Notebook page related to measurements / pubsub evt
Showing
2 changed files
with
324 additions
and
50 deletions
Show diff stats
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): | ... | ... |