#!/usr/bin/env python # # Copyright 2008 by Timothy (rhacer) Grant # This code is released under the terms of the GNU Public License v2.0 # # $LastChangedDate$ __version__ = "$Revision: 46 $" import wx class RadioChoice(wx.Choice): """ RadioChoice is a Choice class that acts like a Radio button. If you include several RadioChoice choice widgets with the same choice lists, the other Choice widgets will have their content adjusted based on the selection in the first Choice. EXAMPLE if you have three Choice widgets with choices of "Red", "Green", "Blue" and you select "Green" on the first widget, the other two Choice widgets will only offer "Red" and "Blue" In order to avoid any sort of strange deadlock errors the first item on the list must be the "null" choice something to indicate this is NOT really selected (e.g., "Unassigned" or "Unset") All widgets in a RadioChoice group must be members of the same container. Multiple Groups of Radio choices may be created as long as they all use a separate group. EXAMPLE choices = ['None', 'Red', 'Green', 'Blue'] myChoice1 = RadioChoice(self, group=RC_GROUP1, choices=choices) myChoice2 = RadioChoice(self, group=RC_GROUP1, choices=choices) myChoice2 = RadioChoice(self, group=RC_GROUP1, choices=choices) Calling ResetItems() on any widget in the group will reset the available options on ALL of the widgets in the group. """ def __init__(self, parent, group='DEFAULT_GROUP', *args, **kwargs): wx.Choice.__init__(self, parent, *args, **kwargs) self.parent = parent self.group = group self.static_choices = kwargs['choices'] self.null_choice = self.static_choices[0] self.siblings = self._get_sibling_controls() for ctrl in self.siblings: ctrl.siblings = self.siblings if self.static_choices <> ctrl.static_choices: raise ValueError, "Choice lists must be identical" self.SetStringSelection(self.null_choice) self.Bind(wx.EVT_CHOICE, self._adjust_sibling_values) def ResetItems(self, choices): for ctrl in self.siblings: c = self if ctrl == self else ctrl c.static_choices = choices c.null_choice = c.static_choices[0] wx.Choice.SetItems(c, choices) wx.Choice.SetSelection(c, 0) def _get_sibling_controls(self): siblings = [] for ctrl in self.parent.GetChildren(): if isinstance(ctrl, RadioChoice) and ctrl.group == self.group: siblings.append(ctrl) return siblings def _adjust_sibling_values(self, event): choices = self.static_choices[:] cur_selections = [] for ctrl in self.siblings: v = ctrl.GetStringSelection() cur_selections.append(v) if v <> self.null_choice: if v in choices: choices.remove(ctrl.GetStringSelection()) for i, ctrl in enumerate(self.siblings): t = choices[:] if cur_selections[i] <> self.null_choice: t.insert(self.static_choices.index(cur_selections[i]), cur_selections[i]) ctrl.SetItems(t) ctrl.SetStringSelection(cur_selections[i]) event.Skip() class MainFrame(wx.Frame): def __init__(self, parent, *args, **kwargs): wx.Frame.__init__(self, parent, *args, **kwargs) self.parent = parent self.choices1 = ['Choose Colour 1', 'Orange', 'Indigo', 'Violet', 'Yellow'] self.choices2 = ['Choose Colour 2', 'Red', 'Blue', 'Green'] self.choices3 = ['Choose Treat', 'Chocolate', 'Peanut Butter'] self.choices4 = ['Choose Treat', 'Phish Food', 'Chunky Monkey'] self.cur_choices = self.choices3 group1_bs = wx.StaticBox(self, label="First Group") group1_sbs = wx.StaticBoxSizer(group1_bs, wx.VERTICAL) self.color1_choices = [] for i in range(len(self.choices1) - 1): # If group is not specified, then the default group is used ctrl = RadioChoice(self, choices=self.choices1) self.color1_choices.append(ctrl) group1_sbs.Add(ctrl, flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, border=5) group2_bs = wx.StaticBox(self, label="Second Group") group2_sbs = wx.StaticBoxSizer(group2_bs, wx.VERTICAL) self.color2_choices = [] for i in range(len(self.choices2) - 1): # Specify a group name ctrl = RadioChoice(self, group='GROUP2', choices=self.choices2) self.color2_choices.append(ctrl) group2_sbs.Add(ctrl, flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, border=5) group3_bs = wx.StaticBox(self, label="Third Group") group3_sbs = wx.StaticBoxSizer(group3_bs, wx.VERTICAL) self.treat_choices = [] for i in range(len(self.choices3) - 1): # Specify a group name ctrl = RadioChoice(self, group='GROUP3', choices=self.choices3) self.treat_choices.append(ctrl) group3_sbs.Add(ctrl, flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, border=5) self.btn = wx.Button(self, label='Change List') self.btn.Bind(wx.EVT_BUTTON, self.change_list) group3_sbs.Add(self.btn, flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, border=5) main_bs = wx.BoxSizer(wx.VERTICAL) main_bs.Add(group1_sbs, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) main_bs.Add(group2_sbs, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) main_bs.Add(group3_sbs, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5) self.SetSizerAndFit(main_bs) def change_list(self, event): if self.cur_choices == self.choices3: self.cur_choices = self.choices4 self.treat_choices[0].ResetItems(self.choices4) else: self.cur_choices = self.choices3 self.treat_choices[0].ResetItems(self.choices3) class App(wx.App): def OnInit(self): self.frame = MainFrame(None, title="Radio Choice Demo") self.frame.Show(True) self.SetTopWindow(self.frame) return True def main(): app = App(0) # wx.lib.inspection.InspectionTool().Show() app.MainLoop() if __name__ == '__main__': main()