Is it possible to get the popup option (pop function in the code) on hovering the cursor on the button?
import tkinter as tk
from tkinter import Tk, BOTH, Menu
def pop(bt):
try:
x = bt.winfo_rootx()+238
y = bt.winfo_rooty()+10
popup.tk_popup(x, y, 0)
finally:
popup.grab_release()
root = tk.Tk()
popup = Menu(root, tearoff=0,relief='raised')
popup.add_command(label="About")
popup.add_command(label="User manual")
popup.add_command(label="Contact us")
button1 =tk.Button(root, text="HELP",height=3,width=26,command=lambda: controller.show_frame(HelpPage))
button1.configure(command = lambda: pop(button1))
button1.place(x=0,y=0
)
root.mainloop()
.
button1.bind('<Enter>',pop(button1)) #gives the following output without the mouse cursor over that button.
Try this:
import tkinter as tk
window = None
def leave_window(event):
global window
if 0 < root.winfo_pointerx() - root.winfo_rootx() < button.winfo_width():
if 0 < root.winfo_pointery() - root.winfo_rooty() < button.winfo_height():
# Mouse still over button
return None
if window is not None:
window.destroy()
window = None
def create_window(event):
global window
if window is not None:
# The window is already open
return None
window = tk.Toplevel(root)
window.overrideredirect(True)
label = tk.Label(window, text="Text", bg="black", fg="white")
label.pack()
window.update()
# Move the window to the cursor's
x = root.winfo_pointerx()
y = root.winfo_pointery()-window.winfo_height()
window.geometry("+%i+%i" % (x, y))
window.bind("<Leave>", leave_window)
root = tk.Tk()
button = tk.Button(root, text="-------- Hover the mouse here --------")
button.pack(fill="both", expand=True)
button.bind("<Enter>", create_window)
button.bind("<Leave>", leave_window)
root.mainloop()
I binded to <Enter> and <Leave> to check if the mouse is over the button. If it is, it creates a window with a label called text. You can change it to it showing the menu.
For more answers look here.
Please bind your Button with mouse enter and call pop function.
Check out this code :
button1.bind('<Enter>', pop)
Control your popup location as well as destroy popup on '' case again bind it and call custom function that will destroy popup.
You need <Enter> and <Leave> bind sequences to map and unmap the Menu. Tkinter Menu has two methods post and unpost where post shows the menu at given coordinates, and unpost hides it away. Unfortunately, I couldn't test it as the unpost functionality doesn't work on macOS or Linux [refer to this link for the same]. I also changed the x, y coords to map the Menu in the center of the widget (Button), it can be changed if required.
Here is the complete sample code.
import tkinter as tk
from tkinter import Tk, BOTH, Menu
def pop(evt):
but = evt.widget
if str(evt.type) == "Enter":
# Map the menu in the center of the width.
x = but.winfo_rootx() + int(but.winfo_width()/2)
y = but.winfo_rooty() + int(but.winfo_height()/2)
popup.tk_popup(x, y)
elif str(evt.type) == "Leave":
popup.unpost()
root = tk.Tk()
root.geometry("300x300")
popup = Menu(root, tearoff=0, relief='raised')
popup.add_command(label="About")
popup.add_command(label="User manual")
popup.add_command(label="Contact us")
button1 = tk.Button(root, text="HELP", height=3, width=26)
button1.bind('<Enter>', pop)
button1.bind('<Leave>', pop)
button1.pack(pady=100)
root.mainloop()
Like said, unpost doesn't work on macOS or Linux, so I couldn't test the sample code 100% but it should work fine.
Related
Does anyone know an event for saying "when the element is grabbed" by grab I mean not just pressed or clicked but when we try to move an element from one position to another (it's more like drag the element)
For example I want to do two things at the same time on a widget (a Label) when I click on it, it display other labels (2 others) and when I grab it => I can move it
The initial line to lunch an event when the mouse is clicked is :
widget.bind('<Button-1>', function)
How I can click and drag a widget at the same time ??? There's a event to do that ?
The full code you don't necessarily have to read it :
import tkinter as tk
from tkinter import *
from tkinter import ttk
root = Tk()
root.geometry('800x600')
root.resizable(False,False)
frame = tk.Frame(root)
frame.pack(expand=1,fill=BOTH)
label = tk.Label(frame,text='˃LABEL',fg='green')
label.grid(column=0,row=0)
label2 = Label(frame,text='˃LABEL2',fg='green')
label3 = Label(frame,text='˃LABEL3',fg='green')
def change(event):
label['text'] = '˅LABEL'
label2.grid(column=0,row=1)
label3.grid(column=0,row=2)
widget = event.widget
widget._drag_start_x = event.x
widget._drag_start_y = event.y
if label['text'] == '˅LABEL':
label.bind('<Button-1>', hide)
def hide(event):
label['text'] = '˃LABEL'
label2.grid_forget()
label3.grid_forget()
if label['text'] == '˃LABEL':
label.bind('<Button-1>',change)
label.bind('<Button-1>',change)
def make_draggable(widget):
widget.bind("<Button-1>", change)
widget.bind("<B1-Motion>", on_drag_motion)
#def on_drag_start(event):
def on_drag_motion(event):
widget = event.widget
x = widget.winfo_x() - widget._drag_start_x + event.x
y = widget.winfo_y() - widget._drag_start_y + event.y
widget.place(x=x, y=y)
make_draggable(label)
root.mainloop()
Good days all, I am new here, and I have stuck the problem a few days a problem currently with Tkinter, I have done some research about how to close Tkinter window when the mouse has clicked away from it but there are not much information to do so.
So, my problem is how to close the Tkinter window when the mouse clicked outside the Tkinter? I have tried the method of FocusOut to my Tkinter. However, I have tried to bind with root, it will close the window even though I clicked inside the frame widget. Then, I bind with the frame, the Tkinter will close when I clicked outside the Tkinter. Therefore, I have proved that the idea to close the Tkinter is works so far.
Then a new problem has happened, when I clicked the Combobox widget in the window, the window will close also. Is there any better solution to prove this concept?
Here is the code to indicate my problem.
import tkinter as tk
from tkinter import StringVar, ttk,messagebox
root = tk.Tk()
root.title("Sample Window")
root.minsize(300,350)
info_frame = tk.LabelFrame(root, text = "Information")
info_frame.pack(padx = 5, pady = 5 , fill = "both",expand=True)
tabControl = ttk.Notebook(info_frame)
person1tab = ttk.Frame(tabControl)
tabControl.add(person1tab,text = "Person1")
tabControl.pack(expand=1,fill="both")
person2tab = ttk.Frame(tabControl)
tabControl.add(person2tab,text = "Person2")
tabControl.pack(expand=1,fill="both")
fname_var = tk.StringVar()
lname_var = tk.StringVar()
gender_var = tk.StringVar()
age_var = tk.IntVar()
fname_label = tk.Label(person1tab, text = "First name:").pack(padx=5,pady=3)
fname_entry = tk.Entry(person1tab, textvariable=fname_var).pack(padx=5,pady=3)
lname_label = tk.Label(person1tab, text = "Last name:").pack(padx=5,pady=3)
lname_entry = tk.Entry(person1tab, textvariable=lname_var).pack(padx=5,pady=3)
gender_label = tk.Label(person1tab, text = "Gender:").pack(padx=5,pady=3)
gender_combo = ttk.Combobox(person1tab, textvariable=gender_var,state='readonly')
gender_combo['values'] = ('Male','Female')
gender_combo.current(0)
gender_combo.pack(padx=5,pady=3)
age_label = tk.Label(person1tab, text = "Age:").pack(padx=5,pady=3)
age_label = tk.Entry(person1tab, textvariable=age_var).pack(padx=5,pady=3)
page2label = tk.Label(person2tab,text = "This is tab 2.").pack(padx=5,pady=3)
def lossfocus(event):
root.quit()
pass
tabControl.bind('<FocusOut>', lossfocus)
root.mainloop()
You can still bind <FocusOut> on root window, but you need to check:
whether the widget that trigger this event is root window
no other widget in this root window getting the focus:
def lossfocus(event):
if event.widget is root:
# check which widget getting the focus
w = root.tk.call('focus')
if not w:
# not widget in this window
root.destroy()
I am trying to stop the main window from running until a button has been pressed on a separate Toplevel window.
Example:
from tkinter import *
let_user_through = False
window = Tk()
def activate_main_window():
global let_user_through
let_user_through = True
frame = Toplevel()
b = Button(frame, text="Enter", command=activate_main_window).pack()
if let_user_through == True:
lbl = Label(window, text="Hello")
#bunch of code
#bunch of code
window.mainloop()
In this example, in the main window there is a label that reads: "Hello".
But I don't want people to be able to see it if they haven't pressed the button on the frame
Once the user has pressed the button, the frame will destroy itself and the main window will continue executing a bunch of code.
I'm a beginner to tkinter so i'm not sure if the answer is obvious or not. Thanks!
You can use frame.wait_window() to wait until frame is destroyed. Also you need to call frame.destroy() inside activate_main_window().
from tkinter import *
let_user_through = False
window = Tk()
def activate_main_window():
global let_user_through
let_user_through = True
frame.destroy() # need to destroy frame
# wait for root window becomes visible
# otherwise "frame" may be open behind root window
window.wait_visibility()
frame = Toplevel()
Button(frame, text="Enter", command=activate_main_window).pack()
frame.grab_set() # capture keyboard/mouse events
frame.wait_window() # wait for "frame" to be destroyed
if let_user_through:
Label(window, text="Hello").pack()
#bunch of code
#bunch of code
# should it be within the above for loop?
window.mainloop()
A small change to your code using window.withdraw and window.deiconify works for me. #acw1668 correctly pointed out an error in my original code, so here is the fix.
Your main window is invisible until user presses button.
import tkinter as tk
let_user_through = False
window = tk.Tk()
window.withdraw()
def activate_main_window():
global let_user_through
let_user_through = True
frame.destroy() # need to destroy frame
frame = tk.Toplevel()
tk.Button(frame, text="Enter", command=activate_main_window).pack()
frame.wait_window() # wait for "frame" to be destroyed
if let_user_through:
tk.Label(window, text="Hello").pack()
window.update()
window.deiconify()
#bunch of code
#bunch of code
window.mainloop()
I've created a class that removes the need for let_user_through and sets up code for any next steps.
import tkinter as tk
class invisible:
def __init__( self ):
self.window = tk.Tk()
self.window.withdraw() # make window invisible
self.frame = tk.Toplevel()
tk.Button(
self.frame, text = "Enter", command = self.activate_main_window ).pack( fill='both' )
self.frame.wait_window( ) # wait for "frame"
self.button = tk.Button( self.window, text = "Hello", command = self.remove_next )
self.button.pack( fill = 'both')
self.window.update()
self.window.deiconify() # make window visible
def activate_main_window( self ):
self.frame.destroy() # need to destroy frame
def remove_next( self ):
self.button.destroy()
tk.Label( self.window, text = "Bunch of codeA" ).pack( fill = 'both' )
tk.Label( self.window, text = "Bunch of codeB" ).pack( fill = 'both' )
tk.Label( self.window, text = "Bunch of codeC" ).pack( fill = 'both' )
# continue code initialization
if __name__ == '__main__':
make = invisible()
tk.mainloop()
I was writing a GUI library in Python based on tkinter and I was designing and building all the widgets, but I have come to the PopUp menus.
Due that tkinter picks system menus and this can't be customized, I write the following code to make a frame where I can put my customized buttons in and works as a popup.
from tkinter import *
root = Tk()
w = Label(root, text="Right-click to display menu", width=40, height=20)
w.place(x=0)
def function1():
print('function1 activated')
# create a menu
f = Frame(root,width=80,height=60,background='green')
b2 = Button(f,text='function',command=function1)
b2.pack()
def open_popup(event):
try:
f.place(x=event.x, y=event.y)
root.after(1)
f.focus_set()
w.bind_all("<Button-1>",close_popup)
except:
print("Can't open popup menu")
def close_popup(event):
try:
f.place_forget()
root.after(1)
w.unbind_all("<Button-1>")
except:
print("Can't close popup menu")
w.bind("<Button-3>", open_popup)
b = Button(root, text="Quit", command=root.destroy)
b.pack()
root.mainloop()
Everything works well, if I clicked with the mouse right-button the popup menu appears, and if I clicked on every other part the popup menu dissapears.
The problem is that, due to bind_all when I press the button of my popup menu, function1 doesn't run and the event handler closes the popup. I have tried with only bind but this time, function1 runs and the event handler doesn't activates.
Is there anyway I can do that?
Thanks
I would do this using a tracking variable.
We can first assign None to f as a way to check if f is currently set up.
If f is not None then we create frame and button. Then when the function is activated we can run function and destroy the frame the button was in. This also destroys the button and then we set f back to None for out next use.
Take a look at the below reworked example.
Let me know if you have any questions.
from tkinter import *
root = Tk()
w = Label(root, text="Right-click to display menu", width=40, height=20)
w.place(x=0)
f = None # Tracking F to see if it is None or not.
def function1():
global f
print('function1 activated')
# add this to the end of the function to destroy the frame and reset f
if f != None:
f.destroy()
f = None
def open_popup(event):
global f
# if f is None then create frame and button else don't
if f == None:
f = Frame(root,width=80,height=60,background='green')
f.place(x=event.x, y=event.y)
b2 = Button(f,text='function',command=function1)
b2.pack()
else:
print("Can't open popup menu")
w.bind("<Button-3>", open_popup)
b = Button(root, text="Quit", command=root.destroy)
b.pack()
root.mainloop()
I found a way to do this without modifying so much the code, the idea of the tracking variable was good but doesn't solve all the problems, and this code does.
from tkinter import *
root = Tk()
w = Label(root, text="Right-click to display menu", width=40, height=20)
w.pack()
def function1():
print('function1 activated')
try:
f.place_forget()
except:
pass
# create a menu
f = Frame(root,width=80,height=60,background='green')
b2 = Button(f,text='function',command=function1)
b2.place(x=0,y=5)
def open_popup(event):
try:
f.place(x=event.x, y=event.y)
root.after(1)
f.focus_set()
except:
pass
def close_popup(event):
try:
f.place_forget()
root.after(1)
w.unbind_all("<Button-1>")
except:
pass
def enable_depopup(event):
w.bind_all("<Button-1>",close_popup)
def disable_depopup(event):
w.unbind_all("<Button-1>")
w.bind("<Button-3>", open_popup)
w.bind("<Motion>", enable_depopup)
f.bind("<Motion>", disable_depopup)
b = Button(root, text="Quit", command=root.destroy)
b.pack()
root.mainloop()
In this way, everytime I move the mouse over the parent window, the <Button-1> of the mouse is binded to close the popup menu.
And doing a trick, that is place the button of the menu a few pixels down, this let the mouse pass through the popup frame to reach the button and disable the <Button-1> binding letting me click the button.
The function of the button activate the place_forget method of the frame, so everything works correctly.
If I created Tkinter window with some text that filled the whole window and now wanted to replace the window with a new text, is there a way to refresh the window?
For Example:
a= 100
win= Tk()
win.geometry("500x300")
while a > 0:
if a%2 == 0:
lbl = Label (win, bg = "purple")
lbl.pack()
else:
lbl = Label (win, bg = "blue")
lbl.pack()
a= x-1
The problem with this code is that the Tkinter window does not refresh and just provides the end result instead of showing the windows changing colors.
Thanks for the help!
That is not the way to change UI states, because even if you refreshed the window it would be so quick you won't notice, instead change the state, wait some time and change the state again e.g. here I show how to animate color
from Tkinter import *
index = 0
def changeColor():
global index
if index%2==0:
label.configure(bg = "purple")
else:
label.configure(bg = "blue")
index+=1
label.after(1000, changeColor)
root = Tk()
mainContainer = Frame(root)
label = Label(mainContainer, text="")
label.configure(text="msg will change every sec")
label.pack(side=LEFT, ipadx=5, ipady=5)
mainContainer.pack()
label.after(1000, changeColor)
root.title("Timed event")
root.mainloop()
This Is How I Do To Update Data From Sql Server in tkinter GUI python3
from tkinter import *
import os
window=Tk()
window.geometry('300x300')
def update():
window.destroy()
os.system('test.py')
Button(window,text="Refresh",command=update)
window.mainloop()