Reputation: 1913
EDIT This has now moved to Python 3.4.3 tkinter - Program freezes on declaration of IntVar or any other tkinter data type as that is the root of the problem which has now been solved. Basically, NEVER name ANYTHING "master" in Python 3.x using tkinter, it causes an infinite loop :( . My full code has now been removed as it is my coursework and wouldn't want people nicking it :D EDIT
I'm relatively new to tkinter and can't see where I'm going wrong. I have tried using a StringVar() and a regular string to get multiple entry fields to disable when a Checkbutton is on. Here's the frame that the problem is in:
class CActivity(tk.Frame):
def Clear(self):
pass # To be completed
def Today(self):
if self.todayVar == "ON":
self.day.configure(state="disabled")
self.month.configure(state="disabled")
self.year.configure(state="disabled")
else:
self.day.configure(state="normal")
self.month.configure(state="normal")
self.year.configure(state="normal")
def createWidgets(self):
self.title = tk.Label(self)
self.title["text"] = "Add an Activity"
self.title["font"] = ("Times New Roman",30)
self.title["fg"] = "purple"
self.title.grid(row=0,column=0,sticky="W",padx=5,pady=5)
self.todayVar = ""
tk.Label(self,text="Activity Name:",font=("Times New Roman",15)).grid(row=1,column=0,sticky="W",padx=5,pady=5)
name = tk.Entry(self).grid(row=1,column=1,columnspan=3,sticky="E",padx=5,pady=5)
tk.Label(self,text="Priority:",font=("Times New Roman",15)).grid(row=2,column=0,sticky="W",padx=5,pady=5)
priority = tk.Checkbutton(self).grid(row=2,column=1,sticky="W",padx=0,pady=5)
tk.Label(self,text="Today?",font=("Times New Roman",15)).grid(row=3,column=0,sticky="W",padx=5,pady=5)
today = tk.Checkbutton(self,onvalue="ON",offvalue="OFF",variable=self.todayVar,command=self.Today).grid(row=3,column=1,sticky="W",padx=0,pady=5) #problem possibly on this line
tk.Label(self,text="Date (DD/MM/YYYY):",font=("Times New Roman",15)).grid(row=4,column=0,sticky="W",padx=5,pady=5)
day = tk.Entry(self,width=2).grid(row=4,column=1,sticky="W",padx=2,pady=5)
month = tk.Entry(self,width=2).grid(row=4,column=2,sticky="W",padx=2,pady=5)
year = tk.Entry(self,width=4).grid(row=4,column=3,sticky="W",padx=2,pady=5)
self.clear = tk.Button(self, command=self.Clear)
self.clear["text"] = "Clear"
self.clear["font"] = ("Times New Roman",15)
self.clear["fg"] = "red"
self.clear.grid(row=7,column=4,sticky="WE",padx=5,pady=5)
self.back = tk.Button(self)
self.back["text"] = "Back"
self.back["font"] = ("Times New Roman",15)
self.back["fg"] = "red"
self.back["command"] = self.parent.Menu
self.back.grid(row=8,column=4,sticky="WE",padx=5,pady=5)
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.pack()
self.parent = parent
self.createWidgets()
And here it is with a StringVar() instead of a standard Python string:
class CActivity(tk.Frame):
def Clear(self):
pass # To be completed
def Today(self):
if self.todayVar.get() == "ON":
self.day.configure(state="disabled")
self.month.configure(state="disabled")
self.year.configure(state="disabled")
else:
self.day.configure(state="normal")
self.month.configure(state="normal")
self.year.configure(state="normal")
def createWidgets(self):
self.title = tk.Label(self)
self.title["text"] = "Add an Activity"
self.title["font"] = ("Times New Roman",30)
self.title["fg"] = "purple"
self.title.grid(row=0,column=0,sticky="W",padx=5,pady=5)
self.todayVar = tk.StringVar()
tk.Label(self,text="Activity Name:",font=("Times New Roman",15)).grid(row=1,column=0,sticky="W",padx=5,pady=5)
name = tk.Entry(self).grid(row=1,column=1,columnspan=3,sticky="E",padx=5,pady=5)
tk.Label(self,text="Priority:",font=("Times New Roman",15)).grid(row=2,column=0,sticky="W",padx=5,pady=5)
priority = tk.Checkbutton(self).grid(row=2,column=1,sticky="W",padx=0,pady=5)
tk.Label(self,text="Today?",font=("Times New Roman",15)).grid(row=3,column=0,sticky="W",padx=5,pady=5)
today = tk.Checkbutton(self,onvalue="ON",offvalue="OFF",variable=self.todayVar,command=self.Today).grid(row=3,column=1,sticky="W",padx=0,pady=5)
tk.Label(self,text="Date (DD/MM/YYYY):",font=("Times New Roman",15)).grid(row=4,column=0,sticky="W",padx=5,pady=5)
day = tk.Entry(self,width=2).grid(row=4,column=1,sticky="W",padx=2,pady=5)
month = tk.Entry(self,width=2).grid(row=4,column=2,sticky="W",padx=2,pady=5)
year = tk.Entry(self,width=4).grid(row=4,column=3,sticky="W",padx=2,pady=5)
self.clear = tk.Button(self, command=self.Clear)
self.clear["text"] = "Clear"
self.clear["font"] = ("Times New Roman",15)
self.clear["fg"] = "red"
self.clear.grid(row=7,column=4,sticky="WE",padx=5,pady=5)
self.back = tk.Button(self)
self.back["text"] = "Back"
self.back["font"] = ("Times New Roman",15)
self.back["fg"] = "red"
self.back["command"] = self.parent.Menu
self.back.grid(row=8,column=4,sticky="WE",padx=5,pady=5)
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.pack()
self.parent = parent
self.createWidgets()
In the case of the use of a standard string the program runs fine until you click on the Checkbutton at which point the Checkbutton goes grey then the program stops responding. In the case of the use of the StringVar() the tk window doesn't load at all, due to this frame being initialised during the initialisation of the window. Thanks for your help and if you would like the full code to help find the problem just let me know.
Upvotes: 0
Views: 2784
Reputation: 55499
Here's a cut-down version of your code that functions correctly:
#!/usr/bin/env python
''' Toggle disable / normal of Tkinter widgets
Written by PM 2Ring & R. Murray 2015.11.15
See http://stackoverflow.com/q/33711472/4014959
'''
#Python 3 / Python 2 Tkinter import
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
class CActivity(tk.Frame):
def today_cb(self):
if self.todayVar.get():
state = "disabled"
else:
state = "normal"
#print(state)
self.day.configure(state=state)
self.month.configure(state=state)
self.year.configure(state=state)
def create_widgets(self):
title_text = ("Click the 'Today' Checkbutton to\n"
"disable / enable date Entry widgets")
title = tk.Label(self, text=title_text)
title.grid(row=0, column=0, sticky="W", padx=5, pady=5)
self.todayVar = tk.IntVar()
tk.Label(self,text="Today").grid(row=1, column=1,
sticky="W", padx=0, pady=5)
today = tk.Checkbutton(self, variable=self.todayVar,
command=self.today_cb)
today.grid(row=2, column=1, sticky="W", padx=0, pady=5)
#Date Entry widgets
tk.Label(self,text="Day").grid(row=3, column=1,
sticky="W", padx=2, pady=5)
self.day = tk.Entry(self, width=2)
self.day.grid(row=4, column=1, sticky="W", padx=2, pady=5)
tk.Label(self,text="Month").grid(row=3, column=2,
sticky="W", padx=2,pady=5)
self.month = tk.Entry(self, width=2)
self.month.grid(row=4, column=2, sticky="W", padx=2, pady=5)
tk.Label(self,text="Year").grid(row=3, column=3,
sticky="W", padx=2, pady=5)
self.year = tk.Entry(self, width=4)
self.year.grid(row=4, column=3, sticky="W", padx=2, pady=5)
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.pack()
self.parent = parent
self.create_widgets()
if __name__ == '__main__':
master = tk.Tk()
master.title("Disable widgets demo")
frame = CActivity(master)
master.mainloop()
There are a couple of problems with your code.
Your createWidgets
method saves the date Entry
widgets into local variables, eg day
, but they need to be in instance attributes, like self.day
, so they can be accessed by the Checkbutton
's callback method.
Another problem is that you're calling the .grid
method on the same line as the widget constructor. That method returns None
, so doing
day = tk.Entry(self,width=2).grid(row=4,column=1)
sets day
to None.
It's ok to use .grid
(or .pack
) on the same line as the constructor if you don't need to save a reference to the widget, but it just won't work when you do need that reference. :)
I've simplified the Checkbutton
's callback method slightly, to reduce code repetition. I got rid of the onvalue="ON",offvalue="OFF"
in the Checkbutton
's constructor since the default 0 and 1 is adequate for this task, IMHO, (but feel free to change it back if you like :) ), and since we're using a numerical state I'm using an IntVar
rather than a string or StringVar.
I've also change the name of the methods to conform to PEP 8 conventions.
You still have some confusion with local variables vs instance variables in your code. day
is not the same thing as self.day
, so you need to fix that, eg,
day = tk.Entry(self,width=2)
self.day.grid(row=4,column=1,sticky="W",padx=2,pady=5)
needs to be
self.day = tk.Entry(self,width=2)
self.day.grid(row=4,column=1,sticky="W",padx=2,pady=5)
Also, bear in mind what Bryan Oakley has said about widget variable
and textvariable
attributes requiring a reference to an instance of a Tkinter StringVar
, IntVar
, etc.
Upvotes: 1
Reputation: 386362
The variable
and textvariable
attributes require a reference to an instance of a Tkinter StringVar
, IntVar
, DoubleVar
or BooleanVar
. You cannot use normal variables.
Second, you must use the get
method of these variables in order to retrieve the values before comparing them in an if
statement.
self.todayVar = StringVar()
...
today = tk.Checkbutton(..., variable=self.todayVar, ...)
...
if self.todayVar.get() == "ON":
Upvotes: 0