I'm creating part of a program right now for a personal project and I need some help on one aspect of it.
Here is how the program works:
User enters the amount of time to run
User enters the text - Files are modified
Timer is started
optional User can enter "password" to interrupt the timer
Actions are reversed
I have all of the steps coded except the Timer because I'm trying to figure out the best way to do this. Ideally, I'd like the timer to be displaying a countdown, and if the user enters a certain "password" the timer is interrupted and it skips to step number 5.
Would the best way to do this be with a thread? I haven't worked much with threads in the past. I just need someway for the timer to be displayed while also giving control back to the user in case they want to enter that password.
Thanks for any help you provide.
Here's the code:
import time
import urllib
import sys
def restore():
backup = open(r'...backupfile.txt','r')
text = open(r'...file.txt', 'w+')
text.seek(0)
for line in backup:
text.write(line)
backup.close()
text.close()
text = open(r'...file.txt', 'a+')
backup = open(r'...backupfile.txt','w+')
text.seek(0)
for line in text:
backup.write(line)
backup.close()
while True:
url = raw_input('Please enter a URL: ')
try:
if url[:7] != 'http://':
urllib.urlopen('http://' + url)
else:
urllib.urlopen(url)
except IOError:
print "Not a real URL"
continue
text.write(url)
while True:
choice = raw_input('Would you like to enter another url? (y/n): ')
try:
if choice == 'y' or choice == 'n':
break
except:
continue
if choice == 'y':
text.seek(2)
continue
elif choice == 'n':
while True:
choice = raw_input('Would you to restore your file to the original backup (y/n): ')
try:
if choice == 'y' or choice == 'n':
break
except:
continue
if choice == 'y':
text.close()
restore()
sys.exit('Your file has been restored')
else:
text.close()
sys.exit('Your file has been modified')
As you can see, I haven't added the timing part yet. It's pretty straight forward, just adding urls to a text file and then closing them. If the user wants the original file, reverse() is called.
Under Windows you can use msvcrt to ask for a key. Asking for a password is actually more complex, because you have to track several keys. This program stops with F1.
import time
import msvcrt
from threading import Thread
import threading
class worker(Thread):
def __init__(self,maxsec):
self._maxsec = maxsec
Thread.__init__(self)
self._stop = threading.Event()
def run(self):
i = 1
start = time.time()
while not self.stopped():
t = time.time()
dif = t-start
time.sleep(1) # you want to take this out later (implement progressbar)
# print something once in a while
if i%2==0: print '.',
#check key pressed
if msvcrt.kbhit():
if ord(msvcrt.getch()) == 59:
self.stop()
#do stuff
# timeout
if dif > self._maxsec:
break
i+=1
def stop(self):
print 'thread stopped'
self._stop.set()
def stopped(self):
return self._stop.isSet()
print 'number of seconds to run '
timeToRun = raw_input()
#input files
#not implemented
#run
w = worker(timeToRun)
w.run()
#reverse actions
Related
I need to design a script that uses the top portion of the terminal as output where some lines are printed after each second in an infinite loop, and the bottom portion keeps taking user input and also printing them in the above portion (among the regular periodic outputs).
In other words, I need to design a sort of shell.
I tried multithreading with the naive approach like this:
#!/usr/bin/python3
from math import acos
from threading import Thread
from random import choice
from time import sleep
from queue import Queue, Empty
commandQueue = Queue()
def outputThreadFunc():
outputs = ["So this is another output","Yet another output","Is this even working"] # Just for demo
while True:
print(choice(outputs))
try:
inp = commandQueue.get(timeout=0.1)
if inp == 'exit':
return
else:
print(inp)
except Empty:
pass
sleep(1)
def inputThreadFunc():
while True:
command = input("> ") # The shell
if command == 'exit':
return
commandQueue.put(command)
# MAIN CODE
outputThread = Thread(target=outputThreadFunc)
inputThread = Thread(target=inputThreadFunc)
outputThread.start()
inputThread.start()
outputThread.join()
inputThread.join()
print("Exit")
But as obviously expected, the output lines merge with the input lines as the user keeps typing.
Any ideas?
As discussed in comments, used curses library.
Update
used two subwin for input and output
#!/usr/bin/python3
import curses
from math import acos
from threading import Thread
from random import choice
from time import sleep
from queue import Queue, Empty
commandQueue = Queue()
stdscr = curses.initscr()
stdscr.keypad(True)
upperwin = stdscr.subwin(2, 80, 0, 0)
lowerwin = stdscr.subwin(2,0)
def outputThreadFunc():
outputs = ["So this is another output","Yet another output","Is this even working"] # Just for demo
while True:
upperwin.clear()
upperwin.addstr(f"{choice(outputs)}")
try:
inp = commandQueue.get(timeout=0.1)
if inp == 'exit':
return
else:
upperwin.addch('\n')
upperwin.addstr(inp)
except Empty:
pass
upperwin.refresh()
sleep(1)
def inputThreadFunc():
while True:
global buffer
lowerwin.addstr("->")
command = lowerwin.getstr()
if command:
command = command.decode("utf-8")
commandQueue.put(command)
lowerwin.clear()
lowerwin.refresh()
if command == 'exit':
return
# MAIN CODE
outputThread = Thread(target=outputThreadFunc)
inputThread = Thread(target=inputThreadFunc)
outputThread.start()
inputThread.start()
outputThread.join()
inputThread.join()
stdscr.keypad(False)
curses.endwin()
print("Exit")
Old Solution
I've edited your example to use getch insted of input
#!/usr/bin/python3
import curses
import datetime
from math import acos
from threading import Thread
from random import choice
from time import sleep
from queue import Queue, Empty
INFO_REFRESH_SECONDS = 1
commandQueue = Queue()
buffer = list() # stores your input buffer
stdscr = curses.initscr()
stdscr.keypad(True)
def outputThreadFunc():
outputs = ["So this is another output","Yet another output","Is this even working"] # Just for demo
info = choice(outputs), datetime.datetime.now()
while True:
if datetime.datetime.now() - info[1] > datetime.timedelta(seconds=INFO_REFRESH_SECONDS):
# refresh info after certain period of time
info = choice(outputs), datetime.datetime.now() # timestamp which info was updated
inp = ''
buffer_text = ''.join(buffer)
try:
command = commandQueue.get(timeout=0.1)
if command == 'exit':
return
inp = f"\n{command}"
except Empty:
pass
output_string = f"{info[0]}{inp}\n->{buffer_text}"
stdscr.clear()
stdscr.addstr(output_string)
stdscr.refresh()
if inp:
# to make sure you see the command
sleep(1)
def inputThreadFunc():
while True:
global buffer
# get one character at a time
key = stdscr.getch()
curses.echo()
if chr(key) == '\n':
command = ''.join(buffer)
commandQueue.put(command)
if command == 'exit':
return
buffer = []
elif key == curses.KEY_BACKSPACE:
if buffer:
buffer.pop()
else:
buffer.append(chr(key))
# MAIN CODE
outputThread = Thread(target=outputThreadFunc)
inputThread = Thread(target=inputThreadFunc)
outputThread.start()
inputThread.start()
outputThread.join()
inputThread.join()
stdscr.keypad(False)
curses.endwin()
print("Exit")
The simplest solution is to use two scripts; One, a server that prints the output, and the other, a client that sends the user's input to the server. Then you can use a standard solution like tmux to open the two scripts in two panes.
The two are merging because of the way the terminal writes to the output. It collects outputs in a buffer, and when the time is right it outputs everything at once. An easy fix would be to use a '\n' before each actual statement so that each new output is on a separate line.
#!/usr/bin/python3
.
.
.
if inp == 'exit':
return
else:
print("\n", inp) # CHANGE OVER HERE
.
.
.
command = input("\n> ") # CHANGE OVER HERE
if command == 'exit':
return
.
.
.
print("Exit")
Beware that since two threads are running in parallel, the next output will print before you are done typing and pressing enter to the input (unless you can type really fast and have really fast reflexes). Hope this answers your question!
I want to produce an endless loop that would to different things depending on some user input.
When the .py is executed the loop starts and does some 'main' programm and the input window opens for the user. When typing 'alt1' the loop jumps in to the function 'main_alt1' and so on.
user_input = 'main'
user_input = input()
while True:
if user_input == 'main'
main()
elif user_input == 'alt1'
main_alt1()
elif user_input == 'exit'
exit()
The problem here is that the input is either given once before the loop (like in the example) or it stops the loop when the input is inside the loop until the input is given.
Does anyone has a smart way to do something like that. It doesn't need to be with input().
I think it's better to use a class to process the user input:
(I updated the code with the process method)
from multiprocessing import Process
from time import sleep
class InvalidAction(Exception):
pass
class Cmd:
def __init__(self):
self._active_thread = None
def _action_hi(self):
while True:
print('Hi!')
sleep(1)
def _action_ping(self):
while True:
print('Pong!')
sleep(1)
#staticmethod
def _get_method_name(action):
return f'_action_{action}'
def process(self, action: str):
method_name = self._get_method_name(action)
if not hasattr(self, method_name):
raise InvalidAction
if self._active_thread is not None:
self._active_thread.terminate()
self._active_thread = Process(target = getattr(self, method_name, None))
self._active_thread.start()
def main():
cmd = Cmd()
while True:
try:
user_input = input('Action: ')
cmd.process(user_input)
except InvalidAction as e:
print(f'Invalid Action!')
except KeyboardInterrupt:
print('Exiting the loop.')
break
except Exception as e:
print(f'Something went wrong - {e}')
if __name__ == '__main__':
main()
user_input = 'main'
user_input = input()
while True:
if user_input == 'main'
main()
elif user_input == 'alt1'
main_alt1()
elif user_input == 'exit'
exit()
user_input = input()
Taking the the input again at the end of loop works. Since it is while True it runs infinitely till user enters exit
after calling every function you can again take update from user to change variable
import keyboard
while True:
if keyboard.read_key() == 'a':
main_alt1()
elif keyboard.read_key() == 'b':
main_alt2()
elif keyboard.read_key() == 'e':
exit()
else:
main()
Simple "input" in python:
code = input("Entrer your code...")
processCode(code)
I need to ask the user for a password on a usb keyboard but without a screen (so the user doesn't see what he is typing). The text here is just for some tests. The sending is validated by the Enter key of course.
To make sure that the input is always blank when the user starts typing and sends his code, I will need to add a condition to this input.
I would need some sort of time counter which starts after each character entered and if the Enter key is not pressed for 10 seconds, the input will be automatically reset.
Here is an example of code that approximates your question. You can improve it or take inspiration from it:
import keyboard
import time
from threading import Thread
start_time = time.time()
saved_pwd = False
stop_thread = False
def dedupe(items):
seen = set()
for item in items:
if item not in seen:
yield item
seen.add(item)
def count_time():
global saved_pwd, start_time
while True:
lap_time = round(time.time() - start_time, 2)
global stop_thread
if lap_time >= 10:
print("Please Re-enter the password")
saved_pwd = True
break
elif stop_thread:
break
password = []
Thread(target=count_time).start()
while saved_pwd is False:
key = keyboard.read_key()
start_time = time.time()
if key == 'enter':
saved_pwd = True
stop_thread = True
else:
password.append(key)
print("Your pwd: ", ''.join(dedupe(password)))
I try to make a simple game, give a sum random number and input to answer it, I try to limit the time for input, but I can stop the input processing when timeout.
score=0
from threading import Timer
while score<=3:
import random
a=random.randint(0,100)
b=random.randint(0,100)
sum=a+b
d=str(sum)
while True:
print(a,"+",b,"=")
timeout = 3
t = Timer(timeout, print, ['Sorry, times up'])
t.start()
prompt = "you have %d s to input your answer\n" % timeout
c = input(prompt)
t.cancel()
#i want to stop input c and make other code like 'do you want to play again'
if c.isdigit():
break
else:
print('invalid input')
continue
result=c
if result==d:
score=score+1
print('your score',score)
else:
score=score-1
print('your score',score)
else:
print('you win')
Similar question has been answered before here.
I have tested it before and the following codes work in my Ubuntu. I'm not sure if this works in Windows too.
import sys
from select import select
timeout = 5
print "What is your name?"
# Assignment to three different variables based on select() paramenters
# rlist: wait until ready for reading
# wlist: wait until ready for writing
# xlist: wait for an "exceptional condition"
rlist, _, _ = select([sys.stdin], [], [], timeout)
if rlist:
s = sys.stdin.readline()
print "Your name is: %s" %(s)
else:
print "Timeout!! Try again."
Hope this helps.
This works also for me (Debian)
import sys, termios, signal
from _thread import interrupt_main
while True:
timeout = 3
signal.signal(signal.SIGALRM, lambda x,y: interrupt_main())
signal.alarm(3)
try:
c = input("Number? ")
except:
termios.tcflush(sys.stdin, termios.TCIOFLUSH)
answer = input('\nContinue? ')
if (len(answer) == 0) or (answer.lower()[0] != 'y'):
break
signal.alarm(0)
If you are using Windows you could try using interrupt_main as handler for Timer
t = Timer(timeout, interrupt_main)
This approach does not fully work for me. In my linux box the signal handler is able to interrupt the input() call but the timer handler, however, not. Only if you press Enter, the program flow follows to the "Continue" question
How would I write a Python program that would always be looking for user input. I think I would want to have a variable equal to the input and then something different would happen based on what that variable equaled. So if the variable were "w" then it would execute a certain command and keep doing that until it received another input like "d" Then something different would happen but it wouldn't stop until you hit enter.
If you want to constantly look for an user input you'll need multithreading.
Example:
import threading
import queue
def console(q):
while 1:
cmd = input('> ')
q.put(cmd)
if cmd == 'quit':
break
def action_foo():
print('--> action foo')
def action_bar():
print('--> action bar')
def invalid_input():
print('---> Unknown command')
def main():
cmd_actions = {'foo': action_foo, 'bar': action_bar}
cmd_queue = queue.Queue()
dj = threading.Thread(target=console, args=(cmd_queue,))
dj.start()
while 1:
cmd = cmd_queue.get()
if cmd == 'quit':
break
action = cmd_actions.get(cmd, invalid_input)
action()
main()
As you'll see this, will get your messages a little mixed up, something like:
> foo
> --> action foo
bar
> --> action bar
cat
> --> Unknown command
quit
That's beacuse there are two threads writing to stdoutput at the same time. To sync them there's going to be need of lock:
import threading
import queue
def console(q, lock):
while 1:
input() # Afther pressing Enter you'll be in "input mode"
with lock:
cmd = input('> ')
q.put(cmd)
if cmd == 'quit':
break
def action_foo(lock):
with lock:
print('--> action foo')
# other actions
def action_bar(lock):
with lock:
print('--> action bar')
def invalid_input(lock):
with lock:
print('--> Unknown command')
def main():
cmd_actions = {'foo': action_foo, 'bar': action_bar}
cmd_queue = queue.Queue()
stdout_lock = threading.Lock()
dj = threading.Thread(target=console, args=(cmd_queue, stdout_lock))
dj.start()
while 1:
cmd = cmd_queue.get()
if cmd == 'quit':
break
action = cmd_actions.get(cmd, invalid_input)
action(stdout_lock)
main()
Ok, now it's better:
# press Enter
> foo
--> action foo
# press Enter
> bar
--> action bar
# press Enter
> cat
--> Unknown command
# press Enter
> quit
Notice that you'll need to press Enter before typing a command to enter in "input mode".
from http://www.swaroopch.com/notes/Python_en:Control_Flow
#!/usr/bin/python
# Filename: while.py
number = 23
running = True
while running:
guess = int(input('Enter an integer : '))
if guess == number:
print('Congratulations, you guessed it.')
running = False # this causes the while loop to stop
elif guess < number:
print('No, it is a little higher than that.')
else:
print('No, it is a little lower than that.')
else:
print('The while loop is over.')
# Do anything else you want to do here
print('Done')
Maybe select.select is what you are looking for, it checks if there's data ready to be read in a file descriptor so you can only read where it avoiding the need to interrupt the processing (well, in the example it waits one second but replace that 1 with 0 and it'll work perfectly):
import select
import sys
def times(f): # f: file descriptor
after = 0
while True:
changes = select.select([f], [], [], 1)
if f in changes[0]:
data = f.readline().strip()
if data == "q":
break
else:
print "After", after, "seconds you pressed", data
after += 1
times(sys.stdin)
if you want to get input repeatedly from user;
x=1
while x==1:
inp = input('get me an input:')
and based on inp you can perform any condition.
You can also use definitions, say, something like this:
def main():
(your main code)
main()
main()
though generally while loops are much cleaner and don't require global variables :)