不断检查时间列表和当前时间并显示通知

问题描述 投票:0回答:2

我目前正在处理python待办事项应用程序。所有用户创建的事件都存储在名为toDoEvents的字典中,其中包含标题,due_time,提醒时间和描述。现在,我正在使用吐司制作通知系统。我需要经常检查每个事件的hint_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秒启动程序)

python python-3.x pyqt5 toast
2个回答
1
投票

我正在添加另一个答案,因为对该问题的更新使另一个话题有些偏离,但我宁愿将其保留在这里,因为我相信它可能对其他人有用,即使与该话题上的话题无关问题。

下面的示例将QTimer子类化,使它成为仅在分钟变化时“超时”的计时器。看起来似乎有些复杂,但这是由于QTimers的性质所致,它不如人们所期望的那样精确(因为大多数计时器都是如此)。但是,它可以实现更好的实现,同时避免发出不必要的超时信号,因为有时可能会在分钟结束前[[before]]发出超时信号,从而在分钟实际改变时再次发出该信号。]singleShot设置实际上不是必需的,因为无论如何定时器都将以正确的间隔重新启动(需要不断检查,因为在此期间可能会发生一些艰苦的工作过程,导致定时器在分钟更改后发生移位) 。我只是添加了它,因为我think在大量定时器的情况下可能会稍微提高性能,但是我可能是错的。无论如何,即使没有它,它仍然可以工作。

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

显然,计时器有可能在分钟更改之前发出其信号(如果在分钟更改之后发出下一个信号,则会导致失败的通知),但这主要取决于您的实现,我认为这取决于您检查一个时间范围,以确保每个待办事项在应有的时间被捕获。作为一种可能的选择,您可以发出带有预期“ HH:mm”定时的信号(使用类似于我使用的addSecs = 120 if now.second() > 50 else 60的语句),然后检查提醒的实际定时。

事实上,我正在编写几乎相同类型的程序,我建议使用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间隔以毫秒表示为

integer
,因此日期间隔大于32位整数的最大值可能会出现问题。标准整数的最大正值为2147483647,当以毫秒为单位时,为24天,20小时,31分钟,23秒和647 ms。

这意味着,如果您因某个日期的毫秒间隔大于该值而收到提醒,则会得到一个无效的计时器(间隔等于-1)。虽然正常人不太可能会在这段时间内打开计算机,但这不是不可能的(想想便携式设备通常会运行数周而无需重启),因此您可能想添加一个检查到期时间的功能间隔太长,最终添加另一个计时器,该计时器将自己“恢复”直到间隔生效,然后启动通知的实际计时器。


1
投票
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")
© www.soinside.com 2019 - 2024. All rights reserved.