我的tkinter/customtkinter使用高于另一个帧的多个帧。我有两个框架,两个都包含开关,需要相互反转(即当一个是‘开’,另一个必须‘关闭’)。这两个开关都是作为init方法中的实例对象创建的,在它们的“命令”参数中使用一个函数来更改彼此的状态。
最初,我试图直接调用每个对象:
class MainModes(customtkinter.CTkFrame):
def__init__(self, parent)
customtkinter.CTkframe.__init__(self, parent)
self.frame_1 = customtkinter.CTkFrame(self)
self.frame_1.pack()
self.frame_2 = customtkinter.CTkFrame(self)
self.frame_2.pack()
self.switch_1 = customtkinter.CTkSwitch(self.frame_1,
text='Switch 1',
command=lambda: self.switch_2.toggle())
self.switch_2 = customtkinter.CTkSwitch(self.frame_2,
text='Switch 2',
command=lambda: self.switch_1.toggle())这会产生以下错误:
command=lambda: self.switch_2.toggle()
AttributeError: 'MainModes' has no object 'switch_2'我假设这是在定义switch_2之前引用的,但是我对我的理解并不有信心,因为我认为如果是这样的话,它会产生一个NameError (我猜这是相同的错误动态,但是因为我在一个类中,它就是一个AttributeError?)。
相反,我尝试创建一个方法来处理这个问题:
class MainModes(customtkinter.CTkFrame):
def__init__(self, parent)
customtkinter.CTkframe.__init__(self, parent)
self.frame_1 = customtkinter.CTkFrame(self)
self.frame_1.pack()
self.frame_2 = customtkinter.CTkFrame(self)
self.frame_2.pack()
self.switch_1 = customtkinter.CTkSwitch(self.frame_1,
text='Switch 1',
command=lambda: self.toggle_switch(switch_2))
self.switch_2 = customtkinter.CTkSwitch(self.frame_2,
text='Switch 2',
command=lambda: self.toggle_switch(switch_1))
def toggle_switch(self, switch):
self.switch.toggle()这会产生以下错误:
command=lambda: self.toggle_switch(self.switch_2)
AttributeError: 'MainModes' has no attribute 'switch_2'唯一的不同之处在于,这里的措辞已从“对象”改为“属性”。
最后,我试图在init方法中使用Fun广子来处理它,但可以预见,这失败了:
class MainModes(customtkinter.CTkFrame):
def__init__(self, parent)
customtkinter.CTkframe.__init__(self, parent)
self.frame_1 = customtkinter.CTkFrame(self)
self.frame_1.pack()
self.frame_2 = customtkinter.CTkFrame(self)
self.frame_2.pack()
def toggle_switch(switch):
self.switch.toggle()
self.switch_1 = customtkinter.CTkSwitch(self.frame_1,
text='Switch 1',
command=lambda: toggle_switch(switch_2))
self.switch_2 = customtkinter.CTkSwitch(self.frame_2,
text='Switch 2',
command=lambda: toggle_switch(switch_1))这会产生原始错误:
command=lambda: self.switch_2.toggle()
AttributeError: 'MainModes' has no object 'switch_2'我知道这是一个范围确定的问题,就好像我从switch_1对象命令参数中删除函数,然后按需要删除switch_2函数一样。我相信这是一个重复的问题,我已经研究了其中的问题,但我无法找到解决办法。
此外,我发现这一点很难理解,因为在相同的代码中,我有一些按钮,它们引用在init方法中创建的函数,这些函数可以相互重新配置,而且我没有遇到任何这些错误。我把自己彻底搞糊涂了。
编辑:我认为最初的例子可能会给出足够的信息,从概念上看到底是怎么回事,但我知道它们并没有重复这个问题。我在下面包含了一个完全工作的小示例,它展示了GUI的基本结构(现在可能走得太远了):
import tkinter
import customtkinter
# Main application
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
#container to pack different windows of the app into
container = customtkinter.CTkFrame(self)
container.pack(expand=True, fill='both')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
self.frames['homescreen'] = HomeScreen(container, self)
self.frames['page_1'] = MainModes(container, self)
for F in ('homescreen', 'page_1'):
self.frames[F].grid(row = 0, column = 0, sticky='nsew')
self.show_frame('homescreen')
def show_frame(self, page_class):
frame = self.frames[page_class]
frame.tkraise()
class HomeScreen(customtkinter.CTkFrame):
def __init__(self, parent, controller):
customtkinter.CTkFrame.__init__(self, parent)
self.controller = controller
#Configure rows and columns
self.grid_rowconfigure(0, weight=1)
self.grid_rowconfigure(1, weight=1)
#Define buttons
page_1_button = customtkinter.CTkButton(self,
text="Page 1",
command=lambda: controller.show_frame('page_1'))
#Position of buttons in the main_window
page_1_button.grid(row=0, column=0, sticky='nsew')
class MainModes(customtkinter.CTkFrame):
def __init__(self, parent, controller):
customtkinter.CTkFrame.__init__(self, parent)
self.controller = controller
#overall layout
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
self.grid_rowconfigure(0, weight=1) #mode_1 and mode_2 tabs are contained here
self.grid_rowconfigure(1, weight=1) #all widgets are contained in two frames in this row, clicking between mode_1 and mode_2 buttons raises different frames containing different widgets
self.grid_rowconfigure(2, weight=1) #back button is here
self.frame = customtkinter.CTkFrame(self) #this frame contains the mode_1 and mode_2 frames and they are raised over one another according to which tab is selected
self.frame.grid_rowconfigure(0, weight=1)
self.frame.grid_columnconfigure(0, weight=1)
#====================================Mode 1 Frame====================================#
self.mode_1_frame = customtkinter.CTkFrame(self.frame)
self.mode_1_frame.grid_columnconfigure(0, weight=1)
self.mode_1_frame.grid_rowconfigure(0, weight=1)
self.mode_1_frame.grid(row=0, column=0, sticky='nsew')
#====================================Mode 2 Frame====================================#
self.mode_2_frame = customtkinter.CTkFrame(self.frame)
self.mode_2_frame.grid_columnconfigure(0, weight=1)
self.mode_2_frame.grid_rowconfigure(0, weight=1)
self.mode_2_frame.grid(row=0, column=0, sticky='nsew')
#====================================Mode 1 Frame Widgets====================================#
self.mode_1_switch_var = tkinter.StringVar(self.mode_1_frame)
self.mode_1_switch_var.set(value='Mode 1: ON')
#function that sets the textvariable values of mode_1_switch and mode_2_switch when either is toggled
def switch_functions(switch_var, mode, switch):
switch_var.set(value=f'{mode}: ' + switch.get())
self.mode_1_switch = customtkinter.CTkSwitch(self.mode_1_frame,
textvariable=self.mode_1_switch_var,
onvalue='ON',
offvalue='OFF',
command=lambda: [switch_functions(self.mode_1_switch_var, 'Mode 1', self.mode_1_switch), self.mode_2_switch.toggle()])
self.mode_1_switch.select()#turns switch on at open
self.mode_1_switch.grid(row=0, column=0)
#====================================Mode_2 Frame Widgets====================================#
self.mode_2_switch_var = tkinter.StringVar(self.mode_2_frame)
self.mode_2_switch_var.set(value='Mode 2: OFF')
self.mode_2_switch = customtkinter.CTkSwitch(self.mode_2_frame,
textvariable=self.mode_2_switch_var,
onvalue='ON',
offvalue='OFF',
command=lambda: [switch_functions(self.mode_2_switch_var, 'Mode 2', self.mode_2_switch), self.mode_1_switch.toggle()])
self.mode_2_switch.grid(row=0, column=0)
#====================================Frame toggle and back buttons====================================#
self.mode_2_button = customtkinter.CTkButton(self,
text='Mode 2',
command=lambda: self.mode_2_frame.tkraise())
self.mode_1_button = customtkinter.CTkButton(self,
text = 'Mode 1',
command=lambda: self.mode_1_frame.tkraise())
self.back_button = customtkinter.CTkButton(self,
text='Back',
command=lambda: controller.show_frame('homescreen'))
self.mode_1_button.grid(row=0, column=0, sticky='nsew')
self.mode_2_button.grid(row=0, column=1, sticky='nsew')
self.frame.grid(row=1, columnspan=2, sticky='nsew')
self.back_button.grid(row=2, column=0, columnspan=2, sticky='nsew')
self.mode_1_frame.tkraise()
if __name__ == '__main__':
app = App()
app.mainloop()发布于 2022-05-10 15:23:37
“在创建开关后尝试配置”命令:
self.switch_1.config(command=lambda: self.switch_2.toggle())和
self.switch_2.config(command=lambda: self.switch_1.toggle()) 在init()方法的末尾。-西尔维斯特·库恩
这绝对是正确的方法,不幸的是,问题是(我真的应该看到!)每当任何一个开关被切换时,它都会创建一个无限的回调函数交换,因为它们之间不断地来回切换。我通过在customtkinter模块中添加一个控制参数来解决这个问题,这样就可以传递一个参数来阻止正在执行的开关的命令函数。这可能是错误的做法,但它对我有效。
https://stackoverflow.com/questions/72172263
复制相似问题