PyQt5在子线程中自动更新当前时间

发布于 2025-01-15 02:17:33 字数 3186 浏览 3 评论 0原文

我想自动更新 Qstatusbar 中的当前时间。我使用 mainUI 线程来显示时间,并使用子线程来执行其他长时间运行的任务。这工作正常。

现在,我想使用子线程来显示时间。如下图,当我点击按钮3时,时间被冻结,而点击按钮1和按钮2时,时间正在运行。

import time
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

class WorkerSignals(QObject):
    finished = pyqtSignal()
    result = pyqtSignal(object)

class Worker(QRunnable):
    def __init__(self, fn, *args, **kwargs):
        super(Worker, self).__init__()
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()
    def run(self):
        result = self.fn(*self.args, **self.kwargs)
        self.signals.result.emit(result)
        self.signals.finished.emit()

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.threadpool = QThreadPool()
        self.set_main_layout()
        self.show_current_time()

    def set_main_layout(self):
        self.layout = QVBoxLayout()

        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)

        self.btn1 = QPushButton('Sleep with thread')
        self.btn1.clicked.connect(self.btn1_onclick)

        self.btn2 = QPushButton('Sum with thread')
        self.btn2.clicked.connect(self.btn2_onclick)

        self.btn3 = QPushButton('Sleep without thread')
        self.btn3.clicked.connect(self.btn3_onclick)

        self.layout.addWidget(self.btn1)
        self.layout.addWidget(self.btn2)
        self.layout.addWidget(self.btn3)

        w = QWidget()
        w.setLayout(self.layout)
        self.setCentralWidget(w)

        self.status_bar = self.statusBar()
        self.status_bar.showMessage('Ready')
        self.time = QLabel()
        self.status_bar.addPermanentWidget(self.time)
        self.setStatusBar(self.status_bar)

    def show_current_time(self):
        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(lambda: self.time.setText(QTime.currentTime().toString("hh:mm:ss")))
        self.timer.start()

    def btn1_onclick(self):
        worker = Worker(self.btn1_fn)
        worker.signals.result.connect(self.btn1_result)
        worker.signals.finished.connect(lambda: print('Btn1 finished...'))
        self.threadpool.start(worker)

    def btn1_fn(self):
        i = 0
        while i < 5:
            print(f'Btn1 sleeping...{i}')
            time.sleep(1)
            i += 1
        return i

    def btn1_result(self,i):
        pass

    def btn2_onclick(self):
        worker = Worker(self.btn2_fn)
        worker.signals.result.connect(self.btn2_result)
        worker.signals.finished.connect(lambda: print('Btn2 finished...'))
        self.threadpool.start(worker)

    def btn2_fn(self):
        sum=0
        print('Btn2 summing...')
        for i in range(50000000):
            sum+=1
        return sum

    def btn2_result(self, num):
        print(f'Btn2 sum: {num}')

    def btn3_onclick(self):
        i=0
        while i<5:
            print(f'Btn3 sleeping...{i}')
            time.sleep(1)
            i+=1

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec_()

I want to automatically update the current time in Qstatusbar. I used mainUI thread for displaying time and subthreads for other long-running tasks. This is working fine.

Now, I would like to use the subthread for displaying time. As shown below, when I click on button 3, the time is frozen while clicking on buttons 1 and button 2 the time is running.

import time
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

class WorkerSignals(QObject):
    finished = pyqtSignal()
    result = pyqtSignal(object)

class Worker(QRunnable):
    def __init__(self, fn, *args, **kwargs):
        super(Worker, self).__init__()
        self.fn = fn
        self.args = args
        self.kwargs = kwargs
        self.signals = WorkerSignals()
    def run(self):
        result = self.fn(*self.args, **self.kwargs)
        self.signals.result.emit(result)
        self.signals.finished.emit()

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.threadpool = QThreadPool()
        self.set_main_layout()
        self.show_current_time()

    def set_main_layout(self):
        self.layout = QVBoxLayout()

        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)

        self.btn1 = QPushButton('Sleep with thread')
        self.btn1.clicked.connect(self.btn1_onclick)

        self.btn2 = QPushButton('Sum with thread')
        self.btn2.clicked.connect(self.btn2_onclick)

        self.btn3 = QPushButton('Sleep without thread')
        self.btn3.clicked.connect(self.btn3_onclick)

        self.layout.addWidget(self.btn1)
        self.layout.addWidget(self.btn2)
        self.layout.addWidget(self.btn3)

        w = QWidget()
        w.setLayout(self.layout)
        self.setCentralWidget(w)

        self.status_bar = self.statusBar()
        self.status_bar.showMessage('Ready')
        self.time = QLabel()
        self.status_bar.addPermanentWidget(self.time)
        self.setStatusBar(self.status_bar)

    def show_current_time(self):
        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(lambda: self.time.setText(QTime.currentTime().toString("hh:mm:ss")))
        self.timer.start()

    def btn1_onclick(self):
        worker = Worker(self.btn1_fn)
        worker.signals.result.connect(self.btn1_result)
        worker.signals.finished.connect(lambda: print('Btn1 finished...'))
        self.threadpool.start(worker)

    def btn1_fn(self):
        i = 0
        while i < 5:
            print(f'Btn1 sleeping...{i}')
            time.sleep(1)
            i += 1
        return i

    def btn1_result(self,i):
        pass

    def btn2_onclick(self):
        worker = Worker(self.btn2_fn)
        worker.signals.result.connect(self.btn2_result)
        worker.signals.finished.connect(lambda: print('Btn2 finished...'))
        self.threadpool.start(worker)

    def btn2_fn(self):
        sum=0
        print('Btn2 summing...')
        for i in range(50000000):
            sum+=1
        return sum

    def btn2_result(self, num):
        print(f'Btn2 sum: {num}')

    def btn3_onclick(self):
        i=0
        while i<5:
            print(f'Btn3 sleeping...{i}')
            time.sleep(1)
            i+=1

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec_()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

掩耳倾听 2025-01-22 02:17:33

在 Qt(以及大多数其他 gui 框架)中,所有 gui 小部件都位于同一线程(称为 gui 线程)中。您不能像在 btn3_onclick 中尝试那样只冻结一个按钮。从 gui 线程调用 time.sleep(1) (就像你所做的那样)会冻结整个 gui 线程,这意味着它会冻结所有窗口和所有小部件。

您不需要线程定期更新 ui,只需使用计时器即可。 QTimer 不使用线程,它只是将事件安排到事件循环。

timer = QtCore.QTimer()
timer.timeout.connect(lambda: ui.label.setText(datetime.datetime.now().strftime("%H:%M:%S"))
timer.start(1000)

In Qt (and in most other gui frameworks), all gui widgets live in same thread (named gui thread). You can not freeze just one button like you try to do in btn3_onclick. Calling time.sleep(1) from gui thread (like you did) freezes whole gui thread which means it freezes all windows and all widgets.

You dont need thread to periodically update ui, just use timer. QTimer doesn't use thread it just schedules event to eventloop.

timer = QtCore.QTimer()
timer.timeout.connect(lambda: ui.label.setText(datetime.datetime.now().strftime("%H:%M:%S"))
timer.start(1000)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文