Python: Printing data and receiving user input simultaneously - python-3.x

I want to be able to receive user input and print stuff simultaneously, without them interfering. Ideally, that would be printing regularly and having the user type input to the bottom of the terminal window, for example:
printed line
printed line
printed line
printed line
printed line
(this is where the next print would happen)
Enter Input: writing a new input...
This should look like a chat app or something of that sort.
If there is no good way to do that, any way to print above the input line would be amazing too.
Thanks!

Sadly it is not very feasible to both take input and give output in python, without importing modules to directly interact with the OS.
But you can can get pretty close to it with this code:
import curses
import random # for random messages
#this should be async
def get_message():
message = [str(random.randint(0,9)) for i in range(15)]
return "".join(message)
def handle_command(cmd): # handle commands
if cmd=="exit":
exit(0)
def handle_message(msg): # send a message
raise NotImplementedError
def draw_menu(stdscr):
stdscr.erase()
stdscr.refresh()
curses.raw()
k = 0
typed=""
while True:
# Initialization
stdscr.erase()
height, width = stdscr.getmaxyx()
stdscr.addstr(0, 0, "Welcome to chat app")
msg = get_message()
if msg: # add a message if it exists
stdscr.addstr(1, 0, msg)
if k==ord("\n"): # submit on enter
if typed.startswith("/"): # execute a command
typed = typed[1:]
handle_command(typed)
elif typed.startswith("./"): # bypass commands with a dot
typed = typed[1:]
handle_message(typed)
else:
handle_message(typed) # send the message
typed = ""
elif k==263: # Backspace
typed = typed[:-1] # erase last character
stdscr.addstr(height-1, 0, typed)
elif k==3: # Ctr+C
typed="" # Delete the whole string
stdscr.addstr(height-1, 0, typed)
elif k!=0:
typed += chr(k) # add the char to the string
stdscr.addstr(height-1, 0, typed)
stdscr.refresh() # refresh
# Wait for next input
k = stdscr.getch()
def main():
curses.wrapper(draw_menu)
if __name__ == "__main__": # dont import
main()
the only thing that is to do to update the messages is to type a new char.
And I do not recommend you to build a chat in the terminal for anything other then educational value (trust me I tried it).
It would be better if you tried it using a web platform (e.g. Tauri or Electron)
Also the code cannot:
insert automatic line breaks (it errors)
send any messages (must implement yourself)
show any user names (must implement yourself)

Related

Terminal ignore input while python loop is running

So I am creating a program that executes commands based on live user input. To do this I am using pynput (essentially a keylogger) which reads in each keystroke while the program is active. The problem I am running into is that each keystroke is still being read into the terminal, so once the program ends, the terminal is bombarded with a random string of characters and/or keystrokes. Is there anyway I can get the terminal to ignore these inputs while the program is running?
Code snippet below.
def on_press(key):
global current_command
try:
# check to see if command is standard letter key, if it is, convert it to a command
current_command = commands.Command(key.char)
except AttributeError:
# if not a letter command, check if it's a valid keyboard command; if it is, convert to command
# TODO: finalize valid keys
valid_keys = {
"Key.space": "space",
"Key.shift": "shift",
"Key.tab": "tab",
"Key.enter": "enter",
"Key.caps_lock": "caps",
"Key.esc": "escape"
}
valid_command = valid_keys.get(str(key))
if valid_command is not None:
current_command = commands.Command(valid_command)
else:
# not a recognized command, creating command that will be logged, but ignored
current_command = commands.Command(str(key))
# stops listener
return False
def run_rover():
while True:
# during loop, inputs must be ignored by terminal
command: commands.Command = input.collect_input()
print(command)
output = execute.execute(command)
if output is not None:
print("->" + output + "<-")
if output == "powering off":
exit(0)

Convert text to speech on mac when button pressed

I am trying to create a script which will read a text file and then speak one line of the file every time I press a button on my keyboard. I am using the system accessibility voice on my Mac as the voice sounds more human than some of the python modules I tried.
So far my script runs and speaks a line but the spoken lines are not in order but rather are just lines spoken at random, whereas I would like each line to be spoken once and in order.
Also, I would rather use another key (not alphanumeric) such as right arrow to invoke the function but not sure how to specify that in my script?
I am just learning to code so any help would be much appreciated.
I am using Python 3.9.1 on Mac OSX 10.15.5
filename = ('file.txt')
with open(filename) as f:
lines = f.readlines()
# for x in lines:
# pass
def say():
while True:
try: # used try so that if any key other than the specified key is pressed an error will not be shown
for x in lines:
if keyboard.is_pressed('l'): # if key 'l' is pressed
subprocess.call(['say', x])
print('You Pressed A Key!')
#break # finishing the loop
except:
break # this should break loop if user presses a key other than the given key but doesn't work
say()
Ok I have now managed to fix the issue of reading from a file and reading each line in sequence (see clode below).
And aparantly you can just specify the key with 'up' or 'right' which works. It does output random characters to the screen so if anyone has a better way of doing this, I'd love to hear it
import keyboard
import subprocess
filename = ('text.txt')
with open(filename) as f:
lines = f.readlines()
def say():
i=0
while i <len(lines):
try: # used try so that if any key other than the specified key is pressed an error will not be shown
if keyboard.is_pressed('up'): # if key 'up' is pressed
subprocess.call(['say', lines[i]])
i += 1
except:
break # this should break loop if user presses a key other than the given key but doesn't work
say()
import keyboard
import subprocess
CURRENT_LINE = 0 #records line to be read out, first line is 0.
def ttsLine(line): #TTS function, uses macs say as per you requested.
try:
k = subprocess.check_output(["say",line.strip()]) #using subprocess, line.strip() to clean each line.
return True #returns true so we know if say command was successful
except:
return False #returns false if exceptions occured.
filename = ('text.txt') #text file with lines to be read.
with open(filename) as f:
lines = f.readlines() #saving lines to array.
while True:
keyboard.wait("up") #Waits until up key is pressed.
if not ttsLine(lines[CURRENT_LINE]): #if ttsLine returned false, something went wrong and script halts.
print("Something went wrong while reading out", lines[CURRENT_LINE])
break
CURRENT_LINE += 1 #else we update line to next one
if CURRENT_LINE == len(lines): #if all lines have been read, script ends.
break

how can i keep/ save the user input in dictionary?

import pandas as pd
from pandas import DataFrame
words = {}
def add_word():
ask = input("Do You Want To Add a New Word?(y/n): ")
if ask == 'y':
new_word = input("type the word you want to add: ")
word_meaning = input("type the word meaning: ")
words[new_word] = [word_meaning]
elif ask == 'n':
pass
add_word()
table = pd.DataFrame(data=words)
table_transposed = table.transpose()
print(table_transposed)
as you can see, i want to make a dictionary but i don't know how to save the user's input.
i want to take the user input and save it in the dictionary, so the next time he uses the program he can see everything he added before
When you make and populate (fill) a dictionary in a running Python program, that dictionary only exists as long as the program is running. When you close the program - that memory is wiped and any modifications that are made are not stored.
As Tomerikoo pointed out, this solution: shelving dictionaries will allow you to preserve your dictionary after the program is closed.
I copy the code from the link (jabaldonedo's solution) and annotate it for you for clarity.
import shelve # this is a Python library that allows you to store dictionaries after the program is closed
data = {'foo':'foo value'} # this is a mock-up dictionary. "words" in your case
d = shelve.open('myfile.db') # this creates a storage container in the program folder that can store multiple dictionaries. You can see this file appear when this code runs.
d['data'] = data # you make a section in that storage container, give it a name, e.g. "data" in this case, and store your dictionary in that section. You will store your "words" here.
d.close() # close the storage container if you do not intend to put anything else inside.
When you close and open up the program, the dictionary will not automatically pop into your running memory - you need to write code to access it. It can be made as an option in your game menu, e.g. "Load existing dictionary of words".
Back to jabaldonedo's solution:
import shelve # no need to import again, if you are writing in the same python program, this is for demonstration
d = shelve.open('myfile.db') # open the storage container where you put the dictionary
data = d['data'] # pull out the section, where you stored the dictionary and save it into a dictionary variable in the running program. You can now use it normally.
d.close() # close the storage container if you do not intend to use it for now.
EDIT: Here is how this could be used in the specific context provided in your answer. Note that I imported an additional library and changed the flags in your shelve access commands.
As I mentioned in my comment, you should first attempt to load the dictionary before writing new things into it:
import shelve
import dbm # this import is necessary to handle the custom exception when shelve tries to load a missing file as "read"
def save_dict(dict_to_be_saved): # your original function, parameter renamed to not shadow outer scope
with shelve.open('shelve2.db', 'c') as s: # Don't think you needed WriteBack, "c" flag used to create dictionary
s['Dict'] = dict_to_be_saved # just as you had it
def load_dict(): # loading dictionary
try: # file might not exist when we try to open it
with shelve.open('shelve2.db', 'r') as s: # the "r" flag used to only read the dictionary
my_saved_dict = s['Dict'] # load and assign to a variable
return my_saved_dict # give the contents of the dictionary back to the program
except dbm.error: # if the file is not there to load, this error happens, so we suppress it...
print("Could not find a saved dictionary, returning a blank one.")
return {} # ... and return an empty dictionary instead
words = load_dict() # first we attempt to load previous dictionary, or make a blank one
ask = input('Do you want to add a new word?(y/n): ')
if ask == 'y':
new_word = input('what is the new word?: ')
word_meaning = input('what does the word mean?: ')
words[new_word] = word_meaning
save_dict(words)
elif ask == 'n':
print(words) # You can see that the dictionary is preserved between runs
print("Oh well, nothing else to do here then.")
import shelve
words = {}
def save_dict(words):
s = shelve.open('shelve2.db', writeback=True)
s['Dict'] = words
s.sync()
s.close()
def load_dict():
s = shelve.open('shelve2.db', writeback=True)
dict = s['Dict']
print(dict)
s.close()
ask = input('Do you want to add a new word?(y/n): ')
if ask == 'y':
new_word = input('what is the new word?: ')
word_meaning = input('what does the word mean?: ')
words[new_word] = word_meaning
save_dict(words)
elif ask == 'n':
load_dict()
so this is my code after making the save_dict and load_dict functions, it works fine but when i run the program and write a new_word and word_meaning it overwrites the previous data, i believe i am missing something in the save_dict function, if you can point the problem to me i would be so grateful

Format controlled input using PyInquirer

I want to create a script that uses a time input from the user. At the moment I am using PyInquirer because it looks cool but I am not sure if it is capable of doing what I need.
The first prompt asks the user to choose Duration or Time like so:
If the user then chooses Time the next prompt looks as follows:
Here the input must only have the default format of HH:MM [AM/PM] where values can only be selected by using the up and down arrows or typed. The cursor must ignore and skip the ':'.
Therefore, the HH and MM may be typed or spinned using the up and down arrow keys and the AM/PM are spinned as well. If typing the HH and MM the cursor must automatically skip the colon.
Here is my code so far:
from PyInquirer import prompt, print_json
typeQ = [
{
'type' : 'list',
'name' : 'type',
'message' : 'Duration or Time based?',
'choices' : [
'Duration',
'Time'
]
}
]
time = [
{
'type' : 'input',
'name' : 'time',
'message' : 'Specify time:'
}
]
typeA = prompt(typeQ)
if (typeA == 'Duration'):
pass
else:
time = prompt(time)
This is an example how to use pythons curses library with arrows as key input in a while loop. The mytime() method is only accessed via the arrow key strokes. Some additional keys are also added such you can play with it. See "#" for inline comments.
Please post next time your example code and what you've tried fist which is behind the screens you show in the question. Its sailing a little blind without code. If included it would make life easier to anticipate on what structure you use. It also helps up-voting your question instead of people down-voting it due to so-called lack of effort.
Now you have what I've posted and possibly you have to retrofit it to work for your own code. It was a fun morning exercise. Enjoy.
import curses, sys, time, re
screen = curses.initscr()
curses.curs_set(0)
screen.keypad(True)
curses.noecho()
regex_time = re.compile('[0-9][0-9][:][0-9][0-9]') # time expression selection key.
def mytime():
screen.addstr("\nTime entry please + [enter]. Input format is [HH:mm].\n")
event = screen.getstr(5) # use (0, 0, 15) for location of input string top left in cmd window.
if regex_time.match(event.decode()):
screen.addstr("\n You entry = %s h.\n\n >> Add code to do something else here << \n\n" % event.decode())
screen.refresh()
time.sleep(2)
# do something else goes here.
else:
screen.addstr('\nDid not receive time\nTry again next round.\n')
screen.refresh()
time.sleep(2)
# do something else goes here.
while True:
try:
screen.addstr("Press a key\n")
curses.echo()
event = screen.getch() # get single character
screen.refresh()
if event == curses.KEY_LEFT:
screen.addstr("Left Arrow Key pressed\n")
screen.refresh()
time.sleep(0.5)
mytime()
elif event == curses.KEY_RIGHT:
screen.addstr("Right Arrow Key pressed\n")
screen.refresh()
time.sleep(2)
elif event == 81: # 'Q' for quite program.
curses.echo() # shows which char you pressed.
screen.addstr('\nThe Quite key "%s" was pressed\n' % (event))
screen.refresh()
time.sleep(3)
break
else:
# keystroke identification except for above and regular expression that are comform time annotation.
curses.echo() # shows which char you pressed.
screen.addstr('\nThe key "%s" was pressed\n' % (event))
time.sleep(2)
screen.refresh()
except Exception as err:
# continue
print (err)
# below code could replace the break in the quite elif statement.
screen.addstr("\nExit program.\n")
screen.refresh()
time.sleep(3)
curses.endwin()

How to read keypresses in the background in python

I get that it's a very difficult thing to pick up on in the background, but what I'm looking for is a program that records a keypress, and how much time it takes between keypresses. None of what I have looked into was able to record in the background, or actually work.
EDIT:
import win32con, ctypes, ctypes.wintypes
def esc_pressed():
print("Hotkey hit!")
ctypes.windll.user32.RegisterHotKey(None, 1, 0, 0xDD) # this binds the ']' key
try:
msg = ctypes.wintypes.MSG()
ctypes.windll.user32.GetMessageA
while ctypes.windll.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
esc_pressed()
ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))
ctypes.windll.user32.DispatchMessageA(ctypes.byref(msg))
finally:
ctypes.windll.user32.UnregisterHotKey(None, 1)
This allows for the program to work in the background, but it takes the inputted character you bound, instead of picking up on it. I still need to make sure the inputted character gets to the window with focus.
You probably have to catch the key and then simulate the same key press again. Try checking out Python Keyboard module for that.
EDIT: Added code sample.
import keyboard, time
def KeyPressSample(startkey='tab', endkey='esc'):
while True: # making a inifinte loop
try:
if keyboard.is_pressed(startkey):
time.sleep(0.25)
print("%s Key pressed." % startkey)
elif keyboard.is_pressed(endkey):
print("%s Key pressed." % endkey)
break
except KeyboardInterrupt:
print('\nDone Reading input. Keyboard Interupt.')
break
except Exception as e:
print(e)
break

Resources