Python 3 tkinter destroy all child windows at exit of root - python-3.x

SO i have been working on this for a while now and i just have no idea what i am doing wrong. I have a main window my 'root' but i also have a 'helpmenu'. right now, when you close the root helpmenu will remain opened if you opened it. i want helpmenu to close when the root closes.
i have tried to cutout all the lines of code from my program which i believed had no baring on the situation but forgive me if it is still long.
import atexit
from tkinter import *
root = Tk()
def quit():
helpmenu.destroy()
atexit.register(quit)
def helpmenu():
helpmenu = Tk()
helpmenu.geometry('800x600')
helpmenu.title('Help')
help_Label = Label(helpmenu, text='Welcome to the Boring Program!')
help_Label.pack()
class GUI(object):
def __init__(self, master):
self.master = master
def activateCheck1():
if c1Var.get() == 1: #whenever checked
c3.config(state=NORMAL)
elif c1Var.get() == 0: #whenever unchecked
c3.config(state=DISABLED)
def activateCheck2():
if c2Var.get() == 1: #whenever checked
c4.config(state=NORMAL)
elif c2Var.get() == 0: #whenever unchecked
c4.config(state=DISABLED)
c1Var = IntVar()
c2Var = IntVar()
c1_Label = Label(root, text="Select Which Edges to Program")
c1_Label.grid(row=2, columnspan=2)
c1 = Checkbutton(root, text="Left Edge", variable=c1Var, command=activateCheck1)
if parser.get('CONFIGURATION', 'LeftEdgeProgram') == '1':
c1.select()
c1.grid(row=3, column=0)
c2 = Checkbutton(root, text="Right Edge", variable=c2Var, command=activateCheck2)
if parser.get('CONFIGURATION', 'RightEdgeProgram') == '1':
c2.select()
c2.grid(row=3, column=1)
c3Var = IntVar()
c4Var = IntVar()
c3_Label = Label(root, text="Select Which Edges to Insert Glue and Dowels")
c3_Label.grid(row=4, columnspan=2)
c3 = Checkbutton(root, text="Left Edge", variable=c3Var)
if parser.get('CONFIGURATION', 'LeftEdgeDowel') == '1':
c3.select()
c3.grid(row=5, column=0)
c4 = Checkbutton(root, text="Right Edge", variable=c4Var)
if parser.get('CONFIGURATION', 'RightEdgeDowel') == '1':
c4.select()
c4.grid(row=5, column=1)
menubar = Menu(root)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Print", state='disabled')
filemenu.add_command(label="Save Configuration", command=save_config)
filemenu.add_command(label="Help", command=helpmenu)
filemenu.add_command(label="Restore Defaults", state='disabled')
filemenu.add_command(label="About", command=restore_config)
menubar.add_cascade(label="Options", menu=filemenu)
root.config(menu=menubar)
myGUI = GUI(root)
root.mainloop()

Firstly you are creating two instances of a tk() windows. Creating two tk() windows almost never should be done, this is because two tk windows cannot communicate with eachother. Instead you should use a TopLevel window.
You should also change your HelpMenu to a class object which inherits the TopLevel Class
This should look something like this:
class HelpMenu(Toplevel):
def __init__(self, master, *args, **kwargs):
super(HelpMenu, self).__init__(master, *args, **kwargs)
self.geometry('800x600')
self.title('Help')
help_Label = Label(self, text='Welcome to the Boring Program!')
help_Label.pack()
The only thing that now needs to be changed is the command for the help option in the toolbar
Something like this (notice I am using a lambda function to make this change):
filemenu.add_command(label="Help", command= lambda:HelpMenu(self.master) )
This will fix your problem, because when the master window of a TopLevel window is closed the TopLevel window is automatically closed.

Related

How to check if the mouse button is released outside the button? Python

I am trying to assign some behaviors to the buttons, some I have already achieved like:
Change the color of the button if the mouse is positioned over it.
Restore the default button color.
Save the last button pressed in green.
Today I realized that when I press a button without releasing the click, and I move the mouse pointer off the button and release the click, it turns green, but without having executed the linked function, I would like the button not to change color . I am trying to eliminate this behavior, but I have no ideas. The code is executable, it works with python 3.7. Thanks.
from tkinter import *
class TypeButton(Button):
def __init__(self, parent, *args, **kwargs):
kwargs = {'font':('Calibri',9,'bold'), 'bg': '#11161d', 'fg':'white',
'width':10, 'bd':0, 'activebackground':'#bdfe04', **kwargs}
super().__init__(parent, *args, **kwargs)
class Example(Frame):
def __init__(self,parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.frame = Frame(self, bg='#11161d')
self.frame .grid(padx=(10,10), pady=(6,6))
self.container1 = None
self.creator_buttons()
#????????????????
self.bind_all("<B1-Motion>", self.callback)
def creator_buttons(self):
mobiles = [['Frog', 'Fox', 'Boomer', 'Ice', 'J.d', 'Grub', 'Lightning', 'Aduka', 'Knight', 'Kalsiddon', 'Mage'],
['Randomizer', 'Jolteon', 'Turtle', 'Armor', 'A.sate', 'Raon', 'Trico', 'Nak', 'Bigfoot', 'Barney', 'Dragon']]
self.mobiles2 = ['Fox','Knight','Jolteon','Barney','Dragon']
self.buttons22 = []
for index1, mobil in enumerate(mobiles):
for index2, texto in enumerate(mobil):
number = 11 if index1 == 1 else 0
btn = B1ButtonCls (self.frame_1, text=texto, command= self.callback)
n1 = 5 if index2 == 0 else 0
n2 = 5 if index2 == 10 else 0
btn .grid(column=index2 , row=index1 , pady=3, padx=(n1,n2))
btn.bind("<Enter>", self.enter_mouse)
btn.bind("<Leave>", self.leave_mouse)
btn.bind("<Button-1>", self.clic_mouse)
if texto in self.mobiles2: btn.config(fg='yellow')
self.buttons22.append(btn)
def enter_mouse(self, event):
widget1 = event.widget
if not widget1 .cget('bg') == '#bdfe04':
widget1 .config(bg="#24364a")
def leave_mouse(self, event):
if not event.widget .cget('bg') == '#bdfe04':
event.widget.config(bg='#11161d')
def clic_mouse(self, event):
widget1 = event.widget
widget1.config(bg='#bdfe04', fg='black')
if self.container1 is not None and self.container1 != widget1:
if self.container1 .cget('text') in self.mobiles2:
self.container1 .config (bg='#11161d', fg='yellow')
else:
self.container1 .config (bg='#11161d', fg='white')
self.container1 = widget1
def callback(self):
print('Closed')
root = Tk()
app = Example(root).pack()
root.mainloop()
You can use the winfo_containing method to know which widget is under the mouse when you release a button. You can then compare the result of that function call to the widget that was clicked on.
Here's an example that displays one of two text messages depending on whether or not you released the mouse button over the same widget that was clicked on.
import tkinter as tk
def _button_press(event):
label.configure(text="")
def _button_release(event):
widget_under_cursor = event.widget.winfo_containing(event.x_root, event.y_root)
if widget_under_cursor == event.widget:
label.configure(text="you released over the button")
else:
label.configure(text="you did not release over the button")
root = tk.Tk()
label = tk.Label(root, width=40)
label.pack(side="top", fill="x")
for i in range(10):
button = tk.Button(root, text=f"Button #{i+1}")
button.pack()
button.bind("<ButtonPress-1>", _button_press)
button.bind("<ButtonRelease-1>", _button_release)
root.mainloop()

How to close current tkinter window and open a new tkinter window when the user inputs test into a tkinter Entry widget

I would like to close the current tkinter window, and open a new tkinter window when the user inputs text into an entry widget. That might sound confusing so let me explain.
First tkinter window:
When the user inputs text into the Entry widget, I would like this current window to close and the second window to open with the text the user entered displayed in a label widget.
Second window:
Here is my current code:
from tkinter import *
user_input = ''
class Startup_window:
def __init__(self, master):
self.master = master
master.title('Window 1')
def get_input(event):
global user_input
# Gets the user input from the Entry widget
user_input = self.input.get()
# Clears Entry widget
self.input.delete(0, END)
master.destroy()
self.label = Label(master, text='Input:')
self.label.grid(row=0, column=0)
self.input = Entry(master)
self.input.grid(row=0, column=1)
self.input.focus()
self.input.bind("<Return>", get_input)
class Main_window:
def __init__(self, master):
self.master = master
master.title('Window 2')
self.label = Label(master, text="You've entered (user_input)" + user_input)
self.label.pack()
root = Tk()
startup_window = Startup_window(root)
if user_input != '':
main_window = Main_window(root)
mainloop()
I am new to tkinter and object oriented programming so any help would be greatly appreciated!
This would work for you. As #CoolCloud mentioned, the if condition will be checked only once. Because mainloop() actually waits for events to be triggered (maybe through some buttons or whatever), NOT that it executes the blocks of code over and over again
import tkinter as tk
class MainApp():
def __init__(self, root):
self.root = root
self.inputBox = tk.Entry(self.root)
self.inputBox.pack()
self.inputBox.bind('<Return>', self.checkInput)
def checkInput(self, args):
if self.inputBox.get() != '':
userVal = self.inputBox.get()
self.root.destroy() # try root.quit if it doesn't work
newWindow = tk.Tk()
myLabel = tk.Label(newWindow, text='You entered: ' + userVal)
myLabel.pack()
win = tk.Tk()
MainApp(win)
win.mainloop()

How can I access button click from main in python, tkinter?

I'm new to python and tkinter GUI, I need to access button click action from the main function, is there any possibility to do that? any way I can access it in click function inside the class that I have comment it. what is the correct method to do it? thank you
from tkinter import *
class Youtube:
def __init__(self,master):
self.master = master
master.title('Youtube_Downloder')
self.screen = Entry(master, state='normal', width=25, background="White", foreground="blue",
font=('Arial', 12))
self.screen.grid(row=1, column=0, columnspan=4, padx=5, pady=5)
self.layout = Label(master, text='Past the Link below', font=('Arial', 16),foreground="red")
self.layout.grid(row=0, column=1)
self.create_button('Add Link',1,30)
def create_button(self,val,row,column,width=12):
button = Button(self.master, text=val, width=width, command=lambda: self.click(val))
return button.grid(row=row,column=column)
def click(self,clicked_button=None):
if clicked_button == 'Add Link':
#self.create_label(self.screen.get(), 3, 1) #this methord working
return 'Add Link'
def create_label(self,text,row,column,font=('Arial',8)):
return Label(self.master, text=text, font=font).grid(row=row, column=column)
def main():
root = Tk()
my_gui = Youtube(root)
if my_gui.click() == 'Add Link':
my_gui.create_label(my_gui.screen.get(), 3, 1)
root.mainloop()
if __name__ == '__main__':
main()
I'm assuming it could be this button:
def create_button(self,val,row,column,width=12):
button = Button(self.master, text=val, width=width, command=lambda: self.click(val))
return button.grid(row=row,column=column)
Change button to self.button. When you've done that you can access it in main by using my_gui.button.
If you ever decide to use an mvc model, this will be the approach as well.

Using Tkinter to disable entry with specified input

I would like to use Tkinter to be able to disable one entry if 'no' is selected from a drop down menu.
from tkinter import *
def disableEntry(entry):
entry.config(state='disable')
def allowEntry(entry):
entry.config(state='normal')
def main():
print("test")
root = Tk() #create a TK root window
root.title("Lunch and Learn") #Title of the window
L1 = Label(root, text = "Label 1").grid(row=0, column=0, padx=30, pady=(20,5))
L2 = Label(root, text = "Label 2").grid(row=1, column=0, pady=5)
var = StringVar()
E1 = Entry(root,bd =3)
E1.grid(row=0, column=1)
D1 = OptionMenu(root,var,"yes","no")
D1.grid(row=1,column=1)
if var.get() == 'no':
disableEntry(E1)
elif var.get() == 'yes':
allowEntry(E1)
B2 = Button(text = "Submit", command=main).grid(row=4, column=2)
root.mainloop()
the above code is a simple example of what i have tried. I have created two functions called 'disableEntry' and 'allowEntry' which should change the state of the entry box but they don't appear to do anything when i change the input of the drop down menu.
i dont know if i am approaching this the wrong way or if there is a standardized way to do this.
any help would be appreciated.
You need a way to check the state of the selection after it is changed. That can be achieved with adding a callback command to the OptionMenu widget.
You were checking the correct variable, but the point you were checking it at was before the screen window had even displayed.
from tkinter import Label, StringVar, OptionMenu, Entry, Tk, Button
# change the state of the Entry widget
def change_state(state='normal'):
E1.config(state=state)
def main():
print("test")
# callback function triggered by selecting from OptionMenu widget
def callback(*args):
if var.get() == 'no':
change_state(state='disable')
elif var.get() == 'yes':
change_state(state='normal')
root = Tk() #create a TK root window
root.title("Lunch and Learn") #Title of the window
L1 = Label(root, text="Label 1").grid(row=0, column=0, padx=30, pady=(20, 5))
L2 = Label(root, text="Label 2").grid(row=1, column=0, pady=5)
var = StringVar()
E1 = Entry(root, bd=3)
E1.grid(row=0, column=1)
D1 = OptionMenu(root, var, "yes", "no", command=callback)
D1.grid(row=1, column=1)
B2 = Button(text = "Submit", command=main).grid(row=4, column=2)
root.mainloop()

Why doesn't the program wait for the function result? [duplicate]

I have a tkinter class:
class DBCreatorWin():
def closeWindow(self):
tkMessageBox.showinfo("Ilmiont SQLite Database Manager", "This window cannot be closed.\nEnter a database name and press Continue.")
def returnName(self):
dbName = self.entry.get()
self.window.destroy()
return dbName
def __init__(self):
self.window = Toplevel()
self.window.transient(tkRoot)
self.window.grab_set()
self.window.resizable(width=False, height=False)
self.window.title("Ilmiont SQLite Database Manager")
self.window.protocol("WM_DELETE_WINDOW", self.closeWindow)
self.label = Label(self.window, text="Enter the name of the database to be created: ")
self.entry = Entry(self.window, width=30)
self.button = Button(self.window, text="Continue", command=self.returnName)
self.label.grid(row=0, column=0)
self.entry.grid(row=0, column=1)
self.button.grid(row=1, column=0, columnspan=2)
I want to create an instance of this class within my main code and wait for the return value. The user types a name into the entry field and presses the Continue button. At that point, the value should be returned to where the class was originally instantiated. How do I go about this? I can't seem to make it work in a normal way and am new to tkinter.
Thanks in advance,
Ilmiont
There are a couple of ways to do this. The basic idea is to use a tkinter method to wait for a specific event before returning. Tkinter provides two methods to do just that: wait_window and wait_variable. The most common method is to open a window and then wait for it to be destroyed. Some good examples can be found on the effbot site, on a page titled Dialog Windows.
Here's a simple illustration. It's not production-ready, but illustrates the general idea. At the very least you'll want to add a grab on the dialog so that you can't interact with the main window while the dialog is open, since you said you want the dialog to be modal.
import Tkinter as tk
class MyDialog(object):
def __init__(self, parent):
self.toplevel = tk.Toplevel(parent)
self.var = tk.StringVar()
label = tk.Label(self.toplevel, text="Pick something:")
om = tk.OptionMenu(self.toplevel, self.var, "one", "two","three")
button = tk.Button(self.toplevel, text="OK", command=self.toplevel.destroy)
label.pack(side="top", fill="x")
om.pack(side="top", fill="x")
button.pack()
def show(self):
self.toplevel.deiconify()
self.toplevel.wait_window()
value = self.var.get()
return value
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.button = tk.Button(self, text="Click me!", command=self.on_click)
self.label = tk.Label(self, width=80)
self.label.pack(side="top", fill="x")
self.button.pack(pady=20)
def on_click(self):
result = MyDialog(self).show()
self.label.configure(text="your result: %s" % result)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
You can't.
The whole way the tkinter works is with callbacks. The command that you're using is the callback and you'll have to use the value inside the class. Here is an example:
def do_stuf(self):
tkMessageBox.showinfo("Foo", returnName())
....................
self.button = Button(self.window, text="Continue", command=self.do_stuff)

Resources