当前位置:   article > 正文

PyQt中的多线程使用方法(以PySide6为例)_pyside6 控制台输出 多线程

pyside6 控制台输出 多线程

在Qt中,开启多线程的方法有多种,总体分成QThread、QObject、QRunnable、QtConcurrent三大类方法。

而放到PyQt和PySide具体的使用中,使用方法可以说十分类似。

一、继承QThread类及run方法。

此方法可以说是Qt线程的基础,主要方法是重写一个类, 继承QThread类,然后重写run方法。

例如:

  1. # !/usr/bin python3
  2. # -*- encoding=utf-8 -*-
  3. # Description :
  4. # Author :
  5. # @Email :
  6. # @File : 01_thread.py
  7. # @Time : 2023-06-27 15:37:08
  8. # @Project : qt
  9. from PySide6.QtCore import QThread, Signal
  10. from PySide6.QtWidgets import QWidget, QApplication, QPushButton
  11. class Work(QThread):
  12. result = Signal()
  13. def __init__(self):
  14. QThread.__init__(self)
  15. def run(self) -> None:
  16. for i in range(0, 10):
  17. print('i = {}'.format(i))
  18. self.usleep(1000000)
  19. self.result.emit()
  20. class Widget(QWidget):
  21. def __init__(self):
  22. QWidget.__init__(self)
  23. self._work = Work()
  24. self._work.result.connect(self._result)
  25. self._widget()
  26. def _widget(self):
  27. _btn = QPushButton('确定')
  28. _btn.setParent(self)
  29. _btn.clicked.connect(self._run)
  30. def _run(self):
  31. self._work.start()
  32. def _result(self):
  33. print('线程结束')
  34. if __name__ == '__main__':
  35. app = QApplication()
  36. window = Widget()
  37. window.show()
  38. app.exec()

此方法的特点:

1、优点:可以通过信号槽与外界进行通信。
2、缺点:
1)每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。
2)要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。
3、适用场景:QThread适用于那些常驻内存的任务。

二、使用QObject类的moveToThread方法,调用QThread执行线程

创建一个继承QObject的类(如Work类),然后new一个Qthread,并把创建的Work类moveToThread到创建好的子线程中,然后start方法启动子线程,这样就实现了一个子线程。主线程通过发送信号,调用Work中的方法,从而实现在子线程中的计算。

  1. # !/usr/bin python3
  2. # -*- encoding=utf-8 -*-
  3. # Description :
  4. # Author :
  5. # @Email :
  6. # @File : 02_object.py
  7. # @Time : 2023-06-27 16:00:40
  8. # @Project : qt
  9. from PySide6.QtCore import QThread, Signal, QObject
  10. from PySide6.QtWidgets import QWidget, QApplication, QPushButton
  11. class Work(QObject):
  12. result = Signal()
  13. is_run = False
  14. def __init__(self):
  15. QObject.__init__(self)
  16. def run(self) -> None:
  17. for i in range(0, 10):
  18. print('i = {}'.format(i))
  19. QThread.usleep(1000000)
  20. self.result.emit()
  21. def start(self):
  22. self.is_run = True
  23. def stop(self):
  24. self.is_run = False
  25. class Widget(QWidget):
  26. def __init__(self):
  27. QWidget.__init__(self)
  28. self._widget()
  29. def _widget(self):
  30. _btn = QPushButton('确定')
  31. _btn.setParent(self)
  32. _btn.clicked.connect(self._run)
  33. def _run(self):
  34. _work = Work()
  35. _thread = QThread()
  36. _work.result.connect(self._result)
  37. _work.moveToThread(_thread)
  38. def _result(self):
  39. print('线程结束')
  40. if __name__ == '__main__':
  41. app = QApplication()
  42. window = Widget()
  43. window.show()
  44. 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启动线程

代码示例:

  1. # !/usr/bin python3
  2. # -*- encoding=utf-8 -*-
  3. # Description :
  4. # Author :
  5. # @Email :
  6. # @File : 03_runable.py
  7. # @Time : 2023-06-27 16:07:54
  8. # @Project : qt
  9. from PySide6.QtCore import QThread, Signal, QObject, QRunnable, QThreadPool
  10. from PySide6.QtWidgets import QWidget, QApplication, QPushButton
  11. class Runnable(QRunnable):
  12. def __init__(self):
  13. QRunnable.__init__(self)
  14. def run(self):
  15. for i in range(0, 10):
  16. print('i = {}'.format(i))
  17. QThread.usleep(1000000)
  18. class Widget(QWidget):
  19. def __init__(self):
  20. QWidget.__init__(self)
  21. self._widget()
  22. def _widget(self):
  23. _btn = QPushButton('确定')
  24. _btn.setParent(self)
  25. _btn.clicked.connect(self._run)
  26. def _run(self):
  27. _run_able = Runnable()
  28. _run_able.setAutoDelete(True) # Auto-deletion is enabled by default
  29. _pool = QThreadPool.globalInstance()
  30. _pool.start(_run_able)
  31. # _pool.waitForDone() # 会阻塞UI
  32. # 可同时启动多个线程
  33. # QRunnable 不是QObject的子类 不能使用Signal, 不能使用信号槽与外界通信。
  34. if __name__ == '__main__':
  35. app = QApplication()
  36. window = Widget()
  37. window.show()
  38. app.exec()

二、此方法的特点:

优点:无需手动释放资源,QThreadPool启动线程执行完成后会自动释放。
缺点:不能使用信号槽与外界通信。
适用场景:QRunnable适用于线程任务量比较大,需要频繁创建线程。QRunnable能有效减少内存开销。

四、使用QRunnable类中的静态方法create创建新线程

原理还是使用了QRunnable类来调用和开启新线程,但因为QRunnable类提供了一个静态方法->create方法, 所以可以实现在不写新类的情况下,直接开启和运行新线程。

代码示例:

  1. # !/usr/bin python3
  2. # -*- encoding=utf-8 -*-
  3. # Description :
  4. # Author :
  5. # @Email :
  6. # @File : 04_runable.py
  7. # @Time : 2023-06-27 16:24:47
  8. # @Project : qt
  9. from PySide6.QtCore import QThread, QRunnable, QThreadPool
  10. from PySide6.QtWidgets import QWidget, QApplication, QPushButton
  11. class Widget(QWidget):
  12. def __init__(self):
  13. QWidget.__init__(self)
  14. self._widget()
  15. def _widget(self):
  16. _btn = QPushButton('确定')
  17. _btn.setParent(self)
  18. _btn.clicked.connect(self._run)
  19. def _run(self):
  20. _run_able = QRunnable.create(self._runnable)
  21. _run_able.setAutoDelete(True) # Auto-deletion is enabled by default
  22. _pool = QThreadPool.globalInstance()
  23. _pool.start(_run_able)
  24. # _pool.waitForDone() # 会阻塞UI
  25. def _runnable(self) -> None:
  26. for i in range(0, 10):
  27. print('i = {}'.format(i))
  28. QThread.usleep(500000)
  29. if __name__ == '__main__':
  30. app = QApplication()
  31. window = Widget()
  32. window.show()
  33. app.exec()

五、QtConcurrent,并不能实现。

在Qt中,还有一种可以开启新线程的方法,QtConcurrent。

但遗憾的是,不管是在PyQt中,还是在PySide中,都没有方法可以调用此方式。

因为这个实现方法调用的是一个namespace, 而不是一个类,因此无法封装。

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/854096
推荐阅读
  

闽ICP备14008679号