0

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()

  • Why are you making them global? And why use `global()` instead of just declaring it as global? There's no need. Have you seen the section "Using shared data" in [this answer](https://stackoverflow.com/a/33650527/7432)? – Bryan Oakley Aug 16 '21 at 14:58
  • Yes I saw this, and I used it to create the variables in the controller for all the other widgets. However, the text widget it is different, because I can not link the text to a variable, or at least I dont get how to do it.. – startingover Aug 16 '21 at 15:08
  • `tk.Text(self, height=10, width=60).grid(column=1, row= 3, padx=5)` return None. Should be: `variable = tk.Text(self, height=10, width=60)` and `variable.grid(column=1, row= 3, padx=5)`. – 8349697 Aug 16 '21 at 15:21
  • @8349697 the .grid is actually working, my problem is on making the textwidget input available in multiple classes – startingover Aug 16 '21 at 15:33
  • When I run your code, I get the error: `AttributeError: 'NoneType' object has no attribute 'get'`. – 8349697 Aug 16 '21 at 15:36

1 Answers1

0

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.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • I am very sorry, but somehow it does not seem to work 1) I added `user_message_entry": Text(), ` to the controller , added the widget to PageOne `tk.Text(self,self.controller.app_data["user_message_entry"], height=10, width=60).grid(column=1, row= 3, padx=5)` and then try to get it `body = self.controller.app_data['user_message_entry'].get("1.0", "end")` I do not get any error, but when I try to print it it is empty – startingover Aug 16 '21 at 16:04
  • @startingover: I apologize that my answer wasn't clear. You can't define `user_message_entry` up at the top. You have to do it inside of `PageOne.__init__`. I updated my answer to be more clear. – Bryan Oakley Aug 16 '21 at 16:09
  • Somehow I am still not able to print the body of it, not sure what am I missing here.. I added the following to PageOne `self.controller.app_data["user_message_entry"] = Text(self, height=10, width=60)` and the following to PageTwo... `body = self.controller.app_data['user_message_entry'].get(0.0, END)` My printing function is then always in PageTwo – startingover Aug 17 '21 at 06:57
  • I tried the class possibility as well, and still I am not able to print it.. it looks like it returns always an empty space - not sure why – startingover Aug 17 '21 at 11:16
  • @startingover: the code in `PageTwo.__init__` happens almost immediately after the text widget is created. The user won't have had a chance to type anything yet. – Bryan Oakley Aug 17 '21 at 14:39
  • wow, thanks so much!! It works !! Very much appreciate your patience!!! – startingover Aug 17 '21 at 15:02