Вопрос или проблема
Я написал скрипт ниже, чтобы выполнить действие в установленное время, заданное в виджете ввода. Он запускается при нажатии кнопки. Как только текущее время равно тому, которое задано на таймере, выполняется действие, и поток закрывается. Я хочу использовать как можно меньше ресурсов. Можете ли вы предложить изменения, чтобы сделать этот таймер как можно более надежным и эффективным?
#!/usr/bin/python3.9
import tkinter as tk
from tkinter import ttk
import time
from datetime import datetime
from threading import Event
from threading import Thread
timer_flag = False
class Timer(Thread):
def __init__(self, set_time):
super().__init__()
self.set_time = set_time
self.event = Event()
def run(self):
while True:
time_now= datetime.now().strftime('%H:%M')
if time_now == self.set_time:
frame.run_this_action()
self.event.set()
if self.event.is_set():
print('Поток был остановлен')
break
time.sleep(1)
def halt(self, timeout=None):
print("ПОТОК ВЫКЛ.")
self.event.set()
Thread.join(self, timeout)
class MainFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.button=tk.Button(text="ВЫКЛ", bg='red', bd=8, activebackground="yellow",
height=6, width=12, relief="raised", command=self.toggle, font="ubuntu 16")
self.button.grid(row=1, column=2, padx=200, pady=100)
self.entry_var = tk.StringVar()
self.entry_var.set('00:00')
self.entry=tk.Entry(textvariable=self.entry_var, font="ubuntu 16",
justify='center', foreground='red', insertontime=0)
self.entry.grid(row=1, column=3, sticky=tk.W, ipady=10)
def toggle(self):
if self.button.config('relief')[-1] == 'sunken':
self.button.config(relief="raised", text="ВЫКЛ", bg='red',
font="ubuntu 16")
self.end()
else:
self.button.config(relief="sunken", text="ВКЛ", bg="green",
font="ubuntu 16")
self.start()
## запуск таймера
def start(self):
global timer_flag
timer_flag = False
set_time= self.entry.get()
timer_thread=Timer(set_time)
timer_thread.start()
self.timer_monitor(timer_thread)
self.stop_(timer_thread)
## для таймера
def timer_monitor(self, thread):
if thread.is_alive():
#проверяем поток каждые 100мс
self.after(100,lambda:self.timer_monitor(thread))
else:
print("поток остановлен")
self.button.config(relief="raised", text="ВЫКЛ", bg='red',
font="ubuntu 16")
def stop_(self, thread):
if timer_flag == False:
#проверяем поток каждые 100мс
self.after(100,lambda:self.stop_(thread))
else:
thread.halt()
print("ВЫКЛ")
def end(self):
global timer_flag
timer_flag = True
print("флаг таймера", timer_flag)
def run_this_action(self):
print("действие выполнено")
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("1200x550") # воздух
#self.resizable(False, False)
self.title('кофейный таймер v5')
if __name__ == "__main__":
app = App()
frame=MainFrame(app)
app.mainloop()
Ответ или решение
В вашем скрипте задача заключается в реализации таймера для выполнения определенного действия в заданное время, используя минимально возможные ресурсы. Вот несколько рекомендаций по оптимизации вашего кода и повышению его надежности и эффективности.
-
Избегание использования
time.sleep(1)
в цикле: Вместо этого можно использовать более точный способ ожидания времени (например,after
в Tkinter), который не блокирует основной поток приложения. -
Использование
tkinter
для отслеживания времени: Вместо создания нового потока для таймера, можно использовать встроенный механизмafter()
, который будет планировать выполнение действия в заданное время. Это снизит общую нагрузку на ресурсы. -
Формат ввода времени: Лучше добавить обработку исключений для заметки о неправильном формате времени. Это улучшит взаимодействие с пользователем.
Вот переработанная версия вашего кода с учетом предложенных изменений:
#!/usr/bin/python3.9
import tkinter as tk
from tkinter import ttk
from datetime import datetime
class MainFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.button = tk.Button(text="Старт", bg='green', bd=8, activebackground="yellow",
height=6, width=12, relief="raised", command=self.toggle)
self.button.grid(row=1, column=2, padx=200, pady=100)
self.entry_var = tk.StringVar()
self.entry_var.set('00:00')
self.entry = tk.Entry(textvariable=self.entry_var, font="ubuntu 16",
justify='center', foreground='red')
self.entry.grid(row=1, column=3, sticky=tk.W, ipady=10)
self.timer_job = None # Переменная для хранения задачи таймера
def toggle(self):
if self.timer_job:
self.end_timer()
else:
self.start_timer()
def start_timer(self):
set_time = self.entry.get()
# Проверка, чтобы корректно обработать ввод времени
try:
datetime.strptime(set_time, '%H:%M')
except ValueError:
print("Неправильный формат времени. Используйте HH:MM.")
return
self.button.config(relief="sunken", text="СТОП", bg="red")
# Запустим функцию таймера
self.run_timer(set_time)
def run_timer(self, set_time):
current_time = datetime.now().strftime('%H:%M')
if current_time == set_time:
self.run_this_action()
self.timer_job = None # Сбросить таймер
self.button.config(relief="raised", text="Старт", bg='green')
else:
self.timer_job = self.after(1000, lambda: self.run_timer(set_time)) # Проверять каждую секунду
def end_timer(self):
if self.timer_job:
self.after_cancel(self.timer_job)
self.timer_job = None
self.button.config(relief="raised", text="Старт", bg='green')
def run_this_action(self):
print("Действие выполнено!")
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("1200x550")
self.title('Кофейный таймер')
if __name__ == "__main__":
app = App()
frame = MainFrame(app)
app.mainloop()
Основные изменения:
- Отказ от потоков: Используется метод
after()
для выполнения отложенных операций, что позволяет избежать блочащих операций и упростить управление состоянием программы. - Проверка времени: Добавлен блок для проверки правильности введенного времени, чтобы избежать ошибок исполнения.
- Ясность кода: Улучшены названия методов и переменных для повышения читаемости.
Эти изменения сделают ваш таймер более эффективным и надежным, а также снизят использование ресурсов.