Single-module non-blocking Keypress Detection in Python - python

TL;DR, I'm trying to make a python-only single-module platform-nonspecific non-blocking keypress detector that works in IDLE.
Good luck.
So say I'm running a loop, in IDLE, for an arbitrary amount of time. I want a way to get user input without blocking the loop, on any system I want.
Example:
import keyboard #(is one module in same directory)(Mac, Windows, Linux, who knows?)
teststring = ''
while True:
if keyboard.isDown('a'):
teststring += 'a'
if keyboard.isDown('enter'):
if 'a' in testring:
break
... (neural network or whatever continues)
To put it a different way, I'm trying to make a universal event listener that works in IDLE. If one already exists, do let me know, but I have done some research and none work in one easy-to-use module.
Note:
You don't have to write this, I just want to know what pre-installed Python modules you would use to do this and a general idea of how to go about it.

Related

How can I send keystrokes and mouse movement to a specific PID?

How can I send keystrokes and mouse movements to a specific running program through its PID. I've used both pywinauto and pynput, and they work great, but I want to send keys to a program that is not in focus. I found this question: How to I send keystroke to Linux process in Python by PID? but it never explains what filePath is a path to.
If you could help solve for this example, that would be great! I want to send the "d" key to an open Minecraft tab for 10 seconds, and then send the "a" key for the next 10 seconds and stop. I would need this to be able to run in the background, so it could not send the keys to the computer as a whole, but only to the Minecraft tab. I am on Windows 10 by the way.
Any help would be appreciated!
Pretty sure you won't be able to, at least not easily let me explain a little bit how all of this works.
Lets start with the hardware and os, the OS has certain functions to read the input you give the computer. This input goes into a "pipe", the OS is reading input, and putting into the pipe, on the other side of the pipe there may be an application running, or it may not. The OS typically manages this (which app to put on the pipe listening) by defining which app/window is active. Apps access this pipe with the API given by the OS, they read the input and decide on it.
The libraries you cited above, change the values of the keyboard and mouse, in other words, they make the OS read other values, not the real ones, then the OS puts them in the "pipe", and are read by the app that is listening on the pipe (the one active). Some apps have their own API's for this, but I would guess Minecraft doesn't. If they don't have an API, what can you do? well, as I said, nothing easy, first of all "hacking" the app, in other words change it to listen to some other input/output rather than the one given by the OS, (this would be you making your own API). The other one would be you changing the OS, which would also be extremely hard, but maybe a tiny bitty easier. It also depends on your OS, I think Microsoft does offer input injection api's
So, simple options, first, run a VM with a GUI and use pywinauto, pyautogui, etc. The other option would be if you can run it in the browser, do so, and use something like Selenium to automate the input.
Quick note, why does selenium works and the browser can read input in the background? Easy, it's not, it just executes the code it would execute if it would have read the input! javascript, cool isn't
With ahk you can do this with Python+AutoHotkey
pip install ahk
pip install "ahk[binary]"
from ahk import AHK
from ahk.window import Window
ahk = AHK()
win = Window.from_pid(ahk, pid='20366')
win.send('abc') # send keys directly to the window
Note that some programs may simply ignore inputs when they are not in focus. However, you can test this works in general even when not in focus by testing with a program like notepad
Full disclosure: I author the ahk library.

What is the best way to pause/stop/command a Python script anytime anywhere?

I have a Python script running selenium framework in a command line and running continuously to control Chrome do background data processing and monitoring. My simplified code structure is as following
while True:
Do_task1() # a blocking function
Do_task2() # another blocking fucntion
... # calling many blocking functions below, including wait(), time.sleep()
I need a way to interrupt the script anytime and anywhere to pause, terminate safely and give commands. What are the best ways to do this?
I've thought and tried of several ways but I am not exactly sure how to approach it:
I tried this:
if msvcrt.kbhit():
key = msvcrt.getch().decode("utf-8").lower()
if key == "j":
self.setting1 = True
elif key == "k":
self.setting2 = True
in the while loop, but it has to pass through bunch of blocking calls before reacting to my keypresses. And the command isn't exactly accepting my keyboard input in real time, that is I'll enter a character input, and I think it will be in the background buffer, then once the code execution reaches the code above, it starts to do stuff.
For terminating the script, I just do Ctrl-C in the CMD window, which I don't think it's the best way, and I should properly end the program ending background processes like Chromedriver.
I thought of having a GUI which runs somehow asynchronously and I can have buttons for pausing, and terminating the script. But I am not exactly sure how to approach it, or if this is a good idea to even try. Any suggestion is welcomed.
Use a script monitoring/workflow monitoring framework like AirBnB's Airflow or Luigi. I had only done brief research on this.
A related question but I don't need to return exactly where it's left off
I usually use try and except to do this
while True:
try:
Do_Task1()
Do_Task2()
except KeyBoardInterrrupt:
break

Receiving stdin input while other threads / processes are outputting

I'm asking the same question as this post, but in Python under Linux. I have a thread running in the background which output things constantly, but I would like to keep the input line at the bottom of the terminal without been flushed away. Can someone give me a code example? Suppose my code is like this:
from time import sleep
from multiprocessing import Process
def subproc():
while True:
print "something"
sleep(1)
def main():
p = Process(target=subproc)
p.start()
c = raw_input()
EDIT: by the way, I would like to keep the use of the raw_input function, if possible, because I want to have input history available.
unfortunately, there isn't any way of doing this that doesn't involve some serious or irritating consequences.
The way terminals scroll and handle stdin/stdout has not varied much in decades.
Take a look at terminal escape sequences. As long as the user terminal is based on the VT100 or ansi spec, these should work. You can use them to set the write location, clear lines, etc. But if a user scrolls, the illusion will break.
The problem of specifying a specific location on screen to output a character was addressed for early games like angband, rogue and nethack. The solution was the Curses library, and there happens to be a curses python module
By using curses, you can achieve what your after. But I know of no way of doing this in standard standard shells

Python: How to get input from console while an infinite loop is running?

I'm trying to write a simple Python IRC client. So far I can read data, and I can send data back to the client if it automated. I'm getting the data in a while True, which means that I cannot enter text while at the same time reading data. How can I enter text in the console, that only gets sent when I press enter, while at the same time running an infinite loop?
Basic code structure:
while True:
read data
#here is where I want to write data only if it contains '/r' in it
Another way to do it involves threads.
import threading
# define a thread which takes input
class InputThread(threading.Thread):
def __init__(self):
super(InputThread, self).__init__()
self.daemon = True
self.last_user_input = None
def run(self):
while True:
self.last_user_input = input('input something: ')
# do something based on the user input here
# alternatively, let main do something with
# self.last_user_input
# main
it = InputThread()
it.start()
while True:
# do something
# do something with it.last_user_input if you feel like it
What you need is an event loop of some kind.
In Python you have a few options to do that, pick one you like:
Twisted https://twistedmatrix.com/trac/
Asyncio https://docs.python.org/3/library/asyncio.html
gevent http://www.gevent.org/
and so on, there are hundreds of frameworks for this, you could also use any of the GUI frameworks like tkinter or PyQt to get a main event loop.
As comments have said above, you can use threads and a few queues to handle this, or an event based loop, or coroutines or a bunch of other architectures. Depending on your target platforms one or the other might be best. For example on windows the console API is totally different to unix ptys. Especially if you later need stuff like colour output and so on, you might want to ask more specific questions.
You can use a async library (see answer of schlenk) or use https://docs.python.org/2/library/select.html
This module provides access to the select() and poll() functions
available in most operating systems, epoll() available on Linux 2.5+
and kqueue() available on most BSD. Note that on Windows, it only
works for sockets; on other operating systems, it also works for other
file types (in particular, on Unix, it works on pipes). It cannot be
used on regular files to determine whether a file has grown since it
was last read.

Listening to keyboard events without trapping them?

I'm writing an command-line application which listens for Control key release events in X Windows and alerts another process when it detects them.
Being new to GNU/Linux, I'd prefer avoiding to fumble with GCC and therefore I'm looking for a scripting-based solution. Since I know a bit of Python, it seemed natural to go for a Python-based solution, and after scavenging the Internet for examples and reading Python Xlib docs, I've put together this programs which works, but with a caveat: it traps events instead of just listening for them (I mean such events are not passed anymore to the application which they were directed to in the first place).
I've tracked down Control key codes by running "xev". Since I've remapped my modifier keys, on your system they may be different.
To keep things simple, I've left out the code which deals with the external process.
Thank you for your help.
Software:
Python 2.7.2
Python Xlib 0.15 RC1
Perl v5.10.1
Debian GNU/Linux version: 6.0.3
Kernel version: Linux debian 2.6.32-5-686
EDIT: What I can't figure out is that keyboard events do not get trapped unless they are processed (in my programs, this means the line 'print "KeyRelease"' gets executed). Since in my code I don't call any method either on Xlib or on the event object, I don't understand where the difference in processing lies.
EDIT2: Suggestions about alternative solutions besides using Xlib are also welcome.
EDIT3: I know Perl too, and suggestions about Perl libraries which could help are welcome too, as long as they don't require recent versions of system libraries, since Debian notoriously lags behind when it comes to packages available in its repositories, and compiling and installing last versions of libraries can be difficult if they have many dependencies (I've tried installing PyGTK, but gave up after failing to reference an up-to-date GLib I had installed).
#!/usr/bin/env python
from Xlib.display import Display
from Xlib import X
Control_R = 64 # Keycode for right Control.
Control_L = 108 # Keycode for left Control.
keycodes = [Control_R, Control_L] # Keycodes we are listening for.
# Handle X events.
def handle_event(event):
# Let us know whether this event is about a Key Release of
# one of the key we are interest in.
if event.type == X.KeyRelease:
keycode = event.detail
if keycode in keycodes:
print "KeyRelease"
# Objects needed to call Xlib.
display = Display()
root = display.screen().root
# Tell the X server we want to catch KeyRelease events.
root.change_attributes(event_mask = X.KeyReleaseMask)
# Grab those keys.
for keycode in keycodes:
root.grab_key(keycode, X.AnyModifier, 1, X.GrabModeAsync, X.GrabModeAsync)
# Event loop.
while 1:
event = root.display.next_event()
handle_event(event)
Thanks to the pykeylogger library mentioned by Croad Langshan, and to the helpful example code provided by Tim Alexander, the author of such library, I've been able to change my program to:
#!/usr/bin/env python
from pyxhook import HookManager
watched_keys = ["Control_R", "Control_L"]
def handle_event (event):
if event.Key in watched_keys:
print "KeyRelease"
hm = HookManager()
hm.HookKeyboard()
hm.KeyUp = handle_event
hm.start()
This program accomplishes my goal without any issue. You can read the fields of the "event" object for further information about the event (see source code of "pyxhook.py").
You need to use the XRecord extension. It can be used with pyxlib (pykeylogger mentioned in the other answer uses this), or by wrapping libX11 and libXtst via ctypes (as I did in synaptiks).
Note however, that programming with xrecord (and to some degree also with XLib in general) is somehwat hard, because the API is badly documented, and quite baroque and counter-intuitive.
Presumably this project must have code that solves that problem:
http://sourceforge.net/apps/mediawiki/pykeylogger/index.php?title=Main_Page
Note that the XRecord extension continues to be broken in a few distributions. The reason I hadn't gone back and updated that library for a while was because it was broken in Ubuntu for multiple releases. There's a way to do it as well with an XInput overlay, (I'm told) but I never pursued that because I didn't want to deal with an overlay rather than hooking the X events directly.
Comment back if there's any issues with using the code in the pyxhook lib, I tried to make it as simple/robust as possible, but I may have missed stuff when putting it together. It was a while ago.

Categories