我正在添加另一个答案,因为对该问题的更新使另一个话题有些偏离,但我宁愿将其保留在这里,因为我相信它可能对其他人有用,即使与该话题上的话题无关问题。
下面的示例将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)。虽然正常人不太可能会在这段时间内打开计算机,但这不是不可能的(想想便携式设备通常会运行数周而无需重启),因此您可能想添加一个检查到期时间的功能间隔太长,最终添加另一个计时器,该计时器将自己“恢复”直到间隔生效,然后启动通知的实际计时器。