news 2026/10/9 21:41:44

用PyQt5打造轻量级数据库操作工具:从QSqlTableModel到SQLite实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用PyQt5打造轻量级数据库操作工具:从QSqlTableModel到SQLite实战

简介:一份基于Python PyQt5开发的数据库操作小工具源码,同时附带了SQLite数据库文件,面向正在学习PyQt5界面编程与sqlite3数据库交互的开发者,尤其适合需要轻量级桌面数据库管理场景的动手实践。包内共171个文件,压缩包大小12.48MB,主体包含19个Python源码、10个Qt Designer界面(ui)文件及10个cpp工程源文件,另有大量bmp图标资源用于完善界面展示,整体目录结构清晰,便于按模块阅读与二次开发。已有119人学习下载。源码中实现了数据库连接、增删改查等基础操作,并通过DatabaseManager类封装核心方法,配合try-except异常处理与事务提交逻辑,能够直观展示PyQt5界面层与数据库逻辑层的分工方式;包内db3数据库文件也可直接用于测试,省去建库步骤。对于想要快速上手PyQt5表单设计、信号槽绑定或多表操作的人来说,这套工程提供了可运行、可改造的完整参考,适合作为课设或内部小工具的基础模板。

1. 用PyQt5写数据库操作小工具,解决的不只是“命令看不清”的问题

用Python PyQt5写一个数据库操作小工具,几乎每个做后端或者运维的开发者都在某个阶段冒出过这个念头。最直接的触发场景一般是:测试环境有个临时库要改几百行数据,项目里没有现成的管理后台,重量级的数据库客户端对非专业同事又太沉,而命令行敲SQL确实不直观。于是干脆自己动手,做一个带界面的数据库操作小工具,把连接、查询、增删改查和结果展示都框在一个窗口里。这类项目通常还会附带一个SQLite数据库文件,拿到就能直接跑,源码结构清楚,核心代码三五十行就能点亮主窗口。适合谁?写过一点Python、想学PyQt5的初级开发者,以及需要一个轻量数据库操作界面替自己省事的全栈工程师,都能从这套方案里拿到自己想要的东西。

2. 先吃透PyQt5的SQL模块:为什么用QTableView + QSqlTableModel而不是手绘表格

2.1 三层结构一次说清:QSqlDatabase管连接、QSqlQuery管SQL、Model管数据

Qt的SQL模块设计得很规整,拆开看就是三个角色:QSqlDatabase负责建立和维护连接,QSqlQuery负责执行SQL语句,QSqlTableModel这类Model负责把数据表映射到界面组件上。新手最容易犯的错是一上来就操作QTableWidget,把数据一行一行塞进去,数据库一变化就得手动同步,工具稍微复杂一点就维护不动了。

QSqlDatabase.addDatabase("QSQLITE")是一个全局工厂方法,它按驱动名创建连接对象,默认连接可以直接通过无参的QSqlDatabase.database()拿回来。为什么用工厂而不是直接QSqlDatabase()?因为同一个进程里可能要同时开SQLite和MySQL两组连接,工厂模式允许给每个连接起名字,后续查询和释放都靠这个名字定位。看一个最小连接片段:

from PyQt5.QtSql import QSqlDatabase db = QSqlDatabase.addDatabase("QSQLITE") # 注册驱动并拿到连接实例 db.setDatabaseName("demo.db") # SQLite 对应文件路径 if not db.open(): print("连接失败:", db.lastError().text())

这段代码里有个细节值得养成习惯:open()失败一定要读db.lastError().text()。否则驱动版本不对、路径写错、文件被占用,你只能看到一个冷冰冰的False,排查效率极低。lastError()返回的是QSqlError对象,包含数据库驱动给出的原始错误文本,几乎所有QtSQL调用失败都能靠它定位问题。

2.2 QSqlTableModel的两种打开方式:绑定整表和自定义SQL填模型

QSqlTableModel是QtSQL模块里最实用的一个类,它把“表”这个概念直接映射成Model。只需要告诉它“连接哪张表”,它自己会生成SELECT语句、维护缓存、处理分页,界面绑上QTableView就能显示。这里有一个最常见的对比:QTableWidget是“手动填格子”,数据量小且固定时无所谓;QSqlTableModel是“表驱动”,修改完数据可以批量提交,也支持回滚。

import sys from PyQt5.QtWidgets import QApplication, QTableView from PyQt5.QtSql import QSqlDatabase, QSqlTableModel app = QApplication(sys.argv) db = QSqlDatabase.addDatabase("QSQLITE") db.setDatabaseName("demo.db") if not db.open(): sys.exit("数据库打开失败: " + db.lastError().text()) view = QTableView() model = QSqlTableModel() model.setTable("users") # 绑定 users 表 model.setEditStrategy(QSqlTableModel.OnManualSubmit) # 手动提交 model.select() # 真正执行 SELECT view.setModel(model) view.resize(800, 500) view.show() sys.exit(app.exec_())

setEditStrategy三个取值,按使用场景选:OnFieldChange是任意单元格改动立刻写库,适合单机小工具;OnRowChange是当前行切换时提交上一行,适合表格连续录入;OnManualSubmit是全部改动攒着,点保存按钮才提交,适合带确认界面的工具。我的习惯是工具类默认OnManualSubmit,用户误改还有后悔药,提交前能统一校验。

2.3 初始化数据库文件:DDL、种子数据和一个自动检查连接的小函数

配套的数据库文件不是凭空来的,工具在启动时最好自己检查表是否存在,不存在就建表、插种子数据。这样做的好处是任何人拿到源码和空的数据库文件都能一键跑起来,不用再手动执行建表脚本。下面是一个通用的初始化函数:

from PyQt5.QtSql import QSqlDatabase, QSqlQuery def init_database(db_path: str): db = QSqlDatabase.addDatabase("QSQLITE") db.setDatabaseName(db_path) if not db.open(): raise RuntimeError(db.lastError().text()) query = QSqlQuery(db) # 先查 sqlite_master,避免每次启动都重复建表 query.exec_("SELECT name FROM sqlite_master WHERE type='table' AND name='users'") if not query.next(): query.exec_(""" CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE, created_at TEXT DEFAULT (datetime('now', 'localtime')) ) """) query.prepare("INSERT INTO users (name, email) VALUES (?, ?)") for name, email in [("A同学", "a@example.com"), ("开发者B", "b@example.com")]: query.addBindValue(name) query.addBindValue(email) query.exec_() return db

注意exec_()末尾的下划线,PyQt5为了和Python关键字exec区分,给所有Qt方法加了下划线,这是PyQt5的正常写法。?占位符是SQLite的参数风格,配addBindValue按顺序绑定。为什么不直接拼字符串?种子数据和用户输入一样,都要防注入,习惯养成后写增删改查时不会踩拼接SQL的坑。datetime('now','localtime')取的是本地时区时间,不是UTC,国内工具用着更直观。

3. 把工具真正跑起来:主窗口布局、SQL查询面板与增删改查的完整代码

3.1 主窗口骨架:工具栏、状态栏和可拖拽的左右分栏

工具的主窗口我用QMainWindow,左侧放一个QTreeWidget列数据库里的表名,右侧放QTableView显示结果,中间用QSplitter隔开,用户可以自由拖动比例。顶部留一行查询面板,输入SQL后回车直接执行,这比把所有操作都埋在菜单里顺手得多。

from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTableView, QTreeWidget, QSplitter, QLineEdit, QPushButton, QToolBar, QStatusBar) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("数据库操作小工具") self.resize(1024, 680) # 查询面板 query_panel = QWidget() q_layout = QHBoxLayout(query_panel) self.sql_edit = QLineEdit() self.sql_edit.setPlaceholderText("输入 SQL 后回车执行") self.run_btn = QPushButton("执行") q_layout.addWidget(self.sql_edit, 1) q_layout.addWidget(self.run_btn) # 左侧表树 + 右侧结果表 splitter = QSplitter(Qt.Horizontal) self.table_tree = QTreeWidget() self.table_tree.setHeaderLabel("表") self.result_view = QTableView() splitter.addWidget(self.table_tree) splitter.addWidget(self.result_view) splitter.setStretchFactor(1, 1) # 右侧给更多空间 container = QWidget() outer = QVBoxLayout(container) outer.addWidget(query_panel) outer.addWidget(splitter, 1) self.setCentralWidget(container) toolbar = QToolBar("主工具栏") self.addToolBar(toolbar) self.statusBar().showMessage("就绪")

setStretchFactor(1, 1)是让结果表吃掉多出来的空间,左侧树保持默认宽度。工具栏在简化代码里没放按钮,实际使用时把“新增”“删除”“导出”塞进去,工具类软件最忌讳功能藏在菜单深层。

3.2 原生SQL查询:QLineEdit输入、QTableView输出,结果动态生成模型

查询面板是工具的核心入口。用户输入的可能是一条SELECT,也可能是UPDATE,所以不能像QSqlTableModel那样只做单选,而是用QSqlQuery执行原生SQL,动态生成一个QStandardItemModel来展示结果。这样无论查询语句多复杂,结果列数、列名都能跟着SQL走。

from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtSql import QSqlQuery from PyQt5.QtWidgets import QMessageBox def run_sql(self): sql = self.sql_edit.text().strip() if not sql: return query = QSqlQuery(self.db) if not query.exec_(sql): QMessageBox.critical(self, "SQL错误", query.lastError().text()) return model = QStandardItemModel() if query.isActive() and query.record().count() > 0: cols = query.record().count() # 动态取列名,不能用固定表头 headers = [query.record().fieldName(i) for i in range(cols)] model.setHorizontalHeaderLabels(headers) while query.next(): row = [QStandardItem(str(query.value(i) or "")) for i in range(cols)] model.appendRow(row) self.result_view.setModel(model) self.statusBar().showMessage(f"返回行数 / 影响行数: {query.numRowsAffected()}")

query.isActive()判断SQL是否产生了结果集,SELECT语句会返回True,UPDATE/DELETE虽然执行成功但record().count()为0,此时表格清空即可。query.value(i)返回的是QVariant,做一次str()包裹再塞给QStandardItem,能避免None值和类型转换导致显示异常。这个函数是工具里使用频率最高的路径,值得认真打磨。

3.3 增删改查走表单弹窗:prepared statement绑定参数,别拼字符串

在表格里直接编辑固然方便,但生产数据操作还是要有个确认动作。我用一个QDialog做表单弹窗,新增、编辑、删除都从这里走,减少误操作的概率。下面这段是新增用户的完整写法:

from PyQt5.QtWidgets import QDialog, QFormLayout, QLineEdit, QPushButton, QHBoxLayout class UserDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("新增用户") form = QFormLayout(self) self.name_edit = QLineEdit() self.email_edit = QLineEdit() form.addRow("姓名", self.name_edit) form.addRow("邮箱", self.email_edit) buttons = QHBoxLayout() save_btn = QPushButton("保存") cancel_btn = QPushButton("取消") buttons.addWidget(save_btn) buttons.addWidget(cancel_btn) form.addRow(buttons) save_btn.clicked.connect(self.accept) cancel_btn.clicked.connect(self.reject) def add_user(self): dlg = UserDialog(self) if dlg.exec_() != QDialog.Accepted: return query = QSqlQuery(self.db) query.prepare("INSERT INTO users(name, email) VALUES (?, ?)") query.addBindValue(dlg.name_edit.text().strip()) query.addBindValue(dlg.email_edit.text().strip()) if not query.exec_(): QMessageBox.warning(self, "插入失败", query.lastError().text()) self.refresh_model()

prepare()会让数据库提前编译SQL,addBindValue()按顺序把用户输入填进占位符,全程没有字符串拼接,SQL注入在根源上被堵死。更新操作只是把SQL换成UPDATE users SET name=?, email=? WHERE id=?,再补一个addBindValue(id),逻辑完全一致。

3.4 刷新模型和状态栏同步:每次操作后让界面说真话

工具做过增删改之后,界面上要有即时反馈。我写了一个refresh_model(),每次提交成功后调用它重刷表格和状态栏。这里有个关键细节:QTableView绑定的Model如果被替换,旧Model会被释放,所以不能只调select(),要看当前到底绑的是哪个Model再决定刷新逻辑。

def refresh_model(self, table_name: str = "users"): self.model = QSqlTableModel(self, self.db) self.model.setTable(table_name) self.model.setEditStrategy(QSqlTableModel.OnManualSubmit) self.model.select() self.result_view.setModel(self.model) self.statusBar().showMessage(f"{table_name} 表共 {self.model.rowCount()} 行")

如果SQL查询面板刚执行过一条自定义SQL,此时结果区绑的是QStandardItemModel,再调refresh_model就会把它换成表模型。这是设计取舍:原生SQL查询和表模型操作共用一块显示区,必然有一个覆盖的关系,只要每次操作后都主动刷新,用户看到的就是最新状态。状态栏同步很关键,它让用户确认“刚才那条更新到底影响了几行”。

4. 数据库连接参数与事务策略:一份连接配置,既跑SQLite也接MySQL

4.1 各数据库驱动的连接参数对照与什么时候用SQLite

PyQt5的SQL模块抽象做得比较统一,换数据库基本就是换驱动名和几个参数。我整理过一份对照表,工具里直接用代码判断:

数据库驱动名关键参数注意点
SQLiteQSQLITEsetDatabaseName(文件路径)不需要主机和端口,文件即库
MySQL / MariaDBQMYSQLsetHostName / setPort / setDatabaseName / setUserName / setPasswordQt安装包可能没带mysql插件
PostgreSQLQPSQL同上驱动通常需要单独编译
ODBC数据源QODBCsetDatabaseName(DSN名)适合接SQL Server等

工具默认SQLite,理由很实际:零配置,一个文件拷走就能跑,适合“源码+数据库”这种交付形态。但代码不能写死,所以我封装了一个连接函数,靠驱动名切换:

def connect_db(driver: str, params: dict): conn_name = params.get("conn_name", "main_conn") db = QSqlDatabase.addDatabase(driver, conn_name) if driver == "QSQLITE": db.setDatabaseName(params["db_file"]) else: db.setHostName(params["host"]) db.setPort(params["port"]) db.setDatabaseName(params["dbname"]) db.setUserName(params["user"]) db.setPassword(params["password"]) if not db.open(): raise RuntimeError(db.lastError().text()) return db

给连接起名字的习惯值得养成。addDatabase不传连接名时用的是默认连接,一旦窗口或线程里开了第二个连接,默认连接容易张冠李戴。带名字的连接在释放时也更明确:QSqlDatabase.removeDatabase(conn_name)。

4.2 批量写入一万行:事务提交节奏为什么是“500条一提交”

默认情况下,每一条SQL执行完就自动提交了,这在批量写入时是灾难。一万行INSERT如果每条都独立事务,磁盘同步的损耗会让写入慢得不可接受。解决办法是用数据库事务包住,分批提交。我这里用500条一提交,兼顾内存占用和失败回滚粒度:

def batch_insert(db, total=10000, batch=500): db.transaction() # 显式开启事务 query = QSqlQuery(db) query.prepare("INSERT INTO logs(msg, created_at) VALUES (?, datetime('now'))") for i in range(total): query.addBindValue(f"log-{i}") if not query.exec_(): db.rollback() # 出错回滚整批 raise RuntimeError(query.lastError().text()) if i > 0 and i % batch == 0: db.commit() # 每500条落一次盘 db.transaction() # 开启下一批事务 db.commit()

为什么不是每条提交或一万条一次性提交?每条提交慢,一万条一次性提交会占住事务很久,SQLite可能出现database is locked,MySQL等大库也可能锁竞争变激烈。500条是一个折中,回滚时最多损失500条,执行时间也稳定。事务中间一旦出现错误,rollback()把当前批次全部撤销,避免半截数据入库。

4.3 连接生命周期:不要随手removeDatabase

连接生命周期管理是工具里最容易被忽视的部分。主窗口单线程跑着一条长连接,程序退出时先close()再removeDatabase(),看似简单,但顺序错了会看到一个奇怪的警告:QSqlDatabasePrivate::removeDatabase: connection ... is still in use。原因通常是连接对象还有其他引用活着,比如QSqlQuery或QSqlTableModel还在用它。

def teardown(self): self.model = None # 先释放 Model 对连接的引用 self.db.close() # 再关闭连接 conn_name = self.db.connectionName() del self.db # 解除 Python 侧引用 QSqlDatabase.removeDatabase(conn_name)

顺序不能反。QSqlQuery如果不传连接参数,会自动从默认连接取,如果那个连接恰好被移除,后续查询就会在运行时崩。工具里我坚持所有QSqlQuery(db)都显式传入连接对象,一是线程安全,二是代码清楚,查问题时不至于猜数据从哪个连接来的。

5. PyQt5开发数据库工具避坑记录:驱动加载失败、表格不刷新、SQLite锁都在这里

5.1 QSQLITE driver not loaded:打包后最容易踩的雷

现象:源码环境跑得好好的,用PyInstaller打包成exe后,双击运行报QSqlDatabase: QSQLITE driver not loaded。原因:PyInstaller默认不会把PyQt5的sqldrivers插件目录完整收进包,运行时Qt找不到驱动。解决:打包时显式加上插件目录,Windows下用分号分隔源和目标:

pyinstaller --windowed --name=DBTool \ --add-data "PyQt5/Qt/plugins/sqldrivers;PyQt5/Qt/plugins/sqldrivers" main.py

Linux/macOS要把分号换成冒号。还有一种情况是本地开发环境本身没装全PyQt5,只装了PyQt5-Qt5的子集,重装完整PyQt5即可。

5.2 编辑完表格点保存,数据却“消失”了

现象:表格里改了内容,点保存按钮后表格变成了原来那几行,改动全部不见。原因:setEditStrategy(QSqlTableModel.OnManualSubmit)模式下,改动只存在Model的内存缓存里,没调submitAll()就直接调了select(),select()用数据库最新数据把缓存覆盖了。解决:保存时先提交再刷新:

if not self.model.submitAll(): QMessageBox.warning(self, "保存失败", self.model.lastError().text()) else: self.model.select()

注意lastError()要从Model上取,不要从QSqlQuery上取,两者错误状态不是同一个。

5.3 database is locked:谁把SQLite文件锁住了

现象:工具里执行批量更新时报database is locked,表现时好时坏。原因:同一时间有另一个连接正在写同一个SQLite文件,或者自己的事务开着没提交就继续执行下一条写操作。SQLite的写锁粒度是整个文件,长事务最危险。解决:事务包小包短,及时commit();连接建立后执行PRAGMA busy_timeout=3000,让SQLite等锁最多3秒:

query = QSqlQuery(db) query.exec_("PRAGMA busy_timeout=3000") query.exec_("PRAGMA journal_mode=WAL")

journal_mode=WAL把写并发能力提一档,工具场景几乎必开。

5.4 select()之后表格不刷新,新数据看不见

现象:Insert成功后调了model.select(),界面还是旧数据。原因:select()执行失败被忽略了,或者setTable()之后只执行过一次select(),后续插入的数据没触发模型的数据变化信号。解决:先检查model.lastError(),SQLite在AUTOINCREMENT表上执行批量插入时,如果事务没提交,外面查不到是正常的,优先怀疑事务没结束。另外,setTable()后必须重新select(),光是setTable不会加载数据。

5.5 中文路径和中文表名引发的玄学问题

现象:数据库文件放在D:\数据\demo.db时,工具时好时坏,打包后经常打不开库;表名带中文也偶发查询失败。原因:大部分是Python 3的字符串编码和Windows控制台或文件系统编码不一致,还有少量是SQL拼接时中文没加引号。解决:文件路径全部用pathlib.Path处理,不要手动拼字符串;表名和字段名在SQL里用反引号包起来,如SELECT * FROM用户表``。中文乱码一旦出现,先确认db.setDatabaseName()传的是str而不是bytes,encode()操作在这里只会添乱。

6. 从能用变得好用:PyInstaller打包、定时巡检和导出CSV的三种能力

6.1 PyInstaller打包参数:windowed隐藏控制台并带上SQL驱动

工具给自己用可以跑源码,给别人用就得打包。我常用的打包命令是:

pyinstaller --windowed --name=DBTool --add-data "PyQt5/Qt/plugins/sqldrivers;PyQt5/Qt/plugins/sqldrivers" main.py

--windowed隐藏控制台窗口,避免U盘拷走的工具在别人桌面上弹黑色命令框吓人。--add-data把sqldrivers目录打进包,解决5.1里的驱动缺失。打包时间会有点长,几十秒到几分钟都正常,体积在几十MB上下,PyQt5的应用这个体积是正常水平。

6.2 定时巡检:让工具自动查一次表并弹窗提醒

工具能定时跑SQL,就比普通数据库客户端多了一层“巡检”能力。用QTimer在后台轮询,发现异常数据弹窗提醒。注意QTimer的回调跑在主线程,SQL查询要快,别在槽函数里做重型统计。

from PyQt5.QtCore import QTimer self.timer = QTimer(self) self.timer.timeout.connect(self.check_alert) self.timer.start(60000) # 每 60000 毫秒即每分钟跑一次 def check_alert(self): query = QSqlQuery(self.db) query.exec_("SELECT COUNT(*) FROM alerts WHERE status='pending'") if query.next() and query.value(0) > 0: QMessageBox.warning(self, "巡检提醒", f"有 {query.value(0)} 条待处理提醒")

凌晨跑批后看看有没有失败记录,或者盯着一张业务表的数据量有没有突增,这种小功能在运维场景里相当实用,代码量却只有几行。

6.3 导出CSV数据:不引第三方库也能把结果留下

导出功能是工具最后的体面。用Python标准库csv就能完成,不引入openpyxl这样的大依赖。重点是编码用utf-8-sig,Excel打开才不乱码:

import csv def export_csv(self, file_path: str): query = QSqlQuery(self.db) query.exec_("SELECT * FROM users") with open(file_path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) cols = query.record().count() headers = [query.record().fieldName(i) for i in range(cols)] writer.writerow(headers) while query.next(): writer.writerow([query.value(i) for i in range(cols)])

如果哪天需要多sheet、单元格合并或者自定义样式,再换openpyxl不迟,CSV足够对付90%的交付场景。我的习惯是每次导出前先弹一个文件选择框让用户确认路径,而不是默认存到程序目录,导出完了顺手在状态栏显示“已导出N行”。

这个工具做到这里已经能覆盖日常需求了。回看整个方案,核心就一句话:QSqlTableModel管表、QSqlQuery管SQL、QTimer管巡检,三层各司其职。踩过的坑集中在驱动打包、事务提交时机和SQLite锁这三块,每次遇到我就先往这三个方向排查,省掉一大半瞎折腾的时间。有人问为什么不直接用现成的数据库管理软件,对这个场景来说,自己写的工具能按自己的操作习惯定制,还能把业务校验逻辑融进去,这本身就是价值。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 21:39:36

Godot引擎移植鸿蒙PC:跨生态适配的技术断层与分阶段实践

1. 项目概述:这不是一次简单的“移植”,而是一场跨生态的系统级适配Godot 游戏编辑器移植鸿蒙 PC——光看标题,很多人第一反应是“不就是换个平台编译一下?”但我在游戏引擎底层开发和跨平台工具链打磨上干了十多年,亲…

作者头像 李华
网站建设 2026/10/9 21:39:21

HiClaw 开源版本地安装:5 分钟跑通 OpenClaw 团队协作

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/9 21:35:55

低功耗电源管理实战:PCA9422+ATmega32替代分立器件方案

最近在跟进一个低功耗采集终端的电源部分,原来的方案其实是“散件堆叠”:一颗 LDO 稳 3.3V,一颗升降压给通信模块,再用 GPIO 控制负载开关。MCU 用的是 ATmega32,控制逻辑不算复杂。可真把板子拿回来实测,问…

作者头像 李华
网站建设 2026/10/9 21:35:50

Evolver 与 Hermess Agent 实战:把 endpoint 改到 TaoToken 的配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/9 21:34:34

极简命名与状态闭环:用七个状态位构建可复现的自动化流程

1. 从一串“1”说起:这个项目到底在做什么第一次看到“1111111”这个标题,我脑子里蹦出来的第一个念头是:这要么是随手敲的占位符,要么就是一个刻意用极简符号命名的项目。做过几年项目的人都知道,真正被反复打磨的东西…

作者头像 李华
网站建设 2026/10/9 21:33:32

数学建模获奖论文复现指南:从模型拆解到Python代码实现

简介:这份资源是MathorCup高校数学建模挑战赛第八届特等奖论文(题号C4028),面向备战数模竞赛的高校学生与指导教师,聚焦陆基导弹打击航母的数学建模与算法设计这一典型军事运筹问题。压缩包内仅含1个PDF文件&#xff0…

作者头像 李华