Прилагается минимальный рабочий пример проблемы, с которой я столкнулся:
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()
Каков наилучший способ доступа в классе C к объекту класса B?
В принципе, я создал приложение TKinter, в котором каждый кадр является классом, и я хочу, чтобы в одном кадре был доступ к переменной другого кадра.
РЕДАКТИРОВАТЬ @all, спасибо всем за вклад. Я расширил минимальный рабочий пример для своей конкретной проблемы:
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()
То, чего мне не хватало, было self.parent=parent, как было предложено @Obaskly, и теперь оно работает. Это хороший способ сделать это? Переменные становятся довольно длинными: self.parent.inputframe.textentry1.get()
Итак, вы хотите получить доступ к экземпляру B
из C
?
@topsail, как это могло выглядеть? А конкретно ограничение определенных атрибутов?
@Obaskly Да, именно
Вам нужно немного больше кода, чтобы быть минимальным примером: вы утверждаете, что ваше tkinter
приложение имеет несколько фреймов, но ваш опубликованный код не создает экземпляры нескольких классов.
Доступ к атрибуту класса можно получить только через экземпляр класса. Для вашего опубликованного кода не создан экземпляр класса B.
В вашем коде есть две ошибки:
Вы пытаетесь получить доступ к B.a1
, что неверно, поскольку a1
— это переменная экземпляра, а не переменная класса. Вы должны получить к нему доступ через экземпляр B
, который принадлежит A
.
Метод ab()
внутри C
должен принимать self
в качестве первого параметра, который будет методом экземпляра.
Вам необходимо сохранить ссылку на экземпляр B
, созданный в классе A
.
Это должно выглядеть так, чтобы класс C
мог получить доступ к экземпляру B
и его атрибутам через своего родителя 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()
РЕДАКТИРОВАТЬ
Имена переменных могут быть довольно длинными, это точно. Есть несколько методов, которые вы можете использовать. Например, как и при использовании ссылок, вы можете создавать ярлыки ссылок на часто используемые виджеты или переменные.
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())
Или вы можете использовать класс контроллера; который хранит состояние вашего приложения и позволяет фреймам взаимодействовать через него.
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()
Спасибо. Кажется, мне не хватало этого self.parent = родителя. Я добавил к своему вопросу расширенный минимальный пример. Не могли бы вы взглянуть на это, если это лучший способ?
@TMul Я отредактировал свой ответ, добавив несколько предложений
@Обласки, спасибо
Если вы хотите избежать длинных цепочек точек и лучше соблюдать Закон Деметры, я бы попросил основной класс передавать переменные, а не требовать от других классов их извлечения.
В идеале CALCframe
не нужно знать внутренности main_window
, INPUTframe
и IMREGframe
. В противном случае, если вы решите повторно использовать CALCframe
в другом месте или вам понадобится вложить его дальше, он перестанет работать.
В вашем конкретном случае простой способ обойти это — передать main_window
переменные Tkinter INPUTframe
и IMREGframe
в CALCframe
. Это отделяет CALCframe
и позволяет использовать его с любым родителем, если вы передаете соответствующие переменные.
Вот ваш код обновлен, чтобы показать, что я имею в виду:
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, self.inputframe.textentry1, self.imregframe.SFAtrue)
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, textentry1, SFAtrue, width=800, height=100):
tk.Frame.__init__(self, parent, width=width, height=height)
self.place(x=0,y=370)
self.parent=parent
self.SFAtrue = SFAtrue
self.textentry1 = textentry1
self.create_widgets()
def create_widgets(self):
button4 = tk.Button(self, text='xxx', command = lambda: self.calculate(self.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.textentry1.get())
main_window().mainloop()
большое спасибо! Это кажется хорошим. Я попробую.
Вы можете разрешить классу передавать ссылку на себя в другой класс, раскрывая свое «я» как свойство или через метод. Обычно вы не хотите, чтобы классы выдавали всю информацию о себе таким образом, но это может сработать (обратной стороной является то, что вещи за пределами этого класса могут в основном испортить все, что касается класса, если вы слишком небрежны в передаче таких ссылок). Возможно, было бы лучше, если бы классы разрешали доступ только к тем вещам, которые они содержат, и предназначены для совместного использования.