我的 tkinter/customtkinter GUI 使用了多個相互疊加的框架。我有兩個框架,它們都包含需要相互反轉的開關(即,當一個為“ON”時,另一個必須為“OFF”)。這兩個開關都是作為 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方法中的函式來處理它,但可以預見的是,這失敗了:
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()
uj5u.com熱心網友回復:
創建后“嘗試配置開關”命令:
self.switch_1.config(command=lambda: self.switch_2.toggle())
和
self.switch_2.config(command=lambda: self.switch_1.toggle())
在init () 方法的末尾。——西爾維斯特·克魯因
這絕對是正確的方法,不幸的是問題是(我真的應該看到!)每當切換任何一個開關時,它都會創建一個無限的回呼函式交換,因為它們不斷地相互切換。我通過將一個控制引數放入 customtkinter 模塊來解決這個問題,這樣可以傳遞一個引數來停止正在執行的開關的命令功能。這可能是錯誤的做事方式,但它對我有用。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/474322.html
