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):
120
121 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg
122
124 """A dialog holding a list and a few buttons to act on the items."""
125
126
127
150
153
156
161
172
175
178
179
180
182 if not self.__ignore_OK_button:
183 self._BTN_ok.SetDefault()
184 self._BTN_ok.Enable(True)
185
186 if self.edit_callback is not None:
187 self._BTN_edit.Enable(True)
188
189 if self.delete_callback is not None:
190 self._BTN_delete.Enable(True)
191
193 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
194 if not self.can_return_empty:
195 self._BTN_cancel.SetDefault()
196 self._BTN_ok.Enable(False)
197 self._BTN_edit.Enable(False)
198 self._BTN_delete.Enable(False)
199
214
231
252
268
284
300
301
302
316
317 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button)
318
334
335 left_extra_button = property(lambda x:x, _set_left_extra_button)
336
352
353 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
354
370
371 right_extra_button = property(lambda x:x, _set_right_extra_button)
372
374 return self.__new_callback
375
377 if callback is not None:
378 if self.refresh_callback is None:
379 raise ValueError('refresh callback must be set before new callback can be set')
380 if not callable(callback):
381 raise ValueError('<new> callback is not a callable: %s' % callback)
382 self.__new_callback = callback
383
384 if callback is None:
385 self._BTN_new.Enable(False)
386 self._BTN_new.Hide()
387 else:
388 self._BTN_new.Enable(True)
389 self._BTN_new.Show()
390
391 new_callback = property(_get_new_callback, _set_new_callback)
392
394 return self.__edit_callback
395
397 if callback is not None:
398 if not callable(callback):
399 raise ValueError('<edit> callback is not a callable: %s' % callback)
400 self.__edit_callback = callback
401
402 if callback is None:
403 self._BTN_edit.Enable(False)
404 self._BTN_edit.Hide()
405 else:
406 self._BTN_edit.Enable(True)
407 self._BTN_edit.Show()
408
409 edit_callback = property(_get_edit_callback, _set_edit_callback)
410
412 return self.__delete_callback
413
415 if callback is not None:
416 if self.refresh_callback is None:
417 raise ValueError('refresh callback must be set before delete callback can be set')
418 if not callable(callback):
419 raise ValueError('<delete> callback is not a callable: %s' % callback)
420 self.__delete_callback = callback
421
422 if callback is None:
423 self._BTN_delete.Enable(False)
424 self._BTN_delete.Hide()
425 else:
426 self._BTN_delete.Enable(True)
427 self._BTN_delete.Show()
428
429 delete_callback = property(_get_delete_callback, _set_delete_callback)
430
432 return self.__refresh_callback
433
441
443 if callback is not None:
444 if not callable(callback):
445 raise ValueError('<refresh> callback is not a callable: %s' % callback)
446 self.__refresh_callback = callback
447 if callback is not None:
448 wx.CallAfter(self._set_refresh_callback_helper)
449
450 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
451
454
455 list_tooltip_callback = property(lambda x:x, _set_list_tooltip_callback)
456
457
458
460 if message is None:
461 self._LBL_message.Hide()
462 return
463 self._LBL_message.SetLabel(message)
464 self._LBL_message.Show()
465
466 message = property(lambda x:x, _set_message)
467
468 from Gnumed.wxGladeWidgets import wxgGenericListManagerPnl
469
471 """A panel holding a generic multi-column list and action buttions."""
472
498
499
500
503
513
516
519
522
523
524
526 if self.edit_callback is not None:
527 self._BTN_edit.Enable(True)
528 if self.delete_callback is not None:
529 self._BTN_remove.Enable(True)
530 if self.__select_callback is not None:
531 item = self._LCTRL_items.get_selected_item_data(only_one=True)
532 self.__select_callback(item)
533
535 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
536 self._BTN_edit.Enable(False)
537 self._BTN_remove.Enable(False)
538 if self.__select_callback is not None:
539 self.__select_callback(None)
540
551
556
570
584
600
616
632
633
634
636 return self.__new_callback
637
639 if callback is not None:
640 if not callable(callback):
641 raise ValueError('<new> callback is not a callable: %s' % callback)
642 self.__new_callback = callback
643 self._BTN_add.Enable(callback is not None)
644
645 new_callback = property(_get_new_callback, _set_new_callback)
646
648 return self.__select_callback
649
651 if callback is not None:
652 if not callable(callback):
653 raise ValueError('<select> callback is not a callable: %s' % callback)
654 self.__select_callback = callback
655
656 select_callback = property(_get_select_callback, _set_select_callback)
657
659 return self._LBL_message.GetLabel()
660
662 if msg is None:
663 self._LBL_message.Hide()
664 self._LBL_message.SetLabel(u'')
665 else:
666 self._LBL_message.SetLabel(msg)
667 self._LBL_message.Show()
668 self.Layout()
669
670 message = property(_get_message, _set_message)
671
687
688 left_extra_button = property(lambda x:x, _set_left_extra_button)
689
705
706 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
707
723
724 right_extra_button = property(lambda x:x, _set_right_extra_button)
725
726 from Gnumed.wxGladeWidgets import wxgItemPickerDlg
727
729
731
732 try:
733 msg = kwargs['msg']
734 del kwargs['msg']
735 except KeyError:
736 msg = None
737
738 wxgItemPickerDlg.wxgItemPickerDlg.__init__(self, *args, **kwargs)
739
740 if msg is None:
741 self._LBL_msg.Hide()
742 else:
743 self._LBL_msg.SetLabel(msg)
744
745 self.allow_duplicate_picks = True
746
747 self._LCTRL_left.activate_callback = self.__pick_selected
748 self.__extra_button_callback = None
749
750 self._LCTRL_left.SetFocus()
751
752
753
754 - def set_columns(self, columns=None, columns_right=None):
755 self._LCTRL_left.set_columns(columns = columns)
756 if columns_right is None:
757 self._LCTRL_right.set_columns(columns = columns)
758 else:
759 if len(columns_right) < len(columns):
760 cols = columns
761 else:
762 cols = columns_right[:len(columns)]
763 self._LCTRL_right.set_columns(columns = cols)
764
772
775
780
786
789
792
793 picks = property(get_picks, lambda x:x)
794
810
811 extra_button = property(lambda x:x, _set_extra_button)
812
813
814
816 if self._LCTRL_left.get_selected_items(only_one = True) == -1:
817 return
818
819 right_items = self._LCTRL_right.get_string_items()
820 right_data = self._LCTRL_right.get_item_data()
821 if right_data is None:
822 right_data = []
823
824 selected_items = self._LCTRL_left.get_selected_string_items(only_one = False)
825 selected_data = self._LCTRL_left.get_selected_item_data(only_one = False)
826
827 if self.allow_duplicate_picks:
828 right_items.extend(selected_items)
829 right_data.extend(selected_data)
830 self._LCTRL_right.set_string_items(items = right_items)
831 self._LCTRL_right.set_data(data = right_data)
832 self._LCTRL_right.set_column_widths()
833
834
835 return
836
837 for sel_item, sel_data in zip(selected_items, selected_data):
838 if sel_item in right_items:
839 continue
840 right_items.append(sel_item)
841 right_data.append(sel_data)
842 self._LCTRL_right.set_string_items(items = right_items)
843 self._LCTRL_right.set_data(data = right_data)
844 self._LCTRL_right.set_column_widths()
845
846
847
849 if self._LCTRL_right.get_selected_items(only_one = True) == -1:
850 return
851
852 for item_idx in self._LCTRL_right.get_selected_items(only_one = False):
853 self._LCTRL_right.remove_item(item_idx)
854
855 if self._LCTRL_right.GetItemCount() == 0:
856 self._BTN_right2left.Enable(False)
857
858
859
860
861
862
864 self._BTN_left2right.Enable(True)
865
867 if self._LCTRL_left.get_selected_items(only_one = True) == -1:
868 self._BTN_left2right.Enable(False)
869
871 self._BTN_right2left.Enable(True)
872
874 if self._LCTRL_right.get_selected_items(only_one = True) == -1:
875 self._BTN_right2left.Enable(False)
876
879
882
885
888
889 left_item_tooltip_callback = property(lambda x:x, _set_left_item_tooltip_callback)
890
893
894 right_item_tooltip_callback = property(lambda x:x, _set_right_item_tooltip_callback)
895
896
897 -class cReportListCtrl(wx.ListCtrl, listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorterMixin):
898
899
900
901
902
903
904
905
906 map_item_idx2data_idx = wx.ListCtrl.GetItemData
907
908 sort_order_tags = {
909 True: u' [\u03b1\u0391 \u2192 \u03c9\u03A9]',
910 False: u' [\u03c9\u03A9 \u2192 \u03b1\u0391]'
911 }
912
914
915 self.debug = None
916
917 try:
918 kwargs['style'] = kwargs['style'] | wx.LC_REPORT
919 except KeyError:
920 kwargs['style'] = wx.LC_REPORT
921
922 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL)
923
924 wx.ListCtrl.__init__(self, *args, **kwargs)
925 listmixins.ListCtrlAutoWidthMixin.__init__(self)
926
927
928 self._invalidate_sorting_metadata()
929 listmixins.ColumnSorterMixin.__init__(self, 0)
930
931
932
933 self.__widths = None
934 self.__data = None
935 self.__activate_callback = None
936 self.__rightclick_callback = None
937
938 self.__item_tooltip_callback = None
939 self.__tt_last_item = None
940 self.__tt_static_part = _("""Select the items you want to work on.
941
942 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.""")
943 self.Bind(wx.EVT_MOTION, self._on_mouse_motion)
944
945 self.__next_line_to_search = 0
946 self.__search_data = None
947 self.__search_dlg = None
948 self.__searchable_cols = None
949
950 self.Bind(wx.EVT_CHAR, self._on_char)
951 self.Bind(wx.EVT_FIND_CLOSE, self._on_search_dlg_closed)
952 self.Bind(wx.EVT_FIND, self._on_search_first_match)
953 self.Bind(wx.EVT_FIND_NEXT, self._on_search_next_match)
954
955
956
958 """(Re)define the columns.
959
960 Note that this will (have to) delete the items.
961 """
962 self.ClearAll()
963 self.__tt_last_item = None
964 if columns is None:
965 return
966 for idx in range(len(columns)):
967 self.InsertColumn(idx, columns[idx])
968
969 self._invalidate_sorting_metadata()
970
972 """Set the column width policy.
973
974 widths = None:
975 use previous policy if any or default policy
976 widths != None:
977 use this policy and remember it for later calls
978
979 This means there is no way to *revert* to the default policy :-(
980 """
981
982 if widths is not None:
983 self.__widths = widths
984 for idx in range(len(self.__widths)):
985 self.SetColumnWidth(col = idx, width = self.__widths[idx])
986 return
987
988
989 if self.__widths is not None:
990 for idx in range(len(self.__widths)):
991 self.SetColumnWidth(col = idx, width = self.__widths[idx])
992 return
993
994
995 if self.GetItemCount() == 0:
996 width_type = wx.LIST_AUTOSIZE_USEHEADER
997 else:
998 width_type = wx.LIST_AUTOSIZE
999 for idx in range(self.GetColumnCount()):
1000 self.SetColumnWidth(col = idx, width = width_type)
1001
1003 """All item members must be unicode()able or None."""
1004
1005 wx.BeginBusyCursor()
1006 self._invalidate_sorting_metadata()
1007
1008
1009 loop = 0
1010 while True:
1011 if loop > 3:
1012 _log.debug('unable to delete list items after looping 3 times, continuing and hoping for the best')
1013 break
1014 loop += 1
1015 if self.debug is not None:
1016 _log.debug('[round %s] GetItemCount() before DeleteAllItems(): %s (%s, thread [%s])', loop, self.GetItemCount(), self.debug, thread.get_ident())
1017 if not self.DeleteAllItems():
1018 _log.debug('DeleteAllItems() failed (%s)', self.debug)
1019 item_count = self.GetItemCount()
1020 if self.debug is not None:
1021 _log.debug('GetItemCount() after DeleteAllItems(): %s (%s)', item_count, self.debug)
1022 if item_count == 0:
1023 break
1024 wx.SafeYield(None, True)
1025 _log.debug('GetItemCount() not 0 after DeleteAllItems() (%s)', self.debug)
1026 time.sleep(0.3)
1027 wx.SafeYield(None, True)
1028
1029 if items is None:
1030 self.data = None
1031 wx.EndBusyCursor()
1032 return
1033
1034
1035 for item in items:
1036 try:
1037 item[0]
1038 if not isinstance(item, basestring):
1039 is_numerically_iterable = True
1040
1041 else:
1042 is_numerically_iterable = False
1043 except TypeError:
1044 is_numerically_iterable = False
1045
1046 if is_numerically_iterable:
1047
1048
1049 col_val = unicode(item[0])
1050 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
1051 for col_num in range(1, min(self.GetColumnCount(), len(item))):
1052 col_val = unicode(item[col_num])
1053 self.SetStringItem(index = row_num, col = col_num, label = col_val)
1054 else:
1055
1056 col_val = unicode(item)
1057 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
1058
1059
1060 self.data = items
1061
1062 wx.EndBusyCursor()
1063
1065 """<data> assumed to be a list corresponding to the item indices"""
1066 if data is not None:
1067 item_count = self.GetItemCount()
1068 if len(data) != item_count:
1069 _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())
1070 for item_idx in range(len(data)):
1071 self.SetItemData(item_idx, item_idx)
1072 self.__data = data
1073 self.__tt_last_item = None
1074
1075
1076 return
1077
1082
1083 data = property(_get_data, set_data)
1084
1091
1093 if self.__is_single_selection:
1094 return [self.GetFirstSelected()]
1095 selections = []
1096 idx = self.GetFirstSelected()
1097 while idx != -1:
1098 selections.append(idx)
1099 idx = self.GetNextSelected(idx)
1100 return selections
1101
1102 selections = property(__get_selections, set_selections)
1103
1104
1105
1107 labels = []
1108 for col_idx in self.GetColumnCount():
1109 col = self.GetColumn(col = col_idx)
1110 labels.append(col.GetText())
1111 return labels
1112
1114 if item_idx is not None:
1115 return self.GetItem(item_idx)
1116
1118 return [ self.GetItem(item_idx) for item_idx in range(self.GetItemCount()) ]
1119
1121 return [ self.GetItemText(item_idx) for item_idx in range(self.GetItemCount()) ]
1122
1124
1125 if self.__is_single_selection or only_one:
1126 return self.GetFirstSelected()
1127
1128 items = []
1129 idx = self.GetFirstSelected()
1130 while idx != -1:
1131 items.append(idx)
1132 idx = self.GetNextSelected(idx)
1133
1134 return items
1135
1137
1138 if self.__is_single_selection or only_one:
1139 return self.GetItemText(self.GetFirstSelected())
1140
1141 items = []
1142 idx = self.GetFirstSelected()
1143 while idx != -1:
1144 items.append(self.GetItemText(idx))
1145 idx = self.GetNextSelected(idx)
1146
1147 return items
1148
1150 if self.__data is None:
1151 return None
1152
1153 if item_idx is not None:
1154 return self.__data[self.map_item_idx2data_idx(item_idx)]
1155
1156
1157
1158
1159 return [ self.__data[self.map_item_idx2data_idx(item_idx)] for item_idx in range(self.GetItemCount()) ]
1160
1162
1163 if self.__is_single_selection or only_one:
1164 if self.__data is None:
1165 return None
1166 idx = self.GetFirstSelected()
1167 if idx == -1:
1168 return None
1169 return self.__data[self.map_item_idx2data_idx(idx)]
1170
1171 data = []
1172 if self.__data is None:
1173 return data
1174 idx = self.GetFirstSelected()
1175 while idx != -1:
1176 data.append(self.__data[self.map_item_idx2data_idx(idx)])
1177 idx = self.GetNextSelected(idx)
1178
1179 return data
1180
1182 self.Select(idx = self.GetFirstSelected(), on = 0)
1183
1185
1186
1187
1188
1189
1190
1191 self.DeleteItem(item_idx)
1192 self.__tt_last_item = None
1193 self._invalidate_sorting_metadata()
1194
1195
1196
1198 event.Skip()
1199 if self.__activate_callback is not None:
1200 self.__activate_callback(event)
1201
1203 event.Skip()
1204 if self.__rightclick_callback is not None:
1205 self.__rightclick_callback(event)
1206
1208
1209 if evt.GetModifiers() != wx.MOD_CMD:
1210 evt.Skip()
1211 return
1212
1213 if unichr(evt.GetRawKeyCode()) != u'f':
1214 evt.Skip()
1215 return
1216
1217 if self.__search_dlg is not None:
1218 self.__search_dlg.Close()
1219 return
1220
1221 if len(self.__searchable_cols) == 0:
1222 return
1223
1224 if self.__search_data is None:
1225 self.__search_data = wx.FindReplaceData()
1226 self.__search_dlg = wx.FindReplaceDialog (
1227 self,
1228 self.__search_data,
1229 _('Search in list'),
1230 wx.FR_NOUPDOWN | wx.FR_NOMATCHCASE | wx.FR_NOWHOLEWORD
1231 )
1232 self.__search_dlg.Show(True)
1233
1235 """Update tooltip on mouse motion.
1236
1237 for s in dir(wx):
1238 if s.startswith('LIST_HITTEST'):
1239 print s, getattr(wx, s)
1240
1241 LIST_HITTEST_ABOVE 1
1242 LIST_HITTEST_BELOW 2
1243 LIST_HITTEST_NOWHERE 4
1244 LIST_HITTEST_ONITEM 672
1245 LIST_HITTEST_ONITEMICON 32
1246 LIST_HITTEST_ONITEMLABEL 128
1247 LIST_HITTEST_ONITEMRIGHT 256
1248 LIST_HITTEST_ONITEMSTATEICON 512
1249 LIST_HITTEST_TOLEFT 1024
1250 LIST_HITTEST_TORIGHT 2048
1251 """
1252 item_idx, where_flag = self.HitTest(wx.Point(event.X, event.Y))
1253
1254
1255 if where_flag not in [
1256 wx.LIST_HITTEST_ONITEMLABEL,
1257 wx.LIST_HITTEST_ONITEMICON,
1258 wx.LIST_HITTEST_ONITEMSTATEICON,
1259 wx.LIST_HITTEST_ONITEMRIGHT,
1260 wx.LIST_HITTEST_ONITEM
1261 ]:
1262 self.__tt_last_item = None
1263 self.SetToolTipString(self.__tt_static_part)
1264 return
1265
1266
1267 if self.__tt_last_item == item_idx:
1268 return
1269
1270
1271 self.__tt_last_item = item_idx
1272
1273
1274
1275 if item_idx == wx.NOT_FOUND:
1276 self.SetToolTipString(self.__tt_static_part)
1277 return
1278
1279
1280 if self.__data is None:
1281 self.SetToolTipString(self.__tt_static_part)
1282 return
1283
1284
1285
1286
1287
1288 if (
1289 (item_idx > (len(self.__data) - 1))
1290 or
1291 (item_idx < -1)
1292 ):
1293 self.SetToolTipString(self.__tt_static_part)
1294 print "*************************************************************"
1295 print "GNUmed has detected an inconsistency with list item tooltips."
1296 print ""
1297 print "This is not a big problem and you can keep working."
1298 print ""
1299 print "However, please send us the following so we can fix GNUmed:"
1300 print ""
1301 print "item idx: %s" % item_idx
1302 print 'where flag: %s' % where_flag
1303 print 'data list length: %s' % len(self.__data)
1304 print "*************************************************************"
1305 return
1306
1307 dyna_tt = None
1308 if self.__item_tooltip_callback is not None:
1309 dyna_tt = self.__item_tooltip_callback(self.__data[self.map_item_idx2data_idx(item_idx)])
1310
1311 if dyna_tt is None:
1312 self.SetToolTipString(self.__tt_static_part)
1313 return
1314
1315 self.SetToolTipString(dyna_tt)
1316
1317
1318
1320 self.__search_dlg.Destroy()
1321 self.__search_dlg = None
1322
1324 evt.Skip()
1325 if self.__search_dlg is None:
1326 return
1327
1328
1329
1330
1332 for row_idx in range(self.__next_line_to_search, self.ItemCount):
1333 for col_idx in range(self.ColumnCount):
1334 if col_idx not in self.__searchable_cols:
1335 continue
1336 col_val = self.GetItem(row_idx, col_idx).GetText()
1337 if regex.search(search_term, col_val, regex.U | regex.I) is not None:
1338 self.Select(row_idx)
1339 self.EnsureVisible(row_idx)
1340 if row_idx == self.ItemCount - 1:
1341
1342 self.__next_line_to_search = 0
1343 else:
1344 self.__next_line_to_search = row_idx + 1
1345 return True
1346
1347 self.__next_line_to_search = 0
1348 return False
1349
1351 self.__on_search_match(evt.GetFindString())
1352
1354 self.__on_search_match(evt.GetFindString())
1355
1356
1357
1359 return self.__activate_callback
1360
1362 if callback is None:
1363 self.Unbind(wx.EVT_LIST_ITEM_ACTIVATED)
1364 else:
1365 if not callable(callback):
1366 raise ValueError('<activate> callback is not a callable: %s' % callback)
1367 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_list_item_activated)
1368 self.__activate_callback = callback
1369
1370 activate_callback = property(_get_activate_callback, _set_activate_callback)
1371
1373 return self.__rightclick_callback
1374
1376 if callback is None:
1377 self.Unbind(wx.EVT_LIST_ITEM_RIGHT_CLICK)
1378 else:
1379 if not callable(callback):
1380 raise ValueError('<rightclick> callback is not a callable: %s' % callback)
1381 self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self._on_list_item_rightclicked)
1382 self.__rightclick_callback = callback
1383
1384 rightclick_callback = property(_get_rightclick_callback, _set_rightclick_callback)
1385
1391
1392
1393
1394
1395
1396 item_tooltip_callback = property(lambda x:x, _set_item_tooltip_callback)
1397
1399
1400 if cols is None:
1401 self.__searchable_cols = range(self.ColumnCount)
1402 return
1403
1404
1405 new_cols = {}
1406 for col in cols:
1407 if col < self.ColumnCount:
1408 new_cols[col] = True
1409 self.__searchable_cols = new_cols.keys()
1410
1411 searchable_columns = property(lambda x:x, _set_searchable_cols)
1412
1413
1414
1416 if self.itemDataMap is None:
1417 self._update_sorting_metadata()
1418 return self
1419
1421 self._cleanup_column_headers()
1422
1423 col_idx, is_ascending = self.GetSortState()
1424 col_state = self.GetColumn(col_idx)
1425 col_state.m_text += self.sort_order_tags[is_ascending]
1426 self.SetColumn(col_idx, col_state)
1427
1429 dict2sort = {}
1430 item_count = self.GetItemCount()
1431 if item_count == 0:
1432 return dict2sort
1433 col_count = self.GetColumnCount()
1434 for item_idx in range(item_count):
1435 dict2sort[item_idx] = ()
1436 if col_count == 0:
1437 continue
1438 for col_idx in range(col_count):
1439 dict2sort[item_idx] += (self.GetItem(item_idx, col_idx).GetText(), )
1440
1441 return dict2sort
1442
1444 for col_idx in range(self.ColumnCount):
1445 col_state = self.GetColumn(col_idx)
1446 if col_state.m_text.endswith(self.sort_order_tags[True]):
1447 col_state.m_text = col_state.m_text[:-len(self.sort_order_tags[True])]
1448 if col_state.m_text.endswith(self.sort_order_tags[False]):
1449 col_state.m_text = col_state.m_text[:-len(self.sort_order_tags[False])]
1450 self.SetColumn(col_idx, col_state)
1451
1456
1459
1461
1462
1463
1464
1465
1466
1467 event.Skip()
1468
1469
1470
1471
1472 if __name__ == '__main__':
1473
1474 if len(sys.argv) < 2:
1475 sys.exit()
1476
1477 if sys.argv[1] != 'test':
1478 sys.exit()
1479
1480 sys.path.insert(0, '../../')
1481
1482 from Gnumed.pycommon import gmI18N
1483 gmI18N.activate_locale()
1484 gmI18N.install_domain()
1485
1486
1488 app = wx.PyWidgetTester(size = (400, 500))
1489 dlg = wx.MultiChoiceDialog (
1490 parent = None,
1491 message = 'test message',
1492 caption = 'test caption',
1493 choices = ['a', 'b', 'c', 'd', 'e']
1494 )
1495 dlg.ShowModal()
1496 sels = dlg.GetSelections()
1497 print "selected:"
1498 for sel in sels:
1499 print sel
1500
1502
1503 def edit(argument):
1504 print "editor called with:"
1505 print argument
1506
1507 def refresh(lctrl):
1508 choices = ['a', 'b', 'c']
1509 lctrl.set_string_items(choices)
1510
1511 app = wx.PyWidgetTester(size = (200, 50))
1512 chosen = get_choices_from_list (
1513
1514 caption = 'select health issues',
1515
1516
1517 columns = ['issue'],
1518 refresh_callback = refresh
1519
1520 )
1521 print "chosen:"
1522 print chosen
1523
1525 app = wx.PyWidgetTester(size = (200, 50))
1526 dlg = cItemPickerDlg(None, -1, msg = 'Pick a few items:')
1527 dlg.set_columns(['Plugins'], ['Load in workplace', 'dummy'])
1528
1529 dlg.set_string_items(['patient', 'emr', 'docs'])
1530 result = dlg.ShowModal()
1531 print result
1532 print dlg.get_picks()
1533
1534
1535
1536 test_item_picker_dlg()
1537
1538
1539
1540