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  __version__ = "$Revision: 1.37 $" 
  16  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
  17  __license__ = "GPL" 
  18   
  19   
  20  import sys, types 
  21   
  22   
  23  import wx 
  24  import wx.lib.mixins.listctrl as listmixins 
  25   
  26   
  27  if __name__ == '__main__': 
  28          sys.path.insert(0, '../../') 
  29   
  30  #================================================================ 
  31  # FIXME: configurable callback on double-click action 
  32   
33 -def get_choices_from_list ( 34 parent=None, 35 msg=None, 36 caption=None, 37 choices=None, 38 selections=None, 39 columns=None, 40 data=None, 41 edit_callback=None, 42 new_callback=None, 43 delete_callback=None, 44 refresh_callback=None, 45 single_selection=False, 46 can_return_empty=False, 47 ignore_OK_button=False, 48 left_extra_button=None, 49 middle_extra_button=None, 50 right_extra_button=None, 51 list_tooltip_callback=None):
52 """Let user select item(s) from a list. 53 54 - new_callback: () 55 - edit_callback: (item data) 56 - delete_callback: (item data) 57 - refresh_callback: (listctrl) 58 - list_tooltip_callback: (item data) 59 60 - left/middle/right_extra_button: (label, tooltip, <callback>) 61 <callback> is called with item_data as the only argument 62 63 returns: 64 on [CANCEL]: None 65 on [OK]: 66 if any items selected: 67 list of selected items 68 else: 69 if can_return_empty is True: 70 empty list 71 else: 72 None 73 """ 74 if caption is None: 75 caption = _('generic multi choice dialog') 76 77 if single_selection: 78 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg, style = wx.LC_SINGLE_SEL) 79 else: 80 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg) 81 82 dlg.refresh_callback = refresh_callback 83 dlg.edit_callback = edit_callback 84 dlg.new_callback = new_callback 85 dlg.delete_callback = delete_callback 86 dlg.list_tooltip_callback = list_tooltip_callback 87 88 dlg.ignore_OK_button = ignore_OK_button 89 dlg.left_extra_button = left_extra_button 90 dlg.middle_extra_button = middle_extra_button 91 dlg.right_extra_button = right_extra_button 92 93 dlg.set_columns(columns = columns) 94 95 if refresh_callback is None: 96 dlg.set_string_items(items = choices) # list ctrl will refresh anyway if possible 97 dlg.set_column_widths() 98 99 if data is not None: 100 dlg.set_data(data = data) # can override data set if refresh_callback is not None 101 102 if selections is not None: 103 dlg.set_selections(selections = selections) 104 dlg.can_return_empty = can_return_empty 105 106 btn_pressed = dlg.ShowModal() 107 sels = dlg.get_selected_item_data(only_one = single_selection) 108 dlg.Destroy() 109 110 if btn_pressed == wx.ID_OK: 111 if can_return_empty and (sels is None): 112 return [] 113 return sels 114 115 return None
116 #---------------------------------------------------------------- 117 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg 118
119 -class cGenericListSelectorDlg(wxgGenericListSelectorDlg.wxgGenericListSelectorDlg):
120 """A dialog holding a list and a few buttons to act on the items.""" 121 122 # FIXME: configurable callback on double-click action 123
124 - def __init__(self, *args, **kwargs):
125 126 try: 127 msg = kwargs['msg'] 128 del kwargs['msg'] 129 except KeyError: msg = None 130 131 wxgGenericListSelectorDlg.wxgGenericListSelectorDlg.__init__(self, *args, **kwargs) 132 133 self.message = msg 134 135 self.left_extra_button = None 136 self.middle_extra_button = None 137 self.right_extra_button = None 138 139 self.refresh_callback = None # called when new/edit/delete callbacks return True (IOW were not cancelled) 140 self.new_callback = None # called when NEW button pressed, no argument passed in 141 self.edit_callback = None # called when EDIT button pressed, data of topmost selected item passed in 142 self.delete_callback = None # called when DELETE button pressed, data of topmost selected item passed in 143 144 self.can_return_empty = False 145 self.ignore_OK_button = False # by default do show/use the OK button
146 #------------------------------------------------------------
147 - def set_columns(self, columns=None):
148 self._LCTRL_items.set_columns(columns = columns)
149 #------------------------------------------------------------
150 - def set_column_widths(self, widths=None):
151 self._LCTRL_items.set_column_widths(widths = widths)
152 #------------------------------------------------------------
153 - def set_string_items(self, items = None):
154 self._LCTRL_items.set_string_items(items = items) 155 self._LCTRL_items.set_column_widths() 156 self._LCTRL_items.Select(0)
157 #------------------------------------------------------------
158 - def set_selections(self, selections = None):
159 self._LCTRL_items.set_selections(selections = selections) 160 if selections is None: 161 return 162 if len(selections) == 0: 163 return 164 if self.ignore_OK_button: 165 return 166 self._BTN_ok.Enable(True) 167 self._BTN_ok.SetDefault()
168 #------------------------------------------------------------
169 - def set_data(self, data = None):
170 self._LCTRL_items.set_data(data = data)
171 #------------------------------------------------------------
172 - def get_selected_item_data(self, only_one=False):
173 return self._LCTRL_items.get_selected_item_data(only_one=only_one)
174 #------------------------------------------------------------ 175 # event handlers 176 #------------------------------------------------------------
177 - def _on_list_item_selected(self, event):
178 if not self.__ignore_OK_button: 179 self._BTN_ok.SetDefault() 180 self._BTN_ok.Enable(True) 181 182 if self.edit_callback is not None: 183 self._BTN_edit.Enable(True) 184 185 if self.delete_callback is not None: 186 self._BTN_delete.Enable(True)
187 #------------------------------------------------------------
188 - def _on_list_item_deselected(self, event):
189 if self._LCTRL_items.get_selected_items(only_one=True) == -1: 190 if not self.can_return_empty: 191 self._BTN_cancel.SetDefault() 192 self._BTN_ok.Enable(False) 193 self._BTN_edit.Enable(False) 194 self._BTN_delete.Enable(False)
195 #------------------------------------------------------------
196 - def _on_new_button_pressed(self, event):
197 if not self.new_callback(): 198 self._LCTRL_items.SetFocus() 199 return 200 if self.refresh_callback is None: 201 self._LCTRL_items.SetFocus() 202 return 203 wx.BeginBusyCursor() 204 try: 205 self.refresh_callback(lctrl = self._LCTRL_items) 206 finally: 207 wx.EndBusyCursor() 208 self._LCTRL_items.set_column_widths() 209 self._LCTRL_items.SetFocus()
210 #------------------------------------------------------------
211 - def _on_edit_button_pressed(self, event):
212 # if the edit button *can* be pressed there are *supposed* 213 # to be both an item selected and an editor configured 214 if not self.edit_callback(self._LCTRL_items.get_selected_item_data(only_one=True)): 215 self._LCTRL_items.SetFocus() 216 return 217 if self.refresh_callback is None: 218 self._LCTRL_items.SetFocus() 219 return 220 wx.BeginBusyCursor() 221 try: 222 self.refresh_callback(lctrl = self._LCTRL_items) 223 finally: 224 wx.EndBusyCursor() 225 self._LCTRL_items.set_column_widths() 226 self._LCTRL_items.SetFocus()
227 #------------------------------------------------------------
228 - def _on_delete_button_pressed(self, event):
229 # if the delete button *can* be pressed there are *supposed* 230 # to be both an item selected and a deletor configured 231 item_data = self._LCTRL_items.get_selected_item_data(only_one=True) 232 if item_data is None: 233 self._LCTRL_items.SetFocus() 234 return 235 if not self.delete_callback(item_data): 236 self._LCTRL_items.SetFocus() 237 return 238 if self.refresh_callback is None: 239 self._LCTRL_items.SetFocus() 240 return 241 wx.BeginBusyCursor() 242 try: 243 self.refresh_callback(lctrl = self._LCTRL_items) 244 finally: 245 wx.EndBusyCursor() 246 self._LCTRL_items.set_column_widths() 247 self._LCTRL_items.SetFocus()
248 #------------------------------------------------------------
249 - def _on_left_extra_button_pressed(self, event):
250 item_data = self._LCTRL_items.get_selected_item_data(only_one=True) 251 if not self.__left_extra_button_callback(item_data): 252 self._LCTRL_items.SetFocus() 253 return 254 if self.refresh_callback is None: 255 self._LCTRL_items.SetFocus() 256 return 257 wx.BeginBusyCursor() 258 try: 259 self.refresh_callback(lctrl = self._LCTRL_items) 260 finally: 261 wx.EndBusyCursor() 262 self._LCTRL_items.set_column_widths() 263 self._LCTRL_items.SetFocus()
264 #------------------------------------------------------------
265 - def _on_middle_extra_button_pressed(self, event):
266 item_data = self._LCTRL_items.get_selected_item_data(only_one=True) 267 if not self.__middle_extra_button_callback(item_data): 268 self._LCTRL_items.SetFocus() 269 return 270 if self.refresh_callback is None: 271 self._LCTRL_items.SetFocus() 272 return 273 wx.BeginBusyCursor() 274 try: 275 self.refresh_callback(lctrl = self._LCTRL_items) 276 finally: 277 wx.EndBusyCursor() 278 self._LCTRL_items.set_column_widths() 279 self._LCTRL_items.SetFocus()
280 #------------------------------------------------------------
281 - def _on_right_extra_button_pressed(self, event):
282 item_data = self._LCTRL_items.get_selected_item_data(only_one=True) 283 if not self.__right_extra_button_callback(item_data): 284 self._LCTRL_items.SetFocus() 285 return 286 if self.refresh_callback is None: 287 self._LCTRL_items.SetFocus() 288 return 289 wx.BeginBusyCursor() 290 try: 291 self.refresh_callback(lctrl = self._LCTRL_items) 292 finally: 293 wx.EndBusyCursor() 294 self._LCTRL_items.set_column_widths() 295 self._LCTRL_items.SetFocus()
296 #------------------------------------------------------------ 297 # properties 298 #------------------------------------------------------------
299 - def _set_ignore_OK_button(self, ignored):
300 self.__ignore_OK_button = ignored 301 if self.__ignore_OK_button: 302 self._BTN_ok.Hide() 303 self._BTN_ok.Enable(False) 304 else: 305 self._BTN_ok.Show() 306 if self._LCTRL_items.get_selected_items(only_one=True) == -1: 307 if self.can_return_empty: 308 self._BTN_ok.Enable(True) 309 else: 310 self._BTN_ok.Enable(False) 311 self._BTN_cancel.SetDefault()
312 313 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button) 314 #------------------------------------------------------------
315 - def _set_left_extra_button(self, definition):
316 if definition is None: 317 self._BTN_extra_left.Enable(False) 318 self._BTN_extra_left.Hide() 319 self.__left_extra_button_callback = None 320 return 321 322 (label, tooltip, callback) = definition 323 if not callable(callback): 324 raise ValueError('<left extra button> callback is not a callable: %s' % callback) 325 self.__left_extra_button_callback = callback 326 self._BTN_extra_left.SetLabel(label) 327 self._BTN_extra_left.SetToolTipString(tooltip) 328 self._BTN_extra_left.Enable(True) 329 self._BTN_extra_left.Show()
330 331 left_extra_button = property(lambda x:x, _set_left_extra_button) 332 #------------------------------------------------------------
333 - def _set_middle_extra_button(self, definition):
334 if definition is None: 335 self._BTN_extra_middle.Enable(False) 336 self._BTN_extra_middle.Hide() 337 self.__middle_extra_button_callback = None 338 return 339 340 (label, tooltip, callback) = definition 341 if not callable(callback): 342 raise ValueError('<middle extra button> callback is not a callable: %s' % callback) 343 self.__middle_extra_button_callback = callback 344 self._BTN_extra_middle.SetLabel(label) 345 self._BTN_extra_middle.SetToolTipString(tooltip) 346 self._BTN_extra_middle.Enable(True) 347 self._BTN_extra_middle.Show()
348 349 middle_extra_button = property(lambda x:x, _set_middle_extra_button) 350 #------------------------------------------------------------
351 - def _set_right_extra_button(self, definition):
352 if definition is None: 353 self._BTN_extra_right.Enable(False) 354 self._BTN_extra_right.Hide() 355 self.__right_extra_button_callback = None 356 return 357 358 (label, tooltip, callback) = definition 359 if not callable(callback): 360 raise ValueError('<right extra button> callback is not a callable: %s' % callback) 361 self.__right_extra_button_callback = callback 362 self._BTN_extra_right.SetLabel(label) 363 self._BTN_extra_right.SetToolTipString(tooltip) 364 self._BTN_extra_right.Enable(True) 365 self._BTN_extra_right.Show()
366 367 right_extra_button = property(lambda x:x, _set_right_extra_button) 368 #------------------------------------------------------------
369 - def _get_new_callback(self):
370 return self.__new_callback
371
372 - def _set_new_callback(self, callback):
373 if callback is not None: 374 if self.refresh_callback is None: 375 raise ValueError('refresh callback must be set before new callback can be set') 376 if not callable(callback): 377 raise ValueError('<new> callback is not a callable: %s' % callback) 378 self.__new_callback = callback 379 380 if callback is None: 381 self._BTN_new.Enable(False) 382 self._BTN_new.Hide() 383 else: 384 self._BTN_new.Enable(True) 385 self._BTN_new.Show()
386 387 new_callback = property(_get_new_callback, _set_new_callback) 388 #------------------------------------------------------------
389 - def _get_edit_callback(self):
390 return self.__edit_callback
391
392 - def _set_edit_callback(self, callback):
393 if callback is not None: 394 if not callable(callback): 395 raise ValueError('<edit> callback is not a callable: %s' % callback) 396 self.__edit_callback = callback 397 398 if callback is None: 399 self._BTN_edit.Enable(False) 400 self._BTN_edit.Hide() 401 else: 402 self._BTN_edit.Enable(True) 403 self._BTN_edit.Show()
404 405 edit_callback = property(_get_edit_callback, _set_edit_callback) 406 #------------------------------------------------------------
407 - def _get_delete_callback(self):
408 return self.__delete_callback
409
410 - def _set_delete_callback(self, callback):
411 if callback is not None: 412 if self.refresh_callback is None: 413 raise ValueError('refresh callback must be set before delete callback can be set') 414 if not callable(callback): 415 raise ValueError('<delete> callback is not a callable: %s' % callback) 416 self.__delete_callback = callback 417 418 if callback is None: 419 self._BTN_delete.Enable(False) 420 self._BTN_delete.Hide() 421 else: 422 self._BTN_delete.Enable(True) 423 self._BTN_delete.Show()
424 425 delete_callback = property(_get_delete_callback, _set_delete_callback) 426 #------------------------------------------------------------
427 - def _get_refresh_callback(self):
428 return self.__refresh_callback
429
431 wx.BeginBusyCursor() 432 try: 433 self.refresh_callback(lctrl = self._LCTRL_items) 434 finally: 435 wx.EndBusyCursor() 436 self._LCTRL_items.set_column_widths()
437
438 - def _set_refresh_callback(self, callback):
439 if callback is not None: 440 if not callable(callback): 441 raise ValueError('<refresh> callback is not a callable: %s' % callback) 442 self.__refresh_callback = callback 443 if callback is not None: 444 wx.CallAfter(self._set_refresh_callback_helper)
445 446 refresh_callback = property(_get_refresh_callback, _set_refresh_callback) 447 #------------------------------------------------------------
448 - def _set_list_tooltip_callback(self, callback):
449 self._LCTRL_items.item_tooltip_callback = callback
450 451 list_tooltip_callback = property(lambda x:x, _set_list_tooltip_callback) 452 #def _get_tooltip(self, item): # inside a class 453 #def _get_tooltip(item): # outside a class 454 #------------------------------------------------------------
455 - def _set_message(self, message):
456 if message is None: 457 self._LBL_message.Hide() 458 return 459 self._LBL_message.SetLabel(message) 460 self._LBL_message.Show()
461 462 message = property(lambda x:x, _set_message)
463 #================================================================ 464 from Gnumed.wxGladeWidgets import wxgGenericListManagerPnl 465
466 -class cGenericListManagerPnl(wxgGenericListManagerPnl.wxgGenericListManagerPnl):
467 """A panel holding a generic multi-column list and action buttions.""" 468
469 - def __init__(self, *args, **kwargs):
470 471 try: 472 msg = kwargs['msg'] 473 del kwargs['msg'] 474 except KeyError: msg = None 475 476 wxgGenericListManagerPnl.wxgGenericListManagerPnl.__init__(self, *args, **kwargs) 477 478 if msg is None: 479 self._LBL_message.Hide() 480 else: 481 self._LBL_message.SetLabel(msg) 482 483 # new/edit/delete must return True/False to enable refresh 484 self.__new_callback = None # called when NEW button pressed, no argument passed in 485 self.edit_callback = None # called when EDIT button pressed, data of topmost selected item passed in 486 self.delete_callback = None # called when DELETE button pressed, data of topmost selected item passed in 487 self.refresh_callback = None # called when new/edit/delete callbacks return True (IOW were not cancelled) 488 489 self.__select_callback = None # called when an item is selected, data of topmost selected item passed in
490 #------------------------------------------------------------ 491 # external API 492 #------------------------------------------------------------
493 - def set_columns(self, columns=None):
494 self._LCTRL_items.set_columns(columns = columns)
495 #------------------------------------------------------------
496 - def set_string_items(self, items = None):
497 self._LCTRL_items.set_string_items(items = items) 498 self._LCTRL_items.set_column_widths() 499 500 if (items is None) or (len(items) == 0): 501 self._BTN_edit.Enable(False) 502 self._BTN_remove.Enable(False) 503 else: 504 self._LCTRL_items.Select(0)
505 #------------------------------------------------------------
506 - def set_selections(self, selections = None):
507 self._LCTRL_items.set_selections(selections = selections)
508 #------------------------------------------------------------
509 - def set_data(self, data = None):
510 self._LCTRL_items.set_data(data = data)
511 #------------------------------------------------------------
512 - def get_selected_item_data(self, only_one=False):
513 return self._LCTRL_items.get_selected_item_data(only_one=only_one)
514 #------------------------------------------------------------ 515 # event handlers 516 #------------------------------------------------------------
517 - def _on_list_item_selected(self, event):
518 if self.edit_callback is not None: 519 self._BTN_edit.Enable(True) 520 if self.delete_callback is not None: 521 self._BTN_remove.Enable(True) 522 if self.__select_callback is not None: 523 item = self._LCTRL_items.get_selected_item_data(only_one=True) 524 self.__select_callback(item)
525 #------------------------------------------------------------
526 - def _on_list_item_deselected(self, event):
527 if self._LCTRL_items.get_selected_items(only_one=True) == -1: 528 self._BTN_edit.Enable(False) 529 self._BTN_remove.Enable(False) 530 if self.__select_callback is not None: 531 self.__select_callback(None)
532 #------------------------------------------------------------
533 - def _on_add_button_pressed(self, event):
534 if not self.new_callback(): 535 return 536 if self.refresh_callback is None: 537 return 538 wx.BeginBusyCursor() 539 try: 540 self.refresh_callback(lctrl = self._LCTRL_items) 541 finally: 542 wx.EndBusyCursor()
543 #------------------------------------------------------------
544 - def _on_list_item_activated(self, event):
545 if self.edit_callback is None: 546 return 547 self._on_edit_button_pressed(event)
548 #------------------------------------------------------------
549 - def _on_edit_button_pressed(self, event):
550 item = self._LCTRL_items.get_selected_item_data(only_one=True) 551 if item is None: 552 return 553 if not self.edit_callback(item): 554 return 555 if self.refresh_callback is None: 556 return 557 wx.BeginBusyCursor() 558 try: 559 self.refresh_callback(lctrl = self._LCTRL_items) 560 finally: 561 wx.EndBusyCursor()
562 #------------------------------------------------------------
563 - def _on_remove_button_pressed(self, event):
564 item = self._LCTRL_items.get_selected_item_data(only_one=True) 565 if item is None: 566 return 567 if not self.delete_callback(item): 568 return 569 if self.refresh_callback is None: 570 return 571 wx.BeginBusyCursor() 572 try: 573 self.refresh_callback(lctrl = self._LCTRL_items) 574 finally: 575 wx.EndBusyCursor()
576 #------------------------------------------------------------ 577 # properties 578 #------------------------------------------------------------
579 - def _get_new_callback(self):
580 return self.__new_callback
581
582 - def _set_new_callback(self, callback):
583 if callback is not None: 584 if not callable(callback): 585 raise ValueError('<new> callback is not a callable: %s' % callback) 586 self.__new_callback = callback 587 self._BTN_add.Enable(callback is not None)
588 589 new_callback = property(_get_new_callback, _set_new_callback) 590 #------------------------------------------------------------
591 - def _get_select_callback(self):
592 return self.__select_callback
593
594 - def _set_select_callback(self, callback):
595 if callback is not None: 596 if not callable(callback): 597 raise ValueError('<select> callback is not a callable: %s' % callback) 598 self.__select_callback = callback
599 600 select_callback = property(_get_select_callback, _set_select_callback) 601 #------------------------------------------------------------
602 - def _get_message(self):
603 return self._LBL_message.GetLabel()
604
605 - def _set_message(self, msg):
606 if msg is None: 607 self._LBL_message.Hide() 608 self._LBL_message.SetLabel(u'') 609 else: 610 self._LBL_message.SetLabel(msg) 611 self._LBL_message.Show() 612 self.Layout()
613 614 message = property(_get_message, _set_message)
615 #================================================================ 616 from Gnumed.wxGladeWidgets import wxgItemPickerDlg 617
618 -class cItemPickerDlg(wxgItemPickerDlg.wxgItemPickerDlg):
619
620 - def __init__(self, *args, **kwargs):
621 622 try: 623 msg = kwargs['msg'] 624 del kwargs['msg'] 625 except KeyError: 626 msg = None 627 628 wxgItemPickerDlg.wxgItemPickerDlg.__init__(self, *args, **kwargs) 629 630 if msg is None: 631 self._LBL_msg.Hide() 632 else: 633 self._LBL_msg.SetLabel(msg) 634 635 self._LCTRL_left.activate_callback = self.__pick_selected 636 #self._LCTRL_left.item_tooltip_callback = self.__on_get_item_tooltip 637 638 self._LCTRL_left.SetFocus()
639 #------------------------------------------------------------ 640 # external API 641 #------------------------------------------------------------
642 - def set_columns(self, columns=None, columns_right=None):
643 self._LCTRL_left.set_columns(columns = columns) 644 if columns_right is None: 645 self._LCTRL_right.set_columns(columns = columns) 646 else: 647 if len(columns_right) < len(columns): 648 cols = columns 649 else: 650 cols = columns_right[:len(columns)] 651 self._LCTRL_right.set_columns(columns = cols)
652 #------------------------------------------------------------
653 - def set_string_items(self, items = None):
654 self._LCTRL_left.set_string_items(items = items) 655 self._LCTRL_left.set_column_widths() 656 self._LCTRL_right.set_string_items() 657 658 self._BTN_left2right.Enable(False) 659 self._BTN_right2left.Enable(False)
660 #------------------------------------------------------------
661 - def set_selections(self, selections = None):
662 self._LCTRL_left.set_selections(selections = selections)
663 #------------------------------------------------------------
664 - def set_choices(self, choices=None, data=None):
665 self.set_string_items(items = choices) 666 if data is not None: 667 self.set_data(data = data)
668 #------------------------------------------------------------
669 - def set_picks(self, picks=None, data=None):
670 self._LCTRL_right.set_string_items(picks) 671 self._LCTRL_right.set_column_widths() 672 if data is not None: 673 self._LCTRL_right.set_data(data = data)
674 #------------------------------------------------------------
675 - def set_data(self, data = None):
676 self._LCTRL_left.set_data(data = data)
677 #------------------------------------------------------------
678 - def get_picks(self):
679 return self._LCTRL_right.get_item_data()
680 #------------------------------------------------------------ 681 # internal helpers 682 #------------------------------------------------------------
683 - def __pick_selected(self, event=None):
684 if self._LCTRL_left.get_selected_items(only_one = True) == -1: 685 return 686 687 right_items = self._LCTRL_right.get_string_items() 688 right_data = self._LCTRL_right.get_item_data() 689 690 right_items.extend(self._LCTRL_left.get_selected_string_items(only_one = False)) 691 self._LCTRL_right.set_string_items(items = right_items) 692 del right_items 693 694 if right_data is None: 695 self._LCTRL_right.set_data(data = self._LCTRL_left.get_selected_item_data(only_one = False)) 696 else: 697 right_data.extend(self._LCTRL_left.get_selected_item_data(only_one = False)) 698 self._LCTRL_right.set_data(data = right_data) 699 del right_data 700 701 self._LCTRL_right.set_column_widths()
702 #------------------------------------------------------------
703 - def __remove_selected_picks(self):
704 if self._LCTRL_right.get_selected_items(only_one = True) == -1: 705 return 706 707 for item_idx in self._LCTRL_right.get_selected_items(only_one = False): 708 self._LCTRL_right.remove_item(item_idx) 709 710 if self._LCTRL_right.GetItemCount() == 0: 711 self._BTN_right2left.Enable(False)
712 #------------------------------------------------------------ 713 # event handlers 714 #------------------------------------------------------------
715 - def _on_left_list_item_selected(self, event):
716 self._BTN_left2right.Enable(True)
717 #------------------------------------------------------------
718 - def _on_left_list_item_deselected(self, event):
719 if self._LCTRL_left.get_selected_items(only_one = True) == -1: 720 self._BTN_left2right.Enable(False)
721 #------------------------------------------------------------
722 - def _on_right_list_item_selected(self, event):
723 self._BTN_right2left.Enable(True)
724 #------------------------------------------------------------
725 - def _on_right_list_item_deselected(self, event):
726 if self._LCTRL_right.get_selected_items(only_one = True) == -1: 727 self._BTN_right2left.Enable(False)
728 #------------------------------------------------------------
729 - def _on_button_left2right_pressed(self, event):
730 self.__pick_selected()
731 #------------------------------------------------------------
732 - def _on_button_right2left_pressed(self, event):
733 self.__remove_selected_picks()
734 #================================================================
735 -class cReportListCtrl(wx.ListCtrl, listmixins.ListCtrlAutoWidthMixin):
736 737 # FIXME: searching by typing 738
739 - def __init__(self, *args, **kwargs):
740 741 try: 742 kwargs['style'] = kwargs['style'] | wx.LC_REPORT 743 except KeyError: 744 kwargs['style'] = wx.LC_REPORT 745 746 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL) 747 748 wx.ListCtrl.__init__(self, *args, **kwargs) 749 listmixins.ListCtrlAutoWidthMixin.__init__(self) 750 751 self.__widths = None 752 self.__data = None 753 self.__activate_callback = None 754 755 self.Bind(wx.EVT_MOTION, self._on_mouse_motion) 756 self.__item_tooltip_callback = None 757 self.__tt_last_item = None 758 self.__tt_static_part = _("""Select the items you want to work on. 759 760 A discontinuous selection may depend on your holding down a platform-dependent modifier key (<ctrl>, <alt>, etc) or key combination (eg. <ctrl-shift> or <ctrl-alt>) while clicking.""")
761 #------------------------------------------------------------ 762 # setters 763 #------------------------------------------------------------
764 - def set_columns(self, columns=None):
765 """(Re)define the columns. 766 767 Note that this will (have to) delete the items. 768 """ 769 self.ClearAll() 770 self.__tt_last_item = None 771 if columns is None: 772 return 773 for idx in range(len(columns)): 774 self.InsertColumn(idx, columns[idx])
775 #------------------------------------------------------------
776 - def set_column_widths(self, widths=None):
777 """Set the column width policy. 778 779 widths = None: 780 use previous policy if any or default policy 781 widths != None: 782 use this policy and remember it for later calls 783 784 This means there is no way to *revert* to the default policy :-( 785 """ 786 # explicit policy ? 787 if widths is not None: 788 self.__widths = widths 789 for idx in range(len(self.__widths)): 790 self.SetColumnWidth(col = idx, width = self.__widths[idx]) 791 return 792 793 # previous policy ? 794 if self.__widths is not None: 795 for idx in range(len(self.__widths)): 796 self.SetColumnWidth(col = idx, width = self.__widths[idx]) 797 return 798 799 # default policy ! 800 if self.GetItemCount() == 0: 801 width_type = wx.LIST_AUTOSIZE_USEHEADER 802 else: 803 width_type = wx.LIST_AUTOSIZE 804 for idx in range(self.GetColumnCount()): 805 self.SetColumnWidth(col = idx, width = width_type)
806 #------------------------------------------------------------
807 - def set_string_items(self, items = None):
808 """All item members must be unicode()able or None.""" 809 810 self.DeleteAllItems() 811 self.__data = items 812 self.__tt_last_item = None 813 814 if items is None: 815 return 816 817 for item in items: 818 try: 819 item[0] 820 if not isinstance(item, basestring): 821 is_numerically_iterable = True 822 else: 823 is_numerically_iterable = False 824 except TypeError: 825 is_numerically_iterable = False 826 827 if is_numerically_iterable: 828 # cannot use errors='replace' since then 829 # None/ints/unicode strings fail to get encoded 830 col_val = unicode(item[0]) 831 row_num = self.InsertStringItem(index = sys.maxint, label = col_val) 832 for col_idx in range(1, min(self.GetColumnCount(), len(item))): 833 col_val = unicode(item[col_idx]) 834 self.SetStringItem(index = row_num, col = col_idx, label = col_val) 835 else: 836 # cannot use errors='replace' since then None/ints/unicode strings fails to get encoded 837 col_val = unicode(item) 838 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
839 #------------------------------------------------------------
840 - def set_data(self, data = None):
841 """<data must be a list corresponding to the item indices>""" 842 self.__data = data 843 self.__tt_last_item = None
844 #------------------------------------------------------------
845 - def set_selections(self, selections=None):
846 self.Select(0, on = 0) 847 if selections is None: 848 return 849 for idx in selections: 850 self.Select(idx = idx, on = 1)
851 #self.SetItemState(idx, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) 852
853 - def __get_selections(self):
854 if self.__is_single_selection: 855 return [self.GetFirstSelected()] 856 selections = [] 857 idx = self.GetFirstSelected() 858 while idx != -1: 859 selections.append(idx) 860 idx = self.GetNextSelected(idx) 861 return selections
862 863 selections = property(__get_selections, set_selections) 864 #------------------------------------------------------------ 865 # getters 866 #------------------------------------------------------------
867 - def get_column_labels(self):
868 labels = [] 869 for col_idx in self.GetColumnCount(): 870 col = self.GetColumn(col = col_idx) 871 labels.append(col.GetText()) 872 return labels
873 #------------------------------------------------------------
874 - def get_item(self, item_idx=None):
875 if item_idx is not None: 876 return self.GetItem(item_idx)
877 #------------------------------------------------------------
878 - def get_items(self):
879 return [ self.GetItem(item_idx) for item_idx in range(self.GetItemCount()) ]
880 #------------------------------------------------------------
881 - def get_string_items(self):
882 return [ self.GetItemText(item_idx) for item_idx in range(self.GetItemCount()) ]
883 #------------------------------------------------------------
884 - def get_selected_items(self, only_one=False):
885 886 if self.__is_single_selection or only_one: 887 return self.GetFirstSelected() 888 889 items = [] 890 idx = self.GetFirstSelected() 891 while idx != -1: 892 items.append(idx) 893 idx = self.GetNextSelected(idx) 894 895 return items
896 #------------------------------------------------------------
897 - def get_selected_string_items(self, only_one=False):
898 899 if self.__is_single_selection or only_one: 900 return self.GetItemText(self.GetFirstSelected()) 901 902 items = [] 903 idx = self.GetFirstSelected() 904 while idx != -1: 905 items.append(self.GetItemText(idx)) 906 idx = self.GetNextSelected(idx) 907 908 return items
909 #------------------------------------------------------------
910 - def get_item_data(self, item_idx = None):
911 if self.__data is None: # this isn't entirely clean 912 return None 913 914 if item_idx is not None: 915 return self.__data[item_idx] 916 917 return [ self.__data[item_idx] for item_idx in range(self.GetItemCount()) ]
918 #------------------------------------------------------------
919 - def get_selected_item_data(self, only_one=False):
920 921 if self.__is_single_selection or only_one: 922 if self.__data is None: 923 return None 924 idx = self.GetFirstSelected() 925 if idx == -1: 926 return None 927 return self.__data[idx] 928 929 data = [] 930 if self.__data is None: 931 return data 932 idx = self.GetFirstSelected() 933 while idx != -1: 934 data.append(self.__data[idx]) 935 idx = self.GetNextSelected(idx) 936 937 return data
938 #------------------------------------------------------------
939 - def deselect_selected_item(self):
940 self.Select(idx = self.GetFirstSelected(), on = 0)
941 #------------------------------------------------------------
942 - def remove_item(self, item_idx=None):
943 self.DeleteItem(item_idx) 944 if self.__data is not None: 945 del self.__data[item_idx] 946 self.__tt_last_item = None
947 #------------------------------------------------------------ 948 # event handlers 949 #------------------------------------------------------------
950 - def _on_list_item_activated(self, event):
951 event.Skip() 952 if self.__activate_callback is not None: 953 self.__activate_callback(event)
954 #------------------------------------------------------------
955 - def _on_mouse_motion(self, event):
956 """Update tooltip on mouse motion. 957 958 for s in dir(wx): 959 if s.startswith('LIST_HITTEST'): 960 print s, getattr(wx, s) 961 962 LIST_HITTEST_ABOVE 1 963 LIST_HITTEST_BELOW 2 964 LIST_HITTEST_NOWHERE 4 965 LIST_HITTEST_ONITEM 672 966 LIST_HITTEST_ONITEMICON 32 967 LIST_HITTEST_ONITEMLABEL 128 968 LIST_HITTEST_ONITEMRIGHT 256 969 LIST_HITTEST_ONITEMSTATEICON 512 970 LIST_HITTEST_TOLEFT 1024 971 LIST_HITTEST_TORIGHT 2048 972 """ 973 item_idx, where_flag = self.HitTest(wx.Point(event.X, event.Y)) 974 975 # pointer on item related area at all ? 976 if where_flag not in [ 977 wx.LIST_HITTEST_ONITEMLABEL, 978 wx.LIST_HITTEST_ONITEMICON, 979 wx.LIST_HITTEST_ONITEMSTATEICON, 980 wx.LIST_HITTEST_ONITEMRIGHT, 981 wx.LIST_HITTEST_ONITEM 982 ]: 983 self.__tt_last_item = None # not on any item 984 self.SetToolTipString(self.__tt_static_part) 985 return 986 987 # same item as last time around ? 988 if self.__tt_last_item == item_idx: 989 return 990 991 # remeber the new item we are on 992 self.__tt_last_item = item_idx 993 994 # HitTest() can return -1 if it so pleases, meaning that no item 995 # was hit or else that maybe there aren't any items (empty list) 996 if item_idx == wx.NOT_FOUND: 997 self.SetToolTipString(self.__tt_static_part) 998 return 999 1000 # do we *have* item data ? 1001 if self.__data is None: 1002 self.SetToolTipString(self.__tt_static_part) 1003 return 1004 1005 # under some circumstances the item_idx returned 1006 # by HitTest() may be out of bounds with respect to 1007 # self.__data, this hints at a sync problem between 1008 # setting display items and associated data 1009 if ( 1010 (item_idx > (len(self.__data) - 1)) 1011 or 1012 (item_idx < -1) 1013 ): 1014 self.SetToolTipString(self.__tt_static_part) 1015 print "*************************************************************" 1016 print "GNUmed has detected an inconsistency with list item tooltips." 1017 print "" 1018 print "This is not a big problem and you can keep working." 1019 print "" 1020 print "However, please send us the following so we can fix GNUmed:" 1021 print "" 1022 print "item idx: %s" % item_idx 1023 print 'where flag: %s' % where_flag 1024 print 'data list length: %s' % len(self.__data) 1025 print "*************************************************************" 1026 return 1027 1028 dyna_tt = None 1029 if self.__item_tooltip_callback is not None: 1030 dyna_tt = self.__item_tooltip_callback(self.__data[item_idx]) 1031 1032 if dyna_tt is None: 1033 self.SetToolTipString(self.__tt_static_part) 1034 return 1035 1036 self.SetToolTipString(dyna_tt)
1037 #------------------------------------------------------------ 1038 # properties 1039 #------------------------------------------------------------
1040 - def _get_activate_callback(self):
1041 return self.__activate_callback
1042
1043 - def _set_activate_callback(self, callback):
1044 if callback is None: 1045 self.Unbind(wx.EVT_LIST_ITEM_ACTIVATED) 1046 else: 1047 if not callable(callback): 1048 raise ValueError('<activate> callback is not a callable: %s' % callback) 1049 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_list_item_activated) 1050 self.__activate_callback = callback
1051 1052 activate_callback = property(_get_activate_callback, _set_activate_callback) 1053 #------------------------------------------------------------
1054 - def _set_item_tooltip_callback(self, callback):
1055 if callback is not None: 1056 if not callable(callback): 1057 raise ValueError('<item_tooltip> callback is not a callable: %s' % callback) 1058 self.__item_tooltip_callback = callback
1059 1060 item_tooltip_callback = property(lambda x:x, _set_item_tooltip_callback)
1061 #================================================================ 1062 # main 1063 #---------------------------------------------------------------- 1064 if __name__ == '__main__': 1065 1066 if len(sys.argv) < 2: 1067 sys.exit() 1068 1069 if sys.argv[1] != 'test': 1070 sys.exit() 1071 1072 from Gnumed.pycommon import gmI18N 1073 gmI18N.activate_locale() 1074 gmI18N.install_domain() 1075 1076 #------------------------------------------------------------
1077 - def test_wxMultiChoiceDialog():
1078 app = wx.PyWidgetTester(size = (400, 500)) 1079 dlg = wx.MultiChoiceDialog ( 1080 parent = None, 1081 message = 'test message', 1082 caption = 'test caption', 1083 choices = ['a', 'b', 'c', 'd', 'e'] 1084 ) 1085 dlg.ShowModal() 1086 sels = dlg.GetSelections() 1087 print "selected:" 1088 for sel in sels: 1089 print sel
1090 #------------------------------------------------------------
1091 - def test_get_choices_from_list():
1092 1093 def edit(argument): 1094 print "editor called with:" 1095 print argument
1096 1097 def refresh(lctrl): 1098 choices = ['a', 'b', 'c'] 1099 lctrl.set_string_items(choices) 1100 1101 app = wx.PyWidgetTester(size = (200, 50)) 1102 chosen = get_choices_from_list ( 1103 # msg = 'select a health issue\nfrom the list below\n', 1104 caption = 'select health issues', 1105 #choices = [['D.M.II', '4'], ['MS', '3'], ['Fraktur', '2']], 1106 #columns = ['issue', 'no of episodes'] 1107 columns = ['issue'], 1108 refresh_callback = refresh 1109 #, edit_callback = edit 1110 ) 1111 print "chosen:" 1112 print chosen 1113 #------------------------------------------------------------
1114 - def test_item_picker_dlg():
1115 app = wx.PyWidgetTester(size = (200, 50)) 1116 dlg = cItemPickerDlg(None, -1, msg = 'Pick a few items:') 1117 dlg.set_columns(['Plugins'], ['Load in workplace', 'dummy']) 1118 #dlg.set_columns(['Plugins'], []) 1119 dlg.set_string_items(['patient', 'emr', 'docs']) 1120 result = dlg.ShowModal() 1121 print result 1122 print dlg.get_picks()
1123 #------------------------------------------------------------ 1124 #test_get_choices_from_list() 1125 #test_wxMultiChoiceDialog() 1126 test_item_picker_dlg() 1127 1128 #================================================================ 1129 # 1130