python - 如何使 Tkinter Button 更改其自己的文本属性?
问题描述
上下文:这是我正在制作的程序的一部分,该程序使用 Tkinter 构建一个尊重特定 json 模式的表单。
它以递归方式工作,并且每次在模式中遇到“对象”类型(实际上对应于 python 中的 dict 类型)时都会调用 build_dict_form。build_rec_form 将调用另一个函数来完成表单的该部分。
def build_dict_form(schema, master):
answers = {}
def btn_k_cmd_factory(k, k_properties, k_frame):
def res():
if k in answers:
del answers[k]
#TODO: delete subframe of k_frame
else:
answers[k] = build_rec_form(k_properties, k_frame)
# btn_k.text = "Cancel"
return res
properties = schema["properties"]
if "maxProperties" not in schema:
for k in properties:
frm_k = tk.Frame(master=master, bd=4, highlightbackground="red", highlightthickness=0.5)
lbl_k = tk.Label(master=frm_k, text=k)
lbl_k.pack()
answers[k] = build_rec_form(properties[k], frm_k)
frm_k.pack()
else:
for k in properties:
frm_k = tk.Frame(master=master, bd=4, highlightbackground="red", highlightthickness=0.5)
lbl_k = tk.Label(master=frm_k, text=k)
btn_k = tk.Button(master=frm_k, text="choose", command=btn_k_cmd_factory(k, properties[k], frm_k))
lbl_k.pack()
btn_k.pack()
frm_k.pack()
return answers
我现在的问题是,当我点击 btn_k 时,它的文本属性更改为“取消”(TODO 不是这个问题的一部分)。
- 我不能简单地取消注释该
btn_k.text = "Cancel"
行,因为在 btn_k_cmd_factory 内部我无权访问 btn_k 对象。 - 我不能将 btn_k 作为工厂的参数传递,因为在调用工厂时它尚未创建。
- 出于某种原因,我不明白我不能在没有命令属性的情况下创建按钮并在之后更改它。
- 我必须通过工厂,否则不同按钮的不同命令会被打乱,所以这样的技巧是行不通的。
我怎样才能使它工作?
编辑:如果你需要运行,你可以使用:
import tkinter as tk
import jsonschema
type_conversion = {
"object": dict,
"integer": int,
"string": str,
"array": list
}
def deep_eval(structure):
t = type(structure)
if t == dict:
return {k: deep_eval(structure[k]) for k in structure}
if t == list:
return [deep_eval(k) for k in structure]
else:
return structure()
def ask_new_item(schema):
res = {}
validated = {}
def validate():
nonlocal validated
validated = deep_eval(res)
try:
jsonschema.validate(instance=validated, schema=schema)
window.destroy()
except jsonschema.exceptions.ValidationError as e:
print("schema not validated:")
print(e)
window = tk.Tk()
btn_close = tk.Button(master=window, text="Validate", command=validate)#TODO: ["state"]=tk.DISABLED
res = build_rec_form(schema, window)
btn_close.pack()
window.mainloop()
return validated
def build_rec_form(schema, master):
frm_subform = tk.Frame(master=master, bd=4, highlightbackground="black", highlightthickness=0.5)
helper_text = f"{schema.get('description', '<no description>')}, of type {schema['type']}"
lbl_helper = tk.Label(master=frm_subform, text=helper_text)
lbl_helper.pack()
t_python = type_conversion[schema["type"]]
res_method = None
if t_python == str or t_python == int:
untyped_method =build_str_form(schema, frm_subform)
def res_method(): return t_python(untyped_method())
elif t_python == dict:
res_method = build_dict_form(schema, frm_subform)
elif t_python == list:
res_method = build_list_form(schema, frm_subform)
else:
print(f"type not supported: {t_python}")
frm_subform.pack()
return res_method
def build_dict_form(schema, master):
answers = {}
def btn_k_cmd_factory(k, k_properties, k_frame):
def res():
if k in answers:
del answers[k]
#TODO: delete subframe of k_frame
else:
answers[k] = build_rec_form(k_properties, k_frame)
# btn_k.text = "Cancel"
return res
properties = schema["properties"]
if "maxProperties" not in schema:
for k in properties:
frm_k = tk.Frame(master=master, bd=4, highlightbackground="red", highlightthickness=0.5)
lbl_k = tk.Label(master=frm_k, text=k)
lbl_k.pack()
answers[k] = build_rec_form(properties[k], frm_k)
frm_k.pack()
else:
for k in properties:
frm_k = tk.Frame(master=master, bd=4, highlightbackground="red", highlightthickness=0.5)
lbl_k = tk.Label(master=frm_k, text=k)
btn_k = tk.Button(master=frm_k, text="choose", command=btn_k_cmd_factory(k, properties[k], frm_k))
lbl_k.pack()
btn_k.pack()
frm_k.pack()
return answers
def build_list_form(schema, master):
#TODO
def res():
return []
return res
def build_str_form(schema, master):
ent_answer = tk.Entry(master=master)
ent_answer.pack()
return ent_answer.get
if __name__ == "__main__":
schema = {
"type": "object",
"properties": {
"Prop1": {
"type": "string"
},
"Prop2": {
"type": "integer"
}
}
"maxProperties": 1
}
print(ask_new_item(schema))
解决方案
config
您可以使用或编辑文本configure
:
btn_k.text.config(text='Cancel')
您也可以使用属性索引:
btn_k['text'] = 'Cancel'
“出于某种原因,我不明白我不能在没有命令属性的情况下创建按钮并在之后更改它。” 〜你可以,类似于上面:
btn_k['command'] = new_func
编辑:进一步探讨问题,您的按钮只有在else
被触发时才会创建,因此您可能希望在函数顶部预定义按钮:
def build_dict_form(schema, master):
answers = {}
btn_k = None
def btn_k_cmd_factory(k, k_properties, k_frame):
def res():
if k in answers:
del answers[k]
#TODO: delete subframe of k_frame
else:
answers[k] = build_rec_form(k_properties, k_frame)
if btn_k is not None:
btn_k['text'] = 'Cancel'
return res
编辑 2:传入参数,稍后更改文本怎么样?结合以上提示:
def build_dict_form(....):
def btn_k_cmd_factory(k, k_properties, k_frame, btn_k):
....
else:
answers[k] = build_rec_form(k_properties, k_frame)
btn_k['text'] = 'Cancel'
....
else:
....
btn_k = tk.Button(master=frm_k, text="choose")
btn_k['command'] = btn_k_cmd_factory(k, properties[k], frm_k, btn_k)
推荐阅读
- c# - 如果类接受许多参数,那么创建不可变类的最简洁方法是什么?
- javascript - 如何抓取 javascript 哈希链接内容?
- javascript - momentjs 弃用警告不会消失
- c# - 如何在不使用磁盘和内存不足的情况下将大文件从 api 流式传输到 api?
- python - pyOpenGL:创建方向射线的麻烦
- android - WebView Android 不加载 URL
- amazon-web-services - 通过 Python 开发工具包更改 AWS Cognitio “启用的身份提供商”
- android - 多个布局文件的 Android 数据绑定错误
- mathematical-optimization - 如何在 Gurobi 中编写线性组合
- node.js - 添加训练短语时的 Dialogflow v2 Nodejs 客户端库 UpdateIntent