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

Source Code for Module Gnumed.wxpython.gmListWidgets

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