2

With Python 2.7, I would like to turn the state of an "Entry" widget to normal/disable thank to a checkbutton.

With the help of this question Disable widget with checkbutton?, I can do it with 1 checkbutton and 1 Entry

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

import Tkinter as tk

root = tk.Tk()


class Principal(tk.Tk):
    def __init__(self, *args, **kwargs):
        self.foo = tk.StringVar()
        self.nac = tk.IntVar()

        self.ck1 = tk.Checkbutton(root, text='test',
            variable=self.nac, command=self.naccheck)
        self.ck1.pack()

        self.ent1 = tk.Entry(root, width=20, background='white',
            textvariable=self.foo, state='disabled')
        self.ent1.pack()

    def naccheck(self):
        print "check"
        if self.nac.get() == 0:
            self.ent1.configure(state='disabled')
        else:
            self.ent1.configure(state='normal')

app = Principal()
root.mainloop()

Problem come when I want to have 2 or more pair (checkbutton/entry). In my final interface, I may have 20 or more of this pair, so I would like to avoid to have 20 or more the same "naccheck" method.

I tried this:

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

import Tkinter as tk

root = tk.Tk()


class Principal(tk.Tk):
    def __init__(self, *args, **kwargs):
        self.foo = tk.StringVar()
        self.nac = {}
        self.ent = {}

        self.ent["test"] = tk.Entry(root, width=20, background='white', textvariable=self.foo, state='disabled')
        self.ent["test"].pack()

        self.ent["image"] = tk.Entry(root, width=20, background='white', textvariable=self.foo, state='disabled')
        self.ent["image"].pack()

        self.nac["test"] = tk.IntVar()
        self.ck1 = tk.Checkbutton(root, text='test', variable=self.nac["test"], command=self.naccheck("test"))
        self.ck1.pack()

        self.nac["image"] = tk.IntVar()
        self.ck1 = tk.Checkbutton(root, text='image', variable=self.nac["image"], command=self.naccheck("image"))
        self.ck1.pack()


    def naccheck(self,item):
        print "check "+item
        print self.nac[item].get()
        if self.nac[item].get() == 0:
            self.ent[item].configure(state='disabled')
        else:
            self.ent[item].configure(state='normal')

app = Principal()
root.mainloop()

Unfortunately, when I launch this code, the method "naccheck" is called immediately for each checkbutton, and never after when I click on one ...

What I have done wrong?

Cœur
  • 37,241
  • 25
  • 195
  • 267
ericc
  • 741
  • 3
  • 8
  • 19
  • Why don't you store all of your `Entry`s in an array, and then run a `for` loop over them in `naccheck`? – Joel Cornett Feb 26 '13 at 21:52
  • my Entry are in an array ! 1 checkbutton must "activate" the related Entry. What is the advantage of looping through Entry ? – ericc Feb 26 '13 at 22:01

1 Answers1

8

There are many ways to solve this problem. One way is to pass the entry and checkbutton variable into your check function. Create the entry widget and variable first. Then, create the checkbutton and pass in the variable and entry to your callback:

ent = tk.Entry(...)
var = tk.IntVar()
chk = tk.Checkbutton(..., command=lambda e=ent, v=var: self.naccheck(e,v))

Notice the use of lambda, which is a simple technique for creating anonymous functions. This enables you to pass arguments to a callback without having to create named functions. Another option is to use functools.partial. There are no doubt dozens of examples of this on StackOverflow as this is a very common question.

Next, you need to modify your function to accept arguments:

def naccheck(self, entry, var):
    if var.get() == 0:
        entry.configure(state='disabled')
    else:
        entry.configure(state='normal')
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thanks ! The "lambda" function do the trick :-) .Just a typo error in the command when you define "v" and then use "c" in 'naccheck' call. – ericc Feb 27 '13 at 05:32
  • @ericc: thanks for letting me know about the typo; I fixed it. – Bryan Oakley Feb 27 '13 at 12:09
  • After a test, why should I use "e" and "v" at all ? Seems that, without modifying my code, just adding "lambda", (command=lambda :self.naccheck("test")) it also work ! – ericc Feb 27 '13 at 12:28
  • @ericc: lambda is useful _if_ you want to pass in v and c. There are other ways to solve the problem, like you suggest. In the case where you are storing objects in dictionaries with identical keys, passing in the key works fine. – Bryan Oakley Feb 27 '13 at 13:00