01 使用 Q designer 设置信号和槽
- 系统本身的槽(函数)
- 指定个人定义的槽(函数)绑定

02 定义线程业务逻辑+发送信号
代码这块要实现一个线程类, 用来处理每个按钮点击触发的业务逻辑
class Runthread_contend(QtCore.QThread):# 定义信号 到时候需要在这个线程中发送信号到主界面的绑定的槽(函数)start_print_contend = pyqtSignal(dict)start_print_int = pyqtSignal(int) # 定义信号类型start_print_str = pyqtSignal(str,int) # 定义信号, 发送了2个参数数据# 当主界面 初始化Runthread_contend(c1,c2) 携带了2个参数 在__init__ 接收了def __init__(self, url_list, Cookie):super(Runthread_contend, self).__init__()self._isPause = False # 用来结束 Qthreadself.mutex = QMutex() # 创建线程锁 批量网址刷self.count = 0self.Cookie = Cookiedef run(self):# 这里就是你书写业务的逻辑, 如果太多可调用函数的方式处理, 这里一般是循环for i in range(20):self.start_print_str.emit("你好", i) # 这就是发送信号 , 在上面定义了信号的类型,这里要严格规范self.test("你好", i) # 这就是调用函数在函数里面发送信号def test(self, val, num):self.start_print_str.emit(val, num)
03 实例化 线程函数 + 绑定槽信号
class MainWindowUI(QMainWindow, Ui_BaiduAD):def __init__(self, parent=None):super(MainWindowUI, self).__init__(parent)self.contend = Noneself.date = "......."#........初始化一些界面的信息, 比如可以在加载界面的是否读取配置等等@QtCore.pyqtSlot() # 这里就是用designer 设计触发按钮事件绑定了 Baidu_kaiShiRenWu函数def Baidu_kaiShiRenWu(self):# 1. 获取输入框的数据 , 其他也一样的道理Therad_num = self.lineEdit_xianChenNum.text()# 2. 初始化 线程函数, 并把数据带进去self.contend = Runthread_contend(Therad_num)# 3. 线程函数 中的 信号 与 主线程的(槽)函数 绑定 TS_print函数self.contend.start_print_str.connect(self.TS_print, Qt.DirectConnection)# 4. 执行线程self.contend.satrt()def TS_print(self,msg,val_num):# 方式一 直接更新GUI# 这里信号那边有数据 会发送信号到这里来 , 这里你就可以去更新界面的数据,方便显示给用户看print(msg,val_num)self.组件的名字.setText(msg) # 根据组件的不同 方式不同有的可以 append(msg)# 方式二 使用线程更新GUI""" 一般这里我会设置一个线程 处理 更新 数据到GUI(界面) args(参数1,) 注意一定要在最后面加 逗号th = threading.Thread(target=self.TS_print_UI, args=(msg,val_num,), name='thread-{:03d}'.format(int(random.random())), )th.start()time.sleep(1)th.join()"""def TS_print_UI(self, msg,val_num):print(msg,val_num)self.toolButton_KaiShiRenWu.setEnabled(False) # 更新了按钮的状态
绑定外部槽文件+外部信号文件
02 - 03 都是属于在一个文件内调用 内部槽函数 和内部Qt类信号!!!
下面我们来实现如何调用外部槽函数 和外部槽文件, 那为什么需要这样做呢? 原因很简单, 因为在一个文件中 书写槽函数 和Qt类信号, 随着业务的逻辑越来越多, 你寻找某个功能的时候需要从一个文件几千行代码里寻找这个功能,, 那时非常头疼的事情, 那么现在来看看我的结构吧!
项目结构


01 槽函数文件结构
这个类就是我们定义的槽文件中的类 初始化的时候 需要填写一个 参数 那就是parent , 这个parent是什么呢,这个parent是继承了父类也就是主线程界面相关的数据,
这个时候,需要在外部曹函数更新主线程UI的数据就需要用到 parnet
平时我们在内部曹函数中只需要 self.lineText组件即可, 可是在外部槽文件中的槽函数 需要在 self后再书写parent,才能更新主线的UI数据
注意 这个parent是重点!!!!!
import threading,randomimport timefrom PyQt5.QtWidgets import QTableWidgetItemclass Groove:"""槽函数类 初始化"""def __init__(self, parent):self.parent = parentself.thread_num = 1def set_ui(self, msg):print(f"接收到信号 {msg}")self.parent.lineText.setText(msg) # 更新了UI界面的输入框数据
02 Qt类信号结构
外部文件Qt类线程 跟单文件的时候Qt线程类一样, 没什么区别! 现在讲一下 pyqtSignal() 这个是用来定义信号的类型, 位置要固定 一般有5种 , 也可以一个信号携带多个参数
str字符串类型int数值类型dict字典类型(json数据)list列表类型- 空类型的信号
"PyQt_PyObject"万能 任何Python类型,这通常用于传递实际Python类型未知的对象
信号发送 emit() , 这个信号发送是做什么的呢? 作用实在这个Qt类线程 处理业务逻辑 得到数据返回到界面更新, 不能直接更新到主线程UI上, 所以需要使用槽函数来接收或更新到主线程的UI
from PyQt5.QtCore import Qt, pyqtSignalfrom PyQt5 import QtWidgets, QtCore# 线程类class Runthread_file(QtCore.QThread):start_print = pyqtSignal(str) # 这里是定义发送信号的类型"""start_print = pyqtSignal(int)start_print = pyqtSignal(dict)start_print = pyqtSignal(list)start_print = pyqtSignal()start_print = pyqtSignal("PyQt_PyObject")start_print = pyqtSignal(str, int, str, list)"""def __init__(self, filePath):super(Runthread_file, self).__init__() # 默认初始化自已,不需要懂,照着做就可以self.filePath = filePathdef run(self):try:with open(self.filePath, 'r', encoding="utf-8-sig") as f:for line in f: # 1.一行一行读取后, 自动关闭文件self.start_print.emit(line)except Exception as e:print("ccdcd ", e) # 转换文本 为UTF-8
03 主程序的目录结构
- self.G = Groove(self) # 实例化外部 槽函数文件, 并且需要携带 self 作为参数, 这里很重要, 决定能否在外部曹函数更新UI
- self.signer = None # 待实例化Qt类线程的时候需要使用到这个变量, 如果不初始化这个变量后续实例化后不会生效
如果主线程需要用到 外部曹函数的变量,或者想修改更新 外部曹函数的变量的值, 可通过 self.G.thread_num = 2 即可 , 注意的是 一定要在实例化后, 才能获取到曹函数的变量值
from PyQt5.QtCore import Qt, QSettingsfrom PyQt5.QtGui import QCursorfrom PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QAbstractItemView, QHeaderViewfrom PyQt5 import QtWidgets, QtCoreimport random, os, sysfrom resource.taobao_ui import Ui_MainWindow # 引入了 Qt设计师的UI文件from main_Thread_Signer.Signer1 import Runthread_file, Runthread_start # 引入外部Qt类线程from main_Groove_Fn.Fn1 import Groove # 引入外部曹函数class MainWindowUI(QMainWindow, Ui_MainWindow):def __init__(self, parent=None):super(MainWindowUI, self).__init__(parent)self.setupUi(self)self.G = Groove(self) # 实例化外部 槽函数文件, 并且需要携带 self 作为参数, 这里很重要, 决定能否在外部曹函数更新UIself.signer = None # 待实例化Qt类线程的时候需要使用到这个变量, 如果不初始化这个变量后续实例化后不会生效def setupUi(self,mainWindow):super().setupUi(mainWindow)self.setWindowTitle("程序标题名称")self.setWindowIcon(QtGui.QIcon(':/images/myy.ico'))@QtCore.pyqtSlot()def TaoBao_YijianBaocun(self):print("点击了按钮")self.signer = Runthread_file() # 实例化Qt类线程 可携带参数self.signer.start_print.connect(self.G.set_ui, Qt.DirectConnection) # 绑定槽函数self.signer.start() # 执行线程if __name__ == "__main__":app = QtWidgets.QApplication(sys.argv)w = MainWindowUI()w.show()sys.exit(app.exec_())
05 开发项目模版
具有 隐藏框, 透明背景, 窗口可移动 启动程序自动读取配置 功能
from PyQt5.QtCore import Qt, QSettingsfrom PyQt5.QtGui import QCursorfrom PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QAbstractItemView, QHeaderViewfrom PyQt5 import QtWidgets, QtCoreimport random, os, sys# from resource.taobao_ui import Ui_MainWindow# from main_Thread_Signer.Signer1 import Runthread_file, Runthread_start# from main_Groove_Fn.Fn1 import Grooveclass MainWindowUI(QMainWindow, Ui_MainWindow):def __init__(self, parent=None):super(MainWindowUI, self).__init__(parent)self.setupUi(self)self.setGroove()self.setQtThread()self.setOtherConfig()def mousePressEvent(self, event):if event.button() == Qt.LeftButton:self.m_flag = Trueself.m_Position = event.globalPos() - self.pos() # 获取鼠标相对窗口的位置event.accept()self.setCursor(QCursor(Qt.OpenHandCursor)) # 更改鼠标图标def mouseMoveEvent(self, QMouseEvent):if Qt.LeftButton and self.m_flag:self.move(QMouseEvent.globalPos() - self.m_Position) # 更改窗口位置QMouseEvent.accept()def mouseReleaseEvent(self, QMouseEvent):self.m_flag = Falseself.setCursor(QCursor(Qt.ArrowCursor))def setupUi(self,mainWindow):super().setupUi(mainWindow)self.setWindowTitle("程序标题名称")self.setWindowIcon(QtGui.QIcon(':/images/myy.ico')) # 这里改成决定路径的图片打包后即可正常显示self.setWindowFlags(Qt.WindowMaximizeButtonHint | Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint) # 置顶,影藏边框self.setAttribute(Qt.WA_TranslucentBackground) # 透明背景self.tableWidget_RewuLeiBiao.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置 不可选择单个单元格,只可选择一行。self.tableWidget_RewuLeiBiao.horizontalHeader().resizeSection(0, 200) # 设置表头第一列的宽度为200self.tableWidget_RewuLeiBiao.horizontalHeader().resizeSection(1, 120)self.tableWidget_RewuLeiBiao.horizontalHeader().resizeSection(2, 200)self.tableWidget_RewuLeiBiao.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) # 宽度自动调整,充满屏幕self.textBrowser_RiZhi.append("[免责声明]")def setOtherConfig(self): # 设置其它配置self.settings = QSettings('config.ini', QSettings.IniFormat)self.initRecentFileList()def saveFileList(self): # 保存配置print("进来执行保存", self.lineEdit_Xsrf_token.text())self.settings.setValue("Set/Xsrf_token", self.lineEdit_Xsrf_token.text())self.settings.setValue("Set/Bx_umidtoken", self.lineEdit_Bx_umidtoken.text())self.settings.setValue("Set/Callback", self.lineEdit_Callback.text())self.settings.setValue("Set/SessionId", self.lineEdit_SessionId.text())self.settings.setValue("Set/API", self.lineEdit_API.text())self.settings.setValue("Set/chrome", self.lineEdit_chrome.text())self.settings.setValue("Set/R5", self.lineEdit_chromedriver.text())self.settings.setValue("Set/Cookie", self.textEdit_Cookie.toPlainText())self.settings.setValue("Set/Bx_ua", self.textEdit_Bx_ua.toPlainText())def initRecentFileList(self):self.lineEdit_Xsrf_token.setText(self.settings.value("Set/Xsrf_token"))self.lineEdit_Bx_umidtoken.setText(self.settings.value("Set/Bx_umidtoken"))self.lineEdit_Callback.setText(self.settings.value("Set/Callback"))self.lineEdit_SessionId.setText(self.settings.value("Set/SessionId"))self.lineEdit_API.setText(self.settings.value("Set/API"))self.lineEdit_chrome.setText(self.settings.value("Set/chrome"))self.lineEdit_chromedriver.setText(self.settings.value("Set/R5"))self.textEdit_Cookie.setText(self.settings.value("Set/Cookie"))self.textEdit_Bx_ua.setText(self.settings.value("Set/Bx_ua"))def setGroove(self): # 实例化外部槽self.G = Groove(self)def setQtThread(self): # 实例化Qt线程信号self.singel_addFile = None # 导入文本self.specified = None # 开始任务self.signel_file = None@QtCore.pyqtSlot()def Kaishi_btn(self):self.specified.start_print_dict.connect(self.G.updateFile, Qt.DirectConnection) #绑定曹函数if __name__ == "__main__":app = QtWidgets.QApplication(sys.argv)w = MainWindowUI()w.show()sys.exit(app.exec_())
from PyQt5.QtCore import Qt, pyqtSignalfrom PyQt5 import QtWidgets, QtCoreimport time, threading, requests, random, json, refrom fake_useragent import UserAgent# 线程类 发送信号使用 .emit()class Runthread_file(QtCore.QThread):start_print = pyqtSignal(str)def __init__(self, filePath):super(Runthread_file, self).__init__()self.filePath = filePathdef run(self):try:with open(self.filePath, 'r', encoding="utf-8-sig") as f:for line in f: # 1.一行一行读取后, 自动关闭文件self.start_print.emit(line)except Exception as e:print("导入错误信息提示 ", e) # 转换文本 为UTF-8
from PyQt5.QtWidgets import QTableWidgetItemclass Groove:"""槽函数类"""def __init__(self, parent):self.parent = parentdef addFile(self):self.parent.textBrowser_RiZhi.append("[免责声明]")
06 开发无界面项目模版
# -*- coding: utf-8 -*-"""@author: chenchen@software: PyCharm@file: index2.py@功能:@time: 2022-09-17 18:45"""import timefrom PyQt5 import QtCorefrom PyQt5.QtCore import Qt, pyqtSignaldef Receive(val):print('接收到信号:', val)class RunTherad(QtCore.QThread):start_prin = pyqtSignal(str)def __init__(self):super(RunTherad, self).__init__()self.log = '日志'print('初始化成功:', self.log)def run(self):while True:time.sleep(2)self.start_prin.emit('你好世界')class Main(QtCore.QObject):def __init__(self, parent=None):super(Main, self).__init__(parent)self.signal = RunTherad()def start(self):self.signal.start_prin.connect(Receive, Qt.DirectConnection)self.signal.start()if __name__ == '__main__':import sysapp = QtCore.QCoreApplication(sys.argv)main = Main()main.start()sys.exit(app.exec_())
