I know I can stop print from writing a newline by adding a comma
print "Hello, world!",
# print("Hello, world!", end='') for Python 3.x
But how do I stop raw_input (or input for Python 3.x) from writing a newline?
print "Hello, ",
name = raw_input()
print ", how do you do?"
Result:
Hello, Tomas
, how do you do?
Result I want:
Hello, Tomas, how do you do?
But how do I stop raw_input from writing a newline?
In short: You can't.
raw_input() will always echo the text entered by the user, including the trailing newline. That means whatever the user is typing will be printed to standard output.
If you want to prevent this, you will have to use a terminal control library such as the curses module. This is not portable, though -- for example, curses in not available on Windows systems.
I see that nobody has given a working solution, so I decided I might give it a go.
As Ferdinand Beyer said, it is impossible to get raw_input() to not print a new line after the user input. However, it is possible to get back to the line you were before.
I made it into an one-liner. You may use:
print '\033[{}C\033[1A'.format(len(x) + y),
where x is an integer of the length of the given user input and y an integer of the length of raw_input()'s string. Though it might not work on all terminals (as I read when I learned about this method), it works fine on mine. I'm using Kubuntu 14.04.
The string '\033[4C' is used to jump 4 indexes to the right, so it would be equivalent to ' ' * 4. In the same way, the string '\033[1A' is used to jump 1 line up. By using the letters A, B, C or D on the last index of the string, you can go up, down, right and left respectively.
Note that going a line up will delete the existing printed character on that spot, if there is one.
This circumvents it, somewhat, but doesn't assign anything to variable name:
print("Hello, {0}, how do you do?".format(raw_input("Enter name here: ")))
It will prompt the user for a name before printing the entire message though.
You can use getpass instead of raw_input if you don't want it to make a new line!
import sys, getpass
def raw_input2(value="",end=""):
sys.stdout.write(value)
data = getpass.getpass("")
sys.stdout.write(data)
sys.stdout.write(end)
return data
An alternative to backtracking the newline is defining your own function that emulates the built-in input function, echoing and appending every keystroke to a response variable except Enter (which will return the response), whilst also handling Backspace, Del, Home, End, arrow keys, line history, KeyboardInterrupt, EOFError, SIGTSTP and pasting from the clipboard. It's very simple.
Note that on Windows, you'll need to install pyreadline if you want to use line history with the arrow keys like in the usual input function, although it's incomplete so the functionality is still not quite right. In addition, if you're not on v1511 or greater of Windows 10, you'll need to install the colorama module (if you're on Linux or macOS, nothing needs to be done).
Also, due to msvcrt.getwch using '\xe0' to indicate special characters, you won't be able to type 'à'. You should be able to paste it though.
Below is code that makes this work on updated Windows 10 systems (at least v1511), Debian-based Linux distros and maybe macOS and other *NIX operating systems. It should also work regardless of whether you have pyreadline installed on Windows, though it'll lack some functionality.
In windows_specific.py:
"""Windows-specific functions and variables for input_no_newline."""
import ctypes
from msvcrt import getwch # pylint: disable=import-error, unused-import
from shared_stuff import ANSI
try:
import colorama # pylint: disable=import-error
except ImportError:
kernel32 = ctypes.windll.kernel32
# Enable ANSI support to move the text cursor
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
else:
colorama.init()
def get_clipboard_data():
"""Return string previously copied from Windows clipboard.
Adapted from <http://stackoverflow.com/a/23285159/6379747>.
"""
CF_TEXT = 1
user32 = ctypes.windll.user32
user32.OpenClipboard(0)
try:
if user32.IsClipboardFormatAvailable(CF_TEXT):
data = user32.GetClipboardData(CF_TEXT)
data_locked = kernel32.GlobalLock(data)
text = ctypes.c_char_p(data_locked)
kernel32.GlobalUnlock(data_locked)
finally:
user32.CloseClipboard()
return text.value
def sigtstp():
"""Raise EOFError from Ctrl-Z since SIGTSTP doesn't exist on Windows."""
raise EOFError
input_code = {
**ANSI,
'CSI': [['\xe0', '\x00'], ''],
'up': 'H',
'down': 'P',
'right': 'M',
'left': 'K',
'end': 'O',
'home': 'G',
'backspace': '\b',
'del': 'S',
}
In unix_specific.py:
"""Functions and variables for Debian-based Linux distros and macOS."""
import sys
import os
import tty
import signal
import termios
from shared_stuff import ANSI
def getwch():
"""Return a single character from user input without echoing.
ActiveState code, adapted from
<http://code.activestate.com/recipes/134892> by Danny Yoo under
the Python Software Foundation license.
"""
file_descriptor = sys.stdin.fileno()
old_settings = termios.tcgetattr(file_descriptor)
try:
tty.setraw(file_descriptor)
char = sys.stdin.read(1)
finally:
termios.tcsetattr(file_descriptor, termios.TCSADRAIN, old_settings)
return char
def get_clipboard_data():
"""Return nothing; *NIX systems automagically change sys.stdin."""
return ''
def sigtstp():
"""Suspend the script."""
os.kill(os.getpid(), signal.SIGTSTP)
input_code = {
**ANSI,
'CSI': ['\x1b', '['],
'backspace': '\x7f',
'del': ['3', '~'],
}
In readline_available.py:
"""Provide functions for up and down arrows if readline is installed.
Basically to prevent duplicate code and make it work on systems without
readline.
"""
try:
import readline
except ImportError:
import pyreadline as readline
from shared_stuff import move_cursor
def init_history_index():
"""Return index for last element of readline.get_history_item."""
# readline.get_history_item is one-based
return readline.get_current_history_length() + 1
def restore_history(history_index, replaced, cursor_position):
"""Replace 'replaced' with history and return the replacement."""
try:
replacement = readline.get_history_item(history_index)
except IndexError:
replacement = None
if replacement is not None:
move_cursor('right', len(replaced) - cursor_position)
print('\b \b' * len(replaced), end='', flush=True)
print(replacement, end='', flush=True)
return replacement
return replaced
def store_and_replace_history(history_index, replacement, old_history):
"""Store history and then replace it."""
old_history[history_index] = readline.get_history_item(history_index)
try:
readline.replace_history_item(history_index - 1, replacement)
except AttributeError:
# pyreadline is incomplete
pass
def handle_prev_history(history_index, replaced, old_history,
input_replaced, history_modified):
"""Handle some up-arrow logic."""
try:
history = readline.get_history_item(history_index - 1)
except IndexError:
history = None
if history is not None:
if history_index > readline.get_current_history_length():
readline.add_history(replaced)
input_replaced = True
else:
store_and_replace_history(
history_index, replaced, old_history)
history_modified = True
history_index -= 1
return (history_index, input_replaced, history_modified)
def handle_next_history(history_index, replaced, old_history,
input_replaced, history_modified):
"""Handle some down-arrow logic."""
try:
history = readline.get_history_item(history_index + 1)
except IndexError:
history = None
if history is not None:
store_and_replace_history(history_index, replaced, old_history)
history_modified = True
history_index += 1
input_replaced = (not history_index
== readline.get_current_history_length())
return (history_index, input_replaced, history_modified)
def finalise_history(history_index, response, old_history,
input_replaced, history_modified):
"""Change history before the response will be returned elsewhere."""
try:
if input_replaced:
readline.remove_history_item(history_index - 1)
elif history_modified:
readline.remove_history_item(history_index - 1)
readline.add_history(old_history[history_index - 1])
except AttributeError:
# pyreadline is also missing remove_history_item
pass
readline.add_history(response)
In readline_unavailable.py:
"""Provide dummy functions for if readline isn't available."""
# pylint: disable-msg=unused-argument
def init_history_index():
"""Return an index of 1 which probably won't ever change."""
return 1
def restore_history(history_index, replaced, cursor_position):
"""Return the replaced thing without replacing it."""
return replaced
def store_and_replace_history(history_index, replacement, old_history):
"""Don't store history."""
pass
def handle_prev_history(history_index, replaced, old_history,
input_replaced, history_modified):
"""Return 'input_replaced' and 'history_modified' without change."""
return (history_index, input_replaced, history_modified)
def handle_next_history(history_index, replaced, old_history,
input_replaced, history_modified):
"""Also return 'input_replaced' and 'history_modified'."""
return (history_index, input_replaced, history_modified)
def finalise_history(history_index, response, old_history,
input_replaced, history_modified):
"""Don't change nonexistent history."""
pass
In shared_stuff.py:
"""Provide platform-independent functions and variables."""
ANSI = {
'CSI': '\x1b[',
'up': 'A',
'down': 'B',
'right': 'C',
'left': 'D',
'end': 'F',
'home': 'H',
'enter': '\r',
'^C': '\x03',
'^D': '\x04',
'^V': '\x16',
'^Z': '\x1a',
}
def move_cursor(direction, count=1):
"""Move the text cursor 'count' times in the specified direction."""
if direction not in ['up', 'down', 'right', 'left']:
raise ValueError("direction should be either 'up', 'down', 'right' "
"or 'left'")
# A 'count' of zero still moves the cursor, so this needs to be
# tested for.
if count != 0:
print(ANSI['CSI'] + str(count) + ANSI[direction], end='', flush=True)
def line_insert(text, extra=''):
"""Insert text between terminal line and reposition cursor."""
if not extra:
# It's not guaranteed that the new line will completely overshadow
# the old one if there is no extra. Maybe something was 'deleted'?
move_cursor('right', len(text) + 1)
print('\b \b' * (len(text)+1), end='', flush=True)
print(extra + text, end='', flush=True)
move_cursor('left', len(text))
And finally, in input_no_newline.py:
#!/usr/bin/python3
"""Provide an input function that doesn't echo a newline."""
try:
from windows_specific import getwch, get_clipboard_data, sigtstp, input_code
except ImportError:
from unix_specific import getwch, get_clipboard_data, sigtstp, input_code
try:
from readline_available import (init_history_index, restore_history,
store_and_replace_history,
handle_prev_history, handle_next_history,
finalise_history)
except ImportError:
from readline_unavailable import (init_history_index, restore_history,
store_and_replace_history,
handle_prev_history, handle_next_history,
finalise_history)
from shared_stuff import ANSI, move_cursor, line_insert
def input_no_newline(prompt=''): # pylint: disable=too-many-branches, too-many-statements
"""Echo and return user input, except for the newline."""
print(prompt, end='', flush=True)
response = ''
position = 0
history_index = init_history_index()
input_replaced = False
history_modified = False
replacements = {}
while True:
char = getwch()
if char in input_code['CSI'][0]:
char = getwch()
# Relevant input codes are made of two to four characters
if char == input_code['CSI'][1]:
# *NIX uses at least three characters, only the third is
# important
char = getwch()
if char == input_code['up']:
(history_index, input_replaced, history_modified) = (
handle_prev_history(
history_index, response, replacements, input_replaced,
history_modified))
response = restore_history(history_index, response, position)
position = len(response)
elif char == input_code['down']:
(history_index, input_replaced, history_modified) = (
handle_next_history(
history_index, response, replacements, input_replaced,
history_modified))
response = restore_history(history_index, response, position)
position = len(response)
elif char == input_code['right'] and position < len(response):
move_cursor('right')
position += 1
elif char == input_code['left'] and position > 0:
move_cursor('left')
position -= 1
elif char == input_code['end']:
move_cursor('right', len(response) - position)
position = len(response)
elif char == input_code['home']:
move_cursor('left', position)
position = 0
elif char == input_code['del'][0]:
if ''.join(input_code['del']) == '3~':
# *NIX uses '\x1b[3~' as its del key code, but only
# '\x1b[3' has currently been read from sys.stdin
getwch()
backlog = response[position+1 :]
response = response[:position] + backlog
line_insert(backlog)
elif char == input_code['backspace']:
if position > 0:
backlog = response[position:]
response = response[: position-1] + backlog
print('\b', end='', flush=True)
position -= 1
line_insert(backlog)
elif char == input_code['^C']:
raise KeyboardInterrupt
elif char == input_code['^D']:
raise EOFError
elif char == input_code['^V']:
paste = get_clipboard_data()
backlog = response[position:]
response = response[:position] + paste + backlog
position += len(paste)
line_insert(backlog, extra=paste)
elif char == input_code['^Z']:
sigtstp()
elif char == input_code['enter']:
finalise_history(history_index, response, replacements,
input_replaced, history_modified)
move_cursor('right', len(response) - position)
return response
else:
backlog = response[position:]
response = response[:position] + char + backlog
position += 1
line_insert(backlog, extra=char)
def main():
"""Called if script isn't imported."""
# "print(text, end='')" is equivalent to "print text,", and 'flush'
# forces the text to appear, even if the line isn't terminated with
# a '\n'
print('Hello, ', end='', flush=True)
name = input_no_newline() # pylint: disable=unused-variable
print(', how do you do?')
if __name__ == '__main__':
main()
As you can see, it's a lot of work for not that much since you need to deal with the different operating systems and basically reimplement a built-in function in Python rather than C. I'd recommend that you just use the simpler TempHistory class I made in another answer, which leaves all the complicated logic-handling to the built-in function.
Like Nick K. said, you'll need to move the text cursor back to before the newline was echoed. The problem is that you can't easily get the length of the previous line in order to move rightward, lest you store every string printed, prompted and inputted in its own variable.
Below is a class (for Python 3) that fixes this by automatically storing the last line from the terminal (provided you use its methods). The benefit of this compared to using a terminal control library is that it'll work in the standard terminal for both the latest version of Windows as well as *NIX operating systems. It'll also print the 'Hello, ' prompt before getting input.
If you're on Windows but not v1511 of Windows 10, then you'll need to install the colorama module or else this won't work, since they brought ANSI cursor movement support in that version.
# For the sys.stdout file-like object
import sys
import platform
if platform.system() == 'Windows':
try:
import colorama
except ImportError:
import ctypes
kernel32 = ctypes.windll.kernel32
# Enable ANSI support on Windows 10 v1511
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
else:
colorama.init()
else:
# Fix Linux arrow key support in Python scripts
import readline
class TempHistory:
"""Record one line from the terminal.
It is necessary to keep track of the last line on the terminal so we
can move the text cursor rightward and upward back into the position
before the newline from the `input` function was echoed.
Note: I use the term 'echo' to refer to when text is
shown on the terminal but might not be written to `sys.stdout`.
"""
def __init__(self):
"""Initialise `line` and save the `print` and `input` functions.
`line` is initially set to '\n' so that the `record` method
doesn't raise an error about the string index being out of range.
"""
self.line = '\n'
self.builtin_print = print
self.builtin_input = input
def _record(self, text):
"""Append to `line` or overwrite it if it has ended."""
if text == '':
# You can't record nothing
return
# Take into account `text` being multiple lines
lines = text.split('\n')
if text[-1] == '\n':
last_line = lines[-2] + '\n'
# If `text` ended with a newline, then `text.split('\n')[-1]`
# would have merely returned the newline, and not the text
# preceding it
else:
last_line = lines[-1]
# Take into account return characters which overwrite the line
last_line = last_line.split('\r')[-1]
# `line` is considered ended if it ends with a newline character
if self.line[-1] == '\n':
self.line = last_line
else:
self.line += last_line
def _undo_newline(self):
"""Move text cursor back to its position before echoing newline.
ANSI escape sequence: `\x1b[{count}{command}`
`\x1b` is the escape code, and commands `A`, `B`, `C` and `D` are
for moving the text cursor up, down, forward and backward {count}
times respectively.
Thus, after having echoed a newline, the final statement tells
the terminal to move the text cursor forward to be inline with
the end of the previous line, and then move up into said line
(making it the current line again).
"""
line_length = len(self.line)
# Take into account (multiple) backspaces which would
# otherwise artificially increase `line_length`
for i, char in enumerate(self.line[1:]):
if char == '\b' and self.line[i-1] != '\b':
line_length -= 2
self.print('\x1b[{}C\x1b[1A'.format(line_length),
end='', flush=True, record=False)
def print(self, *args, sep=' ', end='\n', file=sys.stdout, flush=False,
record=True):
"""Print to `file` and record the printed text.
Other than recording the printed text, it behaves exactly like
the built-in `print` function.
"""
self.builtin_print(*args, sep=sep, end=end, file=file, flush=flush)
if record:
text = sep.join([str(arg) for arg in args]) + end
self._record(text)
def input(self, prompt='', newline=True, record=True):
"""Return one line of user input and record the echoed text.
Other than storing the echoed text and optionally stripping the
echoed newline, it behaves exactly like the built-in `input`
function.
"""
if prompt == '':
# Prevent arrow key overwriting previously printed text by
# ensuring the built-in `input` function's `prompt` argument
# isn't empty
prompt = ' \b'
response = self.builtin_input(prompt)
if record:
self._record(prompt)
self._record(response)
if not newline:
self._undo_newline()
return response
record = TempHistory()
# For convenience
print = record.print
input = record.input
print('Hello, ', end='', flush=True)
name = input(newline=False)
print(', how do you do?)
As already answered, we can't stop input() from writing a newline. Though it may not satisfy your expectation, somehow the following codes satisfy the condition if -
you don't have any issue clearing the screen
import os
name = input("Hello, ")
os.system("cls") # on linux or mac, use "clear"
print(f"Hello, {name}, how do you do?")
or no issue using the gui dialog box, as dialog box disappears after taking user input, you will see exactly what you expected
import easygui
name = easygui.enterbox("Hello, ", "User Name")
print("Hello, "+name+", how do you do?")
I think you can use this:
name = input("Hello , ")
It should be something like this:-
print('this eliminates the ', end=' ')
print('new line')
The output is this:-
this eliminates the new line.
Related
I am messing around with the blessed library, and I found this basic text editor program. When I run it in my terminal, all of the keys register except for backspace and delete. Inside the program is a function that checks the key inputs:
def readline(term, width=20):
"""A rudimentary readline implementation."""
text = u''
while True:
inp = term.inkey()
if inp.code == term.KEY_ENTER:
break
elif inp.code == term.KEY_ESCAPE or inp == chr(3):
text = None
break
elif not inp.is_sequence and len(text) < width:
text += inp
echo(inp)
elif inp.code in (term.KEY_BACKSPACE, term.KEY_DELETE):
text = text[:-1]
# https://utcc.utoronto.ca/~cks/space/blog/unix/HowUnixBackspaces
#
# "When you hit backspace, the kernel tty line discipline rubs out
# your previous character by printing (in the simple case)
# Ctrl-H, a space, and then another Ctrl-H."
echo(u'\b \b')
return text
Which includes KEY_BACKSPACE and KEY_DELETE. I checked the documentation and ran a program to check the keys myself, and those are the correct names on my device. I am just unsure why all the other keys work except for those? I am on Windows 10 using the default CMD and Python 3.10 if that changes anything.
Thanks!
The code you've pasted in your question is for saving to a file. If you try using backspace when entering the file name, you'll see that it works.
You can find this in the main loop under the logic for Ctrl + S. Notice also that there is no logic here for BACKSPACE or DELETE.
def main():
...
while True:
...
inp = term.inkey()
if inp == chr(3):
# ^c exits
break
elif inp == chr(19):
# ^s saves
...
save(screen, readline(term))
...
continue
...
else:
...
...
Basically the headline.
In Python, I want to take max 280 characters of input from the user, and show a live updating counter on the CLI as the user types the input.(Similar to a Progress bar)
Getting some text to update on the screen is simple, but I don't know how to count the characters as the user is inputting them.
P.S. First-time StackOverflow user, please go easy on me. :)
EDIT:
Codebase: https://github.com/Prathamesh-Ghatole/100DaysOfCode-Writer/blob/master/main.py
I don't have a specific code snippet where I want to implement it yet.
But I basically want to take character input from the user in a loop where each iteration does the following:
Take single character input.
update a variable that counts the total number of input characters.
subtract the number of input characters from the character number limit.
Trigger a flag when the character limit is exceeded.
Using pynput package this seems to work.
from pynput.keyboard import Key, Listener
import os
length = 0
char_count = dict()
text = str()
def on_press(key):
try:
global text
global char_count
global length
if length >= 280:
print("[+] Character Limit Excided!")
return False
elif key == Key.enter:
return False
else:
os.system("cls")
if key == Key.space:
text += ' '
else:
text += f"{key.char}"
char_count[text[-1]] = char_count.get(text[-1], 0) + 1
length += 1
print(f"Enter Text: {text}")
print(f"Characters Left: {280 - length}")
print(f"Char Count {char_count}")
except:
exit()
def main():
os.system("cls")
with Listener(on_press=on_press) as listner:
print("Enter Text: ")
listner.join()
if __name__ == '__main__':
main()
I'm using raw_input in Python to interact with user in shell.
c = raw_input('Press s or n to continue:')
if c.upper() == 'S':
print 'YES'
It works as intended, but the user has to press enter in the shell after pressing 's'. Is there a way to accomplish what I need from an user input without needing to press enter in the shell? I'm using *nixes machines.
Under Windows, you need the msvcrt module, specifically, it seems from the way you describe your problem, the function msvcrt.getch:
Read a keypress and return the
resulting character. Nothing is echoed
to the console. This call will block
if a keypress is not already
available, but will not wait for Enter
to be pressed.
(etc -- see the docs I just pointed to). For Unix, see e.g. this recipe for a simple way to build a similar getch function (see also several alternatives &c in the comment thread of that recipe).
Actually in the meantime (almost 10 years from the start of this thread) a cross-platform module named pynput appeared.
Below a first cut - i.e. that works with lowercase 's' only.
I have tested it on Windows but I am almost 100% positive that it should work on Linux.
from pynput import keyboard
print('Press s or n to continue:')
with keyboard.Events() as events:
# Block for as much as possible
event = events.get(1e6)
if event.key == keyboard.KeyCode.from_char('s'):
print("YES")
Python does not provide a multiplatform solution out of the box.
If you are on Windows you could try msvcrt with:
import msvcrt
print 'Press s or n to continue:\n'
input_char = msvcrt.getch()
if input_char.upper() == 'S':
print 'YES'
curses can do that as well :
import curses, time
def input_char(message):
try:
win = curses.initscr()
win.addstr(0, 0, message)
while True:
ch = win.getch()
if ch in range(32, 127):
break
time.sleep(0.05)
finally:
curses.endwin()
return chr(ch)
c = input_char('Do you want to continue? y/[n]')
if c.lower() in ['y', 'yes']:
print('yes')
else:
print('no (got {})'.format(c))
To get a single character, I have used getch, but I don't know if it works on Windows.
Instead of the msvcrt module you could also use WConio:
>>> import WConio
>>> ans = WConio.getkey()
>>> ans
'y'
On a side note, msvcrt.kbhit() returns a boolean value determining if any key on the keyboard is currently being pressed.
So if you're making a game or something and want keypresses to do things but not halt the game entirely, you can use kbhit() inside an if statement to make sure that the key is only retrieved if the user actually wants to do something.
An example in Python 3:
# this would be in some kind of check_input function
if msvcrt.kbhit():
key = msvcrt.getch().decode("utf-8").lower() # getch() returns bytes data that we need to decode in order to read properly. i also forced lowercase which is optional but recommended
if key == "w": # here 'w' is used as an example
# do stuff
elif key == "a":
# do other stuff
elif key == "j":
# you get the point
I know this is old, but the solution wasn't good enough for me.
I need the solution to support cross-platform and without installing any external Python packages.
My solution for this, in case anyone else comes across this post
Reference: https://github.com/unfor19/mg-tools/blob/master/mgtools/get_key_pressed.py
from tkinter import Tk, Frame
def __set_key(e, root):
"""
e - event with attribute 'char', the released key
"""
global key_pressed
if e.char:
key_pressed = e.char
root.destroy()
def get_key(msg="Press any key ...", time_to_sleep=3):
"""
msg - set to empty string if you don't want to print anything
time_to_sleep - default 3 seconds
"""
global key_pressed
if msg:
print(msg)
key_pressed = None
root = Tk()
root.overrideredirect(True)
frame = Frame(root, width=0, height=0)
frame.bind("<KeyRelease>", lambda f: __set_key(f, root))
frame.pack()
root.focus_set()
frame.focus_set()
frame.focus_force() # doesn't work in a while loop without it
root.after(time_to_sleep * 1000, func=root.destroy)
root.mainloop()
root = None # just in case
return key_pressed
def __main():
c = None
while not c:
c = get_key("Choose your weapon ... ", 2)
print(c)
if __name__ == "__main__":
__main()
If you can use an external library, blessed (cross platform) can do do this quite easily:
from blessed import Terminal
term = Terminal()
with term.cbreak(): # set keys to be read immediately
print("Press any key to continue")
inp = term.inkey() # wait and read one character
Note that while inside the with block, line editing capabilities of the terminal will be disabled.
Documentation for cbreak, inkey, and an example with inkey.
Standard library solution for Unix-like operating systems (including Linux):
def getch():
import sys, termios
fd = sys.stdin.fileno()
orig = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON
new[6][termios.VMIN] = 1
new[6][termios.VTIME] = 0
try:
termios.tcsetattr(fd, termios.TCSAFLUSH, new)
return sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, orig)
This works by putting the terminal into non-canonical input mode before reading from the terminal.
Alternative solution that does not echo the user's input (e.g. if the user presses z, z will not appear on screen):
def getch():
import sys, termios, tty
fd = sys.stdin.fileno()
orig = termios.tcgetattr(fd)
try:
tty.setcbreak(fd) # or tty.setraw(fd) if you prefer raw mode's behavior.
return sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, orig)
Usage example:
print('Press s or n to continue: ', end='', flush=True)
c = getch()
print()
if c.upper() == 'S':
print('YES')
use readchar:
import readchar
key = readchar.readkey()
if key == "a":
print("text")
https://pypi.org/project/readchar/ to webpage
I am currently playing with some cmd/prompt animations/graphics:
import os
import time
def printFrame(timeout, count):
os.system('cls')
l=0
while True:
for k in range(0,9):
for i in range(0,9):
for j in range(0,9):
if j == k and i != 4:
print("|", end="", flush=True)
elif j !=k and i == 4:
print("-", end="", flush=True)
elif j ==k and i == 4:
print("+", end="", flush=True)
else:
print("O", end="", flush=True)
print("")
time.sleep(timeout)
os.system('cls')
l += 1
if l > count:
break
if __name__ == "__main__":
printFrame(0.08, 2)
and i want to get rid of frame blinking - especialy visible in first line, my idea was to use second printing thread:
def printFrame(timeout, count):
#print from example1
def printFrameTwo(timeout, count):
#print from example1 without os.system('cls')
if __name__ == "__main__":
p1 = threading.Thread(target = printFrame, args = (0.08, 2))
p2 = threading.Thread(target = printFrameTwo, args = (0.08, 2))
p1.start()
p2.start()
but the effect was rather disappointing - problems with synchronization and first line still very blinky, second idea was to use 'predefined frames' - but its not very educating - the bonus here is that I can print whole line at once, but still effect is not as expected, third (most promising) idea is to only change necessary 'pixels'/chars in frame - but here I need to move in frame between lines! and curses is not working on windows (at least not in standard). Do you maybe have some ideas how to bite it? (windows, standard libraries) maybe how to speed up 'os.system('cls')'?
I figured it out... You can use ANSI codes to move the cursor then clear the lines without any BLINK!
print('\033[4A\033[2K', end='')
\033[4A Moves the cursor 4 lines up (\033[{lines}A you can replace lines with however many you need) \033[2K Clears all those lines without the screen blinking. You can use it in a simple typewrite function that needs a constant message or a box around it like this:
from time import sleep
def typewrite(text: str):
lines = text.split('\n')
for line in lines:
display = ''
for char in line:
display += char
print(f'╭─ SOME MESSAGE OR SOMEONES NAME ────────────────────────────────────────────╮')
print(f'│ {display:74} │') # :74 is the same as ' ' * 74
print(f'╰────────────────────────────────────────────────────────────────────────────╯')
sleep(0.05)
print('\033[3A\033[2K', end='')
The only problem with this is that the top line is blinking. To fix this all we need to do is to add a empty line that is blinking so the user cant see it. We also move the cursor up from 3 to 4 lines.
def typewrite(text: str):
lines = text.split('\n')
for line in lines:
display = ''
for char in line:
display += char
print('')
print(f'╭─ SOME MESSAGE OR SOMEONES NAME ────────────────────────────────────────────╮')
print(f'│ {display:74} │') # :74 is the same as ' ' * 74
print(f'╰────────────────────────────────────────────────────────────────────────────╯')
sleep(0.05)
print('\033[4A\033[2K', end='')
To make this into your code just print your text and add a print('') at the start. Then use this print('\033[4A\033[2K', end='') but change the 4 to however many lines that you printed including the print(''). Then it should work without blinking. You can put print('\033[4B', end='') at the end which just moves the cursor back up.
If you want to hide the cursor you can use this gibberish or make the cursor the same color as the background:
import ctypes
if os.name == 'nt':
class _CursorInfo(ctypes.Structure):
_fields_ = [("size", ctypes.c_int),
("visible", ctypes.c_byte)]
def hide_cursor() -> None:
if os.name == 'nt':
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci))
ci.visible = False
ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci))
def show_cursor() -> None:
if os.name == 'nt':
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci))
ci.visible = True
ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci))
Note: All of this is still new to me so I am still testing this out to fully understand it.
I am trying to write a basic driver to control a device that only interacts with hyperterminal. All the inputs expect HT formated data and all of the returns include copious equal signs, newlines, and spaces in order to get the formatting correct for human visualization. I am pretty sure that the returns are generating trouble, as I am throwing exceptions a lot, but I am not sure how else I can handle it. Does anyone know of a better way to do this?
Device manual: http://us.startech.com/media/products/PCM815SHNA/Manuals/PCM815SHNA.pdf
import visa
import re
class iopower8(visa.SerialInstrument):
#initialization
def __init__(self,loc):
visa.SerialInstrument.__init__(self,loc)
self.term_chars = '\r' #set terminal characters
self.write('\r\r') #start faux 'Hyperterminal'
def on(self, bank, out):
self.ask("on " + str(bank) + " " + str(out))
for i in range (1,3):#read buffer into void to prevent issues
try:
self.read_raw()
except(visa_exceptions.VisaIOError):
self.buffer_clear()
break
return self.status(bank, out)
def off(self, bank, out):
self.ask("of " + str(bank) + " " + str(out))
for i in range (1,3):#read buffer into void to prevent issues
try:
self.read_raw()
except(visa_exceptions.VisaIOError):
self.buffer_clear()
break
return self.status(bank, out)
def status(self, bank, port): #enter bank and port # you want to check
self.ask("st " + str(bank))
result = 'Error' #send error message regardless
for i in range (1,13):#all 12 lines need to be read out of the buffer to prevent issues later
try:
stuff = self.read()#read the line to a holding srting, and dump in void if wriong line to clear buffer
if stuff.strip() == ('='*44):
break
except(visa_exceptions.VisaIOError):
break
for i in range(1,9):
try:
stuff = self.read()#read the line to a holding string, and dump in void if wriong line to clear buffer.
if i == port: #this offset will get you to the line with the relevant port's status
result = re.match('(.*?)(ON|OFF)', stuff) #regex to the find the on/off status
#convert to boolean
if result.group(2) == 'ON':
result = True
elif result.group(2) =='OFF':
result = False
else:
result = 'ERROR'
except(visa_exceptions.VisaIOError):
self.buffer_clear()
break
return result
def buffer_clear(self): #in case of buffer jamming
while True:
try:
self.read_raw()
except(visa_exceptions.VisaIOError):
break
def all_on(self, bank):
self.ask("on " + str(bank) + " 0")
for i in range (1,3):#read buffer into void to prevent issues
try:
hold = self.read_raw()
except(visa_exceptions.VisaIOError):
self.buffer_clear()
break
def all_off(self, bank):
self.ask("of " + str(bank) + " 0")
for i in range (1,3):#read buffer into void to prevent issues
try:
self.read_raw()
except(visa_exceptions.VisaIOError):
self.buffer_clear()
break
There's nothing special about HiperTerminal. The end of line character is usually '\r\n' or '\n' alone.