How can I set the mouse position in a tkinter window
Solution 1:
The good news is that there is a way to do it.
The intermediate news is that it's not well documented.
The bad news is that it only works on some platforms.
The other intermediate news is that you can step outside of Tk on at least some platforms.
The way to do this in Tcl/Tk is by generating a <Motion>
event with -warp 1
. The documentation on this is sparse, and scattered around a few different pages (start at bind
), but the details are described here. Basically, it's just this:
event generate . <Motion> -warp 1 -x 50 -y 50
So, how do you do this from Tkinter?
Well, event_generate
isn't documented anywhere, and neither is the <Motion>
event, or the warp
parameter… but it's pretty simple to figure out if you know how Tk maps to Tkinter:
window.event_generate('<Motion>', warp=True, x=50, y=50)
And this does indeed generate an event, as you can see by binding <Motion>
. Here's a simple test program:
from tkinter import *
root = Tk()
def key(event):
root.event_generate('<Motion>', warp=True, x=50, y=50)
def motion(event):
print('motion {}, {}'.format(event.x, event.y))
root.bind('<Key>', key)
root.bind('<Motion>', motion)
root.mainloop()
Run it, click the window to make sure it has focus, move the cursor around, and you'll see it print out something like this:
motion 65, 69
motion 65, 70
motion 65, 71
Then hit a key, and it'll print out this:
motion 50, 50
Which is great… except that it may not actually be able to move your cursor, in which case all this does is trick Tk into thinking the cursor moved.
From skimming various forums, it looks like:
- Mac: Does not work.
- You must have Tk 8.6.something or later (see issue 2926819). And you probably have 8.5.something.
- But it's not hard to go right to the Cocoa API.
- Windows: Usually works.
- You must have Tk 8.4.something or later. I couldn't find the bug for this, but you can count on 8.4 with any official Windows binary install of Python 2.7 or 3.x+.
- You also must not be running a full-screen app (which you generally aren't, with Tk).
- On Vista and later, in some cases it won't work. This may have something to do with not owning the desktop session or not being a local console session, or it may have to do with needing Administrator or other privileges.
- If it doesn't work, it's easy to go right to the Win32 API.
- X11 (most linux, *BSD, etc.): Usually
- Your window manager must not have disabled other clients from warping the pointer. Fortunately, that doesn't seem to be a common thing to do.
- If you have this problem, there's no way around it.
- Other platforms (iOS, Android, etc.): No idea.
For Mac, you want to generate and send an NSMouseMoved
event. The easy way to do this is with pyobjc
(which is built in if you're using Apple's Python; otherwise you have to install it):
app = Foundation.NSApplication.sharedApplication()
event = Foundation.NSEvent.mouseEventWithType_location_modifierFlags_timestamp_windowNumber_context_eventNumber_clickCount_pressure_(
Foundation.NSMouseMoved, (50, 50), 0, 0,
app.mainWindow().windowNumber(), None, 0, 0, 0.0)
app.sendEvent_(event)
For Windows, you want to call the SetCursorPos
API, or generate and send a MOUSEEVENT. The former will not work with, e.g., DirectX games; the latter may not work with remote desktops. For this case, you probably want the former. Either way, the easiest way to do this is to install pywin32
, and then it's just:
win32api.SetCursorPos((50, 50))
Solution 2:
For anyone interested in moving the cursor to an absolute position on the screen (using @abarnert's tkinter method):
# Moves the mouse to an absolute location on the screen
def move_mouse_to(x, y):
# Create a new temporary root
temp_root = tk.Tk()
# Move it to +0+0 and remove the title bar
temp_root.overrideredirect(True)
# Make sure the window appears on the screen and handles the `overrideredirect`
temp_root.update()
# Generate the event as @abarnert did
temp_root.event_generate("<Motion>", warp=True, x=x, y=y)
# Make sure that tcl handles the event
temp_root.update()
# Destroy the root
temp_root.destroy()
This function shouldn't interfere with other tkinter windows. Please note that this function creates a new window that is refreshed twice so it might be slow and the user might notice the window created.