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