首页/新闻资讯/正文详情

Qt流程图编辑器设计:QGraphicsView与QUndoStack核心实践

发布时间:2026/11/3 18:00:29 来源:云帆数科 栏目:资讯中心
Qt流程图编辑器设计:QGraphicsView与QUndoStack核心实践
简介面向具备一定C基础的Qt桌面开发者这份基于Qt的流程图编辑器完整工程源码系统演示了自定义图形组件、拖拽交互、连线自动吸附、序列化保存与撤销重做等核心功能的实现思路。工程以QGraphicsView / QGraphicsItem框架为基础通过继承方式定制矩形节点、菱形与连线等图形元素重写paint()完成绘制并维护操作历史栈以支持Undo/Redo同时引入QDataStream实现流程图状态的保存与恢复整体代码组织清晰。资源共43个文件含14个cpp源文件、14个头文件、3个ui界面文件与6张png图标等源码与界面分离目录模块化压缩包仅68KB便于直接导入Qt Creator学习。目前已有1579人学习下载适合希望深入掌握Qt图形视图框架、并参考实战项目完成图形化编辑器开发的开发者。1. 为什么我会把流程图编辑器从 Web 迁回 Qt之前带团队做产线工位配置工具业务方每周都要改流程加一个质检节点、把某个工位的下游改到另一条线。最早用 Web 版拖拽流程图改一次要过服务端等三分钟是常事还要处理浏览器兼容后来我把这套编辑逻辑整体搬到 Qt 上用 Widgets 加 Graphics View 重写流程图编辑器不再依赖网络保存的 JSON 直接灌给产线执行引擎落地反而比预期顺利。基于 Qt 的流程图编辑器不新鲜但它正好卡在桌面工具和低代码平台之间——你要的不是一个画图工具而是一块能嵌进业务系统、能撤销、能序列化、能被 C 代码直接调用的画布。这篇按我做 MES 流程编排和通讯调试工具的路径写适合想给产线、算法编排、上位机做内嵌流程设计的 Qt 开发者新手能跟着把最小版本跑起来熟手能直接跳到避坑清单看边界。2. 先把图模型立住节点、连线与端口的数据结构决定编辑器上限很多人拿到流程图编辑器的第一个动作是往 scene 里addItem一个QGraphicsRectItem结果做到端口、属性、撤销时全部返工。我一般先写数据模型节点、连线、端口是三个独立对象视图层只是把它们画出来的投影。模型设计得越早后面序列化、校验、undo 越省事。2.1 节点不只是一个矩形让节点成为带业务字段的数据容器直接用QGraphicsItem存业务字段是最常见的返工点。节点类型不同需要的字段也不同启动节点没有上游执行节点有超时时间分支节点有条件表达式。为每一种节点建子类会把类爆炸而且QGraphicsItem不应该参与数据存储。我的做法是让每个节点都继承QObject再用一个QVariantMap存扩展属性。// FlowNode.h class FlowNode : public QObject { Q_OBJECT Q_PROPERTY(QString nodeId READ nodeId WRITE setNodeId) Q_PROPERTY(QString type READ type WRITE setType) public: explicit FlowNode(QObject *parent nullptr); QString nodeId() const { return m_nodeId; } void setNodeId(const QString id) { m_nodeId id; } QString type() const { return m_type; } void setType(const QString t) { m_type t; } QPointF pos() const { return m_pos; } void setPos(const QPointF p) { m_pos p; } void setProp(const QString key, const QVariant val); QVariant prop(const QString key) const; QVariantMap props() const { return m_props; } private: QString m_nodeId; QString m_type; QPointF m_pos; QVariantMap m_props; };选QObject而不是纯 struct是因为后面要做三件事第一属性系统配合Q_PROPERTY可以统一走 Qt 的序列化机制第二节点属性变化时能发信号视图层接到信号只刷新自身不需要在每次修改属性后手动调update()第三QUndoCommand里持有的是模型对象指针命令只管数据视图自动跟随。nodeId我统一用QUuid::createUuid().toString(QUuid::WithoutBraces)生成保证同一张图在不同机器上合并时不会撞 ID。type对应流程模板名真正的业务字段放进m_props例如{timeout: 3000, retry: 3}。这样以后新增字段不需要改类只改模板配置。2.2 连线和端口从“无脑连线”到“类型校验 防环检测”端口是节点上的连接锚点连线的合法性判断应该发生在模型层而不是让鼠标随便一拉就能连上。端口最少需要四个字段端口 ID、所属节点 ID、方向、数据类型。数据类型用来阻止把“整数”接到“图片”这种无意义连接。struct FlowPort { QString portId; QString nodeId; QString portName; int direction; // 0 输入, 1 输出 QString dataType; // any / int / bool / image }; class FlowEdge : public QObject { Q_OBJECT public: FlowEdge(FlowPort *src, FlowPort *dst, QObject *parent nullptr) : QObject(parent), m_srcPortId(src-portId), m_dstPortId(dst-portId) {} bool canConnect(FlowPort *src, FlowPort *dst) const { if (!src || !dst) return false; if (src-direction dst-direction) return false; // 不能输出对输出 if (src-nodeId dst-nodeId) return false; // 不能自己连自己 if (src-dataType ! any dst-dataType ! any src-dataType ! dst-dataType) return false; return !hasPath(dst, src); // 防成环 } private: bool hasPath(FlowPort *from, FlowPort *to) const; QString m_srcPortId; QString m_dstPortId; };canConnect里最后的hasPath是环路检测。MES 流程、算法编排这类场景执行引擎一旦遇到 A→B→A 的环会直接跑飞所以我在创建连线时先做一次可达性判断。注意这里我先用端口 ID 而不是节点 ID 做连接关系因为端口 ID 是唯一的序列化和加载时只靠两个字符串就能恢复连接。2.3 用 QUndoStack 把每一步编辑变成可撤销自己维护一个QVectorQByteArray保存快照图一大将卡到用户怀疑人生。Qt 自带的QUndoStack保存的是命令对象每个命令只记录“怎么改回去”不是整张图的副本内存占用小得多。节点增删这类命令模板如下class AddNodeCommand : public QUndoCommand { public: AddNodeCommand(FlowScene *scene, const QPointF pos, QUndoCommand *parent nullptr) : QUndoCommand(parent), m_scene(scene), m_pos(pos) {} void redo() override { auto *node new FlowNode; node-setNodeId(QUuid::createUuid().toString(QUuid::WithoutBraces)); node-setType(default); node-setPos(m_pos); m_nodeId node-nodeId(); m_scene-addNode(node); setText(添加节点); } void undo() override { m_scene-removeNode(m_nodeId); } private: FlowScene *m_scene; QPointF m_pos; QString m_nodeId; };关键参数是m_nodeId必须在redo()里生成并保存而不是构造函数里生成。因为QUndoCommand入栈后第一次执行走的是redo()如果 ID 在构造时固定命令行宏、重放、撤销后的 redo 都会复用同一个 ID两次插入同一 ID 会让场景里的节点映射表崩溃。命令只操作模型层视图层不感知撤销内容。FlowScene::addNode内部在m_nodes里插入节点同时创建一个QGraphicsItem并建立信号连接。这条信号链我建议这样写connect(node, FlowNode::propChanged, item, [item](const QString key, const QVariant val) { Q_UNUSED(key); Q_UNUSED(val); item-update(); });QUndoStack还有一个容易被忽略的参数undoLimit。默认无限制长时间运行会积累命令对象。我会在初始化时设stack-setUndoLimit(200)超过后最早的命令被丢弃避免内存无限增长。3. 用 QGraphicsView 搭画布缩放、平移、框选的最小配置数据模型建好后画布部分反而简单。流程图编辑器最核心的交互就是缩放、平移、框选、双击创建、连线。QGraphicsView 把这套交互的底层全部处理完剩下的只是参数配置和事件转发。3.1 为什么是 Graphics View 而不是 QPainter 硬画如果只是静态画几张图QPainter足够但编辑器要求鼠标点中某个节点时能拿到“这个 item”拖拽时能检测碰撞框选时能批量选中缩放时坐标自动换算。QGraphicsView内部维护了空间索引itemAt()、items()的拾取效率远高于自己遍历。这也是官方 Diagram Scene 示例选它的原因。不要被它的学习曲线吓退你真正要实现的类只有三个QGraphicsScene管模型层不受欢迎QGraphicsView管视口QGraphicsItem管节点和连线的绘制。3.2 最小可运行场景代码先搭一个能跑起来的最小工程#include QApplication #include QGraphicsScene #include QGraphicsView int main(int argc, char *argv[]) { QApplication app(argc, argv); auto *scene new QGraphicsScene; scene-setSceneRect(-500, -500, 1000, 1000); auto *view new QGraphicsView(scene); view-setRenderHint(QPainter::Antialiasing); view-setDragMode(QGraphicsView::RubberBandDrag); view-setTransformationAnchor(QGraphicsView::AnchorUnderMouse); view-setResizeAnchor(QGraphicsView::AnchorViewCenter); view-show(); return app.exec(); }setSceneRect决定可滚动区域不设置的话控件缩小时内容会被裁掉。Antialiasing必须开否则斜向连线会成为锯齿阶梯。RubberBandDrag让左键拖拽出矩形选区这是节点编辑器的基础操作。AnchorUnderMouse让滚轮缩放时鼠标指向的位置保持不动这是编辑器手感的关键。3.3 缩放锚点与中键平移的参数设置滚轮缩放不要直接乘以常数用指数函数让每一格滚动的增量感一致同时限制缩放范围void FlowView::wheelEvent(QWheelEvent *event) { const qreal currentZoom transform().m11(); if (event-angleDelta().y() 0 currentZoom 5.0) return; if (event-angleDelta().y() 0 currentZoom 0.1) return; qreal factor std::pow(1.0015, event-angleDelta().y()); scale(factor, factor); event-accept(); }transform().m11()是当前 X 轴缩放系数用m22()也可以只要视图没做错切两者相等。限制区间[0.1, 5.0]是我调出来比较舒服的范围缩得太小新手找不到节点放得太大绘制开销成倍增加。中键平移有个经典坑直接setDragMode(ScrollHandDrag)后左键拖拽也会变成平移节点永远拖不动。我一般只在按下中键时临时切换模式void FlowView::mousePressEvent(QMouseEvent *event) { if (event-button() Qt::MiddleButton) { setDragMode(QGraphicsView::ScrollHandDrag); QGraphicsView::mousePressEvent(event); return; } QGraphicsView::mousePressEvent(event); } void FlowView::mouseReleaseEvent(QMouseEvent *event) { if (event-button() Qt::MiddleButton) setDragMode(QGraphicsView::RubberBandDrag); QGraphicsView::mouseReleaseEvent(event); }注意不要在mousePressEvent里手写event-accept()然后不调用父类否则场景收不到事件节点拖拽会失灵。3.4 把双击、右键菜单和属性编辑串起来画布双击空白处创建一个新节点右键节点弹出菜单。双击事件要区分是否点到了 itemvoid FlowView::mouseDoubleClickEvent(QMouseEvent *event) { QGraphicsItem *item itemAt(event-pos()); if (!item) { QPointF scenePos mapToScene(event-pos()); emit createNodeRequested(scenePos); } else { QGraphicsView::mouseDoubleClickEvent(event); } }itemAt查到的可能是节点里的子 item所以创建节点信号只由空白区域触发。弹出的右键菜单我建议放在QGraphicsItem::contextMenuEvent里实现而不是视图层统一处理因为不同节点类型的菜单不同。节点绘制本身也要注意边界。boundingRect()返回的是 item 的局部坐标矩形绘制代码必须只在这个矩形内画否则会出现重影class NodeItem : public QGraphicsItem { public: QRectF boundingRect() const override { return QRectF(-60, -30, 120, 60); } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { Q_UNUSED(option); Q_UNUSED(widget); painter-setBrush(QColor(240, 244, 248)); painter-setPen(QPen(QColor(60, 96, 140), 1.5)); painter-drawRoundedRect(boundingRect(), 6, 6); } };paint里尽量不要动态new对象QPen、QBrush可以设为成员变量一次构造多处使用。Debug 和 Release 性能差异过大时十有八九是绘制函数里有频繁的堆分配。4. 把编辑器做成能交付的形态端口吸附、连线校验与 JSON 序列化画布能拖节点后最影响“像不像编辑器”的是连线体验和文件格式。连线要能吸附到端口非法连接要在松开鼠标前就给出反馈保存的文件要能被后端执行引擎直接解析而不是只能被自己的编辑器读。4.1 端口吸附拉一条线靠近端口时自动高亮连线交互的做法是在源端口按下鼠标创建一条临时QGraphicsLineItem鼠标移动时更新终点同时检测终点附近有没有可连接的端口检测到就把端口高亮松开鼠标时如果目标合法删除临时线并创建正式FlowEdge。void FlowView::mouseMoveEvent(QMouseEvent *event) { QPointF scenePos mapToScene(event-pos()); FlowPortItem *hoverPort scene-portAt(scenePos, 12.0); if (hoverPort m_currentPort ! hoverPort) { hoverPort-setHighlight(true); if (m_prevHoverPort) m_prevHoverPort-setHighlight(false); m_prevHoverPort hoverPort; } m_connectionLine-setLine(QLineF(m_startPos, scenePos)); QGraphicsView::mouseMoveEvent(event); }portAt的实现可以直接遍历场景所有FlowPortItem计算距离小于吸附半径的端口。吸附半径的单位是像素但mapToScene返回的是场景坐标所以判断时要换算qreal threshold 12.0 / transform().m11();。不换算的话放大 5 倍后吸附范围会变成 60 像素用户会感觉“还没靠近就被吸走了”。4.2 连线合法性检查类型不一致和成环连接都要拦在mouseReleaseEvent里拿到目标端口后调用FlowEdge::canConnect做最终校验。防环检测用 BFS 从目标端口反向找源端口能走到就说明新连线会造成环bool FlowEdge::hasPath(FlowPort *from, FlowPort *to) const { QSetQString visited; QQueueFlowPort * queue; queue.enqueue(from); while (!queue.isEmpty()) { FlowPort *cur queue.dequeue(); if (cur-portId to-portId) return true; visited.insert(cur-portId); const auto outs cur-outputs(); // 当前端口作为源的所有下游端口 for (FlowPort *next : outs) if (!visited.contains(next-portId)) queue.enqueue(next); } return false; }这里有个语义选择执行引擎拒绝环但状态机语义允许环。我不建议在canConnect里硬编码防环逻辑而是把校验器做成策略对象默认拒绝成环状态机编辑器则传入允许环的校验器。FlowScene持有校验器指针新增连线、加载文件时统一走同一个入口。4.3 序列化JSON 而不是 XML后端执行引擎直接吃Qt 对 JSON 的支持是原生的QJsonObject可以直接和QVariantMap互转这意味着节点的m_props可以直接落盘不需要手写字段映射。我的FlowScene::toJson长这样QJsonObject FlowScene::toJson() const { QJsonObject root; root[version] 1; QJsonArray nodesArr; for (const FlowNode *node : std::as_const(m_nodes)) { QJsonObject obj; obj[id] node-nodeId(); obj[type] node-type(); obj[x] node-pos().x(); obj[y] node-pos().y(); obj[props] QJsonObject::fromVariantMap(node-props()); nodesArr.append(obj); } root[nodes] nodesArr; QJsonArray edgesArr; for (const FlowEdge *edge : std::as_const(m_edges)) { QJsonObject obj; obj[src] edge-srcPortId(); obj[dst] edge-dstPortId(); edgesArr.append(obj); } root[edges] edgesArr; return root; }保存时用QSaveFile做原子写入避免写到一半崩溃把老文件也毁掉bool FlowScene::saveToFile(const QString path) { QSaveFile file(path); if (!file.open(QIODevice::WriteOnly)) return false; file.write(QJsonDocument(toJson()).toJson(QJsonDocument::Indented)); return file.commit(); }加载 JSON 时有一个必须做的校验边引用的端口 ID 必须真实存在。JSON 文件可能是人手改过的也可能从旧版本升级过来缺失端口就直接qWarning()并跳过不要让一个坏边拖垮整个场景。版本号字段也建议从一开始就写上后续格式变更时用版本号做迁移否则上线三个月后改格式只能拍大腿。4.4 导出图片和与其他模块的对接流程图编辑器通常要交付两种产物给后端执行的 JSON给人看的 PNG。导出图片用QGraphicsScene::render但有个坑导出前view如果有滚动条或者缩放render会按 sceneRect 推导的矩形导出实际结果经常被裁。安全做法QImage image(sceneRect.size().toSize(), QImage::Format_ARGB32_Premultiplied); QPainter p(image); scene-render(p, QRectF(image.rect()), scene-sceneRect()); p.end(); image.save(exportPath);scene-render的第二个参数是目标矩形第三个是源矩形。把目标矩形设成image.rect()源矩形设成sceneRect()就能保证整张图完整导出。导出前别忘了把所有 item 的选中状态清掉否则 PNG 里会带着蓝色选框。5. Qt 流程图编辑器避坑清单库版本、platform 插件和发布闪退这一部分是从实际部署和用户反馈里挑出来的高频问题。每条都是现象到原因到解决照着排查能省大半天。5.1 编译报错 “cannot mix incompatible Qt library version”现象用 Qt 5.15.2 开发程序一运行就打日志fatal: cannot mix incompatible Qt library (version 0x50e01) with this library (0x50c04)随后退出。原因编译器链接的库和运行时加载的库版本不一致。最常见的是机器上存在多个 Qt 安装目录系统 PATH 或LD_LIBRARY_PATH指向了另一个版本导致libQt5Core.so.5或Qt5Core.dll加载了两次不同版本。如果你在 Windows 上装了 MinGW 版的 Qt又额外装了 MSVC 版的 Qt两个安装目录同时在 PATH 里触发的概率极高。解决先用ldd 可执行文件 | grep Qt5看实际加载路径Windows 上用dumpbin /dependents 可执行文件.exe检查。然后在工程里显式指定 Qt 库路径运行时不要在 PATH 里混入第二个 Qt 的bin目录。最好的是用windeployqt或linuxdeployqt发布把依赖整理成相对路径。5.2 板子或工控机上报 “could not find the Qt platform plugin linuxfb”现象程序在开发电脑上正常拷到 Linux 工控机后启动报qt.qpa.plugin: could not find the Qt platform plugin linuxfb再在无桌面环境下运行直接黑屏退出。原因Qt 的平台插件没有被打包进部署目录。linuxfb是 Linux 无窗口系统下的 framebuffer 插件默认不在可执行文件同目录程序找不到platforms目录下的libqlinuxfb.so。解决把 Qt 安装目录下plugins/platforms整体拷到可执行文件旁边的platforms目录并设置环境变量QT_QPA_PLATFORM_PLUGIN_PATH指向它。临时测试可以执行export QT_QPA_PLATFORMlinuxfb启动但如果目标机器带 X11 或 Wayland这会让程序放弃 xcb 而走 framebuffer画面渲染会完全不同。正确做法是打包时带上所有平台插件让 Qt 按运行时环境自动选择。5.3 发布版闪退 “0xc0000005”节点和边的生命周期没管好现象Debug 构建一切正常Release 构建里一拖动节点就闪退Windows 事件查看器报0xc0000005访问冲突。原因QGraphicsItem被释放了两次或者持有了悬空指针。最常见的是removeNode里手动delete item而QGraphicsScene析构时又会删除 scene 里的所有 item另一个常见操作是把QGraphicsItem的 parent 设置成scene随后又手动 delete两次释放叠加。解决统一释放入口。所有节点删除都走FlowScene::removeNode这个函数内部同时清理节点连出的边再删除节点对象命令模式里的AddNodeCommand::undo也只能调用它。不要在removeItem之后再用旧指针去访问坐标。发布版对未定义行为的惩罚比 Debug 严格得多这类问题基本都指向“同一个资源两个释放点”。5.4 Qt 5.15.2 里嵌入 QChart 后画布缩放异常现象在流程图节点里嵌QChartView显示采集曲线鼠标滚轮缩放整个画布时图表跟着乱跳甚至 chart 内部曲线被拉伸变形。原因QChartView接收了滚轮事件且它的内部场景坐标与外部QGraphicsView的变换叠加。Qt Charts 在 5.15 版本里对ItemIgnoresTransformations的支持不完整直接给QGraphicsProxyWidget设这个标志会出现图表尺寸不变的副作用。解决最简单的做法是不在节点里放实时QChartView改为在节点paint里用QPainter绘制缩略曲线双击节点弹出独立QChart窗口。如果一定要内嵌就重写外部FlowView::wheelEvent当鼠标位置落在QGraphicsProxyWidget上时不执行缩放把事件转发给 chart。对流程编辑器来说节点内部放不必要的高频刷新图表是性能隐患优先拆出去。5.5 中文路径和文本加载乱码现象流程文件放在D:\生产数据\流程A\xxx.jsonQFile打开成功但QJsonDocument::fromJson返回空对象或 MSVC 编译的源码里写中文节点名程序里显示成乱码。原因Qt 5 的QFile打开文件后直接readAll()时文本流的编码依赖本地字节流而 JSON 文件保存的是 UTF-8。MSVC 环境下源文件的字符串字面量默认按本地代码页GBK解释和 UTF-8 冲突。解决读取文件用QTextStream显式指定编码QTextStream stream(file); stream.setCodec(UTF-8); QJsonObject root QJsonDocument::fromJson(stream.readAll().toUtf8()).object();MSVC 工程建议在.pro里加QMAKE_CXXFLAGS /utf-8或使用#pragma execution_character_set(utf-8)。路径里的中文一般不影响QFile真正会出错的是把QString直接当作const char*传给旧的 C 接口。流程图编辑器里所有文件操作统一走QSaveFile和QTextStream能避开大部分编码坑。6. 进阶把用户操作录成宏用性能数据说服自己6.1 用 QUndoStack 录制宏流程图编辑器经常有“插入一个分支”这类包含多个节点和连线的复合操作。手动按 CtrlZ 要把每一步都撤销一遍体验很差。QUndoStack::beginMacro可以把多个命令打包成一个撤销单位void FlowScene::insertBranch(const QPointF pos) { m_undoStack-beginMacro(插入分支节点); m_undoStack-push(new AddNodeCommand(this, pos QPointF(60, -30))); m_undoStack-push(new AddNodeCommand(this, pos QPointF(60, 30))); m_undoStack-push(new AddEdgeCommand(this, srcPortId, dstPortId1)); m_undoStack-push(new AddEdgeCommand(this, srcPortId, dstPortId2)); m_undoStack-endMacro(); }beginMacro之后的所有push都归入同一个宏只有生命周期对称才能正确让undo()反序执行内部命令。复合操作里如果一个命令失败后续命令要保证不执行否则宏内部会产生半成品状态。这个技巧用来做固定套路库非常有效把常用业务片段保存成模板一键插入整段流程。6.2 用 QElapsedTimer 验证性能编辑器做大后加载慢比操作卡更难排查。我用QElapsedTimer给加载流程计时并且只信 Release 构建的数据QElapsedTimer timer; timer.start(); scene-loadFromJson(json); qDebug() load ms: timer.elapsed();我给自己定的目标是100 个节点时加载少于 150ms1000 个节点时少于 800ms。如果加载超时大概率是在批量创建 item 时每创建一个都触发了视图更新。解法是批量操作前后包一层view-setUpdatesEnabled(false); // 批量创建节点和边 view-setUpdatesEnabled(true);个别情况下还要配合scene-blockSignals(true)因为节点属性变化信号会触发 item 重绘批量加载时这种信号风暴是性能杀手。6.3 一个习惯以前我做第一个流程图版本直接在QGraphicsRectItem上挂业务数据做 undo 时发现命令操作的是视图序列化时还要从视图反推数据改到自己都想删库。后来所有模型数据都放QObjectQGraphicsItem只做呈现命令只操作模型视图靠信号刷新这个习惯让后面所有新功能都变简单了。流程图编辑器的复杂度不在画布而在你愿不愿意在动手画矩形之前把模型层想清楚。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

Ghost为何装不了鸿蒙?镜像克隆、升级回退与开发板烧录全解析
Ghost为何装不了鸿蒙?镜像克隆、升级回退与开发板烧录全解析

Ghost 这个词在装机圈里流传了二十多年,几乎成了一切"一键还原系统"的代名词。所以当鸿蒙系统铺开之后,后台收到的提问里出现频率最高的一类就是:"鸿蒙系统能 ghost 吗""能不能给我的手机换成鸿蒙""鸿蒙系… · 2026/11/3 18:00:29

揭棋规则引擎开发实战:动态翻开机制与三层校验设计
揭棋规则引擎开发实战:动态翻开机制与三层校验设计

简介:这是一份面向Python初学者与AI入门学习者的揭棋(翻棋)游戏开发实践项目,聚焦策略型棋类AI实现与规则建模。资源以精简方式呈现揭棋核心逻辑——所有棋子初始朝下,玩家需通过翻子、移动与推理进行对抗,… · 2026/11/3 18:00:29

STM32F4电子时钟开发实战:RTC配置、显示驱动与掉电保存
STM32F4电子时钟开发实战:RTC配置、显示驱动与掉电保存

简介:STM32F4数码管电子时钟设计工程,面向嵌入式学习者与电子竞赛备赛者,提供了一个完整可编译的Keil工程。工程内重点围绕数码管动态扫描、定时器中断、实时时钟管理和按键调时等模块展开,覆盖74HC595/MAX7219驱动芯片配置、时间… · 2026/11/3 18:00:29

70亿参数重塑音频智能:Kimi-Audio开源模型开启多模态交互新纪元
70亿参数重塑音频智能:Kimi-Audio开源模型开启多模态交互新纪元

70亿参数重塑音频智能:Kimi-Audio开源模型开启多模态交互新纪元 【免费下载链接】Kimi-Audio-7B 我们推出 Kimi-Audio,一个在音频理解、生成与对话方面表现卓越的开源音频基础模型。本仓库提供 Kimi-Audio-7B 的模型检查点。 项目地址: https://ai.git… · 2026/11/3 18:44:26

项目02--JsonRpc
项目02--JsonRpc

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

210亿参数仅激活30亿:ERNIE-4.5轻量化模型如何重塑企业AI格局
210亿参数仅激活30亿:ERNIE-4.5轻量化模型如何重塑企业AI格局

210亿参数仅激活30亿:ERNIE-4.5轻量化模型如何重塑企业AI格局 导语:用1/3成本实现92%复杂推理准确率,百度ERNIE-4.5-21B-A3B-Thinking重新定义企业级AI效率标准 行业现状:大模型落地的"三重困境" 2025年全球AI市场呈… · 2026/11/3 18:43:23

字节跳动发布ByteFF-Pol模型:AI驱动分子模拟革新,重塑计算化学产业格局
字节跳动发布ByteFF-Pol模型:AI驱动分子模拟革新,重塑计算化学产业格局

字节跳动发布ByteFF-Pol模型:AI驱动分子模拟革新,重塑计算化学产业格局 【免费下载链接】byteff2 项目地址: https://ai.gitcode.com/hf_mirrors/ByteDance-Seed/byteff2 2025年11月10日,字节跳动正式推出基于图神经网络(… · 2026/11/3 18:43:23

如何用8GB显卡玩转4K视频生成:阿里WanVideo_comfy完整指南
如何用8GB显卡玩转4K视频生成:阿里WanVideo_comfy完整指南

如何用8GB显卡玩转4K视频生成:阿里WanVideo_comfy完整指南 在AI视频生成领域,专业工具普遍存在"高门槛、高成本、高复杂度"的三高问题,让普通创作者望而却步。阿里巴巴通义万相团队开源的WanVideo_comfy项目通过ComfyUI生态实现电… · 2026/11/3 18:43:23

WanVideo_comfy:8GB显存玩转电影级视频生成,ComfyUI生态终极解决方案
WanVideo_comfy:8GB显存玩转电影级视频生成,ComfyUI生态终极解决方案

WanVideo_comfy:8GB显存玩转电影级视频生成,ComfyUI生态终极解决方案 2025年AI视频生成领域迎来重大技术突破,阿里巴巴通义万相开源的WanVideo_comfy项目通过创新量化技术和模块化设计,将14B参数的大型模型压缩至消费级GPU可运行的… · 2026/11/3 18:43:23

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

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

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

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

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

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

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

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

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

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

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

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

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

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

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

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

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

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

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码