Qt Model/View自定义代理实战:从状态标签到操作按钮

说点我自己的经验。最近做一个数据监控面板,表格部分的要求相当刁钻:同一列里既要有状态色块,又要有进度条,操作列还要放按钮,双击单元格弹出来的还得是自定义编辑器。刚开始我图省事,想靠 QTableView 原生能力硬凑,结果怎么调怎么别扭,直到静下心把 Model/View 里的自定义代理(Delegate)彻底过了一遍,才把这套逻辑真正理顺。今天就用这个项目里的一段实践,聊聊“自定义代理”在 Model/View 模型视图里到底扮演什么角色,以及一个能扛住真实业务场景的代理该怎么设计和落地。

这个主题适合所有正在用 Model/View 架构做表格、列表、树形控件的开发者,尤其是发现“默认显示文本、默认双击编辑”满足不了业务需求,又不想把视图类重写得面目全非的朋友。哪怕你只是刚接触 Qt,只要理解了代理的职责,后面写业务代码会顺手很多。

1. Model/View中的“代理”到底在解决什么问题

1.1 模型、视图、代理三方职责

先说个最容易混淆的点。Model/View 框架刚上手的时候,很多人以为只需要“模型提供数据,视图负责显示”就够了,等到需要自定义单元格内容时才发现,视图根本不该管“怎么画一个单元格”这种事。

实际上 Model/View 把职责拆成了三层:

  • 模型(Model)只管数据本身,包括行列结构、角色数据、增删改查。
  • 视图(View)只管布局、滚动、选区、编辑触发方式这些宏观交互。
  • 代理(Delegate)管的是“单元格级别的显示和编辑细节”。

用生活里的类比:模型像是仓库里的货架系统,视图是商场的陈列区,代理则是负责具体货架上的摆货员——把同一件商品摆成普通价格牌、促销标签、还是带扫码按钮的交互卡片,都归他管。

这种拆分的好处是:数据层完全不知道界面长什么样,视图层不需要关心某个单元格到底渲染成文本还是按钮,代理可以单独复用、单独替换。你甚至可以让同一份数据在普通表格里显示成文本,在另一个界面里显示成图表。

1.2 什么时候值得自己写代理

原生代理(默认行为)实际上已经能完成不少事:文本显示、图标、复选框、双击编辑普通文本。但以下几类情况,默认行为几乎一定撑不住:

  • 想让单元格按数值大小显示不同背景色或阈值颜色。
  • 想显示进度条、星级评分、状态徽标、缩略图。
  • 想给单元格里放一个“操作按钮”,点击后触发业务动作。
  • 想为特定列提供自定义编辑器,比如下拉框、日期选择、颜色选择,或者带多个子控件的复合编辑器。
  • 想统一全局的绘制风格,不想在业务代码里到处写样式表。

在这些场景下,自写代理几乎是标准解法。而且代理是继承关系,你可以做一个通用代理给整张表用,再用 setItemDelegateForColumn 单独给某一列指定更特殊的代理,两种方式还能叠加。

1.3 从QStyledItemDelegate还是QItemDelegate开始

刚开始写代理时最常踩的坑就是基类选错。QItemDelegate 是老一代实现,QStyledItemDelegate 会主动使用当前视图的样式表(QStyle),在配合 QSS、高分辨率缩放、主题切换时表现更稳定。

所以我的建议很简单:新代码一律从 QStyledItemDelegate 继承。只有当你需要彻底脱离样式表、完全自定义绘制逻辑并且不想被全局样式干扰时,才考虑 QItemDelegate。

2. 自定义代理的核心机制拆解

2.1 必须从重写这些虚函数开始

一个完整的自定义代理,核心入口并不神秘,主要就是下面几个虚函数:

虚函数作用典型重写时机
paint()负责绘制单元格内容基本上所有自定义代理都要重写
sizeHint()返回单元格建议尺寸内容高度宽度比较特殊,或需要留边距时
createEditor()创建编辑器控件需要自定义编辑器时
setEditorData()把模型数据灌入编辑器配合 createEditor
setModelData()把编辑器内容写回模型配合 createEditor
updateEditorGeometry()设置编辑器在视图里的几何位置编辑器需要特殊位置时
editorEvent()在编辑器创建前拦截鼠标/键盘事件处理按钮点击、复选框状态变化、拖拽类交互

很多人一上来就盯着 paint 狂写,结果编辑器相关的方法一个不重写,等用户双击单元格时会发现编辑器还是默认的那几个类型。记住,代理负责的是“显示”和“编辑”两半,缺一半就不完整。

2.2 paint:把单元格画成你想要的样子

paint 是整个代理里信息量最大的函数。它的签名通常是:

void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

这里有两个最容易忽略的点。第一,QStyleOptionViewItem 里带了你几乎需要的所有状态:索引数据、选区状态、鼠标悬停状态、焦点状态、启用状态、文本对齐方式、调色板、字体等等。你不需要自己去问视图“当前选中没有”,直接读 option.state 就行。

第二,很多人在自定义绘制之前忘了调用基类的 paint 来画背景,导致选中单元格的背景永远是白的。正确处理方式是先让基类把背景处理掉,再在上面叠加自己的内容:

void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); // 先交给基类绘制选中背景、焦点框等基础内容 QStyledItemDelegate::paint(painter, opt, index); // 再根据业务角色画自己的内容 int value = index.data(Qt::UserRole + 1).toInt(); // ... 绘制逻辑 }

这里还有一个细节:initStyleOption 会把模型里所有标准角色(比如 Qt::DisplayRole、Qt::ForegroundRole、Qt::FontRole)填充到 option 里,如果你不调用它,很多样式信息会丢失。

2.3 createEditor与数据回写

当用户触发编辑(比如双击或按 F2),视图会调用 createEditor 创建编辑器。这里要注意两点:

编辑器是“按需创建”的,不是表格初始化时一次性建好几百个控件。这样能大幅减少控件数量和内存占用。

常见需求里,某个列的下拉框需要根据当前行的类型动态加载选项,createEditor 里的 index 可以帮你获取当前行的数据,从而动态填充。

数据回写的链路也很关键:

void setEditorData(QWidget *editor, const QModelIndex &index) const override { // 编辑器初始化:例如把模型里的值设置到 QSpinBox QSpinBox *spin = qobject_cast<QSpinBox*>(editor); if (spin) { spin->setValue(index.data(Qt::EditRole).toInt()); } } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { QSpinBox *spin = qobject_cast<QSpinBox*>(editor); if (spin) { model->setData(index, spin->value(), Qt::EditRole); } }

很多人只写了 createEditor,忘记 setEditorData,编辑器打开后显示的数据就是空的;只写了 setEditorData 忘记 setModelData,用户改完数据模型没变化。这三个函数必须配套。

2.4 editorEvent:在编辑器开启之前拦截事件

editorEvent 是代理里最容易被忽略但实际很实用的函数。典型场景:一个“操作”列,不需要真正的编辑器,用户单击单元格里的按钮就应该触发业务逻辑。如果你只重写 paint 画了按钮样式,不处理 mouse 事件,点击就没反应。

editorEvent 的流程大致是:鼠标或键盘事件先经过视图,视图把事件分发给当前代理,让代理有机会处理。代理收到事件后,可以用 option.rect 判断事件坐标是否落在某个区域,然后决定是启动编辑、切换状态、还是发业务信号。

例如处理单元格内的一个“下载”按钮:

bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override { if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent *me = static_cast<QMouseEvent*>(event); if (option.rect.adjusted(0, 0, -10, 0).contains(me->pos())) { // 模拟一次数据更新,实际业务里可以发 signal 出来 model->setData(index, QVariant(true), Qt::UserRole + 3); return true; } } return QStyledItemDelegate::editorEvent(event, model, option, index); }

这个函数处理完事件后,视图就不会再继续默认的编辑流程,所以返回 true 很重要。

3. 实操:自己动手做一个多功能代理

3.1 项目场景与需求原型

我在那个数据监控面板里,表结构大概是:设备名称、运行状态、任务进度、最后更新时间、操作。设备名称是普通文本;运行状态要显示成红黄绿状态点加文字;任务进度要显示成进度条;操作列要放“查看”“重试”两个按钮。

这个需求如果全靠默认代理,运行状态和进度条完全没法看,操作列更是只能靠编辑模式硬凑。所以我的做法是写一个继承自 QStyledItemDelegate 的通用代理类,在 paint 和 editorEvent 里按列索引分流处理。

3.2 第一版:实现状态标签列

状态列的核心是画一个圆点加文本。圆点的颜色根据数据值决定,文本内容仍然从标准 DisplayRole 拿。

void PaintDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); QStyledItemDelegate::paint(painter, opt, index); if (index.column() == StatusColumn) { int status = index.data(Qt::UserRole + 1).toInt(); QColor dotColor = statusColor(status); // 根据状态值返回颜色 QRect dotRect = opt.rect.adjusted(8, 0, -8, 0); QRect textRect = opt.rect.adjusted(28, 0, -8, 0); painter->save(); painter->setRenderHint(QPainter::Antialiasing); painter->setPen(Qt::NoPen); painter->setBrush(dotColor); int dotSize = 10; painter->drawEllipse(dotRect.x(), dotRect.center().y() - dotSize / 2, dotSize, dotSize); painter->setPen(opt.palette.text().color()); painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, index.data(Qt::DisplayRole).toString()); painter->restore(); } }

这里用了 painter->save/restore 来保护绘制状态,避免影响后续单元格的绘制。颜色映射函数根据业务状态返回固定颜色,不会因为系统主题变化而乱掉。

3.3 第二版:实现进度条列

进度条列需要在单元格里画一个背景条和一个填充条。我会顺便加一个文本百分比,同时用 option.rect 计算内边距,保证任何列宽下都好看。

void PaintDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { // 先画基础背景 QStyleOptionViewItem opt = option; initStyleOption(&opt, index); QStyledItemDelegate::paint(painter, opt, index); if (index.column() == ProgressColumn) { double progress = index.data(Qt::UserRole + 2).toDouble(); progress = qBound(0.0, progress, 1.0); QRect progressRect = opt.rect.adjusted(4, 8, -4, -8); int x = progressRect.x(); int y = progressRect.y(); int w = progressRect.width(); int h = progressRect.height(); painter->save(); painter->setRenderHint(QPainter::Antialiasing); // 背景槽 painter->setBrush(opt.palette.midlight()); painter->setPen(Qt::NoPen); painter->drawRoundedRect(progressRect, h / 2.0, h / 2.0); // 进度填充 int fillW = qRound(w * progress); QRectF fillRect(x, y, fillW, h); painter->setBrush(QColor("#4C9BE8")); painter->drawRoundedRect(fillRect, h / 2.0, h / 2.0); // 百分数文本 painter->setPen(opt.palette.text().color()); painter->drawText(progressRect, Qt::AlignCenter, QString::number(progress * 100, 'f', 1) + QStringLiteral("%")); painter->restore(); } }

做进度条代理时有个值得注意的点:不要直接 new 一个 QProgressBar 放在单元格上。在表格里成百上千行、不断滚动的情况下,为每个单元格塞控件会直接把界面卡死。绘制式进度条只是画几圈圆角矩形和文本,开销非常低。

3.4 第三版:操作按钮列与点击处理

操作列是两个按钮,在 paint 里分别计算两个按钮的几何区域,然后在 editorEvent 里根据鼠标点击的区域判断哪个按钮被按了。

void PaintDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (index.column() != ActionColumn) { QStyledItemDelegate::paint(painter, option, index); return; } painter->save(); QRect area = option.rect.adjusted(4, 4, -4, -4); QRect viewBtnRect = area.adjusted(0, 0, -6, 0); QRect retryBtnRect = area.adjusted(viewBtnRect.width() + 6, 0, 0, 0); // 画“查看”按钮 drawButton(painter, viewBtnRect, QStringLiteral("查看"), option); // 画“重试”按钮 drawButton(painter, retryBtnRect, QStringLiteral("重试"), option); painter->restore(); }

画按钮时我建议直接用 QStyle 的 drawControl,这样才能和系统风格保持一致性。最简单的做法是构造一个 QStyleOptionButton,然后调用 style()->drawControl(QStyle::CE_PushButton, &btnOption, painter, nullptr)。

editorEvent 里的事件判断逻辑:

bool PaintDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override { if (index.column() == ActionColumn && event->type() == QEvent::MouseButtonRelease) { QMouseEvent *me = static_cast<QMouseEvent*>(event); QRect area = option.rect.adjusted(4, 4, -4, -4); QRect viewBtnRect = area.adjusted(0, 0, -6, 0); QRect retryBtnRect = area.adjusted(viewBtnRect.width() + 6, 0, 0, 0); if (viewBtnRect.contains(me->pos())) { // 发信号或直接更新模型,业务侧自行处理 emit viewClicked(index); return true; } else if (retryBtnRect.contains(me->pos())) { emit retryClicked(index); return true; } } return QStyledItemDelegate::editorEvent(event, model, option, index); }

有个小坑是:如果只监听 MouseButtonPress,用户点击后界面会卡在“按下未松开”的状态,回掉逻辑又执行,容易造成重复触发。所以我一般监听 MouseButtonRelease,至少保证按下和松开是一个完整动作。

3.5 把代理挂载到视图

代理写好后,挂载方式有三种层次:

  • 给整张表设置:table->setItemDelegate(delegate);
  • 单独给某一列设置:table->setItemDelegateForColumn(操作列索引, delegate);
  • 给某个行或某个索引临时设置:用 QAbstractItemView 的信号配合动态替换代理。

实际项目中我往往先做一个最通用的代理实例给整张表,然后在内部用 column 分流。如果业务复杂到不同列差异极大,再拆成多个代理类分别挂载,这样维护起来比较清爽。

4. 性能、响应与样式细节

4.1 绘制式代理与控件式编辑器的性能对比

很多初学者会把“自定义编辑器”和“自定义绘制”混在一起,以为每个单元格都是控件。这是性能问题的根源。

  • 绘制式代理只在 paint 的时候算一次绘制指令,没有控件对象,内存占用几乎为零。
  • 编辑器是“需要编辑时才创建,编辑结束就销毁”的临时控件,只有正在编辑的那一个存在。

所以在表格里放 1 万行数据,用自绘代理基本毫无压力;如果用“每行放一个 QComboBox 或 QProgressBar”的思路,1 万行控件会让界面直接没法看。也许有人会说:“那我不放 1 万行,只放 100 行不就行了吗?”但表格支持滚动,控件数量乘以可见行数,依然很耗资源,而且滚动时控件复用也会带来很多心智负担。

4.2 确保刷新与异步数据更新

代理和模型之间通过数据角色同步。当数据源变化时,模型需要发出 dataChanged 信号,视图收到后会重绘对应索引。常见误区是:业务线程更新了数据,但 UI 线程没收到通知,代理画出来的内容还是老数据。

解决方式很简单:数据更新后,用 QMetaObject::invokeMethod 或信号槽机制回到 GUI 线程,调用模型实例的:

emit model->dataChanged(index, index, {Qt::DisplayRole, Qt::UserRole + 1});

如果你没指定角色,视图只会对默认角色重绘,自定义角色可能不刷新。为了保险,刷新时把涉及的自定义角色一并传进去。

4.3 适配高分屏与主题切换

自绘代理最容易在高分屏上翻车的是圆角矩形边缘发虚和按钮文字挤到边界外。解决办法是尽量使用 QStyle 的 layout 相关接口,以及绘制时统一除以屏幕设备像素比。

另外一个隐蔽问题是:如果代理里写死了颜色,系统从浅色切到深色主题时,单元格会非常突兀。建议从 option.palette 里取颜色,例如背景色用 opt.palette.base(),文本色用 opt.palette.text(),只有业务上有明确含义的色彩(比如状态红绿黄)才用固定色值。

5. 高频问题速查与避坑记录

5.1 问题排查速查表

症状可能原因解决办法
单元格选中背景消失paint 里没有先调用基类先 QStyledItemDelegate::paint 画背景,再叠加内容
编辑器打开后内容为空只写了 createEditor,没写 setEditorData在 setEditorData 里根据 index 初始化控件值
编辑完数据没变化没写 setModelData在 setModelData 里把控件值写回模型
点击自绘按钮没反应没重写 editorEvent,或只处理了 Press 事件重写 editorEvent,监听 Release,正确判断区域
进度条文字总被选中背景遮住绘制顺序错误先画背景、再画内容,或 restore 前再绘制文本
表格行高不够,画的东西被裁切sizeHint 返回尺寸不合理重写 sizeHint,根据内容高度返回合适值
编辑器的弹出位置不对没重写 updateEditorGeometry根据 option.rect 设置编辑器 geometry
同一列不同行显示格式又不一样代理里逻辑耦合过多根据行类型或数据角色分支,抽出策略函数
主题切深色后文字看不清固定用黑色画笔使用 option.palette 中的文字色

5.2 我踩过的几个小的坑

第一,editorEvent 里误返回 true 导致原本正常的双击编辑被吞掉。如果你只是处理按钮单击,一定要在判定区域外返回基类实现,否则普通双击单元格就永远无法进入编辑状态。

第二,sizeHint 的高度不能总是返回固定行高。有些列内容多需要多行显示,有些列又希望行紧凑一点。我可以根据当前索引动态计算高度,但要注意缓存计算结果,否则数据一多,滚动起来 sizeHint 计算会成为性能瓶颈。

第三,paint 里大量使用字体测量或字符串处理时,建议提前计算并缓存。比如百分比文本,一千行表格每次滚动都要调用 size 计算文本宽度,开销不小。可以用 QFontMetrics 或缓存文本宽度,让滚动体验更顺滑。

第四,如果同时使用代理绘制和 QSS 全局样式,部分样式属性可能会被代理覆盖。遇到这种情况,不要和样式表硬刚,直接在代理里统一处理,或者在代理里手动读取 QApplication::style()。

第五个建议是,代理类尽量设计成无状态的,也就是不要在代理成员变量里保存“当前编辑行”“当前点击列”之类的临时状态。代理实例会被视图持有,跨行复用,成员变量一旦残留,下一行就出 bug。把需要的数据都从 index 和 option 里现取,宁可多写几行,也别踩状态残留的坑。

目前这套混合代理已经稳定跑在监控面板里,后面我还准备扩展一下:再加一个“轻量级列表代理”复用到另一个下拉选择界面里,因为整个交互模式完全一致,只是显示和编辑逻辑不同。如果你也在做类似的表格自定义,建议把代理独立成通用类,多花一点时间把列索引、角色号、颜色方案都做成可配置参数,这样后续换需求时只需要改配置,不需要再动绘制逻辑。

相关新闻

基于YOLOv5的驾驶员状态与危险行为检测实践

基于YOLOv5的驾驶员状态与危险行为检测实践

开车的人可能都有过这种时刻&#xff1a;高速上眼皮突然打架&#xff0c;等惊醒过来方向盘已经偏了半条车道。我做“基于YOLOv5的驾驶员状态与危险行为检测”这个项目的出发点&#xff0c;就是想补上这类预警缺口——用视觉方案实时识别驾驶员是否疲劳、分心&#xff0c;以及有…

2026/10/21 7:41:03 阅读更多 →
Agent核心机制详解:规划与工具调用实战

Agent核心机制详解:规划与工具调用实战

Agent 30 课程第3课&#xff0c;这个位置正好卡在“基础铺垫”和“实战上手”之间。第1课讲 Agent 是什么、能解决什么问题&#xff0c;第2课带大家把开发环境跑通、完成第一个能对话的 Agent。如果第3课继续讲概念&#xff0c;课堂节奏就拖了&#xff0c;学员会觉得无聊&#…

2026/10/21 7:40:03 阅读更多 →
Fluent Bit Windows部署实战:MSI与ZIP安装、配置及服务化指南

Fluent Bit Windows部署实战:MSI与ZIP安装、配置及服务化指南

前阵子接手一个 Windows 环境下的日志采集需求&#xff0c;第一反应就是翻官方文档确认 Fluent Bit 的安装方式。说实话&#xff0c;Fluent Bit 在 Linux 上跑习惯了&#xff0c;换到 Windows 总觉得会有各种隐藏问题&#xff0c;但实际走完一遍之后&#xff0c;发现官方给的那…

2026/10/21 7:40:02 阅读更多 →

最新新闻

房地产数据分析实战:从数据清洗到业务看板的完整复盘

房地产数据分析实战:从数据清洗到业务看板的完整复盘

接到这个项目的时候&#xff0c;团队里其实没人觉得它有多特别——REA&#xff0c;房地产数据分析专项&#xff0c;听起来就是把楼盘信息、成交记录、客户登记表整合成一套报表系统。但真做进去才发现&#xff0c;这活儿远比“搭个看板”复杂得多。它卡在业务判断和数据技术中间…

2026/10/21 9:00:09 阅读更多 →
微电网逆变器控制实战:从PQ控制到下垂控制及离并网切换全解析

微电网逆变器控制实战:从PQ控制到下垂控制及离并网切换全解析

上次在实验室折腾储能变流器&#xff0c;要给现成的台架加上孤岛运行能力。手头没有合适的微电网控制器&#xff0c;市面上的产品基本是黑盒&#xff0c;参数改一个都要折腾半天&#xff0c;我索性决定自己动手把控制和算法全部搭起来。整个过程涉及微电网中最典型的两类控制策…

2026/10/21 9:00:09 阅读更多 →
时钟周期、机器周期、指令周期:从晶振到延时的完整换算链

时钟周期、机器周期、指令周期:从晶振到延时的完整换算链

简介&#xff1a;这份PDF资料面向单片机初学者与嵌入式开发工程师&#xff0c;系统梳理时钟周期、机器周期、指令周期与总线周期四个核心时间概念&#xff0c;帮助读者厘清晶振频率、分频关系与指令执行耗时之间的换算逻辑&#xff0c;解决程序延时计算、功耗评估与性能优化中常…

2026/10/21 9:00:09 阅读更多 →
WinCC V17 VBS脚本编程实战:从报警确认到数据导出

WinCC V17 VBS脚本编程实战:从报警确认到数据导出

简介&#xff1a;这份《TIA Portal WinCC Engineering V17 – VBS脚本编程参考手册》面向使用西门子博途WinCC V17进行HMI开发的工程师与技术人员&#xff0c;帮助其在自动化项目中通过VBS脚本实现自定义交互逻辑与系统控制。手册系统梳理了VBS脚本的编程方法&#xff0c;涵盖A…

2026/10/21 9:00:09 阅读更多 →
单片机时钟周期、机器周期与指令周期详解:从计算到避坑

单片机时钟周期、机器周期与指令周期详解:从计算到避坑

简介&#xff1a;这份PDF资料围绕单片机三大核心时间概念展开&#xff0c;面向嵌入式初学者与需要夯实基础的开发者&#xff0c;帮助厘清时钟周期、机器周期与指令周期之间的层级关系及换算方法。资源包共1个PDF文件&#xff0c;大小约53KB&#xff0c;内容以文字讲解与实例计算…

2026/10/21 9:00:09 阅读更多 →
区块链安全基石:椭圆曲线密码学(ECC)原理与工程实践

区块链安全基石:椭圆曲线密码学(ECC)原理与工程实践

聊到区块链&#xff0c;椭圆曲线密码学&#xff08;ECC&#xff09;是个绕不开的名字。你手里的每一个钱包地址、每一笔链上交易的数字签名&#xff0c;背后几乎都能看到它的影子。它解决的问题在密码学里其实很朴素&#xff1a;如何让一个人在不暴露自己私钥的前提下&#xff…

2026/10/21 8:59:08 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/20 9:50:45 阅读更多 →