Attached is a minimum working example of a problem I have:
class A():
def __init__(self, *args, **kwargs):
self.b=B(self)
self.c=C(self)
class B():
def __init__(self, parent):
self.a1=3
class C():
def __init__(self, parent):
self.a2=4
print(B.a1)
def ab():
print(B.a1)
C(object).ab()
What is the best way to access in class C the object of class B?
In principle, I built a TKinter App with every frame being a class and I want in one frame to access the variable of another frame.
EDIT @all thank you all fpr the input. I expanded the minimum working example to my specific problem:
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox
class main_window(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("xxx")
self.geometry("800x1000")
#Build GUI
self.inputframe = INPUTframe(self)
self.imregframe = IMREGframe(self)
self.calcframe = CALCframe(self)
class INPUTframe(tk.Frame):
def __init__(self, parent, width=800, height=150):
tk.Frame.__init__(self, parent, width=width, height=height)
self.place(x=0, y=0)
label= ttk.Label(self, text="xxx", font=('Arial',12))
label.place(x=10,y=0)
self.create_widgets()
def create_widgets(self):
label1 = tk.Label(self, text="xxx", font=('Arial',10))
label1.place(x=30,y=30)
button1 = tk.Button(self, text='xxx',command = lambda: self.path(1))
button1.place(x=100,y=30)
self.textentry1=tk.StringVar()
self.entry1 = tk.Entry (self, textvariable= self.textentry1)
self.entry1.place(x=160,y=30,width="350")
label11 = tk.Label(self, text="xxx", font=('Arial',10))
label11.place(x=500,y=30)
def path(self,var):
match var:
case 1:
self.entry1.delete(0, 'end')
filename = filedialog.askopenfilename(parent=self,title='Choose a file')
self.entry1.insert("end",filename)
return filename
class IMREGframe(tk.Frame):
def __init__(self, parent, width=250, height=150):
tk.Frame.__init__(self, parent, width=width, height=height)
self.place(x=0,y=150)
label4 = tk.Label(self, text="xxx", font=('Arial',12))
label4.place(x=10,y=0)
self.SFAtrue=tk.BooleanVar(value=True)
self.create_widgets()
def create_widgets(self):
#self.SFAtrue=tk.BooleanVar(value=True)
checkb1 = tk.Checkbutton(self, text='xxx', variable=self.SFAtrue, onvalue=0, offvalue=1)
checkb1.place(x=30,y=30)
checkb2 = tk.Checkbutton(self, text='xxx', variable=self.SFAtrue, onvalue=1, offvalue=0)
checkb2.place(x=30,y=50)
class CALCframe(tk.Frame):
def __init__(self, parent, width=800, height=100):
tk.Frame.__init__(self, parent, width=width, height=height)
self.place(x=0,y=370)
self.parent=parent
self.create_widgets()
def create_widgets(self):
button4 = tk.Button(self, text='xxx', command = lambda: self.calculate(self.parent.imregframe.SFAtrue.get())) #bester Weg um Variabele aus anderer Klasse/Objekt zu holen?
button4.config( height = 4, width = 12 )
button4.place(x=50,y=0)
self.Previewtrue=tk.BooleanVar(value=False)
checkb3 = tk.Checkbutton(self, text='xxx',variable=self.Previewtrue, onvalue=1, offvalue=0)
checkb3.place(x=150,y=0)
label5 = tk.Label(self,text="xxx", font=("Arial", 9))
label5.place(x=150,y=30)
label51 = tk.Label(self,text="xxx", font=("Arial", 9))
label51.place(x=150,y=50)
def calculate (self, calcType):
print(calcType)
print(self.parent.inputframe.textentry1.get())
main_window().mainloop()
The thin I was missing was self.parent=parent as suggested by @Obaskly and it works now. Is this a good way to do it? The variables get quite long: self.parent.inputframe.textentry1.get()
There are two things wrong with your code:
You are trying to access B.a1
, which is incorrect because a1
is an instance variable, not a class variable. You should access it through the instance of B
, which is held by A
.
The ab()
method inside C
should accept self
as the first parameter to be an instance method.
You need to maintain a reference to the B
instance created in the A
class.
It should look like this, so that the C
class can access the B
instance and its attributes via its parent A
.
class A():
def __init__(self, *args, **kwargs):
self.b = B(self)
self.c = C(self)
class B():
def __init__(self, parent):
self.a1 = 3
class C():
def __init__(self, parent):
self.parent = parent # Reference to the parent class
self.a2 = 4
print(self.parent.b.a1) # Access B's a1 via the parent
def ab(self):
print(self.parent.b.a1)
# Create an instance of A
instance_of_A = A()
# Access method ab() of class C through instance of A
instance_of_A.c.ab()
EDIT
The variable names can become quite long, that's for sure. There are few techniques you can use. Like using references, for example you can create shortcut references to frequently accessed widgets or variables.
class CALCframe(tk.Frame):
def __init__(self, parent, width=800, height=100):
# ...
# Shortcut references
self.imregframe = self.parent.imregframe
self.inputframe = self.parent.inputframe
self.create_widgets()
def create_widgets(self):
button4 = tk.Button(self, text='xxx', command=lambda: self.calculate(self.imregframe.SFAtrue.get()))
button4.config(height=4, width=12)
button4.place(x=50, y=0)
# ...
def calculate(self, calcType):
print(calcType)
print(self.inputframe.textentry1.get())
Or you can use a controller Class; that holds the state of your application and allows frames to interact through it.
class AController:
def __init__(self):
self.main_window = main_window(self)
def get_SFAtrue(self):
return self.main_window.imregframe.SFAtrue.get()
def get_textentry1(self):
return self.main_window.inputframe.textentry1.get()
class main_window(tk.Tk):
def __init__(self, controller, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.controller = controller
self.title("xxx")
self.geometry("800x1000")
self.inputframe = INPUTframe(self)
self.imregframe = IMREGframe(self)
self.calcframe = CALCframe(self)
class CALCframe(tk.Frame):
def __init__(self, parent, width=800, height=100):
tk.Frame.__init__(self, parent, width=width, height=height)
self.controller = parent.controller
self.create_widgets()
def create_widgets(self):
button4 = tk.Button(self, text='xxx', command=self.calculate)
button4.config(height=4, width=12)
button4.place(x=50, y=0)
def calculate(self):
calcType = self.controller.get_SFAtrue()
textEntry1 = self.controller.get_textentry1()
print(calcType)
print(textEntry1)
AController()