At the moment I'm trying to use Python to detect when the left mouse button is being held and then start to rapidly send this event instead of only once. What I basically want to do is that when the left mouse button is held it clicks and clicks again until you let it go. But I'm a bit puzzled with the whole Xlib, I think it's very confusing actually. Any help on how to do this would be really awesome. That's what I've got so far:
#!/usr/bin/env python
import Xlib
import Xlib.display
def main():
display = Xlib.display.Display()
root = display.screen().root
while True:
event = root.display.next_event()
print event
if __name__ == "__main__":
main()
But there is unfortunately no output in the console. After a quick search on the internet I found the following:
root.change_attributes(event_mask=Xlib.X.KeyPressMask)
root.grab_key(keycode, Xlib.X.AnyModifier, 1, Xlib.X.GrabModeAsync,
Xlib.X.GrabModeAsync)
This is seemingly import to catch a special event with the given keycode. But firstly what keycode does the left-mouse click have, if any at all? And secondly how can I detect when it is being held down and then start sending the mouseclick event rapidly. I would be really grateful for help. (Maybe a way to stop this script with a hotkey would be cool aswell...)
Actually you want Xlib.X.ButtonPressMask | Xlib.X.ButtonReleaseMask, to get events for button presses and releases (different from key presses and releases). The events are ButtonPress and ButtonRelease, and the detail instance variable gives you the button number. From when you get the press event, to when you get the release event, you know the button is being held down. Of course you can also receive key events and do something else (e.g. exit your script) when a certain key is pressed.
Edit: this version works fine for me, for example...:
import Xlib
import Xlib.display
def main():
display = Xlib.display.Display(':0')
root = display.screen().root
root.change_attributes(event_mask=
Xlib.X.ButtonPressMask | Xlib.X.ButtonReleaseMask)
while True:
event = root.display.next_event()
print event
if __name__ == "__main__":
main()
Related
import keyboard
import pygame
import mouse
import time
def press_X():
time.sleep(0.2)
keyboard.press('x')
time.sleep(0.6)
keyboard.release('x')
print('Command Executed - press_X')
#SA_R_X V_1.0
#------------------------------------------
while True:
try:
keyboard.add_hotkey('r', press_X)
time.sleep(0.5)
break
except:
keyboard.add_hotkey('r', press_X)
time.sleep(0.5)
break
the problem is the code cannot detect if 'r' is pressed when i am holding 'w' and/or 'space'... (well any key really)
I tried to use a try and except to handle a combination of any key + 'r'. But it did not work. All I need is for the code to be able to detect an 'r' input even if I am pressing/ holding another key at the same time. Then after this the code waits 0.2 seconds before holding down the 'x' key for 0.6 seconds and releasing. Any help is appreciated and it would be very helpful if you included a short explanation on where I went wrong and how you fixed it.
The documentation for this module can be found here. This is where all relevant information can be found.
The best way to do this, from my understanding, is to use an alternative function. If you want the program to continue to run, even though the key has not been pressed, then I suggest using the keyboard.on_press_key() function. This would mean that the rest of your program could still run, and your press_X() function could be run as a callback. Here is an example of how this code could be implemented.
import keyboard
import pygame
import mouse
import time
class App:
running = True
def press_X():
time.sleep(0.2)
keyboard.press('x')
time.sleep(0.6)
keyboard.release('x')
print('Command Executed - press_X')
App.running = False
#SA_R_X V_1.0
#------------------------------------------
keyboard.on_press_key('r', lambda x: press_X()) ## Adds an event listener for the r key
## This will stop execution if there is no code after this point
If you want it to stop the program and wait for the r key to be pressed, then you could use keyboard.wait(). This will basically pause your program until the key is pressed, after which your function would be run. For example, to replace the keyboard.on_press_key('r', lambda x: press_X()):
keyboard.wait('r')
press_X()
From my understanding, keyboard.add_hotkey() does not work in your situation because it is looking for an exact combination of keys being pressed, such as Ctrl+C, and will only go if only the keys in the hotkey are pressed.
I hope this helps, good luck!
I'm currently trying to create a program where it detects the mouse movement on my computer and pops up a window if any movement is detected. However, I've run into some issues with Tkinter threading. I have read multiple other posts on the same issue and understand that Tkinter isn't thread-safe and that it can only be run in the main thread. However, I'm still unsure about the fix for my program. I'd appreciate it if someone could help me take a look.
Essentially, my (simplified) program has two scripts, access_detection.py and password_prompt.py. The first file is responsible for using pynput.mouse to track mouse movement, and the second file is responsible for popping up a Tkinter window and prompting the user for a password whenever mouse movement from the first script is detected.
> password_prompt.py (pops up a window for the user to enter their passcode)
def confirm_passcode(self):
self.access_granted = True
def prompt_password(self) -> bool:
# initialize window
entry = Entry(self.win, width=25, textvariable=self.password, show="*")
entry.pack(pady=((int(self.win.winfo_screenheight()/2) - 100), 0))
button = ttk.Button(self.win, text="Confirm Password", command=self.confirm_passcode)
button.pack(pady=10)
while True:
self.win.attributes('-fullscreen', True)
self.win.attributes('-topmost', True)
if self.access_granted == True:
return True
elif self.access_granted == False and self.access_attempts >= 3:
return False
self.win.update()
> access_detection.py (tracks mouse movement)
def on_move_mouse(self, x, y):
self.security_passed = popup_obj.prompt_password()
if self.security_passed == False:
# do something here
else:
# stops this program
def detection_start(self):
with mouse_listener(on_move=self.on_move_mouse) as mouse_monitor:
mouse_monitor.join()
# driver
if __name__ == "__main__":
monitor = # a class with detection_start()
monitor.detection_start()
The issue raised: RuntimeError: main thread is not in main loop
Currently, the main method is in access_detection.py and I believe the pynput.mouse threading is using the main thread, causing the error since Tkinter isn't thread-safe. Is there a way that I could have these two threads running simultaneously and still accomplish the goal of tracking mouse movement and popping up a window when movement is detected? (with Tkinter in the main thread if it has to be necessary).
The original code is a bit lengthy. It could be found here if you need more info. (ignore main.py)
I wanted to write a program that moves the mouse cursor and presses the shift key so that the computer won't be locked. The Problem is I want that if the user doesn't set two variables they should be automatically 0 and 1 but it seems I am missing something. The other thing that I can't understand is when I start the program if I don't click on it everything is ok, but if I click on the program's window it says "Not Responding" but the program is running. I can't understand what I am doing wrong here. Below is my code:
#!/usr/bin/python
import pyautogui
import time
import sys
import os
import PySimpleGUI as sg
from datetime import datetime
def main():
sg.theme('DarkAmber')
layout = [ [sg.Text('Please enter the time intervall between the movements:', size = (45,1)), sg.Input(key='-IT-', enable_events=True)],
[sg.Text('Please enter how long should the script run:', size = (45,1)), sg.Input(key='-DURATION-', enable_events=True)],
[sg.Button('Start'), sg.Button('Stop')],
[sg.Output(size=(60,15))] ]
window = sg.Window('Press to start to move!', layout, size=(450,250), element_justification='right')
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Stop'):
break
if event == '-IT-' and values['-IT-'] and values['-IT-'][-1] not in ('0123456789'):
window['-IT-'].update(values['-IT-'][:-1])
if event == '-DURATION-' and values['-DURATION-'] and values['-DURATION-'][-1] not in ('0123456789'):
window['-DURATION-'].update(values['-DURATION-'][:-1])
elif event == 'Start':
if values['-IT-'] == "" and values['-DURATION-'] == "":
window['-IT-'].update(1)
window['-DURATION-'].update(0)
elif values['-IT-'] != "" and values['-DURATION-'] == "":
window['-DURATION-'].update(0)
elif values['-IT-'] == "" and values['-DURATION-'] != "":
window['-IT-'].update(1)
move(numMin=int('0'+values['-IT-']), numDuration=int('0'+values['-DURATION-']))
window.close()
main()
Edit:
I've just cut out the unnecessary part of the code.
So if I'm not misunderstanding, I need to use a thread, the second question is for me unclear can I typecast an empty string without adding zero (0) to it? Because when I try just typecast it doesn't work, and if I add a zero the behavior of the code is changing.
Operations That Take a "Long Time"
If you're a Windows user you've seen windows show in their title bar "Not Responding" which is soon followed by a Windows popup stating that "Your program has stopped responding". Well, you too can make that message and popup appear if you so wish! All you need to do is execute an operation that takes "too long" (i.e. a few seconds) inside your event loop.
You have a couple of options for dealing this with. If your operation can be broken up into smaller parts, then you can call Window.Refresh() occasionally to avoid this message. If you're running a loop for example, drop that call in with your other work. This will keep the GUI happy and Window's won't complain.
If, on the other hand, your operation is not under your control or you are unable to add Refresh calls, then the next option available to you is to move your long operations into a thread.
The "Old Way"
There are a couple of demo programs available for you to see how to do this. You basically put your work into a thread. When the thread is completed, it tells the GUI by sending a message through a queue. The event loop will run with a timer set to a value that represents how "responsive" you want your GUI to be to the work completing.
The "New Way" - Window.write_event_value
This new function that is available currently only in the tkinter port as of July 2020 is exciting and represents the future way multi-threading will be handled in PySimpleGUI (or so is hoped).
Previously, a queue was used where your event loop would poll for incoming messages from a thread.
Now, threads can directly inject events into a Window so that it will show up in the window.read() calls. This allows a your event loop to "pend", waiting for normal window events as well as events being generated by threads.
You can see this new capability in action in this demo: Demo_Multithreaded_Write_Event_Value.py
Here is that program for your inspection and education. It's SO nice to no longer poll for threaded events.
import threading
import time
import PySimpleGUI as sg
"""
Threaded Demo - Uses Window.write_event_value communications
Requires PySimpleGUI.py version 4.25.0 and later
This is a really important demo to understand if you're going to be using multithreading in PySimpleGUI.
Older mechanisms for multi-threading in PySimpleGUI relied on polling of a queue. The management of a communications
queue is now performed internally to PySimpleGUI.
The importance of using the new window.write_event_value call cannot be emphasized enough. It will hav a HUGE impact, in
a positive way, on your code to move to this mechanism as your code will simply "pend" waiting for an event rather than polling.
Copyright 2020 PySimpleGUI.org
"""
THREAD_EVENT = '-THREAD-'
cp = sg.cprint
def the_thread(window):
"""
The thread that communicates with the application through the window's events.
Once a second wakes and sends a new event and associated value to the window
"""
i = 0
while True:
time.sleep(1)
window.write_event_value('-THREAD-', (threading.current_thread().name, i)) # Data sent is a tuple of thread name and counter
cp('This is cheating from the thread', c='white on green')
i += 1
def main():
"""
The demo will display in the multiline info about the event and values dictionary as it is being
returned from window.read()
Every time "Start" is clicked a new thread is started
Try clicking "Dummy" to see that the window is active while the thread stuff is happening in the background
"""
layout = [ [sg.Text('Output Area - cprint\'s route to here', font='Any 15')],
[sg.Multiline(size=(65,20), key='-ML-', autoscroll=True, reroute_stdout=True, write_only=True, reroute_cprint=True)],
[sg.T('Input so you can see data in your dictionary')],
[sg.Input(key='-IN-', size=(30,1))],
[sg.B('Start A Thread'), sg.B('Dummy'), sg.Button('Exit')] ]
window = sg.Window('Window Title', layout, finalize=True)
while True: # Event Loop
event, values = window.read()
cp(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event.startswith('Start'):
threading.Thread(target=the_thread, args=(window,), daemon=True).start()
if event == THREAD_EVENT:
cp(f'Data from the thread ', colors='white on purple', end='')
cp(f'{values[THREAD_EVENT]}', colors='white on red')
window.close()
if __name__ == '__main__':
main()
Multithreaded Programs
While on the topic of multiple threads, another demo was prepared that shows how you can run multiple threads in your program that all communicate with the event loop in order to display something in the GUI window. Recall that for PySimpleGUI (at least the tkinter port) you cannot make PySimpleGUI calls in threads other than the main program thread.
The key to these threaded programs is communication from the threads to your event loop. The mechanism chosen for these demonstrations uses the Python built-in queue module. The event loop polls these queues to see if something has been sent over from one of the threads to be displayed.
You'll find the demo that shows multiple threads communicating with a single GUI is called:
Demo_Multithreaded_Queued.py
Once again a warning is in order for plain PySimpleGUI (tkinter based) - your GUI must never run as anything but the main program thread and no threads can directly call PySimpleGUI calls.
I want to make the function mouse.record() run until a key is pressed, rather than a mouse button.
The mouse.record() is a function from the python module mouse:
(mouse/__init__.py)
def record(button=RIGHT, target_types=(DOWN,)):
"""
Records all mouse events until the user presses the given button. Then returns the list of events recorded. Pairs well with `play(events)`.
Note: this is a blocking function. Note: for more details on the mouse hook and events see `hook`.
"""
recorded = []
hook(recorded.append)
wait(button=button, target_types=target_types)
unhook(recorded.append)
return recorded
I've thought that I could merge the mouse module with the keyboard module to achieve a function that records the mouse movement until a keyboard event. There is a similar keyboard function that could be handy:
(keyboard/__init__.py)
def record(until='escape', suppress=False, trigger_on_release=False):
"""
Records all keyboard events from all keyboards until the user presses the given hotkey. Then returns the list of events recorded, of type `keyboard.KeyboardEvent`. Pairs well with `play(events)`.
Note: this is a blocking function. Note: for more details on the keyboard hook and events see `hook`.
"""
start_recording()
wait(until, suppress=suppress, trigger_on_release=trigger_on_release)
return stop_recording()
So, to sum it up, what I want to achieve is a function that records the mouse movement until a keyboard event using the Python modules mouse and keyboard.
Is this possible?
You can merge both without messing up with the module files:
1) Use mouse.hook() to record events without waiting (like it happens with mouse.record() ). It takes a function and return it that event.
2) Use keyboard.wait(key) to wait for the key to be pressed
3) Use mouse.unhook() to stop recording.
Here is an example code:
import mouse
import keyboard
events = [] #This is the list where all the events will be stored
mouse.hook(events.append) #starting the recording
keyboard.wait("a") #Waiting for 'a' to be pressed
mouse.unhook(events.append) #Stopping the recording
You can also use threading to join keyboard and mouse together. The following code would record the mouse events and keyboard events and then play it back. You can stop monitoring by pressing Escape button.
import threading
import mouse
import keyboard
def monitorMouseKeyboardEvents():
#These are the list where all the events will be stored
mouse_events = []
keyboard_events = []
#Start recording
mouse.hook(mouse_events.append) #starting the mouse recording
# keyboard.hook(lambda _: keyboard_events.append(_))
keyboard.start_recording()
keyboard.wait("esc") #Waiting for 'Esc' button to be pressed
#Stopping recording
mouse.unhook(mouse_events.append)
# keyboard.unhook(keyboard_events.append)
keyboard_events = keyboard.stop_recording()
return mouse_events, keyboard_events
def playMouseMouseKeyboardEvents(mouse_events, keyboard_events):
'''
Playing the recorded events at the same time
'''
k_thread = threading.Thread(target = lambda :keyboard.play(keyboard_events))
k_thread.start()
#Mouse threadings:
m_thread = threading.Thread(target = lambda :mouse.play(mouse_events))
m_thread.start()
#waiting for both threadings to be completed
k_thread.join()
m_thread.join()
if __name__ == "__main__":
mouse_events, keyboard_events=monitorMouseKeyboardEvents()
playMouseMouseKeyboardEvents(mouse_events, keyboard_events)
I have this relatively simple program that listens for mouse clicks and when the mouse button is held down prints "1"s. Unfortunately when I let go of the mouse it just keeps printing "1"s, even though there is an if statement which checks if the mouse is clicked and should stop the loop if it is not. I am using the pynput.mouse module for the mouse interaction.
Here is my code:
import time
from pynput.mouse import Listener
def clicked(x, y, button, pressed):
if pressed == True:
while button == button.left:
print("1")
time.sleep(1)
if pressed == False:
break
with Listener(on_click=clicked) as listener:
listener.join()
My theory is that once the loops starts it stops listening for mouse clicks, so it can never stop the loop. Would it be necessary to create a new thread for the loop? If yes, how would I do that?
Thanks!
Your current logic makes it impossible to get out of the loop, since pressed doesn't change within the loop. There is not a statement that checks if the mouse is clicked: your only if statements check whether the mouse was clicked when you entered the function. pressed doesn't change within the function.
Look at the critical logiic:
if pressed == True:
while ...
...
if pressed == False:
break
There is nothing in here to change the value of pressed; the first if guarantees that it's True anywhere within the loop.
Yes, you need to set up another listener that operates within the loop. You already know the building blocks: create a new one within the function and bind it to another operation that breaks the loop. For instance, you might "cheat" and have it reset pressed as a global variable.
You could also research how to do this in other answers, if you want an overall handler solution. keypress and keyrelease have been done often enough.
import pyautogui, random, time
import pynput
keys = ['w', 's', 'a', 'd']
def on_press(key):
p = True
if key == pynput.keyboard.Key.esc:
return False
else:
while p == True:
press = keys[random.randint(0,3)]
pyautogui.keyDown(press)
pyautogui.keyUp(press)
p = False
with pynput.keyboard.Listener(on_press=on_press) as L:
L.join()
a code like this will work instead of what you did.
BTW, that is just an example; feel free to visit my GitHub page: github.com/ironnicko