1 """GNUmed list controls and widgets.
2
3 TODO:
4
5 From: Rob McMullen <rob.mcmullen@gmail.com>
6 To: wxPython-users@lists.wxwidgets.org
7 Subject: Re: [wxPython-users] ANN: ColumnSizer mixin for ListCtrl
8
9 Thanks for all the suggestions, on and off line. There's an update
10 with a new name (ColumnAutoSizeMixin) and better sizing algorithm at:
11
12 http://trac.flipturn.org/browser/trunk/peppy/lib/column_autosize.py
13 """
14
15 __version__ = "$Revision: 1.37 $"
16 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
17 __license__ = "GPL"
18
19
20 import sys, types
21
22
23 import wx
24 import wx.lib.mixins.listctrl as listmixins
25
26
27 if __name__ == '__main__':
28 sys.path.insert(0, '../../')
29 from Gnumed.business import gmPerson
30 from Gnumed.pycommon import gmTools, gmDispatcher
31 from Gnumed.wxpython import gmGuiHelpers
32 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg, wxgGenericListManagerPnl
33
34
35 -def get_choices_from_list (
36 parent=None,
37 msg=None,
38 caption=None,
39 choices=None,
40 selections=None,
41 columns=None,
42 data=None,
43 edit_callback=None,
44 new_callback=None,
45 delete_callback=None,
46 refresh_callback=None,
47 single_selection=False,
48 can_return_empty=False,
49 ignore_OK_button=False,
50 left_extra_button=None,
51 middle_extra_button=None,
52 right_extra_button=None):
107
109 """A dialog holding a list and a few buttons to act on the items."""
110
137
140
143
148
151
154
157
158
159
161 if not self.__ignore_OK_button:
162 self._BTN_ok.SetDefault()
163 self._BTN_ok.Enable(True)
164
165 if self.edit_callback is not None:
166 self._BTN_edit.Enable(True)
167
168 if self.delete_callback is not None:
169 self._BTN_delete.Enable(True)
170
172 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
173 if not self.can_return_empty:
174 self._BTN_cancel.SetDefault()
175 self._BTN_ok.Enable(False)
176 self._BTN_edit.Enable(False)
177 self._BTN_delete.Enable(False)
178
190
204
221
234
247
260
261
262
271
272 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button)
273
288
289 left_extra_button = property(lambda x:x, _set_left_extra_button)
290
305
306 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
307
322
323 right_extra_button = property(lambda x:x, _set_right_extra_button)
324
326 return self.__new_callback
327
329 if callback is not None:
330 if self.refresh_callback is None:
331 raise ValueError('refresh callback must be set before new callback can be set')
332 if not callable(callback):
333 raise ValueError('<new> callback is not a callable: %s' % callback)
334 self.__new_callback = callback
335
336 if callback is None:
337 self._BTN_new.Enable(False)
338 self._BTN_new.Hide()
339 else:
340 self._BTN_new.Enable(True)
341 self._BTN_new.Show()
342
343 new_callback = property(_get_new_callback, _set_new_callback)
344
346 return self.__edit_callback
347
349 if callback is not None:
350 if self.refresh_callback is None:
351 raise ValueError('refresh callback must be set before edit callback can be set')
352 if not callable(callback):
353 raise ValueError('<edit> callback is not a callable: %s' % callback)
354 self.__edit_callback = callback
355
356 if callback is None:
357 self._BTN_edit.Enable(False)
358 self._BTN_edit.Hide()
359 else:
360 self._BTN_edit.Enable(True)
361 self._BTN_edit.Show()
362
363 edit_callback = property(_get_edit_callback, _set_edit_callback)
364
366 return self.__delete_callback
367
369 if callback is not None:
370 if self.refresh_callback is None:
371 raise ValueError('refresh callback must be set before delete callback can be set')
372 if not callable(callback):
373 raise ValueError('<delete> callback is not a callable: %s' % callback)
374 self.__delete_callback = callback
375
376 if callback is None:
377 self._BTN_delete.Enable(False)
378 self._BTN_delete.Hide()
379 else:
380 self._BTN_delete.Enable(True)
381 self._BTN_delete.Show()
382
383 delete_callback = property(_get_delete_callback, _set_delete_callback)
384
386 return self.__refresh_callback
387
395
397 if callback is not None:
398 if not callable(callback):
399 raise ValueError('<refresh> callback is not a callable: %s' % callback)
400 self.__refresh_callback = callback
401 if callback is not None:
402 wx.CallAfter(self._set_refresh_callback_helper)
403
404 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
405
407 """A panel holding a generic multi-column list and action buttions."""
408
428
429
430
433
435 self._LCTRL_items.set_string_items(items = items)
436 self._LCTRL_items.set_column_widths()
437
438 if (items is None) or (len(items) == 0):
439 self._BTN_edit.Enable(False)
440 self._BTN_remove.Enable(False)
441 else:
442 self._LCTRL_items.Select(0)
443
446
449
452
453
454
456 if self.edit_callback is not None:
457 self._BTN_edit.Enable(True)
458 if self.delete_callback is not None:
459 self._BTN_remove.Enable(True)
460
462 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
463 self._BTN_edit.Enable(False)
464 self._BTN_remove.Enable(False)
465
476
490
504
505
506
508 return self.__new_callback
509
511 self.__new_callback = callback
512 self._BTN_add.Enable(callback is not None)
513
514 new_callback = property(_get_new_callback, _set_new_callback)
515
517
519
520 try:
521 kwargs['style'] = kwargs['style'] | wx.LC_REPORT
522 except KeyError:
523 kwargs['style'] = wx.LC_REPORT
524
525 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL)
526
527 wx.ListCtrl.__init__(self, *args, **kwargs)
528 listmixins.ListCtrlAutoWidthMixin.__init__(self)
529
530 self.__widths = None
531 self.__data = None
532
533
534
536 """(Re)define the columns.
537
538 Note that this will (have to) delete the items.
539 """
540 self.ClearAll()
541 if columns is None:
542 return
543 for idx in range(len(columns)):
544 self.InsertColumn(idx, columns[idx])
545
547 """Set the column width policy.
548
549 widths = None:
550 use previous policy if any or default policy
551 widths != None:
552 use this policy and remember it for later calls
553
554 This means there is no way to *revert* to the default policy :-(
555 """
556
557 if widths is not None:
558 self.__widths = widths
559 for idx in range(len(self.__widths)):
560 self.SetColumnWidth(col = idx, width = self.__widths[idx])
561 return
562
563
564 if self.__widths is not None:
565 for idx in range(len(self.__widths)):
566 self.SetColumnWidth(col = idx, width = self.__widths[idx])
567 return
568
569
570 if self.GetItemCount() == 0:
571 width_type = wx.LIST_AUTOSIZE_USEHEADER
572 else:
573 width_type = wx.LIST_AUTOSIZE
574 for idx in range(self.GetColumnCount()):
575 self.SetColumnWidth(col = idx, width = width_type)
576
578 """All item members must be unicode()able or None."""
579
580 self.DeleteAllItems()
581 self.__data = items
582
583 if items is None:
584 return
585
586 for item in items:
587 try:
588 item[0]
589 if not isinstance(item, basestring):
590 is_numerically_iterable = True
591 else:
592 is_numerically_iterable = False
593 except TypeError:
594 is_numerically_iterable = False
595
596 if is_numerically_iterable:
597
598
599 col_val = unicode(item[0])
600 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
601 for col_idx in range(1, min(self.GetColumnCount(), len(item))):
602 col_val = unicode(item[col_idx])
603 self.SetStringItem(index = row_num, col = col_idx, label = col_val)
604 else:
605
606 col_val = unicode(item)
607 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
608
610 """<data must be a list corresponding to the item indices>"""
611 self.__data = data
612
614 self.Select(0, on = 0)
615 for idx in selections:
616 self.Select(idx = idx, on = 1)
617
618
619
620
622 labels = []
623 for col_idx in self.GetColumnCount():
624 col = self.GetColumn(col = col_idx)
625 labels.append(col.GetText())
626 return labels
627
629 if self.__data is None:
630 return None
631
632 return self.__data[item_idx]
633
635
636 if self.__is_single_selection or only_one:
637 return self.GetFirstSelected()
638
639 items = []
640 idx = self.GetFirstSelected()
641 while idx != -1:
642 items.append(idx)
643 idx = self.GetNextSelected(idx)
644
645 return items
646
648
649 if self.__is_single_selection or only_one:
650 if self.__data is None:
651 return None
652 idx = self.GetFirstSelected()
653 if idx == -1:
654 return None
655 return self.__data[idx]
656
657 data = []
658 if self.__data is None:
659 return data
660 idx = self.GetFirstSelected()
661 while idx != -1:
662 data.append(self.__data[idx])
663 idx = self.GetNextSelected(idx)
664
665 return data
666
668 self.Select(idx = self.GetFirstSelected(), on = 0)
669
670
671
672 if __name__ == '__main__':
673
674 from Gnumed.pycommon import gmI18N
675 gmI18N.activate_locale()
676 gmI18N.install_domain()
677
678
680 app = wx.PyWidgetTester(size = (400, 500))
681 dlg = wx.MultiChoiceDialog (
682 parent = None,
683 message = 'test message',
684 caption = 'test caption',
685 choices = ['a', 'b', 'c', 'd', 'e']
686 )
687 dlg.ShowModal()
688 sels = dlg.GetSelections()
689 print "selected:"
690 for sel in sels:
691 print sel
692
694
695 def edit(argument):
696 print "editor called with:"
697 print argument
698
699 def refresh(lctrl):
700 choices = ['a', 'b', 'c']
701 lctrl.set_string_items(choices)
702
703 app = wx.PyWidgetTester(size = (200, 50))
704 chosen = get_choices_from_list (
705
706 caption = 'select health issues',
707
708
709 columns = ['issue'],
710 refresh_callback = refresh
711
712 )
713 print "chosen:"
714 print chosen
715
716 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
717 test_get_choices_from_list()
718
719
720
721
722