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

Source Code for Module Gnumed.wxpython.gmListWidgets

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