简体   繁体   中英

WxPython - trigger checkbox event while setting its value in the 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 bound together, so cb2 gets checked when cb1 does and cb3 gets checked when cb2 does. However, when I set the value of cb2 in OnCb1 routine, the cb2 checkbox event is not triggered, and cb3 checkbox remains unchecked. So I'd like to find a way to trigger somehow cb2 event manually to check all 3 boxes at once when checking only cb1 . I'd be very grateful if anyone gives me a hint.

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's 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)

I couldn't adopt nmz787's code directly, and had to hack around a bit, but I finally got it working for

  • changing the state of a checkbox and
  • having the checkbox receive the event,

just as if the user clicked on it.

I thought I'd post my code in case others are stuck trying to get this to work too. Since it only depended on the checkbox and not on which control was calling it I factored it out as an independent "top-level" function, not as a method on 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 required a keyword, or if there's a more sensible, robust way to get the typeId required, but this worked for me.

I'm not experienced with wxPython so I can't give you a specific example, but I do know that setting the value programmatically will not trigger command events for the widgets. My assumption is that you will have to manually post the event for cb2 after you set its value. You can review a similar question here: wxPython: Calling an event manually

What I might suggest is to subclass the wx.CheckBox and create a SetValueWithEvent() or similar method that will both call SetValue, and post a wx. EVT_CHECKBOX wx. EVT_CHECKBOX event.

PyQt has similar situations where signals may or may not be emitted when programmatically setting values on a widget. They will sometimes give you more than one signal that you can listen for to accomodate either way. Unfortunately, based only on my limited exposure to wxPython examples, I think its a lot more primitive, and a bit less pythonic. So you seem to have to do things yourself a little more often.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM