WxPython - fire checkbox event when setting its value in code
Consider the following piece of code:
import wx
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.cb1 = wx.CheckBox(self, -1, "CheckBox 1")
self.cb2 = wx.CheckBox(self, -1, "CheckBox 2")
self.cb3 = wx.CheckBox(self, -1, "CheckBox 3")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.cb1, 0, wx.ADJUST_MINSIZE, 0)
sizer.Add(self.cb2, 0, wx.ADJUST_MINSIZE, 0)
sizer.Add(self.cb3, 0, wx.ADJUST_MINSIZE, 0)
self.SetSizer(sizer)
self.Layout()
self.Bind(wx.EVT_CHECKBOX, self.OnCb1, self.cb1)
self.Bind(wx.EVT_CHECKBOX, self.OnCb2, self.cb2)
def OnCb1(self, evt):
self.cb2.SetValue(evt.IsChecked())
def OnCb2(self, evt):
self.cb3.SetValue(evt.IsChecked())
if __name__ == "__main__":
app = wx.PySimpleApp(0)
frame = MyFrame(None, -1, "")
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
Here I have 3 checkboxes tied together, so it cb2
checks when cb1
it does and cb3
gets the checkbox if cb2
it does. However, when I set the value cb2
in the subroutine OnCb1
, the checkbox event is cb2
not fired and the checkbox is cb3
not checked. So I would like to find a way to trigger somehow the event cb2
manually so that all 3 fields are checked at once when validating only cb1
. I would be very grateful if someone can tell me.
source to share
Use wx.PostEvent ... like so:
class launcherWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent=None, title='New Window')
#now add the main body, start with a panel
panel = wx.Panel(self)
#instantiate a new dropdown
self.productDropDown = wx.ComboBox(panel, size=wx.DefaultSize, style = wx.CB_READONLY)
#get the products and product subtypes
self.productDict = self.getProductsAndSubtypes()
#setup subtypes first, just in case, since onProductSelection will reference this
self.productSubtypeDropDown = wx.ComboBox(panel, size=wx.DefaultSize, style = wx.CB_READONLY)
#add products
for product in self.productDict.keys():
self.productDropDown.Append(product)
#bind selection event
self.productDropDown.Bind(wx.EVT_COMBOBOX, self.onProductSelection)
#set default selection
self.productDropDown.SetSelection(0)
#pretend that we clicked the product selection, so it event gets called
wx.PostEvent(self.productDropDown, wx.CommandEvent(wx.wxEVT_COMMAND_COMBOBOX_SELECTED))
#now add the dropdown to a sizer, set the sizer for the panel, fit the panel, etc...
def onProductSelection(self, event):
productSelected = self.productDropDown.GetStringSelection()
productSubtypes = self.productDict[productSelected]
#clear any existing product subtypes, since each product may have different ones
self.productSubtypeDropDown.Clear()
for productSubtype in productSubtypes:
self.productSubtypeDropDown.Append(productSubtype)
#select the first item by default
self.productSubtypeDropDown.SetSelection(0)
source to share
I couldn't accept the nmz787 code directly and had to hack a bit, but I finally got it for
- changing the state of the checkbox and
- having a checkbox receives the event
as if the user clicked on it.
I thought I'd post my code in case others get stuck trying to get this to work. Since it only depended on the checkbox and not on which one was calling it, I accounted for it as an independent "top-level" function, not as a method for any class.
def toggleCheckBox(cb):
evt = wx.CommandEvent(commandType=wx.EVT_CHECKBOX.typeId)
evt.SetEventObject(cb)
cb.SetValue( not cb.GetValue())
wx.PostEvent(cb, evt)
I have no idea why the CommandEvent constructor needs the keyword, or if there is a smarter, more reliable way to get the required typeId, but it worked for me.
source to share
I have no experience with wxPython, so I cannot give you a specific example, but I know that setting a value programmatically will not trigger command events for widgets. My guess is that you will have to manually place the event for cb2 after you set its value. You can view a similar question here: wxPython: Calling an Event Manually
What I can suggest is to subclass wx.CheckBox and create a method SetValueWithEvent()
or similar method that will call SetValue and post the event wx. EVT_CHECKBOX
.
PyQt has similar situations where signals may or may not be thrown when programmatically defining values โโin widgets. They sometimes give you more than one signal that you can listen to to place anyway. Unfortunately, based only on my limited involvement with the wxPython examples, I think it is much more primitive and slightly less pythonic. So you seem to have to do things yourself a little more often.
source to share