01 使用 Q designer 设置信号和槽

  1. 系统本身的槽(函数)
  2. 指定个人定义的槽(函数)绑定

image.png

02 定义线程业务逻辑+发送信号

代码这块要实现一个线程类, 用来处理每个按钮点击触发的业务逻辑

  1. class Runthread_contend(QtCore.QThread):
  2. # 定义信号 到时候需要在这个线程中发送信号到主界面的绑定的槽(函数)
  3. start_print_contend = pyqtSignal(dict)
  4. start_print_int = pyqtSignal(int) # 定义信号类型
  5. start_print_str = pyqtSignal(str,int) # 定义信号, 发送了2个参数数据
  6. # 当主界面 初始化Runthread_contend(c1,c2) 携带了2个参数 在__init__ 接收了
  7. def __init__(self, url_list, Cookie):
  8. super(Runthread_contend, self).__init__()
  9. self._isPause = False # 用来结束 Qthread
  10. self.mutex = QMutex() # 创建线程锁 批量网址刷
  11. self.count = 0
  12. self.Cookie = Cookie
  13. def run(self):
  14. # 这里就是你书写业务的逻辑, 如果太多可调用函数的方式处理, 这里一般是循环
  15. for i in range(20):
  16. self.start_print_str.emit("你好", i) # 这就是发送信号 , 在上面定义了信号的类型,这里要严格规范
  17. self.test("你好", i) # 这就是调用函数在函数里面发送信号
  18. def test(self, val, num):
  19. self.start_print_str.emit(val, num)

03 实例化 线程函数 + 绑定槽信号

  1. class MainWindowUI(QMainWindow, Ui_BaiduAD):
  2. def __init__(self, parent=None):
  3. super(MainWindowUI, self).__init__(parent)
  4. self.contend = None
  5. self.date = "......."
  6. #........初始化一些界面的信息, 比如可以在加载界面的是否读取配置等等
  7. @QtCore.pyqtSlot() # 这里就是用designer 设计触发按钮事件绑定了 Baidu_kaiShiRenWu函数
  8. def Baidu_kaiShiRenWu(self):
  9. # 1. 获取输入框的数据 , 其他也一样的道理
  10. Therad_num = self.lineEdit_xianChenNum.text()
  11. # 2. 初始化 线程函数, 并把数据带进去
  12. self.contend = Runthread_contend(Therad_num)
  13. # 3. 线程函数 中的 信号 与 主线程的(槽)函数 绑定 TS_print函数
  14. self.contend.start_print_str.connect(self.TS_print, Qt.DirectConnection)
  15. # 4. 执行线程
  16. self.contend.satrt()
  17. def TS_print(self,msg,val_num):
  18. # 方式一 直接更新GUI
  19. # 这里信号那边有数据 会发送信号到这里来 , 这里你就可以去更新界面的数据,方便显示给用户看
  20. print(msg,val_num)
  21. self.组件的名字.setText(msg) # 根据组件的不同 方式不同有的可以 append(msg)
  22. # 方式二 使用线程更新GUI
  23. """ 一般这里我会设置一个线程 处理 更新 数据到GUI(界面) args(参数1,) 注意一定要在最后面加 逗号
  24. th = threading.Thread(target=self.TS_print_UI, args=(msg,val_num,), name='thread-{:03d}'.format(int(random.random())), )
  25. th.start()
  26. time.sleep(1)
  27. th.join()
  28. """
  29. def TS_print_UI(self, msg,val_num):
  30. print(msg,val_num)
  31. self.toolButton_KaiShiRenWu.setEnabled(False) # 更新了按钮的状态

绑定外部槽文件+外部信号文件

02 - 03 都是属于在一个文件内调用 内部槽函数 和内部Qt类信号!!!

下面我们来实现如何调用外部槽函数 和外部槽文件, 那为什么需要这样做呢? 原因很简单, 因为在一个文件中 书写槽函数 和Qt类信号, 随着业务的逻辑越来越多, 你寻找某个功能的时候需要从一个文件几千行代码里寻找这个功能,, 那时非常头疼的事情, 那么现在来看看我的结构吧!

项目结构

image.pngimage.png

01 槽函数文件结构

这个类就是我们定义的槽文件中的类 初始化的时候 需要填写一个 参数 那就是parent , 这个parent是什么呢,这个parent是继承了父类也就是主线程界面相关的数据,
这个时候,需要在外部曹函数更新主线程UI的数据就需要用到 parnet
平时我们在内部曹函数中只需要 self.lineText组件即可, 可是在外部槽文件中的槽函数 需要在 self后再书写parent,才能更新主线的UI数据
注意 这个parent是重点!!!!!

  1. import threading,random
  2. import time
  3. from PyQt5.QtWidgets import QTableWidgetItem
  4. class Groove:
  5. """槽函数类 初始化"""
  6. def __init__(self, parent):
  7. self.parent = parent
  8. self.thread_num = 1
  9. def set_ui(self, msg):
  10. print(f"接收到信号 {msg}")
  11. 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

  1. from PyQt5.QtCore import Qt, pyqtSignal
  2. from PyQt5 import QtWidgets, QtCore
  3. # 线程类
  4. class Runthread_file(QtCore.QThread):
  5. start_print = pyqtSignal(str) # 这里是定义发送信号的类型
  6. """
  7. start_print = pyqtSignal(int)
  8. start_print = pyqtSignal(dict)
  9. start_print = pyqtSignal(list)
  10. start_print = pyqtSignal()
  11. start_print = pyqtSignal("PyQt_PyObject")
  12. start_print = pyqtSignal(str, int, str, list)
  13. """
  14. def __init__(self, filePath):
  15. super(Runthread_file, self).__init__() # 默认初始化自已,不需要懂,照着做就可以
  16. self.filePath = filePath
  17. def run(self):
  18. try:
  19. with open(self.filePath, 'r', encoding="utf-8-sig") as f:
  20. for line in f: # 1.一行一行读取后, 自动关闭文件
  21. self.start_print.emit(line)
  22. except Exception as e:
  23. print("ccdcd ", e) # 转换文本 为UTF-8

03 主程序的目录结构

  1. self.G = Groove(self) # 实例化外部 槽函数文件, 并且需要携带 self 作为参数, 这里很重要, 决定能否在外部曹函数更新UI
  2. self.signer = None # 待实例化Qt类线程的时候需要使用到这个变量, 如果不初始化这个变量后续实例化后不会生效

如果主线程需要用到 外部曹函数的变量,或者想修改更新 外部曹函数的变量的值, 可通过 self.G.thread_num = 2 即可 , 注意的是 一定要在实例化后, 才能获取到曹函数的变量值

  1. from PyQt5.QtCore import Qt, QSettings
  2. from PyQt5.QtGui import QCursor
  3. from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QAbstractItemView, QHeaderView
  4. from PyQt5 import QtWidgets, QtCore
  5. import random, os, sys
  6. from resource.taobao_ui import Ui_MainWindow # 引入了 Qt设计师的UI文件
  7. from main_Thread_Signer.Signer1 import Runthread_file, Runthread_start # 引入外部Qt类线程
  8. from main_Groove_Fn.Fn1 import Groove # 引入外部曹函数
  9. class MainWindowUI(QMainWindow, Ui_MainWindow):
  10. def __init__(self, parent=None):
  11. super(MainWindowUI, self).__init__(parent)
  12. self.setupUi(self)
  13. self.G = Groove(self) # 实例化外部 槽函数文件, 并且需要携带 self 作为参数, 这里很重要, 决定能否在外部曹函数更新UI
  14. self.signer = None # 待实例化Qt类线程的时候需要使用到这个变量, 如果不初始化这个变量后续实例化后不会生效
  15. def setupUi(self,mainWindow):
  16. super().setupUi(mainWindow)
  17. self.setWindowTitle("程序标题名称")
  18. self.setWindowIcon(QtGui.QIcon(':/images/myy.ico'))
  19. @QtCore.pyqtSlot()
  20. def TaoBao_YijianBaocun(self):
  21. print("点击了按钮")
  22. self.signer = Runthread_file() # 实例化Qt类线程 可携带参数
  23. self.signer.start_print.connect(self.G.set_ui, Qt.DirectConnection) # 绑定槽函数
  24. self.signer.start() # 执行线程
  25. if __name__ == "__main__":
  26. app = QtWidgets.QApplication(sys.argv)
  27. w = MainWindowUI()
  28. w.show()
  29. sys.exit(app.exec_())

05 开发项目模版

具有 隐藏框, 透明背景, 窗口可移动 启动程序自动读取配置 功能

  1. from PyQt5.QtCore import Qt, QSettings
  2. from PyQt5.QtGui import QCursor
  3. from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QAbstractItemView, QHeaderView
  4. from PyQt5 import QtWidgets, QtCore
  5. import random, os, sys
  6. # from resource.taobao_ui import Ui_MainWindow
  7. # from main_Thread_Signer.Signer1 import Runthread_file, Runthread_start
  8. # from main_Groove_Fn.Fn1 import Groove
  9. class MainWindowUI(QMainWindow, Ui_MainWindow):
  10. def __init__(self, parent=None):
  11. super(MainWindowUI, self).__init__(parent)
  12. self.setupUi(self)
  13. self.setGroove()
  14. self.setQtThread()
  15. self.setOtherConfig()
  16. def mousePressEvent(self, event):
  17. if event.button() == Qt.LeftButton:
  18. self.m_flag = True
  19. self.m_Position = event.globalPos() - self.pos() # 获取鼠标相对窗口的位置
  20. event.accept()
  21. self.setCursor(QCursor(Qt.OpenHandCursor)) # 更改鼠标图标
  22. def mouseMoveEvent(self, QMouseEvent):
  23. if Qt.LeftButton and self.m_flag:
  24. self.move(QMouseEvent.globalPos() - self.m_Position) # 更改窗口位置
  25. QMouseEvent.accept()
  26. def mouseReleaseEvent(self, QMouseEvent):
  27. self.m_flag = False
  28. self.setCursor(QCursor(Qt.ArrowCursor))
  29. def setupUi(self,mainWindow):
  30. super().setupUi(mainWindow)
  31. self.setWindowTitle("程序标题名称")
  32. self.setWindowIcon(QtGui.QIcon(':/images/myy.ico')) # 这里改成决定路径的图片打包后即可正常显示
  33. self.setWindowFlags(Qt.WindowMaximizeButtonHint | Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint) # 置顶,影藏边框
  34. self.setAttribute(Qt.WA_TranslucentBackground) # 透明背景
  35. self.tableWidget_RewuLeiBiao.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置 不可选择单个单元格,只可选择一行。
  36. self.tableWidget_RewuLeiBiao.horizontalHeader().resizeSection(0, 200) # 设置表头第一列的宽度为200
  37. self.tableWidget_RewuLeiBiao.horizontalHeader().resizeSection(1, 120)
  38. self.tableWidget_RewuLeiBiao.horizontalHeader().resizeSection(2, 200)
  39. self.tableWidget_RewuLeiBiao.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) # 宽度自动调整,充满屏幕
  40. self.textBrowser_RiZhi.append("[免责声明]")
  41. def setOtherConfig(self): # 设置其它配置
  42. self.settings = QSettings('config.ini', QSettings.IniFormat)
  43. self.initRecentFileList()
  44. def saveFileList(self): # 保存配置
  45. print("进来执行保存", self.lineEdit_Xsrf_token.text())
  46. self.settings.setValue("Set/Xsrf_token", self.lineEdit_Xsrf_token.text())
  47. self.settings.setValue("Set/Bx_umidtoken", self.lineEdit_Bx_umidtoken.text())
  48. self.settings.setValue("Set/Callback", self.lineEdit_Callback.text())
  49. self.settings.setValue("Set/SessionId", self.lineEdit_SessionId.text())
  50. self.settings.setValue("Set/API", self.lineEdit_API.text())
  51. self.settings.setValue("Set/chrome", self.lineEdit_chrome.text())
  52. self.settings.setValue("Set/R5", self.lineEdit_chromedriver.text())
  53. self.settings.setValue("Set/Cookie", self.textEdit_Cookie.toPlainText())
  54. self.settings.setValue("Set/Bx_ua", self.textEdit_Bx_ua.toPlainText())
  55. def initRecentFileList(self):
  56. self.lineEdit_Xsrf_token.setText(self.settings.value("Set/Xsrf_token"))
  57. self.lineEdit_Bx_umidtoken.setText(self.settings.value("Set/Bx_umidtoken"))
  58. self.lineEdit_Callback.setText(self.settings.value("Set/Callback"))
  59. self.lineEdit_SessionId.setText(self.settings.value("Set/SessionId"))
  60. self.lineEdit_API.setText(self.settings.value("Set/API"))
  61. self.lineEdit_chrome.setText(self.settings.value("Set/chrome"))
  62. self.lineEdit_chromedriver.setText(self.settings.value("Set/R5"))
  63. self.textEdit_Cookie.setText(self.settings.value("Set/Cookie"))
  64. self.textEdit_Bx_ua.setText(self.settings.value("Set/Bx_ua"))
  65. def setGroove(self): # 实例化外部槽
  66. self.G = Groove(self)
  67. def setQtThread(self): # 实例化Qt线程信号
  68. self.singel_addFile = None # 导入文本
  69. self.specified = None # 开始任务
  70. self.signel_file = None
  71. @QtCore.pyqtSlot()
  72. def Kaishi_btn(self):
  73. self.specified.start_print_dict.connect(self.G.updateFile, Qt.DirectConnection) #绑定曹函数
  74. if __name__ == "__main__":
  75. app = QtWidgets.QApplication(sys.argv)
  76. w = MainWindowUI()
  77. w.show()
  78. sys.exit(app.exec_())
  1. from PyQt5.QtCore import Qt, pyqtSignal
  2. from PyQt5 import QtWidgets, QtCore
  3. import time, threading, requests, random, json, re
  4. from fake_useragent import UserAgent
  5. # 线程类 发送信号使用 .emit()
  6. class Runthread_file(QtCore.QThread):
  7. start_print = pyqtSignal(str)
  8. def __init__(self, filePath):
  9. super(Runthread_file, self).__init__()
  10. self.filePath = filePath
  11. def run(self):
  12. try:
  13. with open(self.filePath, 'r', encoding="utf-8-sig") as f:
  14. for line in f: # 1.一行一行读取后, 自动关闭文件
  15. self.start_print.emit(line)
  16. except Exception as e:
  17. print("导入错误信息提示 ", e) # 转换文本 为UTF-8
  1. from PyQt5.QtWidgets import QTableWidgetItem
  2. class Groove:
  3. """槽函数类"""
  4. def __init__(self, parent):
  5. self.parent = parent
  6. def addFile(self):
  7. self.parent.textBrowser_RiZhi.append("[免责声明]")

06 开发无界面项目模版

  1. # -*- coding: utf-8 -*-
  2. """
  3. @author: chenchen
  4. @software: PyCharm
  5. @file: index2.py
  6. @功能:
  7. @time: 2022-09-17 18:45
  8. """
  9. import time
  10. from PyQt5 import QtCore
  11. from PyQt5.QtCore import Qt, pyqtSignal
  12. def Receive(val):
  13. print('接收到信号:', val)
  14. class RunTherad(QtCore.QThread):
  15. start_prin = pyqtSignal(str)
  16. def __init__(self):
  17. super(RunTherad, self).__init__()
  18. self.log = '日志'
  19. print('初始化成功:', self.log)
  20. def run(self):
  21. while True:
  22. time.sleep(2)
  23. self.start_prin.emit('你好世界')
  24. class Main(QtCore.QObject):
  25. def __init__(self, parent=None):
  26. super(Main, self).__init__(parent)
  27. self.signal = RunTherad()
  28. def start(self):
  29. self.signal.start_prin.connect(Receive, Qt.DirectConnection)
  30. self.signal.start()
  31. if __name__ == '__main__':
  32. import sys
  33. app = QtCore.QCoreApplication(sys.argv)
  34. main = Main()
  35. main.start()
  36. sys.exit(app.exec_())