1

I'm trying the code posted below as combotest.py on Ubuntu 14.04, MATE desktop, python 2.7, python-wxgtk2.8 2.8.12.1; the application is like this:

combotest

When I click on the combobox and change it directly, the corresponding event handler OnUpdateComboBox fires, and I get this printed on stdout:

OnUpdateComboBox <wx._controls.ComboBox; proxy of <Swig Object of type 'wxComboBox *' at 0x909caf0> > MyComboBox

I am aware that:

http://wxpython-users.1045709.n5.nabble.com/SetValue-on-spinctrl-doesn-t-send-update-event-td2289628.html

the general policy is that changes to a control's value made by the user generates events and changes made programatically do not. So you should plan on SetValue not sending the event,

I'm trying to have the choice changed - and the corresponding handler fired - in the combobox, when the button is clicked. And as recommended in WxPython - trigger checkbox event while setting its value in the code, I'm therefore trying to call wx.PostEvent() to force the call of the combobox event handler, once its selection has been programmatically changed by the button code.

When I click the button, I can see its handler BtnClickHandler does get called, and as expected, it changes the combobox selection to a new item - however, calling wx.PostEvent(), regardless if it is standalone or in wx.CallAfter(), simply never calls the OnUpdateComboBox.

So, how can I call the combobox event handler, when changing its selection programmatically from the button event handler? Or is this a bug?

EDIT: Turns out I can call the function directly, so in BtnClickHandler I can do:

self.OnUpdateComboBox(oevt)

... and then the handler will get called - but something just doesn't feel right about this...

Also, any ideas why self.combo_box.GetLastPosition() returns 2, even though there are 5 items in the combobox ?!

combotest.py

import wxversion
wxversion.select("2.8")
import wx, wx.html
import sys

class Frame(wx.Frame):
  def __init__(self, *args, **kwds):
    kwds["style"] = wx.DEFAULT_FRAME_STYLE
    wx.Frame.__init__(self, *args, **kwds)

    self.label = wx.StaticText(self, wx.ID_ANY, "Click the button to change combobox below: ")
    self.bt_main = wx.Button(self, label="Click ME")
    self.bt_main.Bind(wx.EVT_BUTTON, self.BtnClickHandler)
    self.combo_box = wx.ComboBox(self, wx.ID_ANY, choices=["AAA", "BBB", "CCC", "DDD", "EEE"], style=wx.CB_DROPDOWN | wx.CB_READONLY, name="MyComboBox")
    self.combo_box.SetSelection(0) # this does not call event
    wx.PostEvent(self.combo_box, wx.CommandEvent(wx.EVT_COMBOBOX.typeId)) #neither does this
    self.Bind(wx.EVT_COMBOBOX, self.OnUpdateComboBox, self.combo_box)

    sizer_vmain_app = wx.BoxSizer(wx.VERTICAL)
    sizer_vmain_app.Add(self.label, proportion=0, flag=wx.EXPAND, border=0)
    sizer_vmain_app.Add(self.bt_main, proportion=0, flag=0, border=0)
    sizer_vmain_app.Add(self.combo_box, proportion=0, flag=0, border=0)

    self.SetSizer(sizer_vmain_app)
    self.Layout()

  def OnUpdateComboBox(self, event):
    widget = event.GetEventObject()
    print("OnUpdateComboBox " + repr(widget) + " " + widget.GetName())

  def BtnClickHandler(self, event):
    print("BtnClickHandler " + repr(self.combo_box.GetLastPosition()))
    newsel = (self.combo_box.GetSelection() + 1) % (self.combo_box.GetLastPosition()+1) # GetLastPosition is 2, even with 5 items in box?!
    self.combo_box.SetSelection(newsel) # this does not call event
    oevt = wx.CommandEvent(commandType=wx.EVT_COMBOBOX.typeId)
    oevt.SetEventObject(self.combo_box)
    wx.PostEvent(self.combo_box, oevt) # does nothing
    wx.CallAfter(wx.PostEvent, self.combo_box, oevt) # does nothing

if __name__ == "__main__":
  app = wx.PySimpleApp(0)
  wx.InitAllImageHandlers()
  app_frame = Frame(None, wx.ID_ANY, "")
  app.SetTopWindow(app_frame)
  app_frame.Show()
  app.MainLoop()
Community
  • 1
  • 1
sdaau
  • 36,975
  • 46
  • 198
  • 278

2 Answers2

0

Please don't call the event handler function directly - especially like this. Better solution would be to create a function which will perform whatever will needs to be done when the value in the combo box changes and call this function from both handlers.

Also, it would be nice to know if everything works in wx-3.0/3.1.

Igor
  • 5,620
  • 11
  • 51
  • 103
0

Ok I figured out a solution. Bind directly to the combobox instead of the frame. Here is a working example (wx phoenix 3.03.xyz dev....)

class Frame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)

        self.label = wx.StaticText(self, wx.ID_ANY, "Click the button to change combobox below: ")
        self.bt_main = wx.Button(self, label="Click ME")
        self.bt_main.Bind(wx.EVT_BUTTON, self.BtnClickHandler)
        self.combo_box = wx.ComboBox(self, wx.ID_ANY, choices=["AAA", "BBB", "CCC", "DDD", "EEE"],
                                     style=wx.CB_DROPDOWN | wx.CB_READONLY, name="MyComboBox")
        # self.Bind(wx.EVT_COMBOBOX, self.OnUpdateComboBox, self.combo_box)
        # bind directly to the widget
        self.combo_box.Bind(wx.EVT_COMBOBOX, self.OnUpdateComboBox)

        self.combo_box.SetSelection(0) 
        combo_event = wx.CommandEvent(wx.EVT_COMBOBOX.typeId)
        combo_event.SetEventObject(self.combo_box)
        wx.PostEvent(self.combo_box, combo_event)


        sizer_vmain_app = wx.BoxSizer(wx.VERTICAL)
        sizer_vmain_app.Add(self.label, proportion=0, flag=wx.EXPAND, border=0)
        sizer_vmain_app.Add(self.bt_main, proportion=0, flag=0, border=0)
        sizer_vmain_app.Add(self.combo_box, proportion=0, flag=0, border=0)

        self.SetSizer(sizer_vmain_app)
        self.Layout()

    def OnUpdateComboBox(self, event):
        widget = event.GetEventObject()
        print("OnUpdateComboBox " + repr(widget) + " " + widget.GetName())

    def BtnClickHandler(self, event):
        print("BtnClickHandler " + repr(self.combo_box.GetLastPosition()))
        newsel = (self.combo_box.GetSelection() + 1) % (
        self.combo_box.GetLastPosition() + 1)  # GetLastPosition is 2, even with 5 items in box?!
        self.combo_box.SetSelection(newsel)  # this does not call event
        oevt = wx.CommandEvent(commandType=wx.EVT_COMBOBOX.typeId)
        oevt.SetEventObject(self.combo_box)
        wx.PostEvent(self.combo_box, oevt)  # does nothing
        wx.CallAfter(wx.PostEvent, self.combo_box, oevt)  # does nothing


if __name__ == "__main__":
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    app_frame = Frame(None, wx.ID_ANY, "")
    app.SetTopWindow(app_frame)
    app_frame.Show()
    app.MainLoop()

Side Note

self.combox_box.GetLastPosition() is referring to the cursor position when editing, not the last selection

user2682863
  • 3,097
  • 1
  • 24
  • 38