Home | Trees | Indices | Help |
|
---|
|
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 #================================================================ 15 # $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/wxpython/gmListWidgets.py,v $ 16 # $Id: gmListWidgets.py,v 1.37 2010-02-06 21:37:26 ncq Exp $ 17 __version__ = "$Revision: 1.37 $" 18 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 19 __license__ = "GPL" 20 21 22 import sys, types 23 24 25 import wx 26 import wx.lib.mixins.listctrl as listmixins 27 28 29 if __name__ == '__main__': 30 sys.path.insert(0, '../../') 31 from Gnumed.business import gmPerson 32 from Gnumed.pycommon import gmTools, gmDispatcher 33 from Gnumed.wxpython import gmGuiHelpers 34 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg, wxgGenericListManagerPnl 35 36 #================================================================37 -def get_choices_from_list ( 38 parent=None, 39 msg=None, 40 caption=None, 41 choices=None, 42 selections=None, 43 columns=None, 44 data=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 """Let user select item(s) from a list. 56 57 - edit_callback: (item data) 58 - new_callback: () 59 - delete_callback: (item data) 60 - refresh_callback: (listctrl) 61 62 returns None if cancelled 63 returns list (may be empty) of selected items 64 """ 65 if caption is None: 66 caption = _('generic multi choice dialog') 67 68 if single_selection: 69 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg, style = wx.LC_SINGLE_SEL) 70 else: 71 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg) 72 73 dlg.refresh_callback = refresh_callback 74 dlg.edit_callback = edit_callback 75 dlg.new_callback = new_callback 76 dlg.delete_callback = delete_callback 77 78 dlg.ignore_OK_button = ignore_OK_button 79 dlg.left_extra_button = left_extra_button 80 dlg.middle_extra_button = middle_extra_button 81 dlg.right_extra_button = right_extra_button 82 83 dlg.set_columns(columns = columns) 84 85 if refresh_callback is None: 86 dlg.set_string_items(items = choices) # list ctrl will refresh anyway if possible 87 dlg.set_column_widths() 88 89 if data is not None: 90 dlg.set_data(data=data) # can override data set if refresh_callback is not None 91 92 if selections is not None: 93 dlg.set_selections(selections = selections) 94 dlg.can_return_empty = can_return_empty 95 96 btn_pressed = dlg.ShowModal() 97 sels = dlg.get_selected_item_data(only_one = single_selection) 98 dlg.Destroy() 99 100 if btn_pressed == wx.ID_OK: 101 if can_return_empty and (sels is None): 102 return [] 103 return sels 104 105 return None106 #----------------------------------------------------------------108 """A dialog holding a list and a few buttons to act on the items.""" 109376 #================================================================111 112 try: 113 msg = kwargs['msg'] 114 del kwargs['msg'] 115 except KeyError: msg = None 116 117 wxgGenericListSelectorDlg.wxgGenericListSelectorDlg.__init__(self, *args, **kwargs) 118 119 if msg is None: 120 self._LBL_message.Hide() 121 else: 122 self._LBL_message.SetLabel(msg) 123 124 self.left_extra_button = None 125 self.middle_extra_button = None 126 self.right_extra_button = None 127 128 self.refresh_callback = None # called when new/edit/delete callbacks return True (IOW were not cancelled) 129 self.new_callback = None # called when NEW button pressed, no argument passed in 130 self.edit_callback = None # called when EDIT button pressed, data of topmost selected item passed in 131 self.delete_callback = None # called when DELETE button pressed, data of topmost selected item passed in 132 133 self.ignore_OK_button = False # by default do show/use the OK button 134 135 self.can_return_empty = False136 #------------------------------------------------------------138 self._LCTRL_items.set_columns(columns = columns)139 #------------------------------------------------------------141 self._LCTRL_items.set_column_widths(widths = widths)142 #------------------------------------------------------------144 self._LCTRL_items.set_string_items(items = items) 145 self._LCTRL_items.set_column_widths() 146 self._LCTRL_items.Select(0)147 #------------------------------------------------------------149 self._LCTRL_items.set_selections(selections = selections)150 #------------------------------------------------------------ 153 #------------------------------------------------------------155 return self._LCTRL_items.get_selected_item_data(only_one=only_one)156 #------------------------------------------------------------ 157 # event handlers 158 #------------------------------------------------------------160 if not self.__ignore_OK_button: 161 self._BTN_ok.SetDefault() 162 self._BTN_ok.Enable(True) 163 164 if self.edit_callback is not None: 165 self._BTN_edit.Enable(True) 166 167 if self.delete_callback is not None: 168 self._BTN_delete.Enable(True)169 #------------------------------------------------------------171 if self._LCTRL_items.get_selected_items(only_one=True) == -1: 172 if not self.can_return_empty: 173 self._BTN_cancel.SetDefault() 174 self._BTN_ok.Enable(False) 175 self._BTN_edit.Enable(False) 176 self._BTN_delete.Enable(False)177 #------------------------------------------------------------ 185 #------------------------------------------------------------ 195 #------------------------------------------------------------ 208 #------------------------------------------------------------ 217 #------------------------------------------------------------ 226 #------------------------------------------------------------ 235 #------------------------------------------------------------ 236 # properties 237 #------------------------------------------------------------ 246 247 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button) 248 #------------------------------------------------------------ 263 264 left_extra_button = property(lambda x:x, _set_left_extra_button) 265 #------------------------------------------------------------ 280 281 middle_extra_button = property(lambda x:x, _set_middle_extra_button) 282 #------------------------------------------------------------ 297 298 right_extra_button = property(lambda x:x, _set_right_extra_button) 299 #------------------------------------------------------------ 302304 if callback is not None: 305 if self.refresh_callback is None: 306 raise ValueError('refresh callback must be set before new callback can be set') 307 if not callable(callback): 308 raise ValueError('<new> callback is not a callable: %s' % callback) 309 self.__new_callback = callback 310 311 if callback is None: 312 self._BTN_new.Enable(False) 313 self._BTN_new.Hide() 314 else: 315 self._BTN_new.Enable(True) 316 self._BTN_new.Show()317 318 new_callback = property(_get_new_callback, _set_new_callback) 319 #------------------------------------------------------------ 322324 if callback is not None: 325 if self.refresh_callback is None: 326 raise ValueError('refresh callback must be set before edit callback can be set') 327 if not callable(callback): 328 raise ValueError('<edit> callback is not a callable: %s' % callback) 329 self.__edit_callback = callback 330 331 if callback is None: 332 self._BTN_edit.Enable(False) 333 self._BTN_edit.Hide() 334 else: 335 self._BTN_edit.Enable(True) 336 self._BTN_edit.Show()337 338 edit_callback = property(_get_edit_callback, _set_edit_callback) 339 #------------------------------------------------------------ 342344 if callback is not None: 345 if self.refresh_callback is None: 346 raise ValueError('refresh callback must be set before delete callback can be set') 347 if not callable(callback): 348 raise ValueError('<delete> callback is not a callable: %s' % callback) 349 self.__delete_callback = callback 350 351 if callback is None: 352 self._BTN_delete.Enable(False) 353 self._BTN_delete.Hide() 354 else: 355 self._BTN_delete.Enable(True) 356 self._BTN_delete.Show()357 358 delete_callback = property(_get_delete_callback, _set_delete_callback) 359 #------------------------------------------------------------ 362 366368 if callback is not None: 369 if not callable(callback): 370 raise ValueError('<refresh> callback is not a callable: %s' % callback) 371 self.__refresh_callback = callback 372 if callback is not None: 373 wx.CallAfter(self._set_refresh_callback_helper)374 375 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)378 """A panel holding a generic multi-column list and action buttions.""" 379474 #================================================================381 382 try: 383 msg = kwargs['msg'] 384 del kwargs['msg'] 385 except KeyError: msg = None 386 387 wxgGenericListManagerPnl.wxgGenericListManagerPnl.__init__(self, *args, **kwargs) 388 389 if msg is None: 390 self._LBL_message.Hide() 391 else: 392 self._LBL_message.SetLabel(msg) 393 394 # new/edit/delete must return True/False to enable refresh 395 self.__new_callback = None # called when NEW button pressed, no argument passed in 396 self.edit_callback = None # called when EDIT button pressed, data of topmost selected item passed in 397 self.delete_callback = None # called when DELETE button pressed, data of topmost selected item passed in 398 self.refresh_callback = None # called when new/edit/delete callbacks return True (IOW were not cancelled)399 #------------------------------------------------------------ 400 # external API 401 #------------------------------------------------------------403 self._LCTRL_items.set_columns(columns = columns)404 #------------------------------------------------------------406 self._LCTRL_items.set_string_items(items = items) 407 self._LCTRL_items.set_column_widths() 408 409 if (items is None) or (len(items) == 0): 410 self._BTN_edit.Enable(False) 411 self._BTN_remove.Enable(False) 412 else: 413 self._LCTRL_items.Select(0)414 #------------------------------------------------------------416 self._LCTRL_items.set_selections(selections = selections)417 #------------------------------------------------------------ 420 #------------------------------------------------------------422 return self._LCTRL_items.get_selected_item_data(only_one=only_one)423 #------------------------------------------------------------ 424 # event handlers 425 #------------------------------------------------------------427 if self.edit_callback is not None: 428 self._BTN_edit.Enable(True) 429 if self.delete_callback is not None: 430 self._BTN_remove.Enable(True)431 #------------------------------------------------------------433 if self._LCTRL_items.get_selected_items(only_one=True) == -1: 434 self._BTN_edit.Enable(False) 435 self._BTN_remove.Enable(False)436 #------------------------------------------------------------ 443 #------------------------------------------------------------ 453 #------------------------------------------------------------ 463 #------------------------------------------------------------ 464 # properties 465 #------------------------------------------------------------ 468 472 473 new_callback = property(_get_new_callback, _set_new_callback)476627 #================================================================ 628 # main 629 #---------------------------------------------------------------- 630 if __name__ == '__main__': 631 632 from Gnumed.pycommon import gmI18N 633 gmI18N.activate_locale() 634 gmI18N.install_domain() 635 636 #------------------------------------------------------------478 479 try: 480 kwargs['style'] = kwargs['style'] | wx.LC_REPORT 481 except KeyError: 482 kwargs['style'] = wx.LC_REPORT 483 484 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL) 485 486 wx.ListCtrl.__init__(self, *args, **kwargs) 487 listmixins.ListCtrlAutoWidthMixin.__init__(self) 488 489 self.__widths = None 490 self.__data = None491 #------------------------------------------------------------ 492 # setters 493 #------------------------------------------------------------495 """(Re)define the columns. 496 497 Note that this will (have to) delete the items. 498 """ 499 self.ClearAll() 500 if columns is None: 501 return 502 for idx in range(len(columns)): 503 self.InsertColumn(idx, columns[idx])504 #------------------------------------------------------------506 """Set the column width policy. 507 508 widths = None: 509 use previous policy if any or default policy 510 widths != None: 511 use this policy and remember it for later calls 512 513 This means there is no way to *revert* to the default policy :-( 514 """ 515 # explicit policy ? 516 if widths is not None: 517 self.__widths = widths 518 for idx in range(len(self.__widths)): 519 self.SetColumnWidth(col = idx, width = self.__widths[idx]) 520 return 521 522 # previous policy ? 523 if self.__widths is not None: 524 for idx in range(len(self.__widths)): 525 self.SetColumnWidth(col = idx, width = self.__widths[idx]) 526 return 527 528 # default policy ! 529 if self.GetItemCount() == 0: 530 width_type = wx.LIST_AUTOSIZE_USEHEADER 531 else: 532 width_type = wx.LIST_AUTOSIZE 533 for idx in range(self.GetColumnCount()): 534 self.SetColumnWidth(col = idx, width = width_type)535 #------------------------------------------------------------537 """All item members must be unicode()able or None.""" 538 539 self.DeleteAllItems() 540 self.__data = items 541 542 if items is None: 543 return 544 545 for item in items: 546 try: 547 item[0] 548 if not isinstance(item, basestring): 549 is_numerically_iterable = True 550 else: 551 is_numerically_iterable = False 552 except TypeError: 553 is_numerically_iterable = False 554 555 if is_numerically_iterable: 556 # cannot use errors='replace' since then 557 # None/ints/unicode strings fail to get encoded 558 col_val = unicode(item[0]) 559 row_num = self.InsertStringItem(index = sys.maxint, label = col_val) 560 for col_idx in range(1, min(self.GetColumnCount(), len(item))): 561 col_val = unicode(item[col_idx]) 562 self.SetStringItem(index = row_num, col = col_idx, label = col_val) 563 else: 564 # cannot use errors='replace' since then None/ints/unicode strings fails to get encoded 565 col_val = unicode(item) 566 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)567 #------------------------------------------------------------ 571 #------------------------------------------------------------ 576 #------------------------------------------------------------ 577 # getters 578 #------------------------------------------------------------580 labels = [] 581 for col_idx in self.GetColumnCount(): 582 col = self.GetColumn(col = col_idx) 583 labels.append(col.GetText()) 584 return labels585 #------------------------------------------------------------587 if self.__data is None: # this isn't entirely clean 588 return None 589 590 return self.__data[item_idx]591 #------------------------------------------------------------593 594 if self.__is_single_selection or only_one: 595 return self.GetFirstSelected() 596 597 items = [] 598 idx = self.GetFirstSelected() 599 while idx != -1: 600 items.append(idx) 601 idx = self.GetNextSelected(idx) 602 603 return items604 #------------------------------------------------------------606 607 if self.__is_single_selection or only_one: 608 if self.__data is None: 609 return None 610 idx = self.GetFirstSelected() 611 if idx == -1: 612 return None 613 return self.__data[idx] 614 615 data = [] 616 if self.__data is None: 617 return data 618 idx = self.GetFirstSelected() 619 while idx != -1: 620 data.append(self.__data[idx]) 621 idx = self.GetNextSelected(idx) 622 623 return data624 #------------------------------------------------------------626 self.Select(idx = self.GetFirstSelected(), on = 0)638 app = wx.PyWidgetTester(size = (400, 500)) 639 dlg = wx.MultiChoiceDialog ( 640 parent = None, 641 message = 'test message', 642 caption = 'test caption', 643 choices = ['a', 'b', 'c', 'd', 'e'] 644 ) 645 dlg.ShowModal() 646 sels = dlg.GetSelections() 647 print "selected:" 648 for sel in sels: 649 print sel650 #------------------------------------------------------------ 656 657 def refresh(lctrl): 658 choices = ['a', 'b', 'c'] 659 lctrl.set_string_items(choices) 660 661 app = wx.PyWidgetTester(size = (200, 50)) 662 chosen = get_choices_from_list ( 663 # msg = 'select a health issue\nfrom the list below\n', 664 caption = 'select health issues', 665 #choices = [['D.M.II', '4'], ['MS', '3'], ['Fraktur', '2']], 666 #columns = ['issue', 'no of episodes'] 667 columns = ['issue'], 668 refresh_callback = refresh 669 #, edit_callback = edit 670 ) 671 print "chosen:" 672 print chosen 673 #------------------------------------------------------------ 674 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 675 test_get_choices_from_list() 676 #test_wxMultiChoiceDialog() 677 678 #================================================================ 679 # $Log: gmListWidgets.py,v $ 680 # Revision 1.37 2010-02-06 21:37:26 ncq 681 # - support ignoring OK button 682 # 683 # Revision 1.36 2010/01/31 18:17:33 ncq 684 # - make refresh callback setting smarter: set column widths after setting items 685 # 686 # Revision 1.35 2010/01/21 08:43:23 ncq 687 # - somewhat support setting col widths within get-choice-from-list 688 # 689 # Revision 1.34 2009/11/28 18:30:07 ncq 690 # - hide disabled buttons/show enabled ones 691 # 692 # Revision 1.33 2009/06/29 15:07:58 ncq 693 # - disable edit/delete buttons when setting items to None or [] 694 # 695 # Revision 1.32 2009/06/20 12:45:22 ncq 696 # - call refresh from refresh property setter if callable and not None 697 # 698 # Revision 1.31 2009/06/11 12:37:25 ncq 699 # - much simplified initial setup of list ctrls 700 # 701 # Revision 1.30 2009/06/04 16:32:01 ncq 702 # - use refresh from init if available to simplify external setup code 703 # 704 # Revision 1.29 2009/04/16 12:49:47 ncq 705 # - more sanity checks regarding action callbacks 706 # 707 # Revision 1.28 2009/01/17 23:07:29 ncq 708 # - support remembering previous widths policy 709 # 710 # Revision 1.27 2009/01/15 11:39:59 ncq 711 # - cleanup 712 # 713 # Revision 1.26 2008/12/25 16:55:36 ncq 714 # - allow returniny empty list = no item selected if desired 715 # 716 # Revision 1.25 2008/08/06 13:22:14 ncq 717 # - fix detection of item list type 718 # 719 # Revision 1.24 2008/07/24 14:00:18 ncq 720 # - better comments 721 # - resize columns after list refreshing in generic list selector 722 # - differentiate between iterables and non-iterables by means of 723 # an exception rather than checking for type.ListType in set_string_items 724 # 725 # Revision 1.23 2008/05/31 16:33:07 ncq 726 # - add TODO with URL 727 # 728 # Revision 1.22 2008/02/26 16:28:04 ncq 729 # - when auto-setting col widths in lists w/o items use header as width ;-) 730 # 731 # Revision 1.21 2007/11/28 22:37:00 ncq 732 # - robustify in the absence of selected values 733 # 734 # Revision 1.20 2007/11/23 23:34:39 ncq 735 # - when explicitely setting the selections deselect the 736 # 0th-index item selected by default 737 # 738 # Revision 1.19 2007/11/17 16:38:13 ncq 739 # - cGenericListManagerPnl 740 # 741 # Revision 1.18 2007/10/08 12:56:02 ncq 742 # - document callbacks 743 # - protect against self.__data being None in get(_selected)_item_data() 744 # 745 # Revision 1.17 2007/09/24 18:37:08 ncq 746 # - get_column_labels() 747 # 748 # Revision 1.16 2007/09/20 19:10:15 ncq 749 # - carefully handle list item insertion - handle both list 750 # of lists and list of strings 751 # 752 # Revision 1.15 2007/09/07 22:38:04 ncq 753 # - remove Fit() call since it's counterproductive for the list 754 # 755 # Revision 1.14 2007/09/02 20:54:26 ncq 756 # - remove cruft 757 # - support refresh_callback 758 # 759 # Revision 1.13 2007/08/31 23:05:05 ncq 760 # - fix single selection list 761 # 762 # Revision 1.12 2007/08/29 14:41:54 ncq 763 # - no more singular get_choice_from_list() 764 # - support add/delete callbacks in generic list selector 765 # 766 # Revision 1.11 2007/08/20 16:22:51 ncq 767 # - make get_choice(s)_from_list() more generic 768 # - cleanup, improved test 769 # - support edit button and message in generic list selector 770 # 771 # Revision 1.10 2007/07/22 09:26:25 ncq 772 # - new get_choice_from_list() 773 # 774 # Revision 1.9 2007/07/09 12:45:47 ncq 775 # - fix unicode()ing in set_string_items(): can't use (..., errors='replace') :-( 776 # - factor out cPatientListingCtrl into gmDataMiningWidgets.py 777 # 778 # Revision 1.8 2007/07/07 12:42:00 ncq 779 # - set_string_items now applies unicode() to all item members 780 # - cPatientListingCtrl and test suite 781 # 782 # Revision 1.7 2007/06/28 12:38:15 ncq 783 # - fix logic reversal in get_selected_*() 784 # 785 # Revision 1.6 2007/06/18 20:33:56 ncq 786 # - add get_choice(s)_from_list() 787 # - add cGenericListSelectorDlg 788 # - add set_string_items()/set_selections()/get_selected_items() 789 # - improve test suite 790 # 791 # Revision 1.5 2007/06/12 16:03:02 ncq 792 # - properly get rid of all columns in set_columns() 793 # 794 # Revision 1.4 2007/04/09 18:51:47 ncq 795 # - add support for multiple selections and auto-setting the widths 796 # 797 # Revision 1.3 2007/03/18 14:09:31 ncq 798 # - add set_columns() and set_column_widths() 799 # 800 # Revision 1.2 2006/12/11 20:50:45 ncq 801 # - get_selected_item_data() 802 # - deselect_selected_item() 803 # 804 # Revision 1.1 2006/07/23 20:34:50 ncq 805 # - list controls and widgets 806 # 807 # 808
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Thu Jul 29 04:09:39 2010 | http://epydoc.sourceforge.net |