Can't move graphics using graphics.py and pynput - python-3.x

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)

Related

How to loop until a certain button is pressed using pynput?

My current code:
from pynput import keyboard
from pynput.keyboard import Key, Controller
import threading
import datetime, time
import os
import subprocess
keyboard = Controller()
def loading():
os.system("open /System/Applications/TextEdit.app")
time.sleep(1)
while running:
keyboard.press("a")
def on_press(key):
global running # inform function to assign (`=`) to external/global `running` instead of creating local `running`
if key == keyboard.Key.left:
running = True
# create thread with function `loading`
t = threading.Thread(target=loading)
# start thread
t.start()
if key == keyboard.Key.down:
# to stop loop in thread
running = False
if key == keyboard.Key.right:
# stop listener
return False
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
Im trying to loop keyboard presses, but stop when right or down key is pressed, but I'm getting an error of "'Controller' object has no attribute 'Listener'". My code works when I don't include the listener part, so I'm not exactly sure how to combine them together to make a cohesive program.
The problem is that you wrote from pynput import keyboard and then after that, keyboard = Controller(), so the same variable keyboard then refers to your instance of Controller, and not the module pynput.keyboard (which is what actually has the Listener class in it)
You can use a different name for that, while instantiating it and then wherever else you use it, such as inside loading(), then the code will work.
Note: Using a loop with no delay like while True: keyboard.press('a') is an absolutely dangerous idea - I've had my computer completely hang, with no response , because of statements like that - and had to shut it down by force to be able to continue doing anything. It's better to include a time.sleep there too.

Why is it saying a isnt defined even though i have defined you

so i defined a but when i try to type out a using keybaord.type it just says that it isnt defined
i tried making a global that didnt work i tried moving postions of the code and that didnt work ive tried many other things they didnt work either
from tkinter import *
import webbrowser
from pynput.keyboard import Key, Controller
import time
menu = Tk()
menu.geometry('200x300')
def webop(): # new window definition
global a
def hh():
a = "" + txt.get()
while True:
keyboard = Controller()
time.sleep(1)
keyboard.type(a)
keyboard.press(Key.enter)
keyboard.release(Key.enter)
sp = Toplevel(menu)
sp.title("Spammer")
txt = Entry(sp, width=10)
txt.grid(row=1,column=1)
btn = Button(sp, text='spam', command=hh)
btn.grid(row=1,column=2)
def enc():
window = Toplevel(menu)
window.title("nou")
button1 =Button(menu, text ="Spammer", command =webop) #command linked
button2 = Button(menu, text="Fake bot", command = enc)
button1.grid(row=1,column=2)
button2.grid(row=2,column=2)
menu.mainloop()
global a underneath def webop() gives webop access to the variable a in the enclosing scope (the scope up there where you're doing your imports). Since you haven't defined a in that scope, you're getting the error.
Either way, you generally should avoid using globals like that and pass data to functions using arguments. In order to pass arguments into your Button command you can use a closure.
You should move the part of your code accessing a to the part where that value is set
It is unclear what you're trying to achieve here since when you run webop your program will reach while True and continuously loop there and never reach the code below your while loop
For instance
def hh(a):
a = "" + txt.get()
while True:
keyboard = Controller()
time.sleep(1)
keyboard.type(a)
keyboard.press(Key.enter)
keyboard.release(Key.enter)
btn = Button(sp, text='spam', command=hh)
An alternative approach achieves the same thing using functools partial. See https://www.delftstack.com/howto/python-tkinter/how-to-pass-arguments-to-tkinter-button-command/

Why win32api can not scan keys after filedialog was used in Python 3?

After using "askdirectory" from Tkinter in Python 3 I win32api.GetKeyState() doesn't work.
Hello there!
In Python 3 I am trying to catch keys that were pressed to make several actions in my tool. The logic looks like this:
1. I select a folder to save results.
2. I catch key strokes and do some actions.
3. I save results in the folder.
To select a folder I use "askdirectory" from tkinter.
To scan keys I use win32api.GetKeyState()
When I am trying to scan keys without calling askdirectory, everything works fine. If I call askdirectory and then try to scan keys, win32api.GetKeyState() shows static results. It seems that win32api can not get results anymore.
Can someone help me to solve the problem?
The code below fails on my machine. If I make "folder = filedialog.askdirectory(title="Tilte")" a comment - everything works great.
Thanks in advance.
import time
import tkinter
from tkinter import filedialog
def scan_keys():
import win32api
roll = True
base_left_mouse = win32api.GetKeyState(0x01)
base_shift = win32api.GetKeyState(0x10)
base_ctrl = win32api.GetKeyState(0x11)
base_alt = win32api.GetKeyState(0x12)
base_p = win32api.GetKeyState(0x50)
while roll:
print("Scanning keys")
print(base_left_mouse, base_shift, base_ctrl, base_alt, base_p)
left_mouse = win32api.GetKeyState(0x01)
shift = win32api.GetKeyState(0x10)
ctrl = win32api.GetKeyState(0x11)
alt = win32api.GetKeyState(0x12)
p = win32api.GetKeyState(0x50)
if (alt<0 and p!=base_p):
print("ALT + P is pressed")
elif (ctrl<0 and alt<0 and p!=base_p):
print("Gathering complete")
roll = False
base_left_mouse = win32api.GetKeyState(0x01)
base_shift = win32api.GetKeyState(0x10)
base_ctrl = win32api.GetKeyState(0x11)
base_alt = win32api.GetKeyState(0x12)
base_p = win32api.GetKeyState(0x50)
print("Scanning base keys")
time.sleep(1)
root=tkinter.Tk()
root.withdraw()
folder = filedialog.askdirectory(title="Tilte")
print(folder)
scan_keys()
root.mainloop()
The results of GetKeyState depends on the current message queue,
it reports the state of the keyboard based on the messages you have
retrieved from your input queue.
(From The old new thing)
If the current message queue has been changed, The key message will be sent to the new queue, and the message in the old one is Invalid.(unless you back to the old queue).
GetAsyncKeyState return the status reflect the interrupt-level state associated with the hardware.
And you also need to ignore the least significant bit:
base_x = win32api.GetKeyState(key)&0x8000

How to get the duration of key presses?

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!

PyQtGraph: GUI unresponsive during only one process

I'm writing a GUI for a video camera that can basically run in two modes that I call liveview and recordview. The only difference being that I'm recording in the latter and only viewing in the former.
In liveview mode the image gets updated properly. I've set a button that triggers recordview but during this acquisition the GUI gets unresponsive and the image doesn't get updated. Let me show you the relevant parts of the code:
import numpy as np
from PyQt4 import QtGui, QtCore
import pyqtgraph as pg
from lantz.drivers.andor.ccd import CCD
app = QtGui.QApplication([])
def updateview(): # <-- this works OK
global img, andor
img.setImage(andor.most_recent_image16(andor.detector_shape),
autoLevels=False)
def liveview():
""" Image live view when not recording
"""
global andor, img, viewtimer
andor.acquisition_mode = 'Run till abort'
andor.start_acquisition()
viewtimer.start(0)
def UpdateWhileRec():
global stack, andor, img, n, ishape
j = 0
while j < n:
if andor.n_images_acquired > j:
# Data saving <-- this part (and the whole while-loop) works OK
i, j = andor.new_images_index
stack[i - 1:j] = andor.images16(i, j, ishape, 1, n)
# Image updating <-- this doesn't work
img.setImage(stack[j - 1], autoLevels=False)
liveview() # After recording, it goes back to liveview mode
def record(n):
""" Record an n-frames acquisition
"""
global andor, ishape, viewtimer, img, stack, rectimer
andor.acquisition_mode = 'Kinetics'
andor.set_n_kinetics(n)
andor.start_acquisition()
# Stop the QTimer that updates the image with incoming data from the
# 'Run till abort' acquisition mode.
viewtimer.stop()
QtCore.QTimer.singleShot(1, UpdateWhileRec)
if __name__ == '__main__':
with CCD() as andor:
win = QtGui.QWidget()
rec = QtGui.QPushButton('REC')
imagewidget = pg.GraphicsLayoutWidget()
p1 = imagewidget.addPlot()
img = pg.ImageItem()
p1.addItem(img)
layout = QtGui.QGridLayout()
win.setLayout(layout)
layout.addWidget(rec, 2, 0)
layout.addWidget(imagewidget, 1, 2, 3, 1)
win.show()
viewtimer = QtCore.QTimer()
viewtimer.timeout.connect(updateview)
# Record routine
n = 100
newimage = np.zeros(ishape)
stack = np.zeros((n, ishape[0], ishape[1]))
rec.pressed.connect(lambda: record(n))
liveview()
app.exec_()
viewtimer.stop()
As you see UpdateWhileRec runs only once per acquisition while updateview runs until viewtimer.stop() is called.
I'm new to PyQt and PyQtGraph so regardless of the particular way of solving my present issue, there's probably a better way to do everything else. If that's the case please tell me!
thanks in advanced
Your problem stems from the fact that you need to return control to the Qt event loop for it to redraw the picture. Since you remain in the UpdateWhileRec callback while waiting for the next image to be acquired, Qt never gets a chance to draw the image. It only gets the chance once you exit the function UpdateWhileRec.
I would suggest the following changes.
Then instead of your while loop in UpdateWhileRec, have a QTimer that periodically calls the contents of your current while loop (i would probably suggest a singleshot timer). This ensures control will be returned to Qt so it can draw the image before checking for a new one.
So something like:
def UpdateWhileRec():
# Note, j should be initialised to 0 in the record function now
global stack, andor, img, n, j, ishape
if andor.n_images_acquired > j:
# Data saving <-- this part (and the whole while-loop) works OK
i, j = andor.new_images_index
stack[i - 1:j] = andor.images16(i, j, ishape, 1, n)
# Image updating <-- this should now work
img.setImage(stack[j - 1], autoLevels=False)
if j < n:
QTimer.singleShot(0, UpdateWhileRec)
else:
liveview() # After recording, it goes back to liveview mode
Note, you should probably put functions and variables in a class, and create an instance of the class (an object). That way you don't have to call global everywhere and things are more encapsulated.
Ultimately, you may want to look into whether your andor library supports registering a function to be called when a new image is available (a callback) which would save you doing this constant polling and/or acquiring the images in a thread and posting them back to the GUI thread to be drawn. But one step at a time!

Resources