【问题标题】:Cannot disable buttons in Kivy (Python)无法在 Kivy (Python) 中禁用按钮
【发布时间】:2020-08-29 03:16:29
【问题描述】:

我在禁用 kivy 库中的按钮时遇到问题。当我禁用按钮时,它根本不会禁用。它以某种奇怪的方式等待。

让我给你看我的代码:

import kivy 
from kivy.app import App 
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
import time

class MainApp(App): 
    def build(self):            
        self.l = FloatLayout()      
        b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
        b.bind(on_press=self.press)     
        self.l.add_widget(b)
        return self.l
    
    def press(self, btn):
        btn.disabled = True
        time.sleep(3.0)
        btn.disabled = False    
        
app = MainApp()
app.run()

当我按下按钮时,我想禁用它 3 秒。但不是它程序“冻结”(不禁用按钮),然后在 3 秒后做按下动画(按钮闪烁蓝色)。当然,由于 time.sleep(3.0),程序必须“冻结”,但是在禁用按钮之后(必须是灰色的,但它不会改变颜色......)

如何解决?如果我把 time.sleep() 放在那里代替循环(大约有 1000 万个循环)来模仿程序“做某事”,它的行为方式相同......

那么我该如何解决呢?如何禁用kivy中的按钮,然后做一些事情,完成后再次启用按钮?

谢谢!

编辑:我的问题不是,该程序冻结了 3 秒。我知道调用 time.sleep() 是阻塞的。我不明白为什么按钮在睡眠之前(和期间)没有被禁用......

【问题讨论】:

    标签: python python-3.x user-interface kivy


    【解决方案1】:

    time.sleep 阻止了代码。相反,您需要在 3 秒后使用 Clock 启用按钮。以下是实现目标的更正代码:

    import kivy 
    from kivy.app import App 
    from kivy.uix.button import Button
    from kivy.uix.floatlayout import FloatLayout
    from kivy.clock import Clock
    from functools import partial
    
    class MainApp(App): 
        def build(self):            
            self.l = FloatLayout()      
            b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
            b.bind(on_press=self.press)     
            self.l.add_widget(b)
            return self.l
        
        def press(self, btn):
            btn.disabled = True
            Clock.schedule_once(partial(self.btn_enable, btn), 3)
            
        def btn_enable(self, btn, *args):
            btn.disabled = False
            
    app = MainApp()
    app.run()
    

    【讨论】:

    • 好的,但是使用 time.sleep() 我只想演示一些阻塞代码。在我的真实程序(数百行代码)中,当有人按下按钮(登录)时,我想重建布局(从另一个屏幕)然后切换到这个屏幕(主页屏幕)。在这个重建过程中(实际上大约需要 3 秒)我想禁用登录和注册按钮(作为用户的反馈,有些事情正在发生,在完成之前他不能做任何其他事情。但我的问题来了。即使我在调用 rebuilt() 函数之前禁用按钮,它不会禁用...
    • 当按钮禁用正在进行时(GUI 更新正在进行中),睡眠指令执行并因此阻止 GUI 更新,尽管代码的睡眠行位于按钮禁用行之后。在您的生产代码中,您可以禁用登录按钮,并且可以通过Clock 调用rebuild() 函数,例如Clock.schedule_once(lambda dt: rebuild()) 这样它就不会阻止禁用过程。您可以在生产环境中进行测试。
    • 哦,谢谢!真的行。但是如果你知道kivy,我有一个问题......我只是禁用了登录按钮和注册按钮。它确实立即禁用了两个按钮(并在屏幕重建完成后启用),但即使两个按钮的禁用代码相同 - 第一个按钮(登录按钮)在禁用后具有深蓝色调(第二个仅更深的灰色,没有色调)。你知道为什么吗?我想,这可能是因为我在实际“按下”按钮时禁用了按钮?但是我怎样才能解决它没有那种深蓝色呢?
    • 如果您在on_release 事件而不是按钮的on_press 事件上触发该功能,则可以解决此问题。
    【解决方案2】:

    TL;博士

    动画发生在press 函数被调用之后。这意味着在执行time.sleep 时冻结程序。

    该怎么办?

    相反,您需要做一些非阻塞的事情,这意味着它会在三秒钟内运行,但不会导致程序冻结。 可能可行的方法是利用线程(类似于示例,但处理跨线程发送变量)。

    示例

    这是一个不起作用的代码示例,因此您可以理解它的要点。最有可能的是,您将不得不处理跨线程传递变量:

    import kivy 
    from kivy.app import App 
    from kivy.uix.button import Button
    from kivy.uix.floatlayout import FloatLayout
    # import time
    import threading 
    
    class MainApp(App): 
        def build(self):            
            self.l = FloatLayout()      
            b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
            b.bind(on_press=self.press)     
            self.l.add_widget(b)
            return self.l
        
        def press(self, btn):
            btn.disabled = True
            # time.sleep(3.0)
            threading.Timer(3.0, lambda: btn.disabled = False).start()
            
    app = MainApp()
    app.run()
    

    这是受到this answer 的启发。

    【讨论】:

    • 我能问一下,你的意思是,动画是在我的 press() 函数调用之后发生的吗?当然,它发生在调用它之后,但在调用 time.sleep() 之前,所以这意味着,按钮的禁用必须在冻结 3 秒之前发生,对吧?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多