Why does a tkinter binding event only trigger once? [duplicate] - python

This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 1 year ago.
I'm working on a custom button for a list, and want to make it change color when the mouse hovers above the button. See the following code for how I'm trying to do it:
import tkinter as tk
from tkinter import *
class list_button:
def __init__(self, container, text, font_size):
self.list_button = tk.Button(container, text=text, font = ("helvatica", (0 + font_size)), relief = "flat")
self.list_button.pack()
self.list_button.bind('<Enter>', self.enter_func(self))
self.list_button.bind('<Leave>', self.leave_func(self))
def enter_func(self, key):
print("Enter")
self.list_button.configure(bg = "grey")
def leave_func(self, key):
print("Leave")
self.list_button.configure(bg = "white")
root = tk.Tk()
for i in range(10):
list_button(root, i, 15)
root.mainloop()
What seems to happen is that the code calls the function once and then unbinds the function.
What am I doing wrong here?

For people that are stuck on the same problem. This is the revised code:
import tkinter as tk
from tkinter import *
class list_button:
def __init__(self, container, text, font_size):
self.list_button = tk.Button(container, text=text, font = ("helvatica", (0 + font_size)), relief = "flat")
self.list_button.pack()
self.list_button.bind('<Enter>', self.enter_func)
self.list_button.bind('<Leave>', self.leave_func)
def enter_func(self, key):
print("Enter")
self.list_button.configure(bg = "grey")
def leave_func(self, key):
print("Leave")
self.list_button.configure(bg = "white")
root = tk.Tk()
for i in range(10):
list_button(root, i, 15)
root.mainloop()
The changes are in the following part:
self.list_button.bind('<Enter>', self.enter_func)
self.list_button.bind('<Leave>', self.leave_func)

Related

Tkinter - call function and dependent on it's return destroy messagebox

I am looking for a way to display a simple messagebox with a notification to the user to do something. When this messagebox is displayed I want to call other function, which is checking display in a loop and returning True, if the user followed the instruction, else False. When this function returns True the messagebox should be destroyed so that the program can continue automatically.
The code below is the part of framework, where tkinter is implemented:
import tkinter as tk
from tkinter import messagebox, Toplevel, ttk
class ModalDlg_base(tk.Toplevel):
def __init__(self, parent, title, text):
tk.Toplevel.__init__(self, parent)
self.attributes("-toolwindow", 1)
self.title(title)
self.text = tk.Label(self, text=text)
self.text.grid(row=0, column=0, columnspan=5, padx=8, pady=10)
self.answer = None
self.protocol("WM_DELETE_WINDOW", self.no)
def yes(self):
self.answer = True
self.destroy()
def no(self):
self.answer = False
self.destroy()
def timeout(self):
self.answer = None
self.destroy()
class DlgMessagebox(ModalOkDlg_base):
pass
Here is the script that is being called whenever I need such pop_up:
def _timeout_dialog(root, dlgWindow, timeout):
sw = root.winfo_screenwidth()
sh = root.winfo_screenheight()
dpi = root.winfo_fpixels('1i')
real_width = int(sw * dpi / 96)
real_height = int(sh * dpi / 96)
root.withdraw()
dlgWindow.attributes('-topmost', 'true')
timeout_ms = int(timeout * 1000)
root.after(timeout_ms, dlgWindow.timeout)
dlgWindow.update()
x = (real_width - dlgWindow.winfo_width()) // 2
y = (real_height - dlgWindow.winfo_height()) // 2
dlgWindow.geometry("+%d+%d" % (x, y))
# modal wait
root.wait_window(dlgWindow)
root.destroy()
answer = dlgWindow.answer
if answer is None:
raise TimeoutError
return answer
def message_dialog(title, description, timeout=TIMEOUT_DEFAULT):
root = tk.Tk()
return _timeout_dialog(root, DlgMessagebox(root, title, description), timeout)
I don't know if this is necessary, but I have tried to rework the _timeout_dialog to change from
root.wait_window(dlgWindow)
to
root.wait_variable(dlgWindow)
but all the efforts went for nothing. Of course I have also tried to insert a function as argument to def message_dialog and create a condition in class DlgMessageBox with destroy() method, yet without luck for now.
I hope the problem is now better understandable, again any help here will be appreciated!

How to pass an argument to a button creation command in tkinter [duplicate]

This question already has answers here:
How to pass arguments to a Button command in Tkinter?
(18 answers)
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed last year.
This post was edited and submitted for review last year and failed to reopen the post:
Original close reason(s) were not resolved
So I have the code here:
import tkinter as tk
def add_x(x,y):
return y + x
class creator():
def __init__(self,val):
self.val = val
self.root = tk.Tk()
def func(self,cmd,amount):
change_val = cmd(self.val,amount)
self.val = change_val
return self.val
def btn(self,cmd):
btn_text = tk.StringVar()
def update_btn_text(var):
btn_text.set(var)
btn = tk.Button(self.root, textvariable=btn_text,
command = update_btn_text(cmd))
btn_text.set(0)
btn.pack()
def run(self):
self.root.mainloop()
a = creator(0)
a.btn(a.func(add_x,1))
What this is supposed to do is create a button that will increase the displayed text by one each time it is pressed. The issue is that when I try running it, nothing will happen and it will only change a.val to 1 at the beginning and keep the button text at 0. Does anyone know why this is happening? Thanks!
This got closed because it was associated with another question, but nothing there answered my question. Using lambda breaks my code and partial does nothing.
This works. What you pass to btn has to be a lambda, and what you pass to tk.Button has to be a lambda. I can't help but think this is over-engineered. There are far simpler ways to get an incrementing button.
import tkinter as tk
def add_x(x,y):
return y + x
class creator():
def __init__(self,val):
self.val = val
self.root = tk.Tk()
def func(self,cmd,amount):
change_val = cmd(self.val,amount)
print('cv',change_val)
self.val = change_val
return self.val
def btn(self,cmd):
btn_text = tk.StringVar()
def update_btn_text(var):
btn_text.set(var())
btn = tk.Button(self.root, textvariable=btn_text,
command = lambda : update_btn_text(cmd))
btn_text.set(0)
btn.pack()
def run(self):
self.root.mainloop()
a = creator(0)
a.btn(lambda : a.func(add_x,1))
a.run()

Tkinter entry widget execution [duplicate]

This question already exists:
Entry widget in tkinter
Closed 1 year ago.
So I made a simple program however it doesn't seem to work
my code is:
e = Entry(root, font = 20,borderwidth=5)
e.grid(row=1)
def capture(event):
print(e.get())
e.bind("<Key>", capture)
However the first time I enter something in the box, all I get is an empty string.
As #Art stated:
You can use "<KeyRelease>", e.bind("<Key>", lambda event: e.after(1, capture, event))" or simply Use StringVar()
from tkinter import *
root=Tk()
e = Entry(root, font = 20,borderwidth=5)
e.grid(row=1)
def capture(event):
print(e.get())
e.bind("<Key>", lambda event: e.after(1, capture, event))
root.mainloop()
Or you can use a StringVar()
from tkinter import *
root=Tk()
s=StringVar()
e = Entry(root,textvariable=s, font = 20,borderwidth=5)
e.grid(row=1)
def capture(*args):
print(s.get())
s.trace("w",capture)
root.mainloop()

Python tkinter limit entry input [duplicate]

This question already has an answer here:
(Python) How to limit an entry box to 2 characters max [duplicate]
(1 answer)
Closed 5 years ago.
How do I limit the input on a Entry to only 4 characters
from tkinter import *
window = Tk()
display = Entry(window)
display.grid()
You can do this by running a trace on the attribute textvariable of the entry widget. Whenever this variable is updated you will need to set the variable to it's own value up to the 4th character.
See below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.sv = StringVar()
self.entry = Entry(root, textvariable = self.sv)
self.entry.pack()
self.sv.trace("w", lambda name, index, mode, sv=self.sv: self.callback(self.sv))
def callback(self, sv):
self.sv.set(self.sv.get()[:4])
root = Tk()
App(root)
root.mainloop()

python button does not work when image is added [duplicate]

This question already has answers here:
Image on a button
(5 answers)
Closed 8 years ago.
When I try to add and image to the button, the program will run, but the button will be blank and you cannot click on it. If I change image=Tkinter.PhotoImage(file="C:/TeDOC/OpenFolder.gif") to text='Open Directory it works fine and you are able to click the button. I have no idea why when I change it to an img, it does not work. Any help will be appreciated.
Here is my code:
import Tkinter, Tkconstants, tkFileDialog
class TkFileDialogExample(Tkinter.Frame):
def __init__(self, root):
Tkinter.Frame.__init__(self, root)
# options for buttons
button_opt = {'fill': Tkconstants.BOTH, 'padx': 5, 'pady': 5}
# define buttons
Tkinter.Button(self, image=Tkinter.PhotoImage(file="C:/TeDOC/OpenFolder.gif"), command=self.askdirectory).pack(**button_opt)
# defining options for opening a directory
self.dir_opt = options = {}
options['initialdir'] = 'C:\\'
options['mustexist'] = False
options['parent'] = root
options['title'] = 'This is a title'
def askdirectory(self):
#Returns a selected directoryname.
return tkFileDialog.askdirectory(**self.dir_opt)
if __name__=='__main__':
root = Tkinter.Tk()
TkFileDialogExample(root).pack()
root.mainloop()
First you have to define your image, using the self.image. So try:
self.image = Tkinter.PhotoImage(file="C:/TeDOC/OpenFolder.gif")
Then under your button, put:
Tkinter.Button(self, image=self.image, command=self.askdirectory).pack(**button_opt)
You must save the image in self.
self.image = Tkinter.PhotoImage(file="C:/TeDOC/OpenFolder.gif")
Tkinter.Button(..., image=Tkinter.PhotoImage(file="C:/TeDOC/OpenFolder.gif"), ...
If it is deleted it will not be displayed.

Categories