从另一个类调用小部件以更改其在 tKinter 中的属性 [英] Calling a widget from another class to change its properties in tKinter

查看:22
本文介绍了从另一个类调用小部件以更改其在 tKinter 中的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 tKinter 应用程序的主构造函数中创建了一个函数,用于更新小部件的某些属性,例如他们跨多个框架的文本.我想要做的是在控制器框架中同时更改多个框架中的小部件.

I have made a function in the main constructor of my tKinter app which updates certain properties of widgets e.g. their text across multiple frames. What I'm trying to do is change widgets in multiple frames at the same time while in a controller frame.

def update_widgets(self, frame_list, widget_name, criteria, output):
    for i in frame_list:

        i.widget_name.config(criteria=output)

# update_widgets(self, [Main, AnalysisSection], text_label, text, "foo")
# would result in Main.text_label_config(text="foo") and 
# AnalysisSection.text_label_config(text="foo") ideally.

但是,使用此代码,我遇到了两个问题.首先,我收到一个属性错误,指出两个框架都没有 widget_name 属性.其次,当我尝试使用 self 前缀引用小部件名称时,两个框架都表示它们没有 self 属性.有没有办法解决这个问题?

However with this code, I'm encountering two problems. Firstly, I'm getting an attribute error stating that both frames don't have the attribute widget_name. Secondly, when I tried to refer to the widget names with the self prefix, both frames say they don't have the attribute self. Is there a way to fix this?

完整程序如下:

import tkinter as tk

class Root(tk.Tk):

def __init__(self, *args, **kwargs):


    tk.Tk.__init__(self, *args, **kwargs)
    self.frames = {}

    container = tk.Frame(self)
    container.pack(side="bottom", expand=True)#fill="both", expand=True)

    container.grid_rowconfigure(0, weight=1)
    container.grid_columnconfigure(0, weight=1)

    for X in (A, B):
        frame=X(container, self)
        self.frames[X]=frame
        frame.grid(row=0, column=0, sticky="nsew")

    self.show_frame(A)

def show_frame(self, page):
    frame = self.frames[page]
    frame.tkraise()

def update_widgets(self, frame_list, widget_name, criteria, output):
    for i in frame_list:
        frame = self.frames[i]
        widget = getattr(frame, widget_name)
        widget[criteria] = output



class A(tk.Frame):

     def __init__(self, parent, controller):

         tk.Frame.__init__(self, parent)

         self.controller = controller

         self.text = 'hello'

         self.classLabel = tk.Label(self, text="Frame A")

         self.classLabel.pack(side=tk.TOP)

         # trying to change this widget
         self.wordLabel = tk.Label(self, text="None")
         self.wordLabel.pack(side=tk.TOP)

         self.changeTextLabel = tk.Label(self, text="Change text above across both frames").pack(side=tk.TOP)
         self.changeTextEntry = tk.Entry(self, bg='pink')
         self.changeTextEntry.pack(side=tk.TOP)

         self.changeFrameButton = tk.Button(text="Change to Frame B", command=lambda: self.controller.show_frame(B))
         self.changeFrameButton.pack(side=tk.TOP, fill=tk.X)

         self.changeTextEntryButton = tk.Button(self, text="ENTER", width=5, command=lambda: self.controller.update_widgets([A, B], 'self.wordLabel', 'text', self.changeTextEntry.get()))
         self.changeTextEntryButton.pack(side=tk.TOP, fill=tk.X)


         ### calling this function outside of the button; this is already
         ### called within a function in my project.
         x = self.controller.update_widgets([A, B], 'wordLabel', 'text', '*initial change*')



class B(tk.Frame):

     def __init__(self, parent, controller):

       tk.Frame.__init__(self, parent)

       self.controller = controller

       self.text = 'hello'

       self.classLabel = tk.Label(self, text="Frame B")

       self.classLabel.pack(side=tk.TOP)

       # trying to change this widget
       self.wordLabel = tk.Label(self, text="None")
       self.wordLabel.pack(side=tk.TOP)

       self.changeTextLabel = tk.Label(self, text="Change text above across both frames").pack(side=tk.TOP)
       self.changeTextEntry = tk.Entry(self, bg='light yellow').pack(side=tk.TOP)

       self.changeFrameButton = tk.Button(text="Change to Frame A", command=lambda: self.controller.show_frame(A))
       self.changeFrameButton.pack(side=tk.TOP, fill=tk.X)

       self.changeTextEntryButton = tk.Button(self, text="ENTER", width=5, command=lambda: self.controller.update_widgets([A, B], 'self.wordLabel', 'text', self.changeTextEntry.get()))
       self.changeTextEntryButton.pack(side=tk.TOP, fill=tk.X)

if __name__ == '__main__':
   app = Root()

推荐答案

您代码中的问题是您试图获取的属性而不是实例 一个类.您需要将 i 转换为该类的实际实例.你有一个额外的问题,你正在传递 'self.wordLabel' 而不仅仅是 'wordLabel'.

The problem in your code is that you're trying to get an attribute of a class rather than an instance of a class. You need to convert i to the actual instance of that class. You have the additional problem that you're passing 'self.wordLabel' rather than just 'wordLabel'.

一个简单的解决方法是在 self.frames

A simple fix is to look up the instance in self.frames

def update_widgets(self, frame_list, widget_name, criteria, output):
    for i in frame_list:
        frame = self.frames[i]
        label = getattr(frame, widget_name)
        label[criteria] = output

您还需要将按钮命令更改为如下所示:

You also need to change the button command to look like this:

self.changeTextEntryButton = tk.Button(... command=lambda: self.controller.update_widgets([A,B], 'wordLabel', 'text', self.changeTextEntry.get()))

如果您打算让 update_widgets 始终更新所有页面类,则没有理由传入框架类列表.相反,您可以只迭代已知类:

If you intend for update_widgets to always update all of the page classes, there's no reason to pass the list of frame classes in. Instead, you can just iterate over the known classes:

def update_widgets(self, widget_name, criteria, output):
    for frame in self.frames.values():
        label = getattr(frame, 'classLabel')
        label[criteria] = output

然后您需要修改按钮以删除框架类列表:

You would then need to modify your buttons to remove the list of frame classes:

self.changeTextEntryButton = tk.Button(..., command=lambda: self.controller.update_widgets('wordLabel', 'text', self.changeTextEntry.get()))

这篇关于从另一个类调用小部件以更改其在 tKinter 中的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆