【问题标题】:Prevent method calls for n seconds, show message of the remaining time阻止方法调用 n 秒,显示剩余时间的消息
【发布时间】:2011-01-01 15:06:37
【问题描述】:

我正在开发简单的 GUI,但我被困住了。这是基本流程:

  1. 显示文本text 和:
    • 节省时间time_pressed
    • 启动progressbar 并更新它,直到time_notallow 过期。
  2. 如果用户按下<Next>,查看time_notallow 中指定的秒数是否已过, 如果没有,则不允许显示下一个text

基本上,我想阻止用户调用绑定到<Right> 键的方法,直到time_notallow 通过,并显示一个进度条来通知他们需要等待多长时间。由于我用的是bind,比如……

    self.master.bind('<Right>', self.text_next)

...我没有.after(),就像在小部件中一样。

我尝试过的

  1. master.after()bind 设置为None 并在time_notallow 之后将bind 设置为self.text_next,但它不起作用。
  2. 创建了一个thread,它与while True 循环,以不断检查time_notallow 是否通过,但应用程序崩溃。

任何帮助表示赞赏。

编辑:一个解决方案。在 .after 中使用 lambda() 来计算秒数(感谢 Bryan Oakley)

"""

Stripped-down version to figue out time/event/threading stuff.

What this has to do:

1. Show the window and some text.
2. User presses Next arrow and new text shows. Paint the label red.
3. Prevent user form pressing again (unbind all keys), until 2 seconds passed 
   (time_wait).
4. Make notice of passed time and after 2 seconds bind the keys again and
   paint the label green.
5. Loop the steps 2-4.

"""

import sys
import tkinter as tk
from tkinter import W, E, S, N

class Test(tk.Frame):

    def __init__(self, master=None):
        """Draw the GUI"""
        tk.Frame.__init__(self, master)
        self.draw_widgets()
        self.grid()
        self.time_wait = 2
        self.locked = False
        self.bind_keys()
        self.counter = 0

    def draw_widgets(self):
        """Draw all the widgets on the frame."""
        text = 'Just a sample sentence.'
        #Label with the sentence
        self.lbl_text = tk.Label(self, anchor="center", relief='groove')
        self.lbl_text['text'] = text
        self.lbl_text['font'] = ('Helvetica', 27)
        self.lbl_text.grid(column=0, row=0, sticky=W+E+S+N)
        self.lbl_note = tk.Label(self, anchor="center", relief='groove',
            bg='green')
        self.lbl_note.grid(column=0, row=1, sticky=W+E+S+N)

    def text_next(self, event):
        """Get next text"""
        if not self.locked:
            self.counter += 1
            self.lbl_text['text'] = 'The text number %s!' % self.counter
            self.bind_tonone()
            self.lock(self.time_wait)

    def text_previous(self, event):
        """Get previous text"""
        if not self.locked:
            self.counter -= 1
            self.lbl_text['text'] = 'The text number %s!' % self.counter
            self.bind_tonone()
            self.lock(self.time_wait)

    def bind_keys(self):
        """Bind the keys"""
        self.master.bind('<Left>', self.text_previous)
        self.master.bind('<Right>', self.text_next)
        self.master.bind('<Escape>', self.exit)
        self.lbl_note['bg'] = 'green'

    def bind_tonone(self):
        """Unbind the keys"""
        self.master.bind('<Left>', None)
        self.master.bind('<Right>', None)
        self.master.bind('<Escape>', None)
        self.lbl_note['bg'] = 'red'

    def lock(self, n):
        if n == 0:
            self.locked = False
            self.lbl_note['text'] = ''
            self.lbl_note['bg'] = 'green'

        else:
            self.locked = True
            self.lbl_note['text'] = 'Locked for %s more seconds' % n
            self.lbl_note.after(1000, lambda n = n - 1: self.lock(n))

    def exit(self, event):
        """Exit the program."""
        sys.exit()

def start():
    """Start the gui part."""
    root = tk.Tk()
    app = Test(master=root)
    app.mainloop()

if __name__ == '__main__':
    start()

【问题讨论】:

    标签: python events time tkinter bind


    【解决方案1】:

    您不需要线程或计时器来解决此问题。您所需要的只是一个需要几秒钟等待的过程,并让它每秒调用一次,直到数字降至零。

    它看起来像这样(在我的脑海中,未经测试):

    def __init__(...):
        ...
        self.locked = False
        ...
    
    def text_next(self, event):
        if not self.locked:
            <do the "next" logic>
            self.lock(10) # lock for 10 seconds
    
    def text_previous(self, event):
        if not self.locked:
            <do the "previous" logic>
            self.lock(10) # lock for 10 seconds
    
    def lock(self, n):
        if n == 0:
            self.locked = False
            self.status.config(text="")
        else:
            self.locked = True
            self.status.config(text="Locked for %s more seconds" % n)
            self.status.after(1000, lambda n=n-1: self.lock(n))
    

    【讨论】:

    • 完美运行。十分优雅。顺便说一句,这是我第一次在我的代码中使用lambda。谢谢!
    【解决方案2】:

    尝试使用专门设计的threading.Timer 对象而不是自旋等待线程怎么样?

    【讨论】:

    • 谢谢。我试过了,但是我不能停止Timer:下次用户按键时,它会导致错误(Timer不能启动两次)。
    【解决方案3】:

    使用计时器的快速修复方法是进行一些重构。最初将您的 self.track 设置为 None,然后仅将其设置为在箭头上运行/阻止。

    import sys
    import threading
    import Tkinter as tk
    from Tkinter import W, E, S, N
    
    class Test(tk.Frame):
    
        def __init__(self, master=None):
            """Draw the GUI"""
            tk.Frame.__init__(self, master)
            self.draw_widgets()
            self.grid()
            # Track wait times
            self.time_wait = 2
            # Timer
            self.track = None
            self.bind_keys()
            self.counter = 0
    
        def draw_widgets(self):
            """Draw all the widgets on the frame."""
            text = 'Just a sample sentence.'
            #Label with the sentence
            self.lbl_text = tk.Label(self, anchor="center", relief='groove')
            self.lbl_text['text'] = text
            self.lbl_text['font'] = ('Helvetica', 27)
            self.lbl_text.grid(column=0, row=0, sticky=W+E+S+N)
            self.lbl_note = tk.Label(self, anchor="center", relief='groove',
                bg='green')
            self.lbl_note.grid(column=0, row=1, sticky=W+E+S+N)
    
        def text_next(self, event):
            """Get next text"""
            if not self.track or not self.track.is_alive():
                self.track = threading.Timer(self.time_wait, self.bind_keys)
                self.counter += 1
                self.lbl_text['text'] = 'The text number %s!' % self.counter
                self.bind_tonone()
                self.track.start()
    
        def text_previous(self, event):
            """Get previous text"""
            self.counter -= 1
            self.lbl_text['text'] = 'The text number %s!' % self.counter
    
        def bind_keys(self):
            """Bind the keys"""
            self.master.bind('<Left>', self.text_previous)
            self.master.bind('<Right>', self.text_next)
            self.master.bind('<Escape>', self.exit)
            self.lbl_note['bg'] = 'green'
    
        def bind_tonone(self):
            """Unbind the keys"""
            self.master.bind('<Left>', None)
            self.master.bind('<Right>', None)
            self.master.bind('<Escape>', None)
            self.lbl_note['bg'] = 'red'
    
        def exit(self, event):
            """Exit the program."""
            sys.exit()
    
    def start():
        """Start the gui part."""
        root = tk.Tk()
        app = Test(master=root)
        app.mainloop()
    
    if __name__ == '__main__':
        start()
    

    不过,在预览版中,@Bryan Oakley 有一个更好的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-29
      • 2020-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-14
      • 1970-01-01
      相关资源
      最近更新 更多