tkinter使用按钮名称变量设置状态时抛出错误

2024-06-06 10:50:56 发布

您现在位置:Python中文网/ 问答频道 /正文

我想把一个按钮名作为变量传递给tkinter。此方法适用于文本字段,但不适用于代码中的按钮

我正在构建一个gui应用程序,我希望有一个通用的清除功能,可以清除文本输入字段并将按钮从正常重置为禁用

有多个按钮和字段,因此希望使其通用。 对于我的代码,除了clear all(全部清除)按钮外,其他按钮都存在

我根据传递给函数的内容将变量w_button设置为(现有)按钮的特定名称

def switch_clear(elem_type):
    
if elem_type == 'scn':
    w_button = 'b_clear_scn'
    clear_field = 'scn_file_entry'
    print ('scenario')
elif elem_type == 'sol2':
    w_button = 'b_clear_sol2'
    clear_field = 'sol2_file_entry'
    print ('sol2')
elif elem_type == 'mdl':
    w_button = 'b_clear_mdlList'
    clear_field = 'mdlList_file_entry'
    print ('mdl')
elif elem_type == 'all':
    print ('clear all TBD')
    return()
    
if w_button["state"] == NORMAL:
    clear_field.delete(0, END)
    w_button["state"] = DISABLED
return()

下面是发生的情况:

C:\utils>my_frame3.py
scenario
Traceback (most recent call last):
  File "C:\utils\my_frame3.py", line 127, in <module>
    b_clear_scn =  Button(first_frame, text = "Clear scenario", command = switch_clear('scn'), height = 2, state=DISABLED)
  File "C:\utils\my_frame3.py", line 100, in switch_clear
    if w_button["state"] == NORMAL:
TypeError: string indices must be integers

C:\utils>

我意识到我可以复制并将清除操作推到if/elif语句中,我可能不得不接受这一点,但是-有可能将按钮引用为变量吗?我该怎么做

按以下要求填写代码。尽管使用了code小部件,预览还是一团糟,但是在我的文件中,行的格式是正确的(notepad++)。德克萨斯州

import os, sys
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog

root2 = Tk()
root2.title('Model Processing')
root2.geometry('{}x{}'.format(512, 400))

# colors
color1 = 'light cyan'
color2 = 'gold'
color3 = 'RosyBrown1'
color4 = 'lavender'
color5 = 'linen'

bg_color = 'azure'

# number of items to process; until >1 OK is disabled; if >1 OK is enabled TBD
l_to_do = [] # items to process; scn, sol2, modelList, installed models

# functions/commands

def callback():
    print ('A button was clicked.')
    return;

def my_exit():
    result = messagebox.askquestion("Cancel Model Processing", "Are you sure?", icon='warning')
    if result == 'yes':
        root2.quit()
    else: # user changed mind
        pass

def choose_import_file(str_ftype):
    which_field = ''
    which_clear_btn = ''
    ftype = str_ftype
    mdl_opts = {}
    
    if ftype == 'scn':
        title_msg = 'Choose a scenario file'
        
        mdl_opts['filetypes'] = [('Supported types', ('.scn')),
                               ('scenario files',('.scn'))]     
        which_field = scn_file_entry                             
        which_clear_btn = 'b_clear_scn'

    elif ftype == 'sol2':
        title_msg = 'Choose a SOL2 file'
        mdl_opts['filetypes'] = [('Supported types', ('.txt')),
                               ('sol2 files',('.txt'))]     
        which_field = sol2_file_entry 
        
    elif ftype == 'mdllist':
        title_msg = 'Choose a ModelList file'
        print ('TBD: ModelList file')
                                 
    file_input = filedialog.askopenfilename(title = title_msg, **mdl_opts)
    if file_input == '':
        print ('error or cancelled by user')
        return
    else:
         f_inp_file = os.path.basename(file_input)
         f_inp_file_base = str(file_input.split('.')[0])
         f_inp_file_ext = str.lower(str(file_input.split('.')[1]))

         f_inp_d_name = os.path.dirname(file_input)
         print('File chosen:', f_inp_file_base)

         # populate scenario file field
         which_field.insert(INSERT,file_input)

         which_clear_btn["state"] = NORMAL
         
         # define appropriate clear button active
         # define_clear_btn.configure(state = ACTIVE)

         return

def switch_clear(elem_type):
        
    if elem_type == 'scn':
        if b_clear_scn["state"] == NORMAL:
            scn_file_entry.delete(0, END)
            b_clear_scn["state"] = DISABLED
    elif elem_type == 'sol2':
        f b_clear_sol2["state"] == NORMAL:
            clear_field = 'sol2_file_entry'
            b_clear_sol2["state"] = DISABLED
    elif elem_type == 'mdl':
        if b_clear_mdlList["state"] == NORMAL:
            clear_field = 'mdlList_file_entry'
            b_clear_mdlList["state"] = DISABLED
    elif elem_type == 'all':
        print ('clear all TBD')
        return()
    return()    
    

# create all of the main containers
first_frame = Frame(root2, bg=color5, width = 512, height=90, pady=10)
second_frame = Frame(root2, bg=color5, width = 512, height=90, pady=10)
third_frame = Frame(root2, bg=color5, width=512, height=90, pady=10)
fourth_frame = Frame(root2, bg=color5, width = 512, height = 90, pady=10)

# layout all of the main containers
root2.grid_rowconfigure(3, weight=1)
root2.grid_rowconfigure(2, weight=1)
root2.grid_rowconfigure(1, weight=1)
root2.grid_columnconfigure(0, weight=1)

first_frame.grid(row=0, sticky="ew")
second_frame.grid(row=1, sticky="ew")
third_frame.grid(row=2, sticky="ew")
fourth_frame.grid(row = 3, sticky="e")

# create the widgets for the first frame
#scn_label = Label(first_frame, text = 'Scenario file')
scn_file_entry = Entry(first_frame, background=bg_color, width = 50)
b_choose_scn = Button(first_frame, text = "Choose a scenario..", command = lambda: choose_import_file('scn'), height = 2)
b_clear_scn =  Button(first_frame, text = "Clear scenario", command = switch_clear('scn'), height = 2, state=DISABLED)


# layout the widgets in the first frame
#scn_label.grid(row = 0, column = 0, padx = (10,50), pady=5)
scn_file_entry.grid(row = 0, column = 1, padx = (10,10))
b_choose_scn.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_scn.grid(row=2, column=0, padx = (10,10), sticky=W)

# second frame
# sol2_label = Label(second_frame, text = 'Sol2 file')
sol2_file_entry = Entry(second_frame, background=bg_color, width = 50)
b_choose_sol2 = Button(second_frame, text = "Choose SOL2 file..", command = lambda: choose_import_file('sol2'), height = 2)
b_clear_sol2 =  Button(second_frame, text = "Clear SOL2", command = switch_clear('sol2'), height = 2, state=DISABLED)

# layout the widgets in the second frame
# sol2_label.grid(row = 0, column = 0, padx = (10,50), pady=5)
sol2_file_entry.grid(row = 0, column = 1, padx = (10,10), sticky=EW)
b_choose_sol2.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_sol2.grid(row=2, column=0, padx = (10,10), sticky=W)

# third frame
# mdlList_label = Label(third_frame, text = 'ModelList.txt file')
mdlList_file_entry = Entry(third_frame, background=bg_color, width = 50)
b_choose_mdlList = Button(third_frame, text = "Choose ModelList.txt file..", command = callback, height = 2)
b_clear_mdlList =  Button(third_frame, text = "Clear ModelList", command = callback, height = 2, state=DISABLED)

# layout the widgets in the third frame
#mdlList_label.grid(row = 0, column = 0, padx = (10,10), pady=5, sticky = 'ns')
mdlList_file_entry.grid(row = 0, column = 1, padx = (10,10), sticky=EW)
b_choose_mdlList.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_mdlList.grid(row=2, column=0, padx = (10,10), sticky=W)


#####################################################################
# create bottom widgets
#####################################################################
clear_all = Button(fourth_frame, text='Clear All', padx = '5', command = callback, height = 2, state=DISABLED)
ok_btn = Button(fourth_frame, text='OK', padx = '5', command = callback, height = 2, state=DISABLED)
cancel_btn = Button(fourth_frame, text='Cancel', height = 2, padx = '12', command = my_exit)

#####################################################################
# layout the bottom widgets
#####################################################################
clear_all.grid(row = 0, column = 2, sticky = 'e')
ok_btn.grid(row = 0, column = 3, sticky = 'e')
cancel_btn.grid(row = 0, column = 4, sticky = 'e')

# commands/bindings

root2.mainloop()

Tags: textcolumnframegridfilerowstateentry
1条回答
网友
1楼 · 发布于 2024-06-06 10:50:56

我相信这就是你想要做的。下面是完整的注释和解释^用这种方法,{}变得完全不必要了。按钮直接清除其command中相应的条目,Entry验证负责相应的按钮state

注意:在你发布完整代码之前,我写了所有这些

import tkinter as tk

root = tk.Tk()
root.geometry('400x400')

#toggle the state of buttons based on entry text length        
def toggle_button(name, text):
    global btn_switch
    btn_switch[name]['state'] = 'normal' if len(text) else 'disabled'
    return True


#to hold the buttons so they are easy to position
#just for this example
buttons = tk.Frame(root)
buttons.pack(side='top', anchor='nw')

#create a tcl wrapper for the validate command
vcmd = tk.Widget.register(root, toggle_button)

#mock-up of your entries ~ validate on key press. send widget name and full text to vcmd
scn_file_entry     = tk.Entry(root, width=20)
scn_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
scn_file_entry.pack(side='left', anchor='nw')

sol2_file_entry    = tk.Entry(root, width=20)
sol2_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
sol2_file_entry.pack(side='left', anchor='nw')

mdlList_file_entry = tk.Entry(root, width=20)
mdlList_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
mdlList_file_entry.pack(side='left', anchor='nw')


#mock-up of your buttons ~ delete the entry text in a lambda and let entry validation handle the button state
b_clear_scn = tk.Button(buttons, text="scn", state='disabled')
b_clear_scn.configure(command=lambda: scn_file_entry.delete(0, 'end'))
b_clear_scn.pack(side='left', anchor='nw')

b_clear_sol2 = tk.Button(buttons, text="sol2", state='disabled')
b_clear_sol2.configure(command=lambda: sol2_file_entry.delete(0, 'end'))
b_clear_sol2.pack(side='left', anchor='nw')

b_clear_mdlList = tk.Button(buttons, text="mdl", state='disabled')
b_clear_mdlList.configure(command=lambda: mdlList_file_entry.delete(0, 'end'))
b_clear_mdlList.pack(side='left', anchor='nw')


#create a dictionary of 'widget name':corresponding button, for toggle_button to reference
btn_switch = {
    f'{scn_file_entry}':b_clear_scn,
    f'{sol2_file_entry}':b_clear_sol2,
    f'{mdlList_file_entry}':b_clear_mdlList,
}
   
root.mainloop()

相关问题 更多 >