pepero
pepero

Reputation: 7513

catch TextCtrl's key/char events and forward typed key to the stdin of a subprocess

I am trying to use wx.TextCtrl to catch the typed key events, and directly forward the typed key to the stdin of a subprocess. Please note, for my special purpose, I will completely disable the text editing feature of the TextCtrl. i.e., when I type a letter, the letter will not be appearing on the TextCtrl, it will be directly forwarded.
Here is some code to illustrate what I want.

# inside the main frame
    self.text = wx.TextCtrl(self.panel, wx.ID_ANY, style=wx.TE_MULTILINE)
    self.text.Bind(wx.EVT_KEY_DOWN, self.OnKey)
    self.text.Bind(wx.EVT_CHAR, self.OnChar)
# ...

def OnKey(self, evt):
    keycode = evt.GetKeyCode()
    # ENTER

    if keycode == 13:
        self.subprocess.stdin.read("\n") 
    if keycode == 9:
        self.subprocess.stdin.read("\t") 
    if keycode == 8:
        self.subprocess.stdin.read("\b") 
    if keycode == 316:
        pass # maybe some key will be ignored
    else:
        evt.skip()

def OnChar(self, evt):
    key=chr(keycode)                   
    self.subprocess.stdin.read(key)

I want to forward "ENTER", "TAB", "BACKSPACE", characters, numbers, etc., all the key input events to stdin, without letting TextCtrl to interfere. Is there a good way to do it? Or I have to explicitely match each key one by one?

Thanks for any suggestions!

Upvotes: 0

Views: 2256

Answers (2)

I_4m_Z3r0
I_4m_Z3r0

Reputation: 1090

here you can find some of the modifiers and key states on the "key" python module:

https://pythonhosted.org/pyglet/programming_guide/keyboard_events.html

You can only use 1 binder, like @KEY_DOWN and not both @KEY_DOWN and EVT_CHAR

To manipulate chars you need to check the range of values but using the infos on the website above you can, for example do:

key.A

TO identify "a".

Otherwise you can identify "a" by the keycode associated (unicode or ASCII).

You need to get from the "evt" KeyEvent the:

evt.GetRawKeyCode()

To identify modifiers like SHIFT and CTRL SX ecc pressed with the key:

evt.GetModifiers()

Here, as an example of the usage of the "key" module, a listener I made that makes a TextCtrl accept only numerical values iput. You can modify it if you want. For letters you need to check for numerical values from: 65 (A) to 90 (Z) and from 97 (a) to 122 (z). And u can use:

evt.GetUnicodeKey()

But also

evt.GetRawKeyCode()

Works. So you will have:

rawKey = evt.GetRawKeyCode()
if rawKey >= 65 and rawKey <= 90 and rawKey >= 97 and rawKey <= 122:
      # Inserted a letter from the alphabet.

###########

from pyglet.window import key
from Utils.NumberUtils import NumberUtils

ENTRY_ID = "id"
ENTRY_KEY_EVENT = "keyEvent"
ENTRY_SELECTION_EVENT = "selectionEvent"

MASK_STARTED_RIGHT_SELECTION = 1
MASK_STARTED_LEFT_SELECTION = -1

class KeyboardEventUtils(object):

    __mLastEvt = {}

    def on_change_text_check_is_float_value(self, evt):
        txt = evt.GetEventObject()
        strng = txt.GetValue()
        rawKey = evt.GetRawKeyCode()
        modifiers = evt.GetModifiers()

        if KeyboardEventUtils.__mLastEvt != None and len(KeyboardEventUtils.__mLastEvt) > 0 and KeyboardEventUtils.__mLastEvt[ENTRY_ID] != txt.GetId():
            self._mLastEvt = {}

        if chr(rawKey).isnumeric() or (rawKey == key.PERIOD and not chr(rawKey) in strng and len(strng) > 0) or (rawKey == key.MINUS and not chr(rawKey) in strng and (len(strng) == 0 or txt.GetInsertionPoint() == 0)):
            pos = txt.GetInsertionPoint()
            txt.SetValue(str(float(txt.GetValue()[:pos] + chr(rawKey) + txt.GetValue()[pos:])))
            txt.SetInsertionPoint(pos + 1)

        elif (modifiers == 4 or modifiers == key.MOD_SHIFT) and rawKey == key.LEFT:             # 4 (?) key.MOD_SHIFT (?)
            pos = txt.GetInsertionPoint()
            rng = list(txt.GetSelection())

            if rng[0] == rng[1] and ENTRY_SELECTION_EVENT in KeyboardEventUtils.__mLastEvt:
                del KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT]

            if ENTRY_SELECTION_EVENT in KeyboardEventUtils.__mLastEvt and KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT] == MASK_STARTED_RIGHT_SELECTION:

                rng[1] = rng[1] - 1
            else:
                KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT] = MASK_STARTED_LEFT_SELECTION
                rng[0] = rng[0] - 1

            txt.SetSelection(rng[0], rng[1])

        elif (modifiers == 4 or modifiers == key.MOD_SHIFT):                                    # 4 (?) key.MOD_SHIFT (?)
            pos = txt.GetInsertionPoint()
            rng = list(txt.GetSelection())

            if rng[0] == rng[1] and ENTRY_SELECTION_EVENT in KeyboardEventUtils.__mLastEvt:
                del KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT]

            if ENTRY_SELECTION_EVENT in KeyboardEventUtils.__mLastEvt and KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT] == MASK_STARTED_LEFT_SELECTION:
                rng[0] = rng[0] + 1
            else:
                KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT] = MASK_STARTED_RIGHT_SELECTION
                rng[1] = rng[1] + 1

            txt.SetSelection(rng[0], rng[1])

        elif rawKey == key.LEFT:
            rng = list(txt.GetSelection())

            if rng[0] != rng[1]:
                txt.SetInsertionPoint(rng[0])
            else:
                txt.SetInsertionPoint(txt.GetInsertionPoint() - 1)

        elif rawKey == key.RIGHT:
            rng = list(txt.GetSelection())

            if rng[0] != rng[1]:
                txt.SetInsertionPoint(rng[1])
            else:
                txt.SetInsertionPoint(txt.GetInsertionPoint() + 1)

        elif rawKey == key.UP or rawKey == key.DOWN:
            pass

        elif rawKey == key.BACKSPACE:                                              
            pos = txt.GetInsertionPoint()
            if txt.GetStringSelection() == "":
                txt.SetValue(txt.GetValue()[:pos-1] + txt.GetValue()[pos:])
                txt.SetInsertionPoint(pos - 0x1)
            else:
                r = txt.GetSelection()
                txt.SetValue(txt.GetValue()[:r[0]] + txt.GetValue()[r[1]:])
                txt.SetInsertionPoint(pos)

        elif rawKey == key.DELETE:
            pos = txt.GetInsertionPoint()
            if txt.GetStringSelection() == "":
                txt.SetValue(txt.GetValue()[:pos] + txt.GetValue()[pos+1:])
            else:
                r = txt.GetSelection()
                txt.SetValue(txt.GetValue()[:r[0]] + txt.GetValue()[r[1]:])
            txt.SetInsertionPoint(pos)

        elif modifiers == key.MOD_CTRL and rawKey == key.A:
            txt.SelectAll()

        else:

            KeyboardEventUtils.__mLastEvt[ENTRY_ID] = txt.GetId()
            KeyboardEventUtils.__mLastEvt[ENTRY_KEY_EVENT] = evt
            return False

        KeyboardEventUtils.__mLastEvt[ENTRY_ID] = txt.GetId()
        KeyboardEventUtils.__mLastEvt[ENTRY_KEY_EVENT] = evt
        return True

Upvotes: 0

Jake
Jake

Reputation: 2655

You can do this to convert the key code to a character:

chr(keycode)

That won't get everything though, like enters and tabs. You'd have to handle that on a case by case basis (like you do in your example). Otherwise you'll need to create a dictionary of key code to character mappings:

codemap = {97:'a', 98:'b', 8:'\b'} # Fill this out
self.subprocess.stdin.read(codemap[keycode])

You also might want to play with wx.TE_PROCESS_ENTER and wx.TE_PROCESS_TAB. They enable/disable capturing the enter and tab keys as text.

Upvotes: 0

Related Questions