首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在tkinter/customtkinter中使用实例属性和类方法的范围问题

在tkinter/customtkinter中使用实例属性和类方法的范围问题
EN

Stack Overflow用户
提问于 2022-05-09 12:47:06
回答 1查看 229关注 0票数 0

我的tkinter/customtkinter使用高于另一个帧的多个帧。我有两个框架,两个都包含开关,需要相互反转(即当一个是‘开’,另一个必须‘关闭’)。这两个开关都是作为init方法中的实例对象创建的,在它们的“命令”参数中使用一个函数来更改彼此的状态。

最初,我试图直接调用每个对象:

代码语言:javascript
复制
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())

这会产生以下错误:

代码语言:javascript
复制
    command=lambda: self.switch_2.toggle() 
AttributeError: 'MainModes' has no object 'switch_2'

我假设这是在定义switch_2之前引用的,但是我对我的理解并不有信心,因为我认为如果是这样的话,它会产生一个NameError (我猜这是相同的错误动态,但是因为我在一个类中,它就是一个AttributeError?)。

相反,我尝试创建一个方法来处理这个问题:

代码语言:javascript
复制
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()

这会产生以下错误:

代码语言:javascript
复制
    command=lambda: self.toggle_switch(self.switch_2) 
AttributeError: 'MainModes' has no attribute 'switch_2'

唯一的不同之处在于,这里的措辞已从“对象”改为“属性”。

最后,我试图在init方法中使用Fun广子来处理它,但可以预见,这失败了:

代码语言:javascript
复制
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))

这会产生原始错误:

代码语言:javascript
复制
    command=lambda: self.switch_2.toggle() 
AttributeError: 'MainModes' has no object 'switch_2'

我知道这是一个范围确定的问题,就好像我从switch_1对象命令参数中删除函数,然后按需要删除switch_2函数一样。我相信这是一个重复的问题,我已经研究了其中的问题,但我无法找到解决办法。

此外,我发现这一点很难理解,因为在相同的代码中,我有一些按钮,它们引用在init方法中创建的函数,这些函数可以相互重新配置,而且我没有遇到任何这些错误。我把自己彻底搞糊涂了。

编辑:我认为最初的例子可能会给出足够的信息,从概念上看到底是怎么回事,但我知道它们并没有重复这个问题。我在下面包含了一个完全工作的小示例,它展示了GUI的基本结构(现在可能走得太远了):

代码语言:javascript
复制
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()
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-05-10 15:23:37

“在创建开关后尝试配置”命令:

代码语言:javascript
复制
self.switch_1.config(command=lambda: self.switch_2.toggle())

代码语言:javascript
复制
self.switch_2.config(command=lambda: self.switch_1.toggle()) 

init()方法的末尾。-西尔维斯特·库恩

这绝对是正确的方法,不幸的是,问题是(我真的应该看到!)每当任何一个开关被切换时,它都会创建一个无限的回调函数交换,因为它们之间不断地来回切换。我通过在customtkinter模块中添加一个控制参数来解决这个问题,这样就可以传递一个参数来阻止正在执行的开关的命令函数。这可能是错误的做法,但它对我有效。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72172263

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档