выполнить действие в установленное время

Вопрос или проблема

Я написал скрипт ниже, чтобы выполнить действие в установленное время, заданное в виджете ввода. Он запускается при нажатии кнопки. Как только текущее время равно тому, которое задано на таймере, выполняется действие, и поток закрывается. Я хочу использовать как можно меньше ресурсов. Можете ли вы предложить изменения, чтобы сделать этот таймер как можно более надежным и эффективным?

#!/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()

Ответ или решение

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

  1. Избегание использования time.sleep(1) в цикле: Вместо этого можно использовать более точный способ ожидания времени (например, after в Tkinter), который не блокирует основной поток приложения.

  2. Использование tkinter для отслеживания времени: Вместо создания нового потока для таймера, можно использовать встроенный механизм after(), который будет планировать выполнение действия в заданное время. Это снизит общую нагрузку на ресурсы.

  3. Формат ввода времени: Лучше добавить обработку исключений для заметки о неправильном формате времени. Это улучшит взаимодействие с пользователем.

Вот переработанная версия вашего кода с учетом предложенных изменений:

#!/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() для выполнения отложенных операций, что позволяет избежать блочащих операций и упростить управление состоянием программы.
  • Проверка времени: Добавлен блок для проверки правильности введенного времени, чтобы избежать ошибок исполнения.
  • Ясность кода: Улучшены названия методов и переменных для повышения читаемости.

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

Оцените материал
Добавить комментарий

Капча загружается...