PyQt5 多线程相关一些例子

PyQt5 多线程相关一些例子

demo1 from https://www.cnblogs.com/hhh5460/p/5175322.html

from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QGridLayout
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot

import time
import sys

class Worker(QObject):
    finished = pyqtSignal()
    intReady = pyqtSignal(int)

    @pyqtSlot()
    def work(self): # A slot takes no params
        for i in range(1, 100):
            time.sleep(1)
            self.intReady.emit(i)

        self.finished.emit()
        

class Form(QWidget):
    def __init__(self):
       super().__init__()
       self.label = QLabel("0")

       # 1 - create Worker and Thread inside the Form
       self.worker = Worker()  # no parent!
       self.thread = QThread()  # no parent!

       self.worker.intReady.connect(self.updateLabel)
       self.worker.moveToThread(self.thread)
       self.worker.finished.connect(self.thread.quit)
       self.thread.started.connect(self.worker.work)
       #self.thread.finished.connect(app.exit)

       self.thread.start()

       self.initUI()

    def initUI(self):
        grid = QGridLayout()
        self.setLayout(grid)
        grid.addWidget(self.label,0,0)

        self.move(300, 150)
        self.setWindowTitle('thread test')

    def updateLabel(self, i):
        self.label.setText("{}".format(i))
        #print(i)

        
app = QApplication(sys.argv)
form = Form()
form.show()
sys.exit(app.exec_())
  • 1

demo2 from https://blog.csdn.net/qq_34710142/article/details/80936986

qt_prj/interface_jamming$ cat  QThread4.py
#!/usr/bin/env python
# coding=utf-8
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys

class Worker(QThread):
    sinOut = pyqtSignal(str) # 自定义信号,执行run()函数时,从相关线程发射此信号

    def __init__(self, parent=None):
        super(Worker, self).__init__(parent)
        self.working = True
        self.num = 0

    def __del__(self):
        self.working = False
        self.wait()

    def run(self):
        while self.working == True:
            file_str = 'File index {0}'.format(self.num) # str.format()
            self.num += 1

            # 发出信号
            self.sinOut.emit(file_str)

            # 线程休眠2秒
            self.sleep(2)


class MainWidget(QWidget):
    def __init__(self, parent=None):
        super(MainWidget, self).__init__(parent)

        self.setWindowTitle("QThread 例子")

        # 布局管理
        self.listFile = QListWidget()
        self.btnStart = QPushButton('开始')
        layout = QGridLayout(self)
        layout.addWidget(self.listFile, 0, 0, 1, 2)
        layout.addWidget(self.btnStart, 1, 1)

        # 连接开始按钮和槽函数
        self.btnStart.clicked.connect(self.slotStart)

        # 创建新线程,将自定义信号sinOut连接到slotAdd()槽函数
        self.thread = Worker()
        self.thread.sinOut.connect(self.slotAdd)

    # 开始按钮按下后使其不可用,启动线程
    def slotStart(self):
        self.btnStart.setEnabled(False)
        self.thread.start()

    # 在列表控件中动态添加字符串条目
    def slotAdd(self, file_inf):
        self.listFile.addItem(file_inf)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    demo = MainWidget()
    demo.show()
    sys.exit(app.exec_())
  • 1

在这里插入图片描述

demo3 卡死 from https://blog.csdn.net/qq_34710142/article/details/80936986

qt_prj/interface_jamming$ cat QThread_sleep.py 
#!/usr/bin/env python
# coding=utf-8
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import time 

global sec
sec = 0

def setTime():
    global sec
    sec += 1
    # LED显示数字+1
    lcdNumber.display(sec)

def work():
    # 计时器每秒计数
    timer.start(1000)

    # 开始一次非常耗时的计算
    # 这里用一个2 000 000 000次的循环来模拟
    #for i in range(200000000):
    #    pass
    print('start sleep: %s'%time.time())
    time.sleep(1000)

    #timer.stop()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    top = QWidget()
    top.resize(300, 120)

    # 垂直布局类QVBoxLayout
    layout = QVBoxLayout(top)

    # 添加控件
    lcdNumber = QLCDNumber()
    layout.addWidget(lcdNumber)
    button = QPushButton("测试")
    layout.addWidget(button)
    timer = QTimer()

    # 每次计时结束,触发setTime
    timer.timeout.connect(setTime)

    # 连接测试按钮和槽函数work
    button.clicked.connect(work)

    top.show()
    sys.exit(app.exec_())

似乎还是有顺序的,就是要睡完1000秒后,才去执行setTime函数。改成睡完5秒测试的确是这样的。
在这里插入图片描述
正常情况下,在点击按钮之后,LCD上的数字会随着时间发生变化,但是在实际运行过程中会发现点击按钮之后,程序界面直接停止响应,直到循环结束才开始重新更新,于是计时器始终显示为0。

在上面这个程序中没有引入新的线程,PyQt中所有的窗口都在UI主线程中(就是执行了QApplication.exec()的线程),在这个线程中执行耗时的操作会阻塞UI线程,从而让窗口停止响应。

为了避免出现上述问题,要使用QThread开启一个新的线程,在这个线程中完成耗时的操作

分离出干活的线程

qt_prj/interface_jamming$ cat QThread_live.py
#!/usr/bin/env python
# coding=utf-8
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

global sec
sec = 0

# 增加了一个继承自QThread类的类,重新写了它的run()函数
# run()函数即是新线程需要执行的:执行一个循环;发送计算完成的信号。
class WorkThread(QThread):
    trigger = pyqtSignal()

    def __int__(self):
        super(WorkThread, self).__init__()

    def run(self):
        for i in range(2000000000):
            pass

        # 循环完毕后发出信号
        self.trigger.emit()


def countTime():
    global sec
    sec += 1
    # LED显示数字+1
    lcdNumber.display(sec)


def work():
    # 计时器每秒计数
    timer.start(1000)
    # 计时开始
    workThread.start()
    # 当获得循环完毕的信号时,停止计数
    workThread.trigger.connect(timeStop)


def timeStop():
    timer.stop()
    print("运行结束用时", lcdNumber.value())
    global sec
    sec = 0


if __name__ == "__main__":
    app = QApplication(sys.argv)
    top = QWidget()
    top.resize(300, 120)

    # 垂直布局类QVBoxLayout
    layout = QVBoxLayout(top)

    # 加个显示屏
    lcdNumber = QLCDNumber()
    layout.addWidget(lcdNumber)
    button = QPushButton("测试")
    layout.addWidget(button)

    timer = QTimer()
    workThread = WorkThread()

    button.clicked.connect(work)

    # 每次计时结束,触发 countTime
    timer.timeout.connect(countTime)

    top.show()
    sys.exit(app.exec_())

实时刷新

qt_prj/interface_jamming$ cat  QThread_process.py
#!/usr/bin/env python
# coding=utf-8
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QListWidget, QGridLayout
import sys
import time


class WinForm(QWidget):

    def __init__(self, parent=None):
        super(WinForm, self).__init__(parent)
        self.setWindowTitle("实时刷新界面例子")
        self.listFile = QListWidget()
        self.btnStart = QPushButton('开始')

        layout = QGridLayout(self)
        layout.addWidget(self.listFile, 0, 0, 1, 2)
        layout.addWidget(self.btnStart, 1, 1)
        self.setLayout(layout)

        self.btnStart.clicked.connect(self.slotAdd)

    def slotAdd(self):
        for n in range(10):
            str_n = 'File index {0}'.format(n)
            self.listFile.addItem(str_n)
            QApplication.processEvents()
            time.sleep(0.1)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = WinForm()
    form.show()
    sys.exit(app.exec_())

demo5

进度条

qt_prj/interface_jamming$ cat QThread5.py 
#!/usr/bin/env python
# coding=utf-8
from PyQt5 import QtWidgets, QtCore
import sys
from PyQt5.QtCore import *
import time
 
 
# 继承QThread
class Runthread(QtCore.QThread):
    #  通过类成员对象定义信号对象
    _signal = pyqtSignal(str)
 
    def __init__(self):
        super(Runthread, self).__init__()
 
    def __del__(self):
        self.wait()
 
    def run(self):
        for i in range(100):
            time.sleep(0.05)
            self._signal.emit(str(i))  # 注意这里与_signal = pyqtSignal(str)中的类型相同
        self._signal.emit(str(100))
 
 
class Example(QtWidgets.QWidget):
 
    def __init__(self):
        super().__init__()
        # 按钮初始化
        self.button = QtWidgets.QPushButton('开始', self)
        self.button.setToolTip('这是一个 <b>QPushButton</b> widget')
        self.button.resize(self.button.sizeHint())
        self.button.move(120, 80)
        self.button.clicked.connect(self.start_login)  # 绑定多线程触发事件
 
        # 进度条设置
        self.pbar = QtWidgets.QProgressBar(self)
        self.pbar.setGeometry(50, 50, 210, 25)
        self.pbar.setValue(0)
 
        # 窗口初始化
        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('OmegaXYZ.com')
        self.show()
 
        self.thread = None  # 初始化线程
 
    def start_login(self):
        # 创建线程
        self.button.setEnabled(False)
        self.thread = Runthread()
        # 连接信号
        self.thread._signal.connect(self.call_backlog)  # 进程连接回传到GUI的事件
        # 开始线程
        self.thread.start()
 
    def call_backlog(self, msg):
        self.pbar.setValue(int(msg))  # 将线程的参数传入进度条
        if msg == '100':
            #self.thread.terminate()
            del self.thread
            self.button.setEnabled(True)
 
 
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    myshow = Example()
    myshow.show()
    sys.exit(app.exec_())

在这里插入图片描述

demo 稍微复杂一点的例子

/interface_jamming$ cat QThread6.py
#!/usr/bin/env python
# coding=utf-8
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
import random

class Example(QObject):

    signalStatus = pyqtSignal(str)

    def __init__(self, parent=None):
        super(self.__class__, self).__init__(parent)

        # Create a gui object.
        self.gui = Window()

        # Setup the worker object and the worker_thread.
        self.worker = WorkerObject()
        self.worker_thread = QThread()
        self.worker.moveToThread(self.worker_thread)
        self.worker_thread.start()

        # Make any cross object connections.
        self._connectSignals()

        self.gui.show()

    def _connectSignals(self):
        self.gui.button_start.clicked.connect(self.worker.startWork)
        self.gui.button_cancel.clicked.connect(self.forceWorkerReset)
        self.signalStatus.connect(self.gui.updateStatus)
        self.worker.signalStatus.connect(self.gui.updateStatus)

        self.parent().aboutToQuit.connect(self.forceWorkerQuit)

    def forceWorkerReset(self):
        if self.worker_thread.isRunning():
            self.worker_thread.terminate()
            self.worker_thread.wait()

            self.signalStatus.emit('Idle.')
            self.worker_thread.start()

    def forceWorkerQuit(self):
        if self.worker_thread.isRunning():
            self.worker_thread.terminate()
            self.worker_thread.wait()


class WorkerObject(QObject):

    signalStatus = pyqtSignal(str)

    def __init__(self, parent=None):
        super(self.__class__, self).__init__(parent)

    @pyqtSlot()        
    def startWork(self):
        for ii in range(7):
            number = random.randint(0,5000**ii)
            self.signalStatus.emit('Iteration: {}, Factoring: {}'.format(ii, number))
            factors = self.primeFactors(number)
            print('Number: ', number, 'Factors: ', factors)
        self.signalStatus.emit('Idle.')

    def primeFactors(self, n):
        i = 2
        factors = []
        while i * i <= n:
            if n % i:
                i += 1
            else:
                n //= i
                factors.append(i)
        if n > 1:
            factors.append(n)
        return factors


class Window(QWidget):

    def __init__(self):
        QWidget.__init__(self)
        self.button_start = QPushButton('Start', self)
        self.button_cancel = QPushButton('Cancel', self)
        self.label_status = QLabel('', self)

        layout = QVBoxLayout(self)
        layout.addWidget(self.button_start)
        layout.addWidget(self.button_cancel)
        layout.addWidget(self.label_status)

        self.setFixedSize(400, 200)

    @pyqtSlot(str)
    def updateStatus(self, status):
        self.label_status.setText(status)


if __name__=='__main__':
    app = QApplication(sys.argv)
    example = Example(app)
    sys.exit(app.exec_())

他的退出方式,似乎挺奇怪的,注意到他的父类是app,他这样做到底想干啥?

在这里插入图片描述

一个线程定义多个信号

from https://blog.csdn.net/weixin_34150224/article/details/88320458

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
"""
Created on 2019年1月16日
@author: yiluo
@site: https://github.com/bingshilei
@email: [email protected]
@file: QThreadDemo2
@description: 使用多线程动态添加控件
"""
import time
 
from PyQt5.QtCore import QThread, pyqtSignal, QDateTime
from PyQt5.QtWidgets import QWidget, QLineEdit, QListWidget, QPushButton,\
    QVBoxLayout, QLabel
 
'''
声明线程类
'''
 
 
class addItemThread(QThread):
    add_item = pyqtSignal(str)
    show_time = pyqtSignal(str)
 
    '''
            添加控件
    '''
    def __init__(self,*args, **kwargs):
        super(addItemThread, self).__init__(*args, **kwargs)
        self.num = 0
        
    def run(self, *args, **kwargs):
        while True:
            file_str = 'File index{0}'.format(self.num,*args, **kwargs)
            self.num +=1
            
            #发送添加信号
            self.add_item.emit(file_str)
            
            date = QDateTime.currentDateTime()
            currtime = date.toString('yyyy-MM-dd hh:mm:ss')
            print(currtime)
            self.show_time.emit(str(currtime))
            
            time.sleep(1)
 
 
class Window(QWidget):
 
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        self.setWindowTitle('多线程动态添加控件')
        # x,y,w,h
        self.setGeometry(800, 100, 500, 750)
        #创建QListWidget控件
        self.listWidget = QListWidget()
        #创建按钮控件
        self.btn = QPushButton('开始',self)
        self.lb = QLabel('显示时间',self)
        #创建布局控件
        self.vlayout = QVBoxLayout()
        #将按钮和列表控件添加到布局
        self.vlayout.addWidget(self.btn)
        self.vlayout.addWidget(self.lb)
        self.vlayout.addWidget(self.listWidget)
        #设置窗体的布局
        self.setLayout(self.vlayout)
        
        #绑定按钮槽函数
        self.btn.clicked.connect(self.startThread)
        
        #声明线程实例
        self.additemthread = addItemThread()
        
        #绑定增加控件函数
        self.additemthread.add_item.connect(self.addItem)
        
        #绑定显示时间函数
        
        self.additemthread.show_time.connect(self.showTime)
        
    '''
    @description:按钮开始,启动线程
    '''
    def startThread(self):
        #按钮不可用
        self.btn.setEnabled(False)
        #启动线程
        self.additemthread.start()
 
    '''
    @description:为listwidget增加项
    @param:项的值 
    '''
    def addItem(self,file_str):
        self.listWidget.addItem(file_str)
        
    '''
    @description:显示时间
    @param:项的值 
    '''
    def showTime(self,time):
        self.lb.setText(time)
        
 
if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication
    app = QApplication(sys.argv)
    w = Window()
    w.show()
    sys.exit(app.exec_())

QThread 线程里面启用常规子线程干活

from https://blog.csdn.net/weiyang_tang/article/details/82748111

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

class fileSearchThread(QThread):
    sinOut = pyqtSignal(str)
    # 自定义信号,执行run()函数时,从相关线程发射此信号

    def __init__(self,key):
        super().__init__()
        self.key = key

    def run(self):
        threads=[]
        path = [r"c:\\", r"d:\\", r"e:\\", r"f:\\"]
        #通过多线程对windows下的多个盘符进行文件的遍历查找
        for each in path:
            t = threading.Thread(target=self.search, args=(self.key,each,))
            threads.append(t)
            t.start()

        for i in range(len(threads)): #将主线程阻塞
            threads[i].join()
        print("搜索结束")

    def search(self,keyword, path):
        for dirpath, dirnames, filenames in os.walk(path):
            for filename in filenames:
                if filename.__contains__(keyword):
                    print(os.path.join(dirpath, filename))
                    self.sinOut.emit(os.path.join(dirpath, filename))
            for folder in dirnames:
                if folder.__contains__(keyword):
                    print(os.path.join(dirpath,folder))
                    self.sinOut.emit(os.path.join(dirpath,folder))


class fileSearch(QListWidget):

    def __init__(self):
        super().__init__()
        self.Ui()

    def Ui(self):
        self.key= QLineEdit()
        self.bt=QPushButton("搜索")
        self.result = QListWidget()

        self.bt.clicked.connect(self.ButtonClicked) #按钮单击信号绑定到槽
        # self.line.editingFinished.connect(self.Action)
        self.key.editingFinished.connect(self.ButtonClicked)

        grid = QGridLayout()
        grid.setSpacing(10)  # 创建标签之间的空间

        grid.addWidget(self.key, 1, 0)  # (1,0)表示显示的位置
        grid.addWidget(self.bt, 1, 1)
        grid.addWidget(self.result, 2, 0, 5, 2)  # 指定组件的跨行和跨列的大小,指定这个元素跨5行显示

        self.setLayout(grid)
        for i in range(1,100):
            self.result.addItem("搜索"+str(i)+"个项目")

        self.result.itemClicked.connect(self.Clicked)

        self.setGeometry(300, 300, 500, 500)
        self.setWindowTitle('文件搜索')
        self.setWindowIcon(QIcon('icon.jpg'))
        self.show()

    def Clicked(self, item):
        QMessageBox.information(self, "ListWidget", "You clicked: " + item.text())
        os.startfile(item.text()) #打开文件

    def ButtonClicked(self):
        # 创建新线程,将自定义信号sinOut连接到slotAdd()槽函数
        keyword = self.key.text()
        self.result.clear()
        self.thread=fileSearchThread(keyword)
        self.thread.sinOut.connect(self.slotAdd)
        self.thread.start()

    def slotAdd(self,filename):

        self.result.addItem(str(filename))


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = fileSearch()
    sys.exit(app.exec_())
发布了136 篇原创文章 · 获赞 71 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/u012308586/article/details/104982253