-1

I want the program to:

  1. make the root window to remain open
  2. and when the button is clicked it will open a toplevel window
  3. In the toplevel window when next is clicked, it will destroy that toplevel window and create another toplevel window and this will go on until the all the elements of the List will be iterated through

But it is not working as expected.

Only one toplevel window appears and I cant go to the next toplevel

My code is:

from tkinter import *

root = Tk()
def go_cmd():
    list = [1,2,3,4,5]
    for i in list:
        win = Toplevel()

        def next_cmd():
            win.destroy()

        l = Label(text = i)
        l.grid(row=0,column=0)
        b = Button(text="Next",command=next_cmd)
        b.grid(row=1,column=0)

        win.mainloop()



b1 = Button(root,text = " Go ",command = go_cmd)
b1.grid(row=0,column=0)




root.mainloop()
Nae
  • 14,209
  • 7
  • 52
  • 79
Ani
  • 7
  • 2
  • Welcome to [so]! Please remove redundant parts, for example `root.grid_rowconfigure(...` and `root.grid_columnconfigure(...` to make your code closer to a [mcve]. – Nae Feb 08 '18 at 13:22
  • You can fix your immediate problem by removing `win.mainloop()` but I don't think `Toplevel` works the way you want. There are other problems as well. – Nae Feb 08 '18 at 13:23
  • 2
    For one, you should never call `mainloop` more than once, and definitely not within a loop. – Bryan Oakley Feb 08 '18 at 13:25
  • I think [this question & answers about switching frames](https://stackoverflow.com/q/7546050/7032856) could help you. – Nae Feb 08 '18 at 13:30
  • What you want could be done with using methods like above, but it would be way simpler with OOP, by creating a `Toplevel` subclass. – Nae Feb 08 '18 at 13:39
  • removing win.mainloop() is not helping ...It is creating many unwanted windows ...I am new to python...so can u please help me how to make this functionality... – Ani Feb 08 '18 at 13:39
  • You created a loop that will always create all the windows at once. You need to set it up to only create one window at a time using the list as your reference. – Mike - SMT Feb 08 '18 at 14:17
  • Note that when widgets are created w/o the explicit pass of master argument they become child to the current `Tk` instance, which is `root` in the above code, essentially making the widgets appear in the current `Tk` instance. – Nae Feb 08 '18 at 15:08

2 Answers2

0

"Why tkinter toplevel can't be created repeatedly inside a loop in Python3?"

It can be created repeatedly inside a loop:

try:                        # In order to be able to import tkinter for
    import tkinter as tk    # either in python 2 or in python 3
except ImportError:
    import Tkinter as tk


if __name__ == '__main__':
    root = tk.Tk()
    for _ in range(5):
        tk.Toplevel(root, bg='red')
    root.mainloop()

In the OP's code win.mainloop blocks moving further in the loop it is in as it is a 'while True' loop itself.


One good way to achieve the required behavior would be to create a subclass of Toplevel and call them one by one:

try:                        # In order to be able to import tkinter for
    import tkinter as tk    # either in python 2 or in python 3
except ImportError:
    import Tkinter as tk


class MyToplevel(tk.Toplevel):
    def __init__(self, master, text):
        tk.Toplevel.__init__(self, master)
        self.label = tk.Label(self, text=text)
        self.button = tk.Button(self, text="Next", command=self.destroy)
        self.label.grid(row=0, column=0)
        self.button.grid(row=1, column=0)


def next_window(window_widget, index, a_list):
    if window_widget:
        window_widget.destroy()
    if index < len(a_list):
        window_widget = MyToplevel(root, a_list[index])
        if (index + 1) < len(a_list):
            window_widget.button['command'] = lambda: next_window(
                                            window_widget, index + 1, a_list)


if __name__ == '__main__':
    root = tk.Tk()
    a_list = [1, 2, 3, 4, 5]
    current_window = None
    go_button = tk.Button(root, text="Go", command=lambda:next_window(
                                                current_window, 0, a_list))
    go_button.pack()
    root.mainloop()
Nae
  • 14,209
  • 7
  • 52
  • 79
  • Perhaps a better way would be to write this by making use of class variables to count the instance numbers. – Nae Feb 08 '18 at 15:05
0

You should not use a loop to create your windows. The way you have set up your function will just create all the windows at once.

The below code will create windows based on the list itself so all you will need to do is update the list when you want to change the number of pages that the "Next" button will go to.

Keep in mind this way is probably not the best method of working with the list. For a clearer more OOP method you will probably want to write something up in a Class. The below just serves to show how one could use the list to decide on what Toplevel to create next.

from tkinter import *

root = Tk()

mylist = [1,2,3,4,5]

current_toplevel = None

def go_cmd(x):
    # global is used to make variables that are outside of the function
    # in the global namespace accessible.
    global current_toplevel, mylist

    wx = root.winfo_x()
    wy = root.winfo_y()
    next_index = x + 1

    # This will check if the next_index variable will be within the available
    # index range and if next_index is outside the index range it will reset
    # to index zero. This will prevent the "outside index" error.
    if next_index not in list(range(len(mylist))):
        next_index = 0

    # if the variable current_toplevel is not set to none then destroy it
    # so we can create the next window.
    if current_toplevel != None:
        current_toplevel.destroy()

    current_toplevel = Toplevel()

    # set the location of the new top level window based off of the
    # root windows location. This can be changed to anything but
    # I wanted to use this as the example.
    current_toplevel.geometry("+{}+{}".format(wx, wy))

    Label(current_toplevel, width = 10, text = mylist[x]).grid(row=0,column=0)

    # because we need to prep the "Next" button for the next index
    # we will need to use a lambda command for getting the next window
    b = Button(current_toplevel, width = 10, text="Next",
               command = lambda a = next_index: go_cmd(a)).grid(row=1,column=0)

b1 = Button(root,text = "Go", width = 10,
            command = lambda: go_cmd(0)).grid(row=0,column=0)

root.mainloop()

Here is the code without all the instructional comments.

from tkinter import *

root = Tk()

mylist = [1,2,3,4,5]

current_toplevel = None

def go_cmd(x):
    global current_toplevel, mylist

    wx = root.winfo_x()
    wy = root.winfo_y()
    next_index = x + 1

    if next_index not in list(range(len(mylist))):
        next_index = 0

    if current_toplevel != None:
        current_toplevel.destroy()

    current_toplevel = Toplevel()
    current_toplevel.geometry("+{}+{}".format(wx, wy))

    Label(current_toplevel, width = 10, text = mylist[x]).grid(row=0,column=0)

    b = Button(current_toplevel, width = 10, text="Next",
               command = lambda a = next_index: go_cmd(a)).grid(row=1,column=0)

b1 = Button(root,text = "Go", width = 10,
            command = lambda: go_cmd(0)).grid(row=0,column=0)

root.mainloop()
Mike - SMT
  • 14,784
  • 4
  • 35
  • 79