Search code examples
pythoneventswidgetwxpythontextctrl

wxPython: Redirecting events on other widgets (TextCtrl)


The case study doesn't seem too hard to explain, but I guess TextCtrl in wxPython aren't often used in this way. So here it is: I have a simple window with two TextCtrls. One is an input widget (the user is supposed to enter commands there), the second is an output widget (the system displays the result of the commands). The output field is a read-only TextCtrl, only the system can write in it.

So far so good. Now, I would like to intercept events in the output widget: If users type in this output field (a read-only widget), they should be redirected to the input field and the text they have begun typing should appear there. The first part isn't complicated: I intercept the EVT_KEY_DOWN on the output widget and can do something like self.input.SetFocus(). However, the key that has been pressed by the user is lost. If he/she began to type something, she has to start over again. This is supposed to be a shortcut feature (no matter in what field the user type, it should be written in the input widget).

A short note on why I do this, since it can still quite stupid: Sighted users don't often fool around with read-only widgets; they see them and leave them alone. This application is mostly designed for users with screen readers, who have to move around the output field. The cursor is therefore often there, and a key press doesn't have any effect (since it's a read-only widget). It would be great if, on typing in the output widget, the user was redirected to the input field, with the text he was typing already in this widget.

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        self.panel = MyPanel(self)
        self.Show()
        self.Maximize()

class MyPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)

        # Input field
        self.input = wx.TextCtrl(self, -1, "", size=(125, -5),
                style=wx.TE_PROCESS_ENTER)

        # Ouput
        self.output = wx.TextCtrl(self, -1, "",
                size=(600, 400), style=wx.TE_MULTILINE|wx.TE_READONLY)

        # Add the output fields in the sizer
        sizer.Add(self.input)
        sizer.Add(self.output, proportion=8)

        # Event handler
        self.output.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

    def OnKeyDown(self, e):
        """A key is pressed in the output widget."""
        modifiers = e.GetModifiers()
        key = e.GetUnicodeKey()
        if not key:
            key = e.GetKeyCode()

        print "From there, we should redirect to the input"
        self.input.SetFocus()

# Let's run that
app = wx.App()
MyFrame(None)
app.MainLoop()

Solution

  • Give self.input.EmulateKeyPress(e) a try. If you're on Windows it should work fine. On other platforms it is not perfect, but basically works there too.

    Other options would be to use wx.UiActionSimulator, or simply to append the new character to the input textctrl in your code.