【问题标题】:Why does changing a variable from a lambda not work?为什么从 lambda 更改变量不起作用?
【发布时间】:2017-11-14 18:24:54
【问题描述】:

我一直在研究 Tkinter,发现在不同帧之间传递值时遇到问题,所以我关注了this tutorial here,使用Bryan Oakley 提供的“共享数据”解决方案并将其添加到我自己的代码中。 除了我不能将“共享数据”字典中的值设置为按钮上的命令。

下面代码中的一些 cmets 概述了问题。如果我只是在选择页面的初始化期间尝试更改变量,它会正常更改。但是把它放在一个 lambda 中意味着字典变量根本不会改变。并且尝试为按钮命令使用 def 有其自身的复杂性。

import tkinter as tk
import tkinter.ttk as ttk

# from tkinter import messagebox

TITLE_FONT = ("Segoe UI Light", 22)
SUBTITLE_FONT = ("Segoe UI Light", 12)

window_size = [300, 200]

resistors = []
choice = "default"


class RegApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.iconbitmap(self, default="test.ico")
        tk.Tk.wm_title(self, "Test")

        self.shared_data = {
            "choice": tk.StringVar(),
        }

        container = tk.Frame(self, width=window_size[0], height=window_size[1])
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for F in panels:
            frame = F(container, self)

            self.frames[F] = frame

            frame.grid(row=0, column=0, sticky="NSEW")

        self.show_frame(WelcomePage)

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


class WelcomePage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        title_label = ttk.Label(self, text="Welcome", font=TITLE_FONT)
        subtitle_label = ttk.Label(self, text="Let's run some numbers.", font=SUBTITLE_FONT)
        start_button = ttk.Button(self, text="Begin", width=24, command=lambda: controller.show_frame(ChoicePage))
        title_label.pack(pady=(40, 5))
        subtitle_label.pack(pady=(0, 10))
        start_button.pack()


class ChoicePage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        self.controller.shared_data["choice"].set("test2")  # Here, the variable is set fine

        title_label = ttk.Label(self, text="Is your resistor network \nin series or parallel?", font=SUBTITLE_FONT,
                                justify=tk.CENTER)
        series_button = ttk.Button(self, text="Series", width=24,
                                   command=lambda: [self.controller.shared_data["choice"].set("series"), controller.show_frame(ValuePage)])
        # But when I use it in a lambda, the variable doesn't even seem to set at all. It switches to the next page and has the value ""
        parallel_button = ttk.Button(self, text="Parallel", width=24,
                                     command=lambda: controller.show_frame(ValuePage))

        title_label.pack()
        series_button.pack()
        parallel_button.pack()

        # TODO Make the user select between 'series' and 'parallel'


class ValuePage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        title_label = ttk.Label(self, text=self.controller.shared_data["choice"].get(), font=SUBTITLE_FONT,
                                justify=tk.CENTER)

        title_label.pack()


panels = [WelcomePage, ChoicePage, ValuePage]

app = RegApp()
app.resizable(False, False)
app.geometry('{}x{}'.format(window_size[0], window_size[1]))
app.mainloop()

【问题讨论】:

  • 你不应该使用lambda 来做除了调用单个函数之外的任何事情。如果您需要做更多的事情,请创建一个适当的函数或方法。这将更容易阅读,也更容易调试。
  • 这是因为您在发生任何更改之前初始化了 ValuePage 的实例(在初始化 RegApp 时),因此存在您的问题。使事情更加动态 - 当您在 ValuePage (title_label = ttk.Label(self, textvariable=self.controller.shared_data["choice"], font=SUBTITLE_FONT, justify=tk.CENTER)) 中创建 Label 时,使用 textvariable 而不是 text。此外,两个函数调用的列表理解是一种廉价的黑客攻击!
  • 知道您需要传递 lambda 调用需要首先传递给 lambda 的函数的参数,就像在 command=lambda var=num: self.button_command(var) 中一样,对吧?

标签: python python-3.x tkinter


【解决方案1】:

在帧之间很好地传递数据并不难。我喜欢有两种方法。

方法一:

设置一个框架是这样的......

class ThirdName(tk.Frame):   

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        
        self.controller = controller

现在只需说:

self.data = "this is some data"

现在,当您在另一个框架中时,您可以这样称呼它:

print(ThirdName.data)
>>> "this is some data"

第二种方法就是把它发送到这样的地方:

value_1 = 'Richard'
bobby_def(value_1, 42, 'this is some text')

...

def bobby_def(name, number, text)
    print(text)

    or...
    return(name, number, text)

鲍比会得到数据:)

好的....第二点...在帧之间移动可以通过以下方式完成:

self.button_to_go_to_home_page = tk.Button(self, text='Third\nPage', font=Roboto_Normal_Font,
                             command=lambda: self.controller.show_frame(ThirdName),
                             height=2, width=12, bd = 0, activeforeground=active_fg, activebackground=active_bg, highlightbackground=border_colour,
                             foreground=bg_text_colour, background=background_deselected)
self.button_to_go_to_home_page.place(x=20, y=280)

**用这样的东西设置一个框架:

class SecondName(tk.Frame):   

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        
        self.controller = controller

是的,self.controller 方法也是我使用的一种方法,很高兴将大量数据放在一个帧中。

【讨论】:

    【解决方案2】:

    你为什么使用“controller”而不是“self.controller”?在构造函数的开头分配“self.controller”,然后使用“controller”来代替,这有点令人困惑。也许变量阴影会导致您的问题。

    【讨论】:

      猜你喜欢
      • 2011-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-14
      • 2014-12-07
      • 2019-09-27
      • 2019-09-12
      • 1970-01-01
      相关资源
      最近更新 更多