1 """GNUmed list controls and widgets.
2
3 TODO:
4
5 From: Rob McMullen <rob.mcmullen@gmail.com>
6 To: wxPython-users@lists.wxwidgets.org
7 Subject: Re: [wxPython-users] ANN: ColumnSizer mixin for ListCtrl
8
9 Thanks for all the suggestions, on and off line. There's an update
10 with a new name (ColumnAutoSizeMixin) and better sizing algorithm at:
11
12 http://trac.flipturn.org/browser/trunk/peppy/lib/column_autosize.py
13
14 sorting: http://code.activestate.com/recipes/426407/
15 """
16
17 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
18 __license__ = "GPL v2 or later"
19
20
21 import sys
22 import types
23 import logging
24 import thread
25 import time
26 import re as regex
27
28
29 import wx
30 import wx.lib.mixins.listctrl as listmixins
31
32
33 _log = logging.getLogger('gm.list_ui')
34
35
36
37 -def get_choices_from_list (
38 parent=None,
39 msg=None,
40 caption=None,
41 columns=None,
42 choices=None,
43 data=None,
44 selections=None,
45 edit_callback=None,
46 new_callback=None,
47 delete_callback=None,
48 refresh_callback=None,
49 single_selection=False,
50 can_return_empty=False,
51 ignore_OK_button=False,
52 left_extra_button=None,
53 middle_extra_button=None,
54 right_extra_button=None,
55 list_tooltip_callback=None):
56 """Let user select item(s) from a list.
57
58 - new_callback: ()
59 - edit_callback: (item data)
60 - delete_callback: (item data)
61 - refresh_callback: (listctrl)
62 - list_tooltip_callback: (item data)
63
64 - left/middle/right_extra_button: (label, tooltip, <callback> [, wants_list_ctrl])
65 wants_list_ctrl is optional
66 <callback> is called with item_data (or listctrl) as the only argument
67
68 returns:
69 on [CANCEL]: None
70 on [OK]:
71 if any items selected:
72 list of selected items
73 else:
74 if can_return_empty is True:
75 empty list
76 else:
77 None
78 """
79 if caption is None:
80 caption = _('generic multi choice dialog')
81
82 if single_selection:
83 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg, style = wx.LC_SINGLE_SEL)
84 else:
85 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg)
86
87 dlg.refresh_callback = refresh_callback
88 dlg.edit_callback = edit_callback
89 dlg.new_callback = new_callback
90 dlg.delete_callback = delete_callback
91 dlg.list_tooltip_callback = list_tooltip_callback
92
93 dlg.ignore_OK_button = ignore_OK_button
94 dlg.left_extra_button = left_extra_button
95 dlg.middle_extra_button = middle_extra_button
96 dlg.right_extra_button = right_extra_button
97
98 dlg.set_columns(columns = columns)
99
100 if refresh_callback is None:
101 dlg.set_string_items(items = choices)
102 dlg.set_column_widths()
103
104 if data is not None:
105 dlg.set_data(data = data)
106
107 if selections is not None:
108 dlg.set_selections(selections = selections)
109 dlg.can_return_empty = can_return_empty
110
111 btn_pressed = dlg.ShowModal()
112 sels = dlg.get_selected_item_data(only_one = single_selection)
113 dlg.Destroy()
114
115 if btn_pressed == wx.ID_OK:
116 if can_return_empty and (sels is None):
117 return []
118 return sels
119
120 return None
121
122 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg
123
125 """A dialog holding a list and a few buttons to act on the items."""
126
127
128
151
154
157
162
173
176
179
180
181
183 if not self.__ignore_OK_button:
184 self._BTN_ok.SetDefault()
185 self._BTN_ok.Enable(True)
186
187 if self.edit_callback is not None:
188 self._BTN_edit.Enable(True)
189
190 if self.delete_callback is not None:
191 self._BTN_delete.Enable(True)
192
194 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
195 if not self.can_return_empty:
196 self._BTN_cancel.SetDefault()
197 self._BTN_ok.Enable(False)
198 self._BTN_edit.Enable(False)
199 self._BTN_delete.Enable(False)
200
215
232
253
272
291
310
311
312
326
327 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button)
328
351
352 left_extra_button = property(lambda x:x, _set_left_extra_button)
353
376
377 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
378
401
402 right_extra_button = property(lambda x:x, _set_right_extra_button)
403
405 return self.__new_callback
406
408 if callback is not None:
409 if self.refresh_callback is None:
410 raise ValueError('refresh callback must be set before new callback can be set')
411 if not callable(callback):
412 raise ValueError('<new> callback is not a callable: %s' % callback)
413 self.__new_callback = callback
414
415 if callback is None:
416 self._BTN_new.Enable(False)
417 self._BTN_new.Hide()
418 else:
419 self._BTN_new.Enable(True)
420 self._BTN_new.Show()
421
422 new_callback = property(_get_new_callback, _set_new_callback)
423
425 return self.__edit_callback
426
428 if callback is not None:
429 if not callable(callback):
430 raise ValueError('<edit> callback is not a callable: %s' % callback)
431 self.__edit_callback = callback
432
433 if callback is None:
434 self._BTN_edit.Enable(False)
435 self._BTN_edit.Hide()
436 else:
437 self._BTN_edit.Enable(True)
438 self._BTN_edit.Show()
439
440 edit_callback = property(_get_edit_callback, _set_edit_callback)
441
443 return self.__delete_callback
444
446 if callback is not None:
447 if self.refresh_callback is None:
448 raise ValueError('refresh callback must be set before delete callback can be set')
449 if not callable(callback):
450 raise ValueError('<delete> callback is not a callable: %s' % callback)
451 self.__delete_callback = callback
452
453 if callback is None:
454 self._BTN_delete.Enable(False)
455 self._BTN_delete.Hide()
456 else:
457 self._BTN_delete.Enable(True)
458 self._BTN_delete.Show()
459
460 delete_callback = property(_get_delete_callback, _set_delete_callback)
461
463 return self.__refresh_callback
464
472
474 if callback is not None:
475 if not callable(callback):
476 raise ValueError('<refresh> callback is not a callable: %s' % callback)
477 self.__refresh_callback = callback
478 if callback is not None:
479 wx.CallAfter(self._set_refresh_callback_helper)
480
481 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
482
485
486 list_tooltip_callback = property(lambda x:x, _set_list_tooltip_callback)
487
488
489
491 if message is None:
492 self._LBL_message.Hide()
493 return
494 self._LBL_message.SetLabel(message)
495 self._LBL_message.Show()
496
497 message = property(lambda x:x, _set_message)
498
499 from Gnumed.wxGladeWidgets import wxgGenericListManagerPnl
500
502 """A panel holding a generic multi-column list and action buttions."""
503
529
530
531
534
544
547
550
553
554
555
557 if self.edit_callback is not None:
558 self._BTN_edit.Enable(True)
559 if self.delete_callback is not None:
560 self._BTN_remove.Enable(True)
561 if self.__select_callback is not None:
562 item = self._LCTRL_items.get_selected_item_data(only_one=True)
563 self.__select_callback(item)
564
566 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
567 self._BTN_edit.Enable(False)
568 self._BTN_remove.Enable(False)
569 if self.__select_callback is not None:
570 self.__select_callback(None)
571
582
587
601
615
631
647
663
664
665
667 return self.__new_callback
668
670 if callback is not None:
671 if not callable(callback):
672 raise ValueError('<new> callback is not a callable: %s' % callback)
673 self.__new_callback = callback
674 self._BTN_add.Enable(callback is not None)
675
676 new_callback = property(_get_new_callback, _set_new_callback)
677
679 return self.__select_callback
680
682 if callback is not None:
683 if not callable(callback):
684 raise ValueError('<select> callback is not a callable: %s' % callback)
685 self.__select_callback = callback
686
687 select_callback = property(_get_select_callback, _set_select_callback)
688
690 return self._LBL_message.GetLabel()
691
693 if msg is None:
694 self._LBL_message.Hide()
695 self._LBL_message.SetLabel(u'')
696 else:
697 self._LBL_message.SetLabel(msg)
698 self._LBL_message.Show()
699 self.Layout()
700
701 message = property(_get_message, _set_message)
702
718
719 left_extra_button = property(lambda x:x, _set_left_extra_button)
720
736
737 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
738
754
755 right_extra_button = property(lambda x:x, _set_right_extra_button)
756
757 from Gnumed.wxGladeWidgets import wxgItemPickerDlg
758
760
762
763 try:
764 msg = kwargs['msg']
765 del kwargs['msg']
766 except KeyError:
767 msg = None
768
769 wxgItemPickerDlg.wxgItemPickerDlg.__init__(self, *args, **kwargs)
770
771 if msg is None:
772 self._LBL_msg.Hide()
773 else:
774 self._LBL_msg.SetLabel(msg)
775
776 self.allow_duplicate_picks = True
777
778 self._LCTRL_left.activate_callback = self.__pick_selected
779 self.__extra_button_callback = None
780
781 self._LCTRL_left.SetFocus()
782
783
784
785 - def set_columns(self, columns=None, columns_right=None):
786 self._LCTRL_left.set_columns(columns = columns)
787 if columns_right is None:
788 self._LCTRL_right.set_columns(columns = columns)
789 else:
790 if len(columns_right) < len(columns):
791 cols = columns
792 else:
793 cols = columns_right[:len(columns)]
794 self._LCTRL_right.set_columns(columns = cols)
795
803
806
811
817
820
823
824 picks = property(get_picks, lambda x:x)
825
841
842 extra_button = property(lambda x:x, _set_extra_button)
843
844
845
847 if self._LCTRL_left.get_selected_items(only_one = True) == -1:
848 return
849
850 right_items = self._LCTRL_right.get_string_items()
851 right_data = self._LCTRL_right.get_item_data()
852 if right_data is None:
853 right_data = []
854
855 selected_items = self._LCTRL_left.get_selected_string_items(only_one = False)
856 selected_data = self._LCTRL_left.get_selected_item_data(only_one = False)
857
858 if self.allow_duplicate_picks:
859 right_items.extend(selected_items)
860 right_data.extend(selected_data)
861 self._LCTRL_right.set_string_items(items = right_items)
862 self._LCTRL_right.set_data(data = right_data)
863 self._LCTRL_right.set_column_widths()
864
865
866 return
867
868 for sel_item, sel_data in zip(selected_items, selected_data):
869 if sel_item in right_items:
870 continue
871 right_items.append(sel_item)
872 right_data.append(sel_data)
873 self._LCTRL_right.set_string_items(items = right_items)
874 self._LCTRL_right.set_data(data = right_data)
875 self._LCTRL_right.set_column_widths()
876
877
878
880 if self._LCTRL_right.get_selected_items(only_one = True) == -1:
881 return
882
883 for item_idx in self._LCTRL_right.get_selected_items(only_one = False):
884 self._LCTRL_right.remove_item(item_idx)
885
886 if self._LCTRL_right.GetItemCount() == 0:
887 self._BTN_right2left.Enable(False)
888
889
890
891
892
893
895 self._BTN_left2right.Enable(True)
896
898 if self._LCTRL_left.get_selected_items(only_one = True) == -1:
899 self._BTN_left2right.Enable(False)
900
902 self._BTN_right2left.Enable(True)
903
905 if self._LCTRL_right.get_selected_items(only_one = True) == -1:
906 self._BTN_right2left.Enable(False)
907
910
913
916
919
920 left_item_tooltip_callback = property(lambda x:x, _set_left_item_tooltip_callback)
921
924
925 right_item_tooltip_callback = property(lambda x:x, _set_right_item_tooltip_callback)
926
927
928 -class cReportListCtrl(wx.ListCtrl, listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorterMixin):
929
930
931
932
933
934
935
936
937 map_item_idx2data_idx = wx.ListCtrl.GetItemData
938
939 sort_order_tags = {
940 True: u' [\u03b1\u0391 \u2192 \u03c9\u03A9]',
941 False: u' [\u03c9\u03A9 \u2192 \u03b1\u0391]'
942 }
943
945
946 self.debug = None
947
948 try:
949 kwargs['style'] = kwargs['style'] | wx.LC_REPORT
950 except KeyError:
951 kwargs['style'] = wx.LC_REPORT
952
953 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL)
954
955 wx.ListCtrl.__init__(self, *args, **kwargs)
956 listmixins.ListCtrlAutoWidthMixin.__init__(self)
957
958
959 self._invalidate_sorting_metadata()
960 listmixins.ColumnSorterMixin.__init__(self, 0)
961
962
963
964 self.__widths = None
965 self.__data = None
966 self.__activate_callback = None
967 self.__rightclick_callback = None
968
969 self.__item_tooltip_callback = None
970 self.__tt_last_item = None
971 self.__tt_static_part = _("""Select the items you want to work on.
972
973 A discontinuous selection may depend on your holding down a platform-dependent modifier key (<ctrl>, <alt>, etc) or key combination (eg. <ctrl-shift> or <ctrl-alt>) while clicking.""")
974 self.Bind(wx.EVT_MOTION, self._on_mouse_motion)
975
976 self.__next_line_to_search = 0
977 self.__search_data = None
978 self.__search_dlg = None
979 self.__searchable_cols = None
980
981 self.Bind(wx.EVT_CHAR, self._on_char)
982 self.Bind(wx.EVT_FIND_CLOSE, self._on_search_dlg_closed)
983 self.Bind(wx.EVT_FIND, self._on_search_first_match)
984 self.Bind(wx.EVT_FIND_NEXT, self._on_search_next_match)
985
986
987
989 """(Re)define the columns.
990
991 Note that this will (have to) delete the items.
992 """
993 self.ClearAll()
994 self.__tt_last_item = None
995 if columns is None:
996 return
997 for idx in range(len(columns)):
998 self.InsertColumn(idx, columns[idx])
999
1000 self._invalidate_sorting_metadata()
1001
1003 """Set the column width policy.
1004
1005 widths = None:
1006 use previous policy if any or default policy
1007 widths != None:
1008 use this policy and remember it for later calls
1009
1010 This means there is no way to *revert* to the default policy :-(
1011 """
1012
1013 if widths is not None:
1014 self.__widths = widths
1015 for idx in range(len(self.__widths)):
1016 self.SetColumnWidth(col = idx, width = self.__widths[idx])
1017 return
1018
1019
1020 if self.__widths is not None:
1021 for idx in range(len(self.__widths)):
1022 self.SetColumnWidth(col = idx, width = self.__widths[idx])
1023 return
1024
1025
1026 if self.GetItemCount() == 0:
1027 width_type = wx.LIST_AUTOSIZE_USEHEADER
1028 else:
1029 width_type = wx.LIST_AUTOSIZE
1030 for idx in range(self.GetColumnCount()):
1031 self.SetColumnWidth(col = idx, width = width_type)
1032
1034 """All item members must be unicode()able or None."""
1035
1036 wx.BeginBusyCursor()
1037 self._invalidate_sorting_metadata()
1038
1039
1040 loop = 0
1041 while True:
1042 if loop > 3:
1043 _log.debug('unable to delete list items after looping 3 times, continuing and hoping for the best')
1044 break
1045 loop += 1
1046 if self.debug is not None:
1047 _log.debug('[round %s] GetItemCount() before DeleteAllItems(): %s (%s, thread [%s])', loop, self.GetItemCount(), self.debug, thread.get_ident())
1048 if not self.DeleteAllItems():
1049 _log.debug('DeleteAllItems() failed (%s)', self.debug)
1050 item_count = self.GetItemCount()
1051 if self.debug is not None:
1052 _log.debug('GetItemCount() after DeleteAllItems(): %s (%s)', item_count, self.debug)
1053 if item_count == 0:
1054 break
1055 wx.SafeYield(None, True)
1056 _log.debug('GetItemCount() not 0 after DeleteAllItems() (%s)', self.debug)
1057 time.sleep(0.3)
1058 wx.SafeYield(None, True)
1059
1060 if items is None:
1061 self.data = None
1062 wx.EndBusyCursor()
1063 return
1064
1065
1066 for item in items:
1067 try:
1068 item[0]
1069 if not isinstance(item, basestring):
1070 is_numerically_iterable = True
1071
1072 else:
1073 is_numerically_iterable = False
1074 except TypeError:
1075 is_numerically_iterable = False
1076
1077 if is_numerically_iterable:
1078
1079
1080 col_val = unicode(item[0])
1081 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
1082 for col_num in range(1, min(self.GetColumnCount(), len(item))):
1083 col_val = unicode(item[col_num])
1084 self.SetStringItem(index = row_num, col = col_num, label = col_val)
1085 else:
1086
1087 col_val = unicode(item)
1088 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
1089
1090
1091 self.data = items
1092
1093 wx.EndBusyCursor()
1094
1096 """<data> assumed to be a list corresponding to the item indices"""
1097 if data is not None:
1098 item_count = self.GetItemCount()
1099 if len(data) != item_count:
1100 _log.debug('<data> length (%s) must be equal to number of list items (%s) (%s, thread [%s])', len(data), item_count, self.debug, thread.get_ident())
1101 for item_idx in range(len(data)):
1102 self.SetItemData(item_idx, item_idx)
1103 self.__data = data
1104 self.__tt_last_item = None
1105
1106
1107 return
1108
1113
1114 data = property(_get_data, set_data)
1115
1122
1124 if self.__is_single_selection:
1125 return [self.GetFirstSelected()]
1126 selections = []
1127 idx = self.GetFirstSelected()
1128 while idx != -1:
1129 selections.append(idx)
1130 idx = self.GetNextSelected(idx)
1131 return selections
1132
1133 selections = property(__get_selections, set_selections)
1134
1135
1136
1138 labels = []
1139 for col_idx in self.GetColumnCount():
1140 col = self.GetColumn(col = col_idx)
1141 labels.append(col.GetText())
1142 return labels
1143
1145 if item_idx is not None:
1146 return self.GetItem(item_idx)
1147
1149 return [ self.GetItem(item_idx) for item_idx in range(self.GetItemCount()) ]
1150
1152 return [ self.GetItemText(item_idx) for item_idx in range(self.GetItemCount()) ]
1153
1155
1156 if self.__is_single_selection or only_one:
1157 return self.GetFirstSelected()
1158
1159 items = []
1160 idx = self.GetFirstSelected()
1161 while idx != -1:
1162 items.append(idx)
1163 idx = self.GetNextSelected(idx)
1164
1165 return items
1166
1168
1169 if self.__is_single_selection or only_one:
1170 return self.GetItemText(self.GetFirstSelected())
1171
1172 items = []
1173 idx = self.GetFirstSelected()
1174 while idx != -1:
1175 items.append(self.GetItemText(idx))
1176 idx = self.GetNextSelected(idx)
1177
1178 return items
1179
1181 if self.__data is None:
1182 return None
1183
1184 if item_idx is not None:
1185 return self.__data[self.map_item_idx2data_idx(item_idx)]
1186
1187
1188
1189
1190 return [ self.__data[self.map_item_idx2data_idx(item_idx)] for item_idx in range(self.GetItemCount()) ]
1191
1193
1194 if self.__is_single_selection or only_one:
1195 if self.__data is None:
1196 return None
1197 idx = self.GetFirstSelected()
1198 if idx == -1:
1199 return None
1200 return self.__data[self.map_item_idx2data_idx(idx)]
1201
1202 data = []
1203 if self.__data is None:
1204 return data
1205 idx = self.GetFirstSelected()
1206 while idx != -1:
1207 data.append(self.__data[self.map_item_idx2data_idx(idx)])
1208 idx = self.GetNextSelected(idx)
1209
1210 return data
1211
1213 self.Select(idx = self.GetFirstSelected(), on = 0)
1214
1216
1217
1218
1219
1220
1221
1222 self.DeleteItem(item_idx)
1223 self.__tt_last_item = None
1224 self._invalidate_sorting_metadata()
1225
1226
1227
1229 event.Skip()
1230 if self.__activate_callback is not None:
1231 self.__activate_callback(event)
1232
1234 event.Skip()
1235 if self.__rightclick_callback is not None:
1236 self.__rightclick_callback(event)
1237
1239
1240 if evt.GetModifiers() != wx.MOD_CMD:
1241 evt.Skip()
1242 return
1243
1244 if unichr(evt.GetRawKeyCode()) != u'f':
1245 evt.Skip()
1246 return
1247
1248 if self.__search_dlg is not None:
1249 self.__search_dlg.Close()
1250 return
1251
1252 if self.__searchable_cols is None:
1253 self.searchable_columns = None
1254
1255 if len(self.__searchable_cols) == 0:
1256 return
1257
1258 if self.__search_data is None:
1259 self.__search_data = wx.FindReplaceData()
1260 self.__search_dlg = wx.FindReplaceDialog (
1261 self,
1262 self.__search_data,
1263 _('Search in list'),
1264 wx.FR_NOUPDOWN | wx.FR_NOMATCHCASE | wx.FR_NOWHOLEWORD
1265 )
1266 self.__search_dlg.Show(True)
1267
1269 """Update tooltip on mouse motion.
1270
1271 for s in dir(wx):
1272 if s.startswith('LIST_HITTEST'):
1273 print s, getattr(wx, s)
1274
1275 LIST_HITTEST_ABOVE 1
1276 LIST_HITTEST_BELOW 2
1277 LIST_HITTEST_NOWHERE 4
1278 LIST_HITTEST_ONITEM 672
1279 LIST_HITTEST_ONITEMICON 32
1280 LIST_HITTEST_ONITEMLABEL 128
1281 LIST_HITTEST_ONITEMRIGHT 256
1282 LIST_HITTEST_ONITEMSTATEICON 512
1283 LIST_HITTEST_TOLEFT 1024
1284 LIST_HITTEST_TORIGHT 2048
1285 """
1286 item_idx, where_flag = self.HitTest(wx.Point(event.X, event.Y))
1287
1288
1289 if where_flag not in [
1290 wx.LIST_HITTEST_ONITEMLABEL,
1291 wx.LIST_HITTEST_ONITEMICON,
1292 wx.LIST_HITTEST_ONITEMSTATEICON,
1293 wx.LIST_HITTEST_ONITEMRIGHT,
1294 wx.LIST_HITTEST_ONITEM
1295 ]:
1296 self.__tt_last_item = None
1297 self.SetToolTipString(self.__tt_static_part)
1298 return
1299
1300
1301 if self.__tt_last_item == item_idx:
1302 return
1303
1304
1305 self.__tt_last_item = item_idx
1306
1307
1308
1309 if item_idx == wx.NOT_FOUND:
1310 self.SetToolTipString(self.__tt_static_part)
1311 return
1312
1313
1314 if self.__data is None:
1315 self.SetToolTipString(self.__tt_static_part)
1316 return
1317
1318
1319
1320
1321
1322 if (
1323 (item_idx > (len(self.__data) - 1))
1324 or
1325 (item_idx < -1)
1326 ):
1327 self.SetToolTipString(self.__tt_static_part)
1328 print "*************************************************************"
1329 print "GNUmed has detected an inconsistency with list item tooltips."
1330 print ""
1331 print "This is not a big problem and you can keep working."
1332 print ""
1333 print "However, please send us the following so we can fix GNUmed:"
1334 print ""
1335 print "item idx: %s" % item_idx
1336 print 'where flag: %s' % where_flag
1337 print 'data list length: %s' % len(self.__data)
1338 print "*************************************************************"
1339 return
1340
1341 dyna_tt = None
1342 if self.__item_tooltip_callback is not None:
1343 dyna_tt = self.__item_tooltip_callback(self.__data[self.map_item_idx2data_idx(item_idx)])
1344
1345 if dyna_tt is None:
1346 self.SetToolTipString(self.__tt_static_part)
1347 return
1348
1349 self.SetToolTipString(dyna_tt)
1350
1351
1352
1354 self.__search_dlg.Destroy()
1355 self.__search_dlg = None
1356
1357
1358
1359
1360
1361
1362
1363
1364
1366 for row_idx in range(self.__next_line_to_search, self.ItemCount):
1367 for col_idx in range(self.ColumnCount):
1368 if col_idx not in self.__searchable_cols:
1369 continue
1370 col_val = self.GetItem(row_idx, col_idx).GetText()
1371 if regex.search(search_term, col_val, regex.U | regex.I) is not None:
1372 self.Select(row_idx)
1373 self.EnsureVisible(row_idx)
1374 if row_idx == self.ItemCount - 1:
1375
1376 self.__next_line_to_search = 0
1377 else:
1378 self.__next_line_to_search = row_idx + 1
1379 return True
1380
1381 self.__next_line_to_search = 0
1382 return False
1383
1385 self.__on_search_match(evt.GetFindString())
1386
1388 self.__on_search_match(evt.GetFindString())
1389
1390
1391
1393 return self.__activate_callback
1394
1396 if callback is None:
1397 self.Unbind(wx.EVT_LIST_ITEM_ACTIVATED)
1398 else:
1399 if not callable(callback):
1400 raise ValueError('<activate> callback is not a callable: %s' % callback)
1401 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_list_item_activated)
1402 self.__activate_callback = callback
1403
1404 activate_callback = property(_get_activate_callback, _set_activate_callback)
1405
1407 return self.__rightclick_callback
1408
1410 if callback is None:
1411 self.Unbind(wx.EVT_LIST_ITEM_RIGHT_CLICK)
1412 else:
1413 if not callable(callback):
1414 raise ValueError('<rightclick> callback is not a callable: %s' % callback)
1415 self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self._on_list_item_rightclicked)
1416 self.__rightclick_callback = callback
1417
1418 rightclick_callback = property(_get_rightclick_callback, _set_rightclick_callback)
1419
1425
1426
1427
1428
1429
1430 item_tooltip_callback = property(lambda x:x, _set_item_tooltip_callback)
1431
1433
1434 if cols is None:
1435 self.__searchable_cols = range(self.ColumnCount)
1436 return
1437
1438
1439 new_cols = {}
1440 for col in cols:
1441 if col < self.ColumnCount:
1442 new_cols[col] = True
1443 self.__searchable_cols = new_cols.keys()
1444
1445 searchable_columns = property(lambda x:x, _set_searchable_cols)
1446
1447
1448
1450 if self.itemDataMap is None:
1451 self._update_sorting_metadata()
1452 return self
1453
1455 self._cleanup_column_headers()
1456
1457 col_idx, is_ascending = self.GetSortState()
1458 col_state = self.GetColumn(col_idx)
1459 col_state.m_text += self.sort_order_tags[is_ascending]
1460 self.SetColumn(col_idx, col_state)
1461
1463 dict2sort = {}
1464 item_count = self.GetItemCount()
1465 if item_count == 0:
1466 return dict2sort
1467 col_count = self.GetColumnCount()
1468 for item_idx in range(item_count):
1469 dict2sort[item_idx] = ()
1470 if col_count == 0:
1471 continue
1472 for col_idx in range(col_count):
1473 dict2sort[item_idx] += (self.GetItem(item_idx, col_idx).GetText(), )
1474
1475 return dict2sort
1476
1478 for col_idx in range(self.ColumnCount):
1479 col_state = self.GetColumn(col_idx)
1480 if col_state.m_text.endswith(self.sort_order_tags[True]):
1481 col_state.m_text = col_state.m_text[:-len(self.sort_order_tags[True])]
1482 if col_state.m_text.endswith(self.sort_order_tags[False]):
1483 col_state.m_text = col_state.m_text[:-len(self.sort_order_tags[False])]
1484 self.SetColumn(col_idx, col_state)
1485
1490
1493
1495
1496
1497
1498
1499
1500
1501 event.Skip()
1502
1503
1504
1505
1506 if __name__ == '__main__':
1507
1508 if len(sys.argv) < 2:
1509 sys.exit()
1510
1511 if sys.argv[1] != 'test':
1512 sys.exit()
1513
1514 sys.path.insert(0, '../../')
1515
1516 from Gnumed.pycommon import gmI18N
1517 gmI18N.activate_locale()
1518 gmI18N.install_domain()
1519
1520
1522 app = wx.PyWidgetTester(size = (400, 500))
1523 dlg = wx.MultiChoiceDialog (
1524 parent = None,
1525 message = 'test message',
1526 caption = 'test caption',
1527 choices = ['a', 'b', 'c', 'd', 'e']
1528 )
1529 dlg.ShowModal()
1530 sels = dlg.GetSelections()
1531 print "selected:"
1532 for sel in sels:
1533 print sel
1534
1536
1537 def edit(argument):
1538 print "editor called with:"
1539 print argument
1540
1541 def refresh(lctrl):
1542 choices = ['a', 'b', 'c']
1543 lctrl.set_string_items(choices)
1544
1545 app = wx.PyWidgetTester(size = (200, 50))
1546 chosen = get_choices_from_list (
1547
1548 caption = 'select health issues',
1549
1550
1551 columns = ['issue'],
1552 refresh_callback = refresh
1553
1554 )
1555 print "chosen:"
1556 print chosen
1557
1559 app = wx.PyWidgetTester(size = (200, 50))
1560 dlg = cItemPickerDlg(None, -1, msg = 'Pick a few items:')
1561 dlg.set_columns(['Plugins'], ['Load in workplace', 'dummy'])
1562
1563 dlg.set_string_items(['patient', 'emr', 'docs'])
1564 result = dlg.ShowModal()
1565 print result
1566 print dlg.get_picks()
1567
1568
1569
1570 test_item_picker_dlg()
1571
1572
1573
1574