Python tkinter checkbutton value always equal to 0 - python

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.

Related

Entanglement between Tkinter Checkbuttons

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')

Calling tk.StringVar.set() on a tk.Entry textvariable causes validate="focusout" to stop getting called

The question's in the title, essentially: how do I get the validatecommand callback to continue being called after setting the Entry's textvariable? Here's the Minimum Working Example (MWE):
import tkinter as tk
root = tk.Tk()
sv = tk.StringVar()
def callback():
print(sv.get())
sv.set('Set Text.')
return True
e = tk.Entry(root, textvariable=sv, validate="focusout",
validatecommand=callback)
e.grid()
e = tk.Entry(root)
e.grid()
root.mainloop()
Note that the second tk.Entry widget is there to allow the first one to lose focus, which is the event we're trying to capture.
As the code is now, when you run it, you can change the top Entry widget's text once. It'll correctly get set to Set Text. Then, if you try to change the Entry's text again, the new text will be in the widget, but the callback doesn't happen.
On the other hand, if you comment out the sv.set('Set Text.') code, this behavior completely disappears, and the callback gets called as many times as you wish.
How can I have the sv.set() functionality, while still maintaining the callback getting called every time the Entry widget loses focus?
This is discussed in the Tk manual page for entry:
The validate option will also set itself to none when you edit the entry widget from within either the validateCommand or the invalidCommand. Such editions will override the one that was being validated.
Presumably, this is done to avoid infinite recursion.
You can run this (instead of the given Tcl code, after idle {%W config -validate %v})
root.after_idle(lambda: e.config(validate="focusout"))
from the callback to schedule a reconfiguration of the widget to enable validation again (after changing your sources so that e is the right Entry widget, i.e. not the second one).

Python, Tkinter - Making get() dynamic

Below is some code that I'm testing with. In this code, I am able to create a window and have a Label on top and a Entry field on bottom. When I type in that entry field, I can dynamically change what's in the label. Now, I have included a function that is trying to evaluate a variable assigned to "tex", by storing what is predefined in the Entry widget. Which is "cat". This is picked up by:
tex = e.get()
I understand that get() is not changing dynamically as I change the text in the entry widget. So it cannot change to "dog" when I change the string in the entry widget. Is this possible? Here is the code:
from Tkinter import *
import time
root = Tk()
def change():
if tex == ("cat"):
time.sleep(0.5)
pass
else:
time.sleep(0.5)
e.delete(0, END)
e.insert(0, "dog")
v = StringVar()
e = Entry(root, textvariable=v)
e.insert(0, "cat")
e.pack(side=BOTTOM)
tex = e.get() #When text is defined with get(), it does not change
#dynamically with the entry widget
l = Label(root, textvariable=v)
l.pack(side=TOP)
change()
root.mainloop()
Any help would be appreciated.
To answer your specific question, no, there is no way for tex to magically keep updated when the entry widget changes. That feature is exactly why the textvariable attribute exists -- it causes the variable to always be updated with the value in the entry widget. If you need the data in another variable, you will have to call the .get() method.
That being said, you can set a trace on the variable, or a binding on the entry widget, and have it call code that can update tex to whatever you want when the entry widget changes.
For example, the following code shows how to have callback called whenever the value in the entry widget changes:
def callback(*args):
global tex
tex = e.get()
v.trace("w", callback)
For more information about what arguments are passed to the callback, see What are the arguments to Tkinter variable trace method callbacks?
All that being said, you have a couple of critical flaws in your code. First, you are creating the StringVar incorrectly. It needs to be v = StringVar() (note the trailing ()).
Also, you should never call sleep in the main thread of a GUI program. Read up on the after method if you want to execute some code after some time has elapsed.

omitting Toplevel's parent argument

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.

Checking if the user presses 'Return' while selected in an Entry box Tkinter

I'm using Tkinter to create a GUI for a simple geometry calculator I'm creating.
Basically, what I have is an Entry box. What I want is for the program/GUI/system to detect when the user of the program hits the 'Enter' or 'return' key WHILE they are in the Entry box. When this is detected, I want the contents of the Entry box to be appended to a list I have defined earlier. I also want a simple label to be created on the GUI that displays the contents of the list (including the appended item(s)). Note that the list begins with nothing in it.
Here is my code so far:
from tkinter import *
#Window setup(ignore this)
app = Tk()
app.title('Geometry Calculator')
app.geometry('384x192+491+216')
app.iconbitmap('Geo.ico')
app.minsize(width=256, height=96)
app.maxsize(width=384, height=192)
app.configure(bg='WhiteSmoke')
#This is the emtry list...
PointList = []
#Here is where I define the variable that I will be appending to the list (which is the object of the Entry box below)
StrPoint = StringVar()
def list_add(event):
#I don't really know how the bind-checking works and how I would implement it; I want to check if the user hits enter while in the Entry box here
if event.char == '':
PointList.append(StrPoint)
e1 = Entry(textvariable=StrPoint).grid(row=0, column=0)
app.bind('<Return>', list_add)
mainloop()
I don't really know the proper way to check for 'Return' and then use it in an if statement.
I hope you understand what I'm trying to get help with, and I've looked all around for an explanation that I could understand with no success.
Instead of binding with the app just bind it with the Entry widget object,i.e,e1
from tkinter import *
#Window setup(ignore this)
app = Tk()
app.title('Geometry Calculator')
app.geometry('384x192+491+216')
app.iconbitmap('Geo.ico')
app.minsize(width=256, height=96)
app.maxsize(width=384, height=192)
app.configure(bg='WhiteSmoke')
#This is the emtry list...
PointList = []
#Here is where I define the variable that I will be appending to the list (which is the object of the Entry box below)
StrPoint = StringVar()
def list_add(event):
print ("hello")
#I don't really know how the bind-checking works and how I would implement it; I want to check if the user hits enter while in the Entry box here
if event.char == '':
PointList.append(StrPoint)
e1 = Entry(textvariable=StrPoint)
e1.grid(row=0, column=0)#use grid in next line,else it would return None
e1.bind('<Return>', list_add)# bind Entry
mainloop()
The solution is to set the binding on the widget itself. That way, the binding will only apply while focus is on that widget. And since you're binding on a specific key, you don't need to check for the value later. You know the user pressed return, because that's the only thing that will cause the binding to fire.
...
e1.bind('<Return>', list_add)
...
You have another problem in that your list_add function needs to call the get method of the variable rather than accessing the variable directly. However, since you aren't using any of the special features of a StringVar, you really don't need it -- it's just one more thing you have to manage.
Here's how to do it without the StringVar:
def list_add(event):
PointLit.append(e1.get())
...
e1 = Entry(app)
e1.grid(row=0, column=0)
e1.bind('<Return>', list_add)
Note that you need to create the widget and lay out the widget in two steps. Doing it the way you did it (e1=Entry(...).grid(...) will cause e1 to be None since that is what .grid(...) returns.

Categories

Resources