繁体   English   中英

在Qthread上的QTimer

[英]QTimer on a Qthread

我有一个GUI女巫,我需要使用Qtimer不断更新,因为我使用了工人Qthread,这是我的代码:

from PyQt5.QtWidgets import QApplication, QPushButton, QWidget
from PyQt5.QtCore import QThread, QTimer
import sys
import threading


class WorkerThread(QThread):
    def run(self):
        print("thread started from :" + str(threading.get_ident()))
        timer = QTimer(self)
        timer.timeout.connect(self.work)
        timer.start(5000)
        self.exec_()

    def work(self):
        print("working from :" + str(threading.get_ident()))
        QThread.sleep(5)


class MyGui(QWidget):

    worker = WorkerThread()

    def __init__(self):
        super().__init__()
        self.initUi()
        print("Starting worker from :" + str(threading.get_ident()))
        self.worker.start()

    def initUi(self):
        self.setGeometry(500, 500, 300, 300)
        self.pb = QPushButton("Button", self)
        self.pb.move(50, 50)
        self.show()


app = QApplication(sys.argv)
gui = MyGui()
app.exec_()

输出为:

Starting worker from :824
thread started from :5916
working from :824
working from :824

计时器在主线程上工作,巫婆冻结了Gui,我该如何解决?

抱歉,我误解了这个问题。 可能另一个问题的答案可能会对您有所帮助。 主要消息是您应该在Qt中使用主事件循环来不冻结GUI,而不是在__init__上执行线程: Pyqt5 qthread +信号不起作用+ gui冻结

您可以通过使用带有装饰器@pyqtSlot() Qt插槽来实现。

------------旧的(错误的)答案---------

QTimer已经可以在单独的线程上工作,所以我认为您可以自己编写该部分而不必做。 例如,您可以执行功能中已经执行的操作:

 def update_gui(self):
     # edit: here is where you can add your gui update code:
     self.setGeometry(500, 500, 300, 300)
     self.pb = QPushButton("Button", self)
     self.pb.move(50, 50)
     self.show()
     # /edit (this is obviously only the setup code, but you get the idea)

     self.update_timer = QTimer()
     self.update_timer.setInterval(int(5000))
     self.update_timer.timeout.connect(self.update_gui)
     self.update_timer.start()

并在__init__调用它。 这就是我实现了一些文本框的方法,该文本框会在几秒钟后清除。

答:在您的情况下,我看不到需要使用QThread

TL; DR;

什么时候需要在GUI上下文中使用另一个线程?

当某些任务可以阻塞主线程(称为GUI线程)时,仅应使用一个线程,并且由于该任务很耗时而导致阻塞,从而阻止了GUI事件循环正常执行其工作。 所有现代GUI都在一个事件循环中执行,该事件循环使您可以从操作系统(如键盘,鼠标等)接收通知,还可以根据用户修改GUI的状态。

在您的情况下,我看不到任何繁重的任务,因此也看不到需要QThread,我真的不知道您要定期运行的任务是什么。

假设您有一个任务消耗大量时间(例如30秒),并且必须每半小时执行一次,那么线程是必要的。 在您的情况下,您想使用QTimer。

让我们分几部分讲一下, QTimer是一个从QObject继承的类,并且QObject与父对象属于同一类,如果没有父对象,则它属于创建它的线程。 另一方面,通常认为QThreadQt线程 ,但事实并非如此, QThread是允许处理本机线程生命周期的类,并且在docs中明确指出: QThread类提供了独立于平台的方式来管理线程

了解了以上内容后,让我们分析您的代码:

timer = QTimer(self)

在上面的代码中,self是QTimer的父级,self是QThread ,因此QTimer属于QThread的父级或创建QThread的线程,而不属于QThread处理的线程。

然后,让我们看看创建QThread的代码:

worker = WorkerThread()

如我们所见, QThread没有父级,那么QThread属于创建它的线程,即QThread属于主线程,因此它的QTimer子级也属于主线程。 还请注意,如果该方法在其他地方,则QThread处理的新线程仅具有run()方法的范围,而上述所有内容都属于创建QThread的字段,通过以上所有操作,我们看到代码输出正确,并且QThread.sleep(5)在主线程上运行,导致eventloop崩溃并且GUI冻结。

因此,解决方案是删除QTimer的父级,使它所属的线程属于run()方法之一,然后在同一方法内移动工作函数。 另一方面,考虑到以上结果,以下结果是不必要的做法:不必要地创建静态属性

import sys
import threading
from PyQt5.QtCore import QThread, QTimer
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget


class WorkerThread(QThread):
    def run(self):
        def work():
            print("working from :" + str(threading.get_ident()))
            QThread.sleep(5)
        print("thread started from :" + str(threading.get_ident()))
        timer = QTimer()
        timer.timeout.connect(work)
        timer.start(10000)
        self.exec_()

class MyGui(QWidget):
    def __init__(self):
        super().__init__()
        self.initUi()
        self.worker = WorkerThread(self)
        print("Starting worker from :" + str(threading.get_ident()))
        self.worker.start()

    def initUi(self):
        self.setGeometry(500, 500, 300, 300)
        self.pb = QPushButton("Button", self)
        self.pb.move(50, 50)


if __name__ == '__main__':    
    app = QApplication(sys.argv)
    gui = MyGui()
    gui.show()
    sys.exit(app.exec_())

输出:

Starting worker from :140068367037952
thread started from :140067808999168
working from :140067808999168
working from :140067808999168

观察结果:

  • 已模拟的繁重任务为5秒,并且该任务必须每10秒执行一次。 如果您的任务花费的时间长于周期,则应创建其他线程。

  • 如果您的任务是执行不像显示时间那么重的定期任务,则不要使用新线程,因为这会给一个简单的任务增加复杂性,此外,这可能会导致调试和测试阶段变得更加复杂。

试试吧:

import sys
import threading
from PyQt5.QtGui     import *
from PyQt5.QtCore    import *
from PyQt5.QtWidgets import *

class WorkerThread(QThread):

    workSignal = pyqtSignal(str)

    def run(self):
        print("thread started from :" + str(threading.get_ident()))
        textLabel = "thread started from :" + str(threading.get_ident())
        self.workSignal.emit(textLabel)
        self.work()

    def work(self):
        print("working from :" + str(threading.get_ident()))
        textLabel = "working from :" + str(threading.get_ident())
        self.workSignal.emit(textLabel)


class MyGui(QWidget):

    worker = WorkerThread()

    def __init__(self):
        super().__init__()
        self.initUi()
        print("Starting worker from :" + str(threading.get_ident()))
        self.lbl.setText("Starting worker from :" + str(threading.get_ident()))

        self.worker.workSignal.connect(self.showLabel)

    def initUi(self):
        self.setGeometry(700, 350, 300, 150)

        self.lcdTime = QLCDNumber(self)
        self.lcdTime.setSegmentStyle(QLCDNumber.Filled)   # Outline Filled Flat
        self.lcdTime.setDigitCount(8)    

        self.timer = QTimer(self)
        self.lbl   = QLabel(self) 
        self.pb = QPushButton("Button Close", self, clicked=self.close)

        vbox = QVBoxLayout()
        vbox.addWidget(self.lcdTime)
        vbox.addWidget(self.lbl)
        vbox.addWidget(self.pb)
        self.setLayout(vbox)

        self.timer.timeout.connect(self.showTime)
        self.timer.start(1000)        
        self.numSec = 0
        self.show()

    def showTime(self):
        time = QTime.currentTime()
        text = time.toString("hh:mm:ss")           
        if ((time.second() % 2) == 0):
            text = text[0:2] + ' ' + text[3:5] + ' ' + text[6:]
        self.lcdTime.display(text)
        self.numSec += 1
        if self.numSec == 5:
            self.worker.start()
            self.numSec = 0

    def showLabel(self, textLabel):
        self.lbl.setText(textLabel)

app = QApplication(sys.argv)
gui = MyGui()
app.exec_()

在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM