Package Gnumed :: Package wxpython :: Module gmListWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmListWidgets

  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: /cvsroot/gnumed/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 None
83 #----------------------------------------------------------------
84 -class cGenericListSelectorDlg(wxgGenericListSelectorDlg.wxgGenericListSelectorDlg):
85 """A dialog holding a list and a few buttons to act on the items.""" 86
87 - def __init__(self, *args, **kwargs):
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 = False
109 #------------------------------------------------------------
110 - def set_columns(self, columns=None):
111 self._LCTRL_items.set_columns(columns = columns)
112 #------------------------------------------------------------
113 - def set_column_widths(self, widths=None):
114 self._LCTRL_items.set_column_widths(widths = widths)
115 #------------------------------------------------------------
116 - def set_string_items(self, items = None):
117 self._LCTRL_items.set_string_items(items = items) 118 self._LCTRL_items.set_column_widths() 119 self._LCTRL_items.Select(0)
120 #------------------------------------------------------------
121 - def set_selections(self, selections = None):
122 self._LCTRL_items.set_selections(selections = selections)
123 #------------------------------------------------------------
124 - def set_data(self, data = None):
125 self._LCTRL_items.set_data(data = data)
126 #------------------------------------------------------------
127 - def get_selected_item_data(self, only_one=False):
128 return self._LCTRL_items.get_selected_item_data(only_one=only_one)
129 #------------------------------------------------------------ 130 # event handlers 131 #------------------------------------------------------------
132 - def _on_list_item_selected(self, event):
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 #------------------------------------------------------------
143 - def _on_list_item_deselected(self, event):
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 #------------------------------------------------------------
151 - def _on_new_button_pressed(self, event):
152 if not self.new_callback(): 153 return 154 if self.refresh_callback is None: 155 return 156 self.refresh_callback(lctrl = self._LCTRL_items) 157 self._LCTRL_items.set_column_widths()
158 #------------------------------------------------------------
159 - def _on_edit_button_pressed(self, event):
160 # if the edit button *can* be pressed there are *supposed* 161 # to be both an item selected and an editor configured 162 if not self.edit_callback(self._LCTRL_items.get_selected_item_data(only_one=True)): 163 return 164 if self.refresh_callback is None: 165 return 166 self.refresh_callback(lctrl = self._LCTRL_items) 167 self._LCTRL_items.set_column_widths()
168 #------------------------------------------------------------
169 - def _on_delete_button_pressed(self, event):
170 # if the delete button *can* be pressed there are *supposed* 171 # to be both an item selected and a deletor configured 172 item_data = self._LCTRL_items.get_selected_item_data(only_one=True) 173 if item_data is None: 174 return 175 if not self.delete_callback(item_data): 176 return 177 if self.refresh_callback is None: 178 return 179 self.refresh_callback(lctrl = self._LCTRL_items) 180 self._LCTRL_items.set_column_widths()
181 #------------------------------------------------------------ 182 # properties 183 #------------------------------------------------------------
184 - def _set_ignore_OK_button(self, ignored):
185 self.__ignore_OK_button = ignored 186 if self.__ignore_OK_button: 187 self._BTN_ok.Enable(False) 188 self._BTN_ok.Hide() 189 else: 190 self._BTN_ok.Enable(True) 191 self._BTN_ok.Show()
192 193 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button) 194 #------------------------------------------------------------
195 - def _get_new_callback(self):
196 return self.__new_callback
197
198 - def _set_new_callback(self, callback):
199 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 #------------------------------------------------------------
215 - def _get_edit_callback(self):
216 return self.__edit_callback
217
218 - def _set_edit_callback(self, callback):
219 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 #------------------------------------------------------------
235 - def _get_delete_callback(self):
236 return self.__delete_callback
237
238 - def _set_delete_callback(self, callback):
239 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 #------------------------------------------------------------
255 - def _get_refresh_callback(self):
256 return self.__refresh_callback
257
259 self.refresh_callback(lctrl = self._LCTRL_items) 260 self._LCTRL_items.set_column_widths()
261
262 - def _set_refresh_callback(self, callback):
263 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)
271 #================================================================
272 -class cGenericListManagerPnl(wxgGenericListManagerPnl.wxgGenericListManagerPnl):
273 """A panel holding a generic multi-column list and action buttions.""" 274
275 - def __init__(self, *args, **kwargs):
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 #------------------------------------------------------------
297 - def set_columns(self, columns=None):
298 self._LCTRL_items.set_columns(columns = columns)
299 #------------------------------------------------------------
300 - def set_string_items(self, items = None):
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 #------------------------------------------------------------
310 - def set_selections(self, selections = None):
311 self._LCTRL_items.set_selections(selections = selections)
312 #------------------------------------------------------------
313 - def set_data(self, data = None):
314 self._LCTRL_items.set_data(data = data)
315 #------------------------------------------------------------
316 - def get_selected_item_data(self, only_one=False):
317 return self._LCTRL_items.get_selected_item_data(only_one=only_one)
318 #------------------------------------------------------------ 319 # event handlers 320 #------------------------------------------------------------
321 - def _on_list_item_selected(self, event):
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 #------------------------------------------------------------
327 - def _on_list_item_deselected(self, event):
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 #------------------------------------------------------------
332 - def _on_add_button_pressed(self, event):
333 if not self.new_callback(): 334 return 335 if self.refresh_callback is None: 336 return 337 self.refresh_callback(lctrl = self._LCTRL_items)
338 #------------------------------------------------------------
339 - def _on_edit_button_pressed(self, event):
340 item = self._LCTRL_items.get_selected_item_data(only_one=True) 341 if item is None: 342 return 343 if not self.edit_callback(item): 344 return 345 if self.refresh_callback is None: 346 return 347 self.refresh_callback(lctrl = self._LCTRL_items)
348 #------------------------------------------------------------
349 - def _on_remove_button_pressed(self, event):
350 item = self._LCTRL_items.get_selected_item_data(only_one=True) 351 if item is None: 352 return 353 if not self.delete_callback(item): 354 return 355 if self.refresh_callback is None: 356 return 357 self.refresh_callback(lctrl = self._LCTRL_items)
358 #------------------------------------------------------------ 359 # properties 360 #------------------------------------------------------------
361 - def _get_new_callback(self):
362 return self.__new_callback
363
364 - def _set_new_callback(self, callback):
365 self.__new_callback = callback 366 self._BTN_add.Enable(callback is not None)
367 368 new_callback = property(_get_new_callback, _set_new_callback)
369 #================================================================
370 -class cReportListCtrl(wx.ListCtrl, listmixins.ListCtrlAutoWidthMixin):
371
372 - def __init__(self, *args, **kwargs):
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 = None
386 #------------------------------------------------------------ 387 # setters 388 #------------------------------------------------------------
389 - def set_columns(self, columns=None):
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 #------------------------------------------------------------
400 - def set_column_widths(self, widths=None):
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 #------------------------------------------------------------
431 - def set_string_items(self, items = None):
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 #------------------------------------------------------------
463 - def set_data(self, data = None):
464 """<data must be a list corresponding to the item indices>""" 465 self.__data = data
466 #------------------------------------------------------------
467 - def set_selections(self, selections=None):
468 self.Select(0, on = 0) 469 for idx in selections: 470 self.Select(idx = idx, on = 1)
471 #------------------------------------------------------------ 472 # getters 473 #------------------------------------------------------------
474 - def get_column_labels(self):
475 labels = [] 476 for col_idx in self.GetColumnCount(): 477 col = self.GetColumn(col = col_idx) 478 labels.append(col.GetText()) 479 return labels
480 #------------------------------------------------------------
481 - def get_item_data(self, item_idx = None):
482 if self.__data is None: # this isn't entirely clean 483 return None 484 485 return self.__data[item_idx]
486 #------------------------------------------------------------
487 - def get_selected_items(self, only_one=False):
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 items
499 #------------------------------------------------------------
500 - def get_selected_item_data(self, only_one=False):
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 data
519 #------------------------------------------------------------
520 - def deselect_selected_item(self):
521 self.Select(idx = self.GetFirstSelected(), on = 0)
522 #================================================================ 523 # main 524 #---------------------------------------------------------------- 525 if __name__ == '__main__': 526 527 from Gnumed.pycommon import gmI18N 528 gmI18N.activate_locale() 529 gmI18N.install_domain() 530 531 #------------------------------------------------------------
532 - def test_wxMultiChoiceDialog():
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 sel
545 #------------------------------------------------------------
546 - def test_get_choices_from_list():
547 548 def edit(argument): 549 print "editor called with:" 550 print argument
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