簡體   English   中英

如何使 Tkinter 按鈕更改自己的文本屬性?

[英]How to make a Tkinter Button change its own text attribute?

上下文:這是我正在制作的程序的一部分,該程序使用 Tkinter 構建一個表單,該表單尊重特定的 json 模式。

它以遞歸方式工作,並且每次在模式中遇到“對象”類型(實際上對應於 python 中的 dict 類型)時都會調用 build_dict_form。 build_rec_form 將調用另一個 function 來完成表單的該部分。

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 object。
  • 我不能將 btn_k 作為工廠的參數傳遞,因為在調用工廠時它尚未創建。
  • 出於某種原因,我不明白我不能在沒有命令屬性的情況下創建按鈕並在之后更改它。
  • 我必須通過工廠 go ,否則不同按鈕的不同命令會被打亂,所以這樣的技巧不起作用。

我怎樣才能使它工作?

編輯:如果你需要運行,你可以使用:

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))

您可以使用configconfigure編輯文本:

btn_k.text.config(text='Cancel')

您也可以使用屬性索引:

btn_k['text'] = 'Cancel'

“出於某種原因,我不明白我不能在沒有命令屬性的情況下創建按鈕並在之后更改它。” 〜你可以,類似於上面:

btn_k['command'] = new_func

編輯:進一步探討問題,只有在觸發else時才會創建按鈕,因此您可能需要預定義 function 頂部的按鈕:

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)

首先,“text”和“command”都不是 Button 對象的屬性:訪問它們需要通過btn['text']btn['command']

考慮到這一點,可以在創建按鈕后更改按鈕的命令:

btn_k = tk.Button(master=frm_k, text="choose")
btn_k['command'] = btn_k_cmd_factory(k, properties[k], frm_k, btn_k)

因此將按鈕 object 作為參數用於其自己的命令 function。


基於@CoolCloud 的回答

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM