Python/tkinter radio buttons always return default value, displaying multiple options

  Kiến thức lập trình

I am writing mass spectrometry data reduction software and want to give my users options between different fitting styles to the data. My problem is that the radio box selection always returns the default value, rather than whatever is selected at the time the “Confirm” button is pressed and the program moves onto the next window.

I am going crazy trying to find the hidden difference between my minimal reproducible example, which works:

import tkinter as tk

def sequence_selector(sequence_data, all_data=None):
    selected_sequences = []  # Initialize as empty list outside of confirm_selection

    def confirm_selection():
        nonlocal selected_sequences  
        selected_sequences[:] = [i + 1 for i in listbox.curselection()]  # Modify the list in place


    # --- GUI Setup ---
    root = tk.Tk()

    listbox = tk.Listbox(root, selectmode='multiple')
    for seq in sequence_data:
        listbox.insert(tk.END, f"Sequence {seq}")

    fit_type_var = tk.StringVar(value='Linear')
    for ft in ['Average', 'Linear', 'Double exponential']:
        tk.Radiobutton(root, text=ft, variable=fit_type_var, value=ft).pack()

    tk.Button(root, text="Reduce", command=confirm_selection).pack()


    return selected_sequences, fit_type_var.get()

# --- Example Data ---
sequence_data = [1, 2, 3]

# --- Main Function ---
selected_sequences, selected_fit_type = sequence_selector(sequence_data)

print("Selected Sequences (outside sequence_selector):", selected_sequences)
print("Selected Fit Type (outside sequence_selector):", selected_fit_type)

and my code, which always returns the default value assigned to fit_type_var and has multiple options selected at start-up:

def sequence_selector(sequence_data, all_data):

    # other irrelevant functions

    # Confirm button
    def confirm_selection():
        nonlocal selected_sequences
        selected_sequences = [sequence_data[len(sequence_data) - i - 1]['sequence_number'] for i in listbox.curselection()]
        selected_fit_type = fit_type_var.get()
        if not selected_sequences:
            message = ('Please select a sequence to reduce.nClick on a sequence in the list to select it.')
            messagebox.showerror('Error', message)
        print('selected fit type', selected_fit_type)


    selected_sequences = None

    # Tkinter GUI
    root = tk.Tk() 

    # initialize the main frame for the sequence selector
    main_frame = tk.Frame(root)
    main_frame.pack(fill="both", expand=True)

    # other unrelated code...

    right-hand frame for fit-type options
    right_frame = tk.Frame(main_frame)
    right_frame.pack(side=tk.RIGHT, fill=tk.Y, pady=(10,0))

    fit_type_var = tk.StringVar(value='Linear')
    tk.Label(right_frame, text="Choose a raw data fit type:", font=('TkDefaultFont', 10, 'bold')).pack(anchor='w')
    for fit_type in ['Average', 'Linear', 'Double exponential']:
        rb = tk.Radiobutton(
            right_frame, text=fit_type, variable=fit_type_var, value=fit_type, anchor='w'

    # reduce/confirm selection button
    reduce_button = tk.Button(root, text="Reduce selected sequence(s)", command=confirm_selection,
        font=('TkDefaultFont', 14),
        relief=tk.RAISED, bd=4                          
    reduce_button.pack(fill=tk.BOTH, expand=True)

    return selected_sequences, fit_type_var.get()

The full sequence_selector function is here.

Any help would be greatly appreciated!