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(parent=None, msg=None, caption=None, choices=None, selections=None, columns=None, data=None, edit_callback=None, new_callback=None, delete_callback=None, refresh_callback=None, single_selection=False, can_return_empty=False, ignore_OK_button=False):38 """Let user select item(s) from a list. 39 40 - edit_callback: (item data) 41 - new_callback: () 42 - delete_callback: (item data) 43 - refresh_callback: (listctrl) 44 45 returns None if cancelled 46 returns list (may be empty) of selected items 47 """ 48 if caption is None: 49 caption = _('generic multi choice dialog') 50 51 if single_selection: 52 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg, style = wx.LC_SINGLE_SEL) 53 else: 54 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg) 55 dlg.refresh_callback = refresh_callback 56 dlg.edit_callback = edit_callback 57 dlg.new_callback = new_callback 58 dlg.delete_callback = delete_callback 59 dlg.ignore_OK_button = ignore_OK_button 60 dlg.set_columns(columns = columns) 61 62 if refresh_callback is None: 63 dlg.set_string_items(items = choices) # list ctrl will refresh anyway if possible 64 dlg.set_column_widths() 65 66 if data is not None: 67 dlg.set_data(data=data) # can override data set if refresh_callback is not None 68 69 if selections is not None: 70 dlg.set_selections(selections = selections) 71 dlg.can_return_empty = can_return_empty 72 73 btn_pressed = dlg.ShowModal() 74 sels = dlg.get_selected_item_data(only_one = single_selection) 75 dlg.Destroy() 76 77 if btn_pressed == wx.ID_OK: 78 if can_return_empty and (sels is None): 79 return [] 80 return sels 81 82 return None83 #----------------------------------------------------------------85 """A dialog holding a list and a few buttons to act on the items.""" 86271 #================================================================88 89 try: 90 msg = kwargs['msg'] 91 del kwargs['msg'] 92 except KeyError: msg = None 93 94 wxgGenericListSelectorDlg.wxgGenericListSelectorDlg.__init__(self, *args, **kwargs) 95 96 if msg is None: 97 self._LBL_message.Hide() 98 else: 99 self._LBL_message.SetLabel(msg) 100 101 self.refresh_callback = None # called when new/edit/delete callbacks return True (IOW were not cancelled) 102 self.new_callback = None # called when NEW button pressed, no argument passed in 103 self.edit_callback = None # called when EDIT button pressed, data of topmost selected item passed in 104 self.delete_callback = None # called when DELETE button pressed, data of topmost selected item passed in 105 106 self.ignore_OK_button = False # by default do show/use the OK button 107 108 self.can_return_empty = False109 #------------------------------------------------------------111 self._LCTRL_items.set_columns(columns = columns)112 #------------------------------------------------------------114 self._LCTRL_items.set_column_widths(widths = widths)115 #------------------------------------------------------------117 self._LCTRL_items.set_string_items(items = items) 118 self._LCTRL_items.set_column_widths() 119 self._LCTRL_items.Select(0)120 #------------------------------------------------------------122 self._LCTRL_items.set_selections(selections = selections)123 #------------------------------------------------------------ 126 #------------------------------------------------------------128 return self._LCTRL_items.get_selected_item_data(only_one=only_one)129 #------------------------------------------------------------ 130 # event handlers 131 #------------------------------------------------------------133 if not self.__ignore_OK_button: 134 self._BTN_ok.SetDefault() 135 self._BTN_ok.Enable(True) 136 137 if self.edit_callback is not None: 138 self._BTN_edit.Enable(True) 139 140 if self.delete_callback is not None: 141 self._BTN_delete.Enable(True)142 #------------------------------------------------------------144 if self._LCTRL_items.get_selected_items(only_one=True) == -1: 145 if not self.can_return_empty: 146 self._BTN_cancel.SetDefault() 147 self._BTN_ok.Enable(False) 148 self._BTN_edit.Enable(False) 149 self._BTN_delete.Enable(False)150 #------------------------------------------------------------ 158 #------------------------------------------------------------ 168 #------------------------------------------------------------ 181 #------------------------------------------------------------ 182 # properties 183 #------------------------------------------------------------ 192 193 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button) 194 #------------------------------------------------------------ 197199 if callback is not None: 200 if self.refresh_callback is None: 201 raise ValueError('refresh callback must be set before new callback can be set') 202 if not callable(callback): 203 raise ValueError('<new> callback is not a callable: %s' % callback) 204 self.__new_callback = callback 205 206 if callback is None: 207 self._BTN_new.Enable(False) 208 self._BTN_new.Hide() 209 else: 210 self._BTN_new.Enable(True) 211 self._BTN_new.Show()212 213 new_callback = property(_get_new_callback, _set_new_callback) 214 #------------------------------------------------------------ 217219 if callback is not None: 220 if self.refresh_callback is None: 221 raise ValueError('refresh callback must be set before edit callback can be set') 222 if not callable(callback): 223 raise ValueError('<edit> callback is not a callable: %s' % callback) 224 self.__edit_callback = callback 225 226 if callback is None: 227 self._BTN_edit.Enable(False) 228 self._BTN_edit.Hide() 229 else: 230 self._BTN_edit.Enable(True) 231 self._BTN_edit.Show()232 233 edit_callback = property(_get_edit_callback, _set_edit_callback) 234 #------------------------------------------------------------ 237239 if callback is not None: 240 if self.refresh_callback is None: 241 raise ValueError('refresh callback must be set before delete callback can be set') 242 if not callable(callback): 243 raise ValueError('<delete> callback is not a callable: %s' % callback) 244 self.__delete_callback = callback 245 246 if callback is None: 247 self._BTN_delete.Enable(False) 248 self._BTN_delete.Hide() 249 else: 250 self._BTN_delete.Enable(True) 251 self._BTN_delete.Show()252 253 delete_callback = property(_get_delete_callback, _set_delete_callback) 254 #------------------------------------------------------------ 257 261263 if callback is not None: 264 if not callable(callback): 265 raise ValueError('<refresh> callback is not a callable: %s' % callback) 266 self.__refresh_callback = callback 267 if callback is not None: 268 wx.CallAfter(self._set_refresh_callback_helper)269 270 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)273 """A panel holding a generic multi-column list and action buttions.""" 274369 #================================================================276 277 try: 278 msg = kwargs['msg'] 279 del kwargs['msg'] 280 except KeyError: msg = None 281 282 wxgGenericListManagerPnl.wxgGenericListManagerPnl.__init__(self, *args, **kwargs) 283 284 if msg is None: 285 self._LBL_message.Hide() 286 else: 287 self._LBL_message.SetLabel(msg) 288 289 # new/edit/delete must return True/False to enable refresh 290 self.__new_callback = None # called when NEW button pressed, no argument passed in 291 self.edit_callback = None # called when EDIT button pressed, data of topmost selected item passed in 292 self.delete_callback = None # called when DELETE button pressed, data of topmost selected item passed in 293 self.refresh_callback = None # called when new/edit/delete callbacks return True (IOW were not cancelled)294 #------------------------------------------------------------ 295 # external API 296 #------------------------------------------------------------298 self._LCTRL_items.set_columns(columns = columns)299 #------------------------------------------------------------301 self._LCTRL_items.set_string_items(items = items) 302 self._LCTRL_items.set_column_widths() 303 304 if (items is None) or (len(items) == 0): 305 self._BTN_edit.Enable(False) 306 self._BTN_remove.Enable(False) 307 else: 308 self._LCTRL_items.Select(0)309 #------------------------------------------------------------311 self._LCTRL_items.set_selections(selections = selections)312 #------------------------------------------------------------ 315 #------------------------------------------------------------317 return self._LCTRL_items.get_selected_item_data(only_one=only_one)318 #------------------------------------------------------------ 319 # event handlers 320 #------------------------------------------------------------322 if self.edit_callback is not None: 323 self._BTN_edit.Enable(True) 324 if self.delete_callback is not None: 325 self._BTN_remove.Enable(True)326 #------------------------------------------------------------328 if self._LCTRL_items.get_selected_items(only_one=True) == -1: 329 self._BTN_edit.Enable(False) 330 self._BTN_remove.Enable(False)331 #------------------------------------------------------------ 338 #------------------------------------------------------------ 348 #------------------------------------------------------------ 358 #------------------------------------------------------------ 359 # properties 360 #------------------------------------------------------------ 363 367 368 new_callback = property(_get_new_callback, _set_new_callback)371522 #================================================================ 523 # main 524 #---------------------------------------------------------------- 525 if __name__ == '__main__': 526 527 from Gnumed.pycommon import gmI18N 528 gmI18N.activate_locale() 529 gmI18N.install_domain() 530 531 #------------------------------------------------------------373 374 try: 375 kwargs['style'] = kwargs['style'] | wx.LC_REPORT 376 except KeyError: 377 kwargs['style'] = wx.LC_REPORT 378 379 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL) 380 381 wx.ListCtrl.__init__(self, *args, **kwargs) 382 listmixins.ListCtrlAutoWidthMixin.__init__(self) 383 384 self.__widths = None 385 self.__data = None386 #------------------------------------------------------------ 387 # setters 388 #------------------------------------------------------------390 """(Re)define the columns. 391 392 Note that this will (have to) delete the items. 393 """ 394 self.ClearAll() 395 if columns is None: 396 return 397 for idx in range(len(columns)): 398 self.InsertColumn(idx, columns[idx])399 #------------------------------------------------------------401 """Set the column width policy. 402 403 widths = None: 404 use previous policy if any or default policy 405 widths != None: 406 use this policy and remember it for later calls 407 408 This means there is no way to *revert* to the default policy :-( 409 """ 410 # explicit policy ? 411 if widths is not None: 412 self.__widths = widths 413 for idx in range(len(self.__widths)): 414 self.SetColumnWidth(col = idx, width = self.__widths[idx]) 415 return 416 417 # previous policy ? 418 if self.__widths is not None: 419 for idx in range(len(self.__widths)): 420 self.SetColumnWidth(col = idx, width = self.__widths[idx]) 421 return 422 423 # default policy ! 424 if self.GetItemCount() == 0: 425 width_type = wx.LIST_AUTOSIZE_USEHEADER 426 else: 427 width_type = wx.LIST_AUTOSIZE 428 for idx in range(self.GetColumnCount()): 429 self.SetColumnWidth(col = idx, width = width_type)430 #------------------------------------------------------------432 """All item members must be unicode()able or None.""" 433 434 self.DeleteAllItems() 435 self.__data = items 436 437 if items is None: 438 return 439 440 for item in items: 441 try: 442 item[0] 443 if not isinstance(item, basestring): 444 is_numerically_iterable = True 445 else: 446 is_numerically_iterable = False 447 except TypeError: 448 is_numerically_iterable = False 449 450 if is_numerically_iterable: 451 # cannot use errors='replace' since then 452 # None/ints/unicode strings fail to get encoded 453 col_val = unicode(item[0]) 454 row_num = self.InsertStringItem(index = sys.maxint, label = col_val) 455 for col_idx in range(1, min(self.GetColumnCount(), len(item))): 456 col_val = unicode(item[col_idx]) 457 self.SetStringItem(index = row_num, col = col_idx, label = col_val) 458 else: 459 # cannot use errors='replace' since then None/ints/unicode strings fails to get encoded 460 col_val = unicode(item) 461 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)462 #------------------------------------------------------------ 466 #------------------------------------------------------------ 471 #------------------------------------------------------------ 472 # getters 473 #------------------------------------------------------------475 labels = [] 476 for col_idx in self.GetColumnCount(): 477 col = self.GetColumn(col = col_idx) 478 labels.append(col.GetText()) 479 return labels480 #------------------------------------------------------------482 if self.__data is None: # this isn't entirely clean 483 return None 484 485 return self.__data[item_idx]486 #------------------------------------------------------------488 489 if self.__is_single_selection or only_one: 490 return self.GetFirstSelected() 491 492 items = [] 493 idx = self.GetFirstSelected() 494 while idx != -1: 495 items.append(idx) 496 idx = self.GetNextSelected(idx) 497 498 return items499 #------------------------------------------------------------501 502 if self.__is_single_selection or only_one: 503 if self.__data is None: 504 return None 505 idx = self.GetFirstSelected() 506 if idx == -1: 507 return None 508 return self.__data[idx] 509 510 data = [] 511 if self.__data is None: 512 return data 513 idx = self.GetFirstSelected() 514 while idx != -1: 515 data.append(self.__data[idx]) 516 idx = self.GetNextSelected(idx) 517 518 return data519 #------------------------------------------------------------521 self.Select(idx = self.GetFirstSelected(), on = 0)533 app = wx.PyWidgetTester(size = (400, 500)) 534 dlg = wx.MultiChoiceDialog ( 535 parent = None, 536 message = 'test message', 537 caption = 'test caption', 538 choices = ['a', 'b', 'c', 'd', 'e'] 539 ) 540 dlg.ShowModal() 541 sels = dlg.GetSelections() 542 print "selected:" 543 for sel in sels: 544 print sel545 #------------------------------------------------------------ 551 552 def refresh(lctrl): 553 choices = ['a', 'b', 'c'] 554 lctrl.set_string_items(choices) 555 556 app = wx.PyWidgetTester(size = (200, 50)) 557 chosen = get_choices_from_list ( 558 # msg = 'select a health issue\nfrom the list below\n', 559 caption = 'select health issues', 560 #choices = [['D.M.II', '4'], ['MS', '3'], ['Fraktur', '2']], 561 #columns = ['issue', 'no of episodes'] 562 columns = ['issue'], 563 refresh_callback = refresh 564 #, edit_callback = edit 565 ) 566 print "chosen:" 567 print chosen 568 #------------------------------------------------------------ 569 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 570 test_get_choices_from_list() 571 #test_wxMultiChoiceDialog() 572 573 #================================================================ 574 # $Log: gmListWidgets.py,v $ 575 # Revision 1.37 2010-02-06 21:37:26 ncq 576 # - support ignoring OK button 577 # 578 # Revision 1.36 2010/01/31 18:17:33 ncq 579 # - make refresh callback setting smarter: set column widths after setting items 580 # 581 # Revision 1.35 2010/01/21 08:43:23 ncq 582 # - somewhat support setting col widths within get-choice-from-list 583 # 584 # Revision 1.34 2009/11/28 18:30:07 ncq 585 # - hide disabled buttons/show enabled ones 586 # 587 # Revision 1.33 2009/06/29 15:07:58 ncq 588 # - disable edit/delete buttons when setting items to None or [] 589 # 590 # Revision 1.32 2009/06/20 12:45:22 ncq 591 # - call refresh from refresh property setter if callable and not None 592 # 593 # Revision 1.31 2009/06/11 12:37:25 ncq 594 # - much simplified initial setup of list ctrls 595 # 596 # Revision 1.30 2009/06/04 16:32:01 ncq 597 # - use refresh from init if available to simplify external setup code 598 # 599 # Revision 1.29 2009/04/16 12:49:47 ncq 600 # - more sanity checks regarding action callbacks 601 # 602 # Revision 1.28 2009/01/17 23:07:29 ncq 603 # - support remembering previous widths policy 604 # 605 # Revision 1.27 2009/01/15 11:39:59 ncq 606 # - cleanup 607 # 608 # Revision 1.26 2008/12/25 16:55:36 ncq 609 # - allow returniny empty list = no item selected if desired 610 # 611 # Revision 1.25 2008/08/06 13:22:14 ncq 612 # - fix detection of item list type 613 # 614 # Revision 1.24 2008/07/24 14:00:18 ncq 615 # - better comments 616 # - resize columns after list refreshing in generic list selector 617 # - differentiate between iterables and non-iterables by means of 618 # an exception rather than checking for type.ListType in set_string_items 619 # 620 # Revision 1.23 2008/05/31 16:33:07 ncq 621 # - add TODO with URL 622 # 623 # Revision 1.22 2008/02/26 16:28:04 ncq 624 # - when auto-setting col widths in lists w/o items use header as width ;-) 625 # 626 # Revision 1.21 2007/11/28 22:37:00 ncq 627 # - robustify in the absence of selected values 628 # 629 # Revision 1.20 2007/11/23 23:34:39 ncq 630 # - when explicitely setting the selections deselect the 631 # 0th-index item selected by default 632 # 633 # Revision 1.19 2007/11/17 16:38:13 ncq 634 # - cGenericListManagerPnl 635 # 636 # Revision 1.18 2007/10/08 12:56:02 ncq 637 # - document callbacks 638 # - protect against self.__data being None in get(_selected)_item_data() 639 # 640 # Revision 1.17 2007/09/24 18:37:08 ncq 641 # - get_column_labels() 642 # 643 # Revision 1.16 2007/09/20 19:10:15 ncq 644 # - carefully handle list item insertion - handle both list 645 # of lists and list of strings 646 # 647 # Revision 1.15 2007/09/07 22:38:04 ncq 648 # - remove Fit() call since it's counterproductive for the list 649 # 650 # Revision 1.14 2007/09/02 20:54:26 ncq 651 # - remove cruft 652 # - support refresh_callback 653 # 654 # Revision 1.13 2007/08/31 23:05:05 ncq 655 # - fix single selection list 656 # 657 # Revision 1.12 2007/08/29 14:41:54 ncq 658 # - no more singular get_choice_from_list() 659 # - support add/delete callbacks in generic list selector 660 # 661 # Revision 1.11 2007/08/20 16:22:51 ncq 662 # - make get_choice(s)_from_list() more generic 663 # - cleanup, improved test 664 # - support edit button and message in generic list selector 665 # 666 # Revision 1.10 2007/07/22 09:26:25 ncq 667 # - new get_choice_from_list() 668 # 669 # Revision 1.9 2007/07/09 12:45:47 ncq 670 # - fix unicode()ing in set_string_items(): can't use (..., errors='replace') :-( 671 # - factor out cPatientListingCtrl into gmDataMiningWidgets.py 672 # 673 # Revision 1.8 2007/07/07 12:42:00 ncq 674 # - set_string_items now applies unicode() to all item members 675 # - cPatientListingCtrl and test suite 676 # 677 # Revision 1.7 2007/06/28 12:38:15 ncq 678 # - fix logic reversal in get_selected_*() 679 # 680 # Revision 1.6 2007/06/18 20:33:56 ncq 681 # - add get_choice(s)_from_list() 682 # - add cGenericListSelectorDlg 683 # - add set_string_items()/set_selections()/get_selected_items() 684 # - improve test suite 685 # 686 # Revision 1.5 2007/06/12 16:03:02 ncq 687 # - properly get rid of all columns in set_columns() 688 # 689 # Revision 1.4 2007/04/09 18:51:47 ncq 690 # - add support for multiple selections and auto-setting the widths 691 # 692 # Revision 1.3 2007/03/18 14:09:31 ncq 693 # - add set_columns() and set_column_widths() 694 # 695 # Revision 1.2 2006/12/11 20:50:45 ncq 696 # - get_selected_item_data() 697 # - deselect_selected_item() 698 # 699 # Revision 1.1 2006/07/23 20:34:50 ncq 700 # - list controls and widgets 701 # 702 # 703
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Fri Jul 16 04:10:45 2010 | http://epydoc.sourceforge.net |