2

I have started working on a GUI system where I need to import a function from one file to be executed in the main file when a button is pressed, but every time I run it I get:

AttributeError: partially initialized module 'Two' has no attribute 'sum'
  (most likely due to a circular import)

The program is supposed to input two values, Value_a and Value_b, and the function being called sum() is supposed to add the two and output a result in a new window. Here is an example of the file I want to import and its function sum():

Two.py:

from tkinter import *  #Import the tkinter module    
import One #This is the main file, One.py

def sum():
    newWindow = Toplevel(One.Window)
    newWindow.title("Sum")
    a = int(One.Value_a.get())
    b = int(One.Value_b.get())
    c = a+b
    Label(newWindow, text= str(c)).grid(row=1, column=0)

This is how the main file looks like:

One.py:

from tkinter import *
import Two

Window = Tk()
Window.title("Main Window")

Value_a = Entry(Window, width=15).grid(row=1, column=0)
Value_b = Entry(Window, width=15).grid(row=2, column=0)
my_button = Button(Window, text="Test", command=lambda: Two.sum).grid(row=3, column=0)

Window.mainloop()

When this is run, I end up getting the above error.

2
  • 1
    Importing One inside Two.py will create another instance of Tk (One.Window), not using the instance of Tk (Window) when running One.py. You need to pass Window, Value_a and Value_b to Two.sum() instead and don't import One inside Two.py. Commented Mar 19, 2021 at 6:36
  • @acw1668: It's not quite that easy. See answer I posted. Commented Mar 19, 2021 at 7:20

2 Answers 2

2

The problem is because you really do have a circular import. Module One imports module Two which imports module One... etc. However the simple fix @acw1668 suggested isn't enough to fix things because the Two module references more than just the Window attribute of the One module. My solution passes the things in module One the function in module Two needs as arguments (so the Two module doesn't need to import it to get access to them).

Another problem with your tkinter code is discussed in the question Tkinter: AttributeError: NoneType object has no attribute , which I suggest you read.

Below are changes to both your modules that fix all these issues.

One.py:

from tkinter import *
import Two


Window = Tk()
Window.title("Main Window")

Value_a = Entry(Window, width=15)
Value_a.grid(row=1, column=0)
Value_b = Entry(Window, width=15)
Value_b.grid(row=2, column=0)

my_button = Button(Window, text="Test",
                   command=lambda: Two.sum(Window, Value_a, Value_b))
my_button.grid(row=3, column=0)

Window.mainloop()

Two.py:

from tkinter import *


def sum(Window, Value_a, Value_b):
    newWindow = Toplevel(Window)
    newWindow.title("Sum")
    a = int(Value_a.get())
    b = int(Value_b.get())
    c = a+b
    Label(newWindow, text= str(c)).grid(row=1, column=0)
Sign up to request clarification or add additional context in comments.

6 Comments

I would prefer using class in One.py and pass the instance of the class to Two.sum().
@acw1668: That would work of course, but it would be even simpler to simply pass multiple arguments to sum().
@acw1668: Since it's so much simpler, I've modified my answer to do it like that — the whole sys.modules approach was just too weird…
Although it is simpler, but if later Two.sum() needs to refer another widget then the function definition is required to be changed. Your original design (or my suggestion of using class instance) need not to change the definition of Two.sum(), only need to change the content of the function.
@martineau: Thanks for the solution. The program works as I wanted it thanks to your adjustment on the code. Thanks.
|
0

(Here's another answer which is very similar to what I initially had in my other answer before doing something I felt was simpler. I'm posting it as separate answer because it turns out that the idea of a module getting a reference to itself has been around for a very long time, so doing so isn't that weird or as hackish as I initially thought.)

The problem is because you really do have a circular import. Module One imports module Two which imports module One... etc. However the simple fix @acw1668 suggested isn't enough to fix things because the Two module references more than just the Window attribute of the One module. My solution passes the whole One module to the function as an argument (so the Two module doesn't need to import it to get access to its attributes).

Another problem with your tkinter code is discussed in the question Tkinter: AttributeError: NoneType object has no attribute , which I suggest you read.

Below are changes to both your modules that fix all these issues. To avoid the circular import, the Button command now passes the calling module as an argument to the sum() function in module Two. While doing something like this is somewhat unusual, in fact is a very logical if you think about it (and want to avoid a circular import`).

One.py:

from tkinter import *
import Two

CURRENT_MODULE = __import__(__name__)

Window = Tk()
Window.title("Main Window")

Value_a = Entry(Window, width=15)
Value_a.grid(row=1, column=0)
Value_b = Entry(Window, width=15)
Value_b.grid(row=2, column=0)

my_button = Button(Window, text="Test", command=lambda: Two.sum(CURRENT_MODULE))
my_button.grid(row=3, column=0)

Window.mainloop()

Two.py:

from tkinter import *

def sum(One):
    newWindow = Toplevel(One.Window)
    newWindow.title("Sum")
    a = int(One.Value_a.get())
    b = int(One.Value_b.get())
    c = a+b
    Label(newWindow, text= str(c)).grid(row=1, column=0)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.