WxPython - trigger checkbox event while setting its value in the code

北慕城南 提交于 2019-12-10 18:43:40

问题


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.


回答1:


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)



回答2:


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.




回答3:


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 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.



来源:https://stackoverflow.com/questions/9765718/wxpython-trigger-checkbox-event-while-setting-its-value-in-the-code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!