-1

in a [former question][1] received a perfect script from @acw1668 for creating popup-windows (see below).

How can this be rewritten in a form that the new windows are not popups but just a switch from one page to the next (the listboxes/candvas are not necessarily needed here)?

Edit: tried to amend the code according to @Bryan Oakley's suggestions. My issue here: I do not manage to pass the list lst from the GUI class to the other page classes without an error message:

 File "/.spyder-py3/temp.py", line 25, in __init__
    frame = F(parent=container, controller=self)

TypeError: __init__() missing 1 required positional argument: 'lst'

What am I missing here? And I do not understand what's happening here:

for F in (StartPage, PageOne, PageTwo):
                page_name = F.__name__
                frame = F(parent=container, controller=self,)

                self.frames[page_name] = frame

If somebody could explain, please?

import tkinter as tk
from tkinter import ttk


class GUI(tk.Tk):

    def __init__(self):

        tk.Tk.__init__(self)
        self.lst = ['a', 'b', 'c']
        container = tk.Frame(self)
        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 (StartPage, PageOne, PageTwo):
            page_name = F.__name__
            frame = F(parent=container, controller=self,)

            self.frames[page_name] = frame

            # put all of the pages in the same location;
            # the one on the top of the stacking order
            # will be the one that is visible.
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame("StartPage", self.lst)

    def show_frame(self, page_name):
        '''Show a frame for the given page name'''
        frame = self.frames[page_name]
        frame.tkraise()


    def show_popup(self, page, lst):

        win = page(self, lst)
        win.grab_set()          # make window modal
        self.wait_window(win)   # make window modal




class StartPage(tk.Frame):

    def __init__(self, parent, controller, lst):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.lst = lst
         # ------------------------------------------------------------------- #
        label = tk.Label(self, text="Check this out")                         
        label.pack(pady=10,padx=10)

         # ------------------- create buttons ---------------------------------
        button1 = ttk.Button(self, text="show all",
                             width = 25, command=lambda: 
                                 controller.show_popup(App, self.lst))
        button1.pack(pady=10, padx=10)        
        button2 = ttk.Button(self, text="show page one",
                             width = 25, command=lambda: 
                                 controller.show_frame(PageOne))
        button2.pack(pady=10, padx=10)        


class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 1")
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()


class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 2")
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()

class App(tk.Toplevel):

    def __init__(self, parent, lst):
        tk.Toplevel.__init__(self, parent)
        self.lst = lst
        self.title('This is the pop up window')
        self.geometry('400x200') 
        label = tk.Label(self, text=self.lst) 
        label.pack(side="top", fill="x", pady=10)
        parent.grid_rowconfigure(0, weight = 1)
        parent.grid_columnconfigure(0, weight = 1)

if __name__ == '__main__': 
    app = GUI() 
    app.mainloop()                  

  [1]: http://stackoverflow.com/questions/41181809/how-to-open-and-close-another-window-with-scrollbar-in-tkinter-for-python-3-5/41182843?noredirect=1#comment69580999_41182843
Al_Iskander
  • 971
  • 5
  • 13
  • 27
  • 1
    If you don't want a popup window, don't inherit from `tk.Toplevel`. Start your research here: http://stackoverflow.com/questions/7546050 – Bryan Oakley Dec 17 '16 at 12:37
  • I did so already but I don't manage to adapt my code to the one you linked. In my example the class GUI is also/already the "StartPage". I do not get to integrate/involve the additional page classes. Any hint how to do that? – Al_Iskander Dec 17 '16 at 12:43
  • 1
    `tk.Toplevel` is used to create second/third/etc. window - so you have to use something different - ie. `tk.Frame`. And you need method to change pages - and sooner or later you will create something like code in @BryanOakley link. It will be easier if `StartPage` will be in separated class, not in GUI. – furas Dec 17 '16 at 14:35
  • 1
    The error is telling you exactly what is wrong. You've defined `StartPage` to require a list parameter, but you aren't passing the list parameter to it. I have no idea what `lst` is supposed to be, but if you want a class to require it, you must include it when constructing the argument. – Bryan Oakley Dec 17 '16 at 16:35
  • exactly, I simply do not get at what part of the code, I have to do this pass. lst is simply some information that will be needed in the different pages. – Al_Iskander Dec 17 '16 at 16:38
  • I thought I correctly passed it here: `self.show_frame("StartPage", self.lst)` but this is apparently not working. – Al_Iskander Dec 17 '16 at 16:47

1 Answers1

1

Your class initializers are defined like this:

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

In order to create an instance of this class it requires three arguments (plus self): parent, controller, and lst.

Now, let's look at how you're creating the instance:

frame = F(parent=container, controller=self,)

Notice how you have the parent and you have the controller, but you haven't passed in anything for lst. That is why the error states "missing 1 required positional argument: 'lst'" -- because you are literally missing one required argument named "lst".

To fix this problem, you simply need to provide this extra argument. For example:

frame = F(parent=container, controller=self, lst=self.lst)

HOWEVER, you probably shouldn't do that. The architecture of this little block of code you copied makes it possible to access values on the GUI class from any of the "page" classes without having to do any extra work.

Because this variable is an attribute of the GUI class, and you are passing a reference to the instance of the GUI class to each "page" (the controller attribute), you can access this data any time you want without having to pass it in at construction time. You can remove it from __init__ and from where you're creating the pages (ie: go back to the original code before your modifications), and then just use self.controller.lst whenever you need the value.

For example:

class SomePage(tk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ...
    def some_function(self):
        print("The value of 'lst' is:", self.controller.lst)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • many thanks @Bryan Oakly. The `controller` in `self.controller.lst` was the missing link. I still have a hard time using and thinking in `parent` and `controller`terms. Btw, I tried `frame = F(parent=container, controller=self, self.lst)` earlier but as you said this is not right and this also produced an error `SyntaxError: positional argument follows keyword argument`. – Al_Iskander Dec 17 '16 at 17:57
  • is it possible to call the page classes from a menu bar command in the GUI class? I notice this does not work as expected ( the pages load automatically if the menu is in GUI and the page classes are called with parantheses like `show_popup(App, self.lst)`) – Al_Iskander Dec 18 '16 at 19:10
  • 1
    @Al_Iskander yes, of course it's possible to call the page classes from a menu bar command. Menubar items can call the same functions as anything else. You might find this useful: http://stackoverflow.com/q/5767228/7432 – Bryan Oakley Dec 18 '16 at 19:12
  • thank you, very helpful @Bryan Oakley. One last question, promised: How can I call the page classes from the Toplevel class `App`? I was hoping `GUI.show_frame("PageOne")` would do the trick but it does not work. I am still confused with all the controller/parent inheritances. – Al_Iskander Dec 18 '16 at 20:47
  • I found the solution myself. I use `self.parent.show_frame("PageOne")`. Thanks anyways for your help and patience. – Al_Iskander Dec 18 '16 at 22:42
  • Am I right with this assumption: in order to pass variables from the tk.Toplevel `App` to another tk.Frame page class, I would have to amend `frame = F(parent=container, controller=self, lst=self.lst)` in `GUI` as you suggested? Or is there a more simple solution? – Al_Iskander Dec 19 '16 at 09:06
  • @Al_Iskander: there's nothing special you need to do. All of these classes are normal python classes, and you pass variables to them the way you do any other classes or objects. Maybe you should work through a python tutorial before going much further. – Bryan Oakley Dec 19 '16 at 12:26
  • Happy to find a suitable tutorial. But This stacked structure is not so standard as you find it in standard tutorials. And the amount of questions related to passing of variables between different classes lets me conclude that it's not always prceived trivial. I typically pass variables over and initialise them in the receiving class. But this approach did not work for me here. Need to find another one or try rewriting the code according to your earlier suggestion. – Al_Iskander Dec 19 '16 at 14:17
  • 1
    I do not understand your comments. This code isn't doing anything out of the ordinary, except that it's creating instances in a loop. You can remove the loop if you want (eg: `self.frames["PageOne"] = PageOne(...)`). Beyond that, it's just normal python classes and objects. You can pass variables however you want -- in function calls, when creating the object, as global variables, etc. – Bryan Oakley Dec 19 '16 at 14:22
  • 1
    @Al_Iskander: I've updated [the original answer](http://stackoverflow.com/a/7557028/7432) to show how you can initialize each class separately, in case it helps. – Bryan Oakley Dec 19 '16 at 14:30
  • Thank you @Bryan Oakley. I am also surprised about the amount of issues I encountered. Your explanations and further links below your original post helped a lot already, too. – Al_Iskander Dec 19 '16 at 14:51