Tkinter Checkbutton not updating when changing variable - python

I have a tkinter GUI that I'm working on in Python 3.8 on my Macbook. I've encountered a problem where changing the variable associated with a checkbutton doesn't change the appearance of the checkbutton itself. I'd like the checkbutton to show up as checked if I set the IntVar() associated with it to 1, and from everything I've read, this should be happening.
Here's some extremely simplified code showing the problem:
import tkinter as tk
class Window():
def __init__(self, master):
var = tk.IntVar()
checkbutton = tk.Checkbutton(master, variable=var)
checkbutton.pack()
var.set(1)
root = tk.Tk()
Window(root)
root.mainloop()
When I run the script, the checkbutton isn't checked. I am still able to check the checkbutton by clicking on it though. Is this a known bug or am I missing something?

Solved: The issue, as jasonharper pointed out, was garbage collection. The tkinter variable wasn't being used for anything and was just being stored as a local variable, so it was thrown out and couldn't be referenced by the checkbutton. Saving the IntVar somewhere that stuck around fixed the problem. One solution was saving the variable in the var attribute of the checkbutton itself:
import tkinter as tk
class Window():
def __init__(self, master):
var = tk.IntVar()
checkbutton = tk.Checkbutton(master, variable=var)
checkbutton.pack()
var.set(1)
checkbutton.var = var
root = tk.Tk()
Window(root)
root.mainloop()

Related

Tkinter: ttk.Label displaying nothing when given StringVar as textvariable, inside a class

I am trying to use the textvariable attribute of ttk.Label to display & update text according to a given StringVar.
from tkinter import *
from tkinter import ttk
class RenderEvent():
def __init__(self, root):
self.root = root
self.frame = ttk.Frame(self.root, padding="20 20 20 20")
self.frame.grid(column=0, row=0, sticky=(N, W, E, S))
self.dialogue = StringVar(self.frame, value="Placeholder")
L = ttk.Label(self.frame, textvariable=self.dialogue)
L.grid(column=1, row=2, sticky=W)
self.dialogue.set("some text here")
And for reference, root is passed in from another file which looks like this, and is used to start the application:
from tkinter import *
from tkinter import ttk
from renderevent import RenderEvent
root = Tk()
RenderEvent(root)
root.mainloop()
If I use text instead of textvariable in the Label creation, it displays a static string just fine. However, once it is set to textvariable (as shown above), nothing will be displayed at all.
I have tried the same with giving the StringVar() no parameters at all in initialization, or passing in self.root.
Emulating this code outside of a class seems to work as intended (the text appears and updates along with the textvariable), but I can't think of why having a class would cause an issue like this.
The reason is that you aren't keeping a reference to the instance of RenderEvent. Since you don't save a reference, python's garbage collector will try to clean it up which causes the variable to be cleaned up as well. The ttk widgets are more sensitive to this than the normal tk widgets.
The simple and arguably best solution is to assign the result of RenderEvent(root) to a variable so that it isn't affected by python's memory management.
re = RenderEvent(root)

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

I want to get value of Entrybox and Checkbutton but I get nothing why? (I am new to tkinter)

I want to get value of Entrybox and Checkbutton but I get nothing why? (I am new to tkinter)
from tkinter import *
def m1():
m1 = Tk()
entry_val = StringVar()
check_val = IntVar()
Entry(m1, textvariable=entry_val).pack()
Checkbutton(m1, text='CheckButton', variable=check_val).pack()
def show():
print(entry_val.get())
print(check_val.get())
Button(m1, text='click!', command=show).pack()
m1.mainloop()
def main():
main = Tk()
Button(main, text='click! (main)', command=m1).pack()
main.mainloop()
main()
The short and simple answer:
In your code you need to change m1 = Tk() to m1 = Toplevel(). This will fix your issue.
The long answer:
When writing a Tkinter GUI 99.99% of the time you are only ever going to use 1 tkinter instance Tk(). The reason for this is that each instance of Tk() is contained within its own personal "Sandbox". Meaning it cannot play with others. So one instance of Tk() cannot communicate with a separate Tk() instance.
It is my understanding that if you do not specify what instance a method belongs to within the method then it will default to the 1st instance of Tk(). So the StringVar() and IntVar() you have create cannot be printed due to them belonging to main. Because main cannot talk to m1 you cannot update this value.
We can actually test this if you change:
entry_val = StringVar()
check_val = IntVar()
To:
entry_val = StringVar(m1)
check_val = IntVar(m1)
You will see your variables update properly.
Or if you change m1 = Tk() to m1 = Toplevel() (the correct solution) you will see that everything works as needed.
Toplevel() is specifically designed for creating new windows in tkinter so everything can stay in the same "Sandbox" and work together.

Why is my tkinter button not displaying, have I installed everything in the module if not how can I install it?

#snakes and ladder
from tkinter import * #pygame is the module that has collections of functions that is used to create a window import everything from tkinter
import time
class window(Frame): #Frame comes from tkinter is what you think as a window
def __init__(self, master = None):#The master widget defines the settings upon initialisation
Frame.__init__(self, master) #This is called the frame class that has been built in python
self.master = master
def __init__window(self): #creation of the init window
self.master.title("Reagan Kambayi") #It changes the title of the title of our widget
self.pack(fill=BOTH, expand=1)#The pack function will pack this in our frame
#placing the button
stop = Button(self, master, message= "Stop")
#intialising the button that will start the stopwatch
stop.place(x=0, y=0)
screen = Tk() #It must be written with capitalised T because there will be an error and it holds the components of the tkinter module
screen.geometry("700x500")
app = window(screen) #The app variable is assigned to the window creation which has the tkinter module
screen.mainloop()
Ok, here we go.
from tkinter import * #pygame is the module that has collections of functions that is used to create a window import everything from tkinter
Pygame has nothing to do with tkinter and you're not importing pygame here, you're importing tkinter.
class window(Frame): #Frame comes from tkinter is what you think as a window
No, it isn't. A Frame widget is just that, a frame inside a window. It doesn't draw a window in and of itself. The parameter Frame in your example isn't even a Frame widget at all, it's value is Tk() which is the function called to draw the first window in tkinter.
def __init__(self, master = None):#The master widget defines the settings upon initialisation
I'm actually at a loss for what you're attempting to do here. master should equal Frame which equals screen which equals Tk() but if I'm correct you're overriding that and telling it to equal None?
Frame.__init__(self, master) #This is called the frame class that has been built in python
I don't think you're doing what you think you're doing here. This answer explains it better than I could.
def __init__window(self): #creation of the init window
If I'm reading your program correctly then window.__init__window() is never called, so none of this function ever actually happens.
self.pack(fill=BOTH, expand=1)#The pack function will pack this in our frame
You're attempting to call .pack() on self which is calling .pack() on Frame. Typically we wouldn't assign a value to self in this way (although this is valid), read this to find out what self should be used for.
#placing the button
stop = Button(self, master, message= "Stop")
This isn't placing the Button widget, this is assigning the Button widget to a variable. Also, Button widgets are declared as Button(parent, *attributes) and there is no message attribute built in. Meaning what you meant to call was Button(self.master, text="Stop").
#intialising the button that will start the stopwatch
stop.place(x=0, y=0)
This is where you're placing the button, but the function that contains this is never called, so it never happens.
app = window(screen) #The app variable is assigned to the window creation which has the tkinter module
What you're actually doing here is calling the class window, all this does in your current program is call window.__init__(), which in itself does essentially nothing.
This is meant with no offence but I think you're lacking a very basic understanding of tkinter and possibly even Pythonic OOP.
I believe what you're trying to do in your program is the below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.root.title("Reagan Kambayi")
self.stop = Button(self.root, text="Stop")
self.stop.place(x=0, y=0)
root = Tk()
App(root)
root.mainloop()

Python themed tkinter: Entry variable will not set

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.

Categories

Resources