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.allow_duplicate_picks = True 746 747 self._LCTRL_left.activate_callback = self.__pick_selected 748 self.__extra_button_callback = None 749 750 self._LCTRL_left.SetFocus()
751 #------------------------------------------------------------ 752 # external API 753 #------------------------------------------------------------
754 - def set_columns(self, columns=None, columns_right=None):
755 self._LCTRL_left.set_columns(columns = columns) 756 if columns_right is None: 757 self._LCTRL_right.set_columns(columns = columns) 758 else: 759 if len(columns_right) < len(columns): 760 cols = columns 761 else: 762 cols = columns_right[:len(columns)] 763 self._LCTRL_right.set_columns(columns = cols)
764 #------------------------------------------------------------
765 - def set_string_items(self, items = None):
766 self._LCTRL_left.set_string_items(items = items) 767 self._LCTRL_left.set_column_widths() 768 self._LCTRL_right.set_string_items() 769 770 self._BTN_left2right.Enable(False) 771 self._BTN_right2left.Enable(False)
772 #------------------------------------------------------------
773 - def set_selections(self, selections = None):
774 self._LCTRL_left.set_selections(selections = selections)
775 #------------------------------------------------------------
776 - def set_choices(self, choices=None, data=None):
777 self.set_string_items(items = choices) 778 if data is not None: 779 self.set_data(data = data)
780 #------------------------------------------------------------
781 - def set_picks(self, picks=None, data=None):
782 self._LCTRL_right.set_string_items(picks) 783 self._LCTRL_right.set_column_widths() 784 if data is not None: 785 self._LCTRL_right.set_data(data = data)
786 #------------------------------------------------------------
787 - def set_data(self, data = None):
788 self._LCTRL_left.set_data(data = data)
789 #------------------------------------------------------------
790 - def get_picks(self):
791 return self._LCTRL_right.get_item_data()
792 793 picks = property(get_picks, lambda x:x) 794 #------------------------------------------------------------
795 - def _set_extra_button(self, definition):
796 if definition is None: 797 self._BTN_extra.Enable(False) 798 self._BTN_extra.Hide() 799 self.__extra_button_callback = None 800 return 801 802 (label, tooltip, callback) = definition 803 if not callable(callback): 804 raise ValueError('<extra button> callback is not a callable: %s' % callback) 805 self.__extra_button_callback = callback 806 self._BTN_extra.SetLabel(label) 807 self._BTN_extra.SetToolTipString(tooltip) 808 self._BTN_extra.Enable(True) 809 self._BTN_extra.Show()
810 811 extra_button = property(lambda x:x, _set_extra_button) 812 #------------------------------------------------------------ 813 # internal helpers 814 #------------------------------------------------------------
815 - def __pick_selected(self, event=None):
816 if self._LCTRL_left.get_selected_items(only_one = True) == -1: 817 return 818 819 right_items = self._LCTRL_right.get_string_items() 820 right_data = self._LCTRL_right.get_item_data() 821 if right_data is None: 822 right_data = [] 823 824 selected_items = self._LCTRL_left.get_selected_string_items(only_one = False) 825 selected_data = self._LCTRL_left.get_selected_item_data(only_one = False) 826 827 if self.allow_duplicate_picks: 828 right_items.extend(selected_items) 829 right_data.extend(selected_data) 830 self._LCTRL_right.set_string_items(items = right_items) 831 self._LCTRL_right.set_data(data = right_data) 832 self._LCTRL_right.set_column_widths() 833 # print u'%s <-> %s (items)' % (self._LCTRL_left.ItemCount, self._LCTRL_right.ItemCount) 834 # print u'%s <-> %s (data)' % (len(self._LCTRL_left.data), len(self._LCTRL_right.data)) 835 return 836 837 for sel_item, sel_data in zip(selected_items, selected_data): 838 if sel_item in right_items: 839 continue 840 right_items.append(sel_item) 841 right_data.append(sel_data) 842 self._LCTRL_right.set_string_items(items = right_items) 843 self._LCTRL_right.set_data(data = right_data) 844 self._LCTRL_right.set_column_widths()
845 # print u'%s <-> %s (items)' % (self._LCTRL_left.ItemCount, self._LCTRL_right.ItemCount) 846 # print u'%s <-> %s (data)' % (len(self._LCTRL_left.data), len(self._LCTRL_right.data)) 847 #------------------------------------------------------------
848 - def __remove_selected_picks(self):
849 if self._LCTRL_right.get_selected_items(only_one = True) == -1: 850 return 851 852 for item_idx in self._LCTRL_right.get_selected_items(only_one = False): 853 self._LCTRL_right.remove_item(item_idx) 854 855 if self._LCTRL_right.GetItemCount() == 0: 856 self._BTN_right2left.Enable(False)
857 858 # print u'%s <-> %s (items)' % (self._LCTRL_left.ItemCount, self._LCTRL_right.ItemCount) 859 # print u'%s <-> %s (data)' % (len(self._LCTRL_left.data), len(self._LCTRL_right.data)) 860 #------------------------------------------------------------ 861 # event handlers 862 #------------------------------------------------------------
863 - def _on_left_list_item_selected(self, event):
864 self._BTN_left2right.Enable(True)
865 #------------------------------------------------------------
866 - def _on_left_list_item_deselected(self, event):
867 if self._LCTRL_left.get_selected_items(only_one = True) == -1: 868 self._BTN_left2right.Enable(False)
869 #------------------------------------------------------------
870 - def _on_right_list_item_selected(self, event):
871 self._BTN_right2left.Enable(True)
872 #------------------------------------------------------------
873 - def _on_right_list_item_deselected(self, event):
874 if self._LCTRL_right.get_selected_items(only_one = True) == -1: 875 self._BTN_right2left.Enable(False)
876 #------------------------------------------------------------
877 - def _on_button_left2right_pressed(self, event):
878 self.__pick_selected()
879 #------------------------------------------------------------
880 - def _on_button_right2left_pressed(self, event):
881 self.__remove_selected_picks()
882 #------------------------------------------------------------
883 - def _on_extra_button_pressed(self, event):
884 self.__extra_button_callback()
885 #------------------------------------------------------------
886 - def _set_left_item_tooltip_callback(self, callback):
887 self._LCTRL_left.item_tooltip_callback = callback
888 889 left_item_tooltip_callback = property(lambda x:x, _set_left_item_tooltip_callback) 890 #------------------------------------------------------------
891 - def _set_right_item_tooltip_callback(self, callback):
892 self._LCTRL_right.item_tooltip_callback = callback
893 894 right_item_tooltip_callback = property(lambda x:x, _set_right_item_tooltip_callback)
895 896 #================================================================
897 -class cReportListCtrl(wx.ListCtrl, listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorterMixin):
898 899 # sorting: at set_string_items() time all items will be 900 # adorned with their initial row number as wxPython data, 901 # this is used later for a) sorting and b) to access 902 # GNUmed data objects associated with rows, 903 # the latter are ordered in initial row number order 904 # at set_data() time 905 906 map_item_idx2data_idx = wx.ListCtrl.GetItemData 907 908 sort_order_tags = { 909 True: u' [\u03b1\u0391 \u2192 \u03c9\u03A9]', 910 False: u' [\u03c9\u03A9 \u2192 \u03b1\u0391]' 911 } 912
913 - def __init__(self, *args, **kwargs):
914 915 self.debug = None 916 917 try: 918 kwargs['style'] = kwargs['style'] | wx.LC_REPORT 919 except KeyError: 920 kwargs['style'] = wx.LC_REPORT 921 922 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL) 923 924 wx.ListCtrl.__init__(self, *args, **kwargs) 925 listmixins.ListCtrlAutoWidthMixin.__init__(self) 926 927 # required for column sorting, MUST have this name 928 self._invalidate_sorting_metadata() # must be called after each (external/direct) list item update 929 listmixins.ColumnSorterMixin.__init__(self, 0) # must be called again after adding columns (why ?) 930 # for debugging sorting: 931 #self.Bind(wx.EVT_LIST_COL_CLICK, self._on_col_click, self) 932 933 self.__widths = None 934 self.__data = None 935 self.__activate_callback = None 936 self.__rightclick_callback = None 937 938 self.__item_tooltip_callback = None 939 self.__tt_last_item = None 940 self.__tt_static_part = _("""Select the items you want to work on. 941 942 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.""") 943 self.Bind(wx.EVT_MOTION, self._on_mouse_motion) 944 945 self.__next_line_to_search = 0 946 self.__search_data = None 947 self.__search_dlg = None 948 self.__searchable_cols = None 949 # self.Bind(wx.EVT_KILL_FOCUS, self._on_lost_focus) 950 self.Bind(wx.EVT_CHAR, self._on_char) 951 self.Bind(wx.EVT_FIND_CLOSE, self._on_search_dlg_closed) 952 self.Bind(wx.EVT_FIND, self._on_search_first_match) 953 self.Bind(wx.EVT_FIND_NEXT, self._on_search_next_match)
954 #------------------------------------------------------------ 955 # setters 956 #------------------------------------------------------------
957 - def set_columns(self, columns=None):
958 """(Re)define the columns. 959 960 Note that this will (have to) delete the items. 961 """ 962 self.ClearAll() 963 self.__tt_last_item = None 964 if columns is None: 965 return 966 for idx in range(len(columns)): 967 self.InsertColumn(idx, columns[idx]) 968 969 self._invalidate_sorting_metadata()
970 #------------------------------------------------------------
971 - def set_column_widths(self, widths=None):
972 """Set the column width policy. 973 974 widths = None: 975 use previous policy if any or default policy 976 widths != None: 977 use this policy and remember it for later calls 978 979 This means there is no way to *revert* to the default policy :-( 980 """ 981 # explicit policy ? 982 if widths is not None: 983 self.__widths = widths 984 for idx in range(len(self.__widths)): 985 self.SetColumnWidth(col = idx, width = self.__widths[idx]) 986 return 987 988 # previous policy ? 989 if self.__widths is not None: 990 for idx in range(len(self.__widths)): 991 self.SetColumnWidth(col = idx, width = self.__widths[idx]) 992 return 993 994 # default policy ! 995 if self.GetItemCount() == 0: 996 width_type = wx.LIST_AUTOSIZE_USEHEADER 997 else: 998 width_type = wx.LIST_AUTOSIZE 999 for idx in range(self.GetColumnCount()): 1000 self.SetColumnWidth(col = idx, width = width_type)
1001 #------------------------------------------------------------
1002 - def set_string_items(self, items=None):
1003 """All item members must be unicode()able or None.""" 1004 1005 wx.BeginBusyCursor() 1006 self._invalidate_sorting_metadata() 1007 1008 # remove existing items 1009 loop = 0 1010 while True: 1011 if loop > 3: 1012 _log.debug('unable to delete list items after looping 3 times, continuing and hoping for the best') 1013 break 1014 loop += 1 1015 if self.debug is not None: 1016 _log.debug('[round %s] GetItemCount() before DeleteAllItems(): %s (%s, thread [%s])', loop, self.GetItemCount(), self.debug, thread.get_ident()) 1017 if not self.DeleteAllItems(): 1018 _log.debug('DeleteAllItems() failed (%s)', self.debug) 1019 item_count = self.GetItemCount() 1020 if self.debug is not None: 1021 _log.debug('GetItemCount() after DeleteAllItems(): %s (%s)', item_count, self.debug) 1022 if item_count == 0: 1023 break 1024 wx.SafeYield(None, True) 1025 _log.debug('GetItemCount() not 0 after DeleteAllItems() (%s)', self.debug) 1026 time.sleep(0.3) 1027 wx.SafeYield(None, True) 1028 1029 if items is None: 1030 self.data = None 1031 wx.EndBusyCursor() 1032 return 1033 1034 # insert new items 1035 for item in items: 1036 try: 1037 item[0] 1038 if not isinstance(item, basestring): 1039 is_numerically_iterable = True 1040 # do not iterate over individual chars in a string, however 1041 else: 1042 is_numerically_iterable = False 1043 except TypeError: 1044 is_numerically_iterable = False 1045 1046 if is_numerically_iterable: 1047 # cannot use errors='replace' since then 1048 # None/ints/unicode strings fail to get encoded 1049 col_val = unicode(item[0]) 1050 row_num = self.InsertStringItem(index = sys.maxint, label = col_val) 1051 for col_num in range(1, min(self.GetColumnCount(), len(item))): 1052 col_val = unicode(item[col_num]) 1053 self.SetStringItem(index = row_num, col = col_num, label = col_val) 1054 else: 1055 # cannot use errors='replace' since then None/ints/unicode strings fails to get encoded 1056 col_val = unicode(item) 1057 row_num = self.InsertStringItem(index = sys.maxint, label = col_val) 1058 1059 # set data to be a copy of items 1060 self.data = items 1061 1062 wx.EndBusyCursor()
1063 #------------------------------------------------------------
1064 - def set_data(self, data=None):
1065 """<data> assumed to be a list corresponding to the item indices""" 1066 if data is not None: 1067 item_count = self.GetItemCount() 1068 if len(data) != item_count: 1069 _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()) 1070 for item_idx in range(len(data)): 1071 self.SetItemData(item_idx, item_idx) 1072 self.__data = data 1073 self.__tt_last_item = None 1074 # string data (rows/visible list items) not modified, 1075 # so no need to call _update_sorting_metadata 1076 return
1077
1078 - def _get_data(self):
1079 # slower than "return self.__data" but helps with detecting 1080 # problems with len(__data)<>self.GetItemCount() 1081 return self.get_item_data() # returns all data if item_idx is None
1082 1083 data = property(_get_data, set_data) 1084 #------------------------------------------------------------
1085 - def set_selections(self, selections=None):
1086 self.Select(0, on = 0) 1087 if selections is None: 1088 return 1089 for idx in selections: 1090 self.Select(idx = idx, on = 1)
1091
1092 - def __get_selections(self):
1093 if self.__is_single_selection: 1094 return [self.GetFirstSelected()] 1095 selections = [] 1096 idx = self.GetFirstSelected() 1097 while idx != -1: 1098 selections.append(idx) 1099 idx = self.GetNextSelected(idx) 1100 return selections
1101 1102 selections = property(__get_selections, set_selections) 1103 #------------------------------------------------------------ 1104 # getters 1105 #------------------------------------------------------------
1106 - def get_column_labels(self):
1107 labels = [] 1108 for col_idx in self.GetColumnCount(): 1109 col = self.GetColumn(col = col_idx) 1110 labels.append(col.GetText()) 1111 return labels
1112 #------------------------------------------------------------
1113 - def get_item(self, item_idx=None):
1114 if item_idx is not None: 1115 return self.GetItem(item_idx)
1116 #------------------------------------------------------------
1117 - def get_items(self):
1118 return [ self.GetItem(item_idx) for item_idx in range(self.GetItemCount()) ]
1119 #------------------------------------------------------------
1120 - def get_string_items(self):
1121 return [ self.GetItemText(item_idx) for item_idx in range(self.GetItemCount()) ]
1122 #------------------------------------------------------------
1123 - def get_selected_items(self, only_one=False):
1124 1125 if self.__is_single_selection or only_one: 1126 return self.GetFirstSelected() 1127 1128 items = [] 1129 idx = self.GetFirstSelected() 1130 while idx != -1: 1131 items.append(idx) 1132 idx = self.GetNextSelected(idx) 1133 1134 return items
1135 #------------------------------------------------------------
1136 - def get_selected_string_items(self, only_one=False):
1137 1138 if self.__is_single_selection or only_one: 1139 return self.GetItemText(self.GetFirstSelected()) 1140 1141 items = [] 1142 idx = self.GetFirstSelected() 1143 while idx != -1: 1144 items.append(self.GetItemText(idx)) 1145 idx = self.GetNextSelected(idx) 1146 1147 return items
1148 #------------------------------------------------------------
1149 - def get_item_data(self, item_idx = None):
1150 if self.__data is None: # this isn't entirely clean 1151 return None 1152 1153 if item_idx is not None: 1154 return self.__data[self.map_item_idx2data_idx(item_idx)] 1155 1156 # if <idx> is None return all data up to item_count, 1157 # in case of len(__data) <> self.GetItemCount() this 1158 # gives the chance to figure out what is going on 1159 return [ self.__data[self.map_item_idx2data_idx(item_idx)] for item_idx in range(self.GetItemCount()) ]
1160 #------------------------------------------------------------
1161 - def get_selected_item_data(self, only_one=False):
1162 1163 if self.__is_single_selection or only_one: 1164 if self.__data is None: 1165 return None 1166 idx = self.GetFirstSelected() 1167 if idx == -1: 1168 return None 1169 return self.__data[self.map_item_idx2data_idx(idx)] 1170 1171 data = [] 1172 if self.__data is None: 1173 return data 1174 idx = self.GetFirstSelected() 1175 while idx != -1: 1176 data.append(self.__data[self.map_item_idx2data_idx(idx)]) 1177 idx = self.GetNextSelected(idx) 1178 1179 return data
1180 #------------------------------------------------------------
1181 - def deselect_selected_item(self):
1182 self.Select(idx = self.GetFirstSelected(), on = 0)
1183 #------------------------------------------------------------
1184 - def remove_item(self, item_idx=None):
1185 # do NOT remove the corresponding data because even if 1186 # the item pointing to this data instance is gone all 1187 # other items will still point to their corresponding 1188 # *initial* row numbers 1189 #if self.__data is not None: 1190 # del self.__data[self.map_item_idx2data_idx(item_idx)] 1191 self.DeleteItem(item_idx) 1192 self.__tt_last_item = None 1193 self._invalidate_sorting_metadata()
1194 #------------------------------------------------------------ 1195 # event handlers 1196 #------------------------------------------------------------
1197 - def _on_list_item_activated(self, event):
1198 event.Skip() 1199 if self.__activate_callback is not None: 1200 self.__activate_callback(event)
1201 #------------------------------------------------------------
1202 - def _on_list_item_rightclicked(self, event):
1203 event.Skip() 1204 if self.__rightclick_callback is not None: 1205 self.__rightclick_callback(event)
1206 #------------------------------------------------------------
1207 - def _on_char(self, evt):
1208 1209 if evt.GetModifiers() != wx.MOD_CMD: 1210 evt.Skip() 1211 return 1212 1213 if unichr(evt.GetRawKeyCode()) != u'f': 1214 evt.Skip() 1215 return 1216 1217 if self.__search_dlg is not None: 1218 self.__search_dlg.Close() 1219 return 1220 1221 if len(self.__searchable_cols) == 0: 1222 return 1223 1224 if self.__search_data is None: 1225 self.__search_data = wx.FindReplaceData() 1226 self.__search_dlg = wx.FindReplaceDialog ( 1227 self, 1228 self.__search_data, 1229 _('Search in list'), 1230 wx.FR_NOUPDOWN | wx.FR_NOMATCHCASE | wx.FR_NOWHOLEWORD 1231 ) 1232 self.__search_dlg.Show(True)
1233 #------------------------------------------------------------
1234 - def _on_mouse_motion(self, event):
1235 """Update tooltip on mouse motion. 1236 1237 for s in dir(wx): 1238 if s.startswith('LIST_HITTEST'): 1239 print s, getattr(wx, s) 1240 1241 LIST_HITTEST_ABOVE 1 1242 LIST_HITTEST_BELOW 2 1243 LIST_HITTEST_NOWHERE 4 1244 LIST_HITTEST_ONITEM 672 1245 LIST_HITTEST_ONITEMICON 32 1246 LIST_HITTEST_ONITEMLABEL 128 1247 LIST_HITTEST_ONITEMRIGHT 256 1248 LIST_HITTEST_ONITEMSTATEICON 512 1249 LIST_HITTEST_TOLEFT 1024 1250 LIST_HITTEST_TORIGHT 2048 1251 """ 1252 item_idx, where_flag = self.HitTest(wx.Point(event.X, event.Y)) 1253 1254 # pointer on item related area at all ? 1255 if where_flag not in [ 1256 wx.LIST_HITTEST_ONITEMLABEL, 1257 wx.LIST_HITTEST_ONITEMICON, 1258 wx.LIST_HITTEST_ONITEMSTATEICON, 1259 wx.LIST_HITTEST_ONITEMRIGHT, 1260 wx.LIST_HITTEST_ONITEM 1261 ]: 1262 self.__tt_last_item = None # not on any item 1263 self.SetToolTipString(self.__tt_static_part) 1264 return 1265 1266 # same item as last time around ? 1267 if self.__tt_last_item == item_idx: 1268 return 1269 1270 # remeber the new item we are on 1271 self.__tt_last_item = item_idx 1272 1273 # HitTest() can return -1 if it so pleases, meaning that no item 1274 # was hit or else that maybe there aren't any items (empty list) 1275 if item_idx == wx.NOT_FOUND: 1276 self.SetToolTipString(self.__tt_static_part) 1277 return 1278 1279 # do we *have* item data ? 1280 if self.__data is None: 1281 self.SetToolTipString(self.__tt_static_part) 1282 return 1283 1284 # under some circumstances the item_idx returned 1285 # by HitTest() may be out of bounds with respect to 1286 # self.__data, this hints at a sync problem between 1287 # setting display items and associated data 1288 if ( 1289 (item_idx > (len(self.__data) - 1)) 1290 or 1291 (item_idx < -1) 1292 ): 1293 self.SetToolTipString(self.__tt_static_part) 1294 print "*************************************************************" 1295 print "GNUmed has detected an inconsistency with list item tooltips." 1296 print "" 1297 print "This is not a big problem and you can keep working." 1298 print "" 1299 print "However, please send us the following so we can fix GNUmed:" 1300 print "" 1301 print "item idx: %s" % item_idx 1302 print 'where flag: %s' % where_flag 1303 print 'data list length: %s' % len(self.__data) 1304 print "*************************************************************" 1305 return 1306 1307 dyna_tt = None 1308 if self.__item_tooltip_callback is not None: 1309 dyna_tt = self.__item_tooltip_callback(self.__data[self.map_item_idx2data_idx(item_idx)]) 1310 1311 if dyna_tt is None: 1312 self.SetToolTipString(self.__tt_static_part) 1313 return 1314 1315 self.SetToolTipString(dyna_tt)
1316 #------------------------------------------------------------ 1317 # search related methods 1318 #------------------------------------------------------------
1319 - def _on_search_dlg_closed(self, evt):
1320 self.__search_dlg.Destroy() 1321 self.__search_dlg = None
1322 #------------------------------------------------------------
1323 - def _on_lost_focus(self, evt):
1324 evt.Skip() 1325 if self.__search_dlg is None: 1326 return
1327 # print self.FindFocus() 1328 # print self.__search_dlg 1329 #self.__search_dlg.Close() 1330 #------------------------------------------------------------
1331 - def __on_search_match(self, search_term):
1332 for row_idx in range(self.__next_line_to_search, self.ItemCount): 1333 for col_idx in range(self.ColumnCount): 1334 if col_idx not in self.__searchable_cols: 1335 continue 1336 col_val = self.GetItem(row_idx, col_idx).GetText() 1337 if regex.search(search_term, col_val, regex.U | regex.I) is not None: 1338 self.Select(row_idx) 1339 self.EnsureVisible(row_idx) 1340 if row_idx == self.ItemCount - 1: 1341 # wrap around 1342 self.__next_line_to_search = 0 1343 else: 1344 self.__next_line_to_search = row_idx + 1 1345 return True 1346 # wrap around 1347 self.__next_line_to_search = 0 1348 return False
1349 #------------------------------------------------------------
1350 - def _on_search_first_match(self, evt):
1351 self.__on_search_match(evt.GetFindString())
1352 #------------------------------------------------------------
1353 - def _on_search_next_match(self, evt):
1354 self.__on_search_match(evt.GetFindString())
1355 #------------------------------------------------------------ 1356 # properties 1357 #------------------------------------------------------------
1358 - def _get_activate_callback(self):
1359 return self.__activate_callback
1360
1361 - def _set_activate_callback(self, callback):
1362 if callback is None: 1363 self.Unbind(wx.EVT_LIST_ITEM_ACTIVATED) 1364 else: 1365 if not callable(callback): 1366 raise ValueError('<activate> callback is not a callable: %s' % callback) 1367 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_list_item_activated) 1368 self.__activate_callback = callback
1369 1370 activate_callback = property(_get_activate_callback, _set_activate_callback) 1371 #------------------------------------------------------------
1372 - def _get_rightclick_callback(self):
1373 return self.__rightclick_callback
1374
1375 - def _set_rightclick_callback(self, callback):
1376 if callback is None: 1377 self.Unbind(wx.EVT_LIST_ITEM_RIGHT_CLICK) 1378 else: 1379 if not callable(callback): 1380 raise ValueError('<rightclick> callback is not a callable: %s' % callback) 1381 self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self._on_list_item_rightclicked) 1382 self.__rightclick_callback = callback
1383 1384 rightclick_callback = property(_get_rightclick_callback, _set_rightclick_callback) 1385 #------------------------------------------------------------
1386 - def _set_item_tooltip_callback(self, callback):
1387 if callback is not None: 1388 if not callable(callback): 1389 raise ValueError('<item_tooltip> callback is not a callable: %s' % callback) 1390 self.__item_tooltip_callback = callback
1391 1392 # the callback must be a function which takes a single argument 1393 # the argument is the data for the item the tooltip is on 1394 # the callback must return None if no item tooltip is to be shown 1395 # otherwise it must return a string (possibly with \n) 1396 item_tooltip_callback = property(lambda x:x, _set_item_tooltip_callback) 1397 #------------------------------------------------------------
1398 - def _set_searchable_cols(self, cols):
1399 # zero-based list of which columns to search 1400 if cols is None: 1401 self.__searchable_cols = range(self.ColumnCount) 1402 return 1403 # weed out columns to be searched which 1404 # don't exist an uniquify them 1405 new_cols = {} 1406 for col in cols: 1407 if col < self.ColumnCount: 1408 new_cols[col] = True 1409 self.__searchable_cols = new_cols.keys()
1410 1411 searchable_columns = property(lambda x:x, _set_searchable_cols) 1412 #------------------------------------------------------------ 1413 # ColumnSorterMixin API 1414 #------------------------------------------------------------
1415 - def GetListCtrl(self):
1416 if self.itemDataMap is None: 1417 self._update_sorting_metadata() 1418 return self # required
1419 #------------------------------------------------------------
1420 - def OnSortOrderChanged(self):
1421 self._cleanup_column_headers() 1422 # annotate sort column 1423 col_idx, is_ascending = self.GetSortState() 1424 col_state = self.GetColumn(col_idx) 1425 col_state.m_text += self.sort_order_tags[is_ascending] 1426 self.SetColumn(col_idx, col_state)
1427 #------------------------------------------------------------
1428 - def _generate_map_for_sorting(self):
1429 dict2sort = {} 1430 item_count = self.GetItemCount() 1431 if item_count == 0: 1432 return dict2sort 1433 col_count = self.GetColumnCount() 1434 for item_idx in range(item_count): 1435 dict2sort[item_idx] = () 1436 if col_count == 0: 1437 continue 1438 for col_idx in range(col_count): 1439 dict2sort[item_idx] += (self.GetItem(item_idx, col_idx).GetText(), ) 1440 1441 return dict2sort
1442 #------------------------------------------------------------
1443 - def _cleanup_column_headers(self):
1444 for col_idx in range(self.ColumnCount): 1445 col_state = self.GetColumn(col_idx) 1446 if col_state.m_text.endswith(self.sort_order_tags[True]): 1447 col_state.m_text = col_state.m_text[:-len(self.sort_order_tags[True])] 1448 if col_state.m_text.endswith(self.sort_order_tags[False]): 1449 col_state.m_text = col_state.m_text[:-len(self.sort_order_tags[False])] 1450 self.SetColumn(col_idx, col_state)
1451 #------------------------------------------------------------
1453 self.itemDataMap = None 1454 self.SetColumnCount(self.GetColumnCount()) 1455 self._cleanup_column_headers()
1456 #------------------------------------------------------------
1457 - def _update_sorting_metadata(self):
1458 self.itemDataMap = self._generate_map_for_sorting()
1459 #------------------------------------------------------------
1460 - def _on_col_click(self, event):
1461 # for debugging: 1462 # print "column clicked : %s" % (event.GetColumn()) 1463 # column, order = self.GetSortState() 1464 # print "column %s sort %s" % (column, order) 1465 # print self._colSortFlag 1466 # print self.itemDataMap 1467 event.Skip()
1468 1469 #================================================================ 1470 # main 1471 #---------------------------------------------------------------- 1472 if __name__ == '__main__': 1473 1474 if len(sys.argv) < 2: 1475 sys.exit() 1476 1477 if sys.argv[1] != 'test': 1478 sys.exit() 1479 1480 sys.path.insert(0, '../../') 1481 1482 from Gnumed.pycommon import gmI18N 1483 gmI18N.activate_locale() 1484 gmI18N.install_domain() 1485 1486 #------------------------------------------------------------
1487 - def test_wxMultiChoiceDialog():
1488 app = wx.PyWidgetTester(size = (400, 500)) 1489 dlg = wx.MultiChoiceDialog ( 1490 parent = None, 1491 message = 'test message', 1492 caption = 'test caption', 1493 choices = ['a', 'b', 'c', 'd', 'e'] 1494 ) 1495 dlg.ShowModal() 1496 sels = dlg.GetSelections() 1497 print "selected:" 1498 for sel in sels: 1499 print sel
1500 #------------------------------------------------------------
1501 - def test_get_choices_from_list():
1502 1503 def edit(argument): 1504 print "editor called with:" 1505 print argument
1506 1507 def refresh(lctrl): 1508 choices = ['a', 'b', 'c'] 1509 lctrl.set_string_items(choices) 1510 1511 app = wx.PyWidgetTester(size = (200, 50)) 1512 chosen = get_choices_from_list ( 1513 # msg = 'select a health issue\nfrom the list below\n', 1514 caption = 'select health issues', 1515 #choices = [['D.M.II', '4'], ['MS', '3'], ['Fraktur', '2']], 1516 #columns = ['issue', 'no of episodes'] 1517 columns = ['issue'], 1518 refresh_callback = refresh 1519 #, edit_callback = edit 1520 ) 1521 print "chosen:" 1522 print chosen 1523 #------------------------------------------------------------
1524 - def test_item_picker_dlg():
1525 app = wx.PyWidgetTester(size = (200, 50)) 1526 dlg = cItemPickerDlg(None, -1, msg = 'Pick a few items:') 1527 dlg.set_columns(['Plugins'], ['Load in workplace', 'dummy']) 1528 #dlg.set_columns(['Plugins'], []) 1529 dlg.set_string_items(['patient', 'emr', 'docs']) 1530 result = dlg.ShowModal() 1531 print result 1532 print dlg.get_picks()
1533 #------------------------------------------------------------ 1534 #test_get_choices_from_list() 1535 #test_wxMultiChoiceDialog() 1536 test_item_picker_dlg() 1537 1538 #================================================================ 1539 # 1540