赞
踩
在Qt中,开启多线程的方法有多种,总体分成QThread、QObject、QRunnable、QtConcurrent三大类方法。
而放到PyQt和PySide具体的使用中,使用方法可以说十分类似。
一、继承QThread类及run方法。
此方法可以说是Qt线程的基础,主要方法是重写一个类, 继承QThread类,然后重写run方法。
例如:
- # !/usr/bin python3
- # -*- encoding=utf-8 -*-
- # Description :
- # Author :
- # @Email :
- # @File : 01_thread.py
- # @Time : 2023-06-27 15:37:08
- # @Project : qt
-
- from PySide6.QtCore import QThread, Signal
- from PySide6.QtWidgets import QWidget, QApplication, QPushButton
-
-
- class Work(QThread):
- result = Signal()
-
- def __init__(self):
- QThread.__init__(self)
-
- def run(self) -> None:
- for i in range(0, 10):
- print('i = {}'.format(i))
- self.usleep(1000000)
- self.result.emit()
-
-
- class Widget(QWidget):
-
- def __init__(self):
- QWidget.__init__(self)
- self._work = Work()
- self._work.result.connect(self._result)
- self._widget()
-
- def _widget(self):
- _btn = QPushButton('确定')
- _btn.setParent(self)
- _btn.clicked.connect(self._run)
-
- def _run(self):
- self._work.start()
-
- def _result(self):
- print('线程结束')
-
-
- if __name__ == '__main__':
- app = QApplication()
-
- window = Widget()
- window.show()
-
- app.exec()

此方法的特点:
1、优点:可以通过信号槽与外界进行通信。 2、缺点: 1)每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。 2)要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。 3、适用场景:QThread适用于那些常驻内存的任务。
二、使用QObject类的moveToThread方法,调用QThread执行线程
创建一个继承QObject的类(如Work类),然后new一个Qthread,并把创建的Work类moveToThread到创建好的子线程中,然后start方法启动子线程,这样就实现了一个子线程。主线程通过发送信号,调用Work中的方法,从而实现在子线程中的计算。
- # !/usr/bin python3
- # -*- encoding=utf-8 -*-
- # Description :
- # Author :
- # @Email :
- # @File : 02_object.py
- # @Time : 2023-06-27 16:00:40
- # @Project : qt
-
- from PySide6.QtCore import QThread, Signal, QObject
- from PySide6.QtWidgets import QWidget, QApplication, QPushButton
-
-
- class Work(QObject):
- result = Signal()
- is_run = False
-
- def __init__(self):
- QObject.__init__(self)
-
- def run(self) -> None:
- for i in range(0, 10):
- print('i = {}'.format(i))
- QThread.usleep(1000000)
- self.result.emit()
-
- def start(self):
- self.is_run = True
-
- def stop(self):
- self.is_run = False
-
-
- class Widget(QWidget):
-
- def __init__(self):
- QWidget.__init__(self)
- self._widget()
-
- def _widget(self):
- _btn = QPushButton('确定')
- _btn.setParent(self)
- _btn.clicked.connect(self._run)
-
- def _run(self):
- _work = Work()
- _thread = QThread()
- _work.result.connect(self._result)
- _work.moveToThread(_thread)
-
- def _result(self):
- print('线程结束')
-
-
- if __name__ == '__main__':
- app = QApplication()
-
- window = Widget()
- window.show()
-
- app.exec()

此方法的特点:
moveToThread对比传统子类化Qthread更灵活,仅需要把你想要执行的代码放到槽,moveToThread这个object到线程,然后拿一个信号连接到这个槽就可以让这个槽函数在线程里执行。可以说,moveToThread给我们编写代码提供了新的思路,当然不是说子类化qthread不好,只是你应该知道还有这种方式去调用线程。
轻量级的函数可以用moveToThread,多个短小精悍能返回快速的线程函数适用 ,无需创建独立线程类,例如你有20个小函数要在线程内做, 全部扔给一个QThread。而我觉得moveToThread和子类化QThread的区别不大,更可能是使用习惯引导。又或者你一开始没使用线程,但是后边发觉这些代码还是放线程比较好,如果用子类化QThread的方法重新设计代码,将会有可能让你把这一段推到重来,这个时候,moveToThread的好处就来了,你可以把这段代码的从属着moveToThread,把代码移到槽函数,用信号触发它就行了。其它的话moveToThread它的效果和子类化QThread的效果是一样的,槽就相当于你的run()函数,你往run()里塞什么代码,就可以往槽里塞什么代码,子类化QThread的线程只可以有一个入口就是run(),而moveToThread就有很多触发的入口。
三、使用QRunnable类
Qrunnable是所有可执行对象的基类。我们可以继承Qrunnable,并重写虚函数void QRunnable。run () 。
我们可以用QThreadPool让我们的一个QRunnable对象在另外的线程中运行,如果autoDelete()返回true(默认),那么QThreadPool将会在run()运行结束后自动删除Qrunnable对象。可以调用void QRunnable::setAutoDelete ( bool autoDelete )更改auto-deletion标记。
需要注意的是,必须在调用QThreadPool::start()之前设置,在调用QThreadPool::start()之后设置的结果是未定义的。
一、实现方法:
1、继承QRunnable。和QThread使用一样, 首先需要将你的线程类继承于QRunnable。
2、重写run函数。还是和QThread一样,需要重写run函数,run是一个纯虚函数,必须重写。
3、使用QThreadPool启动线程
代码示例:
- # !/usr/bin python3
- # -*- encoding=utf-8 -*-
- # Description :
- # Author :
- # @Email :
- # @File : 03_runable.py
- # @Time : 2023-06-27 16:07:54
- # @Project : qt
-
-
- from PySide6.QtCore import QThread, Signal, QObject, QRunnable, QThreadPool
- from PySide6.QtWidgets import QWidget, QApplication, QPushButton
-
-
- class Runnable(QRunnable):
-
- def __init__(self):
- QRunnable.__init__(self)
-
- def run(self):
- for i in range(0, 10):
- print('i = {}'.format(i))
- QThread.usleep(1000000)
-
-
- class Widget(QWidget):
-
- def __init__(self):
- QWidget.__init__(self)
- self._widget()
-
- def _widget(self):
- _btn = QPushButton('确定')
- _btn.setParent(self)
- _btn.clicked.connect(self._run)
-
- def _run(self):
- _run_able = Runnable()
- _run_able.setAutoDelete(True) # Auto-deletion is enabled by default
- _pool = QThreadPool.globalInstance()
- _pool.start(_run_able)
- # _pool.waitForDone() # 会阻塞UI
-
- # 可同时启动多个线程
- # QRunnable 不是QObject的子类 不能使用Signal, 不能使用信号槽与外界通信。
-
-
-
- if __name__ == '__main__':
- app = QApplication()
-
- window = Widget()
- window.show()
-
- app.exec()

二、此方法的特点:
优点:无需手动释放资源,QThreadPool启动线程执行完成后会自动释放。 缺点:不能使用信号槽与外界通信。 适用场景:QRunnable适用于线程任务量比较大,需要频繁创建线程。QRunnable能有效减少内存开销。
四、使用QRunnable类中的静态方法create创建新线程
原理还是使用了QRunnable类来调用和开启新线程,但因为QRunnable类提供了一个静态方法->create方法, 所以可以实现在不写新类的情况下,直接开启和运行新线程。
代码示例:
- # !/usr/bin python3
- # -*- encoding=utf-8 -*-
- # Description :
- # Author :
- # @Email :
- # @File : 04_runable.py
- # @Time : 2023-06-27 16:24:47
- # @Project : qt
-
- from PySide6.QtCore import QThread, QRunnable, QThreadPool
- from PySide6.QtWidgets import QWidget, QApplication, QPushButton
-
-
- class Widget(QWidget):
-
- def __init__(self):
- QWidget.__init__(self)
- self._widget()
-
- def _widget(self):
- _btn = QPushButton('确定')
- _btn.setParent(self)
- _btn.clicked.connect(self._run)
-
- def _run(self):
- _run_able = QRunnable.create(self._runnable)
- _run_able.setAutoDelete(True) # Auto-deletion is enabled by default
- _pool = QThreadPool.globalInstance()
- _pool.start(_run_able)
- # _pool.waitForDone() # 会阻塞UI
-
-
- def _runnable(self) -> None:
- for i in range(0, 10):
- print('i = {}'.format(i))
- QThread.usleep(500000)
-
-
-
- if __name__ == '__main__':
- app = QApplication()
-
- window = Widget()
- window.show()
-
- app.exec()

五、QtConcurrent,并不能实现。
在Qt中,还有一种可以开启新线程的方法,QtConcurrent。
但遗憾的是,不管是在PyQt中,还是在PySide中,都没有方法可以调用此方式。
因为这个实现方法调用的是一个namespace, 而不是一个类,因此无法封装。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。