AI辅助使用Python PySide6 快速进行上位机开发验证

发布时间:2026/8/1 13:21:52

AI辅助使用Python PySide6 快速进行上位机开发验证 文章目录前言1、运行环境2、IDE3、UI框架4、串口通信6、波形显示7、数据处理一、安装环境1.1新建目录1.2安装库二、测试代码2.1新建工程文件夹2.2绑定专属隔离仓2.3创建代码文件总结前言20260731AI带来的便捷使得上位机开发变得非常简单此文章是用于简单快速验证1、运行环境运行环境为Python版本为Python 3.9.22、IDEIDE用的是PyCharm 2021.1.2 (Community Edition)3、UI框架UI框架用的是PySide6这是 Qt 官方The Qt Company亲自下场为最新的 Qt6 写的 Python 封装名副其实的“亲儿子”。最重要的是它是 LGPL 协议你可以合法地用它开发闭源的商业软件赚钱完全免费PyQt6与PySide6的区别Qt6 是一个庞大且极其牛逼的 C 动态链接库.dll / .so。PyQt6 是第三方团队给这个 C 库写的一套 Python 头文件接口Wrapper而且带有“传染性”的 GPL 开源协议商用闭源有风险。PySide6 是 Qt 官方团队亲自下场给这个 C 库写的 Python 头文件接口。它是 LGPL 协议可以放心开发闭源商业软件赚钱。它们的函数名、类名、甚至连底层的枚举变量都完全一样。写出来的代码 99% 都是通用的只是最开头的 #include在 Python 里叫 import名字不同而已。现在业内做新项目几乎全都无脑首选官方亲儿子 PySide6。4、串口通信串口通信用的是pyserial6、波形显示波形显示用的是PyQtGraph7、数据处理数据处理用的:numpy一、安装环境在 Python 中虚拟环境venv就承担了不同项目编译互不影响的作用。如果把所有项目都共用一个全局环境迟早有一天项目 A 需要的库版本会和项目 B 需要的库版本发生冲突这就是 Python 界著名的“依赖地狱Dependency Hell”。所以需要建立虚拟环境我是在E盘新建了文件夹pythonProject然后在此文件夹下新建各个工程的文件比如Serial_Test_01然后在Serial_Test_01文件夹内安装所有必要的库。以后每次一个工程都需要安装一次库这虽然看起来麻烦但这是标准的操作因为如果一年前的老项目用的是 PySide6 的 v6.4 版本其中某个函数的参数定义是 A。今天的新项目要用到最新的 PySide6 v6.8 版本那个函数的参数变成了 B。如果你统一调用全局库只要一升级库一年前的老项目就直接跑不起来了甚至连报错在哪都找不到。Python 虚拟环境venv的哲学就是“物理隔离”。把属于该工程的库文件实实在在地拷贝进该工程的文件夹下。这样无论以后怎么折腾新工程老工程的代码永远安如泰山。不需要担心pip的安装比如PySide6 这个库好几百兆每次建新工程都要重新下载十几分钟吗因为 Python 的包管理器 pip 有一个极其聪明的全局本地缓存Cache机制。• 第一次安装比如你在工程1里它会老老实实从互联网上下载那几百兆的安装包不仅装进你的虚拟环境还会悄悄在电脑 C 盘的深处存一个备份。• 以后再安装比如你在工程2、工程3里当在新的虚拟环境里敲下 pip install PySide6 时pip 会瞬间发现本地缓存里已经有这个版本了。它根本不会耗费任何流量去联网下载而是直接从缓存解压提取通常只需要 2 到 3 秒钟就能安装完毕。所以看似“每个工程都要重新安装”在实际体验中也就是敲一行命令、等几秒钟的事。假设库安装在Serial_Test_01里Serial_Test_01里只是缓存调用还是说安装在Serial_Test_01里了这个文件夹最好不要删方波其他目录的缓存调用库确实实实在在地安装在了 Serial_Test_01 里但它的“缓存下载包”并没有存放在这个工程目录下。所以可以随时删掉 Serial_Test_01 这个文件夹绝对不会影响其他工程秒装库的能力pip install 第一次运行时其实它把文件分成了两份存放在了两个截然不同的地方. “缓存库”在哪里系统的秘密仓库当第一次敲下 pip install PySide6 时pip 会从外网下载一个巨大的压缩包类似于 .zip 或 .whl 格式的安装文件。这个几十上百兆的原始安装包并没有放在你的 E 盘项目里而是被系统悄悄藏在了你 C 盘的全局深度隐藏目录中通常是 C:\Users\你的用户名\AppData\Local\pip\Cache。• 这就好比 你用浏览器下载了一个 .exe 的安装包放在了电脑的“下载”文件夹里。“安装后的库”在哪里当前工程的私有领地pip 在 C 盘存好缓存包之后会立刻把它“解压缩”把里面真正能在代码里运行的 .py 文件、编译好的 C 动态链接库.pyd / .dll完完整整地拷贝一份塞进E:\pythonProject\Serial_Test_01\venv\Lib\site-packages 目录下。• 这就好比 双击了那个 .exe 安装包把软件实体安装到了 E 盘的特定文件夹里。当你新建 Serial_Test_02 时发生了什么当你明天新建了第二个工程并在这个新工程的虚拟环境里敲下 pip install PySide6 时底层逻辑是这样的pip 去看一眼它在 C 盘 AppData 里的“秘密仓库”。它发现“哟这里面已经躺着一个 PySide6 的安装包了不用联网下载了”它直接从 C 盘的缓存包里进行解压缩把运行所需的文件再次物理拷贝一份塞进 E:\pythonProject\Serial_Test_02\venv\Lib\site-packages 里。总结文件可以随便删因为缓存文件统一由 C 盘的 pip 核心机制在集中管理你的 Serial_Test_01 里面装的仅仅是“解压后的工作副本”。如果你的初步验证做完了觉得这个项目没用了你直接把整个 Serial_Test_01 文件夹连同里面的 venv拖进回收站删掉干干净净没有任何后遗症。 你的 C 盘缓存依然完好无损下次建新工程依然是秒级安装1.1新建目录打开 Windows 的命令提示符cmd或 PowerShell第 1 步进入 E 盘并创建/进入总工作区E:mkdirpythonProjectcdpythonProject第 2 步为当前要做的第一个小项目建个文件夹并进去给项目起个纯英文名字比如叫 Serial_Test_01。mkdirSerial_Test_01cdSerial_Test_01第 3 步在这个特定项目里生成隔离舱虚拟环境python-mvenv venv(注这句话的意思是调用 python 内置的 venv 模块在当前目录下生成一个名为 venv 的文件夹。稍微等几秒钟它正在拷贝环境。)第 4 步激活这个项目的隔离舱venv\Scripts\activate(成功标志命令行的最前面会出现一个 (venv)这就代表你已经进入了 Serial_Test_01 这个项目的专属保护罩里。)1.2安装库第 5 步安装当前项目需要的库pipinstallPySide6 pyserial pyqtgraph numpy如果出现以下则安装速度太慢可以按Ctrl C 强行终止当前任务。然后再重新输入安装命令并在末尾加上国内的高速镜像源比如清华源。pip 非常聪明它有断点续传功能。只需执行下面这行命令它会直接从 48.5 MB 的地方接着下而且速度通常会飙升到几 MB/s十几秒就能下完暂停后输入以下镜像pipinstallPySide6 pyserial pyqtgraph numpy-ihttps://pypi.tuna.tsinghua.edu.cn/simple通过镜像高速下载完并安装:出现Successfully installed …则说明安装成功二、测试代码这里我是直接在PyCharm 上进行开发的后期推荐使用CursorCursor 是基于 VS Code 源码修改的“AI 原生编辑器”。它的界面、快捷键、插件生态和 VS Code 一模一样但内核被深度改写以融入 AI。Cursor的使用非常方便Cursor可以对整个工程进行全文检索并且只需要说出一段话列出明确需求就可以直接生成一个demo并且Cursor 写 Python GUI 代码的能力是碾压级的2.1新建工程文件夹打开PyCharm在左上角点击 File - 选择 Open…在弹出的路径选择窗口中找到并选中刚才建的目录E:\pythonProject\Serial_Test_01点击OK2.2绑定专属隔离仓这一步相当于在 C 语言 IDE 里配置 Include 和 Lib 的环境变量告诉 PyCharm 去哪里找刚才下载的 PySide6点击左上角 File - 选择 Settings… (快捷键 CtrlAltS)。在弹出的设置窗口左侧菜单中找到并点击 Project: Serial_Test_01展开后点击 Python InterpreterPython 解释器。此时右侧会显示当前的解释器。点击解释器下拉框旁边的一个 小齿轮图标 ⚙️或者直接是 Add Interpreter 按钮选择 Add…。在弹出的窗口左侧选择 Virtualenv Environment虚拟环境。在右侧勾选 Existing environment现有环境。点击 Interpreter: 路径输入框最右侧的 …浏览按钮。在弹出的文件树里依次展开找到:\pythonProject\Serial_Test_01\venv\Scripts\python.exe选中它一路点击 OK 返回主界面。(此时PyCharm 底部的状态栏会花几秒钟扫描这个环境里的库扫描完就彻底绑定成功了。)2.3创建代码文件在 PyCharm 最左侧的 Project 文件树中右键点击最顶部的 Serial_Test_01 文件夹。选择 New - Python File在弹出的框里输入文件名main然后回车将以下代码输入进去注意以下代码是我专属硬件的数据格式发送代码需要根据实际的进行修改importsysimportnumpyasnpimportpyqtgraphaspgimportserialimportserial.tools.list_portsfromPySide6.QtWidgetsimport(QApplication,QWidget,QVBoxLayout,QHBoxLayout,QPushButton,QComboBox,QLabel,QSpinBox,QDoubleSpinBox,QGroupBox)fromPySide6.QtCoreimportQThread,Signal,QTimer# 硬件参数常量 (保持与原版一致)FS1000VREF4.033GAIN211RMS_WIN_LEN100# 100ms# # 1. 后台通信线程 (相当于下位机的中断服务函数)# classSerialThread(QThread):# 定义一个信号用于把解析好的数据发给主界面data_receivedSignal(float)def__init__(self):super().__init__()self.portNoneself.is_runningFalseself.is_recordingFalsedefopen_port(self,port_name):self.portserial.Serial(port_name,115200,timeout0.1)self.is_runningTrueself.start()# 启动死循环线程defclose_port(self):self.is_runningFalseself.wait()ifself.portandself.port.is_open:self.port.close()defsend_command(self,cmd_str):# 对应原版的 btnStart (b) 和 btnStop (s)ifself.portandself.port.is_open:self.port.write(cmd_str.encode(utf-8))defrun(self):# 串口数据读取与协议解析 (寻找 0xAA 和 0x55)bufferbytearray()whileself.is_running:ifself.port.in_waiting0:bufferself.port.read(self.port.in_waiting)whilelen(buffer)8:ifbuffer[0]0xAAandbuffer[7]0x55:# 提取 raw 数据并处理符号位raw(buffer[4]8)|buffer[5]ifraw32767:raw-65536# 电压转换公式 (保持原版逻辑)voltage_uv(raw/32768.0)*(VREF/GAIN)*1000000.0self.data_received.emit(voltage_uv)# 丢给主线程处理bufferbuffer[8:]else:buffer.pop(0)# # 2. 主界面UI与绘图逻辑# classMainWindow(QWidget):def__init__(self):super().__init__()self.setWindowTitle(ADS1191 肌电监控 (Python 高性能版))self.resize(1000,800)# 初始化数据缓存 (最长保存 10 秒数据)self.max_data_lenFS*10self.raw_datanp.zeros(self.max_data_len)self.rms_historynp.zeros(self.max_data_len//RMS_WIN_LEN)self.data_ptr0self.setup_ui()self.serial_threadSerialThread()self.serial_thread.data_received.connect(self.on_new_data)# 设置 50ms 定时刷新 UI (对应原版的 setInterval)self.ui_timerQTimer()self.ui_timer.timeout.connect(self.update_plots)defsetup_ui(self):main_layoutQVBoxLayout(self)# --- 顶部控制栏 ---ctrl_layoutQHBoxLayout()self.cb_portsQComboBox()self.refresh_ports()self.btn_connectQPushButton(连接串口)self.btn_closeQPushButton(关闭串口)self.btn_startQPushButton(开始 (发送b))self.btn_stopQPushButton(停止 (发送s))self.btn_close.setEnabled(False)self.btn_start.setEnabled(False)self.btn_stop.setEnabled(False)ctrl_layout.addWidget(QLabel(选择端口:))ctrl_layout.addWidget(self.cb_ports)ctrl_layout.addWidget(self.btn_connect)ctrl_layout.addWidget(self.btn_close)ctrl_layout.addWidget(self.btn_start)ctrl_layout.addWidget(self.btn_stop)ctrl_layout.addStretch()main_layout.addLayout(ctrl_layout)# --- 状态显示栏 ---stat_layoutQHBoxLayout()self.lbl_vppQLabel(VPP: 0.00 μV)self.lbl_rmsQLabel(RMS: 0.00 μV)stat_layout.addWidget(self.lbl_vpp)stat_layout.addWidget(self.lbl_rms)stat_layout.addStretch()main_layout.addLayout(stat_layout)# --- 三大波形显示区 ---pg.setConfigOptions(antialiasTrue)# 开启抗锯齿self.graph_layoutpg.GraphicsLayoutWidget()main_layout.addWidget(self.graph_layout)# 1. 时域图self.plot_timeself.graph_layout.addPlot(title时域波形 (μV))self.plot_time.showGrid(xTrue,yTrue,alpha0.3)self.curve_timeself.plot_time.plot(penpg.mkPen(#10b981,width1))self.graph_layout.nextRow()# 2. 频域图 (FFT)self.plot_freqself.graph_layout.addPlot(title频域分析 (FFT))self.plot_freq.showGrid(xTrue,yTrue,alpha0.3)self.curve_freqself.plot_freq.plot(penpg.mkPen(#3b82f6,width1),fillLevel0,brush(59,130,246,50))self.graph_layout.nextRow()# 3. RMS图self.plot_rmsself.graph_layout.addPlot(titleRMS 趋势)self.plot_rms.showGrid(xTrue,yTrue,alpha0.3)self.curve_rmsself.plot_rms.plot(penpg.mkPen(#ff00f2,width2))# 绑定按钮事件self.btn_connect.clicked.connect(self.connect_serial)self.btn_close.clicked.connect(self.close_serial)self.btn_start.clicked.connect(lambda:self.serial_thread.send_command(b))self.btn_stop.clicked.connect(lambda:self.serial_thread.send_command(s))defrefresh_ports(self):self.cb_ports.clear()portsserial.tools.list_ports.comports()forportinports:self.cb_ports.addItem(port.device)defconnect_serial(self):port_nameself.cb_ports.currentText()ifport_name:self.serial_thread.open_port(port_name)self.ui_timer.start(50)# 开启 UI 刷新定时器self.btn_connect.setEnabled(False)self.btn_close.setEnabled(True)self.btn_start.setEnabled(True)self.btn_stop.setEnabled(True)defclose_serial(self):self.ui_timer.stop()self.serial_thread.close_port()self.btn_connect.setEnabled(True)self.btn_close.setEnabled(False)self.btn_start.setEnabled(False)self.btn_stop.setEnabled(False)# 槽函数接收子线程发来的单一数据点defon_new_data(self,val):# 实际开发中这里会加入 scipy.signal.lfilter 的滤波逻辑# 这里为了演示核心骨架先原样存入数组并实现数据左移滚动self.raw_data[:-1]self.raw_data[1:]self.raw_data[-1]val self.data_ptr1# 定时器回调集中进行数学运算和 UI 刷新defupdate_plots(self):ifself.data_ptr0:return# 取最近 5 秒的数据进行显示计算display_win5*FS y_dataself.raw_data[-display_win:]x_datanp.linspace(0,5,display_win)# 1. 刷新时域图self.curve_time.setData(x_data,y_data)# 2. 计算并刷新 FFTfft_valsnp.abs(np.fft.rfft(y_data))/(display_win/2)fft_freqsnp.fft.rfftfreq(display_win,1.0/FS)self.curve_freq.setData(fft_freqs,fft_vals)self.plot_freq.setXRange(0,150)# 只看 0-150Hz 的有用频段# 3. 计算 RMS 并更新文本rms_datay_data[-RMS_WIN_LEN:]cur_rmsnp.sqrt(np.mean(rms_data**2))cur_vppnp.max(y_data)-np.min(y_data)self.lbl_vpp.setText(fVPP:{cur_vpp:.0f}μV)self.lbl_rms.setText(fRMS:{cur_rms:.1f}μV)# 【新增完整的 RMS 曲线刷新逻辑】 # 将最新的 RMS 值追加到历史数组中并把老数据挤出去左移self.rms_history[:-1]self.rms_history[1:]self.rms_history[-1]cur_rms# self.rms_history 的长度是 100对应 5 秒钟的跨度x_rmsnp.linspace(0,5,len(self.rms_history))# 刷新 RMS 图表self.curve_rms.setData(x_rms,self.rms_history)if__name____main__:appQApplication(sys.argv)windowMainWindow()window.show()sys.exit(app.exec())然后在代码空白处右键点击运行硬件连接好就可以看到实时波形显示了总结AI本身就是Python写的用AI再去写Python可以非常好用一定要列清楚需求显示什么数据长什么样如何拆包交给AI就可以快速生成一个上位机了AI也非常擅长写前端html但是Python有个巨大的好处是当代码工程量较大时html数据在多个模块中读取可能会出问题并且不适合操作复杂工程里较大的工程Python则可以用cursor里的Composer 功能阅读整个工程的所有文件。只需要在聊天框用中文输入“我要做一个上位机左边是串口配置右边是 Matplotlib 实时波形图用 PySide6 写”会瞬间帮你自动创建多个文件把界面和底层逻辑全部写好甚至直接把代码“应用”到你的文件中。

相关新闻