Reputation: 2217
wxPython and (grouped) radio-button issue:
I have three radio buttons that are bound in a group, I see this gives me a choice of radio buttons A, B or C, where one is always selected -- I appreciate that this is the nature of the wx.RB_GROUP style;
Is it possible to deselect all buttons as in A=B=C=False by clicking on the radio buttons only? I have streamlined code (below) where the Reset button does this function, but ideally I'd just like to deselect in the GUI.
import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Tutorial", size=(300,250))
panel = wx.Panel(self, wx.ID_ANY)
self.radio1 = wx.RadioButton(panel, label="A", pos=(20,40), style=wx.RB_GROUP)
self.radio2 = wx.RadioButton(panel, label="B", pos=(20,70))
self.radio3 = wx.RadioButton(panel, label="C", pos=(20,100))
btn = wx.Button(panel, label="Check", pos=(20,140))
rst = wx.Button(panel, label="Reset", pos=(20,170))
btn.Bind(wx.EVT_BUTTON, self.onBtn)
rst.Bind(wx.EVT_BUTTON, self.onRst)
def onBtn(self, event):
print "A = ", self.radio1.GetValue()
print "B = ", self.radio2.GetValue()
print "C = ", self.radio3.GetValue()
print "\n\n"
def onRst(self, event):
self.radio1.SetValue( 0 )
self.radio2.SetValue( 0 )
self.radio3.SetValue( 0 )
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()
Upvotes: 1
Views: 5260
Reputation: 2180
This works to de-select a RadioButton like it is a checkbox:
import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Tutorial", size=(300,250))
panel = wx.Panel(self, wx.ID_ANY)
self.radio1 = wx.RadioButton(panel, label="A", name='radio1', pos=(20,40), style=wx.RB_GROUP)
self.radio2 = wx.RadioButton(panel, label="B", name='radio2', pos=(20,70))
self.radio3 = wx.RadioButton(panel, label="C", name='radio3', pos=(20,100))
self.radio1.Bind(wx.EVT_LEFT_DOWN, self.OnRB)
self.radio2.Bind(wx.EVT_LEFT_DOWN, self.OnRB)
self.radio3.Bind(wx.EVT_LEFT_DOWN, self.OnRB)
btn = wx.Button(panel, label="Check", pos=(20,140))
rst = wx.Button(panel, label="Reset", pos=(20,170))
btn.Bind(wx.EVT_BUTTON, self.onBtn)
rst.Bind(wx.EVT_BUTTON, self.onRst)
def OnRB(self, event):
event.GetEventObject().SetValue(not event.GetEventObject().GetValue())
def onBtn(self, event):
print "A = ", self.radio1.GetValue()
print "B = ", self.radio2.GetValue()
print "C = ", self.radio3.GetValue()
print "\n\n"
def onRst(self, event):
self.radio1.SetValue( 0 )
self.radio2.SetValue( 0 )
self.radio3.SetValue( 0 )
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()
Notice that all I added to your code was:
self.radio1.Bind(wx.EVT_LEFT_DOWN, self.OnRB)
self.radio2.Bind(wx.EVT_LEFT_DOWN, self.OnRB)
self.radio3.Bind(wx.EVT_LEFT_DOWN, self.OnRB)
def OnRB(self, event):
event.GetEventObject().SetValue(not event.GetEventObject().GetValue())
Upvotes: 1
Reputation: 3113
So far as I can tell this is not natively supported in wxPython. Furthermore, this is not the expected behaviour of radio buttons in any UI so my first thought is that your interface design is incorrect. Have you considered adding a further "None" radio button which is logically equivalent to none of your radio buttons being selected? Why not?
However, I did try to hack you up a solution but to no success. In fact, given the difficulty I faced I highly suspect that even if it is possible to program this behaviour you're still better off finding an alternative.
My first idea was to overwrite how wx.RadioButton
handles being selected by creating a custom wx.RadioButton
. Here are some of the problems I found with that approach:
wx.EVT_RADIOBUTTON
is only caught after the system has already changed the value of the radio button so I cannot actually overwrite the behaviourwx.EVT_RADIOBUTTON
(when no id
is specified) at different times so it's impossible to predict whether the system has further changes queued which might overwrite any changes you make to the button's value.wx.EVT_RADIOBUTTON
is not generated if the radio button is already selectedwx.EVT_LEFT_DOWN
and wx.EVT_LEFT_UP
events, neither of which fully describes how the radio button knows it has been clickedI then moved on to a custom wx.RadioBox
but I quickly realized that there is no method to completely deselect the buttons inside the radio box, meaning it is not an acceptable choice either.
I suppose you could write some kind of custom wx.Panel
containing multiple wx.CheckBox
objects and try to enforce that only a maximum of one box at a time is selected but you run into the problem again that this is not how users expect check boxes to work. I have seen this behaviour before but every time it hit me as strange and I cannot recommend it. Also it will require considerable effort to develop.
The closest solution I can find to what you're asking is a custom wx.RadioButton
that deselects when double clicked. The problem here is that user's don't expect double clicking to deselect a radio button, so you now need to think about how you will teach them this.
class DeselectableRB(wx.RadioButton):
def __init__(self, parent, label, pos=None, style=0):
#do the default init
wx.RadioButton.__init__(self, parent, label=label, pos=pos, style=style)
#catch double clicks
self.Bind(wx.EVT_LEFT_DCLICK, self.onDClick)
def onDClick(self, event):
self.SetValue(0)
However, I still strongly think that an additional wx.RadioButton
with the label "None" is your best solution as it requires minimal effort and it conforms to user's expectations about how radio buttons should work (making it more intuitive).
Upvotes: 5