i am currently trying to write a short script that will rickroll (open a youtube link) while the user is watching and can't interfere.
I have managed to open insert the link slowly letter by letter and am now trying to block user inputs.
I have tried using the ctypes import to block all inputs, run the script and then unblock again, but it somehow won't block the input. I'm just receiving my RuntimeError message.
How do i fix it, so the inputs get blocked?
Thanks in advance!
Heres the code:
import subprocess
import pyautogui
import time
import ctypes
from ctypes import wintypes
BlockInput = ctypes.windll.user32.BlockInput
BlockInput.argtypes = [wintypes.BOOL]
BlockInput.restype = wintypes.BOOL
blocked = BlockInput(True)
if blocked:
try:
subprocess.Popen(["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",])
time.sleep(3)
pyautogui.write('www.youtube.com/watch?v=DLzxrzFCyOs', interval= 0.5)
pyautogui.hotkey('enter')
finally:
unblocked = BlockInput(False)
else:
raise RuntimeError('Input is already blocked by another thread')
You can use the keyboard module to block all keyboard inputs and the mouse module to constantly move the mouse, preventing the user from moving it.
See these links for more details:
https://github.com/boppreh/keyboard
https://github.com/boppreh/mouse
This blocks all the keys on the keyboard (the 150 is large enough to ensure all keys are blocked).
#### Blocking Keyboard ####
import keyboard
#blocks all keys of keyboard
for i in range(150):
keyboard.block_key(i)
This effectively blocks mouse-movement by constantly moving the mouse to position (1,0).
#### Blocking Mouse-movement ####
import threading
import mouse
import time
global executing
executing = True
def move_mouse():
#until executing is False, move mouse to (1,0)
global executing
while executing:
mouse.move(1,0, absolute=True, duration=0)
def stop_infinite_mouse_control():
#stops infinite control of mouse after 10 seconds if program fails to execute
global executing
time.sleep(10)
executing = False
threading.Thread(target=move_mouse).start()
threading.Thread(target=stop_infinite_mouse_control).start()
#^failsafe^
And then your original code here (the if statement and try/catch block are no longer necessary).
#### opening the video ####
import subprocess
import pyautogui
import time
subprocess.Popen(["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",])
time.sleep(3)
pyautogui.write('www.youtube.com/watch?v=DLzxrzFCyOs', interval = 0.5)
pyautogui.hotkey('enter')
#### stops moving mouse to (1,0) after video has been opened
executing = False
Just a few notes:
The mouse-moving is hard to stop from outside of the program (it's basically impossible to close the program when it is executing, especially as the keyboard is also being blocked), that's why I put in the failsafe, which stops moving the mouse to (1,0) after 10 seconds.
(On Windows) Control-Alt-Delete does allow Task Manager to be opened and then the program can be force-stopped from there.
This doesn't stop the user from clicking the mouse, which can sometimes prevent the YouTube link from being typed in full (i.e. a new tab can be opened)
See a full version of the code here:
https://pastebin.com/WUygDqbG
you could do something like this to block both keyboard and mouse input
from ctypes import windll
from time import sleep
windll.user32.BlockInput(True) #this will block the keyboard input
sleep(15) #input will be blocked for 15 seconds
windll.user32.BlockInput(False) #now the keyboard will be unblocked
Here's a function for blocking keyboard and mouse input. You can pass a number to the blockMouseAndKeys function to adjust the timeout period:
import os
import time
import pyautogui
from threading import Thread
from keyboard import block_key
def blockMouseAndKeys(timeout=5):
global blocking
blockStartTime = time.time()
pyautogui.FAILSAFE = False
blocking = True
try: float(timeout)
except: timeout = 5
def blockKeys(timeout):
global blocking
while blocking:
if timeout:
if time.time()-blockStartTime > timeout:
print(f'Keyboard block timed out after {timeout}s.')
return
for i in range(150):
try: block_key(i)
except: pass
def blockMouse(timeout):
global blocking
while blocking:
def resetMouse(): pyautogui.moveTo(5,5)
Thread(target=resetMouse).start()
if timeout:
if time.time()-blockStartTime > timeout:
print(f'Mouse block timed out after {timeout}s.')
return
def blockTimeout(timeout):
global blocking
time.sleep(timeout)
blocking = False
pyautogui.FAILSAFE = False
print('Done blocking inputs!')
print('Blocking inputs...')
Thread(target=blockKeys, args=[timeout]).start()
Thread(target=blockMouse, args=[timeout]).start()
Thread(target=blockTimeout, args=[timeout]).start()
blockMouseAndKeys(timeout=10)
os.startfile('https://www.youtube.com/watch?v=DLzxrzFCyOs')
Related
I have a GTK application in C that will spawn a Python GTK process to embed a matplotlib figure into a window in the C process using GtkSocket/GtkPlug (uses XEmbed Protocol). The problem I am having is that the import of the matplotlib library takes about 2.5 seconds and during that time the socket widget is simply transparent. I would like to place a Gtk.Spinner in the plug (so the Python side) before the matplotlib import and have the spinner animate asynchronously during the process of importing the matplotlib library. The problem is that in order for the widget to be placed in the plug, and subsequently, for the Gtk.Spinner to animate, it requires iterations of the GTK main loop. I have approached this from a ton of different angles:
(1) Using a thread. The first attempt was trying to run Gtk.main_iteration() via the thread, however, GTK can only be run on the main thread and this does not work. It stalls the program.
(2) Then I tried to use GObject.idle_add from the thread, where the main loop iterations would run from the idle function (apparently the function called via idle is done on the main thread?), but this didn't work either.
(3) Then I tried to import the modules on the thread, while the main thread runs the Gtk.main_iteration()'s to allow the spinner to spin while the imports are taking place. The idea was once the imports are complete, a boolean flag would change to trigger a break from the loop of main iterations. In this case the spinner appears and spins but the plot never shows up. I get an X Server error:
Gdk-WARNING **: master: Fatal IO error 104 (Connection reset by peer) on X server :0.
(4) In lieu of threading, I tried to use GObject.timeout_add to call a function regularly that would perform the Gtk.main_iteration()'s, but doing that results in the original behavior where the socket/plug is transparent until the plot shows up (i.e. no spinner appears nor spins).
I have run out of ideas and now I am coming here hoping for an assist. They key idea is to get the Gtk.Spinner spinning while the Python script is loading the matplotlib library, and once that is done, replace the spinner widget with the figure (while all of this is taking place in a GtkSocket/Plug). I have not created a minimal reproducible example for this since it would be rather complex given the circumstances, but if anyone that is willing to help requests one I could come up with it. However, the relevant code section is below (with previous attempts commented out):
import sys
import gi
import time
import threading
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject
from gi.repository import Pango as pango
if sys.platform != "win32":
GObject.threads_init()
Gdk.threads_init()
# Open socket ID file, read Socket ID into variable, close file
socketFile = open('resources/com/gtkSocket', 'r+')
gtkSock = socketFile.read()
print("The ID of the sockets window in Python is: ", int(gtkSock))
socketFile.close()
# Create plug, create GTK box, add box to plug, add spinner to box
spin_plug = Gtk.Plug.new(int(gtkSock))
socketbox = Gtk.Box()
spin_plug.add(socketbox)
spinner = Gtk.Spinner()
socketbox.pack_start(spinner, expand=True, fill=True, padding=False)
spinner.start()
finished = False
def thread_run():
time.sleep(4)
'''
# Loop for four seconds checking if gtk events are pending, and if so, main loop iterate
t_end = time.time() + 4
while time.time() < t_end:
if (Gtk.events_pending()):
Gtk.main_iteration()
print("Events Pending...")
'''
'''
import argparse
import collections
import csv
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.lines as mlines
from collections import defaultdict
from enum import Enum
from matplotlib.backend_bases import MouseEvent
from matplotlib.pyplot import draw
from matplotlib.widgets import SpanSelector
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FC
from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3
'''
# You cannot run GTK Code on a separate thread from the one running the main loop
# Idle add allows scheduling code to be executed on the main thread
GObject.idle_add(cleanup)
def cleanup():
# Note: Trying to add the Gtk Main Iterations to the idle add function did not work...
print("Closing Spinner Thread...")
spinner.stop()
finished = True
thread.join()
# start a separate thread and immediately return to main loop
#thread = threading.Thread(target=thread_run)
#thread.start()
spin_plug.show_all()
def spin():
busy_wait = 0
while (Gtk.events_pending() or busy_wait < 10):
Gtk.main_iteration()
if (not Gtk.events_pending()):
busy_wait = busy_wait + 1
print("Spin Call complete.")
return True
GObject.timeout_add(50, spin)
'''
# We cannot simply run an infinite Gtk.main() loop, so iterate until the plug has been filled
busy_wait = 0
while (Gtk.events_pending() or busy_wait < 10):
if (finished):
break
print("Busy Wait: %d" % busy_wait)
Gtk.main_iteration()
if (not Gtk.events_pending()):
busy_wait = busy_wait + 1
print("Gtk Main Loop iterations complete.")
'''
Any pointers or ideas would be greatly appreciated.
The solution was performing the imports on a thread while allowing the main thread to do the main loop iterations. Simply doing "import " did not work. Some previous Stack Overflow posts that were useful are here:
Python thread for pre-importing modules
import a module from a thread does not work
Import python modules in the background in REPL
The solution looks like this:
GObject.threads_init()
Gdk.threads_init()
# Open socket ID file, read Socket ID into variable, close file
socketFile = open('resources/com/gtkSocket', 'r+')
gtkSock = socketFile.read()
print("The ID of the sockets window in Python is: ", int(gtkSock))
socketFile.close()
# Create plug, create GTK box, add box to plug, add figure to box
spin_plug = Gtk.Plug.new(int(gtkSock))
socketbox = Gtk.Box()
spin_plug.add(socketbox)
# Create a spinner, pack it, and start it spinning
spinner = Gtk.Spinner()
socketbox.pack_start(spinner, expand=True, fill=True, padding=False)
spinner.start()
spinner.show()
# Flag to break from the Gtk.events_pending() loop
finished = False
# This will load modules on a thread. A simple "import module" does not work
def do_import(module_name):
thismodule = sys.modules[__name__]
module = importlib.import_module(module_name)
setattr(thismodule, module_name, module)
print(module_name, 'imported')
# Use the last module being imported so we know when to break from the Gtk.events_pending()
if (module_name == "matplotlib.pyplot"):
global finished
finished = True
spin_plug.show_all()
modules_to_load = ['argparse', 'collections', 'csv', 'matplotlib', 'matplotlib.pyplot']
# Loop through and create a thread for each module to import from the list
for module_name in modules_to_load:
thread = threading.Thread(target=do_import, args=(module_name,))
thread.start()
# We cannot simply run an infinite Gtk.main() loop, so iterate until the plug has been filled
# Busy wait continues to allow the spinner to spin until the computer loads the modules. Since
# each computer will have a different loading speed, a busy wait of 300 should cover slower
# machines. We can break out of the loop early once the last module is loaded.
busy_wait = 0
while (Gtk.events_pending() or busy_wait < 300):
#print("Busy Wait: %d" % busy_wait)
#print ("finished: %d" % finished)
if (finished):
break
Gtk.main_iteration()
if (not Gtk.events_pending()):
busy_wait = busy_wait + 1
My project is to make a program that you can run while you are playing games or other programs in the background.
When you press a certain key, your notepad should open and also close after you press the same key again.
I have managed to open notepad with subprocess and that works fine but I have no idea to make it open only when a certain key is pressed.
Thanks for any help!
EDIT:
What I tried already:
import subprocess
import keyboard
if keyboard.is_pressed('k'):
subprocess.Popen('C:\\Windows\\System32\\notepad.exe')
input()
here it just doesn't detect any keyboard input, the input() at the end makes the program not close instantly
import subprocess
import keyboard
keyboard.add_hotkey('ctrl+k', print,args=("hello", "test"))
input()
Here if I press "ctrl+k it" will print hello test that means the hotkey works fine. When I switch this part "print,args=("hello", "test")" to "subprocess.Popen('C:\Windows\System32\notepad.exe')"(it should open the program instead of printing hello test) the notepad opens instantly after I run the program and when I press "ctrl+k" I get a big error.
A more complex, but still working example could be the following. With this code your program will be always listening the keyboard, not only when you are focused on the input, so may be mre practical in your case
from pynput import keyboard
import subprocess
import threading
class MyException(Exception): pass
class Listening:
"""Is allways waiting for the keyboard input"""
def __init__(self):
self.notepad_open = False # to know the state
with keyboard.Listener(
on_press=self.on_press) as listener:
try:
listener.join()
except:
pass
def on_press(self, key):
try:
if key.char == "k":
if not self.notepad_open:
self.subprocess = \
subprocess.Popen('C:\\Windows\\System32\\notepad.exe')
self.notepad_open = True # update state
else:
self.subprocess.kill()
self.notepad_open = False # update state
except: # special key was pressed
pass
thread = threading.Thread(target=lambda: Listening())
thread.start()
The problem is that you check for the key 'k' only once at the beginning. If you want the program to correctly work then you should try this:
import time
import subprocess
import keyboard
while True:
if keyboard.is_pressed('k'):
subprocess.Popen('C:\\Windows\\System32\\notepad.exe')
time.sleep(5)
-I used the time so that you can only open the program once 5 seconds(If you're curious, see what happens without it)-
I have a raspberry pi which I have hooked up with a 4 button keypad. Using the signal stuff from blinker I hooked it up to run some methods.
#sender
while True:
if buttonIsDown == True: signal.send()
#reciever
#signal.connect
def sayHI():
print("1")
time.sleep(10)
print("2")
This works fine, however when I push the button for the second time (Within 10 seconds of the previous button press) it does not fire the method as the thread is paused in the time.sleep(10).
How can I get it to fire the method again while the it is still paused(possibly in another thread)
It is an old question, but still it may be useful for someone else.
You can start a new thread every time the signal is emitted, in that way you will be able to catch all the events as soon as they happen. Remember that in your code, since you have a while True, the signal is never connected to the function, you should have defined them in the opposite order.
Here is a working example, based on your code:
import threading
from blinker import signal
from time import sleep
custom_signal = signal(name='custom')
#custom_signal.connect
def slot(sender):
def say_hello():
print("1")
sleep(10)
print("2")
threading.Thread(target=say_hello).start()
while True:
value = int(input('Press 1 to continue: '))
if value == 1:
custom_signal.send()
else:
break
I'm using Selenium Webdriver in my program in order to try and automate something. I am then parsing th resulting page, and checking for a specific element in the page. If the page doesn't have the specific element, then I use sched.scheduler to re-automate the task, by having the user click a button (in the Tkinter GUI). The button runs a function, which schedules a task for sched.scheduler, and has the task be sent to a function in which I created a new process from the multiprocessing module.
This is basically what it is:
import time
import sched
from multiprocessing import Process
#the function needs to run for the first time, then waits for user input if an error shows up
#if it's the second time around, the worker function runs the scheduler
global first_time_happening
first_time_happening = True
terminate = False
scheduler = sched.scheduler(time.time, time.sleep)
def worker():
#insert some working process here using selenium webdriver
print("Worker happened!")
global first_time_happening
if first_time_happening:
first_time_happening = False
elif not first_time_happening:
global relay_to_timer
relay_to_timer = scheduler.enter(5, 2, timer)
scheduler.run()
def process():
p = Process(target=worker)
#p.daemon = True
p.start()
def timer():
if not terminate:
global relay_to_process
relay_to_process = scheduler.enter(5, 2, process)
scheduler.run()
if terminate:
scheduler.cancel(relay_to_process)
scheduler.cancel(relay_to_timer)
def quit_button():
global terminate
terminate = True
if scheduler.empty:
print("The line is empty")
elif not scheduler.empty:
print("Something in the queue!")
while not scheduler.empty:
scheduler.cancel(relay_to_process)
scheduler.cancel(relay_to_timer)
worker()
#simulating where the GUI asks a question, person presses a button, and the button redirects them
#to function worker()
worker()
#simulating a user press the quit button
quit_button()
It keeps running even after I "hit" quit (or call the quit function in this case). I keep getting the queue is empty, but I'm not sure why it isn't working? Any help is appreciated, thanks!!
The scheduler keeps running even with an empty queue just in case somebody (presumably another thread) entered something again. I believe the way to make it end is to raise an exception (whether from the action or delay function) -- .run will propagate it and you can catch it.
To wit...
class AllDoneException(Exception): pass
def worker():
#insert some working process here using selenium webdriver
print("Worker happened!")
global first_time_happening
if first_time_happening:
first_time_happening = False
elif not first_time_happening:
global relay_to_timer
relay_to_timer = scheduler.enter(5, 2, timer)
try:
scheduler.run()
except AllDoneException:
pass
and in function timer
if terminate:
raise AllDoneException
Is there a way of disabling or locking mouse and keyboard using python? I want to freeze the mouse and disable the keyboard.
I haven't tested (actually I've tested the mouse part, and it annoyingly works) but something like this using pyhook would do what you want:
import pythoncom, pyHook
def uMad(event):
return False
hm = pyHook.HookManager()
hm.MouseAll = uMad
hm.KeyAll = uMad
hm.HookMouse()
hm.HookKeyboard()
pythoncom.PumpMessages()
I have extended Fábio Diniz's answer to a class which provides both a block() and an unblock() function which block (selectively) mouse/keyboard inputs. I also added a timeout functionality which (hopefully) addresses the annoyance of locking oneself out.
import pyHook
from threading import Timer
import win32gui
import logging
class blockInput():
def OnKeyboardEvent(self,event):
return False
def OnMouseEvent(self,event):
return False
def unblock(self):
logging.info(" -- Unblock!")
if self.t.is_alive():
self.t.cancel()
try: self.hm.UnhookKeyboard()
except: pass
try: self.hm.UnhookMouse()
except: pass
def block(self, timeout = 10, keyboard = True, mouse = True):
self.t = Timer(timeout, self.unblock)
self.t.start()
logging.info(" -- Block!")
if mouse:
self.hm.MouseAll = self.OnMouseEvent
self.hm.HookMouse()
if keyboard:
self.hm.KeyAll = self.OnKeyboardEvent
self.hm.HookKeyboard()
win32gui.PumpWaitingMessages()
def __init__(self):
self.hm = pyHook.HookManager()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
block = blockInput()
block.block()
import time
t0 = time.time()
while time.time() - t0 < 10:
time.sleep(1)
print(time.time() - t0)
block.unblock()
logging.info("Done.")
You can have a look at the main routine for example usage.
For me, just two lines of programming solved the problem:
from ctypes import *
ok = windll.user32.BlockInput(True) #enable block
#or
ok = windll.user32.BlockInput(False) #disable block
Totally different take since all the solutions mentioned above use a quiet outdated library(pyhook) and this pyhook method personally didnt work for me.
import keyboard
from pynput.mouse import Controller
from time import sleep
def blockinput():
global block_input_flag
block_input_flag = 1
t1 = threading.Thread(target=blockinput_start)
t1.start()
print("[SUCCESS] Input blocked!")
def unblockinput():
blockinput_stop()
print("[SUCCESS] Input unblocked!")
def blockinput_start():
mouse = Controller()
global block_input_flag
for i in range(150):
keyboard.block_key(i)
while block_input_flag == 1:
mouse.position = (0, 0)
def blockinput_stop():
global block_input_flag
for i in range(150):
keyboard.unblock_key(i)
block_input_flag = 0
blockinput()
print("now blocking")
sleep(5)
print("now unblocking")
I just slightly modified the #Robert code and instead of the time I used external interrupt to close the program i.e. if you connect any external drive then the program gets close and your mouse and keyboard will be working perfectly.
import pyHook
from threading import Timer
import win32gui
import logging
import win32file
def locate_usb():#this will check any external Drives
drive_list = []
drivebits = win32file.GetLogicalDrives()
# print(drivebits)
for d in range(1, 26):
mask = 1 << d
if drivebits & mask:
# here if the drive is at least there
drname = '%c:\\' % chr(ord('A') + d)
t = win32file.GetDriveType(drname)
if t == win32file.DRIVE_REMOVABLE:
drive_list.append(drname)
return drive_list
class blockInput():
def OnKeyboardEvent(self,event):
return False
def OnMouseEvent(self,event):
return False
def unblock(self):
try: self.hm.UnhookKeyboard()
except: pass
try: self.hm.UnhookMouse()
except: pass
def block(self ,keyboard = True, mouse = True):
while(1):
if mouse:
self.hm.MouseAll = self.OnMouseEvent
self.hm.HookMouse()
if keyboard:
self.hm.KeyAll = self.OnKeyboardEvent
self.hm.HookKeyboard()
win32gui.PumpWaitingMessages()
cg= locate_usb()
if cg:
break
def __init__(self):
self.hm = pyHook.HookManager()
if __name__ == '__main__':
block = blockInput()
block.block()
block.unblock()
I hope this code will help you
Since 2018, you can now use pynput (v1.4+) to suppress keyboard and mouse events on Windows, Mac, and Linux.
import pynput
# Disable mouse and keyboard events
mouse_listener = pynput.mouse.Listener(suppress=True)
mouse_listener.start()
keyboard_listener = pynput.keyboard.Listener(suppress=True)
keyboard_listener.start()
# Enable mouse and keyboard events
mouse_listener.stop()
keyboard_listener.stop()
This also disables mouse scrolling and clicking.
However, this does not stop users from pressing Ctrl+Alt+Del on Windows. But you can run the script in an elevated command prompt, and the mouse and keyboard will still be disabled, even if they opened Task Manager using Ctrl+Alt+Delete, so there is no harm done (apparently there are way to actually prevent Ctrl+Alt+Delete, but do your own research for that)
You can use pyautogui to do this. Though I recommend adding keyboard for making a stopping key. First, you want to install pyautogui and keyboard.
Please note: this only disables the mouse not the keyboard, that is a very bad idea.
pip install pyautogui
pip install keyboard
Ok, with that sorted, we have to actually make the disabler.
import pyautogui
import keyboard
stopKey = "s" #The stopKey is the button to press to stop. you can also do a shortcut like ctrl+s
maxX, maxY = pyautogui.size() #get max size of screen
While True:
if keyboard.is_pressed(stopKey):
break
else:
pyautogui.moveTo(maxX/2, maxY/2) #move the mouse to the center of the screen
Ok, but there is 2 ways to get out of this. pressing S, and also quickly moving the mouse to one of the corners of the screen (that is a pyautogui failsafe, but we can disable that). If you want to disable the failsafe, add this after the imports:
pyautogui.FAILSAFE = False
Please note that disabling the failsafe is NOT recommended!
Ok, so now the only way to exit is the S key. If you want to stop this somewhere else in your program, do this:
pyautogui.press(stopKey)
Ok, so its not perfect, but it will stop you from doing basically anything with your mouse.