Sub import issues with tkinter messagebox

Dude Random21

I'm writing a GUI application with tkinter, now I wanted to use the messageboxes that tkinter has built in, so I figured since I already have tkinter imported (as tk) it would just be tk.messagebox.showerror() but this more or less worked...

I when running this through IDLE it worked with no issues but if I use python <module-name>.py in the terminal it throws an AttributeError.

Here is the simplest sample I could make:

import tkinter as tk

root = tk.Tk()
tk.messagebox.showerror(title="Test Error", message="This is a test")
root.mainloop()

Now to make things even stranger if I add from tkinter import messagebox as an import than this same code works when run from the terminal (keeping the tk.messagebox.showerror). Obviously it must still be viewed as an attribute of tkinter so why doesn't it work without the explicit import?

I guess I'm not really looking for solution as such (since I found some way to make it work) but more an explanation of why this is happening.

And as always any suggestions are welcome!

Terry Jan Reedy

This question has arisen before in the form of "Why does import tkinter; tkinter.font works in IDLE but not in python itself?" The explanation is the interaction between how import works and how IDLE currently sets up the subprocess where it executes your code.

IMPORT: sys.modules is a dictionary that maps module names to imported module objects. At a high-level, import modname is equivalent to the following.

if modname not in sys.modules:
    sys.modules[modname] = module(modname)
modname = sys.modules[modname]

For a python-coded package, the module is created from modname/__init__.py. Importing a package does not import its submodules. Which is to say, it does not run modname/submodule.py. Subsequently importing a submodule adds the submodule to sys.modules and to the package as an attribute. Importing the module after that binds the existing module, which has a submodule as an attribute, to the name specified.

IDLE: Currently when IDLE sets up a subprocess for running your code, it directly or indirectly imports tkinter and its submodules. (The code is in idlelib/run.py and the idlelib modules it imports.) This can be seen by running

for name in sys.modules:
    if name.startswith('tkinter'):
        print(name)

when IDLE starts. So, when you run import tkinter, it is the second tkinter import in the process, and the name 'tkinter' is bound to the existing tkinter module that has submodules as attributes

Solution: A purpose of IDLE is to develop code that runs in python itself. It is a bug for IDLE to run buggy code that will not run in python. I am working on refactoring idlelib so that the above will not print anything. The only tkinter module ever needed is tkinter.messagebox and that is only needed if there is a problem that prevents running your code. So importing messagebox can be delayed until it is needed, which is very seldom.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related