Create a function that ends mainloop and starts new one in tkinter - python

I'm writing my first GUI program today using Tkinter and I have stumbled onto a problem. I am trying to make a game that starts with an introduction window that closes after you press a button, then opens a new window where you can choose one of two modes. Unfortunately, I just can't get it running. It looks a little something like this.
#These are the functions that I defined to make it work
def start():
root.destroy()
def Rules_mode_1():
root.destroy
rules1 = Tk()
understood1 = Button(rules1, text="I understood", command="Start_game_mode_1")
understood.pack()
rules1.mainloop
# I haven't added rules 2 yet cause I couldn't get it to work with rules 1 so I haven't even #bothered but it would be the same code just switching the 1 for a 2. But really it isn't even
#necessary to have 2 different rule functions because the rules are the same but I couldn't think
#of another way to go about it. if you have an idea let me know
def Start_game_mode_1():
rules1.destroy #<----- THIS IS WHERE THE PROBLEM LIES. JUST DOESN'T RUN
gamemode1 = Tk()
#Here I would have the game
gamemode1.mainloop()
#now same here don't have gamemode 2 yet cause it just doesn't work yet
#This is where it really starts
root = Tk()
startbutton = Button(root, text="Start", command=start)
startbutton.pack
root.mainloop
root = Tk()
def mode():
mode1 = Button(root, command=Rules_mode_1)
mode1.pack
mode2 = #Buttonblablabla
mode()
root.mainloop()
Now I've been trying around for hours, trying to give the mainloops different names. For example giving the
rules1.mainloop
#the name
root.mainloop
but that obviously didn't work. I tried it with dozens of helper function and with the lambda expression and did hours of research but just can't seem to fix it. Does anybody have any ideas? Please be respectful and keep in mind it's my first time using Tkinter.
Thank you for your help!

After the comments didn't really help me I just tried things out for hours and in case anybody ever is having a a similar problem and reads this: The rules1 variable is inside a function, and therefore only local, which means it can't be destroyed in another function. I fixed it by making it a global, like:
def Rules_mode_1():
root.destroy
global rules1
rules1 = Tk()
understood1 = Button(rules1, text="I understood", command="Start_game_mode_1")
understood.pack()
rules1.mainloop
After that I could destroy the mainloop in the next function.

Related

Python tkinter, labels, and function order

This seems so simple but I can't figure out what I need to do to remedy. I have a tkinter project and on a button press, a function runs that takes several seconds. I want a "loading..." type message while the function is running so it's obvious it's actually working and not crashed. I figured a label would be easy enough and on the first line of the function, have label1.set('loading') but I suppose because of the way functions work, the label doesn't set until the function is done running--which is not helpful.
I made a second short function
def update_status(message):
label1.set(message)
and for the button in tkinter, used command=lambda:[update_status('loading'),search()] in hopes that the update_status() function would run first, alter the label, and then the second search() function that takes upwards of 30 seconds would run. But I get the same effect.
What's the simplest way finish running the update_status() function--thereby updating my label acting as the "status", and THEN run the time consuming search() function?
I'm not opposed to something more complicated like a loading window or something similar, but just wanted something simple (I have not even googled any type of loading window--I'm mostly hung up on how to get 2 functions to run on a button click in a sequential order).
Hey I do not think you need 2 functions to do what you want. You simply have to update your root so that the label is directly updated.
Here is an example:
import tkinter as tk
import time
def update_status(message1, message2):
var.set(message1)
root.update()
time.sleep(5)
var.set(message2)
if __name__ == '__main__':
root = tk.Tk()
root.title("Wait for function")
var = tk.StringVar()
var.set('Waiting for input')
label1 = tk.Label(root, textvariable=var)
label1.pack()
Button1 = tk.Button(root, text="Wait", command=lambda:update_status('loading', 'done'))
Button1.pack()
root.mainloop()

Problem with binding 2 functions to a button in tkinter

First of all this is my code:
button_1 = Button(
image=button_image_1,
borderwidth=0,
highlightthickness=0,
command=lambda:[get_to_main_when_clicked(),delete_red_border()],
relief="flat"
)
As you can see I binded 2 functions to this button. To the first one: If a specific condition is true, then the function lets an image appear. The second one then should wait 3 seconds and should delete the appeared image. The only really weird problem is, that it no matter what I do, first executes the delete_red_border() function. It waits 3 seconds, then its trying to delete an image that couldn't be defined and globalized, because the get_to_main_when_clicked() function wasn't executed. How can I solve this?
PS: The specific condition is true.
Don't do this. Create a function specifically for this button. A function is much easier to understand and debug than a lambda, especially a complex lambda. You can then put any logic you want inside the function.
def do_something():
get_to_main_when_clicked()
delete_red_border()
button_1 = Button(..., command=do_something)
I found the solution for it. The problem was, that it didn't refreshed/updated the window after finishing the first function. That was solved by one line of code:
window.update()

Python Tkinter open multiple windows within a function

I have an inbuilt calculator within my program. My main program was designed to be somewhat similar to Microsoft Bob. Now when the user presses the 'Calculator' Button in the main focus, it opens up the part of the calculator where you define your 2 numbers and the operator. When you press 'Results' the answer does not appear in any shape of form until my main program is closed. Any help? Attached is the calculator code sample, and more than happy to change the second window to something else. BTW this is for a school project.
def Calculator():
calculator = Tk()
Number1 = DoubleVar()
Number2 = DoubleVar()
Operator = IntVar()
Operator.set(1)
Entry(calculator,textvariable=Number1,justify="c").grid()
Entry(calculator,textvariable=Number2,justify="c").grid()
Radiobutton(calculator,text="Add",variable=Operator,value=1).grid()
Radiobutton(calculator,text="Subtract",variable=Operator,value=2).grid()
Radiobutton(calculator,text="Multiply",variable=Operator,value=3).grid()
Radiobutton(calculator,text="Divide",variable=Operator,value=4).grid()
Radiobutton(calculator,text="Square",variable=Operator,value=5).grid()
Radiobutton(calculator,text="Square root",variable=Operator,value=6).grid()
Button(calculator,text="results",command=calculator.destroy,width=16).grid()
Number1=Number1.get()
Number2=Number2.get()
Operator=Operator.get()
if Operator==1:Results=Number1+Number2
if Operator==2:Results=Number1-Number2
if Operator==3:Results=Number1*Number2
if Operator==4:Results=Number1/Number2
if Operator==5:Results=math.pow(Number1,Number2)
if Operator==6:Results=Number1*(1/Number2)
Results = "The answer is "+str(Results)
Answer = Tk()
Answer.geometry("150x150")
Label(Answer, text=Results).place(relx=.5,rely=.5,anchor="center")
Answer.mainloop()
You can't have multiple Tk objects with their own mainloops.
Or, rather, you can, but whichever one is currently running mainloop, none of the others (and none of their children) gets to do anything until that mainloop finishes.
What you want is to have multiple Toplevel widgets, with the same Tk as their master. (If you just have one Tk, as you usually do, you can leave that as the default.)
But you have another, equally serious problem here.
You're creating a Calculator with a bunch of Tk vars attached, then immediately trying to read those vars and do something with them. You can't do that.
What you need to do is to put all of that in the callback to some kind of user event, like clicking the Results button.
You have one more problem in your code that makes things a bit harder:
Number1=Number1.get()
What you really want here is two separate variables, one the Tk var, and the other an int. And then you need to make the Tk var accessible in the results callback in some way. The obvious way is to move all of this to a class, and store all your Tk vars as instance attributes. If you don't know how to do that, you can always use globals. (Not ideal, but until you learn classes, it's fine.) Then the actual numbers are just local to the callback function.
Putting it all together:
def Calculator(root):
global Number1, Number2, Operator
calculator = Toplevel()
Number1 = DoubleVar()
Number2 = DoubleVar()
Operator = IntVar()
Operator.set(1)
Entry(calculator,textvariable=Number1,justify="c").grid()
Entry(calculator,textvariable=Number2,justify="c").grid()
Radiobutton(calculator,text="Add",variable=Operator,value=1).grid()
Radiobutton(calculator,text="Subtract",variable=Operator,value=2).grid()
Radiobutton(calculator,text="Multiply",variable=Operator,value=3).grid()
Radiobutton(calculator,text="Divide",variable=Operator,value=4).grid()
Radiobutton(calculator,text="Square",variable=Operator,value=5).grid()
Radiobutton(calculator,text="Square root",variable=Operator,value=6).grid()
Button(calculator,text="results", command=lambda event: calculate(calculator), width=16).grid()
def calculate(calculator):
n1=Number1.get()
n2=Number2.get()
op=Operator.get()
if op==1:Results=n1+n2
if op==2:Results=n1-n2
if op==3:Results=n1*n2
if op==4:Results=n1/n2
if op==5:Results=math.pow(n1,n2)
if op==6:Results=n1*(1/n2)
Results = "The answer is "+str(Results)
calculator.destroy()
Answer = Toplevel()
Answer.geometry("150x150")
Label(Answer, text=Results).place(relx=.5,rely=.5,anchor="center")
root = Tk()
# Presumably your real code has some top-level stuff, where the
# user can ask you to open a calculator, like a button whose
# command calls the Calculator function? But here, we'll just:
Calculator(root)
root.mainloop()

Python, Tkinter: store content of Entry field into a variable

Dear fellow programmers,
I use Python 2.7 on windows 10 64 bits.
I have an issue with a Tkinter window. In a parent program, I want to save a file and I ask the name of the file in a Tkinter window. My problem is that I don't succeed to get this name outside of the Tkinter window. Here is the Python code:
from Tkinter import *
globalFilename = ""
class Master:
def __init__(self, top):
self.filename = ""
frame_e = Frame(top)
frame_e.pack()
self.t_filename = StringVar()
entry = Entry(frame_e, textvariable=self.t_filename, bg="white")
entry.pack()
entry.focus_force()
saveButton = Button(frame_e, text="Save", command=self.on_button)
saveButton.pack(side=BOTTOM, anchor=S)
def on_button(self):
self.filename = self.t_filename.get()
print self.filename
root.quit()
root.destroy()
root = Tk()
root.geometry("100x100+100+50")
M = Master(root)
print M.filename
root.mainloop( )
print M.filename
globalFilename = M.filename
print globalFilename
All print statements in this code give nothing when I enter any text into the Entry textbox. This is not what I expect. If I enter "test" I expect "test" to appear for each print statement (i. e. four times here). I tried to go everywhere on the Internet, I tried various tutorials, I tried to copy various examples, to follow various videos, I just don't succeed in fixing this issue.
Note that this piece of code is embedded into a function called saveGame, which is used in a pygame loop.
Thanks in advance! All the best!
Your code works.
The window is not drawn on the screen until you call mainloop(), so printing M.filename before that point prints an empty string (the initialization value). The mainloop() blocks until the window closes, after which 3 print statements successfully print the value that the user entered into the box.
You may be interested in the easygui module, which does exactly what your program does except you don't have to make it yourself.
Ok, as other posters told, the above code works in a vacuum. It did not work as embedded in my program because I initialized a duplicate tk() before calling my function and initialize it again. I removed this duplicate and it worked.

Python: Closing a sub-child-window prevents the opening of a new sub-child

For my job in a laboratory of my University of Applied Sciences I need to create a Python-programm which creates a child-windows with the possibility to create another one.
So far this works quite fine.
The tricky thing is where I close the childrens child and try to open a new "grandchild" of the main-window.
Closing and opening also works fine on the level of the first child. I can enter that child, go back to the main menu and so on as long I wish.
Here the code I am working on right now:
import tkinter
def Praktika():
global Praktika
Praktika = tkinter.Toplevel(main)
Praktika.geometry("320x200")
Prak1 = tkinter.Button(Praktika, text="Praktikum 1", command =Praktikum1)
Prak1.pack()
Haupt = tkinter.Button(Praktika, text="Hauptmenu", command = ClosePraktika)
Haupt.pack()
def ClosePraktika():
Praktika.destroy()
def Praktikum1():
global Praktikum1
Praktikum1 = tkinter.Toplevel(main)
Praktikum1.geometry("320x200")
Haupt = tkinter.Button(Praktikum1, text="Hauptmenu", command = ClosePraktikum1)
Haupt.pack()
def ClosePraktikum1():
Praktika.destroy()
Praktikum1.destroy()
def CloseAll():
main.quit()
main = tkinter.Tk()
main.geometry("320x200")
main.title("Fueh")
tkinter.Button(main, text="Praktika", command=Praktika).pack()
tkinter.Button(main, text="Exit", command=CloseAll).pack()
main.mainloop()
This is now the third attempt until now and ffter the research I have done I start to think that handling sub-children ain't that easy as I think.
So well,
already thank you very much for the help!
The problem is that you have a function named Praktikum1, and then you create a global variable named Praktikum1 which causes the function to be destroyed. So, the next time you call the function, you're actually "calling" the variable.
Don't use the same name for global variables and for functions.

Categories

Resources