Hey so i'm making a program that has a checkbutton on the main window and a toplevel window that has one aswell. the problem is that for some reason the toplevel checkbutton affects the state of the main checkbutton, or the main checkbutton mimics the top level one (if you check/uncheck the toplevel one, the main one checks/unchecks aswell). Here's an example code which displays the problem:
import tkinter as tk
def toplevel():
top = tk.Toplevel()
top.geometry('200x50')
top_chekbutton = tk.Checkbutton(top, text='top')
top_chekbutton.pack()
top.mainloop()
main = tk.Tk()
main.geometry('200x50')
open_top = tk.Button(main, text='open top', command=toplevel)
main_checkbutton = tk.Checkbutton(main, text='main')
main_checkbutton.pack()
open_top.pack()
main.mainloop()
i didn't define the state variables because they don't seem to be the source of the problem. i'm using python 3.7.7 and tkinter 8.6 on win10.
plz help :(
As a general rule of thumb, every instance of Checkbutton should have a variable associated with it. If you don't, a default value will be used that is identical for all Checkbuttons. All widgets that share the same variable will display the same value.
You can verify this yourself by printing out the value of top_chekbutton.cget("variable") and main_checkbutton.cget("variable"). In both cases the value is "!checkbutton" (at least, with the version of python I'm using).
So, assign a variable for your checkbuttons, such as a BooleanVar, IntVar, or StringVar.
main_var = tk.BooleanVar(value=False)
main_checkbutton = tk.Checkbutton(main, text='main')
Related
I'm somewhat new to python (started in Nov.) and after complete my first "program" I'm trying to built the GUI using Tkinter. I want to put the program on a Toplevel that I've created and have it run, but all Tkinter tutorials only talk about widgets and I don't know how to specify that a code should run on a specific Toplevel window. The best I can figure is to run the in the section where I define the Toplevel as shown in the example below, but that is not working.
from tkinter import *
import tkinter as tk
root=Tk()
root.geometry("500x200")
root.title('Test')
Label(root, text="Test").pack()
def test():
gen_win = Toplevel(root)
gen_win.title("Test")
gen_win.geometry("500x500")
Label(gen_win, text="Test").pack()
print(2+2)
btn_test=tk.Button(root, text="test", command=test).pack(fill=tk.X)
root.mainloop()
The example program (print(2+2)) doesn't print on the toplevel. Any ideas?
#jasonharper gave the correct answer:
"Code doesn't "run on a specific Toplevel window". It just runs, and if it happens to create a widget, or modify the contents of an existing widget, that change becomes visible as soon as your code returns to the mainloop. Label(gen_win, text=str(2+2)).pack() would be the simplest way to make your addition results visible in the window."
I have created a tkinter application where the user can make multiple toplevel windows and have the option of closing them from inside the toplevel. I would like to make a button on the main window that closes all toplevel windows. How would I do this? Is there a way to do this without lists? If these toplevels are parts of classes is there also a way to call a function present in all of them?
Here's how to do the first part of your question about making a button in the main window to delete all the Toplevels without making a list of them. This works by using the universal winfo_children() widget method to find all the child widgets of the root (main) window.
It's unclear to me what you meant about calling a function present in all of them — Toplevel widgets are instances of a predefined tkinter class which supports a predefined set of methods — and you can call them the same way the sample code below does with child.destroy().
import tkinter as tk
root = tk.Tk()
root.title('Main')
root.geometry('200x100')
def close_all(master):
for child in master.winfo_children():
if isinstance(child, tk.Toplevel):
child.destroy() # Call method.
button = tk.Button(root, text=f"Close Toplevels",
command=lambda master=root: close_all(master))
button.pack()
for i in reversed(range(4)): # Create them bottom to top.
toplevel = tk.Toplevel()
toplevel.title(f'Toplevel {i+1}')
toplevel.geometry('200x75')
toplevel.lift()
button = tk.Button(toplevel, text="Close me", command=toplevel.destroy)
button.pack()
root.mainloop()
I put the checkbutton on the text widget, but everytime I select a checkbutton, the function checkbutton_value is called, and it returns 0.
Part of the code is :
def callback():
file_name=askopenfilename()
column_1rowname,column_name=draw_column(file_name)
root = Tk()
root.resizable(width=False,height=False)
root.wm_title("Column")
S = Scrollbar(root,orient="vertical")
text=Text(root,width=15,height=10,yscrollcommand=S.set)
S.config(command=text.yview)
S.pack(side="right",fill="y")
text.pack(side="left",fill="both",expand=True)
#check the value of the checkbutton
def checkbutton_value():
if(var.get()):
print 1
else:
print 0
var=BooleanVar()
chk = Checkbutton(root, text=column_1rowname[1], variable=var, command=checkbutton_value)
text.window_create("end", window=chk)
text.config(state=DISABLED)
errmsg='Error!'
Button(text='File Open',command=callback).pack(fill=X)
mainloop()
The problem is that you have more than one root window. You should only ever create exactly one instance of Tk, and call mainloop exactly once. If you need additional windows, create instances of Toplevel.
Each root window (and all of its children, and all related StringVars etc.) start a new, independent tcl interpreter. Widgets and variables associated with this window can't be used in another tcl interpreter. In your case, the StringVar is associated with the first root window, but the widget is associated with the second. You can't share data between root windows like that.
I was checking out Toplevel of tkinter. From what I've seen from effbot I can omit its parent argument.
1- When I only use Toplevel itself (commenting out root), it creates its own parent I believe since two windows appear and only destroys one after clicking button.
2- If I don't comment out Tk(), it works fine. Two windows, one root - one toplevel and destroys toplevel.
3- If I interchange root and toplevel, first toplevel creates two again(like in first case), then root will create another so three windows will appear and only toplevel gets destroyed.
import tkinter as tk
#root = tk.Tk()
top = tk.Toplevel()
#root.title("Foo")
top.title("Bar")
top.geometry("300x100")
tk.Button(top, text = "Destroy", command=top.destroy).pack()
top.mainloop()
Question is, is there a way to create toplevel before Tk() and get only one window or access its parent and destroy it?
p.s. I found these two questions Toplevel in Tkinter: Prevent Two Windows from Opening && tkinter child window opens two windows?. First question is in 2nd case which is not what I want, and second question has no answer yet and his problem kind of not reproducable.
Also, I tried to get its master value -to destroy is manually- like this but seems like that value is not stored in dictionary where options are stored.
btn = tk.Button(top, text = "Destroy", command=top.destroy)
btn.pack()
print (btn["text"])
>>> Destroy
print (btn["master"])
>>> _tkinter.TclError: unknown option "-master"
It's not that Toplevel creates it's own parent, any widget will create a root window if you don't create one first. There simply must be a root window before any other widget can exist -- that's why it's called a root window. So, to answer your specific question, no, there is no way to create an instance of Toplevel without creating a root window first.
The following Python 3 code has a label and an Entry field that are correctly initialized with a string ("junk" in the example). But when the second "import" line is uncommented to replace the old Entry widget with the new themed widget, the label and Entry fields are not initialized.
Any clue why the themed widget initialization is broken?
from tkinter import *
# from tkinter.ttk import *
class myApp:
def __init__(self, root):
v = StringVar()
v.set("junk")
label = Label(root, textvariable=v)
label.pack()
text_entry = Entry(root, textvariable=v)
text_entry.pack()
root = Tk()
root.title("MyApp")
app = myApp(root)
root.mainloop()
The problem is that v is a local variable. When it goes out of scope (ie: when __init__ finishes executing), v is getting garbage-collected. Change v to self.v and the problem goes away.
Why you see the problem with the ttk Entry widget and not the standard one, I don't know. I guess one is just more sensitive to the garbage collector, or perhaps importing both libraries somehow triggers the garbage collector sooner. Regardless, even with the stock widgets you would eventually have some sort of problem because v will always eventually get garbage-collected.