Input prompt always at the bottom - python

I have a problem. You see, I've created this simple chat server. On the client side, since its a chat server, I'm continuously asking the user for input. The thing is, my input prompt is ">", and if you see in the picture below, it gets meshed with all the messages being printed (in this case behind the message sent by the client) My question is, can I make it so that the prompt ">" is always at the bottom/new line of the terminal?

There are several options:
You can write a simple version using ANSI escape sequences yourself, but this will quickly become a pain.
You can use the curses module from the standard library, which abstracts away the specifics of inidividual terminals, and is less of a pain than trying to use escape sequences only.
You can use a higher level framework built on top of curses, like urwid or npyscreen.
I'd personally go with the last option.

Related

Difference between print and click.echo in Python 3?

I am creating CLI app for Unix terminal using click module. So I see two ways how I can display data:
print(data) and click.echo(data)
What is difference between them and what should I use?
Please, read at least quickstart of library before using it. The answer is in the third part of quickstart.
If you use click click.echo() is preferred because:
Click attempts to support both Python 2 and Python 3 the same way and to be very robust even when the environment is misconfigured. Click wants to be functional at least on a basic level even if everything is completely broken.
What this means is that the echo() function applies some error correction in case the terminal is misconfigured instead of dying with an UnicodeError.
As an added benefit, starting with Click 2.0, the echo function also has good support for ANSI colors. It will automatically strip ANSI codes if the output stream is a file and if colorama is supported, ANSI colors will also work on Windows. See ANSI Colors for more information.
If you don’t need this, you can also use the print() construct / function.

Keeping the user's input intact when outputing to terminal at the same time

To simplify, let's say I'm trying to write a command line two-way chat in Python. I would like the user to input his message with input() at the command prompt, but a listening thread could print a message at any moment. By default, this would "break" the user's input. Visually something like this:
userB>Stop interuserA wrote:Hey check this out!
rupting me!
The closest I was able to find was this answer here which is almost, but not exactly, what I'm looking for, but it did point me towards the blessings package which seems to be what I need (although I'm happy with an answer for any package, or even pure ANSII).
What I'm trying to achieve is to print incoming output from a Thread above the user's input, so that his text doesn't break. Let's say the user is typing:
userB>Stop inter
Suddenly a message comes in from the thread, but our user's input doesn't brake:
userA says: Ok I won't interrupt you
userB>Stop inter
What should my threads theoretical print_incoming_message() method look like to achieve this?
NB: I'm using Linux and am not interested in cross-platform compatibility.
There are two ways of doing this.
One is to use ncurses. There are python bindings for this. With ncurses, the terminal screen is under your complete control, and you can print characters at any point.
Without ncurses, you can't write above the current line. What you can do, however, is print a \r character and go back to the beginning of the line.
If you save the user's input (say he wrote foo), and you want to print the line bar above that, you can output:
\rbar\nfoo
This will overwrite the current line, and introduce a newline, moving the user's input down. The effect is similar, but it won't be as tamper-proof as ncurses.

Python Console with Logging

I'm in the process of rewriting a server program, and I want to add into it a simple console input.
At the moment, it just serves data and prints out one or two lines for each thing it does, as a descriptive measure for anyone watching/debugging.
What I want is to have a "sticky" input bar that is always at the bottom, above which my debug prints appear, so that I can enter commands at any point while the program is printing. This would look a bit like:
...
[88.88.88.88] Handling Connection on Port 11452
[12.12.12.12] Received Data
[44.44.44.44] Sending Disconnect Sequence
>>>Enter Data Here at Any Time
Ideally, this would be done without curses as this would complicate matters. I feel like I must be missing a simple solution.
Thanks in Advance,
Freddy.
If you're looking for a slightly higher-level library than curses, there are a few, like urwid (although of course they don't come with Python, and have to be installed).
If you really want to avoid a windowing library, I suppose you could run the server under screen, configured with an input bar and a rest-of-the-window, and give the server the two virtual TTYs instead of normal input… but that would be a lot more work, not less.
If you want to go lower level, you can always use termios, search termcap, and manually write control sequences to manually take care of scrolling everything but the last line, moving the input cursor, and so on. But that's going to be even more work.
And of course there's always the super-hacky possibility: For every output, and after every input, "redraw" the whole screen by spamming a few hundred blank lines, re-writing the output, and writing the input line. This will look terrible, and not work in a variety of edge cases, but it does avoid any kind of windowing.
However, I think there's a much better way to solve this problem.
You've already got a server. Why not just serve the console API on another "control" port (maybe just on localhost) instead of on the stdin/stdout/tty? That will work even if the server is, e.g., running as a daemon. And it means you can use your favorite fancy Telnet or similar client as a console, instead of having to write something from scratch.

Do not disturb user input while displaying messages to stdout

I just made a little chat CLI-application that works pretty fine actually!
Unfortunately, when one types a message and receives other's messages at the same time, both interlacing... Which renders quite awful:
elton: Hey!
john: how are you doing?
fine anjohn: still not to bed?!
d john: haha
you?elton: fine and you?
I'm looking for a way to avoid this kind of problem. Such as "reserving" the last line for user-input or process some actions when data are received to recalculate the position of user-input.
After some research I found that I shall retrieve each character one by one with getch(). So that I can check regularly if a new message is waiting to be displayed and handle this case.
But if I use getch() I have to redefine manually basic actions (such as backspace, move left and right...), some characters take more than one byte. In brief, it's unusable.
As far as I can tell, you have a few options here. Normally just calling getch you're running the terminal in what's called "cooked" mode. This means that the terminal just displays characters in the order they arrive, nothing too special. However you run in to race conditions as you've discovered.
Option 1: you read things in one character at a time, buffering the entire line. When a new line arrives and you want to print it out, you'd have to (a) grab hold of some kind of print mutex (internal to the program), (b) clear out the current line (print '\r[space][space][space]...\r'), (c) print the incoming line + '\n', and (d) restore the previous buffer to the screen (via print) and unlock the mutex.
This gets ugly. Fast. As you discovered, there's no line editing support or anything fancy. The only good thing about this approach is that it'll probably work in about 99% of terminals.
Option 2: you put the terminal in to raw mode. In this mode, you have complete control over the terminal, but you lose very basic functionality (e.g. typing a key won't display it unless you manually output it). You would still have to manually implement things like navigation, but at least it would work. This method is probably the most flexible and give you the most control, however it's very difficult to work like this, which is why they invented...
Option 3: you use ncurses (or a similar terminal library). The concepts are a little difficult, but FAR easier to learn than doing things in pure raw mode. Editing and windowing (or as you put it, reserving a line) are built-in. The interface can be much prettier than you'd get from cooked mode.
EDIT I'm talking a lot about *nix terminals here, most of which doesn't apply for Windows.
The usual way to do this kind of thing is by writing a "console-mode GUI" instead of a pure CLI.
For example, you can very easily create a very simple interface with a ROWS-2xCOLS output window, a 1xCOLS divider or mode line, and a 1xCOLS input window—as seen in things like IRC clients, emacs, etc. For example:
Or you could do a split view, with a ROWS/2xCOLS window for the other person/people's chat, and a ROWS/2xCOLS window below that for the user's current and previous messages, as seen in most of the early Unix chat programs.
Or something fancy with windows all over the place that pop up and go away as appropriate, as some modern IRC clients do.
Python has curses support built-in—but not on Windows. If you want a cross-platform solution, or a Windows-only solution, see curses-like library from cross-platform console app in python, and the answer is probably there.
Here's a screenshot from the IRC client BitchX, to give you an idea of what's possible without too much work:

Insert keypresses into the Linux console from Python

I have recently been faced with a rather odd task, one result being the necessity for the ability to use DTMF (aka "Touch Tone") tones to control a non-X Linux computer's terminal. The computer has a modem which can be accessed through ALSA, and therefore the sox "rec" program, which is what I am reading the input through. The computer in question is otherwise completely isolated, having no Ethernet or other network interfaces whatsoever. The Goertzel algorithm implementation I am using works very well, as does the eSpeak speech synthesis engine which is the only source of output; this is supposed to work with any Touch Tone phone. It reads back both input (input being octal digits, one ASCII byte at a time)and whatever the dash shell feeds back -- the prompt, the output from commands, etc., using ASCII mnemonics for control characters.
The current method that I am using for interacting with dash and the programs launched through it is the pexpect module. However, I need it to be able to, on demand, read back the entire contents of the line on which the cursor is positioned, and I do not recall pexpect being able to do this (If it is, I cannot tell.). The only other solution that I can think of is to somehow use Python to either control, or act as, the keyboard and console drivers.
Is this, indeed, the only way to go about it (and if so, is it even possible with Python?), or is there another way of having direct access to the contents of the console?
Edit: Through dumb luck, I just recently found that the SVN version of PExpect has pexpect.screen. However, it does not have any way of actually running a program under it. I'll have to keep an eye on its development.
The simple solution is to use the Linux kernel uinput interface. It allows you to insert keypresses and mouse events into the kernel, exactly as if they came from a physical human interface device. This would basically turn your application into a keyboard/mouse.
Since you are working with Python, I recommend you take a look at the python-uinput module.
If you are comfortable with binary I/O in Python, then you can certainly do the same without any libraries; just check out the /usr/include/linux/uinput.h header file for the structures involved (the interface is completely stable), and perhaps some uinput tutorials in C, too.
Note that accessing the /dev/uinput or /dev/input/uinput device (depending on your distribution) normally requires root privileges. I would personally run the Python service as a user and group dedicated to the service, and modify/add a udev rule (check all files under rules.d) to allow read-write access to the uinput device to that group, something like
SUBSYSTEM=="input", ENV{ID_INPUT}=="", IMPORT{builtin}="input_id"
KERNEL=="uinput", MODE="0660", GROUP="the-dedicated-group"
However, if your Python application simply executes programs, you should make it a terminal emulator -- for example, using this. You can do it too without any extra libraries, using the Python pty; the main work is to however simulate a terminal with ANSI escape sequences, so that applications don't get confused, and the existing terminal emulators have such code.
If you want to manipulate the contents of the console, you probably want to use curses. It's well documented here. Look at window.getch() and window.getyx().

Categories

Resources