Checkbuttons always come up as black boxes regardless of the state - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: Checkbuttons always come up as black boxes regardless of the state (/thread-28133.html) |
Checkbuttons always come up as black boxes regardless of the state - kenwatts275 - Jul-06-2020 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() RE: Checkbuttons always come up as black boxes regardless of the state - deanhystad - Jul-07-2020 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() RE: Checkbuttons always come up as black boxes regardless of the state - kenwatts275 - Jul-07-2020 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 RE: Checkbuttons always come up as black boxes regardless of the state - deanhystad - Jul-07-2020 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. RE: Checkbuttons always come up as black boxes regardless of the state - menator01 - Jul-07-2020 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() RE: Checkbuttons always come up as black boxes regardless of the state - kenwatts275 - Jul-07-2020 WOW!!! Menator01, you are awesome Thanks!!! |