0

I have been working on Tkinter and am finding trouble passing values between different frames, so I followed this tutorial here, using the "shared data" solution provided by Bryan Oakley and adding it to my own code. Except I cannot set the value in the "shared data" dictionary as a command on a button.

A few comments in the code below outline the problem. If I just try to change the variable during the init of my choice page, it changes normally. But putting it in a lambda means that the dictionary variable won't change at all. And trying to use a def for the button command has its own complications.

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()
IneffaWolf
  • 73
  • 11
  • 2
    you shouldn't be using `lambda` to do anything other than call a single function. If you need to do any more than that, create a proper function or method. This will be easier to read, and easier to debug. – Bryan Oakley Nov 14 '17 at 01:16
  • It's because you initialising instance of `ValuePage` before any change occures (on initialisation of `RegApp`), hence there's your problem. Make things more dynamic - use `textvariable` instead of `text`, when you create `Label` in `ValuePage` (`title_label = ttk.Label(self, textvariable=self.controller.shared_data["choice"], font=SUBTITLE_FONT, justify=tk.CENTER)`). Also, a list comprehension of two function calls is a cheap hack! – CommonSense Nov 14 '17 at 05:41
  • You _are_ aware that you need to pass arguments of the function that lambda calls needs to passed to lambda first, like in `command=lambda var=num: self.button_command(var)` right? – Nae Nov 14 '17 at 07:04

2 Answers2

1

well passing data between frames isn't too hard. there are 2 ways I like to do it.

Method 1:

setup a frame to be something like this....

class ThirdName(tk.Frame):   

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

now just say something like:

self.data = "this is some data"

now when you're in another frame you can call it like this:

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

Second way is just to send it somewhere like this:

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)

Bobby will get the data :)

ok.... second point... moving between frames can be done with something like this:

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)

**set up a frame with stuff like this:

class SecondName(tk.Frame):   

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

Yes, the self.controller method is one I use too and it's nice to have lots of your data together in one frame.

Community
  • 1
  • 1
David W. Beck
  • 182
  • 1
  • 9
0

Why are you using 'controller' instead of 'self.controller'? It's kind of confusing that you assign 'self.controller' at the start of the constructor and then you use 'controller' instead. Maybe that variable shadowing is causing your problem.