Search code examples
python-3.xtkintermodulenameerror

why does importing a tk variable from main break this code?


I'm trying to organize my code into two separate modules - main, and new_window main passes a tk object to new_window to populate it with a label and a button. Pressing the button is supposed to close the window.

This is main:

import tkinter as tk
from new_window import *

#I create a tk object called "main_menu"
main_menu = tk.Tk()
#The tk object is passed to a class in another module to have its properties applied
main_menu_obj = MainMenu(main_menu)
#the main loop function is called as required by tk
main_menu.mainloop()

This is new_window:

import tkinter as tk
#to close the window, the tk variable created in main must is imported
from main import main_menu

class MainMenu:
    #when this module receives the tk object from main, it adds a label and a button
    def __init__(self, root):
        print('main menu')
        root.title('Main Menu')
        Label_1 = tk.Label(root, text='Main Menu')
        Button_1 = tk.Button(root, text='close',command=self.close)

        Label_1.pack()
        Button_1.pack()

    #when the button is pressed, the tk object "main_menu" is destroyed
    def close(self):
        print('closing window')
        main.main_menu.destroy()

When I run this, I receive: NameError: name 'MainMenu' is not defined. But I just defined this class in new_window. What mistake am I making? If I comment out "from main import main_menu" and comment out "main.main_menu.destroy()" it works great and prints "closing window" when I press the close button. (but obviously it doesn't close the window then).


Solution

  • It is circular import issue, i.e. A imports B and B imports A.

    Actually you don't need to import main inside new_window because you have already passed the reference of the root window to MainMenu class. Therefore you can use this reference to close the root window.

    Updated new_window.py:

    import tkinter as tk
    # don't need to import main here
    
    class MainMenu:
        #when this module receives the tk object from main, it adds a label and a button
        def __init__(self, root):
            # save argument root
            self.root = root
    
            print('main menu')
            root.title('Main Menu')
            Label_1 = tk.Label(root, text='Main Menu')
            Button_1 = tk.Button(root, text='close',command=self.close)
    
            Label_1.pack()
            Button_1.pack()
    
        #when the button is pressed, the tk object "main_menu" is destroyed
        def close(self):
            print('closing window')
            self.root.destroy()