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

2024-05-28 23:11:22 发布

您现在位置:Python中文网/ 问答频道 /正文

我目前正在开发一个python to-do应用程序。所有用户创建的事件都存储在名为toDoEvents的字典中,其中包含标题、到期时间、提醒时间和描述。现在我正在开发一个使用toast的通知系统。我需要经常检查每个活动的提醒日期列表,如果它与当前时间匹配,则调用toast通知。你知道吗

我想做这样的事情,除了每一分钟都要不断检查:

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秒时启动程序)


Tags: fromimportselfeventnewtimetitledef
2条回答

我添加了另一个答案,因为对这个问题的更新使另一个有点离题,但我更愿意把它留在那里,因为我相信它可能对其他人有用,即使与这个问题的主题稍微无关。你知道吗

下面的示例将QTimer子类化,使其成为一个仅在分钟内“超时”更改的计时器。它可能看起来有点复杂,但这是由于qtimer的性质,qtimer可能不像人们期望的那样精确(就像大多数定时器一样);尽管如此,它允许更好的实现,同时避免不必要的超时信号发射,因为有时超时信号可能在一分钟结束之前发射,因此,当分秒发生变化时,它会再次发出。你知道吗

实际上不需要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都在应该捕获的时候被捕获。
作为一种可能的替代方法,您可以发出一个具有预期“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间隔以毫秒表示为整数,因此日期间隔可能会高于32位整数的最大值。标准整数的最大正值为2147483647,当提到毫秒时,它是24天20小时31分23秒647毫秒

这意味着,如果由于某个日期的毫秒间隔大于该值而产生提醒,则会得到一个无效的计时器(间隔等于-1)。
虽然一个正常人不太可能让他/她的电脑开那么长时间,但这并不是不可能的(想想那些经常运行数周而不重新启动的便携式设备),所以你可能需要添加一个功能来检查间隔太长的到期时间,最终,再添加一个计时器,它将“恢复”自身,直到间隔变为有效,然后启动通知的实际计时器。你知道吗

相关问题 更多 >

    热门问题