我有一个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,我该如何解决?
答案 0 :(得分:0)
对不起,我误解了这个问题。可能另一个问题的答案可能会对您有所帮助。主要信息是您应该在Qt中使用主事件循环来不冻结GUI,而不是在__init__
上执行线程:
Pyqt5 qthread + signal not working + gui freeze
您可以通过将Qt插槽与装饰器@pyqtSlot()
配合使用来实现此目的。
------------旧的(错误的)答案---------
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__
中调用它。这就是我实现了一些文本框的方法,该文本框会在几秒钟后清除。
答案 1 :(得分:0)
答案::在您的情况下,我认为不需要使用QThread
。
何时需要在GUI上下文中使用另一个线程?
当某些任务可以阻塞称为GUI线程的主线程时,仅应使用一个线程,并且由于该任务很耗时而导致阻塞,从而阻止了GUI事件循环正常执行其工作。所有现代GUI都在一个事件循环中执行,该事件循环使您可以从操作系统(如键盘,鼠标等)接收通知,还可以根据用户修改GUI的状态。
在您的情况下,我看不到任何繁重的任务,因此看不到需要QThread,我真的不知道您要定期运行的任务是什么。
假设您有一个任务消耗大量时间(例如30秒),并且必须每半小时执行一次,那么线程是必要的。并且您要使用QTimer。
让我们开始吧,QTimer
是一个继承自QObject
的类,而QObject
与父级属于同一类,如果没有父级,则属于该类。属于创建它的线程。另一方面,很多时候认为QThread
是Qt的线程,但事实并非如此,QThread
是允许处理Qt生命周期的类。 docs中明确指出: QThread类提供了一种独立于平台的方式来管理线程 。
了解了以上内容,让我们分析您的代码:
timer = QTimer(self)
在上面的代码中,self是QTimer的父级,而self是QThread
的父级,因此QTimer
属于QThread
的父级的线程或QThread的创建位置,而不是QThread
处理的线程。
然后让我们看一下创建QThread
的代码:
worker = WorkerThread()
我们看到QThread
没有父级,那么QThread
属于创建它的线程,即QThread
属于主线程,因此它的{{1 }} child也属于主线程。还要注意,QTimer
处理的新线程仅具有QThread
方法的作用域,如果该方法在其他地方属于创建run()
的字段,则上述所有内容代码输出正确,并且QThread
在主线程上运行,导致eventloop崩溃并且GUI冻结。
因此解决方案是删除QThread.sleep(5)
的父级,以使其所属的线程属于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_())
观察
:已模拟的繁重任务为5秒,该任务必须每10秒执行一次。如果您的任务花费的时间超过周期,则应创建其他线程。
如果您的任务是执行不像显示时间那么重的定期任务,则不要使用新线程,因为这会增加简单任务的复杂性,否则可能会导致调试和测试阶段变更复杂。
答案 2 :(得分:-1)
尝试一下:
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_()