Search code examples
pythontkintertext-widget

Tkinter Text Widget - variable in controller


I have a small GUI with multiple frames, and different widgets. I was able to make all the variable global, and availabe in the controller, but I am stuck on the text widget. Any suggestion?

Here followig is just an example, where the text widget is in class "PageOne", and the printing function is on "PageTwo"


import tkinter as tk
from tkinter import *
from tkinter import ttk

LARGE_FONT=("Verdana", 12)

class Movies(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self,*args, **kwargs)

        tk.Tk.wm_title(self, "Movie I like")
        tk.Tk.wm_geometry(self, "500x500")
        container=tk.Frame(self)
        container.pack(side="top", fill="both", expand="True")
        
        self.app_data = {"movie":    StringVar(),
                         "popcorn": StringVar(),
                         "monday": BooleanVar(),
                         "tuesday": BooleanVar(),
                         "wednesday": BooleanVar(),
                         "thursday": BooleanVar(),
                         "friday": BooleanVar(),
                         "saturday": BooleanVar(),
                         "sunday": BooleanVar(),
                        }
        self.frames={}

        for F in (StartPage, PageOne, PageTwo):

            frame= F(container, self)
            self.frames[F]= frame

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


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



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

        s = ttk.Style()
        s.configure('my.TButton', font=('Verdana', 8))

        label=tk.Label(self, text="Home Page", font=LARGE_FONT)
        label.pack(pady=10, padx=30)

        label1=tk.Label(self, text="When are you available?")
        label1.pack()
        #CheckButton
        ttk.Checkbutton(self, text='Monday', variable=self.controller.app_data["monday"]).pack()
        ttk.Checkbutton(self, text='Tuesday', variable=self.controller.app_data["tuesday"]).pack()
        ttk.Checkbutton(self, text='Wednesday', variable=self.controller.app_data["wednesday"]).pack()
        ttk.Checkbutton(self, text='Thursday', variable=self.controller.app_data["thursday"]).pack()
        ttk.Checkbutton(self, text='Friday', variable=self.controller.app_data["friday"]).pack()
        ttk.Checkbutton(self, text='Saturday', variable=self.controller.app_data["saturday"]).pack()
        ttk.Checkbutton(self, text='Sunday', variable=self.controller.app_data["sunday"]).pack()

        button_load=ttk.Button(self, text="Kind of Movie ", width=30, style='my.TButton',
                          command= lambda: controller.show_frame(PageOne))
        button_load.pack(padx=30)

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

        mybutton=ttk.Radiobutton(self, text='Drama', variable=self.controller.app_data["movie"], value="Drama").grid(column=1,row=1, sticky=W, padx=10)
        mybutton1=ttk.Radiobutton(self, text='Comedy', variable=self.controller.app_data["movie"], value="Comedy").grid(column=2,row=1, sticky=W, padx=10)
        
        globals()['user_message_entry'] = tk.Text(self, height=10, width=60).grid(column=1, row= 3, padx=5)



        button_next=ttk.Button(self, text="Next >> ", width=30, style='my.TButton',
                          command= lambda: controller.show_frame(PageTwo)).grid(column=3, row= 10, padx=5)
        
class PageTwo(tk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        tk.Frame.__init__(self, parent)
        
        body = globals()['user_message_entry'].get("1.0", "end")
        mybutton2=ttk.Radiobutton(self, text='Salty', variable=self.controller.app_data["popcorn"], value="Salty").grid(column=1,row=1, sticky=W, padx=10)
        mybutton3=ttk.Radiobutton(self, text='Sweet', variable=self.controller.app_data["popcorn"], value="Sweet").grid(column=2,row=1, sticky=W, padx=10)
        
        def save_info():
            pop_info=self.controller.app_data["popcorn"].get()
            movie_info=self.controller.app_data["movie"].get()
            week=["monday", "tuesday", "wednesday","thursday","friday","saturday","sunday"]
            print("you like " + movie_info + " and you would like " + pop_info +" popcorn")
            print("This is the body" + body)
            test=len(week)
            for i in range (0, test):
                if self.controller.app_data[week[i]].get() == True:
                    print(week[i])


        button_submit=ttk.Button(self, text="Submit", width=15,
                          command=save_info).grid(column=3, row= 10, padx=5)


app=Movies()
app.mainloop()


Solution

  • One solution is to store the widget in the controller the same way you save other things.

    For example:

    class PageOne(tk.Frame):
        def __init__(self, parent, controller):
            ...
            self.controller.app_data["user_message_entry"] = Text(self, height=10, width=60)
            ...
    

    Then, to get the value, it's similar to how you get the other variables:

    body = self.controller.app_data['user_message_entry'].get("1.0", "end")
    

    Another way is to access it is to give your controller a function that will return a page. You can then access it using returned page.

    For example, start by adding a function in the controller to return a page:

    class Movies(tk.Tk):
        ...
        def get_page(self, page_class):
            return self.frames[page_class]
        ...
    

    Next, make sure the text widget is an instance variable:

    class PageOne(tk.Frame):
        def __init__(self, parent, controller):
            ...
            self.user_message_entry' = tk.Text(self, height=10, width=60).grid(column=1, row= 3, padx=5)
            ...
    

    And finally, use that function in the controller to get the other page, and from there, get the text widget:

    page = self.controller.get_page(PageOne)
    body = page.textuser_message_entry.get("1.0", "end")
    

    The above describes how to access the text widget from multiple places. However, you have another problem in that the code you wrote attempts to get the text from the widget just a few milliseconds after it has been created. You need to wait until the user has had a chance to type into the widget before getting the data.

    For example, you will need to move body = page.textuser_message_entry.get("1.0", "end") inside of the save_info function.