Create a "switched" function for dynamically generated buttons

51 Views Asked by At

I am creating a tool to review metadata with tkinter. Its users will review the metadata currently associated with a number of datasets and weigh in on whether the metadata is appropriate for the dataset. Each dataset must have its own window. I have a rough layout in place but I am having a difficult time writing functions for the True/False Buttons.

import pandas as pd
from tkinter import *
from tkinter import ttk

def a(self,name):
    print(name)

def btn_true(status, i):
    if status == "off":
        true_button.configure(relief=SUNKEN, bg="#D4D5D3")
        false_button.configure(relief=RAISED, bg="#EDEDED")
        status = "on"

def btn_false(status, i):
    if status == "off":
        true_button.configure(relief=RAISED, bg="#EDEDED")
        false_button.configure(relief=SUNKEN, bg="#D4D5D3")
        status = "on"
        


def submit_review():
    print('')

df = pd.DataFrame({'dataset_name': ['people', 'places', 'things'],
                   'tags': ["['proper nouns', 'humans', 'UTF-8', 'NaN', '’', '']", "['proper nouns', 'country', 'geocode', 'alphanumeric']", "['jargon', 'websites', 'unicode']"],
                   'l_desc': ['Lorem ipsum dolor ','sit amet, consectetur ','adipiscing elit, sed '],
                   's_desc': ['Lorem','sit','adipiscing']})

DATASET_ROW = 0

tag_str = df.loc[DATASET_ROW,"tags"]
tag_str =tag_str.replace("'","")
tag_list = tag_str.strip('][').split(', ') 

window = Tk()
window.title("Metadata Evaluation Tool")
window.geometry("700x400")  

## Canvas & Scrollbar
outer_frame = Frame(window)
outer_frame.pack(fill=BOTH, expand=1)
canvas = Canvas(outer_frame)
canvas.grid_columnconfigure(0, weight=1, uniform="fred")
canvas.pack(side=LEFT, fill=BOTH, expand=1)
my_scrollbar = ttk.Scrollbar(outer_frame, orient=VERTICAL, command = canvas.yview)
my_scrollbar.pack(side=RIGHT, fill=Y)
canvas.configure(yscrollcommand = my_scrollbar.set)
canvas.grid_columnconfigure(0, weight=1, uniform="foo")
canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion = canvas.bbox("all")))

## Place additional frames within the Canvas
metadata_frame = Frame(canvas)
canvas.create_window((0,0), window=metadata_frame, anchor=NW, )

# Page 
current_dataset =Label(metadata_frame, font='Helvetica 12 bold', text=str(df.loc[DATASET_ROW,"dataset_name"]))
current_dataset.grid(row= 0, column=0, padx=20, pady=10, sticky=EW)

# Tags 
tags_info_frame =LabelFrame(metadata_frame, text="Tags",)
tags_info_frame.grid(row= 1, column=0, padx=20, pady=10, sticky=EW)
 
for i in range(len(tag_list)):
    iter_tag_label = Label(tags_info_frame, text=tag_list[i])
    iter_tag_label.grid(row=i, column=0, sticky=W,)

    status="on"
    true_button = Button(tags_info_frame,  text="  TRUE  ", relief=SUNKEN, bg="#D4D5D3", command=lambda : btn_true(status=status, i=i))
    true_button.grid(row=i, column=1)

    status="off"
    false_button = Button(tags_info_frame, text=" FALSE ", relief=RAISED, bg="#EDEDED", command=lambda : btn_false(status=status, i=i))
    false_button.grid(row=i, column=2)


for widget in tags_info_frame.winfo_children():
    widget.grid_configure(padx=10, pady=5)

# Descriptions 
desc_info_frame =LabelFrame(metadata_frame, text="Description(s)")
desc_info_frame.grid(row= 2, column=0, padx=20, pady=10, sticky=EW)

l_desc_label = Label(desc_info_frame, text="Long Description")
l_desc_label.grid(row=0, column=0, sticky=W,)

l_desc_text = Label(desc_info_frame, text=str(df.loc[DATASET_ROW,"l_desc"]), wraplength=500, justify="left")
l_desc_text.grid(row=0, column=1, sticky=W,)

l_desc_entry = Text(desc_info_frame, height=5, width=20)
l_desc_entry.insert(END, str(df.loc[DATASET_ROW,"l_desc"]))
l_desc_entry.grid(row=1, column=1, sticky=EW,)

s_desc_label = Label(desc_info_frame, text="Short Description")
s_desc_label.grid(row=2, column=0, sticky=W,)

s_desc_text = Label(desc_info_frame, text=str(df.loc[DATASET_ROW,"s_desc"]), wraplength=500, justify="left")
s_desc_text.grid(row=2, column=1, sticky=W,)

s_desc_entry = Text(desc_info_frame, height=5, width=20)
s_desc_entry.insert(END, str(df.loc[DATASET_ROW,"s_desc"]))
s_desc_entry.grid(row=3, column=1, sticky=EW,)

for widget in desc_info_frame.winfo_children():
    widget.grid_configure(padx=10, pady=5)

# Submit review
confirmation_frame =Frame(metadata_frame)
confirmation_frame.grid(row= 3, column=0, padx=20, pady=10, sticky=SE)
button = Button(confirmation_frame, text=" Submit ", command= submit_review)
button.grid(row=0, column=0, sticky=SE, padx=20, pady=10)

window.mainloop()

What should happen When the window loads, all of the TRUE buttons should be toggled on and all of the FALSE buttons should be toggled off. Clicking a button that is toggled off should reverse these settings, but only for the buttons in that row. Clicking a button that is already toggled on should do nothing.

What does happen When the window loads, no button is toggled on or off. Clicking any of the buttons toggles only the buttons in the last row.

Updated

The buttons default to the correct setting but all clicks, regardless of row, only affect the last row of buttons.

1

There are 1 best solutions below

0
On

You have applied the answer in the linked question wrong.

Actually you can use single function instead of two functions:

def btn_toggle(on_button, off_button):
    on_button.configure(relief=SUNKEN, bg="#D4D5D3")
    off_button.configure(relief=RAISED, bg="#EDEDED")

...

for i in range(len(tag_list)):
    iter_tag_label = Label(tags_info_frame, text=tag_list[i])
    iter_tag_label.grid(row=i, column=0, sticky=W,)

    true_button = Button(tags_info_frame,  text="  TRUE  ", relief=SUNKEN, bg="#D4D5D3")
    true_button.grid(row=i, column=1)

    false_button = Button(tags_info_frame, text=" FALSE ", relief=RAISED, bg="#EDEDED")
    false_button.grid(row=i, column=2)

    true_button["command"] = lambda tb=true_button, fb=false_button: btn_toggle(tb, fb)
    false_button["command"] = lambda tb=true_button, fb=false_button: btn_toggle(fb, tb)

...