I'm Creating a gui in tkinter and have buttons named btn1 btn2 btn3 etc, what i want the button to do on click is disable the button clicked and enable the next button in order. I can write out 6 seperate functions but that seems to defeat the point of a function.
if (btn1['state'] == tk.NORMAL):
btn1.config(state=tk.DISABLED),
btn2.config(state=tk.NORMAL)
else: print ('already clicked')
this is what i have now, but i want it to look more like btn #+1 (state=DISABLED)
You can put the buttons in a list, and then iterate over the list.
Here's a bit of a contrived example:
import tkinter as tk
root = tk.Tk()
def click(button_number):
button = buttons[button_number]
button.configure(state="disabled")
if button == buttons[-1]:
# user clicked the last button
label.configure(text="BOOM!")
else:
next_button = buttons[button_number+1]
next_button.configure(state="normal")
next_button.focus_set()
label = tk.Label(root, text="")
label.pack(side="bottom", fill="x")
buttons = []
for i in range(10):
state = "normal" if i == 0 else "disabled"
button = tk.Button(root, text=i+1, state=state, width=4,
command=lambda button_number=i: click(button_number))
button.pack(side="left")
buttons.append(button)
buttons[0].focus_set()
root.mainloop()
Related
I am trying to create a 10*10 board of buttons, which when clicked, the clicked button is destroyed and only that one. However, I don't know how to specify which button has been clicked.
from tkinter import *
root = Tk()
root.title("Board")
def buttonClick():
button.destroy()
for i in range(10):
for j in range(10):
button = Button(root, text="", padx=20, pady=10, command=buttonClick)
button.grid(row=i+1, column=j+1)
root.mainloop()
You have to create function which gets widget/button as argument and uses it with destroy()
def buttonClick(widget):
widget.destroy()
And first you have to create Button without command=
button = tk.Button(root, text="", padx=20, pady=10)
and later you can use this button as argument in command=.
button["command"] = lambda widget=button:buttonClick(widget)
It needs to use lambda to assign function with argument.
Because you create many buttons in loop so it also needs to use widget=button in lambda to create unique variable with value from button for every command. If you don't use it then all commands will use reference to the same (last) button - and click on every button will destroy only last button.
Full working code
import tkinter as tk # PEP8: `import *` is not preferred
# --- functions ---
def buttonClick(widget):
widget.destroy()
# --- main ---
root = tk.Tk()
root.title("Board")
for i in range(10):
for j in range(10):
button = tk.Button(root, text="x", padx=20, pady=10)
button["command"] = lambda widget=button:buttonClick(widget)
button.grid(row=i+1, column=j+1)
root.mainloop()
PEP 8 -- Style Guide for Python Code
This can be easily accomplished with a custom class if you're alright with that:
from tkinter import Button, Tk
root = Tk()
root.title("Board")
class Self_Destruct_Button(Button):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.configure(command=self.button_click)
def button_click(self):
self.destroy()
for i in range(10):
for j in range(10):
button = Self_Destruct_Button(root, text="", padx=20, pady=10)
button.grid(row=i + 1, column=j + 1)
root.mainloop()
So the custom class just assigns the command to a button_click method, which destroys the button.
As a side note, I also removed the wildcard import as that's not best practice.
Let me know if this works for you
I created 5 buttons. When users click the button, new window opens. I want the opened windows have different title. For example users click button1, the window that was opened name will be "Button_1". This is some part of my code.
button1= tk.Button(window,image=photo1,command=Calculations)
button2= tk.Button(window,image=photo2,command=Calculations)
button3= tk.Button(window,image=photo3,command=Calculations)
button4= tk.Button(window,image=photo4,command=Calculations)
button5= tk.Button(window,image=photo5,command=Calculations)
def Calculations():
window_2 = tk.Toplevel()
window_2.geometry("1000x1000")
window_2.title("Button_1")
I think if I know which button is pressed, I think I can assign a variable and write the window_2.title() with the format() method. Is there a way to find the button that was pressed and assign it to a variable or another idea?
You pass the button and its name/index using functools.partial(<command>, *arguments).
from functools import partial
def Calculations(button, name):
window_2 = tk.Toplevel()
window_2.geometry("1000x1000")
window_2.title(name)
# Create the button without a command or an empty command
button1= tk.Button(window, image=photo1)
# Configure the command later so that we can also pass in `button1` to the function
button1.config(command=partial(Calculations, button1, "button1"))
button2 = tk.Button(window, image=photo2)
button2.config(command=partial(Calculations, button2, "button2"))
...
You can use a lambda function for the button's command argument.
button1= tk.Button(window,image=photo1,command=lambda: Calculations("1"))
button2= tk.Button(window,image=photo2,command=lambda: Calculations("2"))
button3= tk.Button(window,image=photo3,command=lambda: Calculations("3"))
button4= tk.Button(window,image=photo4,command=lambda: Calculations("4"))
button5= tk.Button(window,image=photo5,command=lambda: Calculations("5"))
def Calculations(num):
window = tk.Toplevel()
window.geometry("1000x1000")
window.title("Button_" + num)
A more efficient solution would be to use a for loop:
for i in range(5):
i += 1
globals()["button" + str(i)] = tk.Button(window, image=globals()["photo" + str(i)], command=lambda i=i: Calculations(str(i)))
def Calculations(num):
window = tk.Toplevel()
window.geometry("1000x1000")
window.title("Button_" + num)
button1= tk.Button(window,image=photo1,command=lambda: Calculations("button one"))
button2= tk.Button(window,image=photo2,command=lambda: Calculations("button two"))
button3= tk.Button(window,image=photo3,command=lambda: Calculations("button three"))
button4= tk.Button(window,image=photo4,command=lambda: Calculations("button four"))
button5= tk.Button(window,image=photo5,command=lambda: Calculations("button 5"))
so then when the button is pressed it will give a str variable which says which button was pressed. Example: "button one"
def Calculations(button):
window = tk.Toplevel()
window.geometry("1000x1000")
window.title(str(button))
then the title of the window will be which button was pressed.
you could also make a set of radio buttons/ check buttons and assign them to tk.IntVar() and make a unique value for each of them and make a 'done' button.
( it does the same thing expect that it will be check buttons)
var = tk.IntVar()
C1 = tk.Checkbutton(frame, onvalue=1, variable=self.var)
C2 = tk.Checkbutton(frame, onvalue=2, variable=self.var)
C3 = tk.Checkbutton(frame, onvalue=3, variable=self.var)
C4 = tk.Checkbutton(frame, onvalue=4, variable=self.var)
done = tk.button(frame, command=calculations)
def Calculations(num):
window = tk.Toplevel()
window.geometry("1000x1000")
window.title("Button_" + var)
Recently I was working on a program where when one clicked a button, it would delete all of the tkinter buttons they made through a .yml file. Here is an example of what I mean:
(All TKinter Root Init Here)
button1 = Button(root, text="hi")
button2 = Button(root, text="hi again")
button3 = Button(root, text="hi again again")
button4 = Button(root, text="OK this is getting tiring")
button5 = Button(root, text="go away")
button6 = Button(root, text="...")
def del_all():
for i in range(999999999):
button(i).place_forget() #I was hoping to make button(i) give the output button1, then button2, and so on.
root.mainloop()
Try nametowidget in tkinter,example like:
import tkinter as tk
r = tk.Tk()
for i in range(5):
tk.Button(r,text=i).pack()
r.nametowidget(".!button").pack_forget()
r.mainloop()
This will remove the first button.If you want to remove the second button, you need to use r.nametowidget(".!button2").pack_forget()
So for you code,you may need to use:
def del_all():
root.nametowidget(".!button").place_forget()
for i in range(2, 999999999):
root.nametowidget(".!button"+str(i)).place_forget()
About the parameter in the nametowidget, there is a clear description.
You could also use winfo_children and use .widgetName to check whether it is a button,like:
import tkinter as tk
r = tk.Tk()
tk.Label(r, text="test").pack()
for i in range(5):
tk.Button(r,text=i).pack()
for i in r.winfo_children():
if i.widgetName == 'button':
i.pack_forget()
r.mainloop()
The solution would depend on how the buttons are named/stored.
For example, if the buttons were a list. Something like:
buttons = ['button1', 'button2', 'button3', 'button4']
Then you an delete by calling:
buttons.remove()
And that would 'clear' the list.
Working on a project in which I use Tkinter in order to create a GUI that gives a list of software in a drop-down and when a particular software is chosen, it takes you to a separate window where a user's name will be entered and they would be added to a database. With the code I have so far, I am able to link a "submit" button on the second window to a function that prints a confirmation message as a test to make sure the button works. My issue now is trying to get the input from the entry field and link the input to the "Submit" button but I can't seem to find a way to do so. I was wondering if I could get some advice on how to go about this. Would classes need to be used in order to make it work? or can I stick with functions and keep the code relatively simple?
I have added the code for my program below.
import tkinter as tk
from tkinter import *
from tkinter import ttk
root = tk.Tk() # Main window
root.title("Software Licences")
root.geometry("300x300")
frame = ttk.Frame(root, padding="50 0 50 50")
frame.pack(fill=tk.BOTH, expand=True)
tkvar = StringVar()
choices = ['Imagenow', # Dropdown menu with software options
'FileMakerPro',
'Acrobat',
'Office',
'Lotus Notes']
tkvar.set('Acrobat') # Shows default dropdown menu option when program is opened
popupMenu = OptionMenu(frame, tkvar, *sorted(choices))
popupLabel = ttk.Label(frame, text="Choose Software")
popupLabel.pack()
popupMenu.pack()
def software_pages(): # In this function is the 2nd window with for each individual software
top = Toplevel()
top.title("Software Licences")
top.geometry("300x300")
myLabel = Label(top, text=tkvar.get()).pack()
employee_entrylbl = Label(top, text="Employee name").pack()
employee_entry = Entry(top, width=25, textvariable=tk.StringVar) # Entry field for adding user's name
employee_entry.pack() # Entry field is displayed
if tkvar.get() == "Acrobat": # for each if statement, button command is link to the functions
# defined below
button = ttk.Button(top, text="Submit", command=add_to_acrobat).pack()
elif tkvar.get() == "Imagenow":
button = ttk.Button(top, text="Submit", command=add_to_imagenow).pack()
elif tkvar.get() == "FileMakerPro":
button = ttk.Button(top, text="Submit", command=add_to_filemakerpro).pack()
elif tkvar.get() == "Office":
button = ttk.Button(top, text="Submit", command=add_to_office).pack()
else:
button = ttk.Button(top, text="Submit", command=add_to_lotusnotes).pack()
exit_button = ttk.Button(top, text="Exit", command=top.destroy).pack() # Exit button for second window
add_emp_button = ttk.Button(frame, text="Next", command=software_pages) # "Next" button in the main window takes the
# user to the second window
add_emp_button.pack()
# Functions below are linked to the button commands of each software in the second window function defined earlier.
# They print out specified messages that confirm the user had been added
def add_to_acrobat():
return print("User added to Acrobat")
def add_to_lotusnotes():
print("User added to IBM")
def add_to_imagenow():
print("User added to imagenow")
def add_to_office():
print("User added to 365")
def add_to_filemakerpro():
print("User added to FMP")
def click_button(): # Function for Exit button for main window
root.destroy()
exit_button = ttk.Button(frame, text="Exit", command=click_button) # Exit button for main window
exit_button.pack()
root.mainloop()
You can pass parameters to the command of tkinter.command using partial from the functools module.
in your case:
button = ttk.Button(top, text="Submit", command=partial(add_to_acrobat, employee_entry)).pack()
in the above line, I send the employee_entry(Which holds your desired text) to the add_to_acrobat function
and the add_acrobat function should look like this:
def add_to_acrobat(e):
print(e.get())
return print("User added to Acrobat")
Hope it helps
from tkinter import *
main = Tk()
def flipper(event):
# I'd like to do this:
#if widgetname == switcher:
#do stuff
#if widgetname == switcher1:
#do stuff
return
switcher = Label(main, bg='white', text="click here", font="-weight bold")
switcher.grid()
switcher.bind("<Button-1>", flipper)
switcher1 = Label(main, bg='white', text="click here", font="-weight bold")
switcher1.grid()
switcher1.bind("<Button-1>", flipper)
switcher2 = Label(main, bg='white', text="click here", font="-weight bold")
switcher2.grid()
switcher2.bind("<Button-1>", flipper)
switcher3 = Label(main, bg='white', text="click here", font="-weight bold")
switcher3.grid()
switcher3.bind("<Button-1>", flipper)
switcher4 = Label(main, bg='white', text="click here", font="-weight bold")
switcher4.grid()
switcher4.bind("<Button-1>", flipper)
switcher5 = Label(main, bg='white', text="click here", font="-weight bold")
switcher5.grid()
switcher5.bind("<Button-1>", flipper)
main.mainloop()
In my event function I'd like to do different things based on the label that is clicked. What im stumped on is that I can only get the identifier number of the widget that is clicked, not the name. If I could get the identifier of all my widgets then I could do:
def flipper(event):
if event.widget == switcher.identifier():
do stuff
but I can't find how to get the id of a specified widget either...
How can I get the name of a widget by its identifier (event.widget())?
Or how can I get the identifier of a specified widget name?
If neither are possible, then I'd have to make a different function and bind for each label which is a lot of work that hopefully is not necessary.
Edit:
from tkinter import *
main = Tk()
def flipper(event, switch):
if switch.widget == 's1':
print("got it")
switcher = Label(main, bg='white', text="click here", font="-weight bold")
switcher.grid()
switcher.bind("<Button-1>", flipper)
switcher.widget = 's1'
main.mainloop()
You can't get the variable name that the widget is assigned to, that would be relatively useless. A widget could be assigned to more than one variable, or none at all.
Getting the label text
You have access to the actual widget, and you can use that to get the text that is on the label. Your example shows that all labels are the same, so this might not be useful to you:
def flipper(event):
print("label text:", event.widget.cget("text"))
Using a custom widget name
You can also give a widget a name. You can't get back precisely the name, but you can come very close. For example, if you create a label like this:
switcher = Label(main, name="switcher", bg='white', text="click here", font="-weight bold")
You can get the string representation of the widget by splitting on "." and taking the last value:
def flipper(event):
print("widget name:", str(event.widget).split(".")[-1])
Passing a name via the binding
Finally, you can set up your bindings such that the name is sent to the function:
switcher.bind("<Button-1>", lambda event: flipper(event, "switcher"))
switcher1.bind("<Button-1>", lambda event: flipper(event, "switcher1"))
You can use event.widget to get standard parameters from clicked widget
example:
import tkinter as tk
def callback(event):
print(event.widget['text'])
main = tk.Tk()
switcher = tk.Label(main, text="click here")
switcher.grid()
switcher.bind("<Button-1>", callback)
main.mainloop()
You can assign own variables to widgets
switcher.extra = "Hello"
and then get it
event.widget.extra
example:
import tkinter as tk
def callback(event):
print(event.widget['text'])
print(event.widget.extra)
main = tk.Tk()
switcher = tk.Label(main, text="click here")
switcher.grid()
switcher.bind("<Button-1>", callback)
switcher.extra = "Hello"
main.mainloop()
You can use lambda to bind function with arguments
bind("<Button-1>", lambda event:callback(event, "Hello"))
example:
import tkinter as tk
def callback(event, extra):
print(event.widget['text'])
print(extra)
main = tk.Tk()
switcher = tk.Label(main, text="click here")
switcher.grid()
switcher.bind("<Button-1>", lambda event:callback(event, "Hello"))
main.mainloop()
I had the same issue I found easy way was to use bind method.
apparent name property is private but can be accessed via _name
This is useful if you plan to generate widgets dynamically at runtime
# Import Module
from tkinter import *
# create root window
root = Tk()
# root window title and dimension
root.title("Welcome to Test window")
# Set geometry (widthxheight)
root.geometry('350x200')
#adding a label to the root window
lbl = Label(root, text = "Press a button")
lbl.grid()
#define mouse up event
def mous_up(ev:Event):
#get calling widget from event
sender:Button = ev.widget
#set label text
lbl.configure(text = sender._name + " up")
#read foreground color from button
#If red make green, else make red
if sender.cget('fg') == "red":
#normal color
sender.configure(fg='lime')
#mouse over color
sender.configure(activeforeground='green')
else:
#normal color
sender.configure(fg="red")
#mouse over color
sender.configure(activeforeground='darkred')
#define mouse down event
def mous_down(ev:Event):
lbl.configure(text = str(ev.widget._name) + " down")
# button widget with red color text
# inside
btn = Button(root, text = "Click me" ,
fg = "red",name = "button-A")
#bind mouse up and mouse down events
btn.bind('<ButtonRelease-1>',mous_up)
btn.bind('<Button-1>',mous_down)
# set Button grid
btn.grid(column=0, row=1)
#Create another button
btn = Button(root, text = "Click me2" ,
fg = "red",name="button2")
#bind mouse up and mouse down events
btn.bind('<ButtonRelease-1>',mous_up)
btn.bind('<Button-1>',mous_down)
#absolute placement of button instead of
#using grid system
btn.place(x=50,y=100)
# all widgets will be here
# Execute Tkinter
root.mainloop()
Quick and dirty - you could have the function check a switcher attribute.
def flipper(event, switch):
if switch.widget == 's1':
do_stuff
return stuff
if switch.widget == 's2':
do_stuff
return stuff
switcher1.widget = 's1'
switcher2.widget = 's2'
I know this is an old post, but I had the same problem and I thought I should share a solution in case anyone is interested. You can give your widget a name by creating a subclass of the widget. E.g. "Button" is a widget. You can make a child widget "MyButton" which inherits from button and then add an instance variable to it (e.g. name, uniqueID etc.)
Here is a code snippet
class MyButton(Button):
def __init__(self, master = None, textVal = "", wName = ""):
Button.__init__(self, master, text = textVal)
self.widgetID = wName #unique identifier for each button.
When you want to create a new button widget, use
b = MyButton(.....),
instead of
b = Button(.......)
This way, you have all the functionality of a button, plus the unique identifier.