Joril
Joril

Reputation: 20596

Receiving keystrokes in an opened wx.ComboCtrl

Coming from this question, I have a wxComboCtrl with a custom popup made of a panel with a bunch of radiobuttons.. My problem is that when I open the popup the combo doesn't get keystrokes, because the events get handled by the panel itself.. I'd like to redirect those KeyEvents to the textctrl of the combo, but I can't find a way to get it to work :/
Am I going the wrong way? Should I manually handle the textctrl value as the user presses keys? I think that would be a bit cumbersome though.. Since supposedly the textctrl already knows how to handle those events..

Here's my testcase (wxPython 2.8 on Linux), the "on_key" method should be the culprit:

import wx
import wx.combo

class CustomPopup(wx.combo.ComboPopup):
    
    def Create(self, parent):
        # Create the popup with a bunch of radiobuttons
        self.panel = wx.Panel(parent)
        sizer = wx.GridSizer(cols=2)
        for x in range(10):
            r = wx.RadioButton(self.panel, label="Element "+str(x))
            r.Bind(wx.EVT_RADIOBUTTON, self.on_selection)
            sizer.Add(r)
        self.panel.SetSizer(sizer)
        
        # Handle keyevents
        self.panel.Bind(wx.EVT_KEY_UP, self.on_key)

    def GetControl(self):
        return self.panel

    def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
        return wx.Size(200, 150)

    def on_key(self, evt):
        if evt.GetEventObject() is self.panel:
            # Trying to redirect the key event to the combo.. But this always returns false :(
            print self.GetCombo().GetTextCtrl().GetEventHandler().ProcessEvent(evt)
        evt.Skip()

    def on_selection(self, evt):
        self.Dismiss()
        wx.MessageBox("Selection made")


class CustomFrame(wx.Frame):

    def __init__(self):
        # Toolbar-shaped frame with a ComboCtrl
        wx.Frame.__init__(self, None, -1, "Test", size=(800,50))
        combo = wx.combo.ComboCtrl(self)
        popup = CustomPopup()
        combo.SetPopupControl(popup)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(combo, 0)
        self.SetSizer(sizer)
        self.Layout()

if __name__ == '__main__':
    app = wx.PySimpleApp()
    CustomFrame().Show()
    app.MainLoop()

Edit:
I found these (unresolved) discussions on the same topic..
"ComboCtrl loses keyboard focus when ComboPopup is shown "
"issue using wx.ComboCtrl"

Upvotes: 0

Views: 774

Answers (2)

wxPythonNewbie
wxPythonNewbie

Reputation: 11

I had the same problem with a custom ComboPopup control using a panel as the basis of the control.

EmulateKeypress just entered a never-ending loop. I found binding the panel to the EVT_KEY_DOWN and EVT_CHAR to the overridden OnComboKeyEvent function and skipping the event down the event handling chain was all that was required to enable entering text into the textbox of the ComboCtrl with the popup open. I think this makes sense. As the Popup isn't strictly a control its events won't belong anywhere until bound into the the wx.app event loop through the Bind() method.

I've included the overridden OnComboKeyEvent function below so that anyone else can explore this problem.

 def create(self, *args, **kwds):
       ...
       self.panel.Bind(wx.EVT_CHAR, self.OnComboKeyEvent)
       self.panel.Bind(wx.EVT_KEY_DOWN, self.OnComboKeyEvent)
       ...
 def OnComboKeyEvent(self,  evt):
        evt_dict = {wx.EVT_TEXT.typeId: "TextEvent", 
                            wx.EVT_CHAR.typeId:"CharEvent", 
                            wx.EVT_CHAR_HOOK.typeId:"CharHookEvent",
                            wx.EVT_KEY_UP.typeId:"KeyUpEvent", 
                            wx.EVT_KEY_DOWN.typeId:"KeyDownEvent"}
        if evt.GetEventType() in evt_dict:
            thelogger.debug("oncombokeyevet:%s" % evt_dict[evt.GetEventType()])
        
        super().OnComboKeyEvent(evt)
        evt.Skip()

Upvotes: 1

Joril
Joril

Reputation: 20596

I've got an answer from Robin Dunn himself on wxpython-users:

Sending low-level events to native widgets like this does not work the way that most people expect it to. You are expecting this to result in the character being added to the text ctrl,but it can't do that because all that ProcessEvent does is send the wx.Event to any bound event handlers and then it stops. It does not go to the next step and convert that event to the equivalent native message and send it on to the native widget. See the "Simulating keyboard events" thread.

It doesn't work especially well on non-Windows platforms, but you could try calling EmulateKeypress instead.

Upvotes: 0

Related Questions