鱼C论坛

 找回密码
 立即注册
查看: 2252|回复: 21

[已解决]pyqt5 多线程问题求助

[复制链接]
发表于 2021-2-22 13:02:39 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
两个py文件
一个ui的ui_Widget.py
  1. # -*- coding: utf-8 -*-

  2. # Form implementation generated from reading ui file 'Widget.ui'
  3. #
  4. # Created by: PyQt5 UI code generator 5.15.1
  5. #
  6. # WARNING: Any manual changes made to this file will be lost when pyuic5 is
  7. # run again.  Do not edit this file unless you know what you are doing.


  8. from PyQt5 import QtCore, QtGui, QtWidgets


  9. class Ui_Widget(object):
  10.     def setupUi(self, Widget):
  11.         Widget.setObjectName("Widget")
  12.         Widget.resize(710, 593)
  13.         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
  14.         sizePolicy.setHorizontalStretch(0)
  15.         sizePolicy.setVerticalStretch(0)
  16.         sizePolicy.setHeightForWidth(Widget.sizePolicy().hasHeightForWidth())
  17.         Widget.setSizePolicy(sizePolicy)
  18.         font = QtGui.QFont()
  19.         font.setPointSize(16)
  20.         Widget.setFont(font)
  21.         self._2 = QtWidgets.QVBoxLayout(Widget)
  22.         self._2.setContentsMargins(5, 5, 5, 5)
  23.         self._2.setSpacing(6)
  24.         self._2.setObjectName("_2")
  25.         self.label = QtWidgets.QLabel(Widget)
  26.         self.label.setEnabled(True)
  27.         self.label.setText("")
  28.         self.label.setPixmap(QtGui.QPixmap("22.jpg"))
  29.         self.label.setObjectName("label")
  30.         self._2.addWidget(self.label)
  31.         self.groupBox_2 = QtWidgets.QGroupBox(Widget)
  32.         self.groupBox_2.setObjectName("groupBox_2")
  33.         self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox_2)
  34.         self.verticalLayout.setContentsMargins(9, 9, 9, 9)
  35.         self.verticalLayout.setSpacing(6)
  36.         self.verticalLayout.setObjectName("verticalLayout")
  37.         self.lineEdit = QtWidgets.QLineEdit(self.groupBox_2)
  38.         self.lineEdit.setClearButtonEnabled(True)
  39.         self.lineEdit.setObjectName("lineEdit")
  40.         self.verticalLayout.addWidget(self.lineEdit)
  41.         self._2.addWidget(self.groupBox_2)
  42.         self.horizontalLayout = QtWidgets.QHBoxLayout()
  43.         self.horizontalLayout.setSpacing(4)
  44.         self.horizontalLayout.setObjectName("horizontalLayout")
  45.         self.groupBox = QtWidgets.QGroupBox(Widget)
  46.         self.groupBox.setObjectName("groupBox")
  47.         self.gridLayout = QtWidgets.QGridLayout(self.groupBox)
  48.         self.gridLayout.setContentsMargins(11, 11, 11, 11)
  49.         self.gridLayout.setSpacing(6)
  50.         self.gridLayout.setObjectName("gridLayout")
  51.         self.comboBox = QtWidgets.QComboBox(self.groupBox)
  52.         self.comboBox.setEditable(False)
  53.         self.comboBox.setObjectName("comboBox")
  54.         self.gridLayout.addWidget(self.comboBox, 1, 0, 1, 3)
  55.         self.btnIniItems = QtWidgets.QPushButton(self.groupBox)
  56.         self.btnIniItems.setObjectName("btnIniItems")
  57.         self.gridLayout.addWidget(self.btnIniItems, 0, 0, 1, 1)
  58.         self.btnClearItems = QtWidgets.QPushButton(self.groupBox)
  59.         self.btnClearItems.setObjectName("btnClearItems")
  60.         self.gridLayout.addWidget(self.btnClearItems, 0, 1, 1, 1)
  61.         self.btnOkItems = QtWidgets.QPushButton(self.groupBox)
  62.         self.btnOkItems.setStyleSheet("background-color: rgb(255, 0, 0);\n"
  63. "font: 16pt "MS Shell Dlg 2";")
  64.         self.btnOkItems.setObjectName("btnOkItems")
  65.         self.gridLayout.addWidget(self.btnOkItems, 0, 2, 1, 1)
  66.         self.horizontalLayout.addWidget(self.groupBox)
  67.         self._2.addLayout(self.horizontalLayout)
  68.         self.plainTextEdit = QtWidgets.QPlainTextEdit(Widget)
  69.         self.plainTextEdit.setObjectName("plainTextEdit")
  70.         self._2.addWidget(self.plainTextEdit)

  71.         self.retranslateUi(Widget)
  72.         self.comboBox.setCurrentIndex(-1)
  73.         QtCore.QMetaObject.connectSlotsByName(Widget)

  74.     def retranslateUi(self, Widget):
  75.         _translate = QtCore.QCoreApplication.translate
  76.         Widget.setWindowTitle(_translate("Widget", "Demo3_6 ComboBox"))
  77.         self.groupBox_2.setTitle(_translate("Widget", "选择的工序如下"))
  78.         self.groupBox.setTitle(_translate("Widget", "选择导出数据的工序"))
  79.         self.btnIniItems.setText(_translate("Widget", "初始化列表"))
  80.         self.btnClearItems.setText(_translate("Widget", "清除列表"))
  81.         self.btnOkItems.setText(_translate("Widget", "确定"))
复制代码


第二个py,文件myWidget.py
  1. import sys
  2. from PyQt5.QtWidgets import  QApplication, QWidget
  3. from PyQt5.QtCore import  pyqtSlot
  4. from ui_Widget import Ui_Widget #ui界面
  5. import itp_drawing as drawing #函数1
  6. import itp_bunching as bunching #函数2
  7. import itp_conductor as conductor #函数3
  8. import itp_extruder as extruder #函数4
  9. import itp_rubber as rubber #函数5
  10. import itp_ccv as ccv #函数6

  11. class QmyWidget(QWidget):
  12.    def __init__(self, parent=None):
  13.       super().__init__(parent)   #调用父类构造函数,创建窗体
  14.       self.ui=Ui_Widget()        #创建UI对象
  15.       self.ui.setupUi(self)      #构造UI界面

  16. ##  ==========由connectSlotsByName() 自动连接的槽函数====================        
  17.    def on_btnIniItems_clicked(self):   ##“初始化列表”按钮
  18.       self.ui.comboBox.clear()    #清除列表
  19.       provinces=["拉丝","束丝","导体","挤出","橡胶","ccv"]    #列表数据
  20.       for i in range(len(provinces)):
  21.          self.ui.comboBox.addItem(provinces[i])

  22.    def on_btnClearItems_clicked(self):    ##“清除列表”按钮
  23.       self.ui.comboBox.clear()

  24.    def on_btnOkItems_clicked(self):  ##“确定”按钮
  25.       if self.ui.lineEdit.text().strip()=='拉丝':
  26.          self.ui.plainTextEdit.clear()
  27.          drawing.concat_file()
  28.          self.ui.plainTextEdit.insertPlainText('拉丝数据导出完毕\n==========\n')
  29.       elif self.ui.lineEdit.text().strip()=='束丝':
  30.          self.ui.plainTextEdit.clear()
  31.          bunching.read_bunching()
  32.          self.ui.plainTextEdit.insertPlainText('束丝数据导出完毕\n==========\n')
  33.       elif self.ui.lineEdit.text().strip() == '导体':
  34.          self.ui.plainTextEdit.clear()
  35.          conductor.concat_file()
  36.          self.ui.plainTextEdit.insertPlainText('导体数据导出完毕\n==========\n')
  37.       elif self.ui.lineEdit.text().strip()=='挤出':
  38.          self.ui.plainTextEdit.clear()
  39.          extruder.concat_file()
  40.          self.ui.plainTextEdit.insertPlainText('挤出数据导出完毕\n==========\n')
  41.       elif self.ui.lineEdit.text().strip()=='橡胶':
  42.          self.ui.plainTextEdit.clear()
  43.          rubber.concat_file()
  44.          self.ui.plainTextEdit.insertPlainText('橡胶数据导出完毕\n==========\n')
  45.       elif self.ui.lineEdit.text().strip()=='ccv':
  46.          self.ui.plainTextEdit.clear()
  47.          ccv.concat_file()
  48.          self.ui.plainTextEdit.insertPlainText('CCV数据导出完毕\n==========\n')
  49.       else:
  50.          self.ui.plainTextEdit.clear()
  51.          self.ui.plainTextEdit.insertPlainText('请选择正确的工序!!!\n==========\n')

  52.    @pyqtSlot(str)    ##“简单的ComboBox”的当前项变化
  53.    def on_comboBox_currentIndexChanged(self,curText):
  54.       self.ui.lineEdit.setText(curText)

  55. ##  ===========窗体测试程序 ================================        
  56. if  __name__ == "__main__":         
  57.    app = QApplication(sys.argv)   
  58.    form=QmyWidget()      
  59.    form.show()
  60.    sys.exit(app.exec_())
复制代码


现在的问题是,6个函数,选中某一个后,由于函数读取数据文本比较耗时,导致界面卡。
如何改成多线程模式?
比如把6个函数改成6个checkbutton,选中几个就新开几个线程,且主界面不死。
最佳答案
2021-2-25 18:14:57
rsj0315 发表于 2021-2-25 13:25
我先写了一个,是combox,选中后,点击按钮,然后开启线程,进行数据清洗。
你说的把dataframe发射出来 ...
不知道是不是必须的用信号发射出来才能实现。
还是我直接在线程中就把数据显示出来。

用信号发射出来不是必须的,但必须是在主线程中更新界面,也就是说你想在子线程中把数据显示出来是行不通的。
还是前面说过的两个方法:
1,类初始化时设置一个属性比如self.whatever = None。定义例如名为update_tableview的方法,方法内根据self.whatever的值设置tabwiew。定义一个清洗数据的方法getdata,方法内清洗数据并将最终结果赋值给self.whatever。实例化一个线程threadx(getdata),先将其finished信号连接至update_tableview,然后可以threadx.start(),当线程结束后,self.whatever的值已经变为清洗后的数据了,然后threadx的finished信号会触发update_tableview,update_tableview会根据self.whatever的值显示tableview。
2.在你的Thread1类中定义一个list类型的信号signal1=pyqtSignal(list),在处理完结果后,signal1.emit([dataFrame])将dataFrame包在列表内发射出去。在你的myWidget内定义一个接收一个参数a(即将被发射的[dataFrame])的显示tabwiew的方法update_tabview,update_tableview内根据a的值设置tableview。然后将Thread1的实例的signal1信号连接至update_tableview即可。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2021-2-22 14:55:36 From FishC Mobile | 显示全部楼层
用QThread,它的线程实例开始时发射started信号,线程结束后发射finished信号,可以用started和finished信号触发槽函数来更新界面。
为什么不用threading?因为子线程不能直接更新QT界面,只能用QT的信号机制来更新界面。
唯一要注意的只有QThread不能赋值给函数局部变量,应该赋值给实例属性或其他生命周期足够长的变量,否则函数一结束线程也将跟着被结束。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-23 09:16:03 | 显示全部楼层
hrp 发表于 2021-2-22 14:55
用QThread,它的线程实例开始时发射started信号,线程结束后发射finished信号,可以用started和finished信 ...

你的意思是在确认按钮这里,threading开6个线程,然后判断,选中了哪个?然后start选中的线程。是这样吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-23 10:23:43 From FishC Mobile | 显示全部楼层
rsj0315 发表于 2021-2-23 09:16
你的意思是在确认按钮这里,threading开6个线程,然后判断,选中了哪个?然后start选中的线程。是这样吗 ...


不是,是定义一个方法,将6个按钮的点击信号都连接到这个方法(甚至你可以分别定义6个不同的方法,将6个按钮的点击信号分别连接到这6个方法)。
方法的内部,先判断是哪个按钮发射的点击信号(用sender获取信号的发射者),再根据不同按钮创建不同任务的线程,线程中处理数据,把结果赋值给一个实例属性a,线程的started和finished信号分别连接线程开始(如果有需要)和结束要执行的方法f,f方法根据a储存的结果更新界面(比如线程结束信号可以连接在界面显示表格的方法),这就实现了线程结束后自动更新界面。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-23 10:36:45 | 显示全部楼层
hrp 发表于 2021-2-23 10:23
不是,是定义一个方法,将6个按钮的点击信号都连接到这个方法(甚至你可以分别定义6个不同的方法,将6个 ...

你的思路很清晰啊,很好!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-23 10:57:29 | 显示全部楼层
hrp 发表于 2021-2-23 10:23
不是,是定义一个方法,将6个按钮的点击信号都连接到这个方法(甚至你可以分别定义6个不同的方法,将6个 ...

感谢,我去研究下这个信号和槽的部分。有好的帖子或者demo欢迎分享
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-23 11:48:17 From FishC Mobile | 显示全部楼层
本帖最后由 hrp 于 2021-2-23 11:55 编辑
rsj0315 发表于 2021-2-23 10:57
感谢,我去研究下这个信号和槽的部分。有好的帖子或者demo欢迎分享


我自己写的 AwesomePyKit 也用到多线程,可以看看,代码写的很烂还没时间重构。
QThread的使用:在RunPyKit.py中搜索NewTask(继承自QThread)就可以了。
NewTask的定义:在library/libm.py里面。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-23 11:54:46 From FishC Mobile | 显示全部楼层
Cool_Breeze 发表于 2021-2-23 10:36
你的思路很清晰啊,很好!

之前写pykit的时候苦于在子线程中无法更新主界面,然后百度说需要通过信号更新主界面,最后摸索出来的办法
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-23 16:10:03 | 显示全部楼层
看到有人展示一个这样的,不传参数的
  1. from PyQt5.Qt import (QApplication, QWidget, QPushButton,
  2.                       QThread,QMutex,pyqtSignal)
  3. import sys
  4. import time

  5. qmut_1 = QMutex() # 创建线程锁
  6. qmut_2 = QMutex()
  7. # 继承QThread
  8. class Thread_1(QThread):  # 线程1
  9.     def __init__(self):
  10.         super().__init__()

  11.     def run(self):
  12.         qmut_1.lock() # 加锁
  13.         values = [1, 2, 3, 4, 5]
  14.         for i in values:
  15.             print(i)
  16.             time.sleep(0.5)  # 休眠
  17.         qmut_1.unlock() # 解锁


  18. class Thread_2(QThread):  # 线程2
  19.     _signal =pyqtSignal()
  20.     def __init__(self):
  21.         super().__init__()

  22.     def run(self):
  23.         # qmut_2.lock()  # 加锁
  24.         values = ["a", "b", "c", "d", "e"]
  25.         for i in values:
  26.             print(i)
  27.             time.sleep(0.5)
  28.         # qmut_2.unlock()  # 解锁
  29.         self._signal.emit()


  30. class MyWin(QWidget):
  31.     def __init__(self):
  32.         super().__init__()
  33.         # 按钮初始化
  34.         self.btn_1 = QPushButton('按钮1', self)
  35.         self.btn_1.move(120, 80)
  36.         self.btn_1.clicked.connect(self.click_1)  # 绑定槽函数

  37.         self.btn_2 = QPushButton('按钮2', self)
  38.         self.btn_2.move(120, 120)
  39.         self.btn_2.clicked.connect(self.click_2)  # 绑定槽函数

  40.     def click_1(self):
  41.         self.thread_1 = Thread_1()  # 创建线程
  42.         self.thread_1.start()  # 开始线程

  43.     def click_2(self):
  44.         self.btn_2.setEnabled(False)
  45.         self.thread_2 = Thread_2()
  46.         self.thread_2._signal.connect(self.set_btn)
  47.         self.thread_2.start()

  48.     def set_btn(self):
  49.         self.btn_2.setEnabled(True)


  50. if __name__ == "__main__":
  51.     app = QApplication(sys.argv)
  52.     myshow = MyWin()
  53.     myshow.show()
  54.     sys.exit(app.exec_())
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-23 16:12:16 | 显示全部楼层
还有一种这样的
  1. import sys
  2. from PyQt5 import QtCore
  3. from PyQt5.QtWidgets import *
  4. from PyQt5.QtGui import *
  5. from PyQt5.QtCore import *

  6. class Demo(QWidget):
  7.     def __init__(self):
  8.         super().__init__()
  9.         self.setGeometry(100, 50, 500, 400)
  10.         self.setWindowTitle('QThread')

  11.         self.thread = Worker()
  12.         self.list = QListWidget()
  13.         self.btn = QPushButton('开始')
  14.         layout = QGridLayout(self)
  15.         layout.addWidget(self.list,0,0,1,2)
  16.         layout.addWidget(self.btn,1,1)
  17.         self.btn.clicked.connect(self.slotStart)
  18.         self.thread.sinOut.connect(self.slotAdd)

  19.     def slotAdd(self,inf):
  20.         self.list.addItem(inf)
  21.         #实时刷新页面
  22.         QApplication.processEvents()

  23.     def slotStart(self):
  24.         self.btn.setEnabled(False)
  25.         self.thread.start()

  26. class Worker(QThread):
  27.     #自定义信号
  28.     sinOut = pyqtSignal(str)

  29.     def __init__(self):
  30.         super().__init__()
  31.         self.working = True
  32.         self.num = 0

  33.     def __del__(self):
  34.         self.working = False
  35.         self.wait()

  36.     def run(self):
  37.         while self.working == True:
  38.             file_str = 'File index{0}'.format(self.num)
  39.             self.num += 1
  40.             #发射信号
  41.             self.sinOut.emit(file_str)
  42.             #休眠2秒
  43.             self.sleep(2)

  44. if __name__ == "__main__":
  45.     app = QApplication(sys.argv)
  46.     form = Demo()
  47.     form.show()
  48.     sys.exit(app.exec_())
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-23 16:43:52 From FishC Mobile | 显示全部楼层
qthread自定义信号更方便
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-23 17:21:48 | 显示全部楼层
hrp 发表于 2021-2-23 16:43
qthread自定义信号更方便

信号我像传递的是用pandas库清洗出来的dataframe,我看了emit的几种形式,好像传输不了这种
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-23 17:41:43 From FishC Mobile | 显示全部楼层
rsj0315 发表于 2021-2-23 17:21
信号我像传递的是用pandas库清洗出来的dataframe,我看了emit的几种形式,好像传输不了这种

可以发射list等类型的啊,把数据装进列表再发射不就好了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-25 13:25:49 | 显示全部楼层
hrp 发表于 2021-2-23 17:41
可以发射list等类型的啊,把数据装进列表再发射不就好了

我先写了一个,是combox,选中后,点击按钮,然后开启线程,进行数据清洗。
你说的把dataframe发射出来,应该怎么写呢?list.append?
我没太懂。

目的:
我是想把这个dataframe用model/view模式在tableview上显示出来。
不知道是不是必须的用信号发射出来才能实现。
还是我直接在线程中就把数据显示出来。

你看看我写的,还没发射dataframe的
  1. import sys
  2. from PyQt5.QtWidgets import  QApplication, QWidget
  3. from PyQt5.QtCore import  pyqtSlot, QThread,pyqtSignal
  4. from ui_Widget import Ui_Widget
  5. import itp_drawing as drawing

  6. class Thread1(QThread):
  7.    signal1 = pyqtSignal()
  8.    def __init__(self):
  9.       super(Thread1, self).__init__()

  10.    def run(self):
  11.       df = drawing.concat_file()



  12. class QmyWidget(QWidget):
  13.    def __init__(self, parent=None):
  14.       super().__init__(parent)   #调用父类构造函数,创建窗体
  15.       self.ui=Ui_Widget()        #创建UI对象
  16.       self.ui.setupUi(self)      #构造UI界面


  17. ##  ==========由connectSlotsByName() 自动连接的槽函数====================        
  18.    def on_btnIniItems_clicked(self):   ##“初始化列表”按钮
  19.       self.ui.comboBox.clear()    #清除列表
  20.       provinces=["拉丝","束丝","导体","挤出","橡胶","ccv"]    #列表数据
  21.       for i in range(len(provinces)):
  22.          self.ui.comboBox.addItem(provinces[i])

  23.    def on_btnClearItems_clicked(self):    ##“清除列表”按钮
  24.       self.ui.comboBox.clear()

  25.    @pyqtSlot()
  26.    def on_btnOkItems_clicked(self):  ##“确定”按钮
  27.       self.thread1 = Thread1()
  28.       self.thread1.start()
  29.       
  30.    @pyqtSlot(str)    ##“简单的ComboBox”的当前项变化
  31.    def on_comboBox_currentIndexChanged(self,curText):
  32.       self.ui.lineEdit.setText(curText)

  33. ##  ===========窗体测试程序 ================================        
  34. if  __name__ == "__main__":         
  35.    app = QApplication(sys.argv)
  36.    form=QmyWidget()
  37.    form.show()
  38.    sys.exit(app.exec_())
复制代码


ui.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-25 18:14:57 From FishC Mobile | 显示全部楼层    本楼为最佳答案   
rsj0315 发表于 2021-2-25 13:25
我先写了一个,是combox,选中后,点击按钮,然后开启线程,进行数据清洗。
你说的把dataframe发射出来 ...
不知道是不是必须的用信号发射出来才能实现。
还是我直接在线程中就把数据显示出来。

用信号发射出来不是必须的,但必须是在主线程中更新界面,也就是说你想在子线程中把数据显示出来是行不通的。
还是前面说过的两个方法:
1,类初始化时设置一个属性比如self.whatever = None。定义例如名为update_tableview的方法,方法内根据self.whatever的值设置tabwiew。定义一个清洗数据的方法getdata,方法内清洗数据并将最终结果赋值给self.whatever。实例化一个线程threadx(getdata),先将其finished信号连接至update_tableview,然后可以threadx.start(),当线程结束后,self.whatever的值已经变为清洗后的数据了,然后threadx的finished信号会触发update_tableview,update_tableview会根据self.whatever的值显示tableview。
2.在你的Thread1类中定义一个list类型的信号signal1=pyqtSignal(list),在处理完结果后,signal1.emit([dataFrame])将dataFrame包在列表内发射出去。在你的myWidget内定义一个接收一个参数a(即将被发射的[dataFrame])的显示tabwiew的方法update_tabview,update_tableview内根据a的值设置tableview。然后将Thread1的实例的signal1信号连接至update_tableview即可。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

 楼主| 发表于 2021-2-26 09:18:04 | 显示全部楼层
hrp 发表于 2021-2-25 18:14
用信号发射出来不是必须的,但必须是在主线程中更新界面,也就是说你想在子线程中把数据显示出来是行不 ...

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-3-7 17:39:36 | 显示全部楼层
关注,还看不懂,等看懂了再来
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-3-8 11:42:25 | 显示全部楼层
按照这个,子线程的额数据清洗后,存储在pkl,然后主线程显示表格的时候再读取pkl
ui给子线程传参.jpg
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-3-31 20:19:19 | 显示全部楼层
转一下大佬的代码,有个 @hrp
NewTask定义
  1. from PyQt5.QtCore import QThread

  2. class NewTask(QThread):
  3.     def __init__(self, target, args=tuple()):
  4.         super().__init__()
  5.         self._args = args
  6.         self._target = target

  7.     def run(self):
  8.         self._target(*self._args)

  9.     def __repr__(self):
  10.         return f"{self._target} with args:{self._args}"

  11.     __str__ = __repr__

  12.     def at_start(self, *callable_objs):
  13.         for cab in callable_objs:
  14.             if callable(cab):
  15.                 self.started.connect(cab)

  16.     def at_finish(self, *callable_objs):
  17.         for cab in callable_objs:
  18.             if callable(cab):
  19.                 self.finished.connect(cab)
复制代码

应用实例
  1.         def do_install():
  2.             for name, code in loop_install(
  3.                 cur_env,
  4.                 package_to_be_installed,
  5.                 pre=install_pre,
  6.                 user=user,
  7.                 index_url=index_url,
  8.             ):
  9.                 item = self.cur_pkgs_info.setdefault(name, ["", "", ""])
  10.                 if not item[0]:
  11.                     item[0] = "- N/A -"
  12.                 item[2] = "安装成功" if code else "安装失败"

  13.         thread_install_pkgs = NewTask(do_install)
  14.         thread_install_pkgs.at_start(
  15.             self.lock_widgets,
  16.             lambda: self.show_loading("正在安装,请稍候..."),
  17.         )
  18.         thread_install_pkgs.at_finish(
  19.             self.table_widget_pkgs_info_update,
  20.             self.hide_loading,
  21.             self.release_widgets,
  22.         )
  23.         thread_install_pkgs.start()
  24.         self.thread_repo.put(thread_install_pkgs, 0)
复制代码

进程池定义

  1. from PyQt5.QtCore import QMutex
  2. class ThreadRepo:
  3.     def __init__(self, interval):
  4.         """interval: 清理已结束线程的时间间隔,单位毫秒。"""
  5.         self._thread_repo = []
  6.         self._timer_clths = QTimer()
  7.         self._mutex = QMutex()
  8.         self._timer_clths.timeout.connect(self.clean)
  9.         self._timer_clths.start(interval)
  10.         self._flag_cleaning = False

  11.     def put(self, threadhandle, level=0):
  12.         """将(线程句柄、重要等级)元组加入线程仓库。"""
  13.         self._mutex.lock()
  14.         self._thread_repo.append((threadhandle, level))
  15.         self._mutex.unlock()

  16.     def clean(self):
  17.         """清除已结束的线程。"""
  18.         if self._flag_cleaning:
  19.             return
  20.         self._mutex.lock()
  21.         index = 0
  22.         self._flag_cleaning = True
  23.         while index < len(self._thread_repo):
  24.             if self._thread_repo[index][0].isRunning():
  25.                 index += 1
  26.                 continue
  27.             del self._thread_repo[index]
  28.         self._flag_cleaning = False
  29.         self._mutex.unlock()

  30.     def stop_all(self):
  31.         """
  32.         按线程重要等级退出线程。
  33.         0级:重要,安全退出;
  34.         1级:不重要,立即退出;
  35.         其他:未知等级,安全退出。
  36.         """
  37.         for thread, level in self._thread_repo:
  38.             if level == 0:
  39.                 thread.quit()
  40.             elif level == 1:
  41.                 thread.terminate()
  42.             else:
  43.                 thread.quit()

  44.     def kill_all(self):
  45.         """立即终止所有线程。"""
  46.         for thread, _ in self._thread_repo:
  47.             thread.terminate()

  48.     def is_empty(self):
  49.         """返回线程仓库是否为空。"""
  50.         return not self._thread_repo
复制代码


均转自AwesomePyKit项目
开源精神!
Salute!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-3-31 22:00:59 | 显示全部楼层
我有一个大胆的想法 :之前的Threading线程结果放到queue里面 然后用Qthread 来读取这个queue里面的东西显示,也就是说 该 Qthread只用来做显示结果用 不参与数据处理过程 (我想到了Iphone系统 不论程序怎么卡 它系统都不会卡)主要是为了实现显示进程和算法进程的互不干扰@hrp 你觉得这个想法怎么样
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-9 06:01

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表