1import types
2import tkinter
3import Pmw
4import collections
5
6class RadioSelect(Pmw.MegaWidget):
7    # A collection of several buttons.  In single mode, only one
8    # button may be selected.  In multiple mode, any number of buttons
9    # may be selected.
10
11    def __init__(self, parent = None, **kw):
12
13        # Define the megawidget options.
14        INITOPT = Pmw.INITOPT
15        optiondefs = (
16            ('buttontype',    'button',      INITOPT),
17            ('command',       None,          None),
18            ('labelmargin',   0,             INITOPT),
19            ('labelpos',      None,          INITOPT),
20            ('orient',       'horizontal',   INITOPT),
21            ('padx',          5,             INITOPT),
22            ('pady',          5,             INITOPT),
23            ('selectmode',    'single',      INITOPT),
24        )
25        self.defineoptions(kw, optiondefs, dynamicGroups = ('Button',))
26
27        # Initialise the base class (after defining the options).
28        Pmw.MegaWidget.__init__(self, parent)
29
30        # Create the components.
31        interior = self.interior()
32        if self['labelpos'] is None:
33            self._radioSelectFrame = self._hull
34        else:
35            self._radioSelectFrame = self.createcomponent('frame',
36                    (), None,
37                    tkinter.Frame, (interior,))
38            self._radioSelectFrame.grid(column=2, row=2, sticky='nsew')
39            interior.grid_columnconfigure(2, weight=1)
40            interior.grid_rowconfigure(2, weight=1)
41
42            self.createlabel(interior)
43
44        # Initialise instance variables.
45        self._buttonList = []
46        if self['selectmode'] == 'single':
47            self._singleSelect = 1
48        elif self['selectmode'] == 'multiple':
49            self._singleSelect = 0
50        else:
51            raise ValueError('bad selectmode option "' + \
52                    self['selectmode'] + '": should be single or multiple')
53
54        if self['buttontype'] == 'button':
55            self.buttonClass = tkinter.Button
56        elif self['buttontype'] == 'radiobutton':
57            self._singleSelect = 1
58            self.var = tkinter.StringVar()
59            self.buttonClass = tkinter.Radiobutton
60        elif self['buttontype'] == 'checkbutton':
61            self._singleSelect = 0
62            self.buttonClass = tkinter.Checkbutton
63        else:
64            raise ValueError('bad buttontype option "' + \
65                    self['buttontype'] + \
66                    '": should be button, radiobutton or checkbutton')
67
68        if self._singleSelect:
69            self.selection = None
70        else:
71            self.selection = []
72
73        if self['orient'] not in ('horizontal', 'vertical'):
74            raise ValueError('bad orient option ' + repr(self['orient']) + \
75                ': must be either \'horizontal\' or \'vertical\'')
76
77        # Check keywords and initialise options.
78        self.initialiseoptions()
79
80    def getcurselection(self):
81        if self._singleSelect:
82            return self.selection
83        else:
84            return tuple(self.selection)
85
86    def getvalue(self):
87        return self.getcurselection()
88
89    def setvalue(self, textOrList):
90        if self._singleSelect:
91            self.__setSingleValue(textOrList)
92        else:
93            # Multiple selections
94            oldselection = self.selection
95            self.selection = textOrList
96            for button in self._buttonList:
97                if button in oldselection:
98                    if button not in self.selection:
99                        # button is currently selected but should not be
100                        widget = self.component(button)
101                        if self['buttontype'] == 'checkbutton':
102                            widget.deselect()
103                        else:  # Button
104                            widget.configure(relief='raised')
105                else:
106                    if button in self.selection:
107                        # button is not currently selected but should be
108                        widget = self.component(button)
109                        if self['buttontype'] == 'checkbutton':
110                            widget.select()
111                        else:  # Button
112                            widget.configure(relief='sunken')
113
114    def numbuttons(self):
115        return len(self._buttonList)
116
117    def index(self, index):
118        # Return the integer index of the button with the given index.
119
120        listLength = len(self._buttonList)
121        if type(index) == int:
122            if index < listLength:
123                return index
124            else:
125                raise ValueError('index "%s" is out of range' % index)
126        elif index is Pmw.END:
127            if listLength > 0:
128                return listLength - 1
129            else:
130                raise ValueError('RadioSelect has no buttons')
131        else:
132            for count in range(listLength):
133                name = self._buttonList[count]
134                if index == name:
135                    return count
136            validValues = 'a name, a number or Pmw.END'
137            raise ValueError('bad index "%s": must be %s' % (index, validValues))
138
139    def button(self, buttonIndex):
140        name = self._buttonList[self.index(buttonIndex)]
141        return self.component(name)
142
143    def add(self, componentName, **kw):
144        if componentName in self._buttonList:
145            raise ValueError('button "%s" already exists' % componentName)
146
147        kw['command'] = \
148                lambda self=self, name=componentName: self.invoke(name)
149        if 'text' not in kw:
150            kw['text'] = componentName
151
152        if self['buttontype'] == 'radiobutton':
153            if 'anchor' not in kw:
154                kw['anchor'] = 'w'
155            if 'variable' not in kw:
156                kw['variable'] = self.var
157            if 'value' not in kw:
158                kw['value'] = kw['text']
159        elif self['buttontype'] == 'checkbutton':
160            if 'anchor' not in kw:
161                kw['anchor'] = 'w'
162
163        button = self.createcomponent(*(componentName,
164                (), 'Button',
165                self.buttonClass, (self._radioSelectFrame,)), **kw)
166
167        if self['orient'] == 'horizontal':
168            self._radioSelectFrame.grid_rowconfigure(0, weight=1)
169            col = len(self._buttonList)
170            button.grid(column=col, row=0, padx = self['padx'],
171                    pady = self['pady'], sticky='nsew')
172            self._radioSelectFrame.grid_columnconfigure(col, weight=1)
173        else:
174            self._radioSelectFrame.grid_columnconfigure(0, weight=1)
175            row = len(self._buttonList)
176            button.grid(column=0, row=row, padx = self['padx'],
177                    pady = self['pady'], sticky='ew')
178            self._radioSelectFrame.grid_rowconfigure(row, weight=1)
179
180        self._buttonList.append(componentName)
181        return button
182
183    def deleteall(self):
184        for name in self._buttonList:
185            self.destroycomponent(name)
186        self._buttonList = []
187        if self._singleSelect:
188            self.selection = None
189        else:
190            self.selection = []
191
192    def __setSingleValue(self, value):
193        self.selection = value
194        if self['buttontype'] == 'radiobutton':
195            widget = self.component(value)
196            widget.select()
197        else:  # Button
198            for button in self._buttonList:
199                widget = self.component(button)
200                if button == value:
201                    widget.configure(relief='sunken')
202                else:
203                    widget.configure(relief='raised')
204
205    def invoke(self, index):
206        index = self.index(index)
207        name = self._buttonList[index]
208
209        if self._singleSelect:
210            self.__setSingleValue(name)
211            command = self['command']
212            if isinstance(command, collections.Callable):
213                return command(name)
214        else:
215            # Multiple selections
216            widget = self.component(name)
217            if name in self.selection:
218                if self['buttontype'] == 'checkbutton':
219                    widget.deselect()
220                else:
221                    widget.configure(relief='raised')
222                self.selection.remove(name)
223                state = 0
224            else:
225                if self['buttontype'] == 'checkbutton':
226                    widget.select()
227                else:
228                    widget.configure(relief='sunken')
229                self.selection.append(name)
230                state = 1
231
232            command = self['command']
233            if isinstance(command, collections.Callable):
234                return command(name, state)
235