Пытаясь понять, почему мой секундомера код ничего не делает (Python) - PullRequest
0 голосов
/ 16 апреля 2020

Я пытаюсь написать python код, который будет отображать секундомер в поле ввода, используя tkinter.

Моя идея состоит в том, чтобы использовать рекурсивную функцию для добавления 1 в блок каждую секунду, используя time.sleep (1).

Я в тупике - ничего не происходит, когда я нажимаю любую кнопку, и я не получайте коды ошибок, поэтому я понятия не имею, почему он не работает.

Единственный раз, когда я получаю что-либо в поле, это после нажатия кнопки Стоп, и всегда "00:00:00".

from tkinter import *
import time


root = Tk()
root.title("Stopwatch")

#box to display stopwatch
box = Entry(root, width = 20, borderwidth = 5)
box.grid(row = 0, column = 0)

#displays stopwatch in the box
def show_timer():
    global hrs, mins, secs, timer
    timer = str(hrs).zfill(2) + ":" + str(mins).zfill(2) + ":" + str(secs).zfill(2)
    box.delete(0, END)
    box.insert(0, timer)

#check = 1 allows the stopwatch to run
def set_check(i):
    global check
    check = i
    return check

#actual function for stopwatch
def stopwatch():
    global check, hrs, mins, secs
    if check == 1:
        time.sleep(1)
        if int(secs) < 59:
            secs += 1
            return secs
        elif mins < 59:
            mins += 1
            secs = 0
            return mins, secs
        else:
            hrs += 1
            mins = 0
            secs = 0
            return hrs, mins, secs
        show_timer()
        stopwatch()
    else:
        show_timer()


def start():
    global hrs, mins, secs
    hrs, mins, secs = 0, 0, 0
    return hrs, mins, secs
    show_timer()
    set_check(1)
    stopwatch()

def stop():
    set_check(0)
    stopwatch()



start_button = Button(root, text = "Start", command = start)
stop_button = Button(root, text = "Stop", command = stop)

start_button.grid(row = 1, column = 0)
stop_button.grid(row = 2, column = 0)

root.mainloop()

1 Ответ

0 голосов
/ 17 апреля 2020

Ваш код имеет несколько проблем.

  • Вызов time.sleep() из функции обратного вызова прерывает событие tkinter l oop, в результате чего GUI не отвечает.
  • from tkinter import * не рекомендуется. Чтобы избежать загрязнения пространства имен, используйте, например, import tkinter as tk.
  • Вы return (= выход из функции) из start, ничего не делая.
  • Вашим функциям не нужны операторы return ; вы, кажется, не используете возвращаемые значения.
  • Все три ветви внутреннего if -статажа в stopwatch вызывают return, поэтому stopwatch никогда не может быть вызвано рекурсивно. Вероятно, это не то, что вы хотели.
  • Это хорошо, потому что рекурсивный вызов из обратного вызова также прервал бы событие-l oop.
  • Вам нужно только использовать global когда вы хотите изменить переменных. Например, show_timer не нужно global. (Кстати, вы можете изменить содержимое таких типов контейнеров, как list и dict без , используя global.)
  • Имена, которые вы обозначенные global не существуют в глобальном контексте.

Как правило, управляемые событиями программы не работают по жесткому расписанию. Вы можете использовать root.after(), чтобы заставить его выполнить обратный вызов через определенное время, но нет гарантии, что это время точное.

Так что я бы предложил:

  • Сохраните время запуска секундомера.
  • Используйте обратный вызов after, который запускается каждую секунду.
  • В этом обратном вызове получите текущее время и вычтите из него время начала. Отобразите разницу.
  • Обратите внимание, что в конце обратного вызова after необходимо перерегистрировать его, чтобы он продолжал работать. (если не была нажата кнопка остановки.

В моих программах tkinter мне нравится сохранять все состояния в types.SimpleNamespace, чтобы было ясно, что это состояние программы. Вот рабочая версия с этими изменениями:

import time
import types
import tkinter as tk


def start():
    state.starttime = time.time()
    state.run = True
    display()
    root.after(1000, update)


def stop():
    state.run = False
    state.starttime = None
    box.delete(0, tk.END)


def update():
    if state.run:
        display()
        root.after(1000, update)


def display():
    difference = int(time.time() - state.starttime)
    minutes, seconds = divmod(difference, 60)
    hours, minutes = divmod(minutes, 60)
    display = "{:02d}:{:02d}:{:02d}".format(hours, minutes, seconds)
    box.delete(0, tk.END)
    box.insert(0, display)


if __name__ == '__main__':
    # Create program state
    state = types.SimpleNamespace()
    state.starttime = None
    state.run = False
    # Create widgets.
    root = tk.Tk()
    root.title("Stopwatch")
    root.attributes('-type', 'dialog')
    # box to display stopwatch
    box = tk.Entry(root, width=20, borderwidth=5)
    box.grid(row=0, column=0)
    start_button = tk.Button(root, text="Start", command=start)
    stop_button = tk.Button(root, text="Stop", command=stop)
    start_button.grid(row=1, column=0)
    stop_button.grid(row=2, column=0)
    # Run the GUI
    root.mainloop()

Редактировать

Если вы новичок в Python, может быть целесообразно начать со скриптов, которые вы можете просто вызвать из команды -line (cmd.exe на ms- windows). Их, как правило, гораздо легче читать и писать, потому что они имеют в основном линейный поток управления. На моем веб-сайте у меня есть статья о двух эквивалентных программах, один простой сценарий командной строки, а другой - tkinter GUI. Здесь вы можете видеть, что последний гораздо более сложен, чем первый.

...