Tkinter: How to assign two commands to a button via a function? - python

I am wanting to make an 'Enter Name' entry that prints a welcome message to the user. I recently posted asking for help on how to switch frames in tkinter. I figured it out and solved that problem.
However, now I want to switch frames and print an entry to a label when hitting a button. I don't receive any errors in my code however the returnEntry() function doesn't do anything. It doesn't raise the frame and I don't think it retrieves the entry text either.(without the returnEntry() function the frame does raise the frame, but with the code it doesn't. Does that make sense or am I complicating things?
Here is the code:
def show_frame(frame):
frame.tkraise()
def returnEntry():
result=NameEntry.get()
Label3.config(text=result)
def WelcomeName():
show_frame(f3)
returnEntry()
f1 = Frame(root)
f2 = Frame(root)
f3 = Frame(root)
f4 = Frame(root)
for frame in (f1, f2, f3, f4):
frame.config(width=500,height=500, bg="light blue")
frame.place(relx=0.5,rely=0.5,anchor="center")
#First Page
Button(f1, text='Click to Play', command=lambda: show_frame(f2)).place(relx=0.5,rely=0.5,anchor="center")
Label(f1, text='Magic 8 Ball', fg="White", bg="dark blue",
font="Veranda 19").place(relx=0.5,rely=0.20,anchor="center")
#Second page
Label(f2, text='Enter your name:',fg="White", bg="dark blue",
font="veranda 17").place(relx=0.5,rely=0.40,anchor="center")
NameEntry=Entry(f2,font="veranda 15").place(relx=0.5,rely=0.5,anchor="center")
Button2=Button(f2, text='Continue', font="veranda 15",
command=lambda: returnEntry).place(relx=0.5,rely=0.60,anchor="center")
#Third Page
Label3=Label(f3,text="",bg="light blue",font="veranda 15").place(relx=0.5,rely=0.35,anchor="center")
Label4=Label(f3, text="Ask me a yes or no question and reveal the truth" , bg="light blue",font="veranda 15")
Label4.place(relx=0.5,rely=0.45,anchor="center")
QuestionEntry=Entry(f3, font="veranda 15").place(relx=0.5,rely=0.52,anchor="center")
Button(f3, text='Ask away', command=lambda: show_frame(f4)).place(relx=0.5,rely=0.6,anchor="center")
Sorry for sloppy code etc...

You can't. You will have to make a 3rd function that calls the two you want, and give that 3rd function to the Button command.
def on_click():
do_thing_1()
do_thing_2()
btn = Button(command=on_click)
I highly recommend you stay away from lambda until you know how it works.
Also, you shouldn't initialize and layout widgets on the same line, it leads to bugs. Always use 2 lines.
# BAD: NameEntry is None, and can't be used
NameEntry=Entry(f2,font="veranda 15").place(relx=0.5,rely=0.5,anchor="center")
# good: NameEntry is an Entry instance, and can be used later
NameEntry=Entry(f2,font="veranda 15")
NameEntry.place(relx=0.5,rely=0.5,anchor="center")

Related

Tkinter - Python, how do I cause a button click to assign a value to a variable?

Using Tkinter and Python. Already created a window for the buttons to be placed on. I want there to be four buttons to appear, and I want to be able to click one of the four buttons, and be able for it to set the selection variable = "whatever I clicked", so that I can then use this variable later to call an API. When I run the program and click on the "General knowledge" button and print the selection, it does correctly print "General knowledge", but then when I try to return this selection variable it just doesn't work and I don't know why.
def select1():
selection = "General Knowledge"
print(selection)
def select2():
selection = "Science"
def select3():
selection = "Entertainment"
def select4():
selection = "Miscellaneous"
button1 = tk.Button(text = "General Knowledge", command = select1)
button1.place(x=100, y=100)
button2 = tk.Button(text = "Science", command = select2)
button2.place(x=100, y=140)
button3 = tk.Button(text = "Entertainment", command = select3)
button3.place(x=100, y=180)
button4 = tk.Button(text = "Miscellaneous", command = select4)
button4.place(x=100, y=220)
There are several ways to accomplish your goal.
One way is to write a single function that will take a value to assign to your variable. This way you can have as many buttons as you like and only a single function.
Not if you are using functions you have to either pass the variable to the function or let the function know it is in the global namespace.
import tkinter as tk
root = tk.Tk()
selection = ''
def assign_value(value):
global selection
selection = value
lbl["text"] = value
print(selection)
lbl = tk.Label(root, text='Selection Goes Here')
lbl.grid(row=0, column=0)
tk.Button(text="General Knowledge", command=lambda: assign_value("General Knowledge")).grid(row=1, column=0)
tk.Button(text="Science", command=lambda: assign_value("Science")).grid(row=2, column=0)
tk.Button(text="Entertainment", command=lambda: assign_value("Entertainment")).grid(row=3, column=0)
tk.Button(text="Miscellaneous", command=lambda: assign_value("Miscellaneous")).grid(row=4, column=0)
root.mainloop()
Or you can assign the value directly from the button.
import tkinter as tk
root = tk.Tk()
selection = tk.StringVar()
selection.set('Selection Goes Here')
lbl = tk.Label(root, textvariable=selection)
lbl.grid(row=0, column=0)
tk.Button(text="General Knowledge", command=lambda: selection.set("General Knowledge")).grid(row=1, column=0)
tk.Button(text="Science", command=lambda: selection.set("Science")).grid(row=2, column=0)
tk.Button(text="Entertainment", command=lambda: selection.set("Entertainment")).grid(row=3, column=0)
tk.Button(text="Miscellaneous", command=lambda: selection.set("Miscellaneous")).grid(row=4, column=0)
root.mainloop()
I am sure if I spent more time on this I could think up something else but the idea is basically write your code in a more DRY (Don't Repeat Yourself) fashion and make sure you are assigning the value to the variable in the global namespace or else it will not work as you expect.

How to disable the following button in Tkinter?

I have a hard time trying to disable the buttons I don't need in Tkinter, until some condition is met.
The following code it's a reference of what I'm doing:
ID_Personal=[]
ID_Product=[]
from tkinter import *
window = Tk()
def addProduct():
def callback():
ID_Product.append(ID_Product_Entry.get())
print("Product registered")
windowProduct = Tk()
lblProduct = Label(windowProduct, text="ID: ")
lblProduct.grid(padx=10, pady=10, row=0, column=0)
ID_Product_Entry = Entry(windowProduct)
ID_Product_Entry.grid(padx=10, pady=10, row=0, column=1)
btnAdd = Button(windowProduct, text="Submit",command=callback)
btnAdd.grid(padx=10, pady=10, row=1, column=0)
windowProduct.mainloop()
def addPersonal():
def callbackPersonal():
ID_Personal.append(ID_Personal_Entry.get())
print("Employee registered")
windowPersonal = Tk()
lblProduct = Label(windowPersonal, text="ID: ")
lblProduct.grid(padx=10, pady=10, row=0, column=0)
ID_Personal_Entry = Entry(windowPersonal)
ID_Personal_Entry.grid(padx=10, pady=10, row=0, column=1)
btnAddP = Button(windowPersonal, text="Submit",command=callbackPersonal)
btnAddP.grid(padx=10, pady=10, row=1, column=0)
windowPersonal.mainloop()
btnProduct = Button(window,text="Product",command=addProduct)
btnProduct.grid(padx=10, pady=10, row=0, column=0)
btnPersonal = Button(window,text="Personal",command=addPersonal)
btnPersonal.grid(padx=10, pady=10, row=1, column=0)
window.mainloop()
Basically what I need to do is disable the button btnProduct when my ID_Personal list it's empty. Once some personal has been registered, then it must be active again. I've been trying but nothing seems to work. I don't know if I'm using the if condition wrongly or what. Thank you in advance for your help.
You can set the state of btnProduct to "disabled" when you create it, as the list ID_Personal is initially empty.
btnProduct = Button(window,text="Product",command=addProduct,state='disabled')
You can then change the button state to "normal" in the callbackPersonal function using btnProduct.config(state='normal')
This will normalize the button the first time an item is added to ID_Personal
Now if your app demands that the button should go back to "disabled" if ID_Personal is empty and turn "normal" only when ID_Personal is non-empty then the above solution won't work. In that case, we need to recursively update the widgets based on changing conditions. The "after" function can help us to achieve that.
def update():
if len(ID_Personal) > 0:
btnProduct.config(state='normal')
else:
btnProduct.config(state='disable')
window.after(1,update)
update()
Adding the above code to your program will help you update your widgets (button in this case) continuously after every 1 millisecond.
Set the state disabled when you create the button.
btnProduct = Button(window,text="Product",command=addProduct, state="disabled")
And in the callbackPersonal function set the state active.
btnProduct.config(state="active")
This way when you run the program the product button will be disabled, but once you add your first personal, the button will be active.

Python password validation without reading file

My son is trying to learn python and wondered if someone could help on here?
The issue we are having is that we can not get password validation to work. Nothing fancy,
1 st window - enter name and password
if correct - 2nd window pops up
if incorrect - 3 window pops up
We have the following code, however the third window also pops up.
I suspect it's something to do with def main or variable.
from tkinter import*
window1=Tk()
window1.title("First window")
username="user"
password="password"
def main():
if eone == "user" and etwo == "password":
master=tk()
master.title("second window")
else:
master1=tk()
master1.title("third window")
Label(window1, text="username").grid(row=0)
Label(window1, text="password").grid(row=1)
eone=Entry(window1)
etwo=Entry(window1, show='*')
eone.grid(row=0, column=1)
etwo.grid(row=1, column=1)
b1 = Button(window1, text="login") ,bg='00c714',fg='#ffffff',command=main())
b1.grid(row=3, column=0, sticky=E)
b2=Button(window1, command=window1.destroy, text="exit", bg='#fc0303' ,fg='#ffffff')
b2.grid(row=3, column=1, sticky=E)
mainloop()
He has spent many hours on it yesterday and would appreciate any help
Thanks
First, the code you posted gave some errors so fixing them:
replacing tk() with ~~Tk()~~ Toplevel() (see the comments)
replacing '00c714' with '#00c714'
removing parantheses here "login")
now it becomes "compile" time error-free. As for your question, 2 things need changing:
When we need to give a callback / command to a button, we give the function itself and not call it! IOW, command=main() will lead to calling main right away when program runs (without pressing to button). Instead, we should do command=main (note the lack of parantheses). Actually this is what is done here also: command=window1.destroy - we need to give the function itself and tkinter will call it with parantheses when button is pressed.
eone == "user" This compares the tkinter Entry widget directly with the string "user"! What you meant is through get method of entries: eone.get() == "user". Same goes for etwo too.
Overall, here is the code with these modifications (and some PEP-8 compliant formatting):
from tkinter import*
window1 = Tk()
window1.title("First window")
username = "user"
password = "password"
def main():
# Change here: using `get` to get what is written in entries
if eone.get() == "user" and etwo.get() == "password":
master = Toplevel()
master.title("second window")
else:
master1 = Toplevel()
master1.title("third window")
Label(window1, text="username").grid(row=0)
Label(window1, text="password").grid(row=1)
eone = Entry(window1)
etwo = Entry(window1, show='*')
eone.grid(row=0, column=1)
etwo.grid(row=1, column=1)
# Change here: using function `main` itself instead of calling it
b1 = Button(window1, text="login", bg="#00c714", fg="#ffffff", command=main)
b1.grid(row=3, column=0, sticky=E)
b2 = Button(window1, command=window1.destroy, text="exit", bg="#fc0303", fg='#ffffff')
b2.grid(row=3, column=1, sticky=E)
mainloop()

Tkinter Stop Program Execution Button

I am attempting to construct a simple game using the Tkinter GUI. The first portion of the game is asking if one wants to play. My Tkinter looks as follows:
window = tk.Tk()
window.rowconfigure(0, minsize=50, weight=1)
window.columnconfigure([0, 1, 2], minsize=50, weight=1)
lbl_value = tk.Label(master=window, text="Welcome!")
lbl_value.grid(row=0, column=1)
lbl_value = tk.Label(master=window, text="Would you like to play?")
lbl_value.grid(row=1, column=1)
btn_yes = tk.Button(master=window, text="Yes", command=window.destroy)
btn_yes.grid(row=2, column=0, sticky="nsew")
btn_no = tk.Button(master=window, text="No", command=**??**)
btn_no.grid(row=2, column=2, sticky="nsew")
window.mainloop()
The "Yes" button is operating as intended, and continuing with the rest of the code. The "No" button however, I am not sure how to get it to exit the the running of the program all together (not just the window), or even better, how to get it to open an additional window stating "Maybe next time", then ending the program when that window is exited out of.
Create a new function where you can define a new tkinter screen which will appear when user press NO button See
def no_button_function(): # function that will be called when user pressed no button
new_window = tk.Toplevel(window)
new_window.title('See You Next Time')
# create the good bye message using labels and buttons accordingly
and now pass this function to NO buttton
btn_no = tk.Button(master=window, text="No", command=no_button_function)
btn_no.grid(row=2, column=2, sticky="nsew")
Hope You Got This.
Tick Mark If Satisfied

Tkinter error when calling function through a button

So I'm trying to use some entry widgets in tkinter and then use a function that does multiple .get() commands at the same time. However I get an error that the function is not defined, even though they are within the same class. Here's the samples of the code:
def GetSubjects():
subject1 = subject1entry.get()
subject2 = subject2entry.get()
subject3= subject3entry.get()
subjectConfirm.grid(row=3, column=0, command=GetSubjects)
As I said these are both in the same class and yet the button cannot call the function.
I am relatively new to tkinter and this site so forgive me if this is easy stuff, but I just couldn't find a solution anywhere else.
Edit: Here's the whole class, I know my code is probably very suboptimal but this is my first large project I've tried
class Menu:
def __init__(self, master):
frame = tk.LabelFrame(master, text="Main Menu", padx=100, pady=10)
frame.grid(row=0, column=0, padx=15, pady=15)
# Create timetable button
createTimetable = tk.Button(frame, text="Create Timetable", command=self.CreateTimetable)
createTimetable.grid(row=0, column=0)
# Exit program button
exitProgram = tk.Button(frame, text="Exit Program", command=self.CloseWindow)
exitProgram.grid(row=1, column=0)
def CloseWindow(self):
root.destroy()
def GetSubjects():
subject1 = subject1entry.get()
subject2 = subject2entry.get()
subject3 = subject3entry.get()
def CreateTimetable(self):
tableWindow = tk.Toplevel(root)
tableWindow.title('Timetable Maker Window')
tableWindow.geometry("800x500+400+200")
# Subject labels
subjectlabel1 = tk.Label(tableWindow, text="Enter your first subject:")
subjectlabel2 = tk.Label(tableWindow, text="Enter your second subject:")
subjectlabel3 = tk.Label(tableWindow, text="Enter your third subject:")
# Subject entry boxes
subject1entry = tk.Entry(tableWindow)
subject2entry = tk.Entry(tableWindow)
subject3entry = tk.Entry(tableWindow)
# Puts subject entry boxes on screen
subject1entry.grid(row=0, column=1)
subject2entry.grid(row=1, column=1)
subject3entry.grid(row=2, column=1)
# Puts subject labels on screen
subjectlabel1.grid(row=0, column=0)
subjectlabel2.grid(row=1, column=0)
subjectlabel3.grid(row=2, column=0)
# Creates subject confirm button
subjectConfirm = tk.Button(tableWindow,text="Press to confirm subjects")
# Puts subject confirm button on screen
subjectConfirm.grid(row=3, column=0, command=GetSubjects)
print(subject1)
print(subject2)
print(subject3)
Your GetSubjects() method should look like this
def GetSubjects(self):
subject1 = self.subject1entry.get()
subject2 = self.subject2entry.get()
subject3 = self.subject3entry.get()
Your CreateTimetable method should look like this (incomplete but give you the right idea)
def CreateTimetable(self):
# Code removed for clarity
self.subject1entry = tk.Entry(tableWindow)
self.subject2entry = tk.Entry(tableWindow)
self.subject3entry = tk.Entry(tableWindow)
And your callback for the button should look like this
subjectConfirm = tk.Button(tableWindow,text="Press to confirm subjects", command=self.GetSubjects)
The properties/methods need to be part of the Menu class so you use self. to tell python to make these properties of the current class (self is the typical convention but could be anything as long as you are consistent).
Oh and the print(subject1) parts need to be at the end of the GetSubjects method instead of where they currently are.

Categories