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

Source Code for Module Gnumed.wxpython.gmListWidgets

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