单击时如何将按钮值从自定义小部件传递到 tkinter 中的主应用程序

how to pass button value from custom widget to main application in tkinter when clicked

我为 tkinter 创建了一个自定义小部件,其中包含 5 个按钮。该小部件在大多数情况下都运行得很漂亮。问题是我不知道如何将用户在小部件中按下的按钮传递给主应用程序。自定义小部件将最后一个按下的按钮存储在一个变量中,但我无法弄清楚如何让主应用程序看到它已被更改而不求助于将按钮释放事件绑定到根。我想尝试进一步构建这个自定义小部件,并且我希望它能够工作而不必进行一些混乱的修改。理想情况下,在下面的示例中,按下按钮时,标签应更改以反映按下的按钮。例如,如果用户单击“2”按钮,标签应更改为“2 X 2 = 4”。如何将按钮上的文字直接传递给主应用程序使用?希望我说得够清楚了。我希望能够像使用 .get() 方法的任何其他 tkinter 小部件一样从小部件获取值。这是我正在使用的代码:

import tkinter as tk
from tkinter import ttk

class ButtonBar(tk.Frame):
    def __init__(self, parent, width=5,  btnLabels=''):
        tk.Frame.__init__(self, parent)
        self.btnLabels = []
        self.btnNames = []
        self.setLabels(btnLabels)
        self.selButton = None
        self.display()

    def getPressedBtn(self,t):
        """
        This method will return the text on the button.
        """
        self.selButton = t
        print(t)

    def createBtnNames(self):
        """
        This method will create the button names for each button. The button
        name will be returned when getPressedBtn() is called.
        """
        for i in range(0,5):
            self.btnNames.append(self.btnLabels[i])


    def display(self):
        """
        This method is called after all options have been set. It will display
        the ButtonBar instance.
        """
        self.clear()
        for i in range(len(self.btnLabels)):
            self.btn = ttk.Button(self, text=self.btnLabels[i], command=lambda t=self.btnNames[i]: self.getPressedBtn(t))
            self.btn.grid(row=0, column=i)

    def setLabels(self, labelList):
        if labelList == '':
            self.btnLabels = ['1', '2', '3', '4', '5']
            self.createBtnNames()

        else:
            btnLabelStr = list(map(str, labelList))
            labelsLen = len(btnLabelStr)

    def clear(self):
        """
        This method clears the ButtonBar of its data.
        """
        for item in self.winfo_children():
            item.destroy()


root = tk.Tk()

def getButtonClicked(event):
    global selBtn
    print(event)
    if example.winfo_exists():
        selBtn = example.selButton
        answer = int(selBtn) * 2
        myLabel.config(text='2 X ' + selBtn + ' = ' + str(answer))

tabLayout = ttk.Notebook(root)
tabLayout.pack(fill='both')
vmTab = tk.Frame(tabLayout)
myLabel = tk.Label(vmTab, text='2 X 0 = 0', width=50, height=10)
myLabel.pack()
vmTab.pack(fill='both')
tabLayout.add(vmTab, text='Volume Movers')
#  Create the ButtonBar.
example = ButtonBar(vmTab)
selBtn = None
example.pack()
lbl = tk.Label(root, text='')
root.mainloop()

我查看了 Whosebug 上的其他一些帖子。这个 was very helpful, but it didn't address the button issue. I though this Subclassing with Tkinter 可能会有所帮助。我不明白如果我使用 root.bind("<ButtonRelease-1>", getButtonClicked) 绑定事件,那么小部件工作正常。还有其他方法吗?

我会说你已经使代码比它应该的更复杂,你真的只需要创建按钮并给它们一些作为参数传递的回调。该回调应该至少接受一个参数,该参数将是按钮上的文本,该文本也将传递给该回调。

import tkinter as tk
from tkinter import ttk


class ButtonBar(tk.Frame):
    def __init__(self, master, values: list, command=None):
        tk.Frame.__init__(self, master)

        for col, text in enumerate(values):
            btn = ttk.Button(self, text=text)
            if command is not None:
                btn.config(command=lambda t=text: command(t))
            btn.grid(row=0, column=col, sticky='news')


def change_label(val):
    res = 2 * int(val)
    new_text = f'2 X {val} = {res}'
    my_label.config(text=new_text)


root = tk.Tk()

my_label = tk.Label(root, text='2 X 0 = 0')
my_label.pack(pady=100)

texts = ['1', '2', '3', '4', '5']
example = ButtonBar(root, values=texts, command=change_label)
example.pack()

root.mainloop()

您还可以将按钮建立在值列表的基础上,这样您就可以指定任何值,它会创建上面有该文本的按钮,按下它们会调用给定的函数,其中包含文本的参数。这样你就可以像使用任何其他小部件一样使用它,它需要主人、一些值(文本)和一个命令。然后你只需创建那个回调,它将接受那个参数,然后相应地更改标签。 (我还删除了所有笔记本的东西,但我只是在展示如何实现你所要求的)

还有:
我强烈建议关注 PEP 8 - Style Guide for Python Code。函数和变量名称应在 snake_case 中,class 中的名称应在 CapitalCase 中。在函数和 class 声明周围有两个空行。对象方法定义周围有一个空行。