How to add hotkey in Python? - python

I'm making a bot for a game and I want to call the function when I press hotkey. I already tried some solutions but it is not good. Here is my code:
def start():
while True:
if keyboard.is_pressed('alt+s'):
break
...
def main():
while True:
if keyboard.is_pressed('alt+p'):
start()
This way is stable but it causes a lag, it is hard for me to type something.
def main():
keyboard.add_hotkey('alt+p', start, args=(), suppress=True, trigger_on_release=True)
while True:
# waiting for the user to press hotkey
continue
As I know keyboard.add_hotkey only returns output so I can't stop the loop in the start function.
Is there any better solution?

although this may be late for you, it may help others if they find this...
exit = false
def start():
exit = true
def main():
keyboard.add_hotkey('alt+p', start, suppress=True, trigger_on_release=True)
while not exit:
# put something here, you can't have an empty loop
keyboard.remove_hotkey('alt+p')
When main is called this will add a hotkey that, when triggered, will run the function start. It will then go into the while loop. (note that, unless your are using subprocessing,+ this will stop everything else from going on until you leave the loop) The while loop will run until exit = false. It would probably be a good idea to remove the hotkey, although you may have a reason not too.

Related

How to kill a thread that uses an input?

I am trying to make an online game using socket and threading. It is a competitive hangman game, where the fastest one to complete the word wins. I want that, when one of the players wins/runs out of lives, the other player gets kicked out of its game and is told that they have won/lost. However, I have run into a problem.
1: If I use "threading", there is no way to close the thread (since the program needs to read user input, I need to use the input() function, which means that the player's thread can't be terminated until they insert a character).
2: If I use "multiprocessing" (which has a terminate() function), I can't use input() (multiprocessing doesn't allow it).
I am in a stalemate, what should I do?
P.D: This is my first stackoverflow post ever, please tell me if I made a mistake at writing the post!
Ok, I got it, it is an ugly and complicated solution, but it works.
I created a program that emulates the "input" command, without being the input command itself (so it doesn't stop the processes).
Here it is:
import pynput.keyboard
global string
string=""
c=0
global oldprinted
oldprinted=""
def GetInput():
import pynput
def on_press(key):
global oldprinted
global string
if key==pynput.keyboard.Key.enter:
listener.stop()
else:
prompt="Introduce your input: "
try:
if key==pynput.keyboard.Key.backspace and len(string)>0:
string=string[:-1]
else:
string+=str(key.char)
prompt+=(string)
print(" "*len(oldprinted),end="\r")
print(prompt,end="\r")
oldprinted=prompt
except:
pass
print("Introduce your input: ",end="\r")
with pynput.keyboard.Listener(suppress=True,on_press=on_press) as listener:
listener.join()
return string
data=GetInput()
print("\n")

How to use Python keyboard module to persistently detect input?

I'm trying to write a script so that when I press "0" I will get an output, but I want to work without restarting the script, and I haven't been successful using the keyboard module which I've seen as being the most commonly used for detecting key inputs in Python.
What I have so far is
import keyboard
def goto(linenum):
global line
line = linenum
line = 1
if line == 1:
while True:
if keyboard.is_pressed('0'):
print('pressed 0')
break
goto(1)
What I tried doing was after the loop breaks to refer back to the beginning of the loop and try again from there, however the script ends after I press 0. If I remove the break then it constantly outputs "pressed 0", which is not what I want to happen.
You can use pynput to detect key input.
I would do something like this:
from pynput.keyboard import Key, Listener
def on_press(key):
if str(key).replace("'","") == '0':
print('pressed 0')
listener = Listener(on_press=on_press)
listener.start()
Once you start the listener, it will be always waiting for the key press and you can still add code after. In fact, with this example I recommend to add something after, maybe an input() so the script won't close immediately.
I think there are a few problems going on here.
goto is a pretty bad pattern to use and I don't think there's anything here to make your implementation work. It kinda sounds like you're going for some kind of recursion like approach here where when 0 is pressed, then it does some 'reset' and starts looping again. If that is the case you could do something like this:
def wait_for_zero():
# wait for press here
# Should add some sort of exit check too
# 'reset' logic, if any here
wait_for_zero()
looping with a weak condition is pretty fast cycling and generally a bad idea without some time delay. This is mentioned in the documentation for keybaord. There also is an example in the documentation for keyboard to wait for a keypress: https://github.com/boppreh/keyboard#waiting-for-a-key-press-one-time

Is there any way for console programs to change their execution way, due to some event, like e.g. key press or some event in code?

All programs which I see in tutorials are console and code is executed from first line to the last line and if there is while everything starts from the first line. Is there any way for console programs to change their execution way, due to some event, like e.g. key press or some event in code? The best example of what I want to do is router CLI. Where can I find such examples?
def main():
while(True):
initial_setup() #choose IPs to monitor
while(True):
do_some_work() # do monitor the IPs
I need some listener in the secons while which detects keypresses and then I go to initial setup, meanwhile do_some_work works and only after I finish adittional changes in initial_setup do_some_work restarts.
Sorry I am noob and not very good in explaining probaly because English is not native for me. The best example from real life I can name is CLI of router, you can setup intreface and meanwhile router do routing in the background.
Code for Sergio S:
import threading
import time
def hello():
while(True):
print("Hello")
time.sleep(2)
def hi():
while(True):
print("hi")
time.sleep(2)
def press_key():
a=input()
a=False
return a
def circle():
MrBoolean=True
while(MrBoolean):
thr=[]
thr.append(threading.Thread(target=hello))
thr.append(threading.Thread(target=hi))
thr.append(threading.Thread(target=press_key))
for i in thr:
i.start()
for i in thr:
i.join()
mrBoolean=thr[3]
def main():
while(True):
circle()
main()
From your description, it seems you're searching for something called multithreading: while one part of the application does one thing, the other does something else. See these other questions for more details: How to use threading in Python? , How to stop a looping thread in Python?
whats_typed = input('Say Aah:')
if whats_typed.strip() == 'Aah':
print('Thanks!')
else:
print('Whoops. Your input was:', whats_typed)
The above changes what is executed depending on user input when the program is run.

Python: detect specific key press and prompt the user

I need to print out sentence "Hello World" every 10 seconds. However, if the user either 'c' or 'Enter', the program should stop printing the sentence and prompt the user to provide with the another sentence. The user-provided sentence is checked and if the sentence contains any digits, a message shows up: "cannot contain digits". Otherwise a message shows up: "correct sentence". After displaying either of the messages, the program continues printing "Hello World".
Here is the code I have strated with. Any hints on how to continue further would be greatly appreciated.
Thanks!
import threading
def looping():
threading.Timer(10.0, looping).start()
print("Hello World!")
looping()
From my understanding of your assignment's instructions, it looks like you're on the right track with using a timer to print "Hello World"! I'd like to upvote Irmen de Jong's comment on your question with regard to the statement "threads and console input/output don't work nicely together", since I've experienced this myself in C programming.
Once you have the timer going, the text it prints to the screen shouldn't have an effect on responding to keyboard input. If it's really required to respond directly to a keypress of 'c' (not followed by 'Enter', as one would normally have to do when reading input from the keyboard with input()), I recommend following one of the solutions in Python method for reading keypress? to figure out how you would like to implement that.
EDIT: Implementing a solution using a thread-based timer is a bit more tricky than I thought.
As you may have found in your research on this problem, the threading.Timer object has both start() and stop() methods that you can use to control the execution of individual thread timers if you've saved a reference to the timer in a variable (e.g. doing my_timer = threading.Timer(10.0, looping) then calling my_timer.start() to start the timer). If you do this, you may be able to call my_timer.stop() to pause the looping, provided you've kept a proper reference to the current timer instance that you need to stop at that point in time.
To make things a bit easier, I chose to create a global variable PAUSE_LOOPING that, when set to False, will stop a new timer instance from being started when looping is called, thereby halting all further repetitions of the function until PAUSE_LOOPING is set back to True and looping() is called again:
import threading
from msvcrt import getch
PAUSE_LOOPING = False
def looping():
global PAUSE_LOOPING
if not PAUSE_LOOPING:
threading.Timer(10.0, looping).start()
print("Hello World!")
looping()
while True:
# key = ord(getch())
# if key == 13: # Enter
# PAUSE_LOOPING = True
input_string = input()
if input_string == "":
PAUSE_LOOPING = True
else:
PAUSE_LOOPING = False
looping()
Commented out in the last code block is one way to grab a key press directly (without needing to press the 'Enter' key as is required by input()) taken from the stackoverflow question I linked to earlier in my answer. This should work as long as you're using Python for Windows (so you have the MS VC++ runtime library msvcrt installed), but to make the script stop when pressing 'Enter' you can use the standard input() function. In my example, typing any other string of characters before pressing 'Enter' will resume looping after it's been paused.
NOTE: Beware of using Python's IDLE to run this code. It won't work. Instead, you must run it from the command line.

How to have input while a while loop ( or similar) is executed

I do believe that thread may accomplish this, although I am not sure. Most of the threads out there that address this problem doesn't address it to match my problem. I have created a simple mud-like fighting system that executes when you 'fight' an NPC. I have the code that runs under a while loop that checks health between you and NPC and if one of you dies then the loop ends.
However
During the loop I want to have it where a user can type in commands, instead of being stuck watching a looping code block without you being able to do anything. From what I have read online it looks like thread module may be of some help to me? Also if anyone has PyGame experience, maybe looking into that would be a solution? Please let me know what you think.
Below is a very simple example of what I am trying to accomplish.
import time
fighting = True
while fighting:
# do the magic here
time.sleep(4) # to give it a nice even pace between loop intervals
Although at any time i want to be able do input a command like a skill or spell.
Any ideas or suggestions?
You can separate your human interface and fight game into separate threads. The fight game uses a queue for input, which uses a timeout to continue. Here is a very simple queue structure that should minimally do what you want.
import time
import threading
import Queue
def fighter(input_queue):
while True:
start = time.time()
# do stuff
wait = time.time() - start()
if wait <= 0.0:
wait = 0
try:
msg = input_queue.get(wait, wait)
if msg == 'done':
return
# do something else with message
except Queue.Empty:
pass
def main():
input_queue = Queue.Queue()
fight_thread = threading.Thread(target=fighter, args=(input_queue,))
fight_thread.start()
while True:
msg = raw_input('hello ') # py 2.x
input_queue.put(msg)
if msg == 'done':
break
fight_thread.join()
If you only want this to work on Windows, and you want to keep your simple event loop:
fighting = True
inputbuf = ''
while fighting:
# do the magic here
while msvcrt.khbit():
newkey = msvcrt.getwche()
inputbuf += newkey
if newkey == '\r':
process_command(inputbuf)
inputbuf = ''
time.sleep(4) # to give it a nice even pace between loop intervals
On the other hand, if you want to use a background thread, it would be a lot simpler:
def background():
for line in sys.stdin:
process_command(line)
bt = threading.Thread(target=background)
bt.start
fighting = True
while fighting:
# do the magic here
time.sleep(4) # to give it a nice even pace between loop intervals
This works cross-platform, and it gives normal line-buffered input (including full readline support), which people will probably like.
However, I'm assuming you want that process_command to share information with the # do the magic here code, and possibly even to set fighting = False. If you do that without any thread synchronization, it will no longer work cross-platform. (It may work on both Windows CPython and Unix CPython, but will probably not work on IronPython or Jython—or, worse, it will work most of the time but randomly fail just often enough that you have to fix it but not often enough that you can debug it…)
What you may be looking for is a non-blocking raw_input implementation. This would allow the loop to keep going while allowing the user a possibility at entering commands.
There is an example of an implementation of this here and here. Maybe you can adapt one of them to suit your purpose.
Edit:
Or, if you're working on Windows...

Categories