阻止方法调用n秒,显示剩余时间的消息

发布于 2024-10-10 06:22:17 字数 3907 浏览 1 评论 0原文

我正在开发简单的 GUI,但我陷入困境。这是基本流程:

  1. 显示文本 text 以及:
    • 节省 time_pressed 时间
    • 启动进度条并更新它,直到time_notallow到期。
  2. 如果用户按,查看time_notallow中指定的秒数是否已经过去, 如果不是,则不允许显示下一个文本

基本上,我想阻止用户调用绑定到 键的方法,直到 time_notallow 通过,并显示进度条以告知他们还有多长时间等待。由于我使用 bind,例如...

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

...我没有 .after(),如小部件中那样。

我尝试使用

  1. master.after()bind 设置为 None 并将 time_notallow 设置为 绑定self.text_next,但它不起作用。
  2. 创建了一个线程,它使用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()

I'm working on simple GUI but I'm stuck. This is the basic flow:

  1. Show text text and:
    • save time in time_pressed
    • start the progressbar and update it until time_notallow expires.
  2. If a user presses <Next> see if seconds specified in time_notallow have passed,
    and if not, don't allow the display of the next text.

Basically, I want to prevent users form calling a method bind to the <Right> key until time_notallow passes, and show a progressbar to inform them how long they'll have to wait. Since I use bind, such as...

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

...I don't have .after(), as in widgets.

What I've tried

  1. master.after() to set bind to None and after time_notallow to bind to self.text_next, but it didn't work.
  2. Created a thread which looped with while True to check constantly if time_notallow is passed or not, but the application crashes.

Any help appreciated.

Edit: A Solution. Using lambda() in .after to count seconds (thanks to 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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

今天小雨转甜 2024-10-17 06:22:17

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

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

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))

You don't need threads or timers to solve this problem. All you need is a procedure that takes a number of seconds to wait, and have it call itself once a second until the number gets down to zero.

It would look something like this (off the top of my head, untested):

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))
惯饮孤独 2024-10-17 06:22:17

不如尝试专门设计的 来代替旋转等待线程threading.Timer 对象?

How about instead of a spin-waiting thread, you try the specifically-designed threading.Timer object?

触ぅ动初心 2024-10-17 06:22:17

使用计时器的快速修复方法是进行一些重构。将 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 有更好的解决方案。

A quick fix using your timer is to refactor a bit. Set your self.track initially to None, then only set it to run/block on arrow.

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()

On preview though, @Bryan Oakley, has a better solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文