This is my first question, I am new to python and this site.
I am designing an app to interact with a database. I added a "close" button that I would like to open a new window asking "close the program?" and 2 buttons: yes and no. When you click no, the new window closes. When you click yes both windows close.
I got my code working but I am quite sure there is a better or smarter way of doing it.
To make it work I had to write "root.destroy()" in the "close_window" method but I am pretty sure there is a smarter way of getting the same result with something like "self.master.destroy()" that uses all the power of python. I show a simplified version of the code below.
Thanks in advance.
Alfonso
from tkinter import *
class Window():
def __init__(self, main):
self.main = main
self.b5=Button(self.main, text="Action 1", width=12)
self.b5.grid(row=0, column=1)
self.b5=Button(self.main, text="Action 2", width=12)
self.b5.grid(row=0, column=2)
self.b6=Button(self.main, text="Close", width=12, command=self.new_window)
self.b6.grid(row=0, column=3)
def new_window(self):
self.newWindow = Toplevel(self.main)
self.app = Demo2(self.newWindow)
def close_window(self):
root.destroy()
class Demo2:
def __init__(self, master):
self.master = master
self.frame = Frame(self.master)
self.l1=Label(self.frame, text="Close the program?")
self.l1.grid(row=0, column=0, columnspan=2)
self.b1=Button(self.frame, text="Yes", command=self.yes_com)
self.b1.grid(row=1, column=0)
self.b1=Button(self.frame, text="No", command=self.no_com)
self.b1.grid(row=1, column=1)
self.frame.pack()
def yes_com(self):
self.master.destroy()
Window.close_window(self)
def no_com(self):
self.master.destroy()
def main():
global root
root = Tk()
app = Window(root)
root.mainloop()
if __name__ == '__main__':
main()
You could simply use the standard dialogs provided by the messagebox module.
Specifically, it provides the askyesno dialog that takes care of the opening/closing of the new window and returns True if the user clicks Yes and False if the user clicks No. Then, you could simply use an if-statement to close the window if the user says so by simply using self.main.destroy() (without the need to declare root as global).
from tkinter import *
from tkinter import messagebox
class Window():
def __init__(self, main):
self.main = main
self.b5=Button(self.main, text="Action 1", width=12)
self.b5.grid(row=0, column=1)
self.b5=Button(self.main, text="Action 2", width=12)
self.b5.grid(row=0, column=2)
self.b6=Button(self.main, text="Close", width=12, command=self.close_window)
self.b6.grid(row=0, column=3)
def close_window(self):
if messagebox.askyesno('Close', 'Close the program?'):
self.main.destroy()
def main():
root = Tk()
app = Window(root)
root.mainloop()
if __name__ == '__main__':
main()
Notes
The yes/no buttons in the standard dialog are localized, meaning that they are translated into the language used by your computer settings.
If you have a look around the messagebox module, you'll see that there are other standard dialogs. IMHO in this case I would use askyescancel, which is used in the exact same way, but seems more semantic.
Pier Paolo has the right answer, but for the sake of science, here's the direct answer to your question:
def yes_com(self):
self.master.master.destroy() # <this class instance>.<toplevel instance>.<Tk instance>.destroy()
Or you could simply quit python:
import sys
def yes_com(self):
sys.exit()
Related
How can I make a command to open second window and close the main window in python.
The code can open the next window but it cant close the main window..
This is the code:
from tkinter import *
def main():
root = Tk()
app = first(root)
class first:
def __init__(self, master):
self.master = master
self.master.title("First")
tit = Label(self.master, text="Im the first page", font=20)
tit.place(relx=0.5, rely=0.4, anchor=CENTER)
sec = Button(self.master, text="Second", width=20, height=10,
bg="black", fg="white", command=self.second_open)
sec.place(relx=0.5, rely=0.5, anchor=CENTER)
def second_open(self):
self.secondwindow = Toplevel(self.master)
self.app = second(self.secondwindow)
class second:
def __init__(self, master):
self.master = master
self.master.title("Second")
tit = Label(self.master, text="Im the second page", font=20)
tit.place(relx=0.5, rely=0.4, anchor=CENTER)
if __name__ == "__main__":
main()
mainloop()
tkinter is designed for you to create one root window, and keep that window until the program ends. That makes it difficult to do what you ask.
A better solution in many cases is to just keep and reconfigure the main window instead of destroying it and creating a new window.
Here's an example based on the code in the question:
def second_open(self):
for child in self.master.winfo_children():
child.destroy()
self.secondwindow = second(self.master)
My aim is to generate a window with a button "NewWindow" on it. If I press the button, the program should open a new window. This window I stored in a class "NewWindow" to quickly reproduce it.
In this "NewWindow" I have another button. If I press it the label of basic window should be updated and the window "NewWindow" should be closed automatically.
Here is my code:
from tkinter import *
class NewWindow(Toplevel):
def __init__(self, master = None):
super().__init__(master = master)
self.title('NewWindow')
self.lb = Label(self, text='Hello')
self.lb.grid(column=0, row=0, columnspan=1)
self.bt1 = Button(self, text="apply Hello", command= self.bt_press)
self.bt1.grid(column=0, row=1)
def bt_press(self):
window.basic_lb.text = "Hello"
window = Tk()
def new_Editor():
a = NewWindow(window)
window.title("BasicWindow")
window.basic_lb = Label(window, text='None')
window.basic_lb.grid(column=0, row=0, columnspan=1)
window.basic_bt = Button(window, text="NewWindow", command=new_Editor)
window.basic_bt.grid(column=0, row=1)
window.mainloop()
Problems:
At start both windows NewWindow and BasicWindow are displayd. I only want to open BasicWindow and NewWindow should be opened after button basic_bt is clicked. How can I solve it? (already solved by commed below)
Why the label text in basic_lb did not get some update after pressing self.bt1?
How is it possible to close NewWindow with use of bt_press method?
You have a few typos/errors in your code that are casuing some of your problems. As #Tim said, when you pass a function to a command like command=function(), it will be called on runtime, not when the button is pressed. You need to pass the function handle to the command, command=function. You got around this by using a lambda function in your button command, but it is easier to just have command=self.bt_press
Answering your second question, window.basic_lb.text = "Hello" is not how you change the text in a tkinter Label, use <Label>.config(text="Hello"). You also should use self.master and define self.master = master in __init__ instead of just using window, because while you can access window due to it not being defined in local scope, it's better to explicitly define it.
You can close a window using window.destroy().
Your working code is now:
from tkinter import *
class NewWindow(Toplevel):
def __init__(self, master = None):
super().__init__(master = master)
self.title('NewWindow')
self.master = master
self.lb = Label(self, text='Hello')
self.lb.grid(column=0, row=0, columnspan=1)
self.bt1 = Button(self, text="apply Hello", command=self.bt_press)
self.bt1.grid(column=0, row=1)
def bt_press(self):
self.master.basic_lb.config(text="Hello")
self.destroy()
window = Tk()
def new_Editor():
a = NewWindow(window)
window.title("BasicWindow")
window.basic_lb = Label(window, text='None')
window.basic_lb.grid(column=0, row=0, columnspan=1)
window.basic_bt = Button(window, text="NewWindow", command=new_Editor)
window.basic_bt.grid(column=0, row=1)
window.mainloop()
I'm currently using tkinter to create a GUI for my program. If I open the golf quiz window and open the help window, then close the golf quiz window and re-open it, I am able to click the help window button and open another instance of the help button. How do I set the Help button to be disabled while the Help window is open?
from tkinter import *
from functools import partial
class Welcome_Screen:
def __init__(self, parent):
self.welcome_screen_frame = Frame(width=200, height=200, pady=10)
self.welcome_screen_frame.grid()
self.quiz_welcome_screen_label = Label(self.welcome_screen_frame, text = "quiz game", font="Arial 20 bold", padx=10)
self.quiz_welcome_screen_label.grid(row=0)
self.welcome_screen_buttons_frame = Frame(self.welcome_screen_frame)
self.welcome_screen_buttons_frame.grid(row=2)
self.golf_quiz_welcome_screen_button = Button(self.welcome_screen_buttons_frame, text="Golf Quiz", font="Arial 10 bold", command=self.golf_quiz_game, padx=10, pady=10)
self.golf_quiz_welcome_screen_button.grid(row=2, column=0, padx=5)
def golf_quiz_game(self):
get_golf_quiz_game = golf_quiz_game(self)
class golf_quiz_game:
def __init__(self, partner):
partner.golf_quiz_welcome_screen_button.config(DISABLED)
self.golf_quiz_box = Toplevel()
self.golf_quiz_box.protocol('WM_DELETE_WINDOW', partial(self.close_golf_quiz_game, partner))
self.golf_quiz_frame = Frame(self.golf_quiz_box)
self.golf_quiz_frame.grid()
self.golf_quiz_heading = Label(self.golf_quiz_frame, text="Golf Quiz game",
font="arial 18 bold", padx=10, pady=10)
self.golf_quiz_heading.grid(row=0)
self.golf_quiz_history_help_dismiss_buttons_frame = Frame(self.golf_quiz_frame)
self.golf_quiz_history_help_dismiss_buttons_frame.grid(row=6, pady=10)
self.help_button = Button(self.golf_quiz_history_help_dismiss_buttons_frame, text="Help", font="Arial 10 bold",command=self.Help, padx=10, pady=10)
self.help_button.grid(row=6, column=1, padx=5)
def close_golf_quiz_game(self, partner):
partner.golf_quiz_welcome_screen_button.config(state=NORMAL)
self.golf_quiz_box.destroy()
def Help(self):
get_help = Help(self)
class Help:
def __init__(self, partner):
partner.help_button.config(state=DISABLED)
self.help_box = Toplevel()
self.help_box.protocol('WM_DELETE_WINDOW', partial(self.close_Help, partner))
self.help_frame = Frame(self.help_box)
self.help_frame.grid()
self.help_heading = Label(self.help_frame, text="Help", font="arial 18 bold")
self.help_heading.grid(row=0)
self.help_text = Label(self.help_frame, text="Test",
width=60, wrap=400)
self.help_text.grid(row=1)
self.help_button = Button(self.help_frame, text="Dismiss", width=10, font="Arial 10 bold", command=partial(self.close_Help, partner), padx=10, pady=10)
self.help_button.grid(row=2, pady=10)
def close_Help(self, partner):
if partner.help_button.winfo_exists():
partner.help_button.config(state=NORMAL)
self.help_box.destroy()
# main routine
if __name__ == "__main__":
root = Tk()
root.title("quiz game")
something = Welcome_Screen(root)
root.mainloop()
Here is how you can do that (this is also the size (linewise (approx.)) of the minimal reproducible example which you should have provided):
from tkinter import Tk, Toplevel, Button
def close_top(top):
btn.config(state='normal')
top.destroy()
def open_help():
btn.config(state='disabled')
top = Toplevel(root)
top.protocol('WM_DELETE_WINDOW', lambda: close_top(top))
top.focus_force()
# put the rest of help stuff here
root = Tk()
btn = Button(root, text='Help', command=open_help)
btn.pack()
root.mainloop()
Class based approach:
from tkinter import Tk, Toplevel, Button
# this would be the window from where you open the help window
class MainWindow(Tk):
def __init__(self):
Tk.__init__(self)
self.btn = Button(self, text='Help',
command=lambda: self.open_help(self.btn))
self.btn.pack()
def open_help(self, btn):
HelpWindow(self, btn)
# this would be the help window
class HelpWindow(Toplevel):
def __init__(self, master, button):
Toplevel.__init__(self, master)
self.button = button
self.button.config(state='disabled')
self.focus_force()
self.protocol('WM_DELETE_WINDOW', self.close)
def close(self):
self.button.config(state='normal')
self.destroy()
MainWindow().mainloop()
Few things:
First of you can simply inherit from container and window classes that way you don't have to separately create them in the class and you can easily reference them in the class using just self
.focus_force() does what it says, it forces focus on the widget
Important (suggestions)
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Don't have space around = if it is used as a part of keyword argument (func(arg='value')) but use if it is used for assigning a value (variable = 'some value'). Have two blank lines around function and class declarations.
You need to make use of class variables inside golf_quiz_game in order to have only one instance of Help window:
class golf_quiz_game:
# class variables
_help_button = None # reference to instance "Help" button
_help_win = None # reference to instance "Help" window
def __init__(self, partner):
...
# set button state based on whether "Help" window is open or not
self.help_button.config(state="normal" if self.__class__._help_win is None else "disabled")
# update class reference of "Help" button
self.__class__._help_button = self.help_button
def close_golf_quiz_game(self, partner):
partner.golf_quiz_welcome_screen_button.config(state=NORMAL)
self.golf_quiz_box.destroy()
# update class reference of "Help" button
self.__class__._help_button = None
def Help(self):
if self.__class__._help_win is None:
# no "Help" window is open, create one
self.__class__._help_win = Help(self)
def help_closed(self):
if self.__class__._help_button:
# enable the "Help" button
self.__class__._help_button.config(state="normal")
# update "Help" window status
self.__class__._help_win = None
class Help:
...
def close_Help(self, partner):
self.help_box.destroy()
# notify partner that "Help" window is closed
partner.help_closed()
I feel like I've scoured the web for an eternity, rephrased my question a thousand times for something I feel like should be very simple.
I wonder if there is a way to check if a Tkinter Widget is active (not greyed out / disabled). I have a set of OptionMenus that start out disabled, and are configured to state=ACTIVE when they click a checkbox, so that the user can select which OptionMenus they want to use.
When I try to "submit" the fields in the OptionMenus, I only want the ones that are ACTIVE. I already tried if OptionMenu.state == ACTIVE but then I get an error that OptionMenu has no attribute state, even though I configure that earlier.
Here is a sample of my code:
from tkinter import *
class Application(Frame):
# Initializing the window and the required variables
def __init__(self, master=None):
Frame.__init__(self, master)
self.checkbox_in_use = BooleanVar(self, False)
self.checkbox = Checkbutton(self, text="check",
var=self.checkbox_in_use,
command=self.check_change
self.checkbox.grid(row=0, column=1, sticky='W')
self.menu = OptionMenu(title_setting,
"Menu",
"Menu",
["Menu1", "Menu2"])
self.menu.grid(row=1, column=1)
self.menu.config(state=DISABLED)
submit = Button(self, text="submit",
command=self.submit_function)
submit.grid(row=2, column=0)
self.master = master
self.init_window()
# Initialize the window
def init_window(self):
self.master.title("Example")
self.pack(fill=BOTH, expand=1)
def check_change(self):
if self.checkbox_in_use.get():
self.menu.config(state=ACTIVE)
else:
self.menu.config(state=DISABLED)
def submit_function(self):
# This is the part I want to do something with.
if self.menu.state == ACTIVE:
print("You are good to go! Do the stuff.")
root = Tk()
root.geometry("400x300")
app = Application(root)
root.mainloop()
Thank you for all responses.
All you need is cget() for this. self.menu.cget('state') will do the trick.
That said I want to point out some other things in your code.
You Application class already has an __init__ at the start so why use:
# Initialize the window
def init_window(self):
self.master.title("Example")
self.pack(fill=BOTH, expand=1)
You really should not pack the frame from inside the frame class but rather when calling the class. Also pack wont work here it will throw an error. Do this instead: app = Application(root).grid().
Take a look at the reformatted example below (with cget()).
from tkinter import *
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master.title("Example")
self.checkbox_in_use = BooleanVar(self, False)
self.checkbox = Checkbutton(self, text="check", var=self.checkbox_in_use, command=self.check_change)
self.checkbox.grid(row=0, column=1, sticky='W')
self.menu = OptionMenu(master,"Menu","Menu",["Menu1", "Menu2"])
self.menu.grid(row=1, column=1)
self.menu.config(state=DISABLED)
Button(self, text="submit", command=self.submit_function).grid(row=2, column=0)
def check_change(self):
if self.checkbox_in_use.get():
self.menu.config(state=ACTIVE)
else:
self.menu.config(state=DISABLED)
def submit_function(self):
print(self.menu.cget('state'))
root = Tk()
root.geometry("400x300")
app = Application(root).grid()
root.mainloop()
So I am currently trying to create a button on a GUI that will let the user generate a new entry field.
I have no idea how to do this. I'm guessing that it will require a lambda function, but apart from that, I have no idea.
Here's the basic code I have so far:
from tkinter import *
class prac:
def autoAddWidget(self,frame,x,y):
self.entryField = Entry(frame,text="Entry Field")
self.entryField.grid(row=x, column=y)
#lambda function?
def __init__(self, master):
frame = Frame(master, width=60, height=50)
frame.pack()
x=1
self.addWidgetButton = Button(frame, text="Add new widget", command=self.autoAddWidget(frame, x,0))
self.addWidgetButton.grid(row=0, column=0)
x+=1
root = Tk()
app = prac(root)
root.mainloop()
Would appreciate the help.
Thanks
You're passing to the command argument result from the method self.autoAddWidget(frame, x,0) not method itself. You have to pass there a reference to a callable object, a function that will be called when the event occurs. Please check a documentation next time before you ask the question.
Ok, I fixed the code, now it works:
from tkinter import *
class Prac:
def autoAddWidget(self):
self.entryField = Entry(self.frame,text="Entry Field")
self.entryField.grid(row=self.x, column=0)
self.x+=1
def __init__(self, master):
self.frame = Frame(master, width=60, height=50)
self.frame.pack()
self.x=1
self.addWidgetButton = Button(self.frame, text="Add new widget", command=self.autoAddWidget)
self.addWidgetButton.grid(row=0, column=0)
root = Tk()
app = Prac(root)
root.mainloop()