0

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.

2
  • self.todayVar = tk.StringVar() is the line on which the error is in the example using this as the program just freezes when it reaches that line (used debug prints). I think it may have something to do with when StringVar() is referenced during the initialisation of the tk window? Commented Nov 14, 2015 at 19:01
  • @PM2Ring That's great, thanks. Commented Nov 14, 2015 at 19:09

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

10 Comments

That works exactly how I wanted, I'll go and implement that in my code now. Thanks!
I still have exactly the same problem, so I think my mistake is elsewhere, though I was already probably making a mistake and it's great that you have helped me with that. I will put my full code in the question and it would be great if you could see what is wrong.
@R.Murray: Ok, but please try to condense your problem down to a minimal reproducible example. Not only does that make it easier to diagnose and fix the problem, it makes your question more valuable to SO, as it will be more likely to help future readers.
@R.Murray: My guess is that you probably have a bunch of other errors similar to the day = tk.Entry(self,width=2).grid(row=4,column=1) thing causing havoc throughout your code, and using somevar where you need self.somevar. So hopefully, once you clean those things up your code will be a lot closer to working correctly.
|
0

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":

2 Comments

That's great to have cleared up but if you read the above answer thread you will see that the problem has evolved somewhat now! Any advice you can give on this is greatly appreciated.
Can you reproduce the OP's problem with the new code, Bryan? As I said above, it works for me, and I can't see any potential trouble-spots.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.