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