0

Running python 3.8 in PyCharm

I'm trying to create a class to allow creation of a tkinter checkbutton (and eventually other widgets) that will hold all the formatting and definition for the widget. I would like to be able to test the state of the checkbutton and act on it in a callback. But like many others, my checkbutton variable seems to always be 0. I'm fairly sure its a garbage collection issue, but I can't figure a way around it. Note the second set of parameters in the class definition specify the location of the label to display the checkbutton variable state. At the moment, the callback is just to display the variable state in a label.

Acknowledgement: I modified code from Burhard Meier's book "Python GUI Programming Cookbook" for this. The code related to the class is mine, the rest is from Burkhard Meier.

'''
This is a modification of code by Burhard A. Meier
in "Python GUI Programming Cookbook"

Created on Apr 30, 2019
@author: Burkhard A. Meier
'''
#======================
# imports
#======================
import tkinter as tk
from tkinter import ttk

# Create instance
win = tk.Tk()

# Add a title
win.title("GUI Test Box")

# Modify adding a Label
a_label = ttk.Label(win, text="Testing TK Widgets")
a_label.grid(column=0, row=0)

# Modified Button Click Function
def click_me():
    action.configure(text='Hello ' + name.get() + ' ' +
                     number_chosen.get())

#Define the Checkbutton Class
class ChkBut(): #lable, xloc, yloc, xlab, ylab
    def __init__(self, lable, xloc, yloc, xlab, ylab):  # , xloc, yloc, text="default", variable = v, onvalue='Set', offvalue='NotSet'):
        v = tk.IntVar()
        self.v = v
        self.lable = lable
        self.xloc = xloc
        self.yloc = yloc
        self.xlab = xlab
        self.ylab = ylab
        c = tk.Checkbutton(
            win,
            text= self.lable,
            variable=self.v,
            command=self.cb(self.v, xlab,ylab))
        c.grid(column=xloc, row=yloc, sticky = tk.W)
        c.deselect()

    def cb (self, c, xlab, ylab):
        if c.get()==1:
            c_label = ttk.Label(win, text="Set")
            c_label.grid(column=xlab, row=ylab)
        elif c.get()==0:
            c_label = ttk.Label(win, text="NotSet")
            c_label.grid(column=xlab, row=ylab)
        else:
            c_label = ttk.Label(win, text=c.v.get())
            c_label.grid(column=xlab, row=ylab)

# Changing the Label
ttk.Label(win, text="Enter a name:").grid(column=0, row=0)

# Adding a Textbox Entry widget
name = tk.StringVar()
name_entered = ttk.Entry(win, width=12, textvariable=name)
name_entered.grid(column=0, row=1)

# Adding a Button
action = ttk.Button(win, text="Click Me!", command=click_me)
action.grid(column=2, row=1)

# Creating three checkbuttons
ttk.Label(win, text="Choose a number:").grid(column=1, row=0)
number = tk.StringVar()
number_chosen = ttk.Combobox(win, width=12, textvariable=number, state='readonly')
number_chosen['values'] = (1, 2, 4, 42, 100)
number_chosen.grid(column=1, row=1)
number_chosen.current(0)

check1 = ChkBut("Button 1", 0, 4, 0, 5)

chVarUn = tk.IntVar()
check2 = tk.Checkbutton(win, text="UnChecked", variable=chVarUn)
check2.deselect()
check2.chVarUn = 0
check2.grid(column=1, row=4, sticky=tk.W)

chVarEn = tk.IntVar()
check3 = tk.Checkbutton(win, text="Enabled", variable=chVarEn)
check3.select()
check3.chVarEn = 0
check3.grid(column=2, row=4, sticky=tk.W)

name_entered.focus()      # Place cursor into name Entry

#======================
# Start GUI
#======================
win.mainloop()
  • Welcome to Stackoverflow. You may want to revise your question so that it gets "minimal". Have look here: https://stackoverflow.com/help/minimal-reproducible-example – tfv Jun 06 '20 at 03:49
  • Try putting in some debugging statements inside of the `cb` function. You may be surprised to learn that the function is being called before the user clicks the button rather than after. – Bryan Oakley Jun 06 '20 at 06:05
  • So I put a print in the callback to see if the callback was being triggered when the checkbutton is togged. It looks like the callback only gets triggered when the class is instantiated with the check1 call. I am not clear why the callback doesn't continue to happen as the instance of check1 still persists in the window. Shouldn't the call back function follow the instance? I wonder if I should I make it an instance method within the class? Or if not how do I make the call back trigger in the instance of check1? – CapJack1234 Jun 06 '20 at 23:41
  • in line 43 (`command=self.cb(self.v, xlab,ylab))`) you can't put command with function parameters without `lambda`. use lambda like this: `command=lambda:self.cb(self.v, xlab,ylab))` – Shihab Aug 14 '21 at 21:56
  • Also to fix this problem use the `onvalue` and `offvalue` so you wil get 0 or 1 to your var. like this in `c = tk.Checkbutton` add `onvalue = 1` and `offvalue = 0` : ` c = tk.Checkbutton( win, text= self.lable, variable=self.v, command=lambda:self.cb(self.v, xlab,ylab), onvalue=1, offvalue=0) ` – Shihab Aug 14 '21 at 22:00

0 Answers0