Python Multithreading with pynput.keyboard.listener - python-3.x

I am building a self-driving rc car. The car is controlled by a raspberry pi (client) that sends image data to my computer (server) and the computer processes the image frames and responds to the car with what to do (all using python sockets). This works perfectly well. I am now trying to add a key listener to python so I can manually control the car WHILE all of the socket interractions are happening. I would like to use multithreading to do so. Here is how I believe it should work:
import cv2
from pynput import keyboard
from Server import Server
###Keyboard Listener###
def keyPress(key): #send keypress to client
server.sendCommand((str(key)))
with keyboard.Listener(on_press=keyPress) as listener: #new listener thread
listener.join() #activate thread
###Server/ client interaction###
host, port = '10.78.1.195', 8000 # home
server = Server(host, port) #server object
server.connect() #connect
while server.isOpened(): #do while the server is open
frame = server.getStreamImage() #get an image from the client each frame
server.sendCommand("0") #send a command to the server (arbituary for now, but will be a neural network ouotput
cv2.imshow("t", frame) # show image
# cv2.imshow("tttttt", nnInput) # show image CONFIGURE PERSPECTIVE TRANSFORM AFTER CAMERA MOUNT
if cv2.waitKey(1) == ord('q'): #quit if q pressed
server.sendCommand('q') # tell client to close
server.close() # break if 'q' pressed
cv2.destroyAllWindows() #close opencv windows
If you would like any of the client or server code, I would be happy to show it. I didn't inlude it because it's working perfectly.
SO, in my mind, the while loop and the keyboard listener should operate in parallel, and I'm not sure why they are not. With this configuration, the keypresses are tracked, but the server/client interactions never begin. I have tried reformatting the code and I can't seem to get the operations to happen parallel.
If there is a simpler or less resource intensive way to do this, I am open to new ideas. This is just what makes the most sense to me. I really just want the code to be as clean as possible.
My python version is 3.7. I am running Ubuntu 19.10. pynput is version 1.4.5.
If I can provide any additional info, please ask. Thank you very much!

Instead of using the with statement to initialise your listener try:
listener = keyboard.Listener(on_press=keyPress)
listener.start()
The with statement is blocking the main thread.
Using start instead of with / join you create a non-blocking thread allowing the main loop to start.

Related

Program Stops Without Reason (RasPi, Linux, Python3)

First, thank for fixing my post. I'm still not sure how to include a sketch. I've been reading posts here for many months, but never posted one before.
My headless RasPi is running two sketches of mine, one reads data from a pm2.5 sensor (PMS7003) and the other is the program listed above that sends information to another Pi, the client, that turns on a pm2.5 capable air filter. (I live in California) The program that reads the PMS7003 sorts the data, called max_index, into one of six categories, 0 thru 5 and saves the current category to a text file. I'm using the 'w' mode during the write operation, so there is only one character in the text file at any time. The server program listed above reads the text file and sends it to a client that turns on the air filter for categories above 2. The client sends the word "done" back to the server to end the transaction.
Until you mentioned it, I didn't realize my mistake, clientsocket.recv(2). I'll fix that and try again.
So, the listener socket should go outside the while loop, leaving the send and receive inside???
Troubleshooting: I start the two programs using nice nohup python3 xxx.py & nice nohup python3 yyy.py. The program that reads the PMS7003 continues running and updating the text file with current category, but the server program falls out of existence after a few days. top -c -u pi reveals only the PMS7003 program running, while the server program is missing. Also, there's nothing in nohup.out or in socketexceptions.txt and I tried looking through system logs in /var/log but was overwhelmed by information and found nothing that made any sense to me.
Since writing to the socketexceptions.txt file is not in a try/except block, the crash might be happening there.
import socket
import time
index = " "
clientsocket = ""
def getmaxindex():
try:
with open('/home/pi/pm25/fan.txt','r')as f:
stat = f.read() #gets max_index from pm25b.py
return(stat)
except:
with open("/home/pi/pm25/socketexceptions.txt",'a')as f:
f.write("Failed to read max index")
def setup(index):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
s.bind(("192.168.1.70", 5050))
except:
with open("/home/pi/pm25/socketexceptions.txt",'a')as f:
f.write("Failed to bind")
try:
s.listen(1)
clientsocket, address = s.accept()
clientsocket.send(index)
rx = clientsocket.recv(2)
if rx == "done":
clientsocket.close()
except:
with open("/home/pi/pm25/socketexceptions.txt",'a')as f:
f.write("Failed to communicate with flient")
while True:
index = getmaxindex().encode('utf-8')
setup(index)
time.sleep(5)
enter code here
It is unknown what program is supposed to do and where exactly you run into problems, since there is only a code dump and no useful error description (what does "stop" mean - hang or exit, where exactly does it stop). But the following condition can never be met:
rx = clientsocket.recv(2)
if rx == "done":
The will receive at most 2 bytes (recv(2)) which is definitely not enough to store the value "done".
Apart from that it makes not real sense to recreate the same listener socket again and again, just to accept a single client and exchange some data. Instead the listener should only be created once and multiple accept should be called on the same listener socket, where each will result in a new client connection.

PYTHON rumps program stops responding after subprocess calls another script

I am currently working on a project that contains many system tray utilities on mac osx, one of these utilities is a 'fake friend soundboard' where I am to enable the user to click on the module then select as example "Discord Ping x1" and the program playback a soundfile of the ping.
Im using RUMPS which enables me to add menus to the system tray however when trying to use libraries to play sounds directly through upon the #rumps.clicked, nothing seems to happen
Instead I've programmed it to run another script that can easily play the sound inside another folder containing all the sounds.
When running the script and clicking a sound, it plays the sound exactly as intended,
but then the program stops responding...
Is this something I'm doing wrong with my code or is there another way i could play the sound using this library?
Heres the code:
import rumps
import subprocess
class sound(rumps.App):
def __init__(self):
super(sound, self).__init__("🔊")
self.menu = ["Fake friend soundboard",
None,
"Discord Sounds:",
"Ping x1",
"Incoming Call",
"Enter Call",
"Leave Call",
"Mute",
"Deafen",
None,
"Skype Sounds:",
"Incoming Call",
None,
"Random:",
"okbuddyretard",
None]
#rumps.clicked("Ping x1")
def about(sender):
subprocess.run("python3 soundboard/discord_pingx1.py", shell=True)
if __name__ == "__main__":
sound().run()
In the soundboard folder there is the sounds folder containing the discord folder containing **
discord_ping.wav
The soundboard folder also hold the discordd_pingx1.py script which is below.
import pyglet
def sound():
sound = pyglet.resource.media('sounds/discord/discord_ping.wav', streaming=False)
sound.play()
pyglet.app.run()
sound()
after running my main.py (opens all the modules)
and clicking the sound tab and then clicking the button set to make the sound,
it plays, but then stops responding.
Any help at all would be much appreciated.
I highly recommend avoiding the insert of subprocess or sys-calls inside of a Python script that executes Objective-C code. This leads to Aborts and Traps. Instead, refer to Apple's APIs and utilize AppleScript (osascript).
Here is a better implementation that calls a script to call your script from osascript:
exec.py
import platform
import subprocess, sys, os
applescript = '''\
do shell script "bash /path/to/file/myShellScript.sh"\
'''
# parse and stdout
args = [item for x in [("-e",l.strip()) for l in applescript.split('\n') if l.strip() != ''] for item in x]
proc = subprocess.Popen(["osascript"] + args ,stdout=subprocess.PIPE )
progname = proc.stdout.read().strip()
sys.stdout.write(str(progname))
The parser takes the string with the osascript in it and parses it for a stdout write. Stdout writes are very safe, considering they are handling data from the same thread.
/path/to/file/myShellScript.sh (add the shebang on line 1 as well)
#!/bin/bash
python3 soundboard/discord_pingx1.py
This 100% handles your problem without causing subprocess errors. If you get hung with subprocess, your computer will continue running Python3. If the Application doesn't stop freezing, exit the app by cmd+Space and typing Activity Monitor. Then, find Python3 by cmd+f and typing python3. Click it and press Quit in the top left corner (the X or stop-light symbol).

Change Window upon flashdrive detection Tkinter

So far I have a GUI but right now I'm having a problem on how to change the window upon usb insertion. I tried pyudev for monitoring devices but whenever I add it in my GUI code, it doesn't run even though these codes worked when separated. Also I want to list the files in the GUI so that the user can select which file to print. Thank you in Advance! :)
This is my code for detection of usb devices.
import pyudev
context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by(subsystem='usb')
for device in iter(monitor.poll, None):
if device.action == 'add':
print('{} connected'.format(device))
if device.action == 'remove':
print('{} removed' .format(device))

Can not pass OSC data using IMU manufacturer's python2.7 example script

I am working with a high refresh rate IMU (x-IO technologies NGIMU) which outputs all data in osc format. The manufacturer provides the following python script to serve the data on linux platforms ( I am running Ubuntu 16.04)
'''
NGIMU Demo python v2.7 script written by Tom Mitchell (teamxe.co.uk) 2016
Requires pyOSC https://trac.v2.nl/wiki/pyOSC
'''
import socket, OSC, threading, time
# Change this to the NGIMU IP address
send_address = '192.168.1.1', 9000
# Set the NGIMU to send to this machine's IP address
c = OSC.OSCClient()
c.connect(send_address)
msg = OSC.OSCMessage()
msg.setAddress('/wifi/send/ip')
msg.append(str(socket.gethostbyname(socket.gethostname())))
c.send(msg)
c.close()
# Set up receiver
receive_address = '192.168.1.2', 8000
s = OSC.OSCServer(receive_address)
s.addDefaultHandlers()
def sensorsHandler(add, tags, args, source):
print add + str(args)
def quaternionHandler(add, tags, args, source):
print add + str(args)
def batteryHandler(add, tags, args, source):
print add + str(args)
# Add OSC handlers
s.addMsgHandler("/sensors", sensorsHandler)
s.addMsgHandler("/quaternion", quaternionHandler)
s.addMsgHandler("/battery", batteryHandler)
# Start OSCServer
print "\nUse ctrl-C to quit."
st = threading.Thread(target = s.serve_forever)
st.start()
# Loop while threads are running
try :
while 1 :
time.sleep(10)
except KeyboardInterrupt :
print "\nClosing OSCServer."
s.close()
print "Waiting for Server-thread to finish"
st.join()
print "Done"
The IMU hosts its own network which I connect to with the computer that is to receieve the data.
I have installed pyOSC from the location referenced in the script.
When I run the script, no data is delivered, only the message "Use ctrl-C to quit".
All connections seem to take place properly. When the script is running, I can see the udp connection at the correct ip and port using the Ubuntu firewall configuration gui. I have tried disabling the firewall but that had no effect.
Separately, I have used another computer to send udp packets to that ip and port and confirmed their receipt.
To say that I am a coding novice is far too generous. Nonetheless, I need to get this script running. Any help you can offer is greatly appreciated.
The problem is that
socket.gethostbyname(socket.gethostname())
is not setting the correct IP. You should change to
msg.setAddress('/wifi/send/ip')
msg.append('192.168.1.2')

Why does X choke after I draw to the root window

For background, I'm running
Debian Lenny, and have tried this with both GNOME and Fluxbox.
Anyway, I've been looking at how to draw on the desktop, and I found and tried this code here:
http://blog.prashanthellina.com/2007/08/24/drawing-on-your-desktop/
It worked fine, except upon terminating it (by hitting control C), X loses it's ability to create new windows.
I had thought that maybe the problem was pygame not releasing some resource, so I added in a block of code to trap the kill signal, giving me the following:
"""
Run the following command in the shell before executing this script
export SDL_WINDOWID=`xwininfo -root|grep "id:"|sed 's/^.*id: //'|sed 's/ (.*$//'`
"""
import pygame
import sys
import random
import time
import signal
pygame.init()
window = pygame.display.set_mode((1280, 1024))
screen = pygame.display.get_surface()
def handle_sigint(signum, frame):
"""I want to ensure resources are released before bailing."""
print("SIGINT received.");
pygame.display.quit()
pygame.quit()
sys.exit(0)
# Set handler to catch C^C Interupts
signal.signal(signal.SIGINT, handle_sigint)
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit(0)
x = random.choice(range(640))
y = random.choice(range(480))
radius = random.choice(range(100))
col_r = random.choice(range(255))
col_g = random.choice(range(255))
col_b = random.choice(range(255))
time.sleep(.03)
rect = pygame.draw.circle(screen, (col_r, col_g, col_b), (x,y), radius)
pygame.display.update(rect)
And so I tried again. The print statement in the interrupt handler tells me that the handler does run when I quit, but I still have the same problem. And even more interestingly, X has no problems while it's running. It's only after terminating it.
Might anybody out there have any idea what's happening, and what I can do to fix the code so it doesn't wreck my X session? Thanks in advance.
Questions, ideas & things to try:
If you have a terminal up before you try a -c, can you still type in it after? If yes, there's likely a problem in your session or window manager, not the x server.
Have you tried Ctrl-Alt-Backspace? Does this fix your problem?
Are gtk-window-decorator and x-session-manager still running?
pygame is typically run out of a window and is normally going to try to cleanup after itself. You've added an explicit call to pygame.display.quit(), but I don't think this changes anything - pygame tries to delete the window referred to by the SDL_WINDOWID variable. Actually succeeding in deleting your root window is probably a bad thing. I'm going to guess that the guy you got this from, running ubuntu, pygame fails to delete the window because he doesn't have permission. You might on your OS.
Since killing your root window is bad, how about just restoring control of the root window back to nautilus (gnome)? something like gconftool-2 --type bool --set /apps/nautilus/preferences/show_desktop true?

Resources