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.35 $"
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):
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.can_return_empty = False
107
110
113
118
121
124
127
128
129
131 self._BTN_ok.Enable(True)
132 self._BTN_ok.SetDefault()
133 if self.edit_callback is not None:
134 self._BTN_edit.Enable(True)
135 if self.delete_callback is not None:
136 self._BTN_delete.Enable(True)
137
139 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
140 if not self.can_return_empty:
141 self._BTN_ok.Enable(False)
142 self._BTN_cancel.SetDefault()
143 self._BTN_edit.Enable(False)
144 self._BTN_delete.Enable(False)
145
153
163
176
177
178
180 return self.__new_callback
181
183 if callback is not None:
184 if self.refresh_callback is None:
185 raise ValueError('refresh callback must be set before new callback can be set')
186 if not callable(callback):
187 raise ValueError('<new> callback is not a callable: %s' % callback)
188 self.__new_callback = callback
189
190 if callback is None:
191 self._BTN_new.Enable(False)
192 self._BTN_new.Hide()
193 else:
194 self._BTN_new.Enable(True)
195 self._BTN_new.Show()
196
197 new_callback = property(_get_new_callback, _set_new_callback)
198
200 return self.__edit_callback
201
203 if callback is not None:
204 if self.refresh_callback is None:
205 raise ValueError('refresh callback must be set before edit callback can be set')
206 if not callable(callback):
207 raise ValueError('<edit> callback is not a callable: %s' % callback)
208 self.__edit_callback = callback
209
210 if callback is None:
211 self._BTN_edit.Enable(False)
212 self._BTN_edit.Hide()
213 else:
214 self._BTN_edit.Enable(True)
215 self._BTN_edit.Show()
216
217 edit_callback = property(_get_edit_callback, _set_edit_callback)
218
220 return self.__delete_callback
221
223 if callback is not None:
224 if self.refresh_callback is None:
225 raise ValueError('refresh callback must be set before delete callback can be set')
226 if not callable(callback):
227 raise ValueError('<delete> callback is not a callable: %s' % callback)
228 self.__delete_callback = callback
229
230 if callback is None:
231 self._BTN_delete.Enable(False)
232 self._BTN_delete.Hide()
233 else:
234 self._BTN_delete.Enable(True)
235 self._BTN_delete.Show()
236
237 delete_callback = property(_get_delete_callback, _set_delete_callback)
238
240 return self.__refresh_callback
241
243 if callback is not None:
244 if not callable(callback):
245 raise ValueError('<refresh> callback is not a callable: %s' % callback)
246 self.__refresh_callback = callback
247 if callback is not None:
248 wx.CallAfter(self.__refresh_callback, lctrl = self._LCTRL_items)
249
250 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
251
253 """A panel holding a generic multi-column list and action buttions."""
254
256
257 try:
258 msg = kwargs['msg']
259 del kwargs['msg']
260 except KeyError: msg = None
261
262 wxgGenericListManagerPnl.wxgGenericListManagerPnl.__init__(self, *args, **kwargs)
263
264 if msg is None:
265 self._LBL_message.Hide()
266 else:
267 self._LBL_message.SetLabel(msg)
268
269
270 self.__new_callback = None
271 self.edit_callback = None
272 self.delete_callback = None
273 self.refresh_callback = None
274
275
276
279
281 self._LCTRL_items.set_string_items(items = items)
282 self._LCTRL_items.set_column_widths()
283
284 if (items is None) or (len(items) == 0):
285 self._BTN_edit.Enable(False)
286 self._BTN_remove.Enable(False)
287 else:
288 self._LCTRL_items.Select(0)
289
292
295
298
299
300
302 if self.edit_callback is not None:
303 self._BTN_edit.Enable(True)
304 if self.delete_callback is not None:
305 self._BTN_remove.Enable(True)
306
308 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
309 self._BTN_edit.Enable(False)
310 self._BTN_remove.Enable(False)
311
318
328
338
339
340
342 return self.__new_callback
343
345 self.__new_callback = callback
346 self._BTN_add.Enable(callback is not None)
347
348 new_callback = property(_get_new_callback, _set_new_callback)
349
351
353
354 try:
355 kwargs['style'] = kwargs['style'] | wx.LC_REPORT
356 except KeyError:
357 kwargs['style'] = wx.LC_REPORT
358
359 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL)
360
361 wx.ListCtrl.__init__(self, *args, **kwargs)
362 listmixins.ListCtrlAutoWidthMixin.__init__(self)
363
364 self.__widths = None
365 self.__data = None
366
367
368
370 """(Re)define the columns.
371
372 Note that this will (have to) delete the items.
373 """
374 self.ClearAll()
375 if columns is None:
376 return
377 for idx in range(len(columns)):
378 self.InsertColumn(idx, columns[idx])
379
381 """Set the column width policy.
382
383 widths = None:
384 use previous policy if any or default policy
385 widths != None:
386 use this policy and remember it for later calls
387
388 This means there is no way to *revert* to the default policy :-(
389 """
390
391 if widths is not None:
392 self.__widths = widths
393 for idx in range(len(self.__widths)):
394 self.SetColumnWidth(col = idx, width = self.__widths[idx])
395 return
396
397
398 if self.__widths is not None:
399 for idx in range(len(self.__widths)):
400 self.SetColumnWidth(col = idx, width = self.__widths[idx])
401 return
402
403
404 if self.GetItemCount() == 0:
405 width_type = wx.LIST_AUTOSIZE_USEHEADER
406 else:
407 width_type = wx.LIST_AUTOSIZE
408 for idx in range(self.GetColumnCount()):
409 self.SetColumnWidth(col = idx, width = width_type)
410
412 """All item members must be unicode()able or None."""
413
414 self.DeleteAllItems()
415 self.__data = items
416
417 if items is None:
418 return
419
420 for item in items:
421 try:
422 item[0]
423 if not isinstance(item, basestring):
424 is_numerically_iterable = True
425 else:
426 is_numerically_iterable = False
427 except TypeError:
428 is_numerically_iterable = False
429
430 if is_numerically_iterable:
431
432
433 col_val = unicode(item[0])
434 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
435 for col_idx in range(1, min(self.GetColumnCount(), len(item))):
436 col_val = unicode(item[col_idx])
437 self.SetStringItem(index = row_num, col = col_idx, label = col_val)
438 else:
439
440 col_val = unicode(item)
441 row_num = self.InsertStringItem(index = sys.maxint, label = col_val)
442
444 """<data must be a list corresponding to the item indices>"""
445 self.__data = data
446
448 self.Select(0, on = 0)
449 for idx in selections:
450 self.Select(idx = idx, on = 1)
451
452
453
455 labels = []
456 for col_idx in self.GetColumnCount():
457 col = self.GetColumn(col = col_idx)
458 labels.append(col.GetText())
459 return labels
460
462 if self.__data is None:
463 return None
464
465 return self.__data[item_idx]
466
468
469 if self.__is_single_selection or only_one:
470 return self.GetFirstSelected()
471
472 items = []
473 idx = self.GetFirstSelected()
474 while idx != -1:
475 items.append(idx)
476 idx = self.GetNextSelected(idx)
477
478 return items
479
481
482 if self.__is_single_selection or only_one:
483 if self.__data is None:
484 return None
485 idx = self.GetFirstSelected()
486 if idx == -1:
487 return None
488 return self.__data[idx]
489
490 data = []
491 if self.__data is None:
492 return data
493 idx = self.GetFirstSelected()
494 while idx != -1:
495 data.append(self.__data[idx])
496 idx = self.GetNextSelected(idx)
497
498 return data
499
501 self.Select(idx = self.GetFirstSelected(), on = 0)
502
503
504
505 if __name__ == '__main__':
506
507 from Gnumed.pycommon import gmI18N
508 gmI18N.activate_locale()
509 gmI18N.install_domain()
510
511
513 app = wx.PyWidgetTester(size = (400, 500))
514 dlg = wx.MultiChoiceDialog (
515 parent = None,
516 message = 'test message',
517 caption = 'test caption',
518 choices = ['a', 'b', 'c', 'd', 'e']
519 )
520 dlg.ShowModal()
521 sels = dlg.GetSelections()
522 print "selected:"
523 for sel in sels:
524 print sel
525
527
528 def edit(argument):
529 print "editor called with:"
530 print argument
531
532 def refresh(lctrl):
533 choices = ['a', 'b', 'c']
534 lctrl.set_string_items(choices)
535
536 app = wx.PyWidgetTester(size = (200, 50))
537 chosen = get_choices_from_list (
538
539 caption = 'select health issues',
540
541
542 columns = ['issue'],
543 refresh_callback = refresh
544
545 )
546 print "chosen:"
547 print chosen
548
549 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
550 test_get_choices_from_list()
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
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