Как динамически обновлять время в мультитаймере pyqt

У меня есть мультитаймер. Он работает по такому принципу:

Мы вводим время, нажимаем кнопку start, вычисляется время окончания таймера и записывается в словари по типу {"Timer 1": QDateTime} и {QDateTime: "Timer 1"}. Затем в QListWidget'ы выводится время окончания таймера и сколько осталось минут до окончания таймера.

Работает по такому принципу потому-что иногда нужно ускорять таймер в х1.5 раз(Есть checkBox).

Все работает отлично, только я ходел бы еще динамическое обновление времени в QListWidget и в QTimeEdit. У меня есть кнопка и функция для этого, но я хочу чтобы время обновлялось автоматически.

Может быть есть способ реализации этого всего этого более простым и эффективным способом, но мне главное чтобы обновлялось время?

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QDate, QTime, QDateTime, Qt
import time

timer_list = ["Timer 1", "Timer 2", "Timer 3", "Timer 4"]
timer_active_by_timeEnd = {}
timer_active_by_timer = {}

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.setWindowModality(QtCore.Qt.WindowModal)
        MainWindow.resize(500, 269)
        font = QtGui.QFont()
        font.setBold(False)
        font.setWeight(50)
        MainWindow.setFont(font)
        MainWindow.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
        MainWindow.setMouseTracking(False)
        MainWindow.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/Icon/Gold-Pickaxe-icon.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        MainWindow.setWindowIcon(icon)
        MainWindow.setIconSize(QtCore.QSize(128, 128))
        MainWindow.setTabShape(QtWidgets.QTabWidget.Rounded)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.label_1 = QtWidgets.QLabel(self.centralwidget)
        self.label_1.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.label_1.setAlignment(QtCore.Qt.AlignCenter)
        self.label_1.setObjectName("label_1")
        self.verticalLayout_3.addWidget(self.label_1)
        self.timeEdit_1 = QtWidgets.QTimeEdit(self.centralwidget)
        self.timeEdit_1.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self.timeEdit_1.setAlignment(QtCore.Qt.AlignCenter)
        self.timeEdit_1.setObjectName("timeEdit_1")
        self.verticalLayout_3.addWidget(self.timeEdit_1)
        self.btn_1 = QtWidgets.QPushButton(self.centralwidget)
        self.btn_1.setMaximumSize(QtCore.QSize(16777215, 25))
        self.btn_1.setObjectName("btn_1")
        self.verticalLayout_3.addWidget(self.btn_1)
        self.horizontalLayout_5.addLayout(self.verticalLayout_3)
        self.verticalLayout_4 = QtWidgets.QVBoxLayout()
        self.verticalLayout_4.setObjectName("verticalLayout_4")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.label_2.setAlignment(QtCore.Qt.AlignCenter)
        self.label_2.setObjectName("label_2")
        self.verticalLayout_4.addWidget(self.label_2)
        self.timeEdit_2 = QtWidgets.QTimeEdit(self.centralwidget)
        self.timeEdit_2.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self.timeEdit_2.setAlignment(QtCore.Qt.AlignCenter)
        self.timeEdit_2.setObjectName("timeEdit_2")
        self.verticalLayout_4.addWidget(self.timeEdit_2)
        self.btn_2 = QtWidgets.QPushButton(self.centralwidget)
        self.btn_2.setMaximumSize(QtCore.QSize(16777215, 25))
        self.btn_2.setObjectName("btn_2")
        self.verticalLayout_4.addWidget(self.btn_2)
        self.horizontalLayout_5.addLayout(self.verticalLayout_4)
        self.verticalLayout.addLayout(self.horizontalLayout_5)
        self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
        self.verticalLayout_5 = QtWidgets.QVBoxLayout()
        self.verticalLayout_5.setObjectName("verticalLayout_5")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.label_3.setAlignment(QtCore.Qt.AlignCenter)
        self.label_3.setObjectName("label_3")
        self.verticalLayout_5.addWidget(self.label_3)
        self.timeEdit_3 = QtWidgets.QTimeEdit(self.centralwidget)
        self.timeEdit_3.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self.timeEdit_3.setAlignment(QtCore.Qt.AlignCenter)
        self.timeEdit_3.setObjectName("timeEdit_3")
        self.verticalLayout_5.addWidget(self.timeEdit_3)
        self.btn_3 = QtWidgets.QPushButton(self.centralwidget)
        self.btn_3.setMaximumSize(QtCore.QSize(16777215, 25))
        self.btn_3.setObjectName("btn_3")
        self.verticalLayout_5.addWidget(self.btn_3)
        self.horizontalLayout_6.addLayout(self.verticalLayout_5)
        self.verticalLayout_13 = QtWidgets.QVBoxLayout()
        self.verticalLayout_13.setObjectName("verticalLayout_13")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.label_4.setAlignment(QtCore.Qt.AlignCenter)
        self.label_4.setObjectName("label_4")
        self.verticalLayout_13.addWidget(self.label_4)
        self.timeEdit_4 = QtWidgets.QTimeEdit(self.centralwidget)
        self.timeEdit_4.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self.timeEdit_4.setAlignment(QtCore.Qt.AlignCenter)
        self.timeEdit_4.setObjectName("timeEdit_4")
        self.verticalLayout_13.addWidget(self.timeEdit_4)
        self.btn_4 = QtWidgets.QPushButton(self.centralwidget)
        self.btn_4.setMaximumSize(QtCore.QSize(16777215, 25))
        self.btn_4.setObjectName("btn_4")
        self.verticalLayout_13.addWidget(self.btn_4)
        self.horizontalLayout_6.addLayout(self.verticalLayout_13)
        self.verticalLayout.addLayout(self.horizontalLayout_6)
        self.horizontalLayout.addLayout(self.verticalLayout)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.verticalLayout_40 = QtWidgets.QVBoxLayout()
        self.verticalLayout_40.setObjectName("verticalLayout_40")
        self.listWidget_2 = QtWidgets.QListWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.listWidget_2.sizePolicy().hasHeightForWidth())
        self.listWidget_2.setSizePolicy(sizePolicy)
        self.listWidget_2.setObjectName("listWidget_2")
        self.verticalLayout_40.addWidget(self.listWidget_2)
        self.horizontalLayout_2.addLayout(self.verticalLayout_40)
        self.verticalLayout_41 = QtWidgets.QVBoxLayout()
        self.verticalLayout_41.setObjectName("verticalLayout_41")
        self.listWidget = QtWidgets.QListWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
        self.listWidget.setSizePolicy(sizePolicy)
        self.listWidget.setObjectName("listWidget")
        self.verticalLayout_41.addWidget(self.listWidget)
        self.horizontalLayout_2.addLayout(self.verticalLayout_41)
        self.verticalLayout_2.addLayout(self.horizontalLayout_2)
        self.horizontalLayout_10 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_10.setObjectName("horizontalLayout_10")
        self.checkBox_2 = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox_2.setMaximumSize(QtCore.QSize(200, 16777215))
        self.checkBox_2.setObjectName("checkBox_2")
        self.horizontalLayout_10.addWidget(self.checkBox_2)
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_10.addItem(spacerItem)
        self.verticalLayout_2.addLayout(self.horizontalLayout_10)
        self.btn_update = QtWidgets.QPushButton(self.centralwidget)
        self.btn_update.setObjectName("btn_update")
        self.verticalLayout_2.addWidget(self.btn_update)
        self.horizontalLayout.addLayout(self.verticalLayout_2)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "BossTimer"))
        self.label_1.setText(_translate("MainWindow", "1"))
        self.btn_1.setText(_translate("MainWindow", "Start"))
        self.label_2.setText(_translate("MainWindow", "2"))
        self.btn_2.setText(_translate("MainWindow", "Start"))
        self.label_3.setText(_translate("MainWindow", "3"))
        self.btn_3.setText(_translate("MainWindow", "Start"))
        self.label_4.setText(_translate("MainWindow", "4"))
        self.btn_4.setText(_translate("MainWindow", "Start"))
        self.checkBox_2.setText(_translate("MainWindow", "x1.5"))
        self.btn_update.setText(_translate("MainWindow", "Update"))


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.lists()                                                    # Cоздаем списки QPushButton и QTimeEdit
        self.btn_connect()                                              # Подключаем кнопки к функции

    def lists(self):
        self.btn_list = [self.btn_1, self.btn_2, self.btn_3, self.btn_4]
        self.tm_list = [self.timeEdit_1, self.timeEdit_2, self.timeEdit_3, self.timeEdit_4]

    def btn_connect(self):
        self.btn_1.clicked.connect(lambda: self.start_timer(0))
        self.btn_2.clicked.connect(lambda: self.start_timer(1))
        self.btn_3.clicked.connect(lambda: self.start_timer(2))
        self.btn_4.clicked.connect(lambda: self.start_timer(3))
        self.btn_update.clicked.connect(self.update)

    def QTimeToSec(self, convert_time):                                 # Функция которая преобразовывает QTime в секунды      
        return (convert_time.hour()*60+convert_time.minute())*60

    def start_timer(self, timer_id):                                    # Функция вызывается при нажатии кнопки старт, в функцию передается id тамера (1, 2, 3, 4)
        if self.btn_list[timer_id].text() == "Start":                   # Если кнопка Start      
            Now = QDateTime.currentDateTime()                           # Получаем текущее вермя
            QTimeLeft = self.tm_list[timer_id].time()                   # Получаем время из QTimeEdit
            SecTimeLeft = self.QTimeToSec(QTimeLeft)                    # Переводим QTime в секунды

            if self.checkBox_2.checkState() == 2:                       # Ускоритель х1.5
                SecTimeLeft = int(SecTimeLeft/1.5)

            timeEnd = Now.addSecs(SecTimeLeft)                          # Вичесляем время окончания таймера
            timer_active_by_timeEnd[timeEnd] = timer_list[timer_id]     # Добавляем в словарь по Окончании времени
            timer_active_by_timer[timer_list[timer_id]] = timeEnd       # Добавляем в словарь по названию таймера
            self.btn_list[timer_id].setText("Stop")                     # Изменяем названия кнопки
        elif self.btn_list[timer_id].text() == "Stop":
            self.b_del(timer_list[timer_id])                            # Удаляем таймер
        self.update_lists()                                             # Обновляем списки

    def b_del(self, timer_id):
        del timer_active_by_timeEnd[timer_active_by_timer[timer_id]]    # Удаляем таймер в словарях
        del timer_active_by_timer[timer_id]                              
        self.btn_list[timer_list.index(timer_id)].setText("Start")      # Меняем названия кнопки

    def update_lists(self):
        res = ""
        timeNow = QTime.currentTime()
        keys = timer_active_by_timeEnd.keys()                           # Получаем ключи из словаря
        keys = sorted(keys)                                             # Сортируем ключи
        self.listWidget_2.clear()                                       # Очищяем виджеты
        self.listWidget.clear()
        for key in keys:                                                # Выводи список на 1-ый виджет. Вычетаем из времени окончания таймера текущее время и переводим в минуты
            addsecs = self.QTimeToSec(timeNow)*-1                          
            timeLeft = key.addSecs(addsecs)
            MinTimeLeft = timeLeft.time().hour()*60+timeLeft.time().minute()
            self.listWidget.addItem(timer_active_by_timeEnd[key] + " -> " + str(MinTimeLeft)+ "m")

        for key in keys:                                                # Выводи список на 2-ой виджет. Переводи QTime в строку
            self.listWidget_2.addItem(key.toString("hh:mm")+" -> "+timer_active_by_timeEnd[key])   

    def update(self):                                                   # Функция обновления времени
        timeNow = QTime.currentTime()
        keys = timer_active_by_timer.keys()                             # Получаем список активных таймеров
        for key in keys:
            addsecs = ((timeNow.hour()*60+timeNow.minute())*60+timeNow.second())*-1     # Переводим текущее время в секунды
            timeLeft = timer_active_by_timer[key].addSecs(addsecs)                      # Вычетаем из времени окончания таймера текущее время
            self.tm_list[timer_list.index(key)].setTime(timeLeft.time())                # Устанавляваем оставшееся время для таймера в QTimeEdit
        self.update_lists()                                                             # Обновляем список


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

Ответы (1 шт):

Автор решения: Alexander Chernin

Вам надо воспользоваться таймером (некий мастер-таймер), для этого используйте класс QTimer:

from PyQt5.QtCore import QTimer

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.lists()                                                    # Cоздаем списки QPushButton и QTimeEdit
        self.btn_connect()                                              # Подключаем кнопки к функции

        self.masterTimer = QTimer()
        self.masterTimer.timeout.connect(self.update) # периодический сигнал-вызов метода через интервал timeout (здесь 0.5 сек)
        self.masterTimer.start(500)                   # СТАРТ таймера с интервалом 0.5 сек

...
→ Ссылка