Постоянно проверять список времени и текущего времени и отображать уведомления - PullRequest
0 голосов
/ 26 октября 2019

В настоящее время я работаю над приложением дел Python. Все созданные пользователем события хранятся в словаре toDoEvents с заголовками, due_time, remind_time и описанием. Прямо сейчас я работаю над системой уведомлений, используя тост. Мне нужно постоянно проверять список remind_dates для каждого события, и, если оно соответствует текущему времени, вызывает тост-уведомление.

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

from win10toast import ToastNotifier
import time
from datetime import datetime
from PyQt5.QtCore import QDateTime

toDoEvents = {}
class TodoEvent:

    def __init__(self, title, due_time, remind_time, description):
        self.title = title
        self.due_time = due_time
        self.remind_time = remind_time
        self.description = description
        toDoEvents[self.title] = self

    def change_title(self, new_title):
        self.title = new_title

    def change_due_time(self, new_due_time):
        self.due_time = new_due_time

    def change_remind_time(self, new_remind_time):
        self.remind_time = new_remind_time

    def change_description(self, new_description):
        self.description = new_description


event1 = TodoEvent("test", QDateTime(2019, 10, 26, 1, 18).toPyDateTime(), QDateTime(2019, 10, 26, 1, 18).toPyDateTime(), "test description")

for event in toDoEvents.values():

    if event.remind_time.strftime("%m/%d/%Y, %H:%M") == datetime.now().strftime("%m/%d/%Y, %I:%M"):
        toaster = ToastNotifier()
        toaster.show_toast(event.title, event.description, threaded=True, icon_path=None, duration=8)
        while toaster.notification_active():
            time.sleep(0.1)

Какой самый эффективный способ сделать это? Редактировать: хотите выполнять код каждый раз, когда меняется минута, а не каждые 60 секунд (что предполагает, что пользователь запускает программу ровно в 0 секунд)

Ответы [ 2 ]

1 голос
/ 27 октября 2019

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

Пример ниже подклассов QTimer делает это таймером, который изменяет только "таймауты" в минуту. Это может показаться немного сложным, но это связано с природой QTimers, которая может быть не такой точной, как можно было бы ожидать (как большинство таймеров);тем не менее, это позволяет улучшить реализацию, избегая ненужных излучений времени ожидания, так как иногда сигнал времени ожидания может излучаться за до конца минуты, таким образом испуская его снова, когда минута действительно меняется.

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

class MinuteTimer(QTimer):
    customTimeout = pyqtSignal()
    def __init__(self, parent=None):
        super(MinuteTimer, self).__init__(parent)
        self.setSingleShot(True)
        # default timer is CoarseTimer, which *could* fire up before, making it
        # possible that the timeout is called twice
        self.setTimerType(Qt.PreciseTimer)
        # to avoid unintentional disconnection (as disconnect() without
        # arguments disconnects the signal from *all* slots it's connected
        # to), we "overwrite" the internal timeout signal attribute name by
        # by switching it with our customTimeout signal instead, keeping
        # its consistency with QTimer objects, at least by object naming
        self._internalTimeout = self.timeout
        self._internalTimeout.connect(self.startAdjusted)
        self._internalTimeout.connect(self.customTimeout)
        self.timeout = self.customTimeout

    def start(self):
        now = QTime.currentTime()
        nextMinute = QTime(now.hour(), now.minute()).addSecs(60)
        # if the next minute happens at or after the midnight of the day
        # after (in this specific case, when "now" is at 23:59), msecsTo()
        # will return a negative value, since QTime has no date reference:
        # 14:30:00 to 14:29:00 is -60 seconds, so 23:59:00 to 00:00:00 is
        # -86340 seconds; using the % modulus operator against the
        # millisecond count in a day can give us the correct positive
        # difference that can be used as an interval for QTimer
        QTimer.start(self, now.msecsTo(nextMinute) % 86400000)

    def startAdjusted(self):
        now = QTime.currentTime()
        # even when using a PreciseTimer, it is possible that the timeout
        # is called before the minute change; if that's the case, we
        # add 2 minutes, just to be "better safe than sorry"
        addSecs = 120 if now.second() > 50 else 60
        nextMinute = QTime(now.hour(), now.minute()).addSecs(addSecs)
        QTimer.start(self, now.msecsTo(nextMinute) % 86400000)


def minuteEvent():
    print('minute changed!')

    for event in toDoEvents.values():
        if event.remind_time.strftime("%m/%d/%Y, %H:%M") == datetime.now().strftime("%m/%d/%Y, %I:%M"):
            toaster = ToastNotifier()
            toaster.show_toast(event.title, event.description, 
                threaded=True, icon_path=None, duration=8)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    minuteTimer = MinuteTimer()
    minuteTimer.timeout.connect(minuteEvent)
    minuteTimer.start()
    sys.exit(app.exec_())

Очевидно, что таймер может излучать свой сигнал до изменения минут (что приводит к неудачному уведомлению, если следующий сигнал испускается после егонезначительное изменение), но это в основном связано с вашей реализацией, и я считаю, что вам нужно проверить диапазон времени, чтобы убедиться, что каждый todo перехватывается, когда это должно быть.
В качестве возможной альтернативы вы можете испустить сигналс ожидаемым временем «ЧЧ: мм» (используя операторы, подобные addSecs = 120 if now.second() > 50 else 60, который я использовал), а затем проверьте фактическое время напоминания.

1 голос
/ 26 октября 2019

На самом деле я пишу программу почти такого же типа, и я бы предложил использовать QTimer вместо постоянной проверки текущего времени. В любом случае, поскольку время синхронизации QTimer по умолчанию имеет интервал в 5% (который может быть очень высоким для длинных интервалов), вы можете установить для timerType значение Qt.PreciseTimer.

class TodoEvent:

    def __init__(self, title, due_time, remind_time, description):
        self.title = title
        self.due_time = due_time
        self.remind_time = remind_time
        self.description = description
        toDoEvents[self.title] = self
        self.timer = QTimer(self)
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.showReminder)
        self.timer.setTimerType(Qt.PreciseTimer)
        self.change_remind_time(remind_time)

    def change_title(self, new_title):
        self.title = new_title

    def change_due_time(self, new_due_time):
        self.due_time = new_due_time

    def change_remind_time(self, new_remind_time):
        self.remind_time = new_remind_time
        self.timer.start(QDateTime.currentDateTime().msecsTo(self.remind_time))

    def change_description(self, new_description):
        self.description = new_description

    def showReminder(self):
        toaster = ToastNotifier()
        toaster.show_toast(self.title, self.description, threaded=True, icon_path=None, duration=8)

event1 = TodoEvent("test", 
                   QDateTime(2019, 10, 26, 1, 18), 
                   QDateTime(2019, 10, 26, 1, 18), 
                   "test description")

Примечание. что интервалы QTimer выражаются в миллисекундах как целое число , поэтому могут возникнуть проблемы с интервалами дат, превышающими максимальное значение 32-битного целого числа. Стандартные целые числа имеют максимальное положительное значение 2147483647, которое в миллисекундах равно 24 дням, 20 часам, 31 минуте, 23 секундам и 647 мс.

Это означает, что если у вас есть напоминание из-задата с интервалом мс, превышающим это значение, вы получите недопустимый таймер (с интервалом, равным -1).
Хотя маловероятно, что обычный человек будет поддерживать свой компьютер в течение этого времени,это не невозможно (подумайте о переносных устройствах, которые часто работают неделями без перезапуска), поэтому вы можете захотеть добавить функцию, которая проверяет время выполнения для слишком больших интервалов и, в конечном итоге, добавить дополнительный таймер, который будет «восстанавливать» себя доинтервал вступил в силу, затем запустите фактический таймер для уведомления.

...