How to get the duration of key presses? - python-3.x

I am trying to get the duration of a key pressed using python but my code works only for the first time and then the next key presses show the total time since the program executed.
from pynput import keyboard
import time
def keydet(key):
return False
def keypress():
def callb(key): # what to do on key-release
ti1 = time.time() - t
ti2 = str(ti1) # converting float value to string
ti3 = ti2[0:5] # cutting the seconds ( time ) , without it , it will print like 0.233446546
print("The key", key, "Pressed For", ti3)
kp = key
print(kp)
a = time.sleep(0.0001)
return key # stop detecting more key-releases
def callb1(key): # what to do on key-press
return False # stop detecting more key-presses
with keyboard.Listener(on_press=callb1) as listener1: # setting code for listening key-press
listener1.join()
t = 0
t = time.time()
with keyboard.Listener(on_release=callb) as listener: # setting code for listening key-release
listener.join()
#Keypress Detector
with keyboard.Listener(on_press=keydet) as listener:
keypress()
I tried googling it but couldn't find the best solution for me. I also saw some questions in this site but nothing worked for me.

I don't know exactly how to do this, but maybe I can point you in the right direction. Have you heard of keyboard make/break codes? When you hold a key down, it keeps sending data to your PC. If there's some low level way to see these, you could simply count them as a way of judging duration. Happy googling!

Related

Trying to make a can counter on a PI 4 in python, Strugging with code for two sensors running at the same time

So I work in the beverage industry and I decided to try and make a can counter using a Raspberry PI4. It needs to use two industrial sensors on the GPIO that detect the cans.
Full Disclosure I have just been googling most code and reading when I get errors in terminal to try and fix the issue. I have done some rudimentary C# and C++ programming doing PLC stuff but it's nothing like what i'm trying to do right now. Just some simple statements for conversions and formulas.
I have had it counting via the sensor on a very rudimentary code
import RPi.GPIO as GPIO
import time
GPIN = 16
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIN, GPIO.IN)
counting = 0
while True:
while GPIO.input(GPIN) == 0:
time.sleep(0.1)
counting = counting + 1
print(counting)
while GPIO.input(GPIN) == 1:
time.sleep(0.1)
This counts in the terminal. It is of note I need to count the on and off state with a slight delay to keep accidental double counts from happening. I have even added in a GUI with guizero that makes it count in a window. although currently I cannot replicate that from what I remember working and i foolishly didn't save that as i was trying to get to the next stage, but the rough of it was instead of the print(counting) section in the above code I had the app.display() info.
Problem is I need it to count 2 sensors at the same time, one before the can rejector and one after. So I did some reading and figured I needed to run two (or maybe 3) loops at the same time. as I have 2 sensors that need a constant loop, plus it seems like a need another loop that runs and refreshes the GUI. I got turned into threading and have been trying to implement it as that seems like what I want but haven't been able to make heads or tails of it. I can get the GUI to display, but the sensors don't read. If I switch back to my simple code it counts away. Im having trouble meshing the two together.
import threading
from guizero import App, Text, PushButton
import RPi.GPIO as GPIO
import time
GPIN1 = 16
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIN1, GPIO.IN)
GPIN2 = 15
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIN2, GPIO.IN)
counting1 = 0
counting2 = 0
counting3 = counting1 - counting2
def sensor1():
global counting1
while GPIO.input(GPIN1) == 0:
time.sleep(0.1)
counting1 = counting1 + 1
while GPIO.input(GPIN1) == 1:
time.sleep(0.1)
def sensor2():
global counting2
while GPIO.input(GPIN2) == 0:
time.sleep(0.1)
counting2 = counting2 + 1
while GPIO.input(GPIN2) == 1:
time.sleep(0.1)
x = threading.Thread(target=sensor1)
y = threading.Thread(target=sensor2)
x.start()
y.start()
while True:
app = App(title="Can Count")
message = Text(app, text="Total")
message = Text(app, text=(counting1))
message = Text(app, text="Rejected")
message = Text(app, text=(counting3))
app.display()
I'm just a bit stumped I'm sure my way isn't the best way to do this, any advice, tips or pointers in the right direction would be appreciated. I'm trying to crash course youtube python tutorials on the side but I am still coming up short.
It seems like I can get the display to show updates if i close the window via the x it restarts the window and shows the update but I have tried a few different things with guizero using a def text(): above that code and text.repeat(10, text) thinking this would redraw the screen but that doesn't work or breaks the gui or the code.
Also I know I call PushButton and don't use it, but the end goal will have a simple reset the counter button.. Just haven't got there yet.

Python 3.x - Multi mouse button activated auto clicker

I am having troubles with the python code i am trying to write. I am attempting to create an auto-clicker that when I hold down left click and one of the side buttons, left click gets triggered every 0.1 seconds. If I let go of either button, the auto-clicker stops. I have searched through the PyAutoGUI, PyNput, and the PyGame libraries, as well as many of stack overflow posts on the topic of mouse detection. Here is the code I have put together so far. (note, a majority of this code is not mine, I simply do not remember where I grabbed the code snippits otherwise they would be linked below.
from pynput import mouse
from pynput.mouse import Button, Controller
import threading
import time
control = Controller()
running = False
leftPressed=False
rightPressed=False
def process():
print('start')
count = 0
while running:
print(count)
count += 1
control.click(Button.left,1)
print("test")
time.sleep(.05)
print('stop')
def on_click(*args):
global running, leftPressed, rightPressed
if args[-1]:
# mouse key pressed
if args[-2].name == "left":
leftPressed = True
print("Left click")
elif args[-2].name == "x2":
rightPressed = True
if leftPressed and rightPressed:
# if both left and right are pressed
running = True
threading.Thread(target=process).start()
elif not args[-1]:
# mouse key released
if args[-2].name == "x1":
leftPressed = False
elif args[-2].name == "x2":
rightPressed = False
# as one key has been released, both are no longer pressed
running = False
with mouse.Listener(on_click=on_click) as listener:
listener.join()
Any help with this project would be greatly appreciated, I've come to realize the biggest issue is that since the "on click" event is expecting a left click to run the thread, left clicking inside of the thread is more than not ideal, however if it is possible to program it in such a way where this isn't an issue it would be greatly appreciated.
The pseudocode for what I am looking for would be:
if Button.x2 == pressed and Button.left == pressed:
bothPressed = True
else:
bothPressed = False
while bothPressed:
Button.left.click()
I have tried using multiple methods of obtaining the mouse inputs through PyGame and PyNput. However I've found that I understand PyNput the best and would prefer to stick with it.

Can't move graphics using graphics.py and pynput

I'm trying to move a single point in python using pynput but it does not move
I've tried making it so that keyboard.press("W") increases pointx and pointy by 10
from graphics import *
from keyboard import *
from pynput import *
keyboard = keyboard.Controller()
pointx = 250
pointy = 250
win = GraphWin("test", 500, 500)
pt = Point(pointx, pointy)
pt.draw(win)
while keyboard.press("w"):
pt.move(10, 10)
pt.draw(win)
There are no error messages
First thing straight away: The pynput and keyboard libraries do the same thing, so you only need to use one of them. As keyboard seems to need root rights on Linux, I suggest using pynput.
Don't use the same names twice. keyboard already is already a package, don't use it as a variable name.
keyboard.Controller() is meant for controlling the keyboard, not for reading it. What you were probably looking for is keyboard.Listener.
With keyboard.Listener, you cannot check for keys directly, instead you get a notification whenever a key gets pressed or released. Those notification(=callback) functions have to be given to keyboard.Listener in its constructor.
You can then either apply an action directly, whenever a key gets pressed, or you can track the current key state in a global variable, like this:
# The global dict that keeps track of the keyboard state
key_state = {}
# The function that gets called when a key gets pressed
def key_down(val):
global key_state
key_state[val] = True
# The function that gets called when a key gets released
def key_up(val):
global key_state
key_state[val] = False
# Initializes the keyboard listener and sets the functions 'key_down' and 'key_up'
# as callback functions
keyboard_listener = keyboard.Listener(on_press=key_down, on_release=key_up)
keyboard_listener.start()
We can then in our program check if a key got pressed with:
if key_state.get(keyboard.KeyCode(char='w')):
The entire program would look something like this:
from graphics import *
from pynput import *
import time
pointx = 250
pointy = 250
win = GraphWin("test", 500, 500)
pt = Point(pointx, pointy)
pt.draw(win)
# The global dict that keeps track of the state of 'w'
key_state = {}
# The function that gets called when a key gets pressed
def key_down(val):
global key_state
key_state[val] = True
# The function that gets called when a key gets released
def key_up(val):
global key_state
key_state[val] = False
# Initializes the keyboard listener and sets the functions 'key_down' and 'key_up'
# as callback functions
keyboard_listener = keyboard.Listener(on_press=key_down, on_release=key_up)
keyboard_listener.start()
# Continuously loop and update the window (important so it doesn't freeze)
while win.isOpen():
win.update()
time.sleep(0.01)
# Little bit of trickery:
# We combine the check if the key exists and if its value is 'true' in one
# single operation, as both 'None' and 'False' are the same value for 'if'.
if key_state.get(keyboard.KeyCode(char='w')):
pt.move(10, 10)

How To Print Strings From Python App to a Browser Text-Field within a Flash Game

I want this small python program to be able to output strings into a text-field within a flash game on Google Chrome. The key press emulations work, however the print function obviously only returns the printed code to console.
I tried a few workarounds using a for loop to push the string values into a .press and .release functions, but (not only is this not optimal code) it (as far as I could tell) can't be done.
def click():
username=textentry.get()
time.sleep(2)
for i in range(1000):
print(username)
keyboard.press(Key.tab)
keyboard.release(Key.tab)
print(f'{i:03}')
keyboard.press(Key.enter)
keyboard.release(Key.enter)
keyboard.press(Key.shift)
keyboard.press(Key.tab)
keyboard.release(Key.tab)
keyboard.release(Key.shift)
Prints fine to console, and the key presses and releases are working. Only issue is that text doesn't get sent to anything outside the console.
My ideal situation is that you can simply start the program and then click into the text-fields (one for username, the other for the 3-code integer) and it will do the rest for you, running through username-associated 000 ints until it gets the friend code.
This program IS not (and couldn't) guess somebody's password. It's for a harmless flash game called stick empires.
The question seems unsolvable (I could be wrong, but after researching for hours and hours the reoccurring answer is that it can't be done.) But here's a semi-work around:
If you use pynputs .type("") function, you can essentially queue up key presses in your operating system as though you yourself were typing a string out. To do this as I requested above, with the first textfield needing a username to be entered, and the second textfield needing a 3 digit integer (iterating up from 000 to 999 for all possibilities) I had to use two for-loops and call a bunch of functions. It looks pretty ugly but it is functional.
#Click function to run inputs
def click():
t = 0; j = 0
lst = []
keyboard = Controller()
username = textentry.get()
a = len(username)
time.sleep(2)
for i in range(1000):
for j in range(a):
keyboard.press(username[j])
keyboard.release(username[j])
j += 1
keyboard.press(Key.tab)
keyboard.release(Key.tab)
lst.append(f'{i:03}')
keyboard.type(lst[i])
keyboard.press(Key.enter)
keyboard.release(Key.enter)
keyboard.press(Key.shift)
keyboard.press(Key.tab)
time.sleep(0.02)
keyboard.release(Key.tab)
keyboard.release(Key.shift)
i += 1
j = 0

Scale not updating during key inputs

I am currently working on scales using tkinter. I have gotten the code to work except one thing. I have bound keys to the motion of a servo motor. When I press the keys however the scale does not follow what the key bindings do.How can I get the scale to follow the key bindings?
Please see code below
....GPIO setup code above not shown...
def fMin(event):
iDCServo = 2.5
pServo.ChangeDutyCycle(iDCServo) #this is pwm code for the servo motor
def fMin2(event):
iDCServo = 7.5
pServo.ChangeDutyCycle(iDCServo)
def fMax(event):
iDCServo = 12.5
pServo.ChangeDutyCycle(iDCServo)
def fMax2(event):
iDCServo = 7.5
pServo.ChangeDutyCycle(iDCServo)
def fOperation():
global guiSliderServo1, iLoop
while True:
win = Tk()
win.wm_title(">>>Servo Slider<<<")
win.geometry("800x100+0+0")
guiSliderServo1 = Scale(win, from_=-45, to_=45, orient=HORIZONTAL, length_=700, sliderlength_=10, tickinterval_=5, command=fSliderServo1)
guiSliderServo1.set(0)
guiSliderServo1.grid(row=0)
guiSliderServo1.pack(side=TOP)
guiSliderServo1.bind('<Key-q>', fMin)
guiSliderServo1.bind('<KeyRelease-q>', fMin2)
guiSliderServo1.bind('<Key-e>', fMax)
guiSliderServo1.bind('<KeyRelease-e>', fMax2)
guiSliderServo1.focus_set()
guiButtonExit = Button(win, text="Exit Slider", command=quit)
guiButtonExit.pack(side=BOTTOM)
win.mainloop()
...there is some remaining code regarding looks, functions and imports not shown, not sure, but probably would just clutter the real question.
Thank you
The while True hogs the computer so there is no time left for Tkinter to update the widget. You should be able to just delete the while True in the above code and be fine because Tkinter's mainloop() essentially does the same thing, i.e. continually checks for a keypress. Also mixing grid and pack, you use both, yields unknown results. Settle on one and use it. Note that this code does not change the scale, and the Button command should be win.quit.

Resources