Detect key pressed in python not working - python

import pyautogui, time, threading, keyboard, msvcrt
global active, exitp
active = False
exitp = False
Names = []
def mainLoop():
global active, exitp
pressedkey = msvcrt.getch()
while True:
if pressedkey == 'z':
active = not active
elif pressedkey == 'x':
exitp = False
break
def running():
global exitp
while not exitp:
print("Running")
time.sleep(3)
start = time.time()
print("Your screen size is: " + str(pyautogui.size()))
width, height = pyautogui.size()
t1 = threading.Thread(target=mainLoop, args=())
t2 = threading.Thread(target=running, args=())
t1.start()
t2.start()
while not exitp:
if active:
pyautogui.click(90, height - 110)
for i in range(len(Names)):
if active:
pyautogui.typewrite(Names)
pyautogui.press("enter")
else:
break
active = False
end = time.time()
print("Execution time: " + str(end-start) + " seconds")
trying to make a loop that exits when i press 'x', and types/stops typing names from my array called "Names" when pressing 'z', however, i'm pressing both 'x' and 'z' but they don't anything, help?

from pynput import keyboard
import pyautogui
import threading
import sys
class Status:
_exit = False
active = True
names = [str(i) for i in range(0, 10, 1)]
width, height = pyautogui.size()
def mainThread():
listenerThread = keyboard.Listener(on_press = onKeyPress)
listenerThread.start()
while(not Status._exit):
if (not Status.active):
continue
pyautogui.click(90, height - 110)
for i in range(len(names)):
if (not Status.active or Status._exit):
break
pyautogui.typewrite(names[i])
pyautogui.press("enter")
keyboard.Listener.stop(listenerThread)
print("Stopped listerning")
sys.exit(0)
def onKeyPress(key):
print("Still listerning")
try:
k = key.char
except Exception:
k = key.name
if (k == "z"):
Status.active = not Status.active
elif (k == "x"):
Status._exit = not Status._exit
#controlThread = threading.Thread(target = mainThread)
#controlThread.start()
mainThread()
Instead of using globals I used a 'static' class which stored the status. I also used a different module to listen to key inputs.
I think this is what you wanted.
EDIT: You don't have to have the main loop as a thread. If you wanted to do other things then set it as a thread (uncomment the two lines and comment the last line) or just leave it how it is.

Related

python hanging on closing/terminating multi-process task. Only stops via CTRL+Break

I'm using multiprocess for screen grab to record keyboard actions and mouse movement associated with the image frame.
After saving, imshow window is to close and the multi process task should terminate, however after saving and closing the imshow window my CMD prompt hangs until I CTRL+Break.
If don't CTRL+Break and just close CMD prompt it appears the multi process task are still live in my task manager.
I know I'm missing .join() and .close() I'm just unsure how to add them in. Every time I try I get VAR not defined. If anyone could take a look at my code and suggest changes that would be greatly appreciated.
import os
import cv2
import mss
import time
import pydirectinput
import multiprocessing
import numpy as np
from utils.getkeys import key_check
from distutils.version import StrictVersion
from multiprocessing import Pipe
win_title = "ML Collection"
fps = 0
fps_dptime = 2
start_time = time.time()
sct = mss.mss()
image_data = "./data/image_data.npy"
keyPress_data = "./data/keyP_data.npy"
position_data = "./data/pos_data.npy"
def create_data():
if os.path.isfile("./data/image_data.npy"):
print("Loading previous data")
img_data = list(np.load(image_data, allow_pickle=True))
keyP_data = list(np.load(keyPress_data, allow_pickle=True))
pos_data = list(np.load(position_data, allow_pickle=True))
else:
print("No previous files found. Creating new data files")
img_data = []
keyP_data = []
pos_data = []
return img_data, keyP_data, pos_data
img_data, keyP_data, pos_data = create_data()
def save_data(img_data, keyP_data, pos_data):
np.save(image_data, img_data)
np.save(keyPress_data, keyP_data)
np.save(position_data, pos_data)
def get_screen(s):
while True:
screen = np.array(
sct.grab({"top": 40, "left": 0, "width": 1280, "height": 800})
)
s.put_nowait(screen)
s.join()
def mss_screen(s):
global fps, start_time
while True:
if not s.empty():
screen = s.get_nowait()
s.task_done()
screen = cv2.cvtColor(screen, cv2.COLOR_BGR2GRAY)
## screen = cv2.Canny(screen, threshold1=119, threshold2=250)
screen = cv2.resize(screen, (244, 244))
cv2.imshow(win_title, screen)
img_data.append(screen)
keys = key_check()
keyP_data.append(keys)
mpos = pydirectinput.position()
pos_data.append(mpos)
fps+=1
fpstime = time.time() - start_time
if (fpstime) >= fps_dptime :
print("FPS: ", fps / (fpstime))
fps = 0
start_time= time.time()
print("Press Numpad+ to close")
if cv2.waitKey(1) & 0xFF == ord("+"):
cv2.destroyAllWindows()
save_data(img_data, keyP_data, pos_data)
s1.close()
s2.close()
break
if __name__=="__main__":
s = multiprocessing.JoinableQueue()
s1 = multiprocessing.Process(target=get_screen, args=(s,))
s2 = multiprocessing.Process(target=mss_screen, args=(s,))
s1.start()
s2.start()
solved by changing:
fps+=1
fpstime = time.time() - start_time
if (fpstime) >= fps_dptime :
print("FPS: ", fps / (fpstime))
fps = 0
start_time= time.time()
print("Press P to close")
if cv2.waitKey(1) & 0xFF == ord("+"):
cv2.destroyAllWindows()
save_data(img_data, keyP_data, pos_data)
s1.close()
s2.close()
break
if __name__=="__main__":
s = multiprocessing.JoinableQueue()
s1 = multiprocessing.Process(target=get_screen, args=(s,))
s2 = multiprocessing.Process(target=mss_screen, args=(s,))
s1.start()
s2.start()
Changed to
killKey = key_check()
fps+=1
fpstime = time.time() - start_time
if (fpstime) >= fps_dptime :
print("FPS: ", fps / (fpstime))
fps = 0
start_time= time.time()
print("Press Numpad+ to close")
if killKey == 'P':
cv2.destroyAllWindows()
save_data(img_data, keyP_data, pos_data)
break
if __name__=="__main__":
s = multiprocessing.JoinableQueue()
s1 = multiprocessing.Process(target=get_screen, args=(s,))
s2 = multiprocessing.Process(target=mss_screen, args=(s,))
s1.start()
s2.start()
while True:
killKey = key_check()
if killKey == 'P':
time.sleep(1)
s1.terminate()
s2.terminate()
break

After() method in tkinter for timer

I want to start a timer when the user clicks a button for the first time in my number click game. I tried to use the after method for this, but when I click a button, the timer stays at 0. The rest of the code works fine without any error messages.
Here's the code:
import tkinter as tk
from random import randint
# create window
window = tk.Tk()
window.title('Clicker')
# create list for random numbers
new_list = []
# define time count:
def time_event():
global current_time, after_id
if clock_started:
current_time += 1
clock["text"] = str(current_time)
after_id = clock.after(1000, time_event)
# define click event
def click_event(event):
global clock_started
new_button = event.widget
clicked_val = int(new_button["text"])
clock_started = True
if not clock_started:
clock_started = True
clock.after(1000, time_event)
if clicked_val == new_list[0]:
del new_list[0]
new_button["state"] = tk.DISABLED
if len(new_list) == 0:
clock.started = False
clock.after_cancel(after_id)
# create buttons
for i in range(25):
new_num = randint(1, 999)
while i in new_list:
new_num = randint(1, 999)
new_list.append(new_num)
new_list.sort()
new_button = tk.Button(window, text=new_num)
new_button.grid(column=i // 5, row=i % 5)
new_button.bind("<Button-1>", click_event)
# create clock
current_time = 0
clock = tk.Label(window, text=str(current_time))
clock.grid(column=2, row=6)
clock_started = False
# run game
window.mainloop()
In your code, clock_started has been initialized to True which implies that this condition if not clock_started: will not be satisfied to begin with and hence the timer doesn't work without giving an error. Your final click_event(event) should look like this:
def click_event(event):
global clock_started
new_button = event.widget
clicked_val = int(new_button["text"])
clock_started = False
if not clock_started:
clock_started = True
clock.after(1000, time_event)
if clicked_val == new_list[0]:
del new_list[0]
new_button["state"] = tk.DISABLED
if len(new_list) == 0:
clock.started = False
clock.after_cancel(after_id)

using pynput for key events instead of tkinter

Here is my entire program. I want to change from using tkinter with a gui, to using pynput no gui for get_key events part of my code. Can anyone show me what code to use? This code is a talking vending machine that reads from the machine contents list, which is a file that gets updated by the vending machine company.
I dont want to use a gui as there will be no monitor attached. It's a Raspberry.
from gtts import gTTS
import pygame
from io import BytesIO
import sys
import time
import csv
pygame.init()
if sys.version_info[0] == 3:
# for Python3
from tkinter import *
else:
# for Python2
from Tkinter import *
def say(text):
tts = gTTS(text=text, slow=False, lang='en-us', lang_check=False)
fp = BytesIO()
tts.write_to_fp(fp)
fp.seek(0)
pygame.mixer.init()
pygame.mixer.music.load(fp)
pygame.mixer.music.play()
def load_list():
with open(r"/home/pi/VendyLogProject/vendylist.csv", mode="r") as infile:
return sorted(list(csv.reader(infile)))
def refresh_list():
global vl, vl2, baseposition
new_items = load_list()
if vl != new_items:
vl = new_items
vl2 = [item[0] for item in vl]
baseposition = vl[0]
vl = load_list()
vl2 = [item[0] for item in vl]
baseposition = vl[-1] # so when reading through it reads entry 0 first, then 1
def current(event=None):
global baseposition # baseposition was defined outside of the function, therefore we call global
say(baseposition[1]+baseposition[0])
def back(event=None):
global baseposition
currentposition = vl.index(baseposition)
if currentposition == 0:
baseposition = vl[-1]
say(baseposition[1]+baseposition[0])
else:
previousposition = int(currentposition) - 1 # previousposition is previous position
baseposition = vl[previousposition]
say(baseposition[1]+baseposition[0])
def forward(event=None):
global baseposition
currentposition = vl.index(baseposition)
if currentposition == (len(vl) - 1):
baseposition = vl[0]
say(baseposition[1]+baseposition[0])
else:
nextposition = int(currentposition) + 1 # nextposition is next position
baseposition = vl[nextposition]
say(baseposition[1]+baseposition[0])
def readnumber(int):
global vl
for item in vl:
global baseposition
currentposition = vl.index(baseposition)
if int == item[0]:
baseposition = vl[vl.index(item)]
say(baseposition[1]+baseposition[0])
def help():
say("Welcome to Vendy log! Use the plus and minus keys to go through the list of snacks or push a number to hear its contents!")
root = Tk()
prompt = ' VendyLog '
label1 = Label(root, text=prompt, width=len(prompt))
label1.pack()
#keys buffer
keybuf = []
def test_after():
if keybuf:
num = ''.join(keybuf)
keybuf.clear()
def get_key(event):
keybuf.append(event.char)
event.char = ''.join(keybuf)
root.after(500,test_after)
if event.char == '-':
back()
elif event.char == '+':
forward()
elif event.char == '.':
current()
elif event.char in vl2:
readnumber(event.char)
elif event.char == '00':
help()
elif event.char == '462.':
sys.exit()
def refresh_list_and_enqueue_next_refresh():
refresh_list()
root.after(60000, refresh_list_and_enqueue_next_refresh)
refresh_list_and_enqueue_next_refresh()
root.bind_all('<Key>', get_key)
root.mainloop()
I edited this comment because after I played with OpenGL and Pygame I found the answer of how to use pynput with tkinter.
This is the code sample what I write it to test if works.
# // Imports
import tkinter, pynput
from tkinter import messagebox
# // Global variables
# // If we define variable type I found that will speed up execution a little
root:object = tkinter.Tk()
app_title:str = "Tkinter and Pynput"
app_size:tuple = (300, 150)
listener_stop:bool = False
# // Logics Keyboard
class Keyboard:
# On button pressed
# On my case I found the "Fn" button from laptop is not triggered...
#staticmethod
def Pressed(key) -> bool:
# If listener_stop is True then stop listening
if listener_stop: print("Keyboard Events are stoped!"); return False
# Else show pressed key
else: print(f"Keyboard pressed: {key}")
# On button released
#staticmethod
def Released(key) -> None:
print(f"Keyboard released: {key}")
# Listen keybboard buttons
#staticmethod
def Listener() -> None:
k_listen = pynput.keyboard.Listener(on_press=Keyboard.Pressed,
on_release=Keyboard.Released
)
k_listen.start()
# // Logics Mouse
class Mouse:
# On move
#staticmethod
def Move(x, y) -> bool:
# If listener_stop is True then stop listening
if listener_stop: print("Mouse Events are stoped!"); return False
else: print(f"Mouse: Moved to {x}x{y}")
# On scroll
#staticmethod
def Scroll(x, y, dx, dy) -> None:
where = "down" if dy < 0 else "up"
print(f"Mouse: Scrolled {where} at {x}x{y}")
# On click
# On my case I found mouse wheel press is not triggered...
#staticmethod
def Click(x, y, button, pressed) -> None:
action = "pressed" if pressed else "released"
print(f"Mouse: {button} was {action} at {x}x{y}")
# Listen keybboard buttons
#staticmethod
def Listener() -> None:
m_listen = pynput.mouse.Listener(on_move=Mouse.Move,
on_click=Mouse.Click,
on_scroll=Mouse.Scroll
)
m_listen.start()
# // Logics Define GUI
class MainApp:
def __init__(self, master):
self.master = master
# Create tkinter interface
self.X = (self.master.winfo_screenwidth() - app_size[0]) // 2
self.Y = (self.master.winfo_screenheight() - app_size[1]) // 2
self.master.wm_title(app_title)
self.master.wm_geometry(f"{app_size[0]}x{app_size[1]}+{self.X}+{self.Y}")
# Magic hapen here :P
self.Screen(self.master)
self.InputEvents()
# Define Screen Informations
def Screen(self, root) -> None:
# Set the main frame
self.frm_main = tkinter.Frame(root)
self.frm_main.pack(expand=True, fill="x", side="top")
# Defain frame components
self.title = tkinter.Label(self.frm_main, text=app_title, font=("Comic Sans MS", 18, "bold"), fg="tomato")
self.title.pack(expand=False, fill="x", side="top")
# Input events
def InputEvents(self) -> None:
Keyboard.Listener()
Mouse.Listener()
# Safe Quit
def SafeQuit(self, master:object = root) -> None:
global listener_stop
if messagebox.askokcancel(f"{app_title} Quit", f"Are you shore you want to quit {app_title}?"):
# We need to make shore if the window was closed and
# listening event are still runing, then make them stop
# You will see in fact they are not stoped (from console point view)
# for that reason we check listener_stop on Mouse Move and on Keyboard Key press.
# If for some reason the app is quit and listener has not stoped then on first action did will stop
# Mouse move after app quit >> Mouse listener will stop
# Keyboard button pressed after app quit >> Keyboard listener will stops
if listener_stop == False:
listener_stop = True
print("Events Listening are stoped!")
master.destroy()
# // Run if this is tha main file
if __name__ == "__main__":
app:object = MainApp(root)
root.protocol("WM_DELETE_WINDOW", app.SafeQuit)
root.mainloop()
I updated the code again with stop lisening keyboard and mouse event.
PS: An updated version of them can be found on my github

how to stop timer at 20 minutes

I am making a timer that starts when the user hits "space" and stops on "p", showing the ending time. I can I stop it at a maximum time of 20 minutes? Is there something like
if time_passed==20:
break
My code:
from turtle import*
from datetime import datetime
...
def start():
undo()
global break1, timerint, startime
break1 = 0
startime = datetime.now()
while True:
timerint = datetime.now()-startime
write(timerint,font=("Arial",50))
undo()
if break1 == 1:
break
def stop():
global break1, timerint, startime
timerint=datetime.now()-startime
write(timerint,font=("Arial",50))
break1 = 1
# Turtle placement code removed
onkeypress(start,"space")
onkeypress(stop,"p")
listen()
No, but you can always check elapsed time with the time.time() method.
import time
start = time.time()
while ...
....
now = time.time()
if now - start > 20 * 60:
break
That's the low-tech version. If you want more sophisticated things, such as a separate timer process, try a full browser search for "Python timer process".
Also, you might consider using Boolean values:
global timer_running, timerint, startime
timer_running = True
startime = datetime.now()
while timer_running:
timerint = datetime.now()-startime
write(timerint,font=("Arial",50))
undo()
def stop():
global timer_running, timerint, startime
timerint = datetime.now()-startime
write(timerint, font=("Arial", 50))
timer_running = False
I recommend getting rid of the while loop and instead build upon turtle's ontimer() events:
from turtle import Turtle, Screen
from datetime import datetime
FONT = ("Arial", 50)
def start():
global timer_running, start_time
if timer_running:
return
start_time = datetime.now()
timer_running = True
screen.ontimer(lambda time=start_time: automatic_stop(time), 20 * 60 * 1000)
screen.ontimer(update, 100)
def update():
if not timer_running:
return
timerint = datetime.now() - start_time
marker.undo()
marker.write(timerint, align='center', font=FONT)
screen.ontimer(update, 100)
def manual_stop():
global timer_running
if not timer_running:
return
timer_running = False
timerint = datetime.now() - start_time
marker.undo()
marker.write(timerint, align='center', font=FONT)
def automatic_stop(time):
global timer_running
if timer_running and start_time == time: # make sure *this* timer is still valid
timer_running = False
marker.undo()
marker.write("Expired!", align='center', font=FONT)
screen = Screen()
marker = Turtle(visible=False)
marker.penup()
marker.write("Hit 'space' to start timer; 'p' to stop", align='center', font=FONT)
start_time = None
timer_running = False
screen.onkeypress(start, "space")
screen.onkeypress(manual_stop, "p")
screen.listen()
screen.mainloop()
We pass automatic_stop() a copy of start_time so that when it wakes up in the distant future it can check if it is still a valid end event or not based on the current start_time. (If you work at the Tk level instead of turtle, you might be able to cancel the timer when no longer needed.)

Raspberry PI Button Push Listener

Is it possible to have button push listener in Python for the Raspberry Pi. I have a non-latching button going to a GPIO. I want to run some python code the first time the button is pushed. Then I want the code to stop on the second button push no matter where it is in the first line of code.
I've used a toggle bit variable called "flag" to register button pushes but obviously there is no listener to determine when the second push is made.
#!/usr/bin/env python
import RPi.GPIO as GPIO
import time
Button = 16 # pin16
def setup():
GPIO.setmode(GPIO.BOARD) # Numbers GPIOs by physical location
GPIO.setup(Button, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Button Input
def motorcontrol():
flag=0
while True:
j=GPIO.input(16)
if j==1: #Robot is activated when button is pressed
flag=1
print "Robot Activated",j
while flag==1:
time.sleep(5)
print "Robot Activated for 5 seconds"
time.sleep(5)
print "Robot Activated for 10 seconds"
time.sleep(5)
print "Robot Activated for 15 seconds"
j=GPIO.input(16)
if j==1: #De activate robot on pushing the button
flag=0
print "Robot DeActivated",j
destroy()
def destroy():
GPIO.cleanup() # Release resource
if __name__ == '__main__': # Program start from here
setup()
try:
motorcontrol()
except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed.
destroy()
You can't use sleep() this way because your while loop can't check your botton. You have to loop all time and check if it is time to display text.
You can use small sleep() to use lees CPU.
import time
current_time = time.time()
text_1 = current_time + 5
text_2 = current_time + 10
text_3 = current_time + 15
flag = True
while flag:
# TODO: check your button and change `flag`
current_time = time.time()
if text_1 and current_time >= text_1:
print("5 seconds")
text_1 = None # to stop displaying
# or show again after 5 seconds
#text_1 = current_time + 5
if text_2 and current_time >= text_2:
print("10 seconds")
text_2 = None # to stop displaying
if text_3 and current_time >= text_3:
print("15 seconds")
text_3 = None # to stop displaying
flag = False
#time.sleep(0.1)
Or more like in most GUI
import time
# --- functions ---
def callback_1():
print("5 seconds")
# add new task to list
tasks.append( (current_time + 5, callback_1) )
def callback_2():
print("10 seconds")
def callback_3():
print("15 seconds")
def callback_4():
global flag
flag = False
# --- main ---
current_time = time.time()
tasks = []
tasks.append( (current_time + 5, callback_1) )
tasks.append( (current_time + 10, callback_2) )
tasks.append( (current_time + 15, callback_3) )
tasks.append( (current_time + 17, callback_4) )
flag = True
while flag:
# TODO: check your button
current_time = time.time()
# this way I execute task and remove from list
new_tasks = []
for t, c in tasks:
if current_time >= t:
c()
else:
new_tasks.append( (t,c) )
tasks = new_tasks
#time.sleep(0.1)
EDIT: I don't have RPi so I try to simulate it using own class GPIO - but maybe it will work on your computer. It shows where you should put code.
#!/usr/bin/env python
import RPi.GPIO as GPIO
import time
'''
#
# for test only - instead of `import RPi.GPIO as GPIO`
#
# simulate button press
current_time = time.time()
button_1 = current_time + 2
button_2 = current_time + 10
class GPIO:
BOARD = None
IN = None
PUD_DOWN = None
#staticmethod
def setmode(a):
pass
#staticmethod
def setup(a, b, pull_up_down=None):
pass
#staticmethod
def input(a):
global button_1, button_2
current_time = time.time()
if button_1 and current_time >= button_1:
button_1 = None
return 1
if button_2 and current_time >= button_2:
button_2 = None
return 1
return 0
#staticmethod
def cleanup():
pass
'''
Button = 16 # pin16
def setup():
GPIO.setmode(GPIO.BOARD) # Numbers GPIOs by physical location
GPIO.setup(Button, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Button Input
def motorcontrol():
flag = False
while True:
j = GPIO.input(16)
if j == 1:
flag = True
print "Robot Activated", j
current_time = time.time()
text_1 = current_time + 5
text_2 = current_time + 10
text_3 = current_time + 15
while flag:
j = GPIO.input(16)
if j == 1:
flag = False
print "Robot DeActivated", j
destroy()
current_time = time.time()
if text_1 and current_time >= text_1:
print "5 seconds"
text_1 = None # to stop displaying
# or show again after 5 seconds
#text_1 = current_time + 5
if text_2 and current_time >= text_2:
print "10 seconds"
text_2 = None # to stop displaying
if text_3 and current_time >= text_3:
print "15 seconds"
text_3 = None # to stop displaying
flag = False
time.sleep(0.1)
def destroy():
GPIO.cleanup()
if __name__ == '__main__':
setup()
try:
motorcontrol()
except KeyboardInterrupt:
destroy()

Categories