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

Source Code for Module Gnumed.wxpython.gmListWidgets

   1  __doc__ = """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 threading 
  25  import time 
  26  import locale 
  27  import os 
  28  import io 
  29  import csv 
  30  import re as regex 
  31  import datetime as pydt 
  32   
  33   
  34  import wx 
  35  import wx.lib.mixins.listctrl as listmixins 
  36   
  37   
  38  if __name__ == '__main__': 
  39          sys.path.insert(0, '../../') 
  40  from Gnumed.pycommon import gmTools 
  41  from Gnumed.pycommon import gmDispatcher 
  42   
  43   
  44  _log = logging.getLogger('gm.list_ui') 
  45  #================================================================ 
  46  # FIXME: configurable callback on double-click action 
  47   
48 -def get_choices_from_list ( 49 parent=None, 50 msg=None, 51 caption=None, 52 columns=None, 53 choices=None, 54 data=None, 55 selections=None, 56 edit_callback=None, 57 new_callback=None, 58 delete_callback=None, 59 refresh_callback=None, 60 single_selection=False, 61 can_return_empty=False, 62 ignore_OK_button=False, 63 left_extra_button=None, 64 middle_extra_button=None, 65 right_extra_button=None, 66 list_tooltip_callback=None):
67 """Let user select item(s) from a list. 68 69 - new_callback: () 70 - edit_callback: (item data) 71 - delete_callback: (item data) 72 - refresh_callback: (listctrl) 73 - list_tooltip_callback: (item data) 74 75 - left/middle/right_extra_button: (label, tooltip, <callback> [, wants_list_ctrl]) 76 <wants_list_ctrl> is optional 77 <callback> is called with item_data (or listctrl) as the only argument 78 if <callback> returns TRUE, the listctrl will be refreshed, if a refresh_callback is available 79 80 returns: 81 on [CANCEL]: None 82 on [OK]: 83 if any items selected: 84 if single_selection: 85 the data of the selected item 86 else: 87 list of data of selected items 88 else: 89 if can_return_empty is True AND [OK] button was pressed: 90 empty list 91 else: 92 None 93 """ 94 if caption is None: 95 caption = u'GMd: ' + _('generic multi choice dialog') 96 else: 97 if not caption.startswith('GMd: '): 98 caption = 'GMd: %s' % caption 99 100 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg, single_selection = single_selection) 101 dlg.refresh_callback = refresh_callback 102 dlg.edit_callback = edit_callback 103 dlg.new_callback = new_callback 104 dlg.delete_callback = delete_callback 105 dlg.list_tooltip_callback = list_tooltip_callback 106 107 dlg.can_return_empty = can_return_empty 108 dlg.ignore_OK_button = ignore_OK_button 109 dlg.left_extra_button = left_extra_button 110 dlg.middle_extra_button = middle_extra_button 111 dlg.right_extra_button = right_extra_button 112 113 dlg.set_columns(columns = columns) 114 115 if refresh_callback is None: 116 dlg.set_string_items(items = choices) # list ctrl will refresh anyway if possible 117 dlg.set_column_widths() 118 119 if data is not None: 120 dlg.set_data(data = data) # can override data set if refresh_callback is not None 121 122 if selections is not None: 123 if single_selection: 124 dlg.set_selections(selections = selections[:1]) 125 else: 126 dlg.set_selections(selections = selections) 127 128 btn_pressed = dlg.ShowModal() 129 sels = dlg.get_selected_item_data(only_one = single_selection) 130 dlg.Destroy() 131 132 if btn_pressed == wx.ID_OK: 133 if can_return_empty and (sels is None): 134 return [] 135 return sels 136 137 return None
138 139 #---------------------------------------------------------------- 140 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg 141
142 -class cGenericListSelectorDlg(wxgGenericListSelectorDlg.wxgGenericListSelectorDlg):
143 """A dialog holding a list and a few buttons to act on the items.""" 144
145 - def __init__(self, *args, **kwargs):
146 147 try: 148 msg = kwargs['msg'] 149 del kwargs['msg'] 150 except KeyError: 151 msg = None 152 153 try: 154 title = kwargs['title'] 155 if not title.startswith('GMd: '): 156 kwargs['title'] = 'GMd: %s' % title 157 except KeyError: 158 kwargs['title'] = 'GMd: %s' % self.__class__.__name__ 159 160 try: 161 single_selection = kwargs['single_selection'] 162 del kwargs['single_selection'] 163 except KeyError: 164 single_selection = False 165 166 wxgGenericListSelectorDlg.wxgGenericListSelectorDlg.__init__(self, *args, **kwargs) 167 168 self.message = msg 169 170 self.left_extra_button = None 171 self.middle_extra_button = None 172 self.right_extra_button = None 173 174 self.refresh_callback = None # called when new/edit/delete callbacks return True (IOW were not cancelled) 175 self.new_callback = None # called when NEW button pressed, no argument passed in 176 self.edit_callback = None # called when EDIT button pressed, data of topmost selected item passed in 177 self.delete_callback = None # called when DELETE button pressed, data of topmost selected item passed in 178 179 self.can_return_empty = False 180 self.ignore_OK_button = False # by default do show/use the OK button 181 182 self.select_callback = None # called when an item is selected, data of topmost selected item passed in 183 self._LCTRL_items.select_callback = self._on_list_item_selected_in_listctrl 184 if single_selection: 185 self._LCTRL_items.SetSingleStyle(wx.LC_SINGLE_SEL, add = True)
186 187 #------------------------------------------------------------
188 - def set_columns(self, columns=None):
189 self._LCTRL_items.set_columns(columns = columns)
190 191 #------------------------------------------------------------
192 - def set_column_widths(self, widths=None):
193 self._LCTRL_items.set_column_widths(widths = widths)
194 195 #------------------------------------------------------------
196 - def set_string_items(self, items=None, reshow=True):
197 self._LCTRL_items.set_string_items(items = items, reshow = reshow) 198 self._LCTRL_items.set_column_widths()
199 #self._LCTRL_items.Select(0) 200 201 #------------------------------------------------------------
202 - def set_selections(self, selections = None):
203 self._LCTRL_items.set_selections(selections = selections) 204 if selections is None: 205 return 206 if len(selections) == 0: 207 return 208 if self.ignore_OK_button: 209 return 210 self._BTN_ok.Enable(True) 211 self._BTN_ok.SetDefault()
212 213 #------------------------------------------------------------
214 - def set_data(self, data = None):
215 self._LCTRL_items.set_data(data = data)
216 217 #------------------------------------------------------------
218 - def get_selected_item_data(self, only_one=False):
219 return self._LCTRL_items.get_selected_item_data(only_one=only_one)
220 221 #------------------------------------------------------------ 222 # event handlers 223 #------------------------------------------------------------
224 - def _on_list_item_deselected(self, event):
225 if self._LCTRL_items.get_selected_items(only_one=True) == -1: 226 if not self.can_return_empty: 227 self._BTN_cancel.SetDefault() 228 self._BTN_ok.Enable(False) 229 self._BTN_edit.Enable(False) 230 self._BTN_delete.Enable(False) 231 232 event.Skip()
233 234 #------------------------------------------------------------
235 - def _on_new_button_pressed(self, event):
236 self.__do_insert() 237 event.Skip()
238 239 #------------------------------------------------------------
240 - def _on_edit_button_pressed(self, event):
241 # if the edit button *can* be pressed there are *supposed* 242 # to be both an item selected and an editor configured 243 self.__do_edit() 244 event.Skip()
245 246 #------------------------------------------------------------
247 - def _on_delete_button_pressed(self, event):
248 # if the delete button *can* be pressed there are *supposed* 249 # to be both an item selected and a deletor configured 250 251 no_items = len(self._LCTRL_items.get_selected_items(only_one = False)) 252 if no_items == 0: 253 return 254 255 if no_items > 1: 256 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP 257 title = _('Deleting list items') 258 question = _( 259 'You have selected %s list items.\n' 260 '\n' 261 'Really delete all %s items ?' 262 ) % (no_items, no_items) 263 dlg = wx.MessageDialog(None, question, title, style) 264 btn_pressed = dlg.ShowModal() 265 dlg.Destroy() 266 if btn_pressed == wx.ID_NO: 267 self._LCTRL_items.SetFocus() 268 return 269 if btn_pressed == wx.ID_CANCEL: 270 self._LCTRL_items.SetFocus() 271 return 272 273 self.__do_delete() 274 event.Skip()
275 276 #------------------------------------------------------------
277 - def _on_left_extra_button_pressed(self, event):
278 if self.__left_extra_button_wants_list: 279 item_data = self._LCTRL_items 280 else: 281 item_data = self._LCTRL_items.get_selected_item_data(only_one=True) 282 if not self.__left_extra_button_callback(item_data): 283 self._LCTRL_items.SetFocus() 284 return 285 if self.__refresh_callback is None: 286 self._LCTRL_items.SetFocus() 287 return 288 wx.BeginBusyCursor() 289 try: 290 self.__refresh_callback(lctrl = self._LCTRL_items) 291 finally: 292 wx.EndBusyCursor() 293 self._LCTRL_items.set_column_widths() 294 self._LCTRL_items.SetFocus()
295 296 #------------------------------------------------------------
297 - def _on_middle_extra_button_pressed(self, event):
298 if self.__middle_extra_button_wants_list: 299 item_data = self._LCTRL_items 300 else: 301 item_data = self._LCTRL_items.get_selected_item_data(only_one=True) 302 if not self.__middle_extra_button_callback(item_data): 303 self._LCTRL_items.SetFocus() 304 return 305 if self.__refresh_callback is None: 306 self._LCTRL_items.SetFocus() 307 return 308 wx.BeginBusyCursor() 309 try: 310 self.__refresh_callback(lctrl = self._LCTRL_items) 311 finally: 312 wx.EndBusyCursor() 313 self._LCTRL_items.set_column_widths() 314 self._LCTRL_items.SetFocus()
315 316 #------------------------------------------------------------
317 - def _on_right_extra_button_pressed(self, event):
318 if self.__right_extra_button_wants_list: 319 item_data = self._LCTRL_items 320 else: 321 item_data = self._LCTRL_items.get_selected_item_data(only_one=True) 322 if not self.__right_extra_button_callback(item_data): 323 self._LCTRL_items.SetFocus() 324 return 325 if self.__refresh_callback is None: 326 self._LCTRL_items.SetFocus() 327 return 328 wx.BeginBusyCursor() 329 try: 330 self.__refresh_callback(lctrl = self._LCTRL_items) 331 finally: 332 wx.EndBusyCursor() 333 self._LCTRL_items.set_column_widths() 334 self._LCTRL_items.SetFocus()
335 336 #------------------------------------------------------------ 337 # internal helpers 338 #------------------------------------------------------------
339 - def _on_list_item_selected_in_listctrl(self, event):
340 event.Skip() 341 if not self.__ignore_OK_button: 342 self._BTN_ok.SetDefault() 343 self._BTN_ok.Enable(True) 344 if self.edit_callback is not None: 345 self._BTN_edit.Enable(True) 346 if self.delete_callback is not None: 347 self._BTN_delete.Enable(True) 348 if self.__select_callback is not None: 349 item = self._LCTRL_items.get_selected_item_data(only_one = True) 350 self.__select_callback(item)
351 352 #------------------------------------------------------------
354 self.__do_delete()
355 356 #------------------------------------------------------------
357 - def __do_delete(self):
358 any_deleted = False 359 for item_data in self._LCTRL_items.get_selected_item_data(only_one = False): 360 if item_data is None: 361 continue 362 if self.__delete_callback(item_data): 363 any_deleted = True 364 365 self._LCTRL_items.SetFocus() 366 367 if any_deleted is False: 368 return 369 370 if self.__refresh_callback is None: 371 return 372 373 wx.BeginBusyCursor() 374 try: 375 self.__refresh_callback(lctrl = self._LCTRL_items) 376 self._LCTRL_items.set_column_widths() 377 finally: 378 wx.EndBusyCursor()
379 380 #------------------------------------------------------------
382 self.__do_edit()
383 384 #------------------------------------------------------------
385 - def __do_edit(self):
386 if not self.__edit_callback(self._LCTRL_items.get_selected_item_data(only_one = True)): 387 self._LCTRL_items.SetFocus() 388 return 389 if self.__refresh_callback is None: 390 self._LCTRL_items.SetFocus() 391 return 392 wx.BeginBusyCursor() 393 try: 394 self.__refresh_callback(lctrl = self._LCTRL_items) 395 self._LCTRL_items.set_column_widths() 396 self._LCTRL_items.SetFocus() 397 finally: 398 wx.EndBusyCursor()
399 400 #------------------------------------------------------------
402 self.__do_insert()
403 404 #------------------------------------------------------------
405 - def __do_insert(self):
406 if not self.__new_callback(): 407 self._LCTRL_items.SetFocus() 408 return 409 if self.__refresh_callback is None: 410 self._LCTRL_items.SetFocus() 411 return 412 wx.BeginBusyCursor() 413 try: 414 self.__refresh_callback(lctrl = self._LCTRL_items) 415 self._LCTRL_items.set_column_widths() 416 self._LCTRL_items.SetFocus() 417 finally: 418 wx.EndBusyCursor()
419 420 #------------------------------------------------------------ 421 # properties 422 #------------------------------------------------------------
423 - def _set_ignore_OK_button(self, ignored):
424 self.__ignore_OK_button = ignored 425 if self.__ignore_OK_button: 426 self._BTN_ok.Hide() 427 self._BTN_ok.Enable(False) 428 else: 429 self._BTN_ok.Show() 430 if self._LCTRL_items.get_selected_items(only_one=True) == -1: 431 if self.can_return_empty: 432 self._BTN_ok.Enable(True) 433 else: 434 self._BTN_ok.Enable(False) 435 self._BTN_cancel.SetDefault()
436 437 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button) 438 439 #------------------------------------------------------------
440 - def _set_left_extra_button(self, definition):
441 if definition is None: 442 self._BTN_extra_left.Enable(False) 443 self._BTN_extra_left.Hide() 444 self.__left_extra_button_callback = None 445 self.__left_extra_button_wants_list = False 446 return 447 448 if len(definition) == 3: 449 (label, tooltip, callback) = definition 450 wants_list = False 451 else: 452 (label, tooltip, callback, wants_list) = definition 453 454 if not callable(callback): 455 raise ValueError('<left extra button> callback is not a callable: %s' % callback) 456 self.__left_extra_button_callback = callback 457 self.__left_extra_button_wants_list = wants_list 458 self._BTN_extra_left.SetLabel(label) 459 self._BTN_extra_left.SetToolTip(tooltip) 460 self._BTN_extra_left.Enable(True) 461 self._BTN_extra_left.Show()
462 463 left_extra_button = property(lambda x:x, _set_left_extra_button) 464 465 #------------------------------------------------------------
466 - def _set_middle_extra_button(self, definition):
467 if definition is None: 468 self._BTN_extra_middle.Enable(False) 469 self._BTN_extra_middle.Hide() 470 self.__middle_extra_button_callback = None 471 self.__middle_extra_button_wants_list = False 472 return 473 474 if len(definition) == 3: 475 (label, tooltip, callback) = definition 476 wants_list = False 477 else: 478 (label, tooltip, callback, wants_list) = definition 479 480 if not callable(callback): 481 raise ValueError('<middle extra button> callback is not a callable: %s' % callback) 482 self.__middle_extra_button_callback = callback 483 self.__middle_extra_button_wants_list = wants_list 484 self._BTN_extra_middle.SetLabel(label) 485 self._BTN_extra_middle.SetToolTip(tooltip) 486 self._BTN_extra_middle.Enable(True) 487 self._BTN_extra_middle.Show()
488 489 middle_extra_button = property(lambda x:x, _set_middle_extra_button) 490 491 #------------------------------------------------------------
492 - def _set_right_extra_button(self, definition):
493 if definition is None: 494 self._BTN_extra_right.Enable(False) 495 self._BTN_extra_right.Hide() 496 self.__right_extra_button_callback = None 497 self.__right_extra_button_wants_list = False 498 return 499 500 if len(definition) == 3: 501 (label, tooltip, callback) = definition 502 wants_list = False 503 else: 504 (label, tooltip, callback, wants_list) = definition 505 506 if not callable(callback): 507 raise ValueError('<right extra button> callback is not a callable: %s' % callback) 508 self.__right_extra_button_callback = callback 509 self.__right_extra_button_wants_list = wants_list 510 self._BTN_extra_right.SetLabel(label) 511 self._BTN_extra_right.SetToolTip(tooltip) 512 self._BTN_extra_right.Enable(True) 513 self._BTN_extra_right.Show()
514 515 right_extra_button = property(lambda x:x, _set_right_extra_button) 516 517 #------------------------------------------------------------
518 - def _get_new_callback(self):
519 return self.__new_callback
520
521 - def _set_new_callback(self, callback):
522 if callback is not None: 523 if self.__refresh_callback is None: 524 raise ValueError('refresh callback must be set before new callback can be set') 525 if not callable(callback): 526 raise ValueError('<new> callback is not a callable: %s' % callback) 527 self.__new_callback = callback 528 529 if callback is None: 530 self._BTN_new.Enable(False) 531 self._BTN_new.Hide() 532 self._LCTRL_items.new_callback = None 533 else: 534 self._BTN_new.Enable(True) 535 self._BTN_new.Show() 536 self._LCTRL_items.new_callback = self._on_insert_key_pressed_in_listctrl
537 538 new_callback = property(_get_new_callback, _set_new_callback) 539 540 #------------------------------------------------------------
541 - def _get_edit_callback(self):
542 return self.__edit_callback
543
544 - def _set_edit_callback(self, callback):
545 if callback is not None: 546 if not callable(callback): 547 raise ValueError('<edit> callback is not a callable: %s' % callback) 548 self.__edit_callback = callback 549 550 if callback is None: 551 self._BTN_edit.Enable(False) 552 self._BTN_edit.Hide() 553 self._LCTRL_items.edit_callback = None 554 else: 555 self._BTN_edit.Enable(True) 556 self._BTN_edit.Show() 557 self._LCTRL_items.edit_callback = self._on_edit_invoked_in_listctrl
558 559 edit_callback = property(_get_edit_callback, _set_edit_callback) 560 561 #------------------------------------------------------------
562 - def _get_delete_callback(self):
563 return self.__delete_callback
564
565 - def _set_delete_callback(self, callback):
566 if callback is not None: 567 if self.__refresh_callback is None: 568 raise ValueError('refresh callback must be set before delete callback can be set') 569 if not callable(callback): 570 raise ValueError('<delete> callback is not a callable: %s' % callback) 571 self.__delete_callback = callback 572 if callback is None: 573 self._BTN_delete.Enable(False) 574 self._BTN_delete.Hide() 575 self._LCTRL_items.delete_callback = None 576 else: 577 self._BTN_delete.Enable(True) 578 self._BTN_delete.Show() 579 self._LCTRL_items.delete_callback = self._on_delete_key_pressed_in_listctrl
580 581 delete_callback = property(_get_delete_callback, _set_delete_callback) 582 583 #------------------------------------------------------------
584 - def _get_refresh_callback(self):
585 return self.__refresh_callback
586
588 wx.BeginBusyCursor() 589 try: 590 self.__refresh_callback(lctrl = self._LCTRL_items) 591 finally: 592 wx.EndBusyCursor() 593 self._LCTRL_items.set_column_widths()
594
595 - def _set_refresh_callback(self, callback):
596 if callback is not None: 597 if not callable(callback): 598 raise ValueError('<refresh> callback is not a callable: %s' % callback) 599 self.__refresh_callback = callback 600 if callback is not None: 601 wx.CallAfter(self._set_refresh_callback_helper)
602 603 refresh_callback = property(_get_refresh_callback, _set_refresh_callback) 604 605 #------------------------------------------------------------
606 - def _get_select_callback(self):
607 return self.__select_callback
608
609 - def _set_select_callback(self, callback):
610 if callback is not None: 611 if not callable(callback): 612 raise ValueError('<select> callback is not a callable: %s' % callback) 613 self.__select_callback = callback
614 615 select_callback = property(_get_select_callback, _set_select_callback) 616 617 #------------------------------------------------------------
618 - def _set_list_tooltip_callback(self, callback):
619 self._LCTRL_items.item_tooltip_callback = callback
620 621 list_tooltip_callback = property(lambda x:x, _set_list_tooltip_callback) 622 #def _get_tooltip(self, item): # inside a class 623 #def _get_tooltip(item): # outside a class 624 #------------------------------------------------------------
625 - def _set_message(self, message):
626 if message is None: 627 self._LBL_message.Hide() 628 return 629 self._LBL_message.SetLabel(message) 630 self._LBL_message.Show()
631 632 message = property(lambda x:x, _set_message)
633 634 #================================================================ 635 from Gnumed.wxGladeWidgets import wxgGenericListManagerPnl 636
637 -class cGenericListManagerPnl(wxgGenericListManagerPnl.wxgGenericListManagerPnl):
638 """A panel holding a generic multi-column list and action buttions.""" 639
640 - def __init__(self, *args, **kwargs):
641 642 try: 643 msg = kwargs['msg'] 644 del kwargs['msg'] 645 except KeyError: msg = None 646 647 wxgGenericListManagerPnl.wxgGenericListManagerPnl.__init__(self, *args, **kwargs) 648 649 if msg is None: 650 self._LBL_message.Hide() 651 else: 652 self._LBL_message.SetLabel(msg) 653 654 self.left_extra_button = None 655 self.middle_extra_button = None 656 self.right_extra_button = None 657 658 # new/edit/delete must return True/False to enable refresh 659 self.refresh_callback = None # called when new/edit/delete callbacks return True (IOW were not cancelled) 660 self.new_callback = None # called when NEW button pressed, no argument passed in 661 self.edit_callback = None # called when EDIT button pressed, data of topmost selected item passed in 662 self.delete_callback = None # called when DELETE button pressed, data of topmost selected item passed in 663 664 self.select_callback = None # called when an item is selected, data of topmost selected item passed in 665 self._LCTRL_items.select_callback = self._on_list_item_selected_in_listctrl
666 667 #------------------------------------------------------------ 668 # external API 669 #------------------------------------------------------------
670 - def set_columns(self, columns=None):
671 self._LCTRL_items.set_columns(columns = columns)
672 673 #------------------------------------------------------------
674 - def set_string_items(self, items=None, reshow=True):
675 self._LCTRL_items.set_string_items(items = items, reshow = reshow) 676 self._LCTRL_items.set_column_widths() 677 678 if (items is None) or (len(items) == 0): 679 self._BTN_edit.Enable(False) 680 self._BTN_remove.Enable(False)
681 #else: 682 # self._LCTRL_items.Select(0) 683 684 #------------------------------------------------------------
685 - def set_selections(self, selections = None):
686 self._LCTRL_items.set_selections(selections = selections)
687 688 #------------------------------------------------------------
689 - def set_data(self, data = None):
690 self._LCTRL_items.set_data(data = data)
691 692 #------------------------------------------------------------
693 - def get_selected_item_data(self, only_one=False):
694 return self._LCTRL_items.get_selected_item_data(only_one=only_one)
695 696 #------------------------------------------------------------ 697 # internal helpers 698 #------------------------------------------------------------
699 - def _on_list_item_selected_in_listctrl(self, event):
700 event.Skip() 701 if self.__edit_callback is not None: 702 self._BTN_edit.Enable(True) 703 if self.__delete_callback is not None: 704 self._BTN_remove.Enable(True) 705 if self.__select_callback is not None: 706 item = self._LCTRL_items.get_selected_item_data(only_one = True) 707 self.__select_callback(item)
708 709 #------------------------------------------------------------
711 self.__do_delete()
712 713 #------------------------------------------------------------
714 - def __do_delete(self):
715 if not self.__delete_callback(self._LCTRL_items.get_selected_item_data(only_one = True)): 716 return 717 if self.__refresh_callback is None: 718 self._LCTRL_items.SetFocus() 719 return 720 wx.BeginBusyCursor() 721 try: 722 self.__refresh_callback(lctrl = self._LCTRL_items) 723 self._LCTRL_items.set_column_widths() 724 self._LCTRL_items.SetFocus() 725 finally: 726 wx.EndBusyCursor()
727 728 #------------------------------------------------------------
730 self.__do_edit()
731 732 #------------------------------------------------------------
733 - def __do_edit(self):
734 if not self.__edit_callback(self._LCTRL_items.get_selected_item_data(only_one = True)): 735 self._LCTRL_items.SetFocus() 736 return 737 if self.__refresh_callback is None: 738 self._LCTRL_items.SetFocus() 739 return 740 wx.BeginBusyCursor() 741 try: 742 self.__refresh_callback(lctrl = self._LCTRL_items) 743 self._LCTRL_items.set_column_widths() 744 self._LCTRL_items.SetFocus() 745 finally: 746 wx.EndBusyCursor()
747 748 #------------------------------------------------------------
750 self.__do_insert()
751 752 #------------------------------------------------------------
753 - def __do_insert(self):
754 if not self.__new_callback(): 755 self._LCTRL_items.SetFocus() 756 return 757 if self.__refresh_callback is None: 758 self._LCTRL_items.SetFocus() 759 return 760 wx.BeginBusyCursor() 761 try: 762 self.__refresh_callback(lctrl = self._LCTRL_items) 763 self._LCTRL_items.set_column_widths() 764 self._LCTRL_items.SetFocus() 765 finally: 766 wx.EndBusyCursor()
767 768 #------------------------------------------------------------ 769 # event handlers 770 #------------------------------------------------------------
771 - def _on_list_item_deselected(self, event):
772 event.Skip() 773 if self._LCTRL_items.get_selected_items(only_one = True) == -1: 774 self._BTN_edit.Enable(False) 775 self._BTN_remove.Enable(False) 776 if self.__select_callback is not None: 777 self.__select_callback(None)
778 779 #------------------------------------------------------------
780 - def _on_list_item_activated(self, event):
781 event.Skip() 782 if self.__edit_callback is None: 783 return 784 self._on_edit_button_pressed(event)
785 786 #------------------------------------------------------------
787 - def _on_add_button_pressed(self, event):
788 if not self.__new_callback(): 789 return 790 if self.__refresh_callback is None: 791 return 792 wx.BeginBusyCursor() 793 try: 794 self.__refresh_callback(lctrl = self._LCTRL_items) 795 finally: 796 wx.EndBusyCursor()
797 798 #------------------------------------------------------------
799 - def _on_edit_button_pressed(self, event):
800 item = self._LCTRL_items.get_selected_item_data(only_one = True) 801 if item is None: 802 return 803 if not self.__edit_callback(item): 804 return 805 if self.__refresh_callback is None: 806 return 807 wx.BeginBusyCursor() 808 try: 809 self.__refresh_callback(lctrl = self._LCTRL_items) 810 finally: 811 wx.EndBusyCursor()
812 813 #------------------------------------------------------------
814 - def _on_remove_button_pressed(self, event):
815 if self._LCTRL_items.get_selected_items(only_one = True) is None: 816 return 817 self.__do_delete()
818 819 #------------------------------------------------------------
820 - def _on_left_extra_button_pressed(self, event):
821 item_data = self._LCTRL_items.get_selected_item_data(only_one = True) 822 if not self.__left_extra_button_callback(item_data): 823 self._LCTRL_items.SetFocus() 824 return 825 if self.__refresh_callback is None: 826 self._LCTRL_items.SetFocus() 827 return 828 wx.BeginBusyCursor() 829 try: 830 self.__refresh_callback(lctrl = self._LCTRL_items) 831 finally: 832 self._LCTRL_items.set_column_widths() 833 self._LCTRL_items.SetFocus() 834 wx.EndBusyCursor()
835 836 #------------------------------------------------------------
837 - def _on_middle_extra_button_pressed(self, event):
838 item_data = self._LCTRL_items.get_selected_item_data(only_one = True) 839 if not self.__middle_extra_button_callback(item_data): 840 self._LCTRL_items.SetFocus() 841 return 842 if self.__refresh_callback is None: 843 self._LCTRL_items.SetFocus() 844 return 845 wx.BeginBusyCursor() 846 try: 847 self.__refresh_callback(lctrl = self._LCTRL_items) 848 finally: 849 self._LCTRL_items.set_column_widths() 850 self._LCTRL_items.SetFocus() 851 wx.EndBusyCursor()
852 853 #------------------------------------------------------------
854 - def _on_right_extra_button_pressed(self, event):
855 item_data = self._LCTRL_items.get_selected_item_data(only_one=True) 856 if not self.__right_extra_button_callback(item_data): 857 self._LCTRL_items.SetFocus() 858 return 859 if self.__refresh_callback is None: 860 self._LCTRL_items.SetFocus() 861 return 862 wx.BeginBusyCursor() 863 try: 864 self.__refresh_callback(lctrl = self._LCTRL_items) 865 finally: 866 wx.EndBusyCursor() 867 self._LCTRL_items.set_column_widths() 868 self._LCTRL_items.SetFocus()
869 870 #------------------------------------------------------------ 871 # properties 872 #------------------------------------------------------------
873 - def _get_new_callback(self):
874 return self.__new_callback
875
876 - def _set_new_callback(self, callback):
877 if callback is not None: 878 if self.__refresh_callback is None: 879 raise ValueError('refresh callback must be set before new callback can be set') 880 if not callable(callback): 881 raise ValueError('<new> callback is not a callable: %s' % callback) 882 self.__new_callback = callback 883 884 if callback is None: 885 self._BTN_add.Enable(False) 886 self._BTN_add.Hide() 887 self._LCTRL_items.new_callback = None 888 else: 889 self._BTN_add.Enable(True) 890 self._BTN_add.Show() 891 self._LCTRL_items.new_callback = self._on_insert_key_pressed_in_listctrl
892 893 new_callback = property(_get_new_callback, _set_new_callback) 894 895 #------------------------------------------------------------
896 - def _get_edit_callback(self):
897 return self.__edit_callback
898
899 - def _set_edit_callback(self, callback):
900 if callback is not None: 901 if not callable(callback): 902 raise ValueError('<edit> callback is not a callable: %s' % callback) 903 self.__edit_callback = callback 904 905 if callback is None: 906 self._BTN_edit.Enable(False) 907 self._BTN_edit.Hide() 908 self._LCTRL_items.edit_callback = None 909 else: 910 self._BTN_edit.Enable(True) 911 self._BTN_edit.Show() 912 self._LCTRL_items.edit_callback = self._on_edit_invoked_in_listctrl
913 914 edit_callback = property(_get_edit_callback, _set_edit_callback) 915 916 #------------------------------------------------------------
917 - def _get_delete_callback(self):
918 return self.__delete_callback
919
920 - def _set_delete_callback(self, callback):
921 if callback is not None: 922 if self.__refresh_callback is None: 923 raise ValueError('refresh callback must be set before delete callback can be set') 924 if not callable(callback): 925 raise ValueError('<delete> callback is not a callable: %s' % callback) 926 self.__delete_callback = callback 927 if callback is None: 928 self._BTN_remove.Enable(False) 929 self._BTN_remove.Hide() 930 self._LCTRL_items.delete_callback = None 931 else: 932 self._BTN_remove.Enable(True) 933 self._BTN_remove.Show() 934 self._LCTRL_items.delete_callback = self._on_delete_key_pressed_in_listctrl
935 936 delete_callback = property(_get_delete_callback, _set_delete_callback) 937 938 #------------------------------------------------------------
939 - def _get_refresh_callback(self):
940 return self.__refresh_callback
941
943 wx.BeginBusyCursor() 944 try: 945 self.__refresh_callback(lctrl = self._LCTRL_items) 946 finally: 947 wx.EndBusyCursor() 948 self._LCTRL_items.set_column_widths()
949
950 - def _set_refresh_callback(self, callback):
951 if callback is not None: 952 if not callable(callback): 953 raise ValueError('<refresh> callback is not a callable: %s' % callback) 954 self.__refresh_callback = callback 955 if callback is not None: 956 wx.CallAfter(self._set_refresh_callback_helper)
957 958 refresh_callback = property(_get_refresh_callback, _set_refresh_callback) 959 960 #------------------------------------------------------------
961 - def _get_select_callback(self):
962 return self.__select_callback
963
964 - def _set_select_callback(self, callback):
965 if callback is not None: 966 if not callable(callback): 967 raise ValueError('<select> callback is not a callable: %s' % callback) 968 self.__select_callback = callback
969 970 select_callback = property(_get_select_callback, _set_select_callback) 971 972 #------------------------------------------------------------
973 - def _get_message(self):
974 return self._LBL_message.GetLabel()
975
976 - def _set_message(self, msg):
977 if msg is None: 978 self._LBL_message.Hide() 979 self._LBL_message.SetLabel('') 980 else: 981 self._LBL_message.SetLabel(msg) 982 self._LBL_message.Show() 983 self.Layout()
984 985 message = property(_get_message, _set_message) 986 987 #------------------------------------------------------------
988 - def _set_left_extra_button(self, definition):
989 if definition is None: 990 self._BTN_extra_left.Enable(False) 991 self._BTN_extra_left.Hide() 992 self.__left_extra_button_callback = None 993 return 994 995 (label, tooltip, callback) = definition 996 if not callable(callback): 997 raise ValueError('<left extra button> callback is not a callable: %s' % callback) 998 self.__left_extra_button_callback = callback 999 self._BTN_extra_left.SetLabel(label) 1000 self._BTN_extra_left.SetToolTip(tooltip) 1001 self._BTN_extra_left.Enable(True) 1002 self._BTN_extra_left.Show()
1003 1004 left_extra_button = property(lambda x:x, _set_left_extra_button) 1005 1006 #------------------------------------------------------------
1007 - def _set_middle_extra_button(self, definition):
1008 if definition is None: 1009 self._BTN_extra_middle.Enable(False) 1010 self._BTN_extra_middle.Hide() 1011 self.__middle_extra_button_callback = None 1012 return 1013 1014 (label, tooltip, callback) = definition 1015 if not callable(callback): 1016 raise ValueError('<middle extra button> callback is not a callable: %s' % callback) 1017 self.__middle_extra_button_callback = callback 1018 self._BTN_extra_middle.SetLabel(label) 1019 self._BTN_extra_middle.SetToolTip(tooltip) 1020 self._BTN_extra_middle.Enable(True) 1021 self._BTN_extra_middle.Show()
1022 1023 middle_extra_button = property(lambda x:x, _set_middle_extra_button) 1024 1025 #------------------------------------------------------------
1026 - def _set_right_extra_button(self, definition):
1027 if definition is None: 1028 self._BTN_extra_right.Enable(False) 1029 self._BTN_extra_right.Hide() 1030 self.__right_extra_button_callback = None 1031 return 1032 1033 (label, tooltip, callback) = definition 1034 if not callable(callback): 1035 raise ValueError('<right extra button> callback is not a callable: %s' % callback) 1036 self.__right_extra_button_callback = callback 1037 self._BTN_extra_right.SetLabel(label) 1038 self._BTN_extra_right.SetToolTip(tooltip) 1039 self._BTN_extra_right.Enable(True) 1040 self._BTN_extra_right.Show()
1041 1042 right_extra_button = property(lambda x:x, _set_right_extra_button)
1043 1044 #================================================================ 1045 from Gnumed.wxGladeWidgets import wxgItemPickerDlg 1046
1047 -class cItemPickerDlg(wxgItemPickerDlg.wxgItemPickerDlg):
1048
1049 - def __init__(self, *args, **kwargs):
1050 1051 try: 1052 msg = kwargs['msg'] 1053 del kwargs['msg'] 1054 except KeyError: 1055 msg = None 1056 1057 try: 1058 title = kwargs['title'] 1059 if not title.startswith('GMd: '): 1060 kwargs['title'] = 'GMd: %s' % title 1061 except KeyError: 1062 kwargs['title'] = 'GMd: %s' % self.__class__.__name__ 1063 1064 wxgItemPickerDlg.wxgItemPickerDlg.__init__(self, *args, **kwargs) 1065 1066 if msg is None: 1067 self._LBL_msg.Hide() 1068 else: 1069 self._LBL_msg.SetLabel(msg) 1070 1071 self.ignore_dupes_on_picking = True 1072 1073 self._LCTRL_left.activate_callback = self.__pick_selected 1074 self.__extra_button_callback = None 1075 1076 self._LCTRL_left.SetFocus()
1077 1078 #------------------------------------------------------------ 1079 # external API 1080 #------------------------------------------------------------
1081 - def set_columns(self, columns=None, columns_right=None):
1082 self._LCTRL_left.set_columns(columns = columns) 1083 if columns_right is None: 1084 self._LCTRL_right.set_columns(columns = columns) 1085 return 1086 1087 if len(columns_right) < len(columns): 1088 cols = columns 1089 else: 1090 cols = columns_right[:len(columns)] 1091 self._LCTRL_right.set_columns(columns = cols)
1092 1093 #------------------------------------------------------------
1094 - def set_string_items(self, items=None, reshow=True):
1095 self._LCTRL_left.set_string_items(items = items, reshow = reshow) 1096 self._LCTRL_left.set_column_widths() 1097 self._LCTRL_right.set_string_items(reshow = False) 1098 1099 self._BTN_left2right.Enable(False) 1100 self._BTN_right2left.Enable(False)
1101 1102 #------------------------------------------------------------
1103 - def set_selections(self, selections = None):
1104 self._LCTRL_left.set_selections(selections = selections)
1105 1106 #------------------------------------------------------------
1107 - def set_choices(self, choices=None, data=None, reshow=True):
1108 self.set_string_items(items = choices, reshow = reshow) 1109 if data is not None: 1110 self.set_data(data = data)
1111 1112 #------------------------------------------------------------
1113 - def set_picks(self, picks=None, data=None, reshow=True):
1114 self._LCTRL_right.set_string_items(picks, reshow = reshow) 1115 self._LCTRL_right.set_column_widths() 1116 if data is not None: 1117 self._LCTRL_right.set_data(data = data)
1118 1119 #------------------------------------------------------------
1120 - def set_data(self, data = None):
1121 self._LCTRL_left.set_data(data = data)
1122 1123 #------------------------------------------------------------
1124 - def get_picks(self):
1125 return self._LCTRL_right.get_item_data()
1126 1127 picks = property(get_picks, lambda x:x) 1128 1129 #------------------------------------------------------------
1130 - def _set_extra_button(self, definition):
1131 if definition is None: 1132 self._BTN_extra.Enable(False) 1133 self._BTN_extra.Hide() 1134 self.__extra_button_callback = None 1135 return 1136 1137 (label, tooltip, callback) = definition 1138 if not callable(callback): 1139 raise ValueError('<extra button> callback is not a callable: %s' % callback) 1140 self.__extra_button_callback = callback 1141 self._BTN_extra.SetLabel(label) 1142 self._BTN_extra.SetToolTip(tooltip) 1143 self._BTN_extra.Enable(True) 1144 self._BTN_extra.Show()
1145 1146 extra_button = property(lambda x:x, _set_extra_button) 1147 1148 #------------------------------------------------------------ 1149 # internal helpers 1150 #------------------------------------------------------------
1151 - def __pick_selected(self, event=None):
1152 if self._LCTRL_left.get_selected_items(only_one = True) == -1: 1153 return 1154 1155 right_items = self._LCTRL_right.get_string_items() 1156 right_data = self._LCTRL_right.get_item_data() 1157 if right_data is None: 1158 right_data = [] 1159 1160 selected_items = self._LCTRL_left.get_selected_string_items(only_one = False) 1161 selected_data = self._LCTRL_left.get_selected_item_data(only_one = False) 1162 1163 if self.ignore_dupes_on_picking is False: 1164 right_items.extend(selected_items) 1165 right_data.extend(selected_data) 1166 self._LCTRL_right.set_string_items(items = right_items, reshow = True) 1167 self._LCTRL_right.set_data(data = right_data) 1168 self._LCTRL_right.set_column_widths() 1169 # print u'%s <-> %s (items)' % (self._LCTRL_left.ItemCount, self._LCTRL_right.ItemCount) 1170 # print u'%s <-> %s (data)' % (len(self._LCTRL_left.data), len(self._LCTRL_right.data)) 1171 return 1172 1173 for sel_item, sel_data in zip(selected_items, selected_data): 1174 if sel_item in right_items: 1175 continue 1176 right_items.append(sel_item) 1177 right_data.append(sel_data) 1178 self._LCTRL_right.set_string_items(items = right_items, reshow = True) 1179 self._LCTRL_right.set_data(data = right_data) 1180 self._LCTRL_right.set_column_widths()
1181 # print u'%s <-> %s (items)' % (self._LCTRL_left.ItemCount, self._LCTRL_right.ItemCount) 1182 # print u'%s <-> %s (data)' % (len(self._LCTRL_left.data), len(self._LCTRL_right.data)) 1183 1184 #------------------------------------------------------------
1185 - def __remove_selected_picks(self):
1186 if self._LCTRL_right.get_selected_items(only_one = True) == -1: 1187 return 1188 1189 for item_idx in self._LCTRL_right.get_selected_items(only_one = False): 1190 self._LCTRL_right.remove_item(item_idx) 1191 1192 if self._LCTRL_right.GetItemCount() == 0: 1193 self._BTN_right2left.Enable(False)
1194 1195 # print u'%s <-> %s (items)' % (self._LCTRL_left.ItemCount, self._LCTRL_right.ItemCount) 1196 # print u'%s <-> %s (data)' % (len(self._LCTRL_left.data), len(self._LCTRL_right.data)) 1197 1198 #------------------------------------------------------------ 1199 # event handlers 1200 #------------------------------------------------------------
1201 - def _on_left_list_item_selected(self, event):
1202 self._BTN_left2right.Enable(True)
1203 #------------------------------------------------------------
1204 - def _on_left_list_item_deselected(self, event):
1205 if self._LCTRL_left.get_selected_items(only_one = True) == -1: 1206 self._BTN_left2right.Enable(False)
1207 #------------------------------------------------------------
1208 - def _on_right_list_item_selected(self, event):
1209 self._BTN_right2left.Enable(True)
1210 #------------------------------------------------------------
1211 - def _on_right_list_item_deselected(self, event):
1212 if self._LCTRL_right.get_selected_items(only_one = True) == -1: 1213 self._BTN_right2left.Enable(False)
1214 #------------------------------------------------------------
1215 - def _on_button_left2right_pressed(self, event):
1216 self.__pick_selected()
1217 #------------------------------------------------------------
1218 - def _on_button_right2left_pressed(self, event):
1219 self.__remove_selected_picks()
1220 #------------------------------------------------------------
1221 - def _on_extra_button_pressed(self, event):
1222 self.__extra_button_callback()
1223 #------------------------------------------------------------
1224 - def _set_left_item_tooltip_callback(self, callback):
1225 self._LCTRL_left.item_tooltip_callback = callback
1226 1227 left_item_tooltip_callback = property(lambda x:x, _set_left_item_tooltip_callback) 1228 #------------------------------------------------------------
1229 - def _set_right_item_tooltip_callback(self, callback):
1230 self._LCTRL_right.item_tooltip_callback = callback
1231 1232 right_item_tooltip_callback = property(lambda x:x, _set_right_item_tooltip_callback)
1233 1234 #================================================================
1235 -class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorterMixin, wx.ListCtrl):
1236 1237 # sorting: at set_string_items() time all items will be 1238 # adorned with their initial row number as wxPython data, 1239 # this is used later for a) sorting and b) to access 1240 # GNUmed data objects associated with rows, 1241 # the latter are ordered in initial row number order 1242 # at set_data() time 1243 1244 sort_order_tags = { 1245 True: ' [\u03b1\u0391 \u2192 \u03c9\u03A9]', 1246 False: ' [\u03c9\u03A9 \u2192 \u03b1\u0391]' 1247 } 1248
1249 - def __init__(self, *args, **kwargs):
1250 1251 self.debug = None 1252 self.map_item_idx2data_idx = self.GetItemData 1253 1254 try: 1255 kwargs['style'] = kwargs['style'] | wx.LC_REPORT 1256 except KeyError: 1257 kwargs['style'] = wx.LC_REPORT 1258 1259 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL) 1260 1261 wx.ListCtrl.__init__(self, *args, **kwargs) 1262 listmixins.ListCtrlAutoWidthMixin.__init__(self) 1263 1264 # required for column sorting 1265 self._invalidate_sorting_metadata() # must be called after each (external/direct) list item update 1266 listmixins.ColumnSorterMixin.__init__(self, 0) # must be called again after adding columns (why ?) 1267 self.__secondary_sort_col = None 1268 # apparently, this MUST be bound under wxP3 - but why ?? 1269 self.Bind(wx.EVT_LIST_COL_CLICK, self._on_col_click, self) 1270 1271 # cols/rows 1272 self.__widths = None 1273 self.__data = None 1274 1275 # event callbacks 1276 self.__select_callback = None 1277 self.__deselect_callback = None 1278 self.__activate_callback = None 1279 self.__new_callback = None 1280 self.__edit_callback = None 1281 self.__delete_callback = None 1282 1283 # context menu 1284 self.__extend_popup_menu_callback = None 1285 self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self._on_list_item_rightclicked) # (also handled by MENU key on EVT_LIST_KEY_DOWN) 1286 1287 # row tooltips 1288 self.__item_tooltip_callback = None 1289 self.__tt_last_item = None 1290 # self.__tt_static_part_base = _( 1291 # u'Select the items you want to work on.\n' 1292 # u'\n' 1293 # u'A discontinuous selection may depend on your holding ' 1294 # u'down a platform-dependent modifier key (<CTRL>, <ALT>, ' 1295 # u'etc) or key combination (eg. <CTRL-SHIFT> or <CTRL-ALT>) ' 1296 # u'while clicking.' 1297 # ) 1298 self.__tt_static_part_base = '' 1299 self.__tt_static_part = self.__tt_static_part_base 1300 self.Bind(wx.EVT_MOTION, self._on_mouse_motion) 1301 1302 # search related: 1303 self.__search_term = None 1304 self.__next_line_to_search = 0 1305 self.__searchable_cols = None 1306 1307 # general event handling 1308 # self.Bind(wx.EVT_KILL_FOCUS, self._on_lost_focus) 1309 self.Bind(wx.EVT_CHAR, self._on_char) # CTRL-F / CTRL-N (LIST_KEY_DOWN does not support modifiers) 1310 self.Bind(wx.EVT_LIST_KEY_DOWN, self._on_list_key_down) # context menu key -> context menu / DEL / INS
1311 1312 #------------------------------------------------------------ 1313 # debug sizing 1314 #------------------------------------------------------------
1315 - def __log_sizing(self, caller_name, *args, **kwargs):
1316 if self.debug is None: 1317 return False 1318 if not self.debug.endswith('_sizing'): 1319 return False 1320 _log.debug('[%s.%s]: *args = (%s), **kwargs = (%s)', self.debug, caller_name, str(args), str(kwargs)) 1321 return True
1322 1323 #------------------------------------------------------------
1324 - def CacheBestSize(self, *args, **kwargs):
1325 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1326 return super(cReportListCtrl, self).CacheBestSize(*args, **kwargs)
1327 1328 #------------------------------------------------------------
1329 - def Fit(self, *args, **kwargs):
1330 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1331 return super(cReportListCtrl, self).Fit(*args, **kwargs)
1332 1333 #------------------------------------------------------------
1334 - def FitInside(self, *args, **kwargs):
1335 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1336 return super(cReportListCtrl, self).FitInside(*args, **kwargs)
1337 1338 #------------------------------------------------------------
1339 - def InvalidateBestSize(self, *args, **kwargs):
1340 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1341 return super(cReportListCtrl, self).InvalidateBestSize(*args, **kwargs)
1342 1343 #------------------------------------------------------------
1344 - def SetBestFittingSize(self, *args, **kwargs):
1345 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1346 return super(cReportListCtrl, self).SetBestFittingSize(*args, **kwargs)
1347 1348 #------------------------------------------------------------
1349 - def SetInitialSize(self, *args, **kwargs):
1350 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1351 return super(cReportListCtrl, self).SetInitialSize(*args, **kwargs)
1352 1353 #------------------------------------------------------------
1354 - def SetClientSize(self, *args, **kwargs):
1355 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1356 return super(cReportListCtrl, self).SetClientSize(*args, **kwargs)
1357 1358 #------------------------------------------------------------
1359 - def SetClientSizeWH(self, *args, **kwargs):
1360 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1361 return super(cReportListCtrl, self).SetClientSizeWH(*args, **kwargs)
1362 1363 #------------------------------------------------------------
1364 - def SetMaxClientSize(self, *args, **kwargs):
1365 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1366 return super(cReportListCtrl, self).SetMaxClientSize(*args, **kwargs)
1367 1368 #------------------------------------------------------------
1369 - def SetMaxSize(self, *args, **kwargs):
1370 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1371 return super(cReportListCtrl, self).SetMaxSize(*args, **kwargs)
1372 1373 #------------------------------------------------------------
1374 - def SetMinClientSize(self, *args, **kwargs):
1375 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1376 return super(cReportListCtrl, self).SetMinClientSize(*args, **kwargs)
1377 1378 #------------------------------------------------------------
1379 - def SetMinSize(self, *args, **kwargs):
1380 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1381 return super(cReportListCtrl, self).SetMinSize(*args, **kwargs)
1382 1383 #------------------------------------------------------------
1384 - def SetSize(self, *args, **kwargs):
1385 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1386 return super(cReportListCtrl, self).SetSize(*args, **kwargs)
1387 1388 #------------------------------------------------------------
1389 - def SetSizeHints(self, *args, **kwargs):
1390 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1391 return super(cReportListCtrl, self).SetSizeHints(*args, **kwargs)
1392 1393 #------------------------------------------------------------
1394 - def SetSizeHintsSz(self, *args, **kwargs):
1395 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1396 return super(cReportListCtrl, self).SetSizeHintsSz(*args, **kwargs)
1397 1398 #------------------------------------------------------------
1399 - def SetSizeWH(self, *args, **kwargs):
1400 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1401 return super(cReportListCtrl, self).SetSizeWH(*args, **kwargs)
1402 1403 #------------------------------------------------------------
1404 - def SetVirtualSize(self, *args, **kwargs):
1405 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1406 return super(cReportListCtrl, self).SetVirtualSize(*args, **kwargs)
1407 1408 #------------------------------------------------------------
1409 - def SetVirtualSizeHints(self, *args, **kwargs):
1410 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1411 return super(cReportListCtrl, self).SetVirtualSizeHints(self, *args, **kwargs)
1412 1413 #------------------------------------------------------------
1414 - def SetVirtualSizeHintsSz(self, *args, **kwargs):
1415 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1416 return super(cReportListCtrl, self).SetVirtualSizeHintsSz(*args, **kwargs)
1417 1418 #------------------------------------------------------------
1419 - def SetVirtualSizeWH(self, *args, **kwargs):
1420 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1421 return super(cReportListCtrl, self).SetVirtualSizeWH(*args, **kwargs)
1422 1423 #------------------------------------------------------------
1424 - def GetAdjustedBestSize(self, *args, **kwargs):
1425 res = super(cReportListCtrl, self).GetAdjustedBestSize(*args, **kwargs) 1426 kwargs['sizing_function_result'] = res 1427 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1428 return res
1429 1430 #------------------------------------------------------------
1431 - def GetEffectiveMinSize(self, *args, **kwargs):
1432 res = super(cReportListCtrl, self).GetEffectiveMinSize(*args, **kwargs) 1433 kwargs['sizing_function_result'] = res 1434 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1435 return res
1436 1437 #------------------------------------------------------------
1438 - def GetBestSize(self, *args, **kwargs):
1439 res = super(cReportListCtrl, self).GetBestSize(*args, **kwargs) 1440 kwargs['sizing_function_result'] = res 1441 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1442 return res
1443 1444 #------------------------------------------------------------
1445 - def GetBestSizeTuple(self, *args, **kwargs):
1446 res = super(cReportListCtrl, self).GetBestSizeTuple(*args, **kwargs) 1447 kwargs['sizing_function_result'] = res 1448 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1449 return res
1450 1451 #------------------------------------------------------------
1452 - def GetBestVirtualSize(self, *args, **kwargs):
1453 res = super(cReportListCtrl, self).GetBestVirtualSize(*args, **kwargs) 1454 kwargs['sizing_function_result'] = res 1455 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1456 return res
1457 1458 #------------------------------------------------------------
1459 - def GetClientSize(self, *args, **kwargs):
1460 res = super(cReportListCtrl, self).GetClientSize(*args, **kwargs) 1461 kwargs['sizing_function_result'] = res 1462 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1463 return res
1464 1465 #------------------------------------------------------------
1466 - def GetClientSize(self, *args, **kwargs):
1467 res = super(cReportListCtrl, self).GetClientSize(*args, **kwargs) 1468 kwargs['sizing_function_result'] = res 1469 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1470 return res
1471 1472 #------------------------------------------------------------
1473 - def GetMaxClientSize(self, *args, **kwargs):
1474 res = super(cReportListCtrl, self).GetMaxClientSize(*args, **kwargs) 1475 kwargs['sizing_function_result'] = res 1476 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1477 return res
1478 1479 #------------------------------------------------------------
1480 - def GetMaxHeight(self, *args, **kwargs):
1481 res = super(cReportListCtrl, self).GetMaxHeight(*args, **kwargs) 1482 kwargs['sizing_function_result'] = res 1483 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1484 return res
1485 1486 #------------------------------------------------------------
1487 - def GetMaxSize(self, *args, **kwargs):
1488 res = super(cReportListCtrl, self).GetMaxSize(*args, **kwargs) 1489 kwargs['sizing_function_result'] = res 1490 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1491 return res
1492 1493 #------------------------------------------------------------
1494 - def GetMaxWidth(self, *args, **kwargs):
1495 res = super(cReportListCtrl, self).GetMaxWidth(*args, **kwargs) 1496 kwargs['sizing_function_result'] = res 1497 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1498 return res
1499 1500 #------------------------------------------------------------
1501 - def GetMinClientSize(self, *args, **kwargs):
1502 res = super(cReportListCtrl, self).GetMinClientSize(*args, **kwargs) 1503 kwargs['sizing_function_result'] = res 1504 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1505 return res
1506 1507 #------------------------------------------------------------
1508 - def GetMinHeight(self, *args, **kwargs):
1509 res = super(cReportListCtrl, self).GetMinHeight(*args, **kwargs) 1510 kwargs['sizing_function_result'] = res 1511 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1512 return res
1513 1514 #------------------------------------------------------------
1515 - def GetMinSize(self, *args, **kwargs):
1516 res = super(cReportListCtrl, self).GetMinSize(*args, **kwargs) 1517 kwargs['sizing_function_result'] = res 1518 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1519 return res
1520 1521 #------------------------------------------------------------
1522 - def GetMinWidth(self, *args, **kwargs):
1523 res = super(cReportListCtrl, self).GetMinWidth(*args, **kwargs) 1524 kwargs['sizing_function_result'] = res 1525 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1526 return res
1527 1528 #------------------------------------------------------------
1529 - def GetSize(self, *args, **kwargs):
1530 res = super(cReportListCtrl, self).GetSize(*args, **kwargs) 1531 kwargs['sizing_function_result'] = res 1532 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1533 return res
1534 1535 #------------------------------------------------------------
1536 - def GetVirtualSize(self, *args, **kwargs):
1537 res = super(cReportListCtrl, self).GetVirtualSize(*args, **kwargs) 1538 kwargs['sizing_function_result'] = res 1539 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1540 return res
1541 1542 #------------------------------------------------------------
1543 - def GetVirtualSizeTuple(self, *args, **kwargs):
1544 res = super(cReportListCtrl, self).GetVirtualSizeTuple(*args, **kwargs) 1545 kwargs['sizing_function_result'] = res 1546 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs) 1547 return res
1548 1549 #------------------------------------------------------------ 1550 # setters 1551 #------------------------------------------------------------
1552 - def set_columns(self, columns=None):
1553 """(Re)define the columns. 1554 1555 Note that this will (have to) delete the items. 1556 """ 1557 self.ClearAll() 1558 self.__tt_last_item = None 1559 if columns is None: 1560 return 1561 for idx in range(len(columns)): 1562 self.InsertColumn(idx, columns[idx]) 1563 1564 listmixins.ColumnSorterMixin.__init__(self, 0) 1565 self._invalidate_sorting_metadata()
1566 1567 #------------------------------------------------------------
1568 - def set_column_widths(self, widths=None):
1569 """Set the column width policy. 1570 1571 widths = None: 1572 use previous policy if any or default policy 1573 widths != None: 1574 use this policy and remember it for later calls 1575 1576 options: 1577 wx.LIST_AUTOSIZE_USEHEADER 1578 wx.LIST_AUTOSIZE 1579 1580 This means there is no way to *revert* to the default policy :-( 1581 """ 1582 # explicit policy ? 1583 if widths is not None: 1584 self.__widths = widths 1585 for idx in range(len(self.__widths)): 1586 self.SetColumnWidth(idx, self.__widths[idx]) 1587 return 1588 1589 # previous policy ? 1590 if self.__widths is not None: 1591 for idx in range(len(self.__widths)): 1592 self.SetColumnWidth(idx, self.__widths[idx]) 1593 return 1594 1595 # default policy ! 1596 if self.GetItemCount() == 0: 1597 width_type = wx.LIST_AUTOSIZE_USEHEADER 1598 else: 1599 width_type = wx.LIST_AUTOSIZE 1600 for idx in range(self.GetColumnCount()): 1601 self.SetColumnWidth(idx, width_type)
1602 1603 #------------------------------------------------------------
1604 - def set_resize_column(self, column='LAST'):
1605 if column != 'LAST': 1606 if column > self.ColumnCount: 1607 return 1608 # this column will take up all remaining space courtesy of the width mixin 1609 self.setResizeColumn(column)
1610 1611 #------------------------------------------------------------
1612 - def remove_items_safely(self, max_tries=3):
1613 tries = 0 1614 while tries < max_tries: 1615 if self.debug is not None: 1616 if self.debug.endswith('_deleting'): 1617 _log.debug('[round %s] <%s>.GetItemCount() before DeleteAllItems(): %s (thread [%s])', tries, self.debug, self.GetItemCount(), threading.get_ident()) 1618 if not self.DeleteAllItems(): 1619 _log.error('<%s>.DeleteAllItems() failed', self.debug) 1620 item_count = self.GetItemCount() 1621 if item_count == 0: 1622 return True 1623 wx.SafeYield(None, True) 1624 _log.error('<%s>.GetItemCount() not 0 (rather: %s) after DeleteAllItems()', self.debug, item_count) 1625 time.sleep(0.3) 1626 wx.SafeYield(None, True) 1627 tries += 1 1628 1629 _log.error('<%s>: unable to delete list items after looping %s times', self.debug, max_tries) 1630 return False
1631 1632 #------------------------------------------------------------
1633 - def set_string_items(self, items=None, reshow=True, unwrap=True):
1634 """All item members must be str()able or None.""" 1635 1636 wx.BeginBusyCursor() 1637 self._invalidate_sorting_metadata() 1638 1639 if self.ItemCount == 0: 1640 topmost_visible = 0 1641 else: 1642 topmost_visible = self.GetFirstSelected() 1643 if topmost_visible == -1: 1644 topmost_visible = self.GetFocusedItem() 1645 if topmost_visible == -1: 1646 topmost_visible = self.TopItem 1647 1648 if not self.remove_items_safely(max_tries = 3): 1649 _log.error("cannot remove items (!?), continuing and hoping for the best") 1650 1651 if items is None: 1652 self.data = None 1653 wx.EndBusyCursor() 1654 return 1655 1656 # insert new items 1657 for item in items: 1658 # item is a single string 1659 # (typical special case: items=rows are a list-of-strings) 1660 if isinstance(item, str): 1661 self.InsertItem(index = sys.maxsize, label = item.replace('\r\n', ' [CRLF] ').replace('\n', ' [LF] ')) 1662 continue 1663 # item is something else, either ... 1664 try: 1665 # ... an iterable 1666 col_val = str(item[0]) 1667 row_num = self.InsertItem(index = sys.maxsize, label = col_val) 1668 for col_num in range(1, min(self.GetColumnCount(), len(item))): 1669 col_val = str(item[col_num]).replace('\r\n', ' [CRLF] ').replace('\n', ' [LF] ') 1670 self.SetItem(index = row_num, column = col_num, label = col_val) 1671 except (TypeError, KeyError, IndexError): 1672 # ... an *empty* iterable [IndexError] 1673 # ... or not iterable (None, int, instance, dict [KeyError] ...) 1674 col_val = str(item).replace('\r\n', ' [CRLF] ').replace('\n', ' [LF] ') 1675 self.InsertItem(index = sys.maxsize, label = col_val) 1676 1677 if reshow: 1678 if self.ItemCount > 0: 1679 if topmost_visible < self.ItemCount: 1680 self.EnsureVisible(topmost_visible) 1681 self.Focus(topmost_visible) 1682 else: 1683 self.EnsureVisible(self.ItemCount - 1) 1684 self.Focus(self.ItemCount - 1) 1685 1686 # set data to be a copy of items 1687 self.data = items 1688 1689 wx.EndBusyCursor()
1690 1691 #--------------------------
1692 - def get_string_items(self):
1693 if self.ItemCount == 0: 1694 return [] 1695 1696 rows = [] 1697 for row_idx in range(self.ItemCount): 1698 row = [] 1699 for col_idx in range(self.ColumnCount): 1700 row.append(self.GetItem(row_idx, col_idx).GetText()) 1701 rows.append(row) 1702 return rows
1703 1704 # old: only returned first column 1705 #return [ self.GetItemText(item_idx) for item_idx in range(self.GetItemCount()) ] 1706 1707 string_items = property(get_string_items, set_string_items) 1708 1709 #------------------------------------------------------------
1710 - def append_string_items_and_data(self, new_items, new_data=None, allow_dupes=False):
1711 if len(new_items) == 0: 1712 return 1713 1714 if new_data is None: 1715 new_data = new_items 1716 1717 existing_data = self.get_item_data() 1718 if existing_data is None: 1719 existing_data = [] 1720 1721 if allow_dupes: 1722 self._LCTRL_right.set_string_items ( 1723 items = self.string_items.extend(new_items), 1724 reshow = True 1725 ) 1726 self.data = existing_data.extend(new_data) 1727 self.set_column_widths() 1728 return 1729 1730 existing_items = self.get_string_items() 1731 for new_item, new_data in zip(new_items, new_data): 1732 if new_item in existing_items: 1733 continue 1734 existing_items.append(new_item) 1735 existing_data.append(new_data) 1736 self.set_string_items ( 1737 items = existing_items, 1738 reshow = True 1739 ) 1740 self.data = existing_data 1741 self.set_column_widths()
1742 1743 #------------------------------------------------------------
1744 - def set_data(self, data=None):
1745 """<data> assumed to be a list corresponding to the item indices""" 1746 if data is not None: 1747 item_count = self.GetItemCount() 1748 if len(data) != item_count: 1749 _log.debug('<data> length (%s) must be equal to number of list items (%s) (%s, thread [%s])', len(data), item_count, self.debug, threading.get_ident()) 1750 for item_idx in range(len(data)): 1751 self.SetItemData(item_idx, item_idx) 1752 self.__data = data 1753 self.__tt_last_item = None 1754 # string data (rows/visible list items) not modified, 1755 # so no need to call _update_sorting_metadata 1756 return
1757
1758 - def _get_data(self):
1759 # slower than "return self.__data" but helps with detecting 1760 # problems with len(__data) != self.GetItemCount(), 1761 # also takes care of returning data in the order corresponding 1762 # to the order get_string_items returns rows 1763 return self.get_item_data() # returns all data since item_idx is None
1764 1765 data = property(_get_data, set_data) 1766 1767 #------------------------------------------------------------
1768 - def set_selections(self, selections=None):
1769 # not sure why this is done: 1770 if self.GetItemCount() > 0: 1771 self.Select(0, on = 0) 1772 if selections is None: 1773 return 1774 for idx in selections: 1775 self.Select(idx = idx, on = 1)
1776
1777 - def __get_selections(self):
1778 if self.ItemCount == 0: 1779 return [] 1780 if self.__is_single_selection: 1781 return [self.GetFirstSelected()] 1782 selections = [] 1783 idx = self.GetFirstSelected() 1784 while idx != -1: 1785 selections.append(idx) 1786 idx = self.GetNextSelected(idx) 1787 return selections
1788 1789 selections = property(__get_selections, set_selections) 1790 1791 #------------------------------------------------------------ 1792 # getters 1793 #------------------------------------------------------------
1794 - def get_column_labels(self):
1795 labels = [] 1796 for col_idx in range(self.ColumnCount): 1797 col = self.GetColumn(col = col_idx) 1798 labels.append(col.Text) 1799 return labels
1800 1801 column_labels = property(get_column_labels, lambda x:x) 1802 1803 #------------------------------------------------------------
1804 - def get_item(self, item_idx=None):
1805 if self.ItemCount == 0: 1806 _log.warning('no items') 1807 return None 1808 if item_idx is not None: 1809 return self.GetItem(item_idx) 1810 _log.error('get_item(None) called') 1811 return None
1812 1813 #------------------------------------------------------------
1814 - def get_items(self):
1815 if self.ItemCount == 0: 1816 return [] 1817 return [ self.GetItem(item_idx) for item_idx in range(self.ItemCount) ]
1818 1819 items = property(get_items, lambda x:x) 1820 1821 #------------------------------------------------------------
1822 - def get_selected_items(self, only_one=False):
1823 1824 if self.ItemCount == 0: 1825 if self.__is_single_selection or only_one: 1826 return None 1827 return [] 1828 1829 if self.__is_single_selection or only_one: 1830 return self.GetFirstSelected() 1831 1832 items = [] 1833 idx = self.GetFirstSelected() 1834 while idx != -1: 1835 items.append(idx) 1836 idx = self.GetNextSelected(idx) 1837 1838 return items
1839 1840 selected_items = property(get_selected_items, lambda x:x) 1841 1842 #------------------------------------------------------------
1843 - def get_selected_string_items(self, only_one=False):
1844 1845 if self.ItemCount == 0: 1846 if self.__is_single_selection or only_one: 1847 return None 1848 return [] 1849 1850 if self.__is_single_selection or only_one: 1851 return self.GetItemText(self.GetFirstSelected()) 1852 1853 items = [] 1854 idx = self.GetFirstSelected() 1855 while idx != -1: 1856 items.append(self.GetItemText(idx)) 1857 idx = self.GetNextSelected(idx) 1858 1859 return items
1860 1861 selected_string_items = property(get_selected_string_items, lambda x:x) 1862 1863 #------------------------------------------------------------
1864 - def get_item_data(self, item_idx=None):
1865 1866 if self.__data is None: # this isn't entirely clean 1867 return None 1868 1869 if item_idx is not None: 1870 return self.__data[self.map_item_idx2data_idx(item_idx)] 1871 1872 # if <idx> is None return all data up to item_count, 1873 # in case of len(__data) != self.GetItemCount() this 1874 # gives the chance to figure out what is going on 1875 return [ self.__data[self.map_item_idx2data_idx(item_idx)] for item_idx in range(self.GetItemCount()) ]
1876 1877 item_data = property(get_item_data, lambda x:x) 1878 1879 #------------------------------------------------------------
1880 - def get_selected_item_data(self, only_one=False):
1881 1882 if self.__is_single_selection or only_one: 1883 if self.__data is None: 1884 return None 1885 idx = self.GetFirstSelected() 1886 if idx == -1: 1887 return None 1888 return self.__data[self.map_item_idx2data_idx(idx)] 1889 1890 data = [] 1891 if self.__data is None: 1892 return data 1893 idx = self.GetFirstSelected() 1894 while idx != -1: 1895 data.append(self.__data[self.map_item_idx2data_idx(idx)]) 1896 idx = self.GetNextSelected(idx) 1897 1898 return data
1899 1900 selected_item_data = property(get_selected_item_data, lambda x:x) 1901 1902 #------------------------------------------------------------
1903 - def deselect_selected_item(self):
1904 self.Select(idx = self.GetFirstSelected(), on = 0)
1905 1906 #------------------------------------------------------------
1907 - def remove_item(self, item_idx=None):
1908 # do NOT remove the corresponding data because even if 1909 # the item pointing to this data instance is gone all 1910 # other items will still point to their corresponding 1911 # *initial* row numbers 1912 #if self.__data is not None: 1913 # del self.__data[self.map_item_idx2data_idx(item_idx)] 1914 self.DeleteItem(item_idx) 1915 self.__tt_last_item = None 1916 self._invalidate_sorting_metadata()
1917 1918 #------------------------------------------------------------ 1919 # internal helpers 1920 #------------------------------------------------------------
1921 - def __show_context_menu(self, item_idx):
1922 1923 if item_idx == -1: 1924 return 1925 1926 if self.ItemCount == 0: 1927 return 1928 1929 items = self.selected_items 1930 if self.__is_single_selection: 1931 if items is None: 1932 no_of_selected_items = 0 1933 else: 1934 no_of_selected_items = 1 1935 else: 1936 no_of_selected_items = len(items) 1937 if no_of_selected_items == 0: 1938 title = _('List Item Actions') 1939 elif no_of_selected_items == 1: 1940 title = _('List Item Actions (selected: 1 entry)') 1941 else: 1942 title = _('List Item Actions (selected: %s entries)') % no_of_selected_items 1943 1944 # build context menu 1945 self._context_menu = wx.Menu(title = title) 1946 1947 needs_separator = False 1948 if self.__new_callback is not None: 1949 menu_item = self._context_menu.Append(-1, _('Add (<INS>)')) 1950 self.Bind(wx.EVT_MENU, self._on_add_row, menu_item) 1951 needs_separator = True 1952 if self.__edit_callback is not None: 1953 menu_item = self._context_menu.Append(-1, _('&Edit')) 1954 self.Bind(wx.EVT_MENU, self._on_edit_row, menu_item) 1955 needs_separator = True 1956 if self.__delete_callback is not None: 1957 menu_item = self._context_menu.Append(-1, _('Delete (<DEL>)')) 1958 self.Bind(wx.EVT_MENU, self._on_delete_row, menu_item) 1959 needs_separator = True 1960 if needs_separator: 1961 self._context_menu.AppendSeparator() 1962 1963 menu_item = self._context_menu.Append(-1, _('Find (<CTRL-F>)')) 1964 self.Bind(wx.EVT_MENU, self._on_show_search_dialog, menu_item) 1965 if self.__search_term is not None: 1966 if self.__search_term.strip() != '': 1967 menu_item = self._context_menu.Append(-1, _('Find next [%s] (<CTRL-N>)') % self.__search_term) 1968 self.Bind(wx.EVT_MENU, self._on_search_match, menu_item) 1969 self._context_menu.AppendSeparator() 1970 1971 col_headers = [] 1972 self._rclicked_row_idx = item_idx 1973 self._rclicked_row_data = self.get_item_data(item_idx = self._rclicked_row_idx) 1974 self._rclicked_row_cells = [] 1975 self._rclicked_row_cells_w_hdr = [] 1976 for col_idx in range(self.ColumnCount): 1977 cell_content = self.GetItem(self._rclicked_row_idx, col_idx).Text.strip() 1978 col_header = self.GetColumn(col_idx).Text.strip() 1979 col_headers.append(col_header) 1980 self._rclicked_row_cells.append(cell_content) 1981 self._rclicked_row_cells_w_hdr.append('%s: %s' % (col_header, cell_content)) 1982 1983 # save to file 1984 save_menu = wx.Menu() 1985 menu_item = save_menu.Append(-1, _('&All rows')) 1986 self.Bind(wx.EVT_MENU, self._all_rows2file, menu_item) 1987 menu_item = save_menu.Append(-1, _('All rows as &CSV')) 1988 self.Bind(wx.EVT_MENU, self._all_rows2csv, menu_item) 1989 menu_item = save_menu.Append(-1, _('&Tooltips of all rows')) 1990 self.Bind(wx.EVT_MENU, self._all_row_tooltips2file, menu_item) 1991 menu_item = save_menu.Append(-1, _('&Data of all rows')) 1992 self.Bind(wx.EVT_MENU, self._all_row_data2file, menu_item) 1993 1994 if no_of_selected_items > 1: 1995 save_menu.AppendSeparator() 1996 menu_item = save_menu.Append(-1, _('&Selected rows')) 1997 self.Bind(wx.EVT_MENU, self._selected_rows2file, menu_item) 1998 menu_item = save_menu.Append(-1, _('&Selected rows as CSV')) 1999 self.Bind(wx.EVT_MENU, self._selected_rows2csv, menu_item) 2000 menu_item = save_menu.Append(-1, _('&Tooltips of selected rows')) 2001 self.Bind(wx.EVT_MENU, self._selected_row_tooltips2file, menu_item) 2002 menu_item = save_menu.Append(-1, _('&Data of selected rows')) 2003 self.Bind(wx.EVT_MENU, self._selected_row_data2file, menu_item) 2004 2005 # 1) set clipboard to item 2006 clip_menu = wx.Menu() 2007 2008 # items for all selected rows if > 1 2009 if no_of_selected_items > 1: 2010 # row tooltips 2011 menu_item = clip_menu.Append(-1, _('Tooltips of selected rows')) 2012 self.Bind(wx.EVT_MENU, self._tooltips2clipboard, menu_item) 2013 # row data as formatted text if available 2014 menu_item = clip_menu.Append(-1, _('Data (formatted as text) of selected rows')) 2015 self.Bind(wx.EVT_MENU, self._datas2clipboard, menu_item) 2016 # all fields of the list row as one line of text 2017 menu_item = clip_menu.Append(-1, _('Content (as one line each) of selected rows')) 2018 self.Bind(wx.EVT_MENU, self._rows2clipboard, menu_item) 2019 clip_menu.AppendSeparator() 2020 2021 # items for the right-clicked row 2022 # row tooltip 2023 menu_item = clip_menu.Append(-1, _('Tooltip of current row')) 2024 self.Bind(wx.EVT_MENU, self._tooltip2clipboard, menu_item) 2025 # row data as formatted text if available 2026 if hasattr(self._rclicked_row_data, 'format'): 2027 menu_item = clip_menu.Append(-1, _('Data (formatted as text) of current row')) 2028 self.Bind(wx.EVT_MENU, self._data2clipboard, menu_item) 2029 # all fields of the list row as one line of text 2030 menu_item = clip_menu.Append(-1, _('Content (as one line) of current row')) 2031 self.Bind(wx.EVT_MENU, self._row2clipboard, menu_item) 2032 # all fields of the list row as multiple lines of text 2033 menu_item = clip_menu.Append(-1, _('Content (one line per column) of current row')) 2034 self.Bind(wx.EVT_MENU, self._row_list2clipboard, menu_item) 2035 # each field of the list row as text with and without header 2036 clip_menu.AppendSeparator() 2037 for col_idx in range(self.ColumnCount): 2038 col_content = self._rclicked_row_cells[col_idx].strip() 2039 # skip empty field 2040 if col_content == '': 2041 continue 2042 col_header = col_headers[col_idx] 2043 if col_header == '': 2044 # skip one-character fields without header, 2045 # actually, no, because in ideographic languages 2046 # one character may mean a lot 2047 #if len(col_content) == 1: 2048 # continue 2049 # without column header 2050 menu_item = clip_menu.Append(-1, _('Column &%s (current row): "%s" [#%s]') % (col_idx+1, shorten_text(col_content, 35), col_idx)) 2051 self.Bind(wx.EVT_MENU, self._col2clipboard, menu_item) 2052 else: 2053 col_menu = wx.Menu() 2054 # with full column header 2055 menu_item = col_menu.Append(-1, '"%s: %s" [#%s]' % (shorten_text(col_header, 8), shorten_text(col_content, 35), col_idx)) 2056 self.Bind(wx.EVT_MENU, self._col_w_hdr2clipboard, menu_item) 2057 # without column header 2058 menu_item = col_menu.Append(-1, '"%s" [#%s]' % (shorten_text(col_content, 35), col_idx)) 2059 self.Bind(wx.EVT_MENU, self._col2clipboard, menu_item) 2060 clip_menu.Append(-1, _('Column &%s (current row): %s') % (col_idx+1, col_header), col_menu) 2061 2062 # 2) append item to current clipboard item 2063 clip_add_menu = wx.Menu() 2064 2065 # items for all selected rows if > 1 2066 if no_of_selected_items > 1: 2067 # row tooltips 2068 menu_item = clip_add_menu.Append(-1, _('Tooltips of selected rows')) 2069 self.Bind(wx.EVT_MENU, self._add_tooltips2clipboard, menu_item) 2070 # row data as formatted text if available 2071 menu_item = clip_add_menu.Append(-1, _('Data (formatted as text) of selected rows')) 2072 self.Bind(wx.EVT_MENU, self._add_datas2clipboard, menu_item) 2073 # all fields of the list row as one line of text 2074 menu_item = clip_add_menu.Append(-1, _('Content (as one line each) of selected rows')) 2075 self.Bind(wx.EVT_MENU, self._add_rows2clipboard, menu_item) 2076 clip_add_menu.AppendSeparator() 2077 2078 # items for the right-clicked row 2079 # row tooltip 2080 menu_item = clip_add_menu.Append(-1, _('Tooltip of current row')) 2081 self.Bind(wx.EVT_MENU, self._add_tooltip2clipboard, menu_item) 2082 # row data as formatted text if available 2083 if hasattr(self._rclicked_row_data, 'format'): 2084 menu_item = clip_add_menu.Append(-1, _('Data (formatted as text) of current row')) 2085 self.Bind(wx.EVT_MENU, self._add_data2clipboard, menu_item) 2086 # all fields of the list row as one line of text 2087 menu_item = clip_add_menu.Append(-1, _('Content (as one line) of current row')) 2088 self.Bind(wx.EVT_MENU, self._add_row2clipboard, menu_item) 2089 # all fields of the list row as multiple lines of text 2090 menu_item = clip_add_menu.Append(-1, _('Content (one line per column) of current row')) 2091 self.Bind(wx.EVT_MENU, self._add_row_list2clipboard, menu_item) 2092 # each field of the list row as text with and without header 2093 clip_add_menu.AppendSeparator() 2094 for col_idx in range(self.ColumnCount): 2095 col_content = self._rclicked_row_cells[col_idx].strip() 2096 # skip empty field 2097 if col_content == '': 2098 continue 2099 col_header = col_headers[col_idx] 2100 if col_header == '': 2101 # without column header 2102 menu_item = clip_add_menu.Append(-1, _('Column &%s (current row): "%s" [#%s]') % (col_idx+1, shorten_text(col_content, 35), col_idx)) 2103 self.Bind(wx.EVT_MENU, self._add_col2clipboard, menu_item) 2104 else: 2105 col_add_menu = wx.Menu() 2106 # with full column header 2107 menu_item = col_add_menu.Append(-1, '"%s: %s" [#%s]' % (shorten_text(col_header, 8), shorten_text(col_content, 35), col_idx)) 2108 self.Bind(wx.EVT_MENU, self._add_col_w_hdr2clipboard, menu_item) 2109 # without column header 2110 menu_item = col_add_menu.Append(-1, '"%s" [#%s]' % (shorten_text(col_content, 35), col_idx)) 2111 self.Bind(wx.EVT_MENU, self._add_col2clipboard, menu_item) 2112 clip_add_menu.Append(-1, _('Column &%s (current row): %s') % (col_idx+1, col_header), col_add_menu) 2113 2114 # 3) copy item to export area 2115 # put into file 2116 # current row 2117 # - fields as one line 2118 # - fields as list 2119 # - data formatted 2120 # - tooltip 2121 # selected rows 2122 # - fields as lines each 2123 # - all data formatted 2124 # - all tooltips 2125 # - as CSV 2126 # send signal 2127 2128 # show menu 2129 #self._context_menu.Append(-1, _('Copy to e&xport area...'), exp_menu) 2130 self._context_menu.Append(-1, _('&Save to file...'), save_menu) 2131 self._context_menu.Append(-1, _('&Copy to clipboard...'), clip_menu) 2132 self._context_menu.Append(-1, _('Append (&+) to clipboard...'), clip_add_menu) 2133 2134 if self.__extend_popup_menu_callback is not None: 2135 self._context_menu.AppendSeparator() 2136 self.__extend_popup_menu_callback(menu = self._context_menu) 2137 2138 # show menu 2139 self.PopupMenu(self._context_menu, wx.DefaultPosition) 2140 self._context_menu.Destroy() 2141 return
2142 2143 #------------------------------------------------------------
2144 - def __handle_delete(self):
2145 if self.__delete_callback is None: 2146 return 2147 2148 no_items = len(self.get_selected_items(only_one = False)) 2149 if no_items == 0: 2150 return 2151 2152 if no_items > 1: 2153 question = _( 2154 '%s list items are selected.\n' 2155 '\n' 2156 'Really delete all %s items ?' 2157 ) % (no_items, no_items) 2158 title = _('Deleting list items') 2159 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP 2160 dlg = wx.MessageDialog(None, question, title, style) 2161 btn_pressed = dlg.ShowModal() 2162 dlg.Destroy() 2163 if btn_pressed == wx.ID_NO: 2164 self.SetFocus() 2165 return 2166 if btn_pressed == wx.ID_CANCEL: 2167 self.SetFocus() 2168 return 2169 2170 self.__delete_callback() 2171 return
2172 2173 #------------------------------------------------------------
2174 - def __handle_insert(self):
2175 if self.__new_callback is None: 2176 return 2177 self.__new_callback()
2178 2179 #------------------------------------------------------------
2180 - def __handle_edit(self):
2181 if self.__edit_callback is None: 2182 return 2183 self.__edit_callback()
2184 2185 #------------------------------------------------------------
2186 - def __show_search_dialog(self):
2187 #print "showing search dlg" 2188 if self.__search_term is None: 2189 #print "no prev search term" 2190 default = '' 2191 else: 2192 #print "prev search term:", self.__search_term 2193 default = self.__search_term 2194 search_term = wx.GetTextFromUser ( 2195 _('Enter the search term:'), 2196 _('List search'), 2197 default_value = default 2198 ) 2199 if search_term.strip() == '': 2200 #print "empty search term" 2201 self.__search_term = None 2202 self.__tt_static_part = self.__tt_static_part_base 2203 return 2204 2205 #print "search term:", search_term 2206 self.__search_term = search_term 2207 self.__tt_static_part = _( 2208 'Current search term: [[%s]]\n' 2209 '\n' 2210 '%s' 2211 ) % ( 2212 search_term, 2213 self.__tt_static_part_base 2214 ) 2215 self.__search_match()
2216 2217 #------------------------------------------------------------ 2218 # event handlers 2219 #------------------------------------------------------------
2220 - def _on_list_item_activated(self, event):
2221 event.Skip() 2222 if self.__activate_callback is not None: 2223 self.__activate_callback(event) 2224 return 2225 # default double-click / ENTER action: edit 2226 self.__handle_edit()
2227 2228 #------------------------------------------------------------
2229 - def _on_list_item_selected(self, event):
2230 if self.__select_callback is not None: 2231 self.__select_callback(event) 2232 else: 2233 event.Skip()
2234 2235 #------------------------------------------------------------
2236 - def _on_list_item_deselected(self, event):
2237 if self.__deselect_callback is not None: 2238 self.__deselect_callback(event) 2239 else: 2240 event.Skip()
2241 2242 #------------------------------------------------------------
2243 - def _on_list_item_rightclicked(self, event):
2244 event.Skip() 2245 self.__show_context_menu(event.Index)
2246 2247 #------------------------------------------------------------
2248 - def _on_list_key_down(self, evt):
2249 evt.Skip() 2250 2251 if evt.KeyCode == wx.WXK_DELETE: 2252 self.__handle_delete() 2253 return 2254 2255 if evt.KeyCode == wx.WXK_INSERT: 2256 self.__handle_insert() 2257 return 2258 2259 if evt.KeyCode == wx.WXK_MENU: 2260 self.__show_context_menu(evt.Index) 2261 return
2262 2263 #------------------------------------------------------------
2264 - def _on_char(self, evt):
2265 2266 if chr(evt.GetRawKeyCode()) == 'f': 2267 if evt.GetModifiers() == wx.MOD_CMD: 2268 #print "search dialog invoked" 2269 self.__show_search_dialog() 2270 return 2271 2272 if chr(evt.GetRawKeyCode()) == 'n': 2273 if evt.GetModifiers() == wx.MOD_CMD: 2274 #print "search-next key invoked" 2275 self.__search_match() 2276 return 2277 2278 evt.Skip() 2279 return
2280 2281 #------------------------------------------------------------
2282 - def _on_mouse_motion(self, event):
2283 """Update tooltip on mouse motion. 2284 2285 for s in dir(wx): 2286 if s.startswith('LIST_HITTEST'): 2287 print s, getattr(wx, s) 2288 2289 LIST_HITTEST_ABOVE 1 2290 LIST_HITTEST_BELOW 2 2291 LIST_HITTEST_NOWHERE 4 2292 LIST_HITTEST_ONITEM 672 2293 LIST_HITTEST_ONITEMICON 32 2294 LIST_HITTEST_ONITEMLABEL 128 2295 LIST_HITTEST_ONITEMRIGHT 256 2296 LIST_HITTEST_ONITEMSTATEICON 512 2297 LIST_HITTEST_TOLEFT 1024 2298 LIST_HITTEST_TORIGHT 2048 2299 """ 2300 item_idx, where_flag = self.HitTest(wx.Point(event.X, event.Y)) 2301 2302 # pointer on item related area at all ? 2303 if where_flag not in [ 2304 wx.LIST_HITTEST_ONITEMLABEL, 2305 wx.LIST_HITTEST_ONITEMICON, 2306 wx.LIST_HITTEST_ONITEMSTATEICON, 2307 wx.LIST_HITTEST_ONITEMRIGHT, 2308 wx.LIST_HITTEST_ONITEM 2309 ]: 2310 self.__tt_last_item = None # not on any item 2311 self.SetToolTip(self.__tt_static_part) 2312 return 2313 2314 # same item as last time around ? 2315 if self.__tt_last_item == item_idx: 2316 return 2317 2318 # remeber the new item we are on 2319 self.__tt_last_item = item_idx 2320 2321 # HitTest() can return -1 if it so pleases, meaning that no item 2322 # was hit or else that maybe there aren't any items (empty list) 2323 if item_idx == wx.NOT_FOUND: 2324 self.SetToolTip(self.__tt_static_part) 2325 return 2326 2327 # do we *have* item data ? 2328 if self.__data is None: 2329 self.SetToolTip(self.__tt_static_part) 2330 return 2331 2332 # under some circumstances the item_idx returned 2333 # by HitTest() may be out of bounds with respect to 2334 # self.__data, this hints at a sync problem between 2335 # setting display items and associated data 2336 if ( 2337 (item_idx > (len(self.__data) - 1)) 2338 or 2339 (item_idx < -1) 2340 ): 2341 self.SetToolTip(self.__tt_static_part) 2342 print("*************************************************************") 2343 print("GNUmed has detected an inconsistency with list item tooltips.") 2344 print("") 2345 print("This is not a big problem and you can keep working.") 2346 print("") 2347 print("However, please send us the following so we can fix GNUmed:") 2348 print("") 2349 print("item idx: %s" % item_idx) 2350 print('where flag: %s' % where_flag) 2351 print('data list length: %s' % len(self.__data)) 2352 print("*************************************************************") 2353 return 2354 2355 dyna_tt = None 2356 if self.__item_tooltip_callback is not None: 2357 dyna_tt = self.__item_tooltip_callback(self.__data[self.map_item_idx2data_idx(item_idx)]) 2358 2359 if dyna_tt is None: 2360 self.SetToolTip(self.__tt_static_part) 2361 return 2362 2363 self.SetToolTip(dyna_tt)
2364 2365 #------------------------------------------------------------ 2366 # context menu event hendlers 2367 #------------------------------------------------------------
2368 - def _on_add_row(self, evt):
2369 evt.Skip() 2370 self.__handle_insert()
2371 2372 #------------------------------------------------------------
2373 - def _on_edit_row(self, evt):
2374 evt.Skip() 2375 self.__handle_edit()
2376 2377 #------------------------------------------------------------
2378 - def _on_delete_row(self, evt):
2379 evt.Skip() 2380 self.__handle_delete()
2381 2382 #------------------------------------------------------------
2383 - def _on_show_search_dialog(self, evt):
2384 evt.Skip() 2385 self.__show_search_dialog()
2386 2387 #------------------------------------------------------------
2388 - def _on_search_match(self, evt):
2389 evt.Skip() 2390 self.__search_match()
2391 2392 #------------------------------------------------------------
2393 - def _all_rows2file(self, evt):
2394 2395 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S')) 2396 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8') 2397 2398 col_labels = self.column_labels 2399 line = '%s' % ' || '.join(col_labels) 2400 txt_file.write('%s\n' % line) 2401 txt_file.write(('=' * len(line)) + '\n') 2402 2403 for item_idx in range(self.ItemCount): 2404 fields = [] 2405 for col_idx in range(self.ColumnCount): 2406 fields.append(self.GetItem(item_idx, col_idx).Text) 2407 txt_file.write('%s\n' % ' || '.join(fields)) 2408 2409 txt_file.close() 2410 gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % txt_name)
2411 2412 #------------------------------------------------------------
2413 - def _all_rows2csv(self, evt):
2414 2415 csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S')) 2416 csv_file = io.open(csv_name, mode = 'wb') 2417 2418 csv_writer = csv.writer(csv_file) 2419 csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ]) 2420 for item_idx in range(self.ItemCount): 2421 fields = [] 2422 for col_idx in range(self.ColumnCount): 2423 fields.append(self.GetItem(item_idx, col_idx).Text) 2424 csv_writer.writerow([ f.encode('utf-8') for f in fields ]) 2425 2426 csv_file.close() 2427 gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % csv_name)
2428 2429 #------------------------------------------------------------
2430 - def _all_row_tooltips2file(self, evt):
2431 2432 if (self.__data is None) or (self.__item_tooltip_callback is None): 2433 return 2434 2435 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_tooltips-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S')) 2436 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8') 2437 2438 for data in self.data: 2439 tt = self.__item_tooltip_callback(data) 2440 if tt is None: 2441 continue 2442 txt_file.write('%s\n\n' % tt) 2443 2444 txt_file.close() 2445 gmDispatcher.send(signal = 'statustext', msg = _('All tooltips saved to [%s].') % txt_name)
2446 2447 #------------------------------------------------------------
2448 - def _all_row_data2file(self, evt):
2449 2450 if self.__data is None: 2451 return 2452 2453 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S')) 2454 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8') 2455 2456 for data in self.data: 2457 if hasattr(data, 'format'): 2458 txt = data.format() 2459 if type(txt) is list: 2460 txt = '\n'.join(txt) 2461 else: 2462 txt = '%s' % data 2463 txt_file.write('%s\n\n' % txt) 2464 2465 txt_file.close() 2466 gmDispatcher.send(signal = 'statustext', msg = _('All data saved to [%s].') % txt_name)
2467 2468 #------------------------------------------------------------
2469 - def _selected_rows2file(self, evt):
2470 2471 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S')) 2472 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8') 2473 2474 col_labels = self.column_labels 2475 line = '%s' % ' || '.join(col_labels) 2476 txt_file.write('%s\n' % line) 2477 txt_file.write(('=' * len(line)) + '\n') 2478 2479 items = self.selected_items 2480 if self.__is_single_selection: 2481 items = [items] 2482 2483 for item_idx in items: 2484 fields = [] 2485 for col_idx in range(self.ColumnCount): 2486 fields.append(self.GetItem(item_idx, col_idx).Text) 2487 txt_file.write('%s\n' % ' || '.join(fields)) 2488 2489 txt_file.close() 2490 gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % txt_name)
2491 2492 #------------------------------------------------------------
2493 - def _selected_rows2csv(self, evt):
2494 2495 csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S')) 2496 csv_file = io.open(csv_name, mode = 'wb') 2497 2498 csv_writer = csv.writer(csv_file) 2499 csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ]) 2500 2501 items = self.selected_items 2502 if self.__is_single_selection: 2503 items = [items] 2504 2505 for item_idx in items: 2506 fields = [] 2507 for col_idx in range(self.ColumnCount): 2508 fields.append(self.GetItem(item_idx, col_idx).Text) 2509 csv_writer.writerow([ f.encode('utf-8') for f in fields ]) 2510 2511 csv_file.close() 2512 gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % csv_name)
2513 2514 #------------------------------------------------------------
2515 - def _selected_row_tooltips2file(self, evt):
2516 2517 if (self.__data is None) or (self.__item_tooltip_callback is None): 2518 return 2519 2520 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_tooltips-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S')) 2521 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8') 2522 2523 for data in self.selected_item_data: 2524 tt = self.__item_tooltip_callback(data) 2525 if tt is None: 2526 continue 2527 txt_file.write('%s\n\n' % tt) 2528 2529 txt_file.close() 2530 gmDispatcher.send(signal = 'statustext', msg = _('Selected tooltips saved to [%s].') % txt_name)
2531 2532 #------------------------------------------------------------
2533 - def _selected_row_data2file(self, evt):
2534 2535 if self.__data is None: 2536 return 2537 2538 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S')) 2539 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8') 2540 2541 for data in self.selected_item_data: 2542 if hasattr(data, 'format'): 2543 txt = data.format() 2544 if type(txt) is list: 2545 txt = '\n'.join(txt) 2546 else: 2547 txt = '%s' % data 2548 txt_file.write('%s\n\n' % txt) 2549 2550 txt_file.close() 2551 gmDispatcher.send(signal = 'statustext', msg = _('Selected data saved to [%s].') % txt_name)
2552 2553 #------------------------------------------------------------
2554 - def _tooltip2clipboard(self, evt):
2555 if wx.TheClipboard.IsOpened(): 2556 _log.debug('clipboard already open') 2557 return 2558 if not wx.TheClipboard.Open(): 2559 _log.debug('cannot open clipboard') 2560 return 2561 data_obj = wx.TextDataObject() 2562 2563 if (self.__data is None) or (self.__item_tooltip_callback is None): 2564 txt = self.__tt_static_part 2565 else: 2566 txt = self.__item_tooltip_callback(self.__data[self.map_item_idx2data_idx(self._rclicked_row_idx)]) 2567 if txt is None: 2568 txt = self.__tt_static_part 2569 2570 data_obj.SetText(txt) 2571 wx.TheClipboard.SetData(data_obj) 2572 wx.TheClipboard.Close()
2573 2574 #------------------------------------------------------------
2575 - def _tooltips2clipboard(self, evt):
2576 if wx.TheClipboard.IsOpened(): 2577 _log.debug('clipboard already open') 2578 return 2579 if not wx.TheClipboard.Open(): 2580 _log.debug('cannot open clipboard') 2581 return 2582 2583 if (self.__data is None) or (self.__item_tooltip_callback is None): 2584 return 2585 2586 tts = [] 2587 for data in self.selected_item_data: 2588 tt = self.__item_tooltip_callback(data) 2589 if tt is None: 2590 continue 2591 tts.append(tt) 2592 2593 if len(tts) == 0: 2594 return 2595 2596 data_obj = wx.TextDataObject() 2597 data_obj.SetText('\n\n'.join(tts)) 2598 wx.TheClipboard.SetData(data_obj) 2599 wx.TheClipboard.Close()
2600 2601 #------------------------------------------------------------
2602 - def _add_tooltip2clipboard(self, evt):
2603 if wx.TheClipboard.IsOpened(): 2604 _log.debug('clipboard already open') 2605 return 2606 if not wx.TheClipboard.Open(): 2607 _log.debug('cannot open clipboard') 2608 return 2609 data_obj = wx.TextDataObject() 2610 2611 txt = '' 2612 # get previous text 2613 got_it = wx.TheClipboard.GetData(data_obj) 2614 if got_it: 2615 txt = data_obj.Text + '\n' 2616 2617 # add text 2618 if (self.__data is None) or (self.__item_tooltip_callback is None): 2619 txt += self.__tt_static_part 2620 else: 2621 tmp = self.__item_tooltip_callback(self.__data[self.map_item_idx2data_idx(self._rclicked_row_idx)]) 2622 if tmp is None: 2623 txt += self.__tt_static_part 2624 else: 2625 txt += tmp 2626 2627 # set text 2628 data_obj.SetText(txt) 2629 wx.TheClipboard.SetData(data_obj) 2630 wx.TheClipboard.Close()
2631 2632 #------------------------------------------------------------
2633 - def _add_tooltips2clipboard(self, evt):
2634 if wx.TheClipboard.IsOpened(): 2635 _log.debug('clipboard already open') 2636 return 2637 if not wx.TheClipboard.Open(): 2638 _log.debug('cannot open clipboard') 2639 return 2640 2641 if (self.__data is None) or (self.__item_tooltip_callback is None): 2642 return 2643 2644 tts = [] 2645 for data in self.selected_item_data: 2646 tt = self.__item_tooltip_callback(data) 2647 if tt is None: 2648 continue 2649 tts.append(tt) 2650 2651 if len(tts) == 0: 2652 return 2653 2654 data_obj = wx.TextDataObject() 2655 txt = '' 2656 # get previous text 2657 got_it = wx.TheClipboard.GetData(data_obj) 2658 if got_it: 2659 txt = data_obj.Text + '\n\n' 2660 txt += '\n\n'.join(tts) 2661 2662 data_obj.SetText(txt) 2663 wx.TheClipboard.SetData(data_obj) 2664 wx.TheClipboard.Close()
2665 2666 #------------------------------------------------------------
2667 - def _row2clipboard(self, evt):
2668 if wx.TheClipboard.IsOpened(): 2669 _log.debug('clipboard already open') 2670 return 2671 if not wx.TheClipboard.Open(): 2672 _log.debug('cannot open clipboard') 2673 return 2674 data_obj = wx.TextDataObject() 2675 data_obj.SetText(' // '.join(self._rclicked_row_cells)) 2676 wx.TheClipboard.SetData(data_obj) 2677 wx.TheClipboard.Close()
2678 2679 #------------------------------------------------------------
2680 - def _rows2clipboard(self, evt):
2681 if wx.TheClipboard.IsOpened(): 2682 _log.debug('clipboard already open') 2683 return 2684 if not wx.TheClipboard.Open(): 2685 _log.debug('cannot open clipboard') 2686 return 2687 2688 rows = [] 2689 for item_idx in self.selected_items: 2690 rows.append(' // '.join([ self.GetItem(item_idx, col_idx).Text.strip() for col_idx in range(self.ColumnCount) ])) 2691 2692 data_obj = wx.TextDataObject() 2693 data_obj.SetText('\n\n'.join(rows)) 2694 wx.TheClipboard.SetData(data_obj) 2695 wx.TheClipboard.Close()
2696 2697 #------------------------------------------------------------
2698 - def _add_row2clipboard(self, evt):
2699 if wx.TheClipboard.IsOpened(): 2700 _log.debug('clipboard already open') 2701 return 2702 if not wx.TheClipboard.Open(): 2703 _log.debug('cannot open clipboard') 2704 return 2705 data_obj = wx.TextDataObject() 2706 2707 txt = '' 2708 # get previous text 2709 got_it = wx.TheClipboard.GetData(data_obj) 2710 if got_it: 2711 txt = data_obj.Text + '\n' 2712 2713 # add text 2714 txt += ' // '.join(self._rclicked_row_cells) 2715 2716 # set text 2717 data_obj.SetText(txt) 2718 wx.TheClipboard.SetData(data_obj) 2719 wx.TheClipboard.Close()
2720 2721 #------------------------------------------------------------
2722 - def _add_rows2clipboard(self, evt):
2723 if wx.TheClipboard.IsOpened(): 2724 _log.debug('clipboard already open') 2725 return 2726 if not wx.TheClipboard.Open(): 2727 _log.debug('cannot open clipboard') 2728 return 2729 2730 rows = [] 2731 for item_idx in self.selected_items: 2732 rows.append(' // '.join([ self.GetItem(item_idx, col_idx).Text.strip() for col_idx in range(self.ColumnCount) ])) 2733 2734 data_obj = wx.TextDataObject() 2735 2736 txt = '' 2737 # get previous text 2738 got_it = wx.TheClipboard.GetData(data_obj) 2739 if got_it: 2740 txt = data_obj.Text + '\n' 2741 txt += '\n\n'.join(rows) 2742 2743 data_obj.SetText(txt) 2744 wx.TheClipboard.SetData(data_obj) 2745 wx.TheClipboard.Close()
2746 2747 #------------------------------------------------------------
2748 - def _row_list2clipboard(self, evt):
2749 if wx.TheClipboard.IsOpened(): 2750 _log.debug('clipboard already open') 2751 return 2752 if not wx.TheClipboard.Open(): 2753 _log.debug('cannot open clipboard') 2754 return 2755 data_obj = wx.TextDataObject() 2756 data_obj.SetText('\n'.join(self._rclicked_row_cells_w_hdr)) 2757 wx.TheClipboard.SetData(data_obj) 2758 wx.TheClipboard.Close()
2759 2760 #------------------------------------------------------------
2761 - def _add_row_list2clipboard(self, evt):
2762 if wx.TheClipboard.IsOpened(): 2763 _log.debug('clipboard already open') 2764 return 2765 if not wx.TheClipboard.Open(): 2766 _log.debug('cannot open clipboard') 2767 return 2768 data_obj = wx.TextDataObject() 2769 2770 txt = '' 2771 # get previous text 2772 got_it = wx.TheClipboard.GetData(data_obj) 2773 if got_it: 2774 txt = data_obj.Text + '\n' 2775 2776 # add text 2777 txt += '\n'.join(self._rclicked_row_cells_w_hdr) 2778 2779 # set text 2780 data_obj.SetText(txt) 2781 wx.TheClipboard.SetData(data_obj) 2782 wx.TheClipboard.Close()
2783 2784 #------------------------------------------------------------
2785 - def _data2clipboard(self, evt):
2786 if wx.TheClipboard.IsOpened(): 2787 _log.debug('clipboard already open') 2788 return 2789 if not wx.TheClipboard.Open(): 2790 _log.debug('cannot open clipboard') 2791 return 2792 data_obj = wx.TextDataObject() 2793 txt = self._rclicked_row_data.format() 2794 if type(txt) == type([]): 2795 txt = '\n'.join(txt) 2796 data_obj.SetText(txt) 2797 wx.TheClipboard.SetData(data_obj) 2798 wx.TheClipboard.Close()
2799 2800 #------------------------------------------------------------
2801 - def _datas2clipboard(self, evt):
2802 if wx.TheClipboard.IsOpened(): 2803 _log.debug('clipboard already open') 2804 return 2805 if not wx.TheClipboard.Open(): 2806 _log.debug('cannot open clipboard') 2807 return 2808 2809 data_as_txt = [] 2810 for data in self.selected_item_data: 2811 if hasattr(data, 'format'): 2812 txt = data.format() 2813 if type(txt) is list: 2814 txt = '\n'.join(txt) 2815 else: 2816 txt = '%s' % data 2817 data_as_txt.append(txt) 2818 2819 data_obj = wx.TextDataObject() 2820 data_obj.SetText('\n\n'.join(data_as_txt)) 2821 wx.TheClipboard.SetData(data_obj) 2822 wx.TheClipboard.Close()
2823 2824 #------------------------------------------------------------
2825 - def _add_data2clipboard(self, evt):
2826 if wx.TheClipboard.IsOpened(): 2827 _log.debug('clipboard already open') 2828 return 2829 if not wx.TheClipboard.Open(): 2830 _log.debug('cannot open clipboard') 2831 return 2832 data_obj = wx.TextDataObject() 2833 2834 txt = '' 2835 # get previous text 2836 got_it = wx.TheClipboard.GetData(data_obj) 2837 if got_it: 2838 txt = data_obj.Text + '\n' 2839 2840 # add text 2841 tmp = self._rclicked_row_data.format() 2842 if type(tmp) == type([]): 2843 txt += '\n'.join(tmp) 2844 else: 2845 txt += tmp 2846 2847 # set text 2848 data_obj.SetText(txt) 2849 wx.TheClipboard.SetData(data_obj) 2850 wx.TheClipboard.Close()
2851 2852 #------------------------------------------------------------
2853 - def _add_datas2clipboard(self, evt):
2854 if wx.TheClipboard.IsOpened(): 2855 _log.debug('clipboard already open') 2856 return 2857 if not wx.TheClipboard.Open(): 2858 _log.debug('cannot open clipboard') 2859 return 2860 2861 data_as_txt = [] 2862 for data in self.selected_item_data: 2863 if hasattr(data, 'format'): 2864 txt = data.format() 2865 if type(txt) is list: 2866 txt = '\n'.join(txt) 2867 else: 2868 txt = '%s' % data 2869 data_as_txt.append(txt) 2870 2871 data_obj = wx.TextDataObject() 2872 txt = '' 2873 # get previous text 2874 got_it = wx.TheClipboard.GetData(data_obj) 2875 if got_it: 2876 txt = data_obj.Text + '\n' 2877 txt += '\n'.join(data_as_txt) 2878 2879 # set text 2880 data_obj.SetText(txt) 2881 wx.TheClipboard.SetData(data_obj) 2882 wx.TheClipboard.Close()
2883 2884 #------------------------------------------------------------
2885 - def _col2clipboard(self, evt):
2886 if wx.TheClipboard.IsOpened(): 2887 _log.debug('clipboard already open') 2888 return 2889 if not wx.TheClipboard.Open(): 2890 _log.debug('cannot open clipboard') 2891 return 2892 data_obj = wx.TextDataObject() 2893 2894 #col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.split(u':', 1)[0].rstrip(u':')) - 1 2895 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']')) 2896 txt = self._rclicked_row_cells[col_idx] 2897 2898 data_obj.SetText(txt) 2899 wx.TheClipboard.SetData(data_obj) 2900 wx.TheClipboard.Close()
2901 2902 #------------------------------------------------------------
2903 - def _add_col2clipboard(self, evt):
2904 if wx.TheClipboard.IsOpened(): 2905 _log.debug('clipboard already open') 2906 return 2907 if not wx.TheClipboard.Open(): 2908 _log.debug('cannot open clipboard') 2909 return 2910 data_obj = wx.TextDataObject() 2911 2912 txt = '' 2913 # get previous text 2914 got_it = wx.TheClipboard.GetData(data_obj) 2915 if got_it: 2916 txt = data_obj.Text + '\n' 2917 2918 # add text 2919 #col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.split(u':', 1)[0].rstrip(u':')) - 1 2920 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']')) 2921 txt += self._rclicked_row_cells[col_idx] 2922 2923 # set text 2924 data_obj.SetText(txt) 2925 wx.TheClipboard.SetData(data_obj) 2926 wx.TheClipboard.Close()
2927 2928 #------------------------------------------------------------
2929 - def _col_w_hdr2clipboard(self, evt):
2930 if wx.TheClipboard.IsOpened(): 2931 _log.debug('clipboard already open') 2932 return 2933 if not wx.TheClipboard.Open(): 2934 _log.debug('cannot open clipboard') 2935 return 2936 data_obj = wx.TextDataObject() 2937 2938 #col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.split(u':', 1)[0].rstrip(u':')) - 1 2939 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']')) 2940 txt = self._rclicked_row_cells_w_hdr[col_idx] 2941 2942 data_obj.SetText(txt) 2943 wx.TheClipboard.SetData(data_obj) 2944 wx.TheClipboard.Close()
2945 2946 #------------------------------------------------------------
2947 - def _add_col_w_hdr2clipboard(self, evt):
2948 if wx.TheClipboard.IsOpened(): 2949 _log.debug('clipboard already open') 2950 return 2951 if not wx.TheClipboard.Open(): 2952 _log.debug('cannot open clipboard') 2953 return 2954 data_obj = wx.TextDataObject() 2955 2956 txt = '' 2957 # get previous text 2958 got_it = wx.TheClipboard.GetData(data_obj) 2959 if got_it: 2960 txt = data_obj.Text + '\n' 2961 2962 # add text 2963 #col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.split(u':', 1)[0].rstrip(u':')) - 1 2964 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']')) 2965 txt += self._rclicked_row_cells_w_hdr[col_idx] 2966 2967 # set text 2968 data_obj.SetText(txt) 2969 wx.TheClipboard.SetData(data_obj) 2970 wx.TheClipboard.Close()
2971 2972 #------------------------------------------------------------ 2973 # search related methods 2974 #------------------------------------------------------------ 2975 # def _on_lost_focus(self, evt): 2976 # evt.Skip() 2977 # if self.__search_dlg is None: 2978 # return 2979 ## print self.FindFocus() 2980 ## print self.__search_dlg 2981 # #self.__search_dlg.Close() 2982 2983 #------------------------------------------------------------
2984 - def __search_match(self):
2985 #print "search_match" 2986 if self.__search_term is None: 2987 #print "no search term" 2988 return False 2989 if self.__search_term.strip() == '': 2990 #print "empty search term" 2991 return False 2992 if self.__searchable_cols is None: 2993 #print "searchable cols not initialized, now setting" 2994 self.searchable_columns = None 2995 if len(self.__searchable_cols) == 0: 2996 #print "no cols to search" 2997 return False 2998 2999 #print "on searching for match on:", self.__search_term 3000 for row_idx in range(self.__next_line_to_search, self.ItemCount): 3001 for col_idx in range(self.ColumnCount): 3002 if col_idx not in self.__searchable_cols: 3003 continue 3004 col_val = self.GetItem(row_idx, col_idx).GetText() 3005 if regex.search(self.__search_term, col_val, regex.U | regex.I) is not None: 3006 self.Select(row_idx) 3007 self.EnsureVisible(row_idx) 3008 if row_idx == self.ItemCount - 1: 3009 # wrap around 3010 self.__next_line_to_search = 0 3011 else: 3012 self.__next_line_to_search = row_idx + 1 3013 return True 3014 # wrap around 3015 self.__next_line_to_search = 0 3016 return False
3017 3018 #------------------------------------------------------------
3019 - def _set_searchable_cols(self, cols):
3020 #print "setting searchable cols to:", cols 3021 # zero-based list of which columns to search 3022 if cols is None: 3023 #print "setting searchable cols to:", range(self.ColumnCount) 3024 self.__searchable_cols = range(self.ColumnCount) 3025 return 3026 # weed out columns to be searched which 3027 # don't exist and uniquify them 3028 new_cols = {} 3029 for col in cols: 3030 if col < self.ColumnCount: 3031 new_cols[col] = True 3032 #print "actually setting searchable cols to:", new_cols.keys() 3033 self.__searchable_cols = new_cols.keys()
3034 3035 searchable_columns = property(lambda x:x, _set_searchable_cols) 3036 3037 #------------------------------------------------------------ 3038 # properties 3039 #------------------------------------------------------------
3040 - def _get_activate_callback(self):
3041 return self.__activate_callback
3042
3043 - def _set_activate_callback(self, callback):
3044 if callback is None: 3045 self.Unbind(wx.EVT_LIST_ITEM_ACTIVATED) 3046 self.__activate_callback = None 3047 return 3048 if not callable(callback): 3049 raise ValueError('<activate> callback is not a callable: %s' % callback) 3050 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_list_item_activated) 3051 self.__activate_callback = callback
3052 3053 activate_callback = property(_get_activate_callback, _set_activate_callback) 3054 3055 #------------------------------------------------------------
3056 - def _get_select_callback(self):
3057 return self.__select_callback
3058
3059 - def _set_select_callback(self, callback):
3060 if callback is None: 3061 self.Unbind(wx.EVT_LIST_ITEM_SELECTED) 3062 self.__select_callback = None 3063 return 3064 if not callable(callback): 3065 raise ValueError('<selected> callback is not a callable: %s' % callback) 3066 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self._on_list_item_selected) 3067 self.__select_callback = callback
3068 3069 select_callback = property(_get_select_callback, _set_select_callback) 3070 3071 #------------------------------------------------------------
3072 - def _get_deselect_callback(self):
3073 return self.__deselect_callback
3074
3075 - def _set_deselect_callback(self, callback):
3076 if callback is None: 3077 self.Unbind(wx.EVT_LIST_ITEM_DESELECTED) 3078 self.__deselect_callback = None 3079 return 3080 if not callable(callback): 3081 raise ValueError('<deselected> callback is not a callable: %s' % callback) 3082 self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self._on_list_item_deselected) 3083 self.__deselect_callback = callback
3084 3085 deselect_callback = property(_get_deselect_callback, _set_deselect_callback) 3086 3087 #------------------------------------------------------------
3088 - def _get_delete_callback(self):
3089 return self.__delete_callback
3090
3091 - def _set_delete_callback(self, callback):
3092 if callback is None: 3093 #self.Unbind(wx.EVT_LIST_ITEM_SELECTED) 3094 self.__delete_callback = None 3095 return 3096 if not callable(callback): 3097 raise ValueError('<delete> callback is not a callable: %s' % callback) 3098 #self.Bind(wx.EVT_LIST_ITEM_SELECTED, self._on_list_item_selected) 3099 self.__delete_callback = callback
3100 3101 delete_callback = property(_get_delete_callback, _set_delete_callback) 3102 3103 #------------------------------------------------------------
3104 - def _get_new_callback(self):
3105 return self.__new_callback
3106
3107 - def _set_new_callback(self, callback):
3108 if callback is None: 3109 self.__new_callback = None 3110 return 3111 if not callable(callback): 3112 raise ValueError('<new> callback is not a callable: %s' % callback) 3113 self.__new_callback = callback
3114 3115 new_callback = property(_get_new_callback, _set_new_callback) 3116 3117 #------------------------------------------------------------
3118 - def _get_edit_callback(self):
3119 return self.__edit_callback
3120
3121 - def _set_edit_callback(self, callback):
3122 if callback is None: 3123 self.__edit_callback = None 3124 return 3125 if not callable(callback): 3126 raise ValueError('<edit> callback is not a callable: %s' % callback) 3127 self.__edit_callback = callback
3128 3129 edit_callback = property(_get_edit_callback, _set_edit_callback) 3130 3131 #------------------------------------------------------------
3132 - def _set_item_tooltip_callback(self, callback):
3133 if callback is not None: 3134 if not callable(callback): 3135 raise ValueError('<item_tooltip> callback is not a callable: %s' % callback) 3136 self.__item_tooltip_callback = callback
3137 3138 # the callback must be a function which takes a single argument 3139 # the argument is the data for the item the tooltip is on 3140 # the callback must return None if no item tooltip is to be shown 3141 # otherwise it must return a string (possibly with \n) 3142 item_tooltip_callback = property(lambda x:x, _set_item_tooltip_callback) 3143 3144 #------------------------------------------------------------
3145 - def _set_extend_popup_menu_callback(self, callback):
3146 if callback is not None: 3147 if not callable(callback): 3148 raise ValueError('<extend_popup_menu> callback is not a callable: %s' % callback) 3149 self.__extend_popup_menu_callback = callback
3150 3151 extend_popup_menu_callback = property(lambda x:x, _set_extend_popup_menu_callback) 3152 3153 #------------------------------------------------------------ 3154 # ColumnSorterMixin API 3155 #------------------------------------------------------------
3156 - def GetListCtrl(self):
3157 if self.itemDataMap is None: 3158 self._update_sorting_metadata() 3159 return self # required
3160 3161 #------------------------------------------------------------
3162 - def OnSortOrderChanged(self):
3163 col_idx, is_ascending = self.GetSortState() 3164 if col_idx == -1: 3165 _log.debug('outside any column (idx: -1) clicked, ignoring') 3166 return 3167 self._remove_sorting_indicators_from_column_headers() 3168 col_state = self.GetColumn(col_idx) 3169 col_state.Text += self.sort_order_tags[is_ascending] 3170 self.SetColumn(col_idx, col_state)
3171 3172 #------------------------------------------------------------
3173 - def GetSecondarySortValues(self, primary_sort_col, primary_item1_idx, primary_item2_idx):
3174 return (primary_item1_idx, primary_item2_idx) 3175 3176 if self.__secondary_sort_col is None: 3177 return (primary_item1_idx, primary_item2_idx) 3178 if self.__secondary_sort_col == primary_sort_col: 3179 return (primary_item1_idx, primary_item2_idx) 3180 3181 secondary_val1 = self.itemDataMap[primary_item1_idx][self.__secondary_sort_col] 3182 secondary_val2 = self.itemDataMap[primary_item2_idx][self.__secondary_sort_col] 3183 3184 if type(secondary_val1) == type('') and type(secondary_val2) == type(''): 3185 secondary_cmp_result = locale.strcoll(secondary_val1, secondary_val2) 3186 elif type(secondary_val1) == type('') or type(secondary_val2) == type(''): 3187 secondary_cmp_result = locale.strcoll(str(secondary_val1), str(secondary_val2)) 3188 else: 3189 secondary_cmp_result = cmp(secondary_val1, secondary_val2) 3190 3191 if secondary_cmp_result == 0: 3192 return (primary_item1_idx, primary_item2_idx) 3193 3194 # make the secondary column always sort ascending 3195 currently_ascending = self._colSortFlag[primary_sort_col] 3196 if currently_ascending: 3197 secondary_val1, secondary_val2 = min(secondary_val1, secondary_val2), max(secondary_val1, secondary_val2) 3198 else: 3199 secondary_val1, secondary_val2 = max(secondary_val1, secondary_val2), min(secondary_val1, secondary_val2) 3200 return (secondary_val1, secondary_val2)
3201 3202 #------------------------------------------------------------
3203 - def _unicode_aware_column_sorter(self, item1, item2):
3204 # http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/ 3205 # http://stackoverflow.com/questions/1097908/how-do-i-sort-unicode-strings-alphabetically-in-python 3206 # PyICU 3207 sort_col, is_ascending = self.GetSortState() 3208 data1 = self.itemDataMap[item1][sort_col] 3209 data2 = self.itemDataMap[item2][sort_col] 3210 if type(data1) == type('') and type(data2) == type(''): 3211 cmp_result = locale.strcoll(data1, data2) 3212 elif type(data1) == type('') or type(data2) == type(''): 3213 cmp_result = locale.strcoll(str(data1), str(data2)) 3214 else: 3215 cmp_result = cmp(data1, data2) 3216 3217 #direction = u'ASC' 3218 if not is_ascending: 3219 cmp_result = -1 * cmp_result 3220 #direction = u'DESC' 3221 # debug: 3222 # if cmp_result < 0: 3223 # op1 = u'\u2191 ' # up 3224 # op2 = u'\u2193' # down 3225 # elif cmp_result > 0: 3226 # op2 = u'\u2191 ' # up 3227 # op1 = u'\u2193' # down 3228 # else: 3229 # op1 = u' = ' 3230 # op2 = u'' 3231 # print u'%s: [%s]%s[%s]%s (%s)' % (direction, data1, op1, data2, op2, cmp_result) 3232 3233 return cmp_result
3234 3235 # defining our own column sorter does not seem to make 3236 # a difference over the default one until we resort to 3237 # something other than locale.strcoll/strxform/cmp for 3238 # actual sorting 3239 #def GetColumnSorter(self): 3240 # return self._unicode_aware_column_sorter 3241 3242 #------------------------------------------------------------
3243 - def _generate_map_for_sorting(self):
3244 dict2sort = {} 3245 item_count = self.GetItemCount() 3246 if item_count == 0: 3247 return dict2sort 3248 col_count = self.GetColumnCount() 3249 for item_idx in range(item_count): 3250 dict2sort[item_idx] = () 3251 if col_count == 0: 3252 continue 3253 for col_idx in range(col_count): 3254 dict2sort[item_idx] += (self.GetItem(item_idx, col_idx).GetText(), ) 3255 # debugging: 3256 #print u'[%s:%s] ' % (item_idx, col_idx), self.GetItem(item_idx, col_idx).GetText() 3257 3258 return dict2sort
3259 3260 #------------------------------------------------------------
3262 for col_idx in range(self.ColumnCount): 3263 col_state = self.GetColumn(col_idx) 3264 initial_header = col_state.Text 3265 if col_state.Text.endswith(self.sort_order_tags[True]): 3266 col_state.Text = col_state.Text[:-len(self.sort_order_tags[True])] 3267 if col_state.Text.endswith(self.sort_order_tags[False]): 3268 col_state.Text = col_state.Text[:-len(self.sort_order_tags[False])] 3269 if col_state.Text == initial_header: 3270 continue 3271 self.SetColumn(col_idx, col_state)
3272 3273 #------------------------------------------------------------
3275 self.itemDataMap = None 3276 self.SetColumnCount(self.GetColumnCount()) 3277 self._remove_sorting_indicators_from_column_headers()
3278 3279 #------------------------------------------------------------
3280 - def _update_sorting_metadata(self):
3281 # MUST have this name 3282 self.itemDataMap = self._generate_map_for_sorting()
3283 3284 #------------------------------------------------------------
3285 - def _on_col_click(self, event):
3286 # this MUST NOT call event.Skip() or else the column sorter mixin# 3287 # will not kick in anymore under wxP 3 3288 #event.Skip() 3289 pass
3290 # debugging: 3291 # sort_col, order = self.GetSortState() 3292 # print u'col clicked: %s / sort col: %s / sort direction: %s / sort flags: %s' % (event.GetColumn(), sort_col, order, self._colSortFlag) 3293 # if self.itemDataMap is not None: 3294 # print u'sort items data map:' 3295 # for key, item in self.itemDataMap.items(): 3296 # print key, u' -- ', item 3297 3298 #------------------------------------------------------------
3299 - def __get_secondary_sort_col(self):
3300 return self.__secondary_sort_col
3301
3302 - def __set_secondary_sort_col(self, col):
3303 if col is None: 3304 self.__secondary_sort_col = None 3305 return 3306 if col > self.GetColumnCount(): 3307 raise ValueError('cannot secondary-sort on col [%s], there are only [%s] columns', col, self.GetColumnCount()) 3308 self.__secondary_sort_col = col
3309 3310 secondary_sort_column = property(__get_secondary_sort_col, __set_secondary_sort_col)
3311 3312 #================================================================
3313 -def shorten_text(text=None, max_length=None):
3314 if len(text) <= max_length: 3315 return text 3316 return text[:max_length-1] + '\u2026'
3317 3318 #================================================================ 3319 # main 3320 #---------------------------------------------------------------- 3321 if __name__ == '__main__': 3322 3323 if len(sys.argv) < 2: 3324 sys.exit() 3325 3326 if sys.argv[1] != 'test': 3327 sys.exit() 3328 3329 sys.path.insert(0, '../../') 3330 3331 from Gnumed.pycommon import gmI18N 3332 gmI18N.activate_locale() 3333 gmI18N.install_domain() 3334 3335 #------------------------------------------------------------
3336 - def test_wxMultiChoiceDialog():
3337 app = wx.PyWidgetTester(size = (400, 500)) 3338 dlg = wx.MultiChoiceDialog ( 3339 parent = None, 3340 message = 'test message', 3341 caption = 'test caption', 3342 choices = ['a', 'b', 'c', 'd', 'e'] 3343 ) 3344 dlg.ShowModal() 3345 sels = dlg.GetSelections() 3346 print("selected:") 3347 for sel in sels: 3348 print(sel)
3349 3350 #------------------------------------------------------------
3351 - def test_get_choices_from_list():
3352 3353 def edit(argument): 3354 print("editor called with:") 3355 print(argument)
3356 3357 def refresh(lctrl): 3358 choices = ['a', 'b', 'c'] 3359 lctrl.set_string_items(choices) 3360 3361 app = wx.App() 3362 chosen = get_choices_from_list ( 3363 # msg = 'select a health issue\nfrom the list below\n', 3364 caption = 'select health issues', 3365 #choices = [['D.M.II', '4'], ['MS', '3'], ['Fraktur', '2']], 3366 #columns = ['issue', 'no of episodes'] 3367 columns = ['issue'], 3368 refresh_callback = refresh, 3369 single_selection = False 3370 #, edit_callback = edit 3371 ) 3372 print("chosen:") 3373 print(chosen) 3374 3375 #------------------------------------------------------------
3376 - def test_item_picker_dlg():
3377 #app = wx.PyWidgetTester(size = (200, 50)) 3378 app = wx.App(size = (200, 50)) 3379 dlg = cItemPickerDlg(None, -1, msg = 'Pick a few items:') 3380 dlg.set_columns(['Plugins'], ['Load in workplace', 'dummy']) 3381 #dlg.set_columns(['Plugins'], []) 3382 dlg.set_string_items(['patient', 'emr', 'docs']) 3383 result = dlg.ShowModal() 3384 print(result) 3385 print(dlg.get_picks())
3386 3387 #------------------------------------------------------------ 3388 test_get_choices_from_list() 3389 #test_wxMultiChoiceDialog() 3390 #test_item_picker_dlg() 3391 3392 #================================================================ 3393