Python Forum
Checkbuttons always come up as black boxes regardless of the state
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Checkbuttons always come up as black boxes regardless of the state
#1
Hello all,
I am writing a program that utilizes a large array of checkbuttons.
When run, the user clicks on the checkbuttons that they want to select, then upon clicking the Go button, it saves the states of the buttons into a text file. Subsequent runs of the program read the text file and set the checkbuttons as checked or unchecked based on the values in text file. However, when I run my program, all the checkbuttons appear with a black box in them regarless of the values in the text file.
Below is a pared down version of the code.
If anyone can explain how to fix this, it would be greatly appreciated.
Thanks in advance.

Edit: I tried running the exact same program on my Fedora LINUX server and it works fine. The error appears when the program is run on Windows or Cygwin. Any help to fix this program in Windows or Cygwin would be appreciated.
Thanks in advance.


from tkinter import *
from tkinter import ttk
import os
from os.path import expanduser
from pathlib import Path

def save_defaults(args):
	default_values = open(default_file,"w+")
	for x in args:
		default_values.write(x+"\n")
	default_values.close()

def main_program():
    button_values = []
    for i in range(6):
        if check_buttons[i].instate(['selected']):
            button_values.append("1")
        else:
            button_values.append("0")
    save_defaults(button_values)
    print("Finished\n")


mw = Tk()
# 999x999 is size of window, 999+999 is the location of the window
mw.geometry('700x300+400+200')
mw.title(__file__)

current_file = Path(__file__).stem
default_file = expanduser("~")+"/python/"+current_file+".default"
# If file does not exist, create one
if not os.path.isfile(default_file):
	cmd = "type NUL > " + default_file
	# cmd = "touch " + default_file    # for LINUX operating system
	os.system(cmd)
default_values = open(default_file,"r")

frame3 = Frame(mw)
framebot = Frame(mw)
frame3.pack(side=TOP,fill=X)
framebot.pack(side=BOTTOM,fill=X)

w3 = Label(frame3, text="Checkbuttons: ",font=("Times",16)).grid(row=0,column=0)

check_buttons = []
row_val = 0
col_val = 1
for i in range(6):
    temp_value = default_values.readline()
    check_buttons.append(ttk.Checkbutton(frame3,text=str(i+1)))
    check_buttons[i].state(["selected"])
    if (temp_value.strip() == "") or (temp_value.strip() == "0"):
        check_buttons[i].state(["!selected"])
    check_buttons[i].grid(row=row_val,column=col_val)
    col_val += 1
    if col_val > 3:
        col_val = 1
        row_val += 1

btn3 = Button(framebot,text='Go',font=("Times",16),command=main_program).pack(side="left")
btn4 = Button(framebot,text='Exit',font=("Times",16),command=mw.quit).pack(side="right")

default_values.close()

mw.mainloop()
Reply
#2
state does not do what you think it does. state is used for styling a ttk widget. You use "selected" in combination with "state" to specify how a widget should appear when selected.

The way to get and set the value of ttk.Checkbutton is use tkinter variables.
from tkinter import *
from tkinter import ttk

mw = Tk()
mw.geometry('700x300+400+200')
frame3 = Frame(mw)
framebot = Frame(mw)
frame3.pack(side=TOP,fill=X)
framebot.pack(side=BOTTOM,fill=X)
 
w3 = Label(frame3, text="Checkbuttons: ",font=("Times",16)).grid(row=0,column=0)
check_buttons = []
for i in range(1, 7):
    value = BooleanVar()
    button = ttk.Checkbutton(frame3, text=str(i), variable=value)
    check_buttons.append(value)
    value.set(i % 2 == 0)
    button.grid(row=i,column=0) 
mw.mainloop()
Reply
#3
Thank you for your response.
I figured out how to fix the problem.
I need to create bogus variables and assign them to the checkbutton widgets.
I cannot use the same variable name for all the checkbuttons or it does not work.
I created the variables bv0, bv1, bv2, bv3 ... etc by utilizing the exec() function.
Then I assign the variables to the checkbutton using variable=globals()[var_name].
It is quite ridiculous that I have to do this.
Below is the updated code.


check_buttons = []
row_val = 0
col_val = 1
for i in range(6):
    temp_value = default_values.readline()
    var_name = "bv"+str(i)
    exec_str = var_name+" = BooleanVar()"
    exec(exec_str)
    check_buttons.append(ttk.Checkbutton(frame3,text=str(i+1),variable=globals()[var_name]))
    check_buttons[i].state(["selected"])
    if (temp_value.strip() == "") or (temp_value.strip() == "0"):
        check_buttons[i].state(["!selected"])
    check_buttons[i].grid(row=row_val,column=col_val)
    col_val += 1
    if col_val > 3:
        col_val = 1
        row_val += 1
Reply
#4
It would be ridiculous if you had to do that. I used a list.
from tkinter import *
from tkinter import ttk
 
mw = Tk()
mw.geometry('700x300+400+200')
frame3 = Frame(mw)
framebot = Frame(mw)
frame3.pack(side=TOP,fill=X)
framebot.pack(side=BOTTOM,fill=X)
  
w3 = Label(frame3, text="Checkbuttons: ",font=("Times",16)).grid(row=0,column=0)
check_buttons = []
for i in range(1, 7):
    value = BooleanVar()
    button = ttk.Checkbutton(frame3, text=str(i), variable=value)
    check_buttons.append(value) # <--- Value variables saved here!!!!
    value.set(i % 2 == 0)
    button.grid(row=i,column=0) 
mw.mainloop()
There often isn't a reason to keep the handle to the control after it is placed and treat the variable as a well designed control that does what you really want.
Reply
#5
I was interested in your code and it inspired me to do a modified version. Just wanted to share. It's by far not the best way but, it works.
#! /usr/bin/env python3

# Do the imports
import pathlib
import os
import tkinter as tk
from tkinter import ttk
from functools import partial

# The main class
class MyClass:
    def __init__(self, parent):
        # Set the parent
        self.parent = parent
        self.parent.columnconfigure(0, weight=1)
        self.parent.rowconfigure(0, weight=1)

        # Container frame hold all other frames
        self.mainframe = tk.Frame(self.parent)
        self.mainframe.grid(column=0, row=0, sticky='new')
        self.mainframe.grid_columnconfigure(0, weight=3)

        # header frame
        self.label_frame = tk.Frame(self.mainframe)
        self.label_frame.grid(column=0, row=0, sticky='new')
        self.label_frame.grid_columnconfigure(0, weight=3)

        # The header label
        self.label = tk.Label(self.label_frame, anchor='n')
        self.label['text'] = 'Checkboxes'
        self.label['font'] = 'sans 16 bold'
        self.label['fg'] = 'blue'
        self.label.grid(column=0, row=0, sticky='new')
        self.label.grid_columnconfigure(0, weight=3)

        # The container frame for all the checkboxes
        self.checkbox_frame = tk.Frame(self.mainframe)
        self.checkbox_frame.grid(column=0, row=1, sticky='new')
        self.checkbox_frame.grid_columnconfigure(0, weight=3)

        # Initiate the files class for usage
        files = Files()
        file_path = GetPath().mypath()

        # Name of the settings file e.g. settings.txt
        file_name = 'settings.txt'

        # Create the file if it does not exist
        files.make_file(file_name)

        # This converts the settings.txt to integers for use in setting
        # the checkbox values to 0 or 1
        defaults = files.convert(file_name)

        # Set file variable
        file = f'{file_path}/{file_name}'

        # Setup some variables
        # vars is used to create a list to store checkbox settings
        # for sending to the Files class for writing
        vars = []

        # Used for setting rows and columns for checkboxes
        col_var = 0
        row_var = 0

        # Create the checkboxes
        for i in range(1,7):
            myvar = tk.IntVar()
            self.checkbox = ttk.Checkbutton(self.checkbox_frame)
            self.checkbox['text'] = f'Checkbox {i}'
            self.checkbox['variable'] = myvar

            # If there is not a defaults setting do nothing as we
            # have not saved any settings yet else we have some settings
            # go ahead and set the values
            if not defaults:
                pass
            else:
                myvar.set(defaults[i-1])

            self.checkbox.grid(column=col_var, row=row_var, sticky='new')
            if col_var >= 2:
                col_var = 0
                row_var += 1
            else:
                col_var += 1

            # Store checkbox settings in vars variable
            vars.append(myvar)

        # Setup the buttons
        self.btn_frame = tk.Frame(self.mainframe)
        self.btn_frame.grid(column=0, row=2, sticky='new', pady=10)
        self.btn_frame['relief'] = 'ridge'
        self.btn_frame['borderwidth'] = 1
        self.btn_frame.grid_columnconfigure(0, weight=3)

        self.btn = tk.Button(self.btn_frame, text='Save', command=partial(files.save, file, vars))
        self.btn.grid(column=0, row=0, sticky='w')

        self.btn = tk.Button(self.btn_frame, fg='red', text='Exit', command=os.sys.exit)
        self.btn.grid(column=1, row=0, sticky='e')


# Class to get current path
class GetPath:
    def mypath(self):
        return pathlib.Path(__file__).parent.absolute()


# Class for creating, writing, saving, and converting settings file
class Files:
    # File does not exist create or file exist pass
    def make_file(self, file):
        if not os.path.isfile(f'{GetPath().mypath()}/{file}'):
            open(f'{GetPath().mypath()}/{file}', 'w+')
        else:
            pass

    # Save the settings file
    def save(self, file, vars):
        try:
            myvars = []
            for val in vars:
                myvars.append(val.get())

            myf = open(file, 'w')
            myf.write(f'{myvars}')
            myf.close()

        except ValueError as error:
            print(error)

    # Convert the entries in the text file to integers to use in the Checkboxes
    # 0 for uncheck and 1 for checked. Set a variable list and return after conversion
    def convert(self, file):
        defaults = []
        with open(f'{GetPath().mypath()}/{file}', 'r') as lines:
            line = lines.read().replace('[','').replace(']','').replace(',','')

        line = line.split()
        for default in line:
            defaults.append(int(default))
        return defaults


def main():
    root = tk.Tk()
    root.title('Checkboxes')
    root.configure(borderwidth=3, relief='ridge', padx=10, pady=5)
    MyClass(root)
    root.mainloop()

if __name__ == '__main__':
    main()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#6
WOW!!!
Menator01, you are awesome Smile
Thanks!!!
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Draw bounding boxes on live feed Jerome 0 228 Jan-20-2024, 10:50 PM
Last Post: Jerome
  remove all color but red, then replace it with black kucingkembar 14 6,917 Dec-29-2021, 07:50 PM
Last Post: deanhystad
  How to read check boxes from word document srikanthpython 0 2,551 Mar-30-2021, 01:58 PM
Last Post: srikanthpython
  How to use nb-black python cde formatter ErnestTBass 3 6,747 Jun-04-2020, 03:51 PM
Last Post: ErnestTBass
  Finance: Black Scholes Model not working pwt 5 3,827 May-27-2020, 10:14 AM
Last Post: buran
  after using openpyxl to add colors to script, black shows up white online in excel Soundtechscott 1 3,651 Jun-08-2019, 10:33 PM
Last Post: Soundtechscott
  State graph Kaluss 1 2,197 Mar-18-2019, 05:29 PM
Last Post: nilamo
  Because the emoji appears black and white at the exit ? nerd 3 5,521 Jan-28-2019, 11:34 PM
Last Post: nerd
  Help with plot, how to show ranking with boxes icebelt 1 2,419 Jan-25-2019, 10:00 AM
Last Post: Larz60+
  Python interface only black and white........ Wilson 3 6,050 Jul-15-2017, 01:20 PM
Last Post: sparkz_alot

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020