1 __doc__ = """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 threading
25 import time
26 import locale
27 import os
28 import io
29 import csv
30 import re as regex
31 import datetime as pydt
32
33
34 import wx
35 import wx.lib.mixins.listctrl as listmixins
36
37
38 if __name__ == '__main__':
39 sys.path.insert(0, '../../')
40 from Gnumed.pycommon import gmTools
41 from Gnumed.pycommon import gmDispatcher
42
43
44 _log = logging.getLogger('gm.list_ui')
45
46
47
48 -def get_choices_from_list (
49 parent=None,
50 msg=None,
51 caption=None,
52 columns=None,
53 choices=None,
54 data=None,
55 selections=None,
56 edit_callback=None,
57 new_callback=None,
58 delete_callback=None,
59 refresh_callback=None,
60 single_selection=False,
61 can_return_empty=False,
62 ignore_OK_button=False,
63 left_extra_button=None,
64 middle_extra_button=None,
65 right_extra_button=None,
66 list_tooltip_callback=None):
138
139
140 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg
141
143 """A dialog holding a list and a few buttons to act on the items."""
144
186
187
190
191
194
195
199
200
201
212
213
216
217
220
221
222
223
225 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
226 if not self.can_return_empty:
227 self._BTN_cancel.SetDefault()
228 self._BTN_ok.Enable(False)
229 self._BTN_edit.Enable(False)
230 self._BTN_delete.Enable(False)
231
232 event.Skip()
233
234
238
239
245
246
275
276
295
296
315
316
335
336
337
338
351
352
355
356
358 any_deleted = False
359 for item_data in self._LCTRL_items.get_selected_item_data(only_one = False):
360 if item_data is None:
361 continue
362 if self.__delete_callback(item_data):
363 any_deleted = True
364
365 self._LCTRL_items.SetFocus()
366
367 if any_deleted is False:
368 return
369
370 if self.__refresh_callback is None:
371 return
372
373 wx.BeginBusyCursor()
374 try:
375 self.__refresh_callback(lctrl = self._LCTRL_items)
376 self._LCTRL_items.set_column_widths()
377 finally:
378 wx.EndBusyCursor()
379
380
383
384
386 if not self.__edit_callback(self._LCTRL_items.get_selected_item_data(only_one = True)):
387 self._LCTRL_items.SetFocus()
388 return
389 if self.__refresh_callback is None:
390 self._LCTRL_items.SetFocus()
391 return
392 wx.BeginBusyCursor()
393 try:
394 self.__refresh_callback(lctrl = self._LCTRL_items)
395 self._LCTRL_items.set_column_widths()
396 self._LCTRL_items.SetFocus()
397 finally:
398 wx.EndBusyCursor()
399
400
403
404
406 if not self.__new_callback():
407 self._LCTRL_items.SetFocus()
408 return
409 if self.__refresh_callback is None:
410 self._LCTRL_items.SetFocus()
411 return
412 wx.BeginBusyCursor()
413 try:
414 self.__refresh_callback(lctrl = self._LCTRL_items)
415 self._LCTRL_items.set_column_widths()
416 self._LCTRL_items.SetFocus()
417 finally:
418 wx.EndBusyCursor()
419
420
421
422
436
437 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button)
438
439
462
463 left_extra_button = property(lambda x:x, _set_left_extra_button)
464
465
488
489 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
490
491
514
515 right_extra_button = property(lambda x:x, _set_right_extra_button)
516
517
519 return self.__new_callback
520
522 if callback is not None:
523 if self.__refresh_callback is None:
524 raise ValueError('refresh callback must be set before new callback can be set')
525 if not callable(callback):
526 raise ValueError('<new> callback is not a callable: %s' % callback)
527 self.__new_callback = callback
528
529 if callback is None:
530 self._BTN_new.Enable(False)
531 self._BTN_new.Hide()
532 self._LCTRL_items.new_callback = None
533 else:
534 self._BTN_new.Enable(True)
535 self._BTN_new.Show()
536 self._LCTRL_items.new_callback = self._on_insert_key_pressed_in_listctrl
537
538 new_callback = property(_get_new_callback, _set_new_callback)
539
540
542 return self.__edit_callback
543
545 if callback is not None:
546 if not callable(callback):
547 raise ValueError('<edit> callback is not a callable: %s' % callback)
548 self.__edit_callback = callback
549
550 if callback is None:
551 self._BTN_edit.Enable(False)
552 self._BTN_edit.Hide()
553 self._LCTRL_items.edit_callback = None
554 else:
555 self._BTN_edit.Enable(True)
556 self._BTN_edit.Show()
557 self._LCTRL_items.edit_callback = self._on_edit_invoked_in_listctrl
558
559 edit_callback = property(_get_edit_callback, _set_edit_callback)
560
561
563 return self.__delete_callback
564
566 if callback is not None:
567 if self.__refresh_callback is None:
568 raise ValueError('refresh callback must be set before delete callback can be set')
569 if not callable(callback):
570 raise ValueError('<delete> callback is not a callable: %s' % callback)
571 self.__delete_callback = callback
572 if callback is None:
573 self._BTN_delete.Enable(False)
574 self._BTN_delete.Hide()
575 self._LCTRL_items.delete_callback = None
576 else:
577 self._BTN_delete.Enable(True)
578 self._BTN_delete.Show()
579 self._LCTRL_items.delete_callback = self._on_delete_key_pressed_in_listctrl
580
581 delete_callback = property(_get_delete_callback, _set_delete_callback)
582
583
585 return self.__refresh_callback
586
588 wx.BeginBusyCursor()
589 try:
590 self.__refresh_callback(lctrl = self._LCTRL_items)
591 finally:
592 wx.EndBusyCursor()
593 self._LCTRL_items.set_column_widths()
594
596 if callback is not None:
597 if not callable(callback):
598 raise ValueError('<refresh> callback is not a callable: %s' % callback)
599 self.__refresh_callback = callback
600 if callback is not None:
601 wx.CallAfter(self._set_refresh_callback_helper)
602
603 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
604
605
607 return self.__select_callback
608
610 if callback is not None:
611 if not callable(callback):
612 raise ValueError('<select> callback is not a callable: %s' % callback)
613 self.__select_callback = callback
614
615 select_callback = property(_get_select_callback, _set_select_callback)
616
617
620
621 list_tooltip_callback = property(lambda x:x, _set_list_tooltip_callback)
622
623
624
626 if message is None:
627 self._LBL_message.Hide()
628 return
629 self._LBL_message.SetLabel(message)
630 self._LBL_message.Show()
631
632 message = property(lambda x:x, _set_message)
633
634
635 from Gnumed.wxGladeWidgets import wxgGenericListManagerPnl
636
638 """A panel holding a generic multi-column list and action buttions."""
639
666
667
668
669
672
673
681
682
683
684
687
688
691
692
695
696
697
698
700 event.Skip()
701 if self.__edit_callback is not None:
702 self._BTN_edit.Enable(True)
703 if self.__delete_callback is not None:
704 self._BTN_remove.Enable(True)
705 if self.__select_callback is not None:
706 item = self._LCTRL_items.get_selected_item_data(only_one = True)
707 self.__select_callback(item)
708
709
712
713
715 if not self.__delete_callback(self._LCTRL_items.get_selected_item_data(only_one = True)):
716 return
717 if self.__refresh_callback is None:
718 self._LCTRL_items.SetFocus()
719 return
720 wx.BeginBusyCursor()
721 try:
722 self.__refresh_callback(lctrl = self._LCTRL_items)
723 self._LCTRL_items.set_column_widths()
724 self._LCTRL_items.SetFocus()
725 finally:
726 wx.EndBusyCursor()
727
728
731
732
734 if not self.__edit_callback(self._LCTRL_items.get_selected_item_data(only_one = True)):
735 self._LCTRL_items.SetFocus()
736 return
737 if self.__refresh_callback is None:
738 self._LCTRL_items.SetFocus()
739 return
740 wx.BeginBusyCursor()
741 try:
742 self.__refresh_callback(lctrl = self._LCTRL_items)
743 self._LCTRL_items.set_column_widths()
744 self._LCTRL_items.SetFocus()
745 finally:
746 wx.EndBusyCursor()
747
748
751
752
754 if not self.__new_callback():
755 self._LCTRL_items.SetFocus()
756 return
757 if self.__refresh_callback is None:
758 self._LCTRL_items.SetFocus()
759 return
760 wx.BeginBusyCursor()
761 try:
762 self.__refresh_callback(lctrl = self._LCTRL_items)
763 self._LCTRL_items.set_column_widths()
764 self._LCTRL_items.SetFocus()
765 finally:
766 wx.EndBusyCursor()
767
768
769
770
772 event.Skip()
773 if self._LCTRL_items.get_selected_items(only_one = True) == -1:
774 self._BTN_edit.Enable(False)
775 self._BTN_remove.Enable(False)
776 if self.__select_callback is not None:
777 self.__select_callback(None)
778
779
781 event.Skip()
782 if self.__edit_callback is None:
783 return
784 self._on_edit_button_pressed(event)
785
786
797
798
812
813
818
819
835
836
852
853
869
870
871
872
874 return self.__new_callback
875
877 if callback is not None:
878 if self.__refresh_callback is None:
879 raise ValueError('refresh callback must be set before new callback can be set')
880 if not callable(callback):
881 raise ValueError('<new> callback is not a callable: %s' % callback)
882 self.__new_callback = callback
883
884 if callback is None:
885 self._BTN_add.Enable(False)
886 self._BTN_add.Hide()
887 self._LCTRL_items.new_callback = None
888 else:
889 self._BTN_add.Enable(True)
890 self._BTN_add.Show()
891 self._LCTRL_items.new_callback = self._on_insert_key_pressed_in_listctrl
892
893 new_callback = property(_get_new_callback, _set_new_callback)
894
895
897 return self.__edit_callback
898
900 if callback is not None:
901 if not callable(callback):
902 raise ValueError('<edit> callback is not a callable: %s' % callback)
903 self.__edit_callback = callback
904
905 if callback is None:
906 self._BTN_edit.Enable(False)
907 self._BTN_edit.Hide()
908 self._LCTRL_items.edit_callback = None
909 else:
910 self._BTN_edit.Enable(True)
911 self._BTN_edit.Show()
912 self._LCTRL_items.edit_callback = self._on_edit_invoked_in_listctrl
913
914 edit_callback = property(_get_edit_callback, _set_edit_callback)
915
916
918 return self.__delete_callback
919
921 if callback is not None:
922 if self.__refresh_callback is None:
923 raise ValueError('refresh callback must be set before delete callback can be set')
924 if not callable(callback):
925 raise ValueError('<delete> callback is not a callable: %s' % callback)
926 self.__delete_callback = callback
927 if callback is None:
928 self._BTN_remove.Enable(False)
929 self._BTN_remove.Hide()
930 self._LCTRL_items.delete_callback = None
931 else:
932 self._BTN_remove.Enable(True)
933 self._BTN_remove.Show()
934 self._LCTRL_items.delete_callback = self._on_delete_key_pressed_in_listctrl
935
936 delete_callback = property(_get_delete_callback, _set_delete_callback)
937
938
940 return self.__refresh_callback
941
943 wx.BeginBusyCursor()
944 try:
945 self.__refresh_callback(lctrl = self._LCTRL_items)
946 finally:
947 wx.EndBusyCursor()
948 self._LCTRL_items.set_column_widths()
949
951 if callback is not None:
952 if not callable(callback):
953 raise ValueError('<refresh> callback is not a callable: %s' % callback)
954 self.__refresh_callback = callback
955 if callback is not None:
956 wx.CallAfter(self._set_refresh_callback_helper)
957
958 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
959
960
962 return self.__select_callback
963
965 if callback is not None:
966 if not callable(callback):
967 raise ValueError('<select> callback is not a callable: %s' % callback)
968 self.__select_callback = callback
969
970 select_callback = property(_get_select_callback, _set_select_callback)
971
972
975
977 if msg is None:
978 self._LBL_message.Hide()
979 self._LBL_message.SetLabel('')
980 else:
981 self._LBL_message.SetLabel(msg)
982 self._LBL_message.Show()
983 self.Layout()
984
985 message = property(_get_message, _set_message)
986
987
1003
1004 left_extra_button = property(lambda x:x, _set_left_extra_button)
1005
1006
1022
1023 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
1024
1025
1041
1042 right_extra_button = property(lambda x:x, _set_right_extra_button)
1043
1044
1045 from Gnumed.wxGladeWidgets import wxgItemPickerDlg
1046
1048
1050
1051 try:
1052 msg = kwargs['msg']
1053 del kwargs['msg']
1054 except KeyError:
1055 msg = None
1056
1057 try:
1058 title = kwargs['title']
1059 if not title.startswith('GMd: '):
1060 kwargs['title'] = 'GMd: %s' % title
1061 except KeyError:
1062 kwargs['title'] = 'GMd: %s' % self.__class__.__name__
1063
1064 wxgItemPickerDlg.wxgItemPickerDlg.__init__(self, *args, **kwargs)
1065
1066 if msg is None:
1067 self._LBL_msg.Hide()
1068 else:
1069 self._LBL_msg.SetLabel(msg)
1070
1071 self.ignore_dupes_on_picking = True
1072
1073 self._LCTRL_left.activate_callback = self.__pick_selected
1074 self.__extra_button_callback = None
1075
1076 self._LCTRL_left.SetFocus()
1077
1078
1079
1080
1081 - def set_columns(self, columns=None, columns_right=None):
1082 self._LCTRL_left.set_columns(columns = columns)
1083 if columns_right is None:
1084 self._LCTRL_right.set_columns(columns = columns)
1085 return
1086
1087 if len(columns_right) < len(columns):
1088 cols = columns
1089 else:
1090 cols = columns_right[:len(columns)]
1091 self._LCTRL_right.set_columns(columns = cols)
1092
1093
1101
1102
1105
1106
1107 - def set_choices(self, choices=None, data=None, reshow=True):
1111
1112
1113 - def set_picks(self, picks=None, data=None, reshow=True):
1118
1119
1122
1123
1126
1127 picks = property(get_picks, lambda x:x)
1128
1129
1145
1146 extra_button = property(lambda x:x, _set_extra_button)
1147
1148
1149
1150
1181
1182
1183
1184
1186 if self._LCTRL_right.get_selected_items(only_one = True) == -1:
1187 return
1188
1189 for item_idx in self._LCTRL_right.get_selected_items(only_one = False):
1190 self._LCTRL_right.remove_item(item_idx)
1191
1192 if self._LCTRL_right.GetItemCount() == 0:
1193 self._BTN_right2left.Enable(False)
1194
1195
1196
1197
1198
1199
1200
1202 self._BTN_left2right.Enable(True)
1203
1207
1209 self._BTN_right2left.Enable(True)
1210
1214
1217
1220
1223
1226
1227 left_item_tooltip_callback = property(lambda x:x, _set_left_item_tooltip_callback)
1228
1231
1232 right_item_tooltip_callback = property(lambda x:x, _set_right_item_tooltip_callback)
1233
1234
1235 -class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorterMixin, wx.ListCtrl):
1236
1237
1238
1239
1240
1241
1242
1243
1244 sort_order_tags = {
1245 True: ' [\u03b1\u0391 \u2192 \u03c9\u03A9]',
1246 False: ' [\u03c9\u03A9 \u2192 \u03b1\u0391]'
1247 }
1248
1250
1251 self.debug = None
1252 self.map_item_idx2data_idx = self.GetItemData
1253
1254 try:
1255 kwargs['style'] = kwargs['style'] | wx.LC_REPORT
1256 except KeyError:
1257 kwargs['style'] = wx.LC_REPORT
1258
1259 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL)
1260
1261 wx.ListCtrl.__init__(self, *args, **kwargs)
1262 listmixins.ListCtrlAutoWidthMixin.__init__(self)
1263
1264
1265 self._invalidate_sorting_metadata()
1266 listmixins.ColumnSorterMixin.__init__(self, 0)
1267 self.__secondary_sort_col = None
1268
1269 self.Bind(wx.EVT_LIST_COL_CLICK, self._on_col_click, self)
1270
1271
1272 self.__widths = None
1273 self.__data = None
1274
1275
1276 self.__select_callback = None
1277 self.__deselect_callback = None
1278 self.__activate_callback = None
1279 self.__new_callback = None
1280 self.__edit_callback = None
1281 self.__delete_callback = None
1282
1283
1284 self.__extend_popup_menu_callback = None
1285 self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self._on_list_item_rightclicked)
1286
1287
1288 self.__item_tooltip_callback = None
1289 self.__tt_last_item = None
1290
1291
1292
1293
1294
1295
1296
1297
1298 self.__tt_static_part_base = ''
1299 self.__tt_static_part = self.__tt_static_part_base
1300 self.Bind(wx.EVT_MOTION, self._on_mouse_motion)
1301
1302
1303 self.__search_term = None
1304 self.__next_line_to_search = 0
1305 self.__searchable_cols = None
1306
1307
1308
1309 self.Bind(wx.EVT_CHAR, self._on_char)
1310 self.Bind(wx.EVT_LIST_KEY_DOWN, self._on_list_key_down)
1311
1312
1313
1314
1316 if self.debug is None:
1317 return False
1318 if not self.debug.endswith('_sizing'):
1319 return False
1320 _log.debug('[%s.%s]: *args = (%s), **kwargs = (%s)', self.debug, caller_name, str(args), str(kwargs))
1321 return True
1322
1323
1325 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1326 return super(cReportListCtrl, self).CacheBestSize(*args, **kwargs)
1327
1328
1329 - def Fit(self, *args, **kwargs):
1330 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1331 return super(cReportListCtrl, self).Fit(*args, **kwargs)
1332
1333
1335 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1336 return super(cReportListCtrl, self).FitInside(*args, **kwargs)
1337
1338
1342
1343
1347
1348
1352
1353
1355 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1356 return super(cReportListCtrl, self).SetClientSize(*args, **kwargs)
1357
1358
1362
1363
1367
1368
1370 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1371 return super(cReportListCtrl, self).SetMaxSize(*args, **kwargs)
1372
1373
1377
1378
1380 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1381 return super(cReportListCtrl, self).SetMinSize(*args, **kwargs)
1382
1383
1384 - def SetSize(self, *args, **kwargs):
1385 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1386 return super(cReportListCtrl, self).SetSize(*args, **kwargs)
1387
1388
1390 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1391 return super(cReportListCtrl, self).SetSizeHints(*args, **kwargs)
1392
1393
1397
1398
1400 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1401 return super(cReportListCtrl, self).SetSizeWH(*args, **kwargs)
1402
1403
1407
1408
1412
1413
1417
1418
1422
1423
1425 res = super(cReportListCtrl, self).GetAdjustedBestSize(*args, **kwargs)
1426 kwargs['sizing_function_result'] = res
1427 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1428 return res
1429
1430
1432 res = super(cReportListCtrl, self).GetEffectiveMinSize(*args, **kwargs)
1433 kwargs['sizing_function_result'] = res
1434 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1435 return res
1436
1437
1439 res = super(cReportListCtrl, self).GetBestSize(*args, **kwargs)
1440 kwargs['sizing_function_result'] = res
1441 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1442 return res
1443
1444
1446 res = super(cReportListCtrl, self).GetBestSizeTuple(*args, **kwargs)
1447 kwargs['sizing_function_result'] = res
1448 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1449 return res
1450
1451
1453 res = super(cReportListCtrl, self).GetBestVirtualSize(*args, **kwargs)
1454 kwargs['sizing_function_result'] = res
1455 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1456 return res
1457
1458
1460 res = super(cReportListCtrl, self).GetClientSize(*args, **kwargs)
1461 kwargs['sizing_function_result'] = res
1462 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1463 return res
1464
1465
1467 res = super(cReportListCtrl, self).GetClientSize(*args, **kwargs)
1468 kwargs['sizing_function_result'] = res
1469 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1470 return res
1471
1472
1474 res = super(cReportListCtrl, self).GetMaxClientSize(*args, **kwargs)
1475 kwargs['sizing_function_result'] = res
1476 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1477 return res
1478
1479
1481 res = super(cReportListCtrl, self).GetMaxHeight(*args, **kwargs)
1482 kwargs['sizing_function_result'] = res
1483 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1484 return res
1485
1486
1488 res = super(cReportListCtrl, self).GetMaxSize(*args, **kwargs)
1489 kwargs['sizing_function_result'] = res
1490 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1491 return res
1492
1493
1495 res = super(cReportListCtrl, self).GetMaxWidth(*args, **kwargs)
1496 kwargs['sizing_function_result'] = res
1497 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1498 return res
1499
1500
1502 res = super(cReportListCtrl, self).GetMinClientSize(*args, **kwargs)
1503 kwargs['sizing_function_result'] = res
1504 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1505 return res
1506
1507
1509 res = super(cReportListCtrl, self).GetMinHeight(*args, **kwargs)
1510 kwargs['sizing_function_result'] = res
1511 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1512 return res
1513
1514
1516 res = super(cReportListCtrl, self).GetMinSize(*args, **kwargs)
1517 kwargs['sizing_function_result'] = res
1518 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1519 return res
1520
1521
1523 res = super(cReportListCtrl, self).GetMinWidth(*args, **kwargs)
1524 kwargs['sizing_function_result'] = res
1525 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1526 return res
1527
1528
1529 - def GetSize(self, *args, **kwargs):
1530 res = super(cReportListCtrl, self).GetSize(*args, **kwargs)
1531 kwargs['sizing_function_result'] = res
1532 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1533 return res
1534
1535
1537 res = super(cReportListCtrl, self).GetVirtualSize(*args, **kwargs)
1538 kwargs['sizing_function_result'] = res
1539 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1540 return res
1541
1542
1544 res = super(cReportListCtrl, self).GetVirtualSizeTuple(*args, **kwargs)
1545 kwargs['sizing_function_result'] = res
1546 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1547 return res
1548
1549
1550
1551
1553 """(Re)define the columns.
1554
1555 Note that this will (have to) delete the items.
1556 """
1557 self.ClearAll()
1558 self.__tt_last_item = None
1559 if columns is None:
1560 return
1561 for idx in range(len(columns)):
1562 self.InsertColumn(idx, columns[idx])
1563
1564 listmixins.ColumnSorterMixin.__init__(self, 0)
1565 self._invalidate_sorting_metadata()
1566
1567
1569 """Set the column width policy.
1570
1571 widths = None:
1572 use previous policy if any or default policy
1573 widths != None:
1574 use this policy and remember it for later calls
1575
1576 options:
1577 wx.LIST_AUTOSIZE_USEHEADER
1578 wx.LIST_AUTOSIZE
1579
1580 This means there is no way to *revert* to the default policy :-(
1581 """
1582
1583 if widths is not None:
1584 self.__widths = widths
1585 for idx in range(len(self.__widths)):
1586 self.SetColumnWidth(idx, self.__widths[idx])
1587 return
1588
1589
1590 if self.__widths is not None:
1591 for idx in range(len(self.__widths)):
1592 self.SetColumnWidth(idx, self.__widths[idx])
1593 return
1594
1595
1596 if self.GetItemCount() == 0:
1597 width_type = wx.LIST_AUTOSIZE_USEHEADER
1598 else:
1599 width_type = wx.LIST_AUTOSIZE
1600 for idx in range(self.GetColumnCount()):
1601 self.SetColumnWidth(idx, width_type)
1602
1603
1605 if column != 'LAST':
1606 if column > self.ColumnCount:
1607 return
1608
1609 self.setResizeColumn(column)
1610
1611
1613 tries = 0
1614 while tries < max_tries:
1615 if self.debug is not None:
1616 if self.debug.endswith('_deleting'):
1617 _log.debug('[round %s] <%s>.GetItemCount() before DeleteAllItems(): %s (thread [%s])', tries, self.debug, self.GetItemCount(), threading.get_ident())
1618 if not self.DeleteAllItems():
1619 _log.error('<%s>.DeleteAllItems() failed', self.debug)
1620 item_count = self.GetItemCount()
1621 if item_count == 0:
1622 return True
1623 wx.SafeYield(None, True)
1624 _log.error('<%s>.GetItemCount() not 0 (rather: %s) after DeleteAllItems()', self.debug, item_count)
1625 time.sleep(0.3)
1626 wx.SafeYield(None, True)
1627 tries += 1
1628
1629 _log.error('<%s>: unable to delete list items after looping %s times', self.debug, max_tries)
1630 return False
1631
1632
1634 """All item members must be str()able or None."""
1635
1636 wx.BeginBusyCursor()
1637 self._invalidate_sorting_metadata()
1638
1639 if self.ItemCount == 0:
1640 topmost_visible = 0
1641 else:
1642 topmost_visible = self.GetFirstSelected()
1643 if topmost_visible == -1:
1644 topmost_visible = self.GetFocusedItem()
1645 if topmost_visible == -1:
1646 topmost_visible = self.TopItem
1647
1648 if not self.remove_items_safely(max_tries = 3):
1649 _log.error("cannot remove items (!?), continuing and hoping for the best")
1650
1651 if items is None:
1652 self.data = None
1653 wx.EndBusyCursor()
1654 return
1655
1656
1657 for item in items:
1658
1659
1660 if isinstance(item, str):
1661 self.InsertItem(index = sys.maxsize, label = item.replace('\r\n', ' [CRLF] ').replace('\n', ' [LF] '))
1662 continue
1663
1664 try:
1665
1666 col_val = str(item[0])
1667 row_num = self.InsertItem(index = sys.maxsize, label = col_val)
1668 for col_num in range(1, min(self.GetColumnCount(), len(item))):
1669 col_val = str(item[col_num]).replace('\r\n', ' [CRLF] ').replace('\n', ' [LF] ')
1670 self.SetItem(index = row_num, column = col_num, label = col_val)
1671 except (TypeError, KeyError, IndexError):
1672
1673
1674 col_val = str(item).replace('\r\n', ' [CRLF] ').replace('\n', ' [LF] ')
1675 self.InsertItem(index = sys.maxsize, label = col_val)
1676
1677 if reshow:
1678 if self.ItemCount > 0:
1679 if topmost_visible < self.ItemCount:
1680 self.EnsureVisible(topmost_visible)
1681 self.Focus(topmost_visible)
1682 else:
1683 self.EnsureVisible(self.ItemCount - 1)
1684 self.Focus(self.ItemCount - 1)
1685
1686
1687 self.data = items
1688
1689 wx.EndBusyCursor()
1690
1691
1693 if self.ItemCount == 0:
1694 return []
1695
1696 rows = []
1697 for row_idx in range(self.ItemCount):
1698 row = []
1699 for col_idx in range(self.ColumnCount):
1700 row.append(self.GetItem(row_idx, col_idx).GetText())
1701 rows.append(row)
1702 return rows
1703
1704
1705
1706
1707 string_items = property(get_string_items, set_string_items)
1708
1709
1711 if len(new_items) == 0:
1712 return
1713
1714 if new_data is None:
1715 new_data = new_items
1716
1717 existing_data = self.get_item_data()
1718 if existing_data is None:
1719 existing_data = []
1720
1721 if allow_dupes:
1722 self._LCTRL_right.set_string_items (
1723 items = self.string_items.extend(new_items),
1724 reshow = True
1725 )
1726 self.data = existing_data.extend(new_data)
1727 self.set_column_widths()
1728 return
1729
1730 existing_items = self.get_string_items()
1731 for new_item, new_data in zip(new_items, new_data):
1732 if new_item in existing_items:
1733 continue
1734 existing_items.append(new_item)
1735 existing_data.append(new_data)
1736 self.set_string_items (
1737 items = existing_items,
1738 reshow = True
1739 )
1740 self.data = existing_data
1741 self.set_column_widths()
1742
1743
1745 """<data> assumed to be a list corresponding to the item indices"""
1746 if data is not None:
1747 item_count = self.GetItemCount()
1748 if len(data) != item_count:
1749 _log.debug('<data> length (%s) must be equal to number of list items (%s) (%s, thread [%s])', len(data), item_count, self.debug, threading.get_ident())
1750 for item_idx in range(len(data)):
1751 self.SetItemData(item_idx, item_idx)
1752 self.__data = data
1753 self.__tt_last_item = None
1754
1755
1756 return
1757
1764
1765 data = property(_get_data, set_data)
1766
1767
1769
1770 if self.GetItemCount() > 0:
1771 self.Select(0, on = 0)
1772 if selections is None:
1773 return
1774 for idx in selections:
1775 self.Select(idx = idx, on = 1)
1776
1778 if self.ItemCount == 0:
1779 return []
1780 if self.__is_single_selection:
1781 return [self.GetFirstSelected()]
1782 selections = []
1783 idx = self.GetFirstSelected()
1784 while idx != -1:
1785 selections.append(idx)
1786 idx = self.GetNextSelected(idx)
1787 return selections
1788
1789 selections = property(__get_selections, set_selections)
1790
1791
1792
1793
1795 labels = []
1796 for col_idx in range(self.ColumnCount):
1797 col = self.GetColumn(col = col_idx)
1798 labels.append(col.Text)
1799 return labels
1800
1801 column_labels = property(get_column_labels, lambda x:x)
1802
1803
1805 if self.ItemCount == 0:
1806 _log.warning('no items')
1807 return None
1808 if item_idx is not None:
1809 return self.GetItem(item_idx)
1810 _log.error('get_item(None) called')
1811 return None
1812
1813
1815 if self.ItemCount == 0:
1816 return []
1817 return [ self.GetItem(item_idx) for item_idx in range(self.ItemCount) ]
1818
1819 items = property(get_items, lambda x:x)
1820
1821
1823
1824 if self.ItemCount == 0:
1825 if self.__is_single_selection or only_one:
1826 return None
1827 return []
1828
1829 if self.__is_single_selection or only_one:
1830 return self.GetFirstSelected()
1831
1832 items = []
1833 idx = self.GetFirstSelected()
1834 while idx != -1:
1835 items.append(idx)
1836 idx = self.GetNextSelected(idx)
1837
1838 return items
1839
1840 selected_items = property(get_selected_items, lambda x:x)
1841
1842
1844
1845 if self.ItemCount == 0:
1846 if self.__is_single_selection or only_one:
1847 return None
1848 return []
1849
1850 if self.__is_single_selection or only_one:
1851 return self.GetItemText(self.GetFirstSelected())
1852
1853 items = []
1854 idx = self.GetFirstSelected()
1855 while idx != -1:
1856 items.append(self.GetItemText(idx))
1857 idx = self.GetNextSelected(idx)
1858
1859 return items
1860
1861 selected_string_items = property(get_selected_string_items, lambda x:x)
1862
1863
1865
1866 if self.__data is None:
1867 return None
1868
1869 if item_idx is not None:
1870 return self.__data[self.map_item_idx2data_idx(item_idx)]
1871
1872
1873
1874
1875 return [ self.__data[self.map_item_idx2data_idx(item_idx)] for item_idx in range(self.GetItemCount()) ]
1876
1877 item_data = property(get_item_data, lambda x:x)
1878
1879
1881
1882 if self.__is_single_selection or only_one:
1883 if self.__data is None:
1884 return None
1885 idx = self.GetFirstSelected()
1886 if idx == -1:
1887 return None
1888 return self.__data[self.map_item_idx2data_idx(idx)]
1889
1890 data = []
1891 if self.__data is None:
1892 return data
1893 idx = self.GetFirstSelected()
1894 while idx != -1:
1895 data.append(self.__data[self.map_item_idx2data_idx(idx)])
1896 idx = self.GetNextSelected(idx)
1897
1898 return data
1899
1900 selected_item_data = property(get_selected_item_data, lambda x:x)
1901
1902
1904 self.Select(idx = self.GetFirstSelected(), on = 0)
1905
1906
1908
1909
1910
1911
1912
1913
1914 self.DeleteItem(item_idx)
1915 self.__tt_last_item = None
1916 self._invalidate_sorting_metadata()
1917
1918
1919
1920
1922
1923 if item_idx == -1:
1924 return
1925
1926 if self.ItemCount == 0:
1927 return
1928
1929 items = self.selected_items
1930 if self.__is_single_selection:
1931 if items is None:
1932 no_of_selected_items = 0
1933 else:
1934 no_of_selected_items = 1
1935 else:
1936 no_of_selected_items = len(items)
1937 if no_of_selected_items == 0:
1938 title = _('List Item Actions')
1939 elif no_of_selected_items == 1:
1940 title = _('List Item Actions (selected: 1 entry)')
1941 else:
1942 title = _('List Item Actions (selected: %s entries)') % no_of_selected_items
1943
1944
1945 self._context_menu = wx.Menu(title = title)
1946
1947 needs_separator = False
1948 if self.__new_callback is not None:
1949 menu_item = self._context_menu.Append(-1, _('Add (<INS>)'))
1950 self.Bind(wx.EVT_MENU, self._on_add_row, menu_item)
1951 needs_separator = True
1952 if self.__edit_callback is not None:
1953 menu_item = self._context_menu.Append(-1, _('&Edit'))
1954 self.Bind(wx.EVT_MENU, self._on_edit_row, menu_item)
1955 needs_separator = True
1956 if self.__delete_callback is not None:
1957 menu_item = self._context_menu.Append(-1, _('Delete (<DEL>)'))
1958 self.Bind(wx.EVT_MENU, self._on_delete_row, menu_item)
1959 needs_separator = True
1960 if needs_separator:
1961 self._context_menu.AppendSeparator()
1962
1963 menu_item = self._context_menu.Append(-1, _('Find (<CTRL-F>)'))
1964 self.Bind(wx.EVT_MENU, self._on_show_search_dialog, menu_item)
1965 if self.__search_term is not None:
1966 if self.__search_term.strip() != '':
1967 menu_item = self._context_menu.Append(-1, _('Find next [%s] (<CTRL-N>)') % self.__search_term)
1968 self.Bind(wx.EVT_MENU, self._on_search_match, menu_item)
1969 self._context_menu.AppendSeparator()
1970
1971 col_headers = []
1972 self._rclicked_row_idx = item_idx
1973 self._rclicked_row_data = self.get_item_data(item_idx = self._rclicked_row_idx)
1974 self._rclicked_row_cells = []
1975 self._rclicked_row_cells_w_hdr = []
1976 for col_idx in range(self.ColumnCount):
1977 cell_content = self.GetItem(self._rclicked_row_idx, col_idx).Text.strip()
1978 col_header = self.GetColumn(col_idx).Text.strip()
1979 col_headers.append(col_header)
1980 self._rclicked_row_cells.append(cell_content)
1981 self._rclicked_row_cells_w_hdr.append('%s: %s' % (col_header, cell_content))
1982
1983
1984 save_menu = wx.Menu()
1985 menu_item = save_menu.Append(-1, _('&All rows'))
1986 self.Bind(wx.EVT_MENU, self._all_rows2file, menu_item)
1987 menu_item = save_menu.Append(-1, _('All rows as &CSV'))
1988 self.Bind(wx.EVT_MENU, self._all_rows2csv, menu_item)
1989 menu_item = save_menu.Append(-1, _('&Tooltips of all rows'))
1990 self.Bind(wx.EVT_MENU, self._all_row_tooltips2file, menu_item)
1991 menu_item = save_menu.Append(-1, _('&Data of all rows'))
1992 self.Bind(wx.EVT_MENU, self._all_row_data2file, menu_item)
1993
1994 if no_of_selected_items > 1:
1995 save_menu.AppendSeparator()
1996 menu_item = save_menu.Append(-1, _('&Selected rows'))
1997 self.Bind(wx.EVT_MENU, self._selected_rows2file, menu_item)
1998 menu_item = save_menu.Append(-1, _('&Selected rows as CSV'))
1999 self.Bind(wx.EVT_MENU, self._selected_rows2csv, menu_item)
2000 menu_item = save_menu.Append(-1, _('&Tooltips of selected rows'))
2001 self.Bind(wx.EVT_MENU, self._selected_row_tooltips2file, menu_item)
2002 menu_item = save_menu.Append(-1, _('&Data of selected rows'))
2003 self.Bind(wx.EVT_MENU, self._selected_row_data2file, menu_item)
2004
2005
2006 clip_menu = wx.Menu()
2007
2008
2009 if no_of_selected_items > 1:
2010
2011 menu_item = clip_menu.Append(-1, _('Tooltips of selected rows'))
2012 self.Bind(wx.EVT_MENU, self._tooltips2clipboard, menu_item)
2013
2014 menu_item = clip_menu.Append(-1, _('Data (formatted as text) of selected rows'))
2015 self.Bind(wx.EVT_MENU, self._datas2clipboard, menu_item)
2016
2017 menu_item = clip_menu.Append(-1, _('Content (as one line each) of selected rows'))
2018 self.Bind(wx.EVT_MENU, self._rows2clipboard, menu_item)
2019 clip_menu.AppendSeparator()
2020
2021
2022
2023 menu_item = clip_menu.Append(-1, _('Tooltip of current row'))
2024 self.Bind(wx.EVT_MENU, self._tooltip2clipboard, menu_item)
2025
2026 if hasattr(self._rclicked_row_data, 'format'):
2027 menu_item = clip_menu.Append(-1, _('Data (formatted as text) of current row'))
2028 self.Bind(wx.EVT_MENU, self._data2clipboard, menu_item)
2029
2030 menu_item = clip_menu.Append(-1, _('Content (as one line) of current row'))
2031 self.Bind(wx.EVT_MENU, self._row2clipboard, menu_item)
2032
2033 menu_item = clip_menu.Append(-1, _('Content (one line per column) of current row'))
2034 self.Bind(wx.EVT_MENU, self._row_list2clipboard, menu_item)
2035
2036 clip_menu.AppendSeparator()
2037 for col_idx in range(self.ColumnCount):
2038 col_content = self._rclicked_row_cells[col_idx].strip()
2039
2040 if col_content == '':
2041 continue
2042 col_header = col_headers[col_idx]
2043 if col_header == '':
2044
2045
2046
2047
2048
2049
2050 menu_item = clip_menu.Append(-1, _('Column &%s (current row): "%s" [#%s]') % (col_idx+1, shorten_text(col_content, 35), col_idx))
2051 self.Bind(wx.EVT_MENU, self._col2clipboard, menu_item)
2052 else:
2053 col_menu = wx.Menu()
2054
2055 menu_item = col_menu.Append(-1, '"%s: %s" [#%s]' % (shorten_text(col_header, 8), shorten_text(col_content, 35), col_idx))
2056 self.Bind(wx.EVT_MENU, self._col_w_hdr2clipboard, menu_item)
2057
2058 menu_item = col_menu.Append(-1, '"%s" [#%s]' % (shorten_text(col_content, 35), col_idx))
2059 self.Bind(wx.EVT_MENU, self._col2clipboard, menu_item)
2060 clip_menu.Append(-1, _('Column &%s (current row): %s') % (col_idx+1, col_header), col_menu)
2061
2062
2063 clip_add_menu = wx.Menu()
2064
2065
2066 if no_of_selected_items > 1:
2067
2068 menu_item = clip_add_menu.Append(-1, _('Tooltips of selected rows'))
2069 self.Bind(wx.EVT_MENU, self._add_tooltips2clipboard, menu_item)
2070
2071 menu_item = clip_add_menu.Append(-1, _('Data (formatted as text) of selected rows'))
2072 self.Bind(wx.EVT_MENU, self._add_datas2clipboard, menu_item)
2073
2074 menu_item = clip_add_menu.Append(-1, _('Content (as one line each) of selected rows'))
2075 self.Bind(wx.EVT_MENU, self._add_rows2clipboard, menu_item)
2076 clip_add_menu.AppendSeparator()
2077
2078
2079
2080 menu_item = clip_add_menu.Append(-1, _('Tooltip of current row'))
2081 self.Bind(wx.EVT_MENU, self._add_tooltip2clipboard, menu_item)
2082
2083 if hasattr(self._rclicked_row_data, 'format'):
2084 menu_item = clip_add_menu.Append(-1, _('Data (formatted as text) of current row'))
2085 self.Bind(wx.EVT_MENU, self._add_data2clipboard, menu_item)
2086
2087 menu_item = clip_add_menu.Append(-1, _('Content (as one line) of current row'))
2088 self.Bind(wx.EVT_MENU, self._add_row2clipboard, menu_item)
2089
2090 menu_item = clip_add_menu.Append(-1, _('Content (one line per column) of current row'))
2091 self.Bind(wx.EVT_MENU, self._add_row_list2clipboard, menu_item)
2092
2093 clip_add_menu.AppendSeparator()
2094 for col_idx in range(self.ColumnCount):
2095 col_content = self._rclicked_row_cells[col_idx].strip()
2096
2097 if col_content == '':
2098 continue
2099 col_header = col_headers[col_idx]
2100 if col_header == '':
2101
2102 menu_item = clip_add_menu.Append(-1, _('Column &%s (current row): "%s" [#%s]') % (col_idx+1, shorten_text(col_content, 35), col_idx))
2103 self.Bind(wx.EVT_MENU, self._add_col2clipboard, menu_item)
2104 else:
2105 col_add_menu = wx.Menu()
2106
2107 menu_item = col_add_menu.Append(-1, '"%s: %s" [#%s]' % (shorten_text(col_header, 8), shorten_text(col_content, 35), col_idx))
2108 self.Bind(wx.EVT_MENU, self._add_col_w_hdr2clipboard, menu_item)
2109
2110 menu_item = col_add_menu.Append(-1, '"%s" [#%s]' % (shorten_text(col_content, 35), col_idx))
2111 self.Bind(wx.EVT_MENU, self._add_col2clipboard, menu_item)
2112 clip_add_menu.Append(-1, _('Column &%s (current row): %s') % (col_idx+1, col_header), col_add_menu)
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130 self._context_menu.Append(-1, _('&Save to file...'), save_menu)
2131 self._context_menu.Append(-1, _('&Copy to clipboard...'), clip_menu)
2132 self._context_menu.Append(-1, _('Append (&+) to clipboard...'), clip_add_menu)
2133
2134 if self.__extend_popup_menu_callback is not None:
2135 self._context_menu.AppendSeparator()
2136 self.__extend_popup_menu_callback(menu = self._context_menu)
2137
2138
2139 self.PopupMenu(self._context_menu, wx.DefaultPosition)
2140 self._context_menu.Destroy()
2141 return
2142
2143
2145 if self.__delete_callback is None:
2146 return
2147
2148 no_items = len(self.get_selected_items(only_one = False))
2149 if no_items == 0:
2150 return
2151
2152 if no_items > 1:
2153 question = _(
2154 '%s list items are selected.\n'
2155 '\n'
2156 'Really delete all %s items ?'
2157 ) % (no_items, no_items)
2158 title = _('Deleting list items')
2159 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP
2160 dlg = wx.MessageDialog(None, question, title, style)
2161 btn_pressed = dlg.ShowModal()
2162 dlg.Destroy()
2163 if btn_pressed == wx.ID_NO:
2164 self.SetFocus()
2165 return
2166 if btn_pressed == wx.ID_CANCEL:
2167 self.SetFocus()
2168 return
2169
2170 self.__delete_callback()
2171 return
2172
2173
2175 if self.__new_callback is None:
2176 return
2177 self.__new_callback()
2178
2179
2181 if self.__edit_callback is None:
2182 return
2183 self.__edit_callback()
2184
2185
2187
2188 if self.__search_term is None:
2189
2190 default = ''
2191 else:
2192
2193 default = self.__search_term
2194 search_term = wx.GetTextFromUser (
2195 _('Enter the search term:'),
2196 _('List search'),
2197 default_value = default
2198 )
2199 if search_term.strip() == '':
2200
2201 self.__search_term = None
2202 self.__tt_static_part = self.__tt_static_part_base
2203 return
2204
2205
2206 self.__search_term = search_term
2207 self.__tt_static_part = _(
2208 'Current search term: [[%s]]\n'
2209 '\n'
2210 '%s'
2211 ) % (
2212 search_term,
2213 self.__tt_static_part_base
2214 )
2215 self.__search_match()
2216
2217
2218
2219
2221 event.Skip()
2222 if self.__activate_callback is not None:
2223 self.__activate_callback(event)
2224 return
2225
2226 self.__handle_edit()
2227
2228
2230 if self.__select_callback is not None:
2231 self.__select_callback(event)
2232 else:
2233 event.Skip()
2234
2235
2237 if self.__deselect_callback is not None:
2238 self.__deselect_callback(event)
2239 else:
2240 event.Skip()
2241
2242
2244 event.Skip()
2245 self.__show_context_menu(event.Index)
2246
2247
2249 evt.Skip()
2250
2251 if evt.KeyCode == wx.WXK_DELETE:
2252 self.__handle_delete()
2253 return
2254
2255 if evt.KeyCode == wx.WXK_INSERT:
2256 self.__handle_insert()
2257 return
2258
2259 if evt.KeyCode == wx.WXK_MENU:
2260 self.__show_context_menu(evt.Index)
2261 return
2262
2263
2265
2266 if chr(evt.GetRawKeyCode()) == 'f':
2267 if evt.GetModifiers() == wx.MOD_CMD:
2268
2269 self.__show_search_dialog()
2270 return
2271
2272 if chr(evt.GetRawKeyCode()) == 'n':
2273 if evt.GetModifiers() == wx.MOD_CMD:
2274
2275 self.__search_match()
2276 return
2277
2278 evt.Skip()
2279 return
2280
2281
2283 """Update tooltip on mouse motion.
2284
2285 for s in dir(wx):
2286 if s.startswith('LIST_HITTEST'):
2287 print s, getattr(wx, s)
2288
2289 LIST_HITTEST_ABOVE 1
2290 LIST_HITTEST_BELOW 2
2291 LIST_HITTEST_NOWHERE 4
2292 LIST_HITTEST_ONITEM 672
2293 LIST_HITTEST_ONITEMICON 32
2294 LIST_HITTEST_ONITEMLABEL 128
2295 LIST_HITTEST_ONITEMRIGHT 256
2296 LIST_HITTEST_ONITEMSTATEICON 512
2297 LIST_HITTEST_TOLEFT 1024
2298 LIST_HITTEST_TORIGHT 2048
2299 """
2300 item_idx, where_flag = self.HitTest(wx.Point(event.X, event.Y))
2301
2302
2303 if where_flag not in [
2304 wx.LIST_HITTEST_ONITEMLABEL,
2305 wx.LIST_HITTEST_ONITEMICON,
2306 wx.LIST_HITTEST_ONITEMSTATEICON,
2307 wx.LIST_HITTEST_ONITEMRIGHT,
2308 wx.LIST_HITTEST_ONITEM
2309 ]:
2310 self.__tt_last_item = None
2311 self.SetToolTip(self.__tt_static_part)
2312 return
2313
2314
2315 if self.__tt_last_item == item_idx:
2316 return
2317
2318
2319 self.__tt_last_item = item_idx
2320
2321
2322
2323 if item_idx == wx.NOT_FOUND:
2324 self.SetToolTip(self.__tt_static_part)
2325 return
2326
2327
2328 if self.__data is None:
2329 self.SetToolTip(self.__tt_static_part)
2330 return
2331
2332
2333
2334
2335
2336 if (
2337 (item_idx > (len(self.__data) - 1))
2338 or
2339 (item_idx < -1)
2340 ):
2341 self.SetToolTip(self.__tt_static_part)
2342 print("*************************************************************")
2343 print("GNUmed has detected an inconsistency with list item tooltips.")
2344 print("")
2345 print("This is not a big problem and you can keep working.")
2346 print("")
2347 print("However, please send us the following so we can fix GNUmed:")
2348 print("")
2349 print("item idx: %s" % item_idx)
2350 print('where flag: %s' % where_flag)
2351 print('data list length: %s' % len(self.__data))
2352 print("*************************************************************")
2353 return
2354
2355 dyna_tt = None
2356 if self.__item_tooltip_callback is not None:
2357 dyna_tt = self.__item_tooltip_callback(self.__data[self.map_item_idx2data_idx(item_idx)])
2358
2359 if dyna_tt is None:
2360 self.SetToolTip(self.__tt_static_part)
2361 return
2362
2363 self.SetToolTip(dyna_tt)
2364
2365
2366
2367
2369 evt.Skip()
2370 self.__handle_insert()
2371
2372
2374 evt.Skip()
2375 self.__handle_edit()
2376
2377
2379 evt.Skip()
2380 self.__handle_delete()
2381
2382
2384 evt.Skip()
2385 self.__show_search_dialog()
2386
2387
2389 evt.Skip()
2390 self.__search_match()
2391
2392
2394
2395 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2396 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2397
2398 col_labels = self.column_labels
2399 line = '%s' % ' || '.join(col_labels)
2400 txt_file.write('%s\n' % line)
2401 txt_file.write(('=' * len(line)) + '\n')
2402
2403 for item_idx in range(self.ItemCount):
2404 fields = []
2405 for col_idx in range(self.ColumnCount):
2406 fields.append(self.GetItem(item_idx, col_idx).Text)
2407 txt_file.write('%s\n' % ' || '.join(fields))
2408
2409 txt_file.close()
2410 gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % txt_name)
2411
2412
2414
2415 csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2416 csv_file = io.open(csv_name, mode = 'wb')
2417
2418 csv_writer = csv.writer(csv_file)
2419 csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ])
2420 for item_idx in range(self.ItemCount):
2421 fields = []
2422 for col_idx in range(self.ColumnCount):
2423 fields.append(self.GetItem(item_idx, col_idx).Text)
2424 csv_writer.writerow([ f.encode('utf-8') for f in fields ])
2425
2426 csv_file.close()
2427 gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % csv_name)
2428
2429
2446
2447
2449
2450 if self.__data is None:
2451 return
2452
2453 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2454 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2455
2456 for data in self.data:
2457 if hasattr(data, 'format'):
2458 txt = data.format()
2459 if type(txt) is list:
2460 txt = '\n'.join(txt)
2461 else:
2462 txt = '%s' % data
2463 txt_file.write('%s\n\n' % txt)
2464
2465 txt_file.close()
2466 gmDispatcher.send(signal = 'statustext', msg = _('All data saved to [%s].') % txt_name)
2467
2468
2470
2471 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2472 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2473
2474 col_labels = self.column_labels
2475 line = '%s' % ' || '.join(col_labels)
2476 txt_file.write('%s\n' % line)
2477 txt_file.write(('=' * len(line)) + '\n')
2478
2479 items = self.selected_items
2480 if self.__is_single_selection:
2481 items = [items]
2482
2483 for item_idx in items:
2484 fields = []
2485 for col_idx in range(self.ColumnCount):
2486 fields.append(self.GetItem(item_idx, col_idx).Text)
2487 txt_file.write('%s\n' % ' || '.join(fields))
2488
2489 txt_file.close()
2490 gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % txt_name)
2491
2492
2494
2495 csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2496 csv_file = io.open(csv_name, mode = 'wb')
2497
2498 csv_writer = csv.writer(csv_file)
2499 csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ])
2500
2501 items = self.selected_items
2502 if self.__is_single_selection:
2503 items = [items]
2504
2505 for item_idx in items:
2506 fields = []
2507 for col_idx in range(self.ColumnCount):
2508 fields.append(self.GetItem(item_idx, col_idx).Text)
2509 csv_writer.writerow([ f.encode('utf-8') for f in fields ])
2510
2511 csv_file.close()
2512 gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % csv_name)
2513
2514
2531
2532
2534
2535 if self.__data is None:
2536 return
2537
2538 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2539 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2540
2541 for data in self.selected_item_data:
2542 if hasattr(data, 'format'):
2543 txt = data.format()
2544 if type(txt) is list:
2545 txt = '\n'.join(txt)
2546 else:
2547 txt = '%s' % data
2548 txt_file.write('%s\n\n' % txt)
2549
2550 txt_file.close()
2551 gmDispatcher.send(signal = 'statustext', msg = _('Selected data saved to [%s].') % txt_name)
2552
2553
2573
2574
2600
2601
2631
2632
2665
2666
2668 if wx.TheClipboard.IsOpened():
2669 _log.debug('clipboard already open')
2670 return
2671 if not wx.TheClipboard.Open():
2672 _log.debug('cannot open clipboard')
2673 return
2674 data_obj = wx.TextDataObject()
2675 data_obj.SetText(' // '.join(self._rclicked_row_cells))
2676 wx.TheClipboard.SetData(data_obj)
2677 wx.TheClipboard.Close()
2678
2679
2681 if wx.TheClipboard.IsOpened():
2682 _log.debug('clipboard already open')
2683 return
2684 if not wx.TheClipboard.Open():
2685 _log.debug('cannot open clipboard')
2686 return
2687
2688 rows = []
2689 for item_idx in self.selected_items:
2690 rows.append(' // '.join([ self.GetItem(item_idx, col_idx).Text.strip() for col_idx in range(self.ColumnCount) ]))
2691
2692 data_obj = wx.TextDataObject()
2693 data_obj.SetText('\n\n'.join(rows))
2694 wx.TheClipboard.SetData(data_obj)
2695 wx.TheClipboard.Close()
2696
2697
2699 if wx.TheClipboard.IsOpened():
2700 _log.debug('clipboard already open')
2701 return
2702 if not wx.TheClipboard.Open():
2703 _log.debug('cannot open clipboard')
2704 return
2705 data_obj = wx.TextDataObject()
2706
2707 txt = ''
2708
2709 got_it = wx.TheClipboard.GetData(data_obj)
2710 if got_it:
2711 txt = data_obj.Text + '\n'
2712
2713
2714 txt += ' // '.join(self._rclicked_row_cells)
2715
2716
2717 data_obj.SetText(txt)
2718 wx.TheClipboard.SetData(data_obj)
2719 wx.TheClipboard.Close()
2720
2721
2723 if wx.TheClipboard.IsOpened():
2724 _log.debug('clipboard already open')
2725 return
2726 if not wx.TheClipboard.Open():
2727 _log.debug('cannot open clipboard')
2728 return
2729
2730 rows = []
2731 for item_idx in self.selected_items:
2732 rows.append(' // '.join([ self.GetItem(item_idx, col_idx).Text.strip() for col_idx in range(self.ColumnCount) ]))
2733
2734 data_obj = wx.TextDataObject()
2735
2736 txt = ''
2737
2738 got_it = wx.TheClipboard.GetData(data_obj)
2739 if got_it:
2740 txt = data_obj.Text + '\n'
2741 txt += '\n\n'.join(rows)
2742
2743 data_obj.SetText(txt)
2744 wx.TheClipboard.SetData(data_obj)
2745 wx.TheClipboard.Close()
2746
2747
2749 if wx.TheClipboard.IsOpened():
2750 _log.debug('clipboard already open')
2751 return
2752 if not wx.TheClipboard.Open():
2753 _log.debug('cannot open clipboard')
2754 return
2755 data_obj = wx.TextDataObject()
2756 data_obj.SetText('\n'.join(self._rclicked_row_cells_w_hdr))
2757 wx.TheClipboard.SetData(data_obj)
2758 wx.TheClipboard.Close()
2759
2760
2762 if wx.TheClipboard.IsOpened():
2763 _log.debug('clipboard already open')
2764 return
2765 if not wx.TheClipboard.Open():
2766 _log.debug('cannot open clipboard')
2767 return
2768 data_obj = wx.TextDataObject()
2769
2770 txt = ''
2771
2772 got_it = wx.TheClipboard.GetData(data_obj)
2773 if got_it:
2774 txt = data_obj.Text + '\n'
2775
2776
2777 txt += '\n'.join(self._rclicked_row_cells_w_hdr)
2778
2779
2780 data_obj.SetText(txt)
2781 wx.TheClipboard.SetData(data_obj)
2782 wx.TheClipboard.Close()
2783
2784
2786 if wx.TheClipboard.IsOpened():
2787 _log.debug('clipboard already open')
2788 return
2789 if not wx.TheClipboard.Open():
2790 _log.debug('cannot open clipboard')
2791 return
2792 data_obj = wx.TextDataObject()
2793 txt = self._rclicked_row_data.format()
2794 if type(txt) == type([]):
2795 txt = '\n'.join(txt)
2796 data_obj.SetText(txt)
2797 wx.TheClipboard.SetData(data_obj)
2798 wx.TheClipboard.Close()
2799
2800
2802 if wx.TheClipboard.IsOpened():
2803 _log.debug('clipboard already open')
2804 return
2805 if not wx.TheClipboard.Open():
2806 _log.debug('cannot open clipboard')
2807 return
2808
2809 data_as_txt = []
2810 for data in self.selected_item_data:
2811 if hasattr(data, 'format'):
2812 txt = data.format()
2813 if type(txt) is list:
2814 txt = '\n'.join(txt)
2815 else:
2816 txt = '%s' % data
2817 data_as_txt.append(txt)
2818
2819 data_obj = wx.TextDataObject()
2820 data_obj.SetText('\n\n'.join(data_as_txt))
2821 wx.TheClipboard.SetData(data_obj)
2822 wx.TheClipboard.Close()
2823
2824
2826 if wx.TheClipboard.IsOpened():
2827 _log.debug('clipboard already open')
2828 return
2829 if not wx.TheClipboard.Open():
2830 _log.debug('cannot open clipboard')
2831 return
2832 data_obj = wx.TextDataObject()
2833
2834 txt = ''
2835
2836 got_it = wx.TheClipboard.GetData(data_obj)
2837 if got_it:
2838 txt = data_obj.Text + '\n'
2839
2840
2841 tmp = self._rclicked_row_data.format()
2842 if type(tmp) == type([]):
2843 txt += '\n'.join(tmp)
2844 else:
2845 txt += tmp
2846
2847
2848 data_obj.SetText(txt)
2849 wx.TheClipboard.SetData(data_obj)
2850 wx.TheClipboard.Close()
2851
2852
2854 if wx.TheClipboard.IsOpened():
2855 _log.debug('clipboard already open')
2856 return
2857 if not wx.TheClipboard.Open():
2858 _log.debug('cannot open clipboard')
2859 return
2860
2861 data_as_txt = []
2862 for data in self.selected_item_data:
2863 if hasattr(data, 'format'):
2864 txt = data.format()
2865 if type(txt) is list:
2866 txt = '\n'.join(txt)
2867 else:
2868 txt = '%s' % data
2869 data_as_txt.append(txt)
2870
2871 data_obj = wx.TextDataObject()
2872 txt = ''
2873
2874 got_it = wx.TheClipboard.GetData(data_obj)
2875 if got_it:
2876 txt = data_obj.Text + '\n'
2877 txt += '\n'.join(data_as_txt)
2878
2879
2880 data_obj.SetText(txt)
2881 wx.TheClipboard.SetData(data_obj)
2882 wx.TheClipboard.Close()
2883
2884
2886 if wx.TheClipboard.IsOpened():
2887 _log.debug('clipboard already open')
2888 return
2889 if not wx.TheClipboard.Open():
2890 _log.debug('cannot open clipboard')
2891 return
2892 data_obj = wx.TextDataObject()
2893
2894
2895 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2896 txt = self._rclicked_row_cells[col_idx]
2897
2898 data_obj.SetText(txt)
2899 wx.TheClipboard.SetData(data_obj)
2900 wx.TheClipboard.Close()
2901
2902
2904 if wx.TheClipboard.IsOpened():
2905 _log.debug('clipboard already open')
2906 return
2907 if not wx.TheClipboard.Open():
2908 _log.debug('cannot open clipboard')
2909 return
2910 data_obj = wx.TextDataObject()
2911
2912 txt = ''
2913
2914 got_it = wx.TheClipboard.GetData(data_obj)
2915 if got_it:
2916 txt = data_obj.Text + '\n'
2917
2918
2919
2920 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2921 txt += self._rclicked_row_cells[col_idx]
2922
2923
2924 data_obj.SetText(txt)
2925 wx.TheClipboard.SetData(data_obj)
2926 wx.TheClipboard.Close()
2927
2928
2930 if wx.TheClipboard.IsOpened():
2931 _log.debug('clipboard already open')
2932 return
2933 if not wx.TheClipboard.Open():
2934 _log.debug('cannot open clipboard')
2935 return
2936 data_obj = wx.TextDataObject()
2937
2938
2939 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2940 txt = self._rclicked_row_cells_w_hdr[col_idx]
2941
2942 data_obj.SetText(txt)
2943 wx.TheClipboard.SetData(data_obj)
2944 wx.TheClipboard.Close()
2945
2946
2948 if wx.TheClipboard.IsOpened():
2949 _log.debug('clipboard already open')
2950 return
2951 if not wx.TheClipboard.Open():
2952 _log.debug('cannot open clipboard')
2953 return
2954 data_obj = wx.TextDataObject()
2955
2956 txt = ''
2957
2958 got_it = wx.TheClipboard.GetData(data_obj)
2959 if got_it:
2960 txt = data_obj.Text + '\n'
2961
2962
2963
2964 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2965 txt += self._rclicked_row_cells_w_hdr[col_idx]
2966
2967
2968 data_obj.SetText(txt)
2969 wx.TheClipboard.SetData(data_obj)
2970 wx.TheClipboard.Close()
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2985
2986 if self.__search_term is None:
2987
2988 return False
2989 if self.__search_term.strip() == '':
2990
2991 return False
2992 if self.__searchable_cols is None:
2993
2994 self.searchable_columns = None
2995 if len(self.__searchable_cols) == 0:
2996
2997 return False
2998
2999
3000 for row_idx in range(self.__next_line_to_search, self.ItemCount):
3001 for col_idx in range(self.ColumnCount):
3002 if col_idx not in self.__searchable_cols:
3003 continue
3004 col_val = self.GetItem(row_idx, col_idx).GetText()
3005 if regex.search(self.__search_term, col_val, regex.U | regex.I) is not None:
3006 self.Select(row_idx)
3007 self.EnsureVisible(row_idx)
3008 if row_idx == self.ItemCount - 1:
3009
3010 self.__next_line_to_search = 0
3011 else:
3012 self.__next_line_to_search = row_idx + 1
3013 return True
3014
3015 self.__next_line_to_search = 0
3016 return False
3017
3018
3020
3021
3022 if cols is None:
3023
3024 self.__searchable_cols = range(self.ColumnCount)
3025 return
3026
3027
3028 new_cols = {}
3029 for col in cols:
3030 if col < self.ColumnCount:
3031 new_cols[col] = True
3032
3033 self.__searchable_cols = new_cols.keys()
3034
3035 searchable_columns = property(lambda x:x, _set_searchable_cols)
3036
3037
3038
3039
3041 return self.__activate_callback
3042
3044 if callback is None:
3045 self.Unbind(wx.EVT_LIST_ITEM_ACTIVATED)
3046 self.__activate_callback = None
3047 return
3048 if not callable(callback):
3049 raise ValueError('<activate> callback is not a callable: %s' % callback)
3050 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_list_item_activated)
3051 self.__activate_callback = callback
3052
3053 activate_callback = property(_get_activate_callback, _set_activate_callback)
3054
3055
3057 return self.__select_callback
3058
3060 if callback is None:
3061 self.Unbind(wx.EVT_LIST_ITEM_SELECTED)
3062 self.__select_callback = None
3063 return
3064 if not callable(callback):
3065 raise ValueError('<selected> callback is not a callable: %s' % callback)
3066 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self._on_list_item_selected)
3067 self.__select_callback = callback
3068
3069 select_callback = property(_get_select_callback, _set_select_callback)
3070
3071
3073 return self.__deselect_callback
3074
3076 if callback is None:
3077 self.Unbind(wx.EVT_LIST_ITEM_DESELECTED)
3078 self.__deselect_callback = None
3079 return
3080 if not callable(callback):
3081 raise ValueError('<deselected> callback is not a callable: %s' % callback)
3082 self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self._on_list_item_deselected)
3083 self.__deselect_callback = callback
3084
3085 deselect_callback = property(_get_deselect_callback, _set_deselect_callback)
3086
3087
3089 return self.__delete_callback
3090
3092 if callback is None:
3093
3094 self.__delete_callback = None
3095 return
3096 if not callable(callback):
3097 raise ValueError('<delete> callback is not a callable: %s' % callback)
3098
3099 self.__delete_callback = callback
3100
3101 delete_callback = property(_get_delete_callback, _set_delete_callback)
3102
3103
3105 return self.__new_callback
3106
3108 if callback is None:
3109 self.__new_callback = None
3110 return
3111 if not callable(callback):
3112 raise ValueError('<new> callback is not a callable: %s' % callback)
3113 self.__new_callback = callback
3114
3115 new_callback = property(_get_new_callback, _set_new_callback)
3116
3117
3119 return self.__edit_callback
3120
3122 if callback is None:
3123 self.__edit_callback = None
3124 return
3125 if not callable(callback):
3126 raise ValueError('<edit> callback is not a callable: %s' % callback)
3127 self.__edit_callback = callback
3128
3129 edit_callback = property(_get_edit_callback, _set_edit_callback)
3130
3131
3137
3138
3139
3140
3141
3142 item_tooltip_callback = property(lambda x:x, _set_item_tooltip_callback)
3143
3144
3146 if callback is not None:
3147 if not callable(callback):
3148 raise ValueError('<extend_popup_menu> callback is not a callable: %s' % callback)
3149 self.__extend_popup_menu_callback = callback
3150
3151 extend_popup_menu_callback = property(lambda x:x, _set_extend_popup_menu_callback)
3152
3153
3154
3155
3157 if self.itemDataMap is None:
3158 self._update_sorting_metadata()
3159 return self
3160
3161
3163 col_idx, is_ascending = self.GetSortState()
3164 if col_idx == -1:
3165 _log.debug('outside any column (idx: -1) clicked, ignoring')
3166 return
3167 self._remove_sorting_indicators_from_column_headers()
3168 col_state = self.GetColumn(col_idx)
3169 col_state.Text += self.sort_order_tags[is_ascending]
3170 self.SetColumn(col_idx, col_state)
3171
3172
3174 return (primary_item1_idx, primary_item2_idx)
3175
3176 if self.__secondary_sort_col is None:
3177 return (primary_item1_idx, primary_item2_idx)
3178 if self.__secondary_sort_col == primary_sort_col:
3179 return (primary_item1_idx, primary_item2_idx)
3180
3181 secondary_val1 = self.itemDataMap[primary_item1_idx][self.__secondary_sort_col]
3182 secondary_val2 = self.itemDataMap[primary_item2_idx][self.__secondary_sort_col]
3183
3184 if type(secondary_val1) == type('') and type(secondary_val2) == type(''):
3185 secondary_cmp_result = locale.strcoll(secondary_val1, secondary_val2)
3186 elif type(secondary_val1) == type('') or type(secondary_val2) == type(''):
3187 secondary_cmp_result = locale.strcoll(str(secondary_val1), str(secondary_val2))
3188 else:
3189 secondary_cmp_result = cmp(secondary_val1, secondary_val2)
3190
3191 if secondary_cmp_result == 0:
3192 return (primary_item1_idx, primary_item2_idx)
3193
3194
3195 currently_ascending = self._colSortFlag[primary_sort_col]
3196 if currently_ascending:
3197 secondary_val1, secondary_val2 = min(secondary_val1, secondary_val2), max(secondary_val1, secondary_val2)
3198 else:
3199 secondary_val1, secondary_val2 = max(secondary_val1, secondary_val2), min(secondary_val1, secondary_val2)
3200 return (secondary_val1, secondary_val2)
3201
3202
3204
3205
3206
3207 sort_col, is_ascending = self.GetSortState()
3208 data1 = self.itemDataMap[item1][sort_col]
3209 data2 = self.itemDataMap[item2][sort_col]
3210 if type(data1) == type('') and type(data2) == type(''):
3211 cmp_result = locale.strcoll(data1, data2)
3212 elif type(data1) == type('') or type(data2) == type(''):
3213 cmp_result = locale.strcoll(str(data1), str(data2))
3214 else:
3215 cmp_result = cmp(data1, data2)
3216
3217
3218 if not is_ascending:
3219 cmp_result = -1 * cmp_result
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233 return cmp_result
3234
3235
3236
3237
3238
3239
3240
3241
3242
3244 dict2sort = {}
3245 item_count = self.GetItemCount()
3246 if item_count == 0:
3247 return dict2sort
3248 col_count = self.GetColumnCount()
3249 for item_idx in range(item_count):
3250 dict2sort[item_idx] = ()
3251 if col_count == 0:
3252 continue
3253 for col_idx in range(col_count):
3254 dict2sort[item_idx] += (self.GetItem(item_idx, col_idx).GetText(), )
3255
3256
3257
3258 return dict2sort
3259
3260
3262 for col_idx in range(self.ColumnCount):
3263 col_state = self.GetColumn(col_idx)
3264 initial_header = col_state.Text
3265 if col_state.Text.endswith(self.sort_order_tags[True]):
3266 col_state.Text = col_state.Text[:-len(self.sort_order_tags[True])]
3267 if col_state.Text.endswith(self.sort_order_tags[False]):
3268 col_state.Text = col_state.Text[:-len(self.sort_order_tags[False])]
3269 if col_state.Text == initial_header:
3270 continue
3271 self.SetColumn(col_idx, col_state)
3272
3273
3278
3279
3283
3284
3290
3291
3292
3293
3294
3295
3296
3297
3298
3300 return self.__secondary_sort_col
3301
3303 if col is None:
3304 self.__secondary_sort_col = None
3305 return
3306 if col > self.GetColumnCount():
3307 raise ValueError('cannot secondary-sort on col [%s], there are only [%s] columns', col, self.GetColumnCount())
3308 self.__secondary_sort_col = col
3309
3310 secondary_sort_column = property(__get_secondary_sort_col, __set_secondary_sort_col)
3311
3312
3313 -def shorten_text(text=None, max_length=None):
3314 if len(text) <= max_length:
3315 return text
3316 return text[:max_length-1] + '\u2026'
3317
3318
3319
3320
3321 if __name__ == '__main__':
3322
3323 if len(sys.argv) < 2:
3324 sys.exit()
3325
3326 if sys.argv[1] != 'test':
3327 sys.exit()
3328
3329 sys.path.insert(0, '../../')
3330
3331 from Gnumed.pycommon import gmI18N
3332 gmI18N.activate_locale()
3333 gmI18N.install_domain()
3334
3335
3337 app = wx.PyWidgetTester(size = (400, 500))
3338 dlg = wx.MultiChoiceDialog (
3339 parent = None,
3340 message = 'test message',
3341 caption = 'test caption',
3342 choices = ['a', 'b', 'c', 'd', 'e']
3343 )
3344 dlg.ShowModal()
3345 sels = dlg.GetSelections()
3346 print("selected:")
3347 for sel in sels:
3348 print(sel)
3349
3350
3352
3353 def edit(argument):
3354 print("editor called with:")
3355 print(argument)
3356
3357 def refresh(lctrl):
3358 choices = ['a', 'b', 'c']
3359 lctrl.set_string_items(choices)
3360
3361 app = wx.App()
3362 chosen = get_choices_from_list (
3363
3364 caption = 'select health issues',
3365
3366
3367 columns = ['issue'],
3368 refresh_callback = refresh,
3369 single_selection = False
3370
3371 )
3372 print("chosen:")
3373 print(chosen)
3374
3375
3377
3378 app = wx.App(size = (200, 50))
3379 dlg = cItemPickerDlg(None, -1, msg = 'Pick a few items:')
3380 dlg.set_columns(['Plugins'], ['Load in workplace', 'dummy'])
3381
3382 dlg.set_string_items(['patient', 'emr', 'docs'])
3383 result = dlg.ShowModal()
3384 print(result)
3385 print(dlg.get_picks())
3386
3387
3388 test_get_choices_from_list()
3389
3390
3391
3392
3393