2017-11-14 4 views
0

私はTkinterに取り組んでいて、異なるフレーム間で値を渡すのが難しいので、Bryan Oakleyで提供される "共有データ"ソリューションを使用して自分のコードに追加してthis tutorial hereに従った。 「共有データ」辞書の値をボタンのコマンドとして設定することはできません。ラムダからの変数の変更が機能しないのはなぜですか?

以下のコードのコメントは、この問題の概要を示しています。選択したページの初期化中に変数を変更しようとすると、それは正常に変更されます。しかし、それをラムダに入れることは、辞書変数がまったく変更されないことを意味します。また、ボタンコマンドに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() 
+2

あなたは、単一の関数を呼び出す以外のことを行うために 'lambda'を使うべきではありません。それ以上のことをする必要がある場合は、適切な関数またはメソッドを作成します。これは読みやすく、デバッグも簡単です。 –

+0

これは、(RegAppの初期化時に)変更が発生する前に 'ValuePage'のインスタンスを初期化しているためです。そのため、問題があります。 'ValuePage'で' Label'を作成するときに、 'text'の代わりに' textvariable'を使用します。 'title_label = ttk.Label(self、textvariable = self.controller.shared_data [" choice "]、font = SUBTITLE_FONT、justify = tk.CENTER) ')。また、2つの関数呼び出しのリストの理解は安いハックです! – CommonSense

+0

あなたはラムダコールがlambdaに渡す必要がある関数の引数を 'command = lambda var = num:self.button_command(var)'のようにまず渡す必要があることを認識していますか? – Nae

答えて

0

フレーム間でデータをよく渡すことはあまり難しくありません。私はそれをしたい2つの方法があります。

方法1:今、あなたは別にいるとき

self.data = "this is some data" 

セットアップこのようなものにするために、フレーム....

class ThirdName(tk.Frame): 

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

     self.controller = controller 

は今だけのようなものを言いますこのように呼び出すことができます:

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) 

はボビーがデータ:)

OK ....第二の点を取得します...フレーム間の移動は次のようにして行うことができます。

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メソッドも私が使用しています。あなたのデータを1つのフレームにまとめるといいです。

0

なぜ「self.controller」の代わりに「controller」を使用していますか?コンストラクタの先頭に 'self.controller'を代入してから、代わりに 'controller'を使うのは混乱します。可変シャドーイングが問題を引き起こしているのかもしれません。

関連する問題