I am writing a program in Python to run on a Raspberry Pi in order to control my Wurlitzer jukebox. The program current accepts the codes for "record selection" (A1, B1, C4, etc.), add those codes to a playlist, and executes the list. My issue is that once a song starts, I would like to be able to press a button ("Y" in the current code) to skip the currently playing song. I can't get this to work.
If I use "player.next()" I get an error: 'MediaPlayer' object has no attribute 'next'.
I tried to stop the player and restart it (thinking it would pick up the next song in the Playlist. This doesn't even stop the player.
I do not want to use subprocess if I can avoid it. I'd like to figure out a way within Python to do the skipping. How would one accomplish this?
import os, sys, csv, vlc, time, threading
from pynput.keyboard import Key, Listener
DefaultUSBPath="/media/pi"
PlayHistory="PlayHistory.csv"
#
# Declare variables
#
USBDrive = None
Action = None
Playlist = []
SelectionCount = []
Sel_char = None
#
# Find the USB Drive
#
for item in os.listdir(DefaultUSBPath):
if os.path.isdir(os.path.join(DefaultUSBPath, item)):
if USBDrive is None:
USBDrive = os.path.join(DefaultUSBPath, item)
else:
USBDrive = USBDrive + ";" + os.path.join(DefaultUSBPath, item)
if USBDrive is None:
print ("Error(0) - No USB Drive detected")
sys.exit()
elif ";" in USBDrive:
print ("Error(1) - More than one USB Drive detected.")
sys.exit()
#
# Adding to playlist - Returns directory contents and adds to playlist
#
def addplaylist(track):
list = None
if os.path.isdir(os.path.join(USBDrive, track)):
files = [f for f in os.listdir(os.path.join(USBDrive, track)) if os.path.isfile(os.path.join(USBDrive, track, f))]
for f in files:
if list is None:
list = os.path.join(USBDrive, track, f)
else:
list = list + ";" + os.path.join(USBDrive, track, f)
else:
print ("Error(2) - Selection is invalid")
if list is not None:
if ";" in list:
list = list.split(";")
else:
print ("Error(3) - Selection has no media")
return list
#
# MediaPlayer function
#
def MusicPlayer(P):
global Playlist, player
while len(Playlist) > 0:
song=Playlist.pop(0)
print("Song: ")
print(song)
player=vlc.MediaPlayer(song)
player.play()
#
# Define keyboard actions
#
def on_press(key):
global Action, Playlist, player
try:
Sel_char = int(key.char)
except:
try:
Sel_char = str(key.char)
Sel_char = Sel_char.upper()
except:
Sel_char = None
if Sel_char == "Z":
return False
elif Sel_char == "Y":
print("Skip")
#player.next() This line causes 'MediaPlayer' object has no attribute 'next'
time.sleep(1)
MusicPlayer(Playlist)
elif type(Sel_char) == str:
Action = Sel_char
elif type(Sel_char) == int:
Action = Action + str(Sel_char)
print("Action: " + Action)
Plist = addplaylist(Action)
if Plist is not None:
print("Added to playlist")
Playlist.append(Plist)
print(Plist)
MusicPlayer(Playlist)
else:
pass
#
# Read keyboard input
#
with Listener(on_press=on_press) as listener:
listener.join()
print ("")
print ("Have a nice day!")
print ("")
sys.exit()
The way you have it coded, I expect you'd have to stop it, remove the current media from the list, then re-start it.
However you may be better off running with a media_list_player, see below for a bare bones version. Note, I'm on Linux and had to hack some code to get the key input, rather than using a specific library or spend time on it, but it should give you enough to work with.
Edit
I apologise, there is a much simpler method when using a media_list_player, although if you want finer control you should use the media_list_player where you control the list's index or for full control use a media_player_new() but that's beyond the scope of this question. (I'll leave the original answer below this code, as it may be useful)
import vlc
import time
## pinched from vlc for keyboard input
import termios
import tty
import sys
mymedia = ["vp.mp3","vp1.mp3","happy.mp3","V1.mp4"]
def getch(): # getchar(), getc(stdin) #PYCHOK flake
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
return ch
## end pinched code
def jukebox():
player.play_item_at_index(0)
while True:
time.sleep(0.25)
k = getch()
if k == "n": #Next
player.next()
if k == "p": #Previous
player.previous()
if k == " ": #Pause
player.pause()
if k == "q": #Quit
player.stop()
return True
player.next()
vlc_instance = vlc.Instance('--no-xlib --quiet ') # no-xlib for linux and quiet don't complain
player = vlc_instance.media_list_player_new()
Media = vlc_instance.media_list_new(mymedia)
player.set_media_list(Media)
print("Welcome to Jukebox")
print("Options - space = Play/Pause, n = Next, p = Previous, q = Quit")
print("Media",mymedia)
jukebox()
Original code
import vlc
import time
## pinched from vlc for keyboard input
import termios
import tty
import sys
def getch(): # getchar(), getc(stdin) #PYCHOK flake
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
return ch
## end pinched code
vlc_instance = vlc.Instance()
player = vlc_instance.media_list_player_new()
mymedia = ["vp.mp3","vp1.mp3","happy.mp3"]
Media = vlc_instance.media_list_new(mymedia)
player.set_media_list(Media)
for index, name in enumerate(mymedia):
print("Playing:",name)
player.play_item_at_index(index)
time.sleep(1)
while player.get_state() != 6:
time.sleep(1)
k = getch()
if k == "y":
player.stop()
break
Related
Can I stop listening for audio on keyboard press?
I tried altering the record function (in init.py) like this:
def record(self, source, duration=None, offset=None):
"""
Records up to ``duration`` seconds of audio from ``source`` (an ``AudioSource`` instance) starting at ``offset`` (or at the beginning if not specified) into an ``AudioData`` instance, which it returns.
If ``duration`` is not specified, then it will record until there is no more audio input.
"""
assert isinstance(source, AudioSource), "Source must be an audio source"
assert source.stream is not None, "Audio source must be entered before recording, see documentation for ``AudioSource``; are you using ``source`` outside of a ``with`` statement?"
frames = io.BytesIO()
seconds_per_buffer = (source.CHUNK + 0.0) / source.SAMPLE_RATE
elapsed_time = 0
offset_time = 0
offset_reached = False
while True: # loop for the total number of chunks needed
if offset and not offset_reached:
offset_time += seconds_per_buffer
if offset_time > offset:
offset_reached = True
buffer = source.stream.read(source.CHUNK)
if len(buffer) == 0: break
if offset_reached or not offset:
elapsed_time += seconds_per_buffer
if keyboard.read_key() == "p":
print("\nYou pressed p")
break
frames.write(buffer)
frame_data = frames.getvalue()
frames.close()
return AudioData(frame_data, source.SAMPLE_RATE, source.SAMPLE_WIDTH)
And calling it from my main script like this:
def Main():
r = sr.Recognizer()
try:
with sr.Microphone() as source:
print("Listening....")
audio = r.record(source)
print("Recognizing....")
r.adjust_for_ambient_noise(source)
text = r.recognize_google(audio)
print(text.lower())
if "lock computer" in text.lower():
ctypes.windll.user32.LockWorkStation()
elif "joke" in text.lower():
joke = pyjokes.get_joke()
speak(joke)
except Exception as e:
print(e)
Main()
This listen to the audio and stops listening when I press p but does not recognize it
I figured it out, I saved the input file and saw that there was no audio in it so google could not reognize it.
The error was in this block:
if keyboard.read_key() == "p":
print("\nYou pressed p")
break
I changed this to:
if keyboard.read_key() == "p":
print("\nYou pressed p")
pressed = True
break
And copied the listen function and dublicated it, changed its name to listen1
And now when I press P it stops listening and recognizing is also working.
So I'm doing a ToDo app, and I need to save an array of sentences and words to a .txt file. I have done some research but haven't found any tutorials that explain it well enough so I could understand it. As I said I'm using Python 3. Code below:
# Command line TO-DO list
userInput = None
userInput2 = None
userInput3 = None
todo = []
programIsRunning = True
print("Welcome to the TODO list made by Alex Chadwick. Have in mind
that closing the program will result in your TODO"
" list to DISAPPEAR. We are working on correcting that.")
print("Available commands: add (will add item to your list); remove
(will remove item from your list); viewTODO (will"
" show you your TODO list); close (will close the app")
with open('TODOList.txt', 'r+') as f:
while programIsRunning == True:
print("Type in your command: ")
userInput = input("")
if userInput == "add":
print("Enter your item")
userInput2 = input("")
todo.append(userInput2)
continue
elif userInput == "viewTODO":
print(todo)
continue
elif userInput == "remove":
print(todo)
userInput3 = input("")
userInput3 = int(userInput3)
userInput3 -= 1
del todo[userInput3]
continue
elif userInput == "close":
print("Closing TODO list")
programIsRunning = False
continue
else:
print("That is not a valid command")
This sounds like a job for pickle!
Pickle is a built-in module for saving objects and data in Python. To use it, you will need to put import pickle at the top of your program.
To save to a file:
file_Name = "testfile.save" #can be whatever you want
# open the file for writing
fileObject = open(file_Name,'wb')
# this writes the object to the file
pickle.dump(THING_TO_SAVE,fileObject)
# here we close the fileObject
fileObject.close()
To load from a file:
# we open the file for reading
fileObject = open(file_Name,'r')
# load the object from the file
LOADED_THING= pickle.load(fileObject)
fileObject.close()
The code in this answer was taken from here.
I hope that helps!
You use a simple text file to store your TODO - items and retrieve them from it:
import sys
fn = "todo.txt"
def fileExists():
"""https://stackoverflow.com/questions/82831/how-do-i-check-whether-a-fi
answer: https://stackoverflow.com/a/82852/7505395
"""
import os
return os.path.isfile(fn)
def saveFile(todos):
"""Saves your file to disk. One line per todo"""
with open(fn,"w") as f: # will overwrite existent file
for t in todos:
f.write(t)
f.write("\n")
def loadFile():
"""Loads file from disk. yields each line."""
if not fileExists():
raise StopIteration
with open(fn,"r") as f:
for t in f.readlines():
yield t.strip()
def printTodos(todos):
"""Prints todos with numbers before them (1-based)"""
for i,t in enumerate(todos):
print(i + 1, t)
def addTodo(todos):
"""Adds a todo to your list"""
todos.append(input("New todo:"))
return todos
def deleteTodos(todos):
"""Prints the todos, allows removal by todo-number (as printed)."""
printTodos(todos)
i = input("Which number to delete?")
if i.isdigit() and 0 < int(i) <= len(todos): # 1 based
r = todos.pop(int(i) - 1)
print("Deleted: ", r)
else:
print("Invalid input")
return todos
def quit():
i = input("Quitting without saving [Yes] ?").lower()
if i == "yes":
exit(0) # this exits the while True: from menu()
def menu():
"""Main loop for program. Prints menu and calls subroutines based on user input."""
# sets up all available commands and the functions they call, used
# for printing commands and deciding what to do
commands = {"quit": quit, "save" : saveFile, "load" : loadFile,
"print" : printTodos,
"add": addTodo, "delete" : deleteTodos}
# holds the loaded/added todos
todos = []
inp = ""
while True:
print("Commands:", ', '.join(commands))
inp = input().lower().strip()
if inp not in commands:
print("Invalid command.")
continue
# some commands need params or return smth, they are handled below
if inp == "load":
try:
todos = [x for x in commands[inp]() if x] # create list, no ""
except StopIteration:
# file does not exist...
todos = []
elif inp in [ "save","print"]:
if todos:
commands[inp](todos) # call function and pass todos to it
else:
print("No todos to",inp) # print noting to do message
elif inp in ["add", "delete"]:
todos = commands[inp](todos) # functions with return values get
# todos passed and result overwrites
# it
else: # quit and print are handled here
commands[inp]()
def main():
menu()
if __name__ == "__main__":
sys.exit(int(main() or 0))
def choosePath():
path = ""
while path != "e" and path !="n":
print('\nWhich way do you go (n, s, e, w):\n')
t = Timer(1 * 1, timeout)
t.start()
answer = input(path)
path = path.lower()
if path =="e":
station()
elif path =="n":
estate()
elif path =="s":
building()
else:
print("\nYou return the way you came are but are soon caught by Mansons and assimilated.\n")
return path
I have received this code and want to add a timer that if answer isn't done with a certain amount of time it says gameover.
You want the Timer class in the threading module.
import threading
t = threading.Timer(<delay in secs>, <callback>, [<function args>])
t.start()
If the user has selected an option in time call t.cancel()
You need to start the timer outside of the while loop. There are also several ways to implement a timer, but this should work (I simplified it, you'll need to adjust it to your business logic)
import time
start_time = datetime.now()
max_time_allowed = 45
while path != "e" and path !="n":
#Business logic here
current_time= datetime.now()
if current_time-start_time > max_time_allowed:
return
Try creating another thread to track the time then update a global boolean:
from threading import Thread
from time import sleep
timeIsUp = False
threadKill = False
def answerTime(self):
sleep(self)
if threadKill = True:
self._is_running = False
else:
timeIsUp = True
self._is_running = False
thread = Thread(target = answerTime, args = (10)
And now you could make your main code have an additional statement in the while loop:
while path != "e" and path !="n" and timeIsUp==False:
...
if path =="e":
station()
threadKill=True
elif path =="n":
estate()
threadKill=True
elif path =="s":
building()
threadKill=True
else:
print("Time is up")
I want to be able to have a program whereby the user can input a paragraph/sentence/word/character whatever and have that stored in a list e.g. in list[0]. Then I want them to be able to write another bit of text and have that stored in e.g. list[1]. Then at any time I want the user to be able to read that from the list by choosing which segment they want to read from e.g. reading "hello" from list[0] whilst in list[1] "hi" is stored. Then when the user exits the program I want the list to be written to an external file. Then, at next start up, the program should read the file contents and store it again in the list so that the user can add more bits of text or read the current bits. When the list is saved to a file it should append new or changed parts but overwrite parts that are the same so as not to have duplicates. I have attempted this without much success. I am to be honest not sure if it is possible. I have browsed similar forums and have found that hasn't helped much so here it is.
My code so far:
import os
import time
import csv
global write_list
global f1_contents
write_list = []
def write():
os.system("cls")
user_story = input("Enter your text: \n")
write_list.append(user_story)
def read():
os.system("cls")
user_select_needs = True
while user_select_needs == True:
user_select = input("Enter the list section to read from or type exit: \n")
if user_select == "exit":
user_select_needs = False
try:
int(user_select)
select = user_select
select = int(select)
try:
print(write_list[select])
user_select_needs = False
enter = input("Press enter:")
except:
print("There is not stored data on that section!")
except ValueError:
print("That is not a valid section!")
def exit():
os.system("cls")
max_num_needs = True
while max_num_needs == True:
set_max_num = input("Set the storage: \n")
try:
int(set_max_num)
max_num = set_max_num
max_num = int(max_num)
max_num_needs = False
except:
print("It must be an integer!")
for i in range(0, max_num):
f = open("Saves.txt", "a")
f.write(write_list[i])
f.close()
os._exit(1)
def main():
store_num_needs = True
while store_num_needs == True:
set_store_num = input("State the current storage amount: \n")
try:
int(set_store_num)
store_num = set_store_num
store_num = int(store_num)
store_num_needs = False
except:
print("It must be an integer!")
try:
f1 = open("Saves.txt", "r")
for i in range(0, store_num+1):
i, = f1.split("#")
f1.close()
except:
print("--------Loading-------")
time.sleep(1)
while True:
os.system("cls")
user_choice = ""
print("Main Menu" + "\n" + "---------")
print("1) Write")
print("2) Read")
print("3) Exit")
while user_choice not in ["1", "2", "3"]:
user_choice = input("Pick 1, 2 or 3 \n")
if user_choice == "1":
write()
elif user_choice == "2":
read()
else:
exit()
if __name__ == "__main__":
main()
It might be too complicated to understand in which case just ask me in comments- otherwise general tips would be nice aswell.
Thanks in advance
A quick point of correction:
global is only required if you're defining a global variable inside a non-global context. In other words, anything defined at the default indentation level, will be accessible by everything else defined below it. For example:
def set_global():
x = 1
def use_global():
x += 1
set_global()
try:
use_global()
except Exception as e:
# `use_global` doesn't know
# about what `set_global` did
print("ERROR: " + str(e))
# to resolve this we can set `x` to a
# default value in a global context:
x = 1
# or, if it were required, we
# could create a global variable
def make_global():
global x
make_global()
# either will work fine
set_global()
use_global()
print(x) # prints 2
Now to the actual question:
I haven't read through the block of code you wrote (probably best to trim it down to just the relevant bits in the future), but this should solve the problem as I understand it, and you described it.
import os
import sys
user_text = []
# login the user somehow
user_file = 'saves.txt'
def writelines(f, lines):
"""Write lines to file with new line characters"""
f.writelines('\n'.join(lines))
def readlines(f):
"""Get lines from file split on new line characters"""
text = f.read()
return text.split('\n') if text else []
class _Choice(object):
"""Class that is equivalent to a set of choices
Example:
>>> class YesObj(Choice):
>>> options = ('y', 'yes')
>>> Yes = YesObj()
>>> assert Yes == 'yes'
>>> assert Yes == 'y'
>>> # assertions evaluate to True
Override the `options` attribute to make use
"""
allowed = ()
def __eq__(self, other):
try:
s = str(other)
except:
raise TypeError("Cannot compare with non-string")
else:
return s.lower() in self.allowed
def _choice_repr(choices):
allowed = []
for c in choices:
if isinstance(c, _Choice):
allowed.extend(c.allowed)
else:
allowed.append(c)
if len(allowed) > 2:
s = ', '.join([repr(c) for c in allowed[:-1]])
s += ', or %s' % repr(allowed[-1])
elif len(allowed) == 1:
s = '%s or %s' % allowed
else:
s = '%s' % allowed[0]
return s
def _choice_sentinel(name, allowed):
"""Creates a sentinel for comparing options"""
return type(name, (_Choice,), {'allowed': list(allowed)})()
Quit = _choice_sentinel('Quit', ('q', 'quit'))
Yes = _choice_sentinel('Yes', ('y', 'yes'))
No = _choice_sentinel('No', ('n', 'no'))
def readline_generator(f):
"""Generate a file's lines one at a time"""
t = f.readline()
# while the line isn't empty
while bool(t):
yield t
t = f.readline()
def read_from_cache():
"""Overwrite `user_text` with file content"""
if not os.path.isfile(user_file):
open(user_file, 'w').close()
globals()['user_text'] = []
else:
with open(user_file, 'r') as f:
lines = readlines(f)
# replace vs extend user text
for i, t in enumerate(lines):
if i == len(user_text):
user_text.extend(lines[i:])
else:
user_text[i] = t
def write_to_cache():
"""Overwrite cache after the first line disagrees with current text
If modifications have been made near the end of the file, this will
be more efficient than a blindly overwriting the cache."""
with open(user_file, 'r+') as f:
i = -1
last_pos = f.tell()
# enumerate is a generator, not complete list
for i, t in enumerate(readline_generator(f)):
if user_text[i] != t:
# rewind to the line before
# this diff was encountered
f.seek(last_pos)
# set the index back one in
# order to catch the change
i -= 1
break
last_pos = f.tell()
# then cut off remainder of file
f.truncate()
# recall that i is the index of the diff
# replace the rest of it with new
# (and potentially old) content
writelines(f, user_text[i+1:])
def blind_write_to_cache():
"""Blindly overwrite the cache with current text"""
with open(user_file, 'w') as f:
writelines(f, user_text)
def overwrite_user_text(i, text, save=False):
"""Overwrite a line of text
If `save` is True, then these changes are cached
"""
try:
user_text[i] = text
except IndexError:
raise IndexError("No text exists on line %r" % (i+1))
if save:
write_to_cache()
def user_input():
"""Get a new line from the user"""
return raw_input("input text: ")
def user_choice(msg, choices):
if len(choices) == 0:
raise ValueError("No choices were given")
ans = raw_input(msg)
if ans not in choices:
print("Invalid Response: '%s'" % ans)
m = "Respond with %s: " % _choice_repr(choices)
return user_choice(m, choices)
else:
return ans
def user_appends():
"""User adds a new line"""
user_text.append(user_input())
def user_reads(*args, **kwargs):
"""Print a set of lines for the user
Selects text via `user_text[slice(*args)]`
Use 'print_init' in kwargs to choose how
many lines are printed out before user must
scroll by pressing enter, or quit with 'q'."""
print_init = kwargs.get('print_init', 4)
sliced = user_text[slice(*args)]
if not isinstance(sliced, list):
sliced = [sliced]
for i, l in enumerate(sliced):
if i < print_init:
print(l)
sys.stdout.flush()
elif user_choice(l, ['', Quit]) == Quit:
break
def user_changes(i=None, save=False):
"""User changes a preexisting line"""
attempt = True
while i is None and attempt:
# get the line the user wants to change
i_text = raw_input("Line to be changed: ")
try:
# make user input an index
i = int(i_text)
except:
# check if they want to try again
c = user_choice("Bad input - '%s' is not an "
"integer. Try again? " % i_text, (Yes, No))
attempt = (c == Yes)
if attempt:
# user gave a valid integer for indexing
try:
user_reads(i-1)
overwrite_user_text(i-1, user_input(), save)
except Exception as e:
print("ERROR: %s" % e)
if user_choice("Try again? ", (Yes, No)):
user_changes(i, save)
# stores whatever text is already on
# file to `user_text` before use
read_from_cache()
I am reading serial data and writing to a csv file using a while loop. I want the user to be able to kill the while loop once they feel they have collected enough data.
while True:
#do a bunch of serial stuff
#if the user presses the 'esc' or 'return' key:
break
I have done something like this using opencv, but it doesn't seem to be working in this application (and i really don't want to import opencv just for this function anyway)...
# Listen for ESC or ENTER key
c = cv.WaitKey(7) % 0x100
if c == 27 or c == 10:
break
So. How can I let the user break out of the loop?
Also, I don't want to use keyboard interrupt, because the script needs to continue to run after the while loop is terminated.
The easiest way is to just interrupt it with the usual Ctrl-C (SIGINT).
try:
while True:
do_something()
except KeyboardInterrupt:
pass
Since Ctrl-C causes KeyboardInterrupt to be raised, just catch it outside the loop and ignore it.
There is a solution that requires no non-standard modules and is 100% transportable:
import _thread
def input_thread(a_list):
raw_input() # use input() in Python3
a_list.append(True)
def do_stuff():
a_list = []
_thread.start_new_thread(input_thread, (a_list,))
while not a_list:
stuff()
the following code works for me. It requires openCV (import cv2).
The code is composed of an infinite loop that is continuously looking for a key pressed. In this case, when the 'q' key is pressed, the program ends. Other keys can be pressed (in this example 'b' or 'k') to perform different actions such as change a variable value or execute a function.
import cv2
while True:
k = cv2.waitKey(1) & 0xFF
# press 'q' to exit
if k == ord('q'):
break
elif k == ord('b'):
# change a variable / do something ...
elif k == ord('k'):
# change a variable / do something ...
For Python 3.7, I copied and changed the very nice answer by user297171 so it works in all scenarios in Python 3.7 that I tested.
import threading as th
keep_going = True
def key_capture_thread():
global keep_going
input()
keep_going = False
def do_stuff():
th.Thread(target=key_capture_thread, args=(), name='key_capture_thread', daemon=True).start()
while keep_going:
print('still going...')
do_stuff()
pip install keyboard
import keyboard
while True:
# do something
if keyboard.is_pressed("q"):
print("q pressed, ending loop")
break
Here is a solution that worked for me. Got some ideas from posts here and elsewhere. Loop won't end until defined key (abortKey) is pressed. The loop stops as fast as possible and does not try to run to next iteration.
from pynput import keyboard
from threading import Thread
from time import sleep
def on_press(key, abortKey='esc'):
try:
k = key.char # single-char keys
except:
k = key.name # other keys
print('pressed %s' % (k))
if k == abortKey:
print('end loop ...')
return False # stop listener
def loop_fun():
while True:
print('sleeping')
sleep(5)
if __name__ == '__main__':
abortKey = 't'
listener = keyboard.Listener(on_press=on_press, abortKey=abortKey)
listener.start() # start to listen on a separate thread
# start thread with loop
Thread(target=loop_fun, args=(), name='loop_fun', daemon=True).start()
listener.join() # wait for abortKey
pyHook might help. http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial#tocpyHook%5FTutorial4
See keyboard hooks; this is more generalized-- if you want specific keyboard interactions and not just using KeyboardInterrupt.
Also, in general (depending on your use) I think having the Ctrl-C option still available to kill your script makes sense.
See also previous question: Detect in python which keys are pressed
There is always sys.exit().
The system library in Python's core library has an exit function which is super handy when prototyping.
The code would be along the lines of:
import sys
while True:
selection = raw_input("U: Create User\nQ: Quit")
if selection is "Q" or selection is "q":
print("Quitting")
sys.exit()
if selection is "U" or selection is "u":
print("User")
#do_something()
I modified the answer from rayzinnz to end the script with a specific key, in this case the escape key
import threading as th
import time
import keyboard
keep_going = True
def key_capture_thread():
global keep_going
a = keyboard.read_key()
if a== "esc":
keep_going = False
def do_stuff():
th.Thread(target=key_capture_thread, args=(), name='key_capture_thread', daemon=True).start()
i=0
while keep_going:
print('still going...')
time.sleep(1)
i=i+1
print (i)
print ("Schleife beendet")
do_stuff()
This is the solution I found with threads and standard libraries
Loop keeps going on until one key is pressed
Returns the key pressed as a single character string
Works in Python 2.7 and 3
import thread
import sys
def getch():
import termios
import sys, tty
def _getch():
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
return _getch()
def input_thread(char):
char.append(getch())
def do_stuff():
char = []
thread.start_new_thread(input_thread, (char,))
i = 0
while not char :
i += 1
print "i = " + str(i) + " char : " + str(char[0])
do_stuff()
From following this thread down the rabbit hole, I came to this, works on Win10 and Ubuntu 20.04. I wanted more than just killing the script, and to use specific keys, and it had to work in both MS and Linux..
import _thread
import time
import sys
import os
class _Getch:
"""Gets a single character from standard input. Does not echo to the screen."""
def __init__(self):
try:
self.impl = _GetchWindows()
except ImportError:
self.impl = _GetchUnix()
def __call__(self): return self.impl()
class _GetchUnix:
def __init__(self):
import tty, sys
def __call__(self):
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
class _GetchWindows:
def __init__(self):
import msvcrt
def __call__(self):
import msvcrt
msvcrt_char = msvcrt.getch()
return msvcrt_char.decode("utf-8")
def input_thread(key_press_list):
char = 'x'
while char != 'q': #dont keep doing this after trying to quit, or 'stty sane' wont work
time.sleep(0.05)
getch = _Getch()
char = getch.impl()
pprint("getch: "+ str(char))
key_press_list.append(char)
def quitScript():
pprint("QUITTING...")
time.sleep(0.2) #wait for the thread to die
os.system('stty sane')
sys.exit()
def pprint(string_to_print): #terminal is in raw mode so we need to append \r\n
print(string_to_print, end="\r\n")
def main():
key_press_list = []
_thread.start_new_thread(input_thread, (key_press_list,))
while True:
#do your things here
pprint("tick")
time.sleep(0.5)
if key_press_list == ['q']:
key_press_list.clear()
quitScript()
elif key_press_list == ['j']:
key_press_list.clear()
pprint("knock knock..")
elif key_press_list:
key_press_list.clear()
main()
This may be helpful
install pynput with --
pip install pynput
from pynput.keyboard import Key, Listener
def on_release(key):
if key == Key.esc:
# Stop listener
return False
# Collect events until released
while True:
with Listener(
on_release=on_release) as listener:
listener.join()
break
Here is a simple Windows solution that safely ends current iteration and then quits. I used it with a counter example that breaks the loop with 'Esc' key and quits. It uses kbhit() and getch() functions from msvcrt package. Time package is only called for easement reasons (to set time delay between events).
import msvcrt, time
print("Press 'Esc' to stop the loop...")
x = 0
while True:
x += 1
time.sleep(0.5)
print(x)
if msvcrt.kbhit():
if msvcrt.getch() == b'\x1b':
print("You have pressed Esc! See you!")
time.sleep(2)
break
kbhit() function returns True if a keypress is waiting to be read
getch() function reads a keypress and returns the resulting character as a byte string. It can be used with any key
b'\x1b' is the byte string character for the 'Esc' key.
Here another example using threading.Event, without the need for catching SIGINT (Ctrl+c).
As #Atcold has mentioned in a comment below the accepted answer, pressing Ctrl+c in the loop, may interrupt a long running operation and leave it in an undefined state. This can specially annoying, when that long running operation comes from a library that you are calling.
In the example below, the user needs to press q and then press Enter. If you want to capture the key stroke immediately, you need something like _Getch() from this answer.
import time
from threading import Thread, Event
def read_input(q_entered_event):
c = input()
if c == "q":
print("User entered q")
q_entered_event.set()
def do_long_running_stuff():
q_pressed_event = Event()
input_thread = Thread(target=read_input,
daemon=True,
args=(q_pressed_event,))
input_thread.start()
while True:
print("I am working ...")
time.sleep(1)
if q_pressed_event.is_set():
break
print("Process stopped by user.")
if __name__ == "__main__":
do_long_running_stuff()
from time import sleep
from threading import Thread
import threading
stop_flag = 0
def Wait_Char():
global stop_flag
v = input("Enter Char")
if(v == "z"):
stop_flag = 1
def h():
while(True):
print("Hello Feto")
time.sleep(1)
if(stop_flag == 1):
break
thread1 = Thread(target=Wait_Char)
thread2 = Thread(target=h)
thread1.start()
thread2.start()
print("threads finished...exiting")
This isn't the best way but it can do the job you want,
Running 2 Threads one waiting for the Key you want to stop the loop with
(Wait_Char Method)
and one for loop
(H Method)
And both see a global variable stop_flag which control the stoping process
Stop when I press z
from pynput import keyboard
def on_press(key):
if key == keyboard.Key.esc:
return False
i = 0
with keyboard.Listener(on_press=on_press) as listener:
# Your infinite loop
while listener.running:
print(i)
i=i+1
print("Done")
It works ...
import keyboard
while True:
print('please say yes')
if keyboard.is_pressed('y'):
break
print('i got u :) ')
print('i was trying to write you are a idiot ')
print(' :( ')
for enter use 'ENTER'