Запретить вызов метода в течение n секунд, показать сообщение оставшегося времени - PullRequest
0 голосов
/ 01 января 2011

Я работаю над простым 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 или нет, но приложение вылетает.

Любая помощь приветствуется.

Редактировать: решение. Использование lambda() в .after для подсчета секунд (спасибо Брайану Оукли)

"""

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

Ответы [ 3 ]

2 голосов
/ 01 января 2011

Вам не нужны потоки или таймеры для решения этой проблемы.Все, что вам нужно, это процедура, которая занимает несколько секунд ожидания и заставляет ее вызывать себя раз в секунду, пока число не станет равным нулю.

Это выглядело бы примерно так (вне головы, не проверено):

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))
0 голосов
/ 01 января 2011

Быстрое исправление с помощью вашего таймера - немного изменить рефакторинг. Сначала установите для 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 есть лучшее решение.

0 голосов
/ 01 января 2011

Как насчет того, чтобы вместо потока с ожиданием вращения вы попробовали специально разработанный threading.Timer объект?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...