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