2

I'm trying to make a Python GUI using tkinter, and I need a menu item that opens another copy of the main window. I tried to do the following code, and when I ran the program, it froze for a bit, then opened a large number of windows. The last error message printed is below.

I have two questions.

  1. How can I accomplish the task of making the "New" button open a new window and instance of the TheThing class? (In IDLE, File > New File has the behavior I'm seeking.)
  2. Why is this error happening?

    RecursionError: maximum recursion depth exceeded while calling a Python object
    

My code:

import tkinter as tk

class TheThing:
    def __init__(self, root):
        root.option_add('*tearOff', False)

        menubar = tk.Menu(root)
        root.config(menu = menubar)

        file = tk.Menu(menubar)
        menubar.add_cascade(menu = file, label = "File")
        file.add_command(label = 'New', command = doathing())

def doathing():
    thing1 = tk.Tk()
    thing2 = TheThing(thing1)

def main():
    win = tk.Tk()
    do = TheThing(win)
    win.mainloop()

if __name__ == '__main__': main()

Places I've already looked for answers:

  • This question seemed like it was having a very similar problem. I may be able to study that and find a solution, but I still won't understand the problem.

  • This question was about recursion, python, and tkinter, but seemed to be about more the after thing.

Community
  • 1
  • 1
rod
  • 23
  • 3
  • `command = doathing()` is executing the callback right away (and in the callback creates a new window, in an endless recursion) and binds its result to the `command` parameter. remove the `()`. Also, what's that supposed to do anyway? – tobias_k Aug 16 '16 at 21:11
  • That fixed it. I removed the `()`, and it seemingly works as intended. It's supposed to open a new window, exactly like the old window. The Python IDLE text editor has this behavior, under `File > New File`. Thank you. – rod Aug 16 '16 at 21:14

1 Answers1

2

The problem is in this line:

    file.add_command(label = 'New', command = doathing())

Here, you execute the doathing callback and then try to bind its result (which is None) to the command. In this specific case, this also leads to an infinite recursion, as the callback will create a new instance of the frame, which will again execute the callback, which will create another frame, and so on. Instead of calling the function, you have to bind the function itself to the command.

    file.add_command(label = 'New', command = doathing)  # no ()

In case you need to pass parameters to that function (not the case here) you can use a lambda:

    file.add_command(label = 'New', command = lambda: doathing(params))

Also, instead of creating another Tk instance you should probably just create a Toplevel instance in the callback, i.e.

def doathing():
    thing1 = tk.Toplevel()
    thing2 = TheThing(thing1)
tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • Thank you. Your answer was extremely helpful. Not only did you answer the question I'd asked, but you answered a lot about some questions I was wondering as I was reading your answer. – rod Aug 16 '16 at 21:39