On 10/28/2016 1:05 AM, Michael Torrie wrote:

Sure you can't get a keyboard scancode when you're in terminal.

As you note in your followup, Marko and Bart want to be able to respond, for instance, to left and right shift, separately and differently. Ascii terminals only send and receive ascii characters, including control 'characters'. Enhanced ('smart') terminals also receive and, I presume, send escape sequences representing editing actions Last I knew a couple of decades ago, there were no ansi code sequences for shift key presses. So responding to these requires that something in the process have access to them.

> But you can get "keystrokes" as it were, without having to read an entire line
from standard in. ...

Is this not what BartC is talking about?  A way of reading in
"keystrokes" in a terminal.

The only specification he has given is reference to the BASIC INKEY$ variable. I don't know how consistent this was across different BASICs. I looked in Microsoft's GW-BASIC reference and it says that it returns '', 'x', or '0x'. This latter represents an extended code "described in Appendix C'. However, Appendix C only lists the standard ASCII codes 000 to 128. So I do not know what else was available and would not know from this book how to emulate GW-BASIC INKEY$.

With tk, everything is available that is standard on any of the major systems. A key event includes widget name, key name (a single char for char keys, a capitalized name otherwise), keycode, and x,y pixel position of the mouse relative to the widget. A tkinter inkey() function might work as follows:

#------------------------------------------------------------
"""tk_inkey.py, 2016 Oct 26
(C) Terry Jan Reedy
Emulate BASIC INKEY$ function/variable as Python inkey() function.
"""
# Create inkey function
from collections import deque
import tkinter as tk
root = tk.Tk()

def setup_inkey(widget):
    # Key values must be stored so inkey can access them.
    # If inkey were always called faster than a person can type,
    # a single nonlocal would suffice.  However, since update()
    # processes all pending keys, a queue is needed.
    q = deque()

    def storekeyname(event):
        q.append(event.keysym)  # or other key description
        return 'break'  # swallow the event

    widget.bind('<Key>', storekeyname)

    def inkey():
        widget.update()  # process all pending keys
        return q.popleft() if q else ''

    return inkey

inkey = setup_inkey(root)

# Test inkey in simulated use.
import time

display = tk.Label(root, text='No Key', width=30)
display.pack()
root.update()

while True:
    time.sleep(.3)  # 'do some calculation'
    try:
        c = inkey()
    except tk.TclError:
        break
    if c:
        display['text'] = c
#---------------------------------------------------------------

This runs *without* blocking root.mainloop(), but rather with root.update() within inkey(). So it can be used within normal code. As I said before, this could be coupled with a tk Text that emulates a console, with custom print and input functions, so it would look like and work like a console application that responds to keypresses.


The use model of INKEY$ is that the programmer must branch to code blocks or functions according to the value returned. An alternative 'non-blocking' use model is that the programmer binds functions to particular events and lets the framework do the dispatching. One difference is when the function is called. A generalized version of the key handler above could triage key presses into those handled immediately, those enqueued for later handling, and those ignored. The decision whether to swallow or pass on events could be different for different events.

--
Terry Jan Reedy

--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to