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