Python: How to print without overwriting current typed input?

Viewed 270

Is it possible to print something without overwriting the already written but not sent input?

This is the client side for a socket and I'm wanting to print messages from the server without overwriting/appending the currently written input.

def listen():
    while True:
        data = s.recv(1024)
        print(data.decode('utf-8'))

def write():
    while True:
        text = input("Text > ")
        s.sendall(bytes(text, 'utf-8'))

listener = threading.Thread(target=listen)
listener.start()

writer = threading.Thread(target=write)
writer.start()

I'd want to print the received data above or below the current input line, but right now it just writes it on the input line.

2 Answers

Add time.sleep(10) after s.sendall(bytes(text, 'utf-8')), because you immediately do input while the data is received and printed later. Having time.sleep you give time for the data to be received and printed.

But this won't help you if you enter the data for input too slow. Generally the code is strange, because why would you do input and print on the same terminal? In real life it doesn't make sense.

For academic purposes you can try this:

is_input_active = False

def listen():
    while True:
        data = s.recv(1024)
        while is_input_active:
            time.sleep(0.2)
        print(data.decode('utf-8'))

def write():
    global is_input_active
    while True:
        is_input_active = True
        text = input("Text > ")
        is_input_active = False
        s.sendall(bytes(text, 'utf-8'))

With this version listen() will block until input function finishes. If you don't want this, try something like:

def listen():
    data = b''
    while True:
        data += s.recv(1024)  # accumulate
        while not is_input_active:
            print(data.decode('utf-8'))
            data = b''  # reset

Interactive terminal input is complex and is usually handled with libraries, it's hard to handle it directly. For line input, readline and its alternatives such as libedit are popular. Most shells and REPLs use such libraries.

Python's standard library has readline module, which is interface to readline or libedit. Importing it makes input use realine/libedit, so it obtains cursor navigation and even auto-completion and history capabilities. And it allows to redraw input line after you draw something in terminal.

To print in previous line, you can use ANSI escape codes.

import threading
import sys
from time import sleep
import readline # importing readline makes input() use it

def listen():
    i = 0
    while True:
        i += 1
        sleep(1)
        # <esc>[s - save cursor position
        # <esc>[1A - move cursor up
        # \r - carriage return
        # print message
        # <esc>[u - restore cursor position
        sys.stdout.write("\x1b[s\x1b[1A\rOutput: "+str(i)+"\x1b[u")
        readline.redisplay()

def write():
    while True:
        print "" # make space for printing messages
        text = input("Text > ")

listener = threading.Thread(target=listen)
listener.daemon = True
listener.start()

write()

readline library also has rl_message and other useful capabilities, but its' not exported by python's readline module.

Related