不断检查时间列表和当前时间并显示通知
Constantly Check Through a List of Times and Current Time and Display Notification
我目前正在开发 python to-do 应用程序。所有用户创建的事件都存储在名为 toDoEvents 的字典中,包含标题、due_time、remind_time 和描述。现在我正在使用 toast 开发一个通知系统。我需要不断检查每个事件的 remind_dates 列表,如果它与当前时间匹配,则调用 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 秒启动程序)
事实上,我正在编写几乎相同类型的程序,我建议使用QTimer instead of constantly checking the current time. Anyway since QTimer default timing has a 5% interval margin (which might be very high for long intervals), you might want to set its 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)。
虽然普通人不太可能让 his/her 计算机保持开启那么长的时间,但这并非不可能(想想经常 运行 数周而不重启的便携式设备),因此您可能想要添加一个函数检查过长间隔的到期时间,并最终添加另一个计时器,该计时器将 "restore" 自身直到间隔变得有效,然后启动通知的实际计时器。
我正在添加另一个答案,因为问题的更新使另一个问题有点偏离主题,但我更愿意将其留在那里,因为我相信它可能对其他人有用,即使与主题。
下面的示例对 QTimer 进行了子类化,使其成为一个 "timeouts" 仅在分钟内更改的计时器。它可能看起来有点复杂,但这是由于 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_())
显然,计时器有可能在分钟更改之前发出信号(如果下一个信号在分钟更改后发出,则会导致通知失败),但这主要是由于您的实现,我相信这是由您检查一个时间范围,以确保每个待办事项都在应有的时间被捕获。
作为一种可能的替代方法,您可以使用预期的 "HH:mm" 时间发出信号(使用类似于我使用的 addSecs = 120 if now.second() > 50 else 60
的语句),然后检查提醒的实际时间。
我目前正在开发 python to-do 应用程序。所有用户创建的事件都存储在名为 toDoEvents 的字典中,包含标题、due_time、remind_time 和描述。现在我正在使用 toast 开发一个通知系统。我需要不断检查每个事件的 remind_dates 列表,如果它与当前时间匹配,则调用 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 秒启动程序)
事实上,我正在编写几乎相同类型的程序,我建议使用QTimer instead of constantly checking the current time. Anyway since QTimer default timing has a 5% interval margin (which might be very high for long intervals), you might want to set its 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)。
虽然普通人不太可能让 his/her 计算机保持开启那么长的时间,但这并非不可能(想想经常 运行 数周而不重启的便携式设备),因此您可能想要添加一个函数检查过长间隔的到期时间,并最终添加另一个计时器,该计时器将 "restore" 自身直到间隔变得有效,然后启动通知的实际计时器。
我正在添加另一个答案,因为问题的更新使另一个问题有点偏离主题,但我更愿意将其留在那里,因为我相信它可能对其他人有用,即使与主题。
下面的示例对 QTimer 进行了子类化,使其成为一个 "timeouts" 仅在分钟内更改的计时器。它可能看起来有点复杂,但这是由于 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_())
显然,计时器有可能在分钟更改之前发出信号(如果下一个信号在分钟更改后发出,则会导致通知失败),但这主要是由于您的实现,我相信这是由您检查一个时间范围,以确保每个待办事项都在应有的时间被捕获。
作为一种可能的替代方法,您可以使用预期的 "HH:mm" 时间发出信号(使用类似于我使用的 addSecs = 120 if now.second() > 50 else 60
的语句),然后检查提醒的实际时间。