Tkinter - On which widget is mouse pointer currently? - python

I want to ask u guys if there is a way of getting name or some id of widget which is mouse pointer currently on. Is there a way of doing this? Thanks for any response.

Normally you get this information from a binding. However, if you want to poll the system at any point to find out which widget is under the mouse, you can use winfo_pointerxy to get the coordinates of the mouse, and then pass those to winfo_containing to get the widget under those coordinates.
Here's an example program that continuously prints out the widget under the mouse:
import tkinter as tk
def print_widget_under_mouse(root):
x,y = root.winfo_pointerxy()
widget = root.winfo_containing(x,y)
print("widget:", widget)
root.after(1000, print_widget_under_mouse, root)
root = tk.Tk()
label_foo = tk.Label(root, text="Foo", name="label_foo")
label_bar = tk.Label(root, text="Bar", name="label_bar")
button = tk.Button(root, text="Button", name="button")
button.pack(side="bottom")
label_foo.pack(fill="both", expand=True)
label_bar.pack(fill="both", expand=True)
print_widget_under_mouse(root)
root.mainloop()

Related

How to update values on tkinter window?

I am trying to make a window that would show the location of the mouse at all times by using pyautogui and tkinter. I am new to tkinter and python overall so I am not quite sure how to make it so that the values would keep updating in the window, if it is even possible. Here is my code so far:
from tkinter import *
import pyautogui as pag
window = Tk()
window.geometry("200x200")
window.title("window")
window.config(background="#4ceefc")
coordinates = pag.position()
label1 = Label(window, text="mouse coordinates:")
label1.place(x=20, y=50)
label2 = Label(window, text=coordinates)
label2.place(x=30, y=90)
window.mainloop()
I tried using a while loop on the labels and window.mainloop() function but this did not work
Create a StringVar() to store the coords, and then assign it to the label's textvariable. You can then bind a '<Motion>' handler to your root window to update the label whenever the mouse moves.
coord_var = StringVar(window)
def on_mousemove(event):
coord_var.set(f'Mouse coordinates: {event.x}, {event.y}')
label1 = Label(window, textvariable=coord_var)
label1.place(x=20, y=50)
window.bind('<Motion>', on_mousemove)
Unless you're using pyautogui for something else, you can get away without it for this.

Tkinter Enter and Motion bindings

Is it possible to track what widgets I enter with my mouse into while it's pressed?
I want to create a chain-like effect that the background of the label\button change while click and drag the mouse and moving from widget to widget.
Thanks :)
You can bind to the <B1-Motion> event, and then use winfo_containing to get the widget under the cursor.
Here's a simple example:
import tkinter as tk
root = tk.Tk()
current_label = tk.Label(root, text="", anchor="w", width=100)
current_label.pack(side="top", fill="x")
def show_widget(event):
widget = event.widget.winfo_containing(event.x_root, event.y_root)
current_label.configure(text=f"widget: {str(widget)}")
for x in range(10):
name = f"Label #{x+1}"
label = tk.Label(root, text=name)
label.pack(padx=10, pady=10)
label.bind("<B1-Motion>", show_widget)
root.mainloop()
I've done stuff like tracking entry/exit for a specific widget:
widget.bind("<Enter>", enter_func)
widget.bind("<Leave>", exit_func)
you may be able to do something cute with that

How to trigger tkinter's "<Enter>" event with mouse down?

In tkinter with Python 3.7, the default behavior for an event binding is that an "<Enter>" event will not be triggered after the mouse has been clicked down before it has been released. I was intending to implement a scrollable table such that it detects "<Button-1>" (Mouse left-click down) and "<ButtonRelease-1>" (Mouse left-click up) events as well as having each table-row's widgets "<Enter>" event bound to detect when the mouse pointer enters a different table row. In this way I could scroll my table by clicking an row and dragging through a table. My assumption was that "<Enter>" events would be triggered even while the mouse button is held down, which was incorrect. So, my entire scrolling implementation hit a brick wall. I need these events to be triggered while the mouse is down or it just won't work. I'm doing something like:
from tkinter import *
class App:
def __init__(self):
self.root = Tk()
# The name kwarg is used to infer the index of the row in the event handlers.
self.labels = [Label(text=f"Label #{i}", name=f"row-{i}") for i in range(5)]
for row, label in enumerate(self.labels):
label.bind("<Button-1>", self.mouse_down)
label.bind("<ButtonRelease-1>", self.mouse_up)
label.bind("<Enter>", self.mouse_enter)
label.grid(row=row, column=0)
mainloop()
def mouse_up(self, event):
idx = self.index_from_event(event)
# Do some with the row with the passed index
def mouse_down(self, event):
idx = self.index_from_event(event)
# Do some with the row with the passed index
def mouse_enter(self, event):
# I would like for this to be triggered even when the mouse is pressed down.
# However, by default tkinter doesn't allow this.
pass
def index_from_event(self, event):
# Get the index of the row from the labels name string.
return str(event.widget).split('-')[-1]
Any way to enable mouse enter events while the mouse button 1 is held down in tkinter? All the docs on effbot (http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm) say about the enter event is:
<Enter>
The mouse pointer entered the widget (this event doesn’t mean that the user pressed the Enter key!).
No, there is no direct way to bind to the enter and leave events when the button is down, except for the widget which first gets the click. It is fairly easy to add that ability, however,
You can bind <B1-Motion> to all widgets which will call a function whenever the mouse moves. You can then use the winfo_containing method to figure out which widget is under the cursor. With that information you could generate a virtual event which you can bind to (or you can skip the virtual events and add your code in the handler for the motion event).
Here's a bit of a contrived example. When you click and move the mouse, show_info will be called. It keeps track of the current widget and compares it to the widget under the cursor. If it's different, it generates a <<B1-Leave>> to the previous widget and a <<B1-Enter>> on the new widget. Those bindings will display "Hello, cursor!" when the cursor is over the label.
import tkinter as tk
current_widget = None
def show_info(event):
global current_widget
widget = event.widget.winfo_containing(event.x_root, event.y_root)
if current_widget != widget:
if current_widget:
current_widget.event_generate("<<B1-Leave>>")
current_widget = widget
current_widget.event_generate("<<B1-Enter>>")
def on_enter(event):
event.widget.configure(text="Hello, cursor!")
def on_leave(event):
event.widget.configure(text="")
root = tk.Tk()
label = tk.Label(root, bd=1, relief="raised")
l1 = tk.Label(root, text="", width=20, bd=1, relief="raised")
l2 = tk.Label(root, text="", width=20, bd=1, relief="raised")
label.pack(side="top", fill="x")
l1.pack(fill="both", expand=True, padx=20, pady=20)
l2.pack(fill="both", expand=True, padx=20, pady=20)
root.bind_all("<B1-Motion>", show_info)
l1.bind("<<B1-Enter>>", on_enter)
l1.bind("<<B1-Leave>>", on_leave)
l2.bind("<<B1-Enter>>", on_enter)
l2.bind("<<B1-Leave>>", on_leave)
tk.mainloop()

How to display canvas coordinates when hovering cursor over canvas?

When I hover over the canvas I want some labels on top of the canvas to display x,y coordinates which stay the same if I keep my cursor still but change when I move it. How would I do this?
You can use a callback method and bind it to a Motion event.
import tkinter
root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.pack()
def moved(event):
canvas.itemconfigure(tag, text="(%r, %r)" % (event.x, event.y))
canvas.bind("<Motion>", moved)
tag = canvas.create_text(10, 10, text="", anchor="nw")
root.mainloop()
Also use <Enter> event. So when you switch between windows (<Alt>+<Tab> hotkey), your cursor will show the correct coordinates.
For example, you put your cursor on the canvas and <Motion> event will track it, but when you press <Alt>+<Tab> and switch to another window, then move your cursor a bit and <Alt>+<Tab> on your canvas again -- coordinates of the your cursor will be wrong, because <Motion> event doesn't track switches between windows. To fix it use <Enter> event.
import tkinter
root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.pack()
def get_coordinates(event):
canvas.itemconfigure(tag, text='({x}, {y})'.format(x=event.x, y=event.y))
canvas.bind('<Motion>', get_coordinates)
canvas.bind('<Enter>', get_coordinates) # handle <Alt>+<Tab> switches between windows
tag = canvas.create_text(10, 10, text='', anchor='nw')
root.mainloop()

Tkinter Top Level window Sizing

I have been looking all over the web trying to find an a way to size a TopLevel() window in tkinter. I read the documentation and have been looking at effbot.org's tkinterbook. I see the options but I just can't seem to get them to work.
def test():
top = Toplevel()
top.title("test")
msg = Message(top, text="test")
msg.pack(pady=10, padx=10)
def test1():
top = Toplevel()
top.title("test1")
msg = Message(top, text="test1")
msg.pack(pady=10, padx=10)
I know that there are height and width options and there is also root.geometry. However I just cant seem to get anything with TopLevel to work...
Also, is the best way to create a new window through a definition and toplevel? All I am wanting is a pop-up window with some formatted text in it.
The following creates a root window then sets its size to 400x400, then creates a Toplevel widget and sets its size to 500x100:
>>> import tkinter as tk
>>> root = tk.Tk()
>>> root.geometry('400x400')
''
>>> top = tk.Toplevel()
>>> top.geometry('500x100')
''
Another option is to use a container widget, like a Canvas or Frame, of your desired size into the window you'd like to work with. Then you can place other objects into this container:
>>> top2 = tk.Toplevel()
>>> frame = tk.Frame(top2, width=100, height=100)
>>> frame.pack()
When you pack() the frame, top2 is automatically adjusted to fit frame's size.
Using geometry() to set a size of AxB results in the same size window as placing an AxB widget into a dynamically-sized window - that is, if you put in a widget (or use geometry() to set the window size to) the width of your screen, the window's borders will be just outside the visible area of your screen.

Categories

Resources