How to loop until a certain button is pressed using pynput? - python-3.x

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.

Related

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/

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)

override alt-f4 closing tkinter window in python 3.6 and replace it with something else

I spent a long time reading through past posts and didn't find quite what I need.
I'm making a kiosk-type window using tkinter. I set it to open fullscreen. I want to override the standard alt-f4 command to close it. I don't want it completely uncloseable though. I'd like to instead make up my own keyboard shortcut (alt-ctrl-g-3 or something like that).
I tried some of the suggestions for overriding alt-f4 and couldn't get them to work. In my init function I included:
self.bind('<Alt-Key-F4>', self.ignorekey())
.
.
.
def self.ignorekey(self)
pass
There is a way to hook the Alt+F4 or pressing the X or any other way as far as I know:
root.protocol("WM_DELETE_WINDOW", do_exit)
where do_exit() is the callback function and root is your main window.
This binding does not pass an event object. As far as I can see this should work for any platform.
Here is an example:
from tkinter import *
root = Tk()
pressed_f4 = False # Is Alt-F4 pressed?
def do_exit():
global pressed_f4
print('Trying to close application')
if pressed_f4: # Deny if Alt-F4 is pressed
print('Denied!')
pressed_f4 = False # Reset variable
else:
close() # Exit application
def alt_f4(event): # Alt-F4 is pressed
global pressed_f4
print('Alt-F4 pressed')
pressed_f4 = True
def close(*event): # Exit application
root.destroy()
root.bind('<Alt-F4>', alt_f4)
root.bind('<Escape>', close)
root.protocol("WM_DELETE_WINDOW",do_exit)
root.mainloop()
I'm not sure that the callback from root.bind('<Alt-F4>', alt_f4) always will run before the callback from root.protocol("WM_DELETE_WINDOW",do_exit). You may have to do more research to establish that.

test pyautogui functions with unittest in python3.6

I need to test functions that use pyautogui. For example, if pressing keyboard key "a" actually press an "a". How can I reproduce this? There is a way to capture the key that it´s been press?
Ideal if it works in Windows and Linux
The PyAutoGUI unit tests have something like this. See the TypewriteThread code for details. Set up a thread to wait a second and call pyautogui.typewrite(), then call input() to accept text. Make sure your window is in focus and the typewrite() thread ends by pressing Enter (the \n character).
import time
import threading
import pyautogui
class TypewriteThread(threading.Thread):
def __init__(self, msg, interval=0.0):
super(TypewriteThread, self).__init__()
self.msg = msg
self.interval = interval
def run(self):
time.sleep(0.25)
pyautogui.typewrite(self.msg, self.interval)
t = TypewriteThread('Hello world!\n')
t.start()
response = input()
print(response == 'Hello world!')

Multiprocessing - tkinter pipeline communication

I have a question on multiprocessing and tkinter. I am having some problems getting my process to function parallel with the tkinter GUI. I have created a simple example to practice and have been reading up to understand the basics of multiprocessing. However when applying them to tkinter, only one process runs at the time. (Using Multiprocessing module for updating Tkinter GUI) Additionally, when I added the queue to communicate between processes, (How to use multiprocessing queue in Python?), the process won't even start.
Goal:
I would like to have one process that counts down and puts the values in the queue and one to update tkinter after 1 second and show me the values.
All advice is kindly appreciated
Kind regards,
S
EDIT: I want the data to be available when the after method is being called. So the problem is not with the after function, but with the method being called by the after function. It will take 0.5 second to complete the calculation each time. Consequently the GUI is unresponsive for half a second, each second.
EDIT2: Corrections were made to the code based on the feedback but this code is not running yet.
class Countdown():
"""Countdown prior to changing the settings of the flows"""
def __init__(self,q):
self.master = Tk()
self.label = Label(self.master, text="", width=10)
self.label.pack()
self.counting(q)
# Countdown()
def counting(self, q):
try:
self.i = q.get()
except:
self.label.after(1000, self.counting, q)
if int(self.i) <= 0:
print("Go")
self.master.destroy()
else:
self.label.configure(text="%d" % self.i)
print(i)
self.label.after(1000, self.counting, q)
def printX(q):
for i in range(10):
print("test")
q.put(9-i)
time.sleep(1)
return
if __name__ == '__main__':
q = multiprocessing.Queue()
n = multiprocessing.Process(name='Process2', target=printX, args = (q,))
n.start()
GUI = Countdown(q)
GUI.master.mainloop()
Multiprocessing does not function inside of the interactive Ipython notebook.
Multiprocessing working in Python but not in iPython As an alternative you can use spyder.
No code will run after you call mainloop until the window has been destroyed. You need to start your other process before you call mainloop.
You are calling wrong the after function. The second argument must be the name of the function to call, not a call to the function.
If you call it like
self.label.after(1000, self.counting(q))
It will call counting(q) and wait for a return value to assign as a function to call.
To assign a function with arguments the syntax is
self.label.after(1000, self.counting, q)
Also, start your second process before you create the window and call counting.
n = multiprocessing.Process(name='Process2', target=printX, args = (q,))
n.start()
GUI = Countdown(q)
GUI.master.mainloop()
Also you only need to call mainloop once. Either position you have works, but you just need one
Edit: Also you need to put (9-i) in the queue to make it count down.
q.put(9-i)
Inside the printX function

Resources