Tkinter object sensitivity to mouse-pointer - python

I am making a program with python using tkinter, but I have a problem. I need to add a code which makes my tkinter object sense when the mouse-pointer is touching it.
Does anyone have recommendations for what I should do? So far, I've been thinking that I could write code which runs like this: If the coordinates of the mouse-pointer are the same as the coordinates of the object, the object would react to that instead.

You can bind to the <Enter> and <Leave> events. They will fire whenever the mouse pointer enters and leaves a widget.
Here's a small demo:
import tkinter as tk
def on_mouse_enter(event):
print("enter...", event.widget)
def on_mouse_leave(event):
print("leave...", event.widget)
root = tk.Tk()
for i in range(10):
label = tk.Label(root, text="Item #{}".format(i), name='label-{}'.format(i))
label.pack()
label.bind("<Enter>", on_mouse_enter)
label.bind("<Leave>", on_mouse_leave)
tk.mainloop()

Related

Tkinter widgets only drawn if I press alt button

I have a small app on Linux in which I sub-class the Frame class from tkinter and sometime the widgets are only drawn after I press the alt button. Everything works fine, though this is the first time I use this GUI and I may miss something.
Follows a short example of my code:
from tkinter import *
class MyFrame(Frame):
def one_method(self):
root_frame.geometry("200x400")
button = Button(self, command=self.clean_and_draw_more_widget, text="Hello")
button.pack(side=TOP) #I also use grid()
the main driver code:
root_frame = Tk()
root_frame.title("Window")
frame = MyFrame(root_frame)
frame.pack()
root_frame.mainloop()
As you can see nothing compless, but still I have this problem.
EDIT
I use grid() and pack() but each one in different methods
Thanks for helping

Python tkinter placing a button in a grid inside a frame freezes on execution

I have created two frames using Tkinter. In one of the frames I am trying to add a button using a grid. When I run the program, there is no output. Instead it just freezes and I have to kill the process.
Here is the code:
from Tkinter import *
window=Tk()
window.title("calculator")
window.geometry("500x500")
window.resizable(0,0)
input_field=StringVar()
display_frame=Frame(window).pack(side="top")
button_frame=Frame(window).pack(side="bottom")
text=Entry(display_frame,font=('arial',20,'bold'),textvariable=input_field,justify="right").pack(fill="x",ipady=10)
clear_button=Button(button_frame,text="C").grid(row=0)
window.mainloop()
However, if I change the clear_button variable as
clear_button=Button(button_frame,text="C").pack()
I get an output. What am I missing here?
You cannot mix grid and pack inside the same container (a Frame/Window).
That said you should realize that your display_frame and button_frame variables are actually None! Why, because Frame(Window) will return a Frame object, but you have applied the pack() function just after that whose return value is None.
So basically the Entry and the Button widgets that you created have master=None and that means they are not inside the Frames that you defined, but actually part of the main Window.
Now you can easily see why clear_button=Button(button_frame,text="C").pack() was working as now the main window has only one geometry manager namely pack.
Here is the working code.
from tkinter import * # "Tkinter" on python 2
window=Tk()
window.title("calculator")
window.geometry("500x500")
window.resizable(0,0)
input_field=StringVar()
display_frame=Frame(window)
display_frame.pack(side="top")
button_frame=Frame(window)
button_frame.pack(side="bottom")
Entry(display_frame,font=('arial',20,'bold'),textvariable=input_field,justify="right").pack(fill="x",ipady=10)
Button(button_frame, text="C").grid(row=0)
window.mainloop()
You cannot use both grid and pack method on widgets having same master.
Here, follow the below thread for a detailed understanding :
python pack() and grid() methods together

Tkinter Focus lost after askstring

I am currently implementing a program that uses many tkinter frames and while subframe is being opened I want the superframe to be locked for the user (otherwise things will not work out). After some research I found the grab_set and grab_release method which worked quite fine.
However once the subframe (instanciated by Toplevel) calls the askstring the grab is "losed" and the user can interact with the superlevel window again. An example would be this (very simplified code):
import tkinter as tk
import tkinter.simpledialog
root = tk.Tk()
def open_sublevel():
tl = tk.Toplevel(root)
def ask():
print(tk.simpledialog.askstring("askstring", "askstring"))
tk.Button(tl, text="ask", command=ask).pack()
tl.grab_set()
root.wait_window(tl)
tl.grab_release()
print("release")
tk.Button(root, text="asdf", command=open_sublevel).pack()
tk.mainloop()
Once the user opens the subframe by clicking "asdf" the frame containing "asdf" will be locked for the duration while the subframe is opened. However once the user selects the "ask"-Button in the subframe this "lock" somehow disappears.
According to the notes in the tkinter library:
A grab directs all events to this and descendant widgets in the application.
I am not able so far to find any documentation that would explain why the grab_set() is falling off after you finish submitting your askstring but I would imaging it is because once the widget is gone the grab_set() falls off. Just like if you were to close out the Toplevel window.
In this case tl.grab_release() does not appear to be needed as grab releases once the window closes.
From what I have tested if you reset the grab_set() after the askstring is done then it will still work properly.
You need to simply add tl.grab_set() just below print(tk.simpledialog.askstring("askstring", "askstring")).
Modified code below:
import tkinter as tk
import tkinter.simpledialog
root = tk.Tk()
def open_sublevel():
tl = tk.Toplevel(root)
tl.grab_set()
def ask():
print(tk.simpledialog.askstring("askstring", "askstring"))
tl.grab_set()
tk.Button(tl, text="ask", command=ask).pack()
print("release")
tk.Button(root, text="asdf", command=open_sublevel).pack()
tk.mainloop()
setting the parent for simpledialog will make simpledialog take focus
x = simpledialog(parent = window_x, title = z etc.)
this will make sure x takes focus and not withdraw

Combining Tkinter mainloop with another event listener

I am trying to build a program that listens for certain key combinations and then shows information to the user in a Tkinter window. To do this, I'm using a keylogger like so (simplified for this example):
from pyHook import HookManager
from pythoncom import PumpMessages
import Tkinter as tk
def on_keyboard_event(event):
label.config(text=event.Key)
root.update()
return True
hm = HookManager()
hm.KeyDown = on_keyboard_event
hm.HookKeyboard()
root = tk.Tk()
label = tk.Label(root, text='Hello world')
label.pack()
PumpMessages()
As expected, the window pops up and shows the user what key they pressed. However, I would like to integrate functionality to show other messages by interacting with the Tkinter window, such as by pressing a button. However, it seems I need Tkinter's mainloop to do this, which I can't figure out how to run alongside PumpMessages(), since it also halts the code similar to mainloop().
I tried running root.mainloop() in a root.after(), and I tried recreating root.mainloop like so:
def mainloop():
root.update()
root.after(50, mainloop)
and then running it right before PumpMessages, but neither of these solutions worked. It also doesn't seem like you can run PumpMessages or root.mainloop in a thread, though I could just not be doing it right. If this is not possible with Tkinter, is there an alternate Python GUI I could use that would make it possible?
You don't need to create a function to use mainloop() so just simply place the mainloop() at the bottom of your code. If you want a delay on it, use root.after(milliseconds, function)
Also, remember to put mainloop() before PumpMessages()
e.g.
def mainloopfunction():
mainloop()
root.after(5000, mainloopfunction)
Hope I could help!

My python code is not working

This simple code is not working.
I mean its running without errors but
is not showing any gui window for text entry.
from Tkinter import *
from tkMessageBox import *
root=Tk()
Label(root,text="first").grid(row=0)
Label(root,text="second").grid(row=2)
e1=Entry(root)
e1.grid(row=0,column=2)
e2=Entry(root)
e2.grid(row=2,column=3)
def info():
s=showinfo(title="wish",message=e1.get()+''+"welcome to python")
Button(root,text="ok",command=info).pack()
root.mainloop()
You can't use grid and pack on two widgets owned by the same parent. This causes the geometry manager to loop forever.
Change your Button positioning to:
Button(root,text="ok",command=info).grid(row=3, column=0)
(or whatever row/column you want it to be in).
Result:

Categories