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
16
17 __version__ = "$Revision: 1.37 $"
18 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
19 __license__ = "GPL"
20
21
22 import sys, types
23
24
25 import wx
26 import wx.lib.mixins.listctrl as listmixins
27
28
29 if __name__ == '__main__':
30 sys.path.insert(0, '../../')
31 from Gnumed.business import gmPerson
32 from Gnumed.pycommon import gmTools, gmDispatcher
33 from Gnumed.wxpython import gmGuiHelpers
34 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg, wxgGenericListManagerPnl
35
36
37 -def get_choices_from_list(parent=None, msg=None, caption=None, choices=None, selections=None, columns=None, data=None, edit_callback=None, new_callback=None, delete_callback=None, refresh_callback=None, single_selection=False, can_return_empty=False, ignore_OK_button=False):
83
85 """A dialog holding a list and a few buttons to act on the items."""
86
88
89 try:
90 msg = kwargs['msg']
91 del kwargs['msg']
92 except KeyError: msg = None
93
94 wxgGenericListSelectorDlg.wxgGenericListSelectorDlg.__init__(self, *args, **kwargs)
95
96 if msg is None:
97 self._LBL_message.Hide()
98 else:
99 self._LBL_message.SetLabel(msg)
100
101 self.refresh_callback = None
102 self.new_callback = None
103 self.edit_callback = None
104 self.delete_callback = None
105
106 self.ignore_OK_button = False
107
108 self.can_return_empty = False
109
112
115
120
123
126
129
130
131
133 if not self.__ignore_OK_button:
134 self._BTN_ok.SetDefault()
135 self._BTN_ok.Enable(True)
136
137 if self.edit_callback is not None:
138 self._BTN_edit.Enable(True)
139
140 if self.delete_callback is not None:
141 self._BTN_delete.Enable(True)
142
144 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
145 if not self.can_return_empty:
146 self._BTN_cancel.SetDefault()
147 self._BTN_ok.Enable(False)
148 self._BTN_edit.Enable(False)
149 self._BTN_delete.Enable(False)
150
158
168
181
182
183
192
193 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button)
194
196 return self.__new_callback
197
199 if callback is not None:
200 if self.refresh_callback is None:
201 raise ValueError('refresh callback must be set before new callback can be set')
202 if not callable(callback):
203 raise ValueError('<new> callback is not a callable: %s' % callback)
204 self.__new_callback = callback
205
206 if callback is None:
207 self._BTN_new.Enable(False)
208 self._BTN_new.Hide()
209 else:
210 self._BTN_new.Enable(True)
211 self._BTN_new.Show()
212
213 new_callback = property(_get_new_callback, _set_new_callback)
214
216 return self.__edit_callback
217
219 if callback is not None:
220 if self.refresh_callback is None:
221 raise ValueError('refresh callback must be set before edit callback can be set')
222 if not callable(callback):
223 raise ValueError('<edit> callback is not a callable: %s' % callback)
224 self.__edit_callback = callback
225
226 if callback is None:
227 self._BTN_edit.Enable(False)
228 self._BTN_edit.Hide()
229 else:
230 self._BTN_edit.Enable(True)
231 self._BTN_edit.Show()
232
233 edit_callback = property(_get_edit_callback, _set_edit_callback)
234
236 return self.__delete_callback
237
239 if callback is not None:
240 if self.refresh_callback is None:
241 raise ValueError('refresh callback must be set before delete callback can be set')
242 if not callable(callback):
243 raise ValueError('<delete> callback is not a callable: %s' % callback)
244 self.__delete_callback = callback
245
246 if callback is None:
247 self._BTN_delete.Enable(False)
248 self._BTN_delete.Hide()
249 else:
250 self._BTN_delete.Enable(True)
251 self._BTN_delete.Show()
252
253 delete_callback = property(_get_delete_callback, _set_delete_callback)
254
256 return self.__refresh_callback
257
261
263 if callback is not None:
264 if not callable(callback):
265 raise ValueError('<refresh> callback is not a callable: %s' % callback)
266 self.__refresh_callback = callback
267 if callback is not None:
268 wx.CallAfter(self._set_refresh_callback_helper)
269
270 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
271
273 """A panel holding a generic multi-column list and action buttions."""
274
276
277 try:
278 msg = kwargs['msg']
279 del kwargs['msg']
280 except KeyError: msg = None
281
282 wxgGenericListManagerPnl.wxgGenericListManagerPnl.__init__(self, *args, **kwargs)
283
284 if msg is None:
285 self._LBL_message.Hide()
286 else:
287 self._LBL_message.SetLabel(msg)
288
289
290 self.__new_callback = None
291 self.edit_callback = None
292 self.delete_callback = None
293 self.refresh_callback = None
294
295
296
299
301 self._LCTRL_items.set_string_items(items = items)
302 self._LCTRL_items.set_column_widths()
303
304 if (items is None) or (len(items) == 0):
305 self._BTN_edit.Enable(False)
306 self._BTN_remove.Enable(False)
307 else:
308 self._LCTRL_items.Select(0)
309
312
315
318
319
320
322 if self.edit_callback is not None:
323 self._BTN_edit.Enable(True)
324 if self.delete_callback is not None:
325 self._BTN_remove.Enable(True)
326
328 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
329 self._BTN_edit.Enable(False)
330 self._BTN_remove.Enable(False)
331
338
348
358
359
360
362 return self.__new_callback
363
365 self.__new_callback = callback
366 self._BTN_add.Enable(callback is not None)
367
368 new_callback = property(_get_new_callback, _set_new_callback)
369
371
373
374 try:
375 kwargs['style'] = kwargs['style'] | wx.LC_REPORT
376 except KeyError:
377 kwargs['style'] = wx.LC_REPORT
378
379 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL)
380
381 wx.ListCtrl.__init__(self, *args, **kwargs)
382 listmixins.ListCtrlAutoWidthMixin.__init__(self)
383
384 self.__widths = None
385 self.__data = None
386
387
388
390 """(Re)define the columns.
391
392 Note that this will (have to) delete the items.
393 """
394 self.ClearAll()
395 if columns is None:
396 return
397 for idx in range(len(columns)):
398 self.InsertColumn(idx, columns[idx])
399
401 """Set the column width policy.
402
403 widths = None:
404 use previous policy if any or default policy
405 widths != None:
406 use this policy and remember it for later calls
407
408 This means there is no way to *revert* to the default policy :-(
409 """
410
411 if widths is not None:
412 self.__widths = widths
413 for idx in range(len(self.__widths)):
414 self.SetColumnWidth(col = idx, width = self.__widths[idx])
415 return
416
417
418 if self.__widths is not None:
419 for idx in range(len(self.__widths)):
420 self.SetColumnWidth(col = idx, width = self.__widths[idx])
421 return
422
423
424 if self.GetItemCount() == 0:
425 width_type = wx.LIST_AUTOSIZE_USEHEADER
426 else:
427 width_type = wx.LIST_AUTOSIZE
428 for idx in range(self.GetColumnCount()):
429 self.SetColumnWidth(col = idx, width = width_type)
430
432 """All item members must be unicode()able or None."""
433
434 self.DeleteAllItems()
435 self.__data = items
436
437 if items is None:
438 return
439
440 for item in items:
441 try:
442 item[0]
443 if not isinstance(item, basestring):
444 is_numerically_iterable = True
445 else:
446 is_numerically_iterable = False
447 except TypeError:
448 is_numerically_iterable = False
449
450 if is_numerically_iterable:
451
452
453 col_val = unicode(item[0])
454 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
455 for col_idx in range(1, min(self.GetColumnCount(), len(item))):
456 col_val = unicode(item[col_idx])
457 self.SetStringItem(index = row_num, col = col_idx, label = col_val)
458 else:
459
460 col_val = unicode(item)
461 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
462
464 """<data must be a list corresponding to the item indices>"""
465 self.__data = data
466
468 self.Select(0, on = 0)
469 for idx in selections:
470 self.Select(idx = idx, on = 1)
471
472
473
475 labels = []
476 for col_idx in self.GetColumnCount():
477 col = self.GetColumn(col = col_idx)
478 labels.append(col.GetText())
479 return labels
480
482 if self.__data is None:
483 return None
484
485 return self.__data[item_idx]
486
488
489 if self.__is_single_selection or only_one:
490 return self.GetFirstSelected()
491
492 items = []
493 idx = self.GetFirstSelected()
494 while idx != -1:
495 items.append(idx)
496 idx = self.GetNextSelected(idx)
497
498 return items
499
501
502 if self.__is_single_selection or only_one:
503 if self.__data is None:
504 return None
505 idx = self.GetFirstSelected()
506 if idx == -1:
507 return None
508 return self.__data[idx]
509
510 data = []
511 if self.__data is None:
512 return data
513 idx = self.GetFirstSelected()
514 while idx != -1:
515 data.append(self.__data[idx])
516 idx = self.GetNextSelected(idx)
517
518 return data
519
521 self.Select(idx = self.GetFirstSelected(), on = 0)
522
523
524
525 if __name__ == '__main__':
526
527 from Gnumed.pycommon import gmI18N
528 gmI18N.activate_locale()
529 gmI18N.install_domain()
530
531
533 app = wx.PyWidgetTester(size = (400, 500))
534 dlg = wx.MultiChoiceDialog (
535 parent = None,
536 message = 'test message',
537 caption = 'test caption',
538 choices = ['a', 'b', 'c', 'd', 'e']
539 )
540 dlg.ShowModal()
541 sels = dlg.GetSelections()
542 print "selected:"
543 for sel in sels:
544 print sel
545
547
548 def edit(argument):
549 print "editor called with:"
550 print argument
551
552 def refresh(lctrl):
553 choices = ['a', 'b', 'c']
554 lctrl.set_string_items(choices)
555
556 app = wx.PyWidgetTester(size = (200, 50))
557 chosen = get_choices_from_list (
558
559 caption = 'select health issues',
560
561
562 columns = ['issue'],
563 refresh_callback = refresh
564
565 )
566 print "chosen:"
567 print chosen
568
569 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
570 test_get_choices_from_list()
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703