当前位置: 首页 > news >正文

PyQt开发完整指南

PyQt开发完整指南

目录

  1. PyQt简介
  2. 环境搭建
  3. 第一个PyQt应用
  4. Qt Designer使用指南
  5. 常用控件详解
  6. 布局管理
  7. 信号与槽机制
  8. 事件处理
  9. 对话框
  10. 菜单栏、工具栏和状态栏
  11. 多线程编程
  12. 数据库操作
  13. 样式表(QSS)
  14. 打包部署
  15. 最佳实践
  16. 常见问题与解决方案

PyQt简介

什么是PyQt

PyQt是Python编程语言和Qt应用程序框架的绑定。它允许Python开发者使用Qt库创建功能丰富的桌面应用程序。PyQt结合了Python的简洁性和Qt的强大功能,成为开发跨平台GUI应用的优秀选择。

PyQt的版本

  • PyQt5: 目前最稳定和广泛使用的版本
  • PyQt6: 最新版本,基于Qt6,提供了更现代的特性

PyQt vs PySide


环境搭建

安装Python

确保系统已安装Python 3.6或更高版本:

python --version

安装PyQt5

使用pip安装PyQt5:

pip install PyQt5

安装PyQt5工具(包含Qt Designer):

pip install pyqt5-tools

安装PyQt6(可选)

pip install PyQt6
pip install pyqt6-tools

验证安装

创建一个简单的测试脚本:

import sys
from PyQt5.QtWidgets import QApplication, QLabelapp = QApplication(sys.argv)
label = QLabel("PyQt5 安装成功!")
label.show()
sys.exit(app.exec_())

第一个PyQt应用

基本窗口应用

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtCore import Qtclass MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("我的第一个PyQt应用")self.setGeometry(100, 100, 600, 400)# 创建中心控件label = QLabel("Hello PyQt!", self)label.setAlignment(Qt.AlignCenter)self.setCentralWidget(label)if __name__ == "__main__":app = QApplication(sys.argv)window = MainWindow()window.show()sys.exit(app.exec_())

应用程序结构解析

  1. QApplication: 管理GUI应用程序的控制流和主要设置
  2. QMainWindow: 提供主应用程序窗口的框架
  3. 事件循环: app.exec_() 启动应用程序的事件循环

Qt Designer使用指南

启动Qt Designer

# Windows
python -m PyQt5.uic.pyuic5designer# Linux/Mac
designer

创建UI文件

  1. 新建窗体(选择Main Window)
  2. 拖拽控件到窗体
  3. 设置控件属性
  4. 保存为.ui文件

将UI文件转换为Python代码

pyuic5 -x design.ui -o design.py

在代码中使用UI文件

from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindowclass MainWindow(QMainWindow):def __init__(self):super().__init__()uic.loadUi('design.ui', self)

常用控件详解

按钮控件

QPushButton(普通按钮)
from PyQt5.QtWidgets import QPushButtonbutton = QPushButton("点击我", self)
button.clicked.connect(self.on_button_clicked)
button.setGeometry(50, 50, 100, 30)def on_button_clicked(self):print("按钮被点击了!")
QRadioButton(单选按钮)
from PyQt5.QtWidgets import QRadioButtonradio1 = QRadioButton("选项1", self)
radio2 = QRadioButton("选项2", self)
radio1.toggled.connect(lambda: self.on_radio_toggled(radio1))
QCheckBox(复选框)
from PyQt5.QtWidgets import QCheckBoxcheckbox = QCheckBox("同意条款", self)
checkbox.stateChanged.connect(self.on_checkbox_changed)def on_checkbox_changed(self, state):if state == 2:  # Qt.Checkedprint("已勾选")else:print("未勾选")

输入控件

QLineEdit(单行文本框)
from PyQt5.QtWidgets import QLineEditline_edit = QLineEdit(self)
line_edit.setPlaceholderText("请输入文本")
line_edit.textChanged.connect(self.on_text_changed)
line_edit.setMaxLength(20)
QTextEdit(多行文本框)
from PyQt5.QtWidgets import QTextEdittext_edit = QTextEdit(self)
text_edit.setPlainText("初始文本")
text_edit.textChanged.connect(self.on_text_changed)
QSpinBox(数字输入框)
from PyQt5.QtWidgets import QSpinBoxspin_box = QSpinBox(self)
spin_box.setMinimum(0)
spin_box.setMaximum(100)
spin_box.setValue(50)
spin_box.valueChanged.connect(self.on_value_changed)

显示控件

QLabel(标签)
from PyQt5.QtWidgets import QLabel
from PyQt5.QtGui import QPixmap# 文本标签
label = QLabel("这是一个标签", self)
label.setWordWrap(True)  # 自动换行# 图片标签
image_label = QLabel(self)
pixmap = QPixmap("image.png")
image_label.setPixmap(pixmap)
image_label.setScaledContents(True)
QProgressBar(进度条)
from PyQt5.QtWidgets import QProgressBarprogress = QProgressBar(self)
progress.setMinimum(0)
progress.setMaximum(100)
progress.setValue(75)

列表控件

QListWidget(列表控件)
from PyQt5.QtWidgets import QListWidget, QListWidgetItemlist_widget = QListWidget(self)
list_widget.addItem("项目1")
list_widget.addItem("项目2")# 添加自定义项目
item = QListWidgetItem("自定义项目")
list_widget.addItem(item)list_widget.itemClicked.connect(self.on_item_clicked)
QComboBox(下拉框)
from PyQt5.QtWidgets import QComboBoxcombo = QComboBox(self)
combo.addItems(["选项1", "选项2", "选项3"])
combo.currentTextChanged.connect(self.on_selection_changed)
QTableWidget(表格控件)
from PyQt5.QtWidgets import QTableWidget, QTableWidgetItemtable = QTableWidget(self)
table.setRowCount(3)
table.setColumnCount(2)
table.setHorizontalHeaderLabels(["列1", "列2"])# 设置单元格内容
table.setItem(0, 0, QTableWidgetItem("数据1"))
table.setItem(0, 1, QTableWidgetItem("数据2"))

布局管理

QHBoxLayout(水平布局)

from PyQt5.QtWidgets import QHBoxLayout, QPushButtonlayout = QHBoxLayout()
layout.addWidget(QPushButton("按钮1"))
layout.addWidget(QPushButton("按钮2"))
layout.addWidget(QPushButton("按钮3"))widget = QWidget()
widget.setLayout(layout)

QVBoxLayout(垂直布局)

from PyQt5.QtWidgets import QVBoxLayout, QLabellayout = QVBoxLayout()
layout.addWidget(QLabel("标签1"))
layout.addWidget(QLabel("标签2"))
layout.addWidget(QLabel("标签3"))

QGridLayout(网格布局)

from PyQt5.QtWidgets import QGridLayout, QPushButtonlayout = QGridLayout()
layout.addWidget(QPushButton("按钮1"), 0, 0)
layout.addWidget(QPushButton("按钮2"), 0, 1)
layout.addWidget(QPushButton("按钮3"), 1, 0, 1, 2)  # 跨列

QFormLayout(表单布局)

from PyQt5.QtWidgets import QFormLayout, QLineEditlayout = QFormLayout()
layout.addRow("姓名:", QLineEdit())
layout.addRow("邮箱:", QLineEdit())
layout.addRow("电话:", QLineEdit())

嵌套布局

# 主垂直布局
main_layout = QVBoxLayout()# 顶部水平布局
top_layout = QHBoxLayout()
top_layout.addWidget(QPushButton("左"))
top_layout.addWidget(QPushButton("中"))
top_layout.addWidget(QPushButton("右"))# 添加到主布局
main_layout.addLayout(top_layout)
main_layout.addWidget(QTextEdit())

信号与槽机制

基本概念

信号与槽是Qt的核心机制,用于对象之间的通信:

  • 信号(Signal): 当特定事件发生时发出
  • 槽(Slot): 响应信号的函数

连接信号与槽

# 基本连接
button.clicked.connect(self.on_button_clicked)# 带参数的连接
slider.valueChanged.connect(lambda value: self.update_label(value))# 断开连接
button.clicked.disconnect(self.on_button_clicked)

自定义信号

from PyQt5.QtCore import pyqtSignal, QObjectclass CustomObject(QObject):# 定义信号custom_signal = pyqtSignal(str)data_signal = pyqtSignal(int, str)def trigger_signal(self):self.custom_signal.emit("Hello")self.data_signal.emit(42, "数据")# 使用自定义信号
obj = CustomObject()
obj.custom_signal.connect(lambda msg: print(f"收到信号: {msg}"))

装饰器方式

from PyQt5.QtCore import pyqtSlotclass MainWindow(QMainWindow):@pyqtSlot()def on_button_clicked(self):print("按钮被点击")@pyqtSlot(int)def on_value_changed(self, value):print(f"值改变为: {value}")

事件处理

重写事件处理方法

class MainWindow(QMainWindow):def closeEvent(self, event):reply = QMessageBox.question(self, '确认', '确定要退出吗?',QMessageBox.Yes | QMessageBox.No)if reply == QMessageBox.Yes:event.accept()else:event.ignore()def keyPressEvent(self, event):if event.key() == Qt.Key_Escape:self.close()def mousePressEvent(self, event):if event.button() == Qt.LeftButton:print(f"鼠标左键点击位置: {event.pos()}")

事件过滤器

class EventFilter(QObject):def eventFilter(self, obj, event):if event.type() == QEvent.KeyPress:print(f"按键: {event.key()}")return Truereturn False# 安装事件过滤器
filter = EventFilter()
widget.installEventFilter(filter)

对话框

QMessageBox(消息框)

from PyQt5.QtWidgets import QMessageBox# 信息框
QMessageBox.information(self, "标题", "这是一条信息")# 警告框
QMessageBox.warning(self, "警告", "这是一条警告")# 错误框
QMessageBox.critical(self, "错误", "发生了错误")# 询问框
reply = QMessageBox.question(self, "询问", "是否继续?",QMessageBox.Yes | QMessageBox.No)

QFileDialog(文件对话框)

from PyQt5.QtWidgets import QFileDialog# 打开文件
file_name, _ = QFileDialog.getOpenFileName(self, "打开文件", "","文本文件 (*.txt);;所有文件 (*)")# 保存文件
file_name, _ = QFileDialog.getSaveFileName(self, "保存文件", "","文本文件 (*.txt)")# 选择目录
directory = QFileDialog.getExistingDirectory(self, "选择目录")

QInputDialog(输入对话框)

from PyQt5.QtWidgets import QInputDialog# 文本输入
text, ok = QInputDialog.getText(self, "输入", "请输入文本:")# 整数输入
num, ok = QInputDialog.getInt(self, "输入", "请输入数字:", value=0, min=-100, max=100)# 选项输入
items = ["选项1", "选项2", "选项3"]
item, ok = QInputDialog.getItem(self, "选择", "请选择:", items)

自定义对话框

class CustomDialog(QDialog):def __init__(self, parent=None):super().__init__(parent)self.setWindowTitle("自定义对话框")self.setModal(True)layout = QVBoxLayout()# 添加控件self.input = QLineEdit()layout.addWidget(QLabel("请输入:"))layout.addWidget(self.input)# 按钮buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)buttons.accepted.connect(self.accept)buttons.rejected.connect(self.reject)layout.addWidget(buttons)self.setLayout(layout)def get_input(self):return self.input.text()# 使用自定义对话框
dialog = CustomDialog(self)
if dialog.exec_() == QDialog.Accepted:result = dialog.get_input()

菜单栏、工具栏和状态栏

菜单栏

def create_menu_bar(self):menubar = self.menuBar()# 文件菜单file_menu = menubar.addMenu("文件(&F)")# 新建动作new_action = QAction("新建(&N)", self)new_action.setShortcut("Ctrl+N")new_action.triggered.connect(self.new_file)file_menu.addAction(new_action)# 打开动作open_action = QAction("打开(&O)", self)open_action.setShortcut("Ctrl+O")open_action.triggered.connect(self.open_file)file_menu.addAction(open_action)# 分隔符file_menu.addSeparator()# 退出动作exit_action = QAction("退出(&X)", self)exit_action.setShortcut("Ctrl+Q")exit_action.triggered.connect(self.close)file_menu.addAction(exit_action)

工具栏

def create_toolbar(self):toolbar = self.addToolBar("工具栏")# 添加带图标的动作new_action = QAction(QIcon("new.png"), "新建", self)new_action.triggered.connect(self.new_file)toolbar.addAction(new_action)open_action = QAction(QIcon("open.png"), "打开", self)open_action.triggered.connect(self.open_file)toolbar.addAction(open_action)# 添加分隔符toolbar.addSeparator()# 添加控件toolbar.addWidget(QLabel("搜索:"))toolbar.addWidget(QLineEdit())

状态栏

def create_status_bar(self):self.status_bar = self.statusBar()# 显示临时消息(5秒)self.status_bar.showMessage("准备就绪", 5000)# 添加永久控件self.progress = QProgressBar()self.progress.setMaximumWidth(200)self.status_bar.addPermanentWidget(self.progress)self.label = QLabel("状态: 正常")self.status_bar.addPermanentWidget(self.label)

右键菜单

def contextMenuEvent(self, event):menu = QMenu(self)copy_action = menu.addAction("复制")paste_action = menu.addAction("粘贴")action = menu.exec_(self.mapToGlobal(event.pos()))if action == copy_action:self.copy()elif action == paste_action:self.paste()

多线程编程

使用QThread

from PyQt5.QtCore import QThread, pyqtSignal
import timeclass WorkerThread(QThread):# 定义信号progress = pyqtSignal(int)finished = pyqtSignal()def __init__(self):super().__init__()self.is_running = Truedef run(self):for i in range(101):if not self.is_running:breakself.progress.emit(i)time.sleep(0.1)self.finished.emit()def stop(self):self.is_running = False# 使用线程
class MainWindow(QMainWindow):def __init__(self):super().__init__()self.worker = WorkerThread()self.worker.progress.connect(self.update_progress)self.worker.finished.connect(self.on_finished)def start_work(self):self.worker.start()def update_progress(self, value):self.progress_bar.setValue(value)def on_finished(self):QMessageBox.information(self, "完成", "任务已完成!")

使用QRunnable和QThreadPool

from PyQt5.QtCore import QRunnable, QThreadPool, pyqtSignal, QObjectclass WorkerSignals(QObject):finished = pyqtSignal()error = pyqtSignal(str)result = pyqtSignal(object)class Worker(QRunnable):def __init__(self, fn, *args, **kwargs):super().__init__()self.fn = fnself.args = argsself.kwargs = kwargsself.signals = WorkerSignals()def run(self):try:result = self.fn(*self.args, **self.kwargs)self.signals.result.emit(result)except Exception as e:self.signals.error.emit(str(e))finally:self.signals.finished.emit()# 使用线程池
threadpool = QThreadPool()
worker = Worker(self.long_running_task, param1, param2)
worker.signals.result.connect(self.handle_result)
threadpool.start(worker)

数据库操作

使用QSqlDatabase

from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel# 创建数据库连接
db = QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('database.db')if not db.open():print("无法打开数据库")return# 创建表
query = QSqlQuery()
query.exec_("""CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,email TEXT NOT NULL)
""")# 插入数据
query.prepare("INSERT INTO users (name, email) VALUES (?, ?)")
query.addBindValue("张三")
query.addBindValue("zhangsan@example.com")
query.exec_()# 查询数据
query.exec_("SELECT * FROM users")
while query.next():id = query.value(0)name = query.value(1)email = query.value(2)print(f"{id}: {name} - {email}")

使用QSqlTableModel

# 创建模型
model = QSqlTableModel()
model.setTable('users')
model.setEditStrategy(QSqlTableModel.OnFieldChange)
model.select()# 设置表头
model.setHeaderData(1, Qt.Horizontal, "姓名")
model.setHeaderData(2, Qt.Horizontal, "邮箱")# 与视图绑定
table_view = QTableView()
table_view.setModel(model)
table_view.hideColumn(0)  # 隐藏ID列# 添加新记录
record = model.record()
record.setValue("name", "李四")
record.setValue("email", "lisi@example.com")
model.insertRecord(-1, record)

样式表(QSS)

基本语法

# 设置单个控件样式
button.setStyleSheet("""QPushButton {background-color: #4CAF50;color: white;border: none;padding: 8px 16px;border-radius: 4px;font-size: 14px;}QPushButton:hover {background-color: #45a049;}QPushButton:pressed {background-color: #3d8b40;}
""")# 设置应用程序全局样式
app.setStyleSheet("""QMainWindow {background-color: #f0f0f0;}QLabel {color: #333;font-size: 12px;}
""")

常用样式示例

/* 按钮样式 */
QPushButton {background-color: #007BFF;color: white;border: none;padding: 10px 20px;border-radius: 5px;font-weight: bold;
}QPushButton:hover {background-color: #0056b3;
}QPushButton:disabled {background-color: #cccccc;color: #666666;
}/* 输入框样式 */
QLineEdit {border: 2px solid #ddd;border-radius: 4px;padding: 8px;font-size: 14px;
}QLineEdit:focus {border-color: #007BFF;
}/* 列表样式 */
QListWidget {border: 1px solid #ddd;border-radius: 4px;background-color: white;
}QListWidget::item {padding: 8px;border-bottom: 1px solid #eee;
}QListWidget::item:selected {background-color: #007BFF;color: white;
}/* 菜单样式 */
QMenuBar {background-color: #f8f9fa;border-bottom: 1px solid #dee2e6;
}QMenuBar::item:selected {background-color: #007BFF;color: white;
}

从文件加载样式

def load_stylesheet(file_path):with open(file_path, 'r', encoding='utf-8') as file:return file.read()# 应用样式
app.setStyleSheet(load_stylesheet('style.qss'))

打包部署

使用PyInstaller

安装PyInstaller
pip install pyinstaller
基本打包命令
# 打包为单个可执行文件
pyinstaller --onefile --windowed main.py# 打包为文件夹
pyinstaller --onedir --windowed main.py# 指定图标
pyinstaller --onefile --windowed --icon=app.ico main.py# 包含额外文件
pyinstaller --onefile --windowed --add-data "assets;assets" main.py
配置文件(.spec)
# main.spec
a = Analysis(['main.py'],pathex=[],binaries=[],datas=[('assets', 'assets')],hiddenimports=['PyQt5.QtPrintSupport'],hookspath=[],runtime_hooks=[],excludes=[],win_no_prefer_redirects=False,win_private_assemblies=False,cipher=None,noarchive=False)pyz = PYZ(a.pure, a.zipped_data, cipher=None)exe = EXE(pyz,a.scripts,a.binaries,a.zipfiles,a.datas,[],name='MyApp',debug=False,bootloader_ignore_signals=False,strip=False,upx=True,console=False,icon='app.ico')

资源文件处理

import sys
import osdef resource_path(relative_path):"""获取资源文件的绝对路径"""try:# PyInstaller创建临时文件夹并将路径存储在_MEIPASS中base_path = sys._MEIPASSexcept Exception:base_path = os.path.abspath(".")return os.path.join(base_path, relative_path)# 使用资源文件
icon_path = resource_path('icons/app.png')
pixmap = QPixmap(icon_path)

最佳实践

项目结构

project/
├── main.py              # 主入口文件
├── ui/                  # UI文件目录
│   ├── __init__.py
│   ├── main_window.ui
│   └── dialogs/
├── widgets/             # 自定义控件
│   ├── __init__.py
│   └── custom_widget.py
├── resources/           # 资源文件
│   ├── icons/
│   ├── images/
│   └── styles/
├── utils/               # 工具函数
│   ├── __init__.py
│   └── helpers.py
├── models/              # 数据模型
│   ├── __init__.py
│   └── data_model.py
└── requirements.txt     # 依赖列表

代码组织

# main_window.py
class MainWindow(QMainWindow):def __init__(self):super().__init__()self.init_ui()self.init_connections()self.load_settings()def init_ui(self):"""初始化UI"""self.setup_window()self.create_widgets()self.create_layouts()self.create_menus()def init_connections(self):"""初始化信号连接"""self.button.clicked.connect(self.on_button_clicked)def load_settings(self):"""加载配置"""settings = QSettings('MyCompany', 'MyApp')geometry = settings.value('geometry')if geometry:self.restoreGeometry(geometry)def save_settings(self):"""保存配置"""settings = QSettings('MyCompany', 'MyApp')settings.setValue('geometry', self.saveGeometry())

内存管理

# 正确删除控件
widget.deleteLater()# 清理布局
def clear_layout(layout):while layout.count():child = layout.takeAt(0)if child.widget():child.widget().deleteLater()# 使用弱引用
import weakref
self.widget_ref = weakref.ref(widget)

异常处理

def safe_operation(self):try:# 危险操作result = self.risky_operation()except FileNotFoundError:QMessageBox.warning(self, "警告", "文件未找到")except Exception as e:QMessageBox.critical(self, "错误", f"发生错误: {str(e)}")# 记录日志logging.error(f"操作失败: {e}", exc_info=True)

性能优化

# 使用QTimer延迟操作
def delayed_operation(self):QTimer.singleShot(100, self.heavy_operation)# 批量更新UI
self.setUpdatesEnabled(False)
# 执行大量UI更新
for i in range(1000):self.add_item(i)
self.setUpdatesEnabled(True)# 使用模型/视图架构
model = QStandardItemModel()
view = QListView()
view.setModel(model)

常见问题与解决方案

1. 中文显示问题

# 设置编码
import sys
if sys.platform == 'win32':import ctypesctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('myapp')# 设置字体
font = QFont("Microsoft YaHei", 10)
app.setFont(font)

2. 高DPI显示问题

# 在创建QApplication之前设置
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)app = QApplication(sys.argv)

3. 线程中更新UI

# 错误方式
class Worker(QThread):def run(self):self.label.setText("更新")  # 错误!# 正确方式
class Worker(QThread):update_signal = pyqtSignal(str)def run(self):self.update_signal.emit("更新")# 主线程中
worker.update_signal.connect(self.label.setText)

4. 资源文件未找到

# 使用Qt资源系统
import resources_rc  # pyrcc5生成的资源文件# 或使用绝对路径
import os
current_dir = os.path.dirname(os.path.abspath(__file__))
icon_path = os.path.join(current_dir, 'icons', 'app.png')

5. 窗口关闭后程序未退出

# 确保最后一个窗口关闭时退出
app.setQuitOnLastWindowClosed(True)# 或在关闭事件中
def closeEvent(self, event):QApplication.quit()

6. 内存泄漏

# 使用parent参数
button = QPushButton("按钮", parent=self)# 断开不需要的信号连接
try:self.signal.disconnect()
except TypeError:pass# 清理定时器
if hasattr(self, 'timer'):self.timer.stop()self.timer.deleteLater()

7. 打包后找不到模块

# 在.spec文件中添加隐藏导入
hiddenimports=['PyQt5.QtPrintSupport', 'PyQt5.QtSql']# 或在命令行中
pyinstaller --hidden-import=PyQt5.QtPrintSupport main.py

学习资源

官方文档

  • Qt Documentation
  • PyQt5 Reference Guide

相关文章:

  • 亚矩阵云手机多开赋能Snapchat矩阵运营:技术原理与场景化破局
  • python基于协同过滤的动漫推荐系统
  • 微服务常用的基础知识
  • 数据结构进阶 第七章 图(Graph)
  • 【数据结构】--排序算法
  • 从零构建vue3项目(二)
  • 算法打卡 day4
  • 基于vue3+ByteMD快速搭建自己的Markdown文档编辑器
  • 洛谷P3871 [TJOI2010] 中位数
  • 【Linux网络编程】多路转接IO(二)epoll
  • 知识变现全链路设计:从IP打造到商业闭环的系统方法论|创客匠人
  • DSP学习笔记1
  • Redis网络通信模块深度解析:单线程Reactor到多线程IO的架构演进
  • ULVAC HPS1600F EGC10GS16GS 电子束电源控制Electron Beam Power Supply Gun Controller
  • SpringBoot 中 @Transactional 的使用
  • Netty:深入解析AbstractByteBufAllocator架构设计
  • 重塑音视频叙事:Premiere文本剪辑与Podcast AI降噪的革命性工作流
  • 机器学习16-强化学习-马尔科夫决策
  • 前端替换打包后文件中的内容方案(可用于渗透测试后将问题版本号清空临时解决方案)
  • 高通手机跑AI系列之——穿衣试装算法