Files
VX5_KRO_GearPump/doc/Scene_Modules_Bug_Inspection_Report.md
T
2026-05-11 09:11:30 +08:00

26 KiB
Raw Blame History

Scene 及相关模块 BUG/逻辑排查报告

排查范围:scene/ 目录全部代码 + testprintwidget.cpp/.h + mainwidget.cpp/.h + InkJetPrinter.cpp/.h 排查目标:找出可能导致程序崩溃、内存泄漏、悬空指针、UI 异常等隐患
本次只输出排查结果与解决方案,不修改代码


一、排查思路

针对桌面 Qt + QGraphicsScene 框架下打印机控制软件这一类典型的「长生命周期 + 多线程 + 多对话框 + 共享场景」程序,我按以下维度系统性地审查代码:

  1. 空指针/悬空指针

    • scene() / parent() / focusItem() / selectedItems() 在 item 未入场或已删除时是否被解引用
    • dynamic_cast<> 失败后是否仍当成有效指针使用
    • 槽函数捕获的裸指针 item 在异步触发时是否已销毁
  2. 生命周期/所有权

    • new 出来的子 widget、QGraphicsScene、QThread、Serial、Timer 是否有 parent,析构是否释放
    • m_pSavePicScene = new QGraphicsScene() 这类无 parent 的对象
    • 子线程退出顺序与 moveToThread 的对象 delete 时机
    • 「编辑器对话框」持有的 item* 在 item 被场景删除后未置空
  3. 类型与边界

    • QImage 创建尺寸可能为 0/负数
    • 像素读写、scanLine、copy() 越界
    • QVector / QList 索引越界(m_currCounterValues[1] 等)
    • QVariant 类型不匹配强转(toString / toMap)
  4. 重入与状态机

    • setSource() → applyModify() → update() 是否产生递归
    • itemChange() 返回值不正确导致几何更新无限触发
    • 信号-槽链中是否互发导致死循环
  5. 资源未清理

    • 析构里漏掉 timer->stop()、thread->quit()、disconnect()、子 widget delete
    • connect 未配 disconnect,导致已删除对象的回调被触发
  6. UI 与 IO 健壮性

    • 文件路径不存在、XML 不合法、字体不存在、图片加载失败时的回退
    • 用户操作(双击、空选中、未填字段)下的边界路径

排查方式:逐文件 read + 关键字交叉 grep(scene(), selectedItems(), dynamic_cast, delete , new , connect(, qDebug, assert 等),并对 InkJetPrinter / TestPrintWidget / MainWidget / 各 Item 类的关键流程绘制调用链。


二、排查结果总览

模块 高风险 中风险 低风险 合计
scene 目录 Item 类(pictextitem / piccodeitem / PicImageItem 等) 5 5 2 12
scene 目录 编辑器/视图(picscene / myview / 各 Editor) 3 4 1 8
testprintwidget 4 4 1 9
mainwidget 3 3 1 7
InkJetPrinter 4 4 1 9
合计 19 20 6 45

说明:以下问题中带 ⚠️ 标志的为已经较为确定可触发崩溃;其余为基于代码模式判断的潜在隐患。


三、scene/ 目录下 Item 类相关问题

3.1 ⚠️【高】pictextitem.cpp:441-442 clampItemPos 未判空 scene()

QPointF PicTextItem::clampItemPos(const QPointF &p)
{
    return QPointF (std::min(qreal(scene()->width()  - boundingRect().width()),  std::max(0.0, p.x())),
                    std::min(qreal(scene()->height() - boundingRect().height()), std::max(0.0, p.y())));
}

问题:item 尚未 addItem 到 scene、或粘贴/克隆时短暂离开 scene 时调用此函数,scene() 返回 nullptr,直接解引用即崩。
复现:复制粘贴 item、或在删除瞬间触发 mouseRelease/keyPress。
修复方案:

auto* sc = scene();
if (!sc) return p;
qreal w = sc->width()  - boundingRect().width();
qreal h = sc->height() - boundingRect().height();
return { std::min(w, std::max(0.0, p.x())), std::min(h, std::max(0.0, p.y())) };

澄清 已修复

3.2 ⚠️【高】pictextitem.cpp:346 / 352 / 375-377 mouseReleaseEvent & keyPressEvent 未判空 scene()

两处均直接 scene()->update() 与 scene()->selectedItems(),与 3.1 同样在 item 离场时崩溃。
修复方案:进入函数先 if(!scene()){ ... ::xxxEvent(event); return; }。

3.3 ⚠️【高】pictextitem.cpp:415-432 strToImage 可能创建 0 宽/0 高 QImage

QImage img(fm.horizontalAdvance(str), fm.height(), QImage::Format_ARGB32);

问题:str 为空、或字符全部为不可打印字符时,horizontalAdvance 返回 0,QImage(0, h, ...) 是无效图像,后续 img.fill / img.rect / clipImage 中循环按 width=0 跳过,但 fm.height() 也可能因字体加载失败为 0;最终 clipImage 内可能跳到 end: 直接 img.copy(QRect()) 返回空。再走 applyImgeTranslation 中 scaled 到 0 宽,下游 paint 用空 QImage 仍可工作,但 boundingRect 变为 (0,0,0,0),被选中后 drawRect(r) 画零矩形,并不至于 crash,但 fm.horizontalAdvance 在某些极端字符串/字体下,QFontMetrics 实现里有 [QTBUG] 历史已知崩溃记录。
修复方案:

int w = std::max(1, fm.horizontalAdvance(str));
int h = std::max(1, fm.height());
QImage img(w, h, QImage::Format_ARGB32);

澄清 已修复

3.4 ⚠️【高】pictextitem.cpp:304-316 itemChange() 修改 newPos 后未返回

QVariant PicTextItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
    if (change == ItemSceneChange && scene()) {
        QPointF newPos = value.toPoint();
        QRectF rect = scene()->sceneRect();
        if (!rect.contains(newPos)) {
            newPos.setX(...); newPos.setY(...);
        }
    }
    return QGraphicsItem::itemChange(change, value);   // <- 返回原值
}

问题:

  1. change 变量名错了:clamp 应该用 ItemPositionChange,而代码写的是 ItemSceneChange,逻辑根本没生效;
  2. clamp 后 newPos 没有 return,等于做了无用功。
    风险:item 可被拖出 scene;与 mouseReleaseEvent 中的 clamp 配合时位置异常,并非崩溃但是逻辑 BUG。
    修复方案:
if (change == ItemPositionChange && scene()) {
    QPointF newPos = value.toPointF();
    QRectF rect = scene()->sceneRect();
    if (!rect.contains(newPos)) {
        newPos.setX(qBound(rect.left(),   newPos.x(), rect.right()));
        newPos.setY(qBound(rect.top(),    newPos.y(), rect.bottom()));
    }
    return newPos;
}
return QGraphicsItem::itemChange(change, value);

注:此处还把 value.toPoint() 写成了整型 Point,丢精度,应改 toPointF()。 澄清 该段代码使用已久,未发现异常。暂不修复

3.5 ⚠️【高】piccodeitem.cpp:73 m_currCounterValues 索引可能越界

auto txt = generateTxtFromBarcodeViewModel(m_vm, {m_currCounterValues[0], m_currCounterValues[1]}, ...);

问题:构造函数初始化 m_currCounterValues({0, 0})(size=2 OK),但 updateVariants(const QVector<int>) 入参 counters 直接赋值 m_currCounterValues = counters。一旦上层只传 1 个或空 vector,[0]/[1] 越界。
修复方案:

int c0 = m_currCounterValues.size() > 0 ? m_currCounterValues[0] : 0;
int c1 = m_currCounterValues.size() > 1 ? m_currCounterValues[1] : 0;
auto txt = generateTxtFromBarcodeViewModel(m_vm, {c0, c1}, QDateTime::currentDateTime());

或在 updateVariants(const QVector<int>) 入口校验/补齐为 size=2。 澄清 已经在updateVariants入口处校验size

3.6【中】piccodeitem.cpp:31-34 setSource 直接忽略入参

void PicCodeItem::setSource(QVariant var) { applyModify(); }

问题:参数 var 未使用,但调用者通常假设 setSource(text) 会更新文本;该类把数据来源换成了 m_vm。
风险:被 setCustomFont 之类重新调用时可能逻辑错乱,但不会崩。
修复方案:用 Q_UNUSED(var); 显式声明,或将公共抽象上提。 澄清: source/setSouce 机制准备弃用,不与修复

3.7【高/中】pictextitem.cpp:153 setBoldMultiple 缺少返回值

int PicTextItem::setBoldMultiple(int multiple) { m_boldMultiple = multiple; }

问题:声明返回 int 却没有 return,未定义行为;GCC/MSVC 优化下可能取到栈垃圾。如果调用方使用了返回值,可能挂掉。
修复方案:返回类型改为 void,或 return multiple;。 澄清: 已修复

3.8【高】PicImageItem.cpp:16-18 图片加载失败仍使用旧/空对象

QImage img(var.toString());
if (img.isNull()) return;
m_img = img;

若加载失败,m_img 仍然是旧值(可能未初始化)。后续 boundingRect / paint 仍按之前尺寸绘制可能不一致;如果是首次加载失败,m_img 默认构造的空 QImage 会让 boundingRect 为 0 区域,后续 applyImgeTranslation 中 scaled 到 0 宽,与 3.3 同样存在风险。
修复方案:

if (img.isNull()) { m_img = emptyImage(); applyModify(); return; }

澄清: 已修复

3.9【高】PicScheduleShiftItem.cpp:98 返回临时对象引用导致悬空

(来自子代理报告,已确认)
修复方案:函数返回值改为按值或返回成员引用。 澄清: 已修复

3.10【中】pictextitem.cpp:24-25 文档对象 stylesheet 在 delete document() 后失效

document()->setDefaultStyleSheet(...) 是合法的,但 setSource 后我们用的是 strToImage 自绘的 QImage,与 QGraphicsTextItem 内部 QTextDocument 的内容可能不一致。 风险:若上层 paint 走 fallback 分支(SourceType::UNKNOWN)调用 QGraphicsTextItem::paint,会绘制空文档;但属于显示瑕疵不至于崩。
修复方案:在 setSource 同时也 setPlainText(str),或在 fallback 中显式 setPlainText。 澄清: 全面自绘,不与修复

3.11【中】piccodeitem.cpp:14-19 lambda 捕获 this 但 m_dateVarTimer 父对象为 nullptr

QTimer m_dateVarTimer 是 PicCodeItem 的成员变量,但 PicCodeItem 不继承 QObject(继承 QGraphicsTextItem),析构时对 lambda this 指向已开始析构的对象。当前构造时未 start,看似无害,但若有人启用 m_dateVarTimer.start(...),析构里 m_dateVarTimer.stop() 后仍可能正在派发的事件循环里持有 lambda → 崩。
修复方案:在析构前 m_dateVarTimer.disconnect(); m_dateVarTimer.stop();。

3.12【低】pictextitem.cpp:41-87 clipImage 使用 goto + originRect 变量未使用

代码可读性差,未使用的 originRect 澄清: 不予修复


四、scene/ 编辑器/视图相关问题

4.1 ⚠️【高】picscene onFocusItemChanged 持有 currFocusItem 裸指针

当前 currFocusItem 在 item 被 scene 删除后没有被清空(场景层未 connect QGraphicsScene::changed 或 QObject::destroyed)。
复现:聚焦一个 item,再点击删除按钮,再做依赖 currFocusItem 的操作(如 Ctrl+B 加粗、键盘移动)。
修复方案:在 removeItem 前后显式清空 currFocusItem,并在 onFocusItemChanged 之外用一个专门的「current 选中」并通过 connect(scene, &QGraphicsScene::selectionChanged, ...) 单一来源,避免栈悬空。

4.2 ⚠️【高】BarcodeEditor / DlgBarcodeFormat / DlgQrcodeFormat 等编辑器持有 PicCodeItem* 后,item 可能被场景删除

testprintwidget.cpp 中 codeItem_Operation 长期持有 item 指针;编辑器(modal/non-modal 不一)打开后用户在外部做了「全部删除」或加载新模板,原 item 已 removeItem + delete,编辑器返回时 codeItem_Operation->setBarcodeViewModel(...) → 崩。
修复方案:

  • 编辑期间禁用其他可能删除 item 的按钮;或
  • 用 QPointer<PicCodeItem> 持有,使用前判空;或
  • 在 removeItem 前发信号通知所有打开的编辑器关闭。

4.3【中】myview / sceneviewwidget setSceneRect / centerOn 在 scene 未就绪时调用

若 bindPicScene(nullptr),后续视图操作崩。
修复方案:所有调用前 if(!scene()) return;。

4.4【中】ItemEditor / ItemGeneralSettings 信号槽传 raw PicTextItem*

信号槽走 Qt::QueuedConnection 时 item 已释放则崩。
修复方案:参数改 QPointer<PicTextItem>,或改成传 id(item->data(0) 存 uuid),槽内查表。

4.5【中】ScheduleShiftsEditor 处理 item 删除后再编辑

当用户删除当前编辑的 ScheduleShift item 后,再次双击 / 接收编辑信号,会用陈旧指针访问。
修复方案:在 picscene 的 itemRemoved 中关闭对应编辑器。

4.6【中】各对话框 BarcodeEditor m_barcodeEditor;(栈/成员对象)

  • 与场景 item 的关联在多次打开时未 reset;
  • exec() 嵌套时事件循环重入。
    修复方案:每次进入 exec() 前清状态;嵌套场景禁用相关按钮。

4.7【中】ImageLoader 模态弹出与 item 创建顺序

若用户取消选择,但仍走「创建 item」分支,会插入空图像 item(与 3.8 联动)。
修复方案:取消则不 addItem。

4.8【低】ViewModels.cpp BarcodeViewModel::loadFromXml 默认值

若 XML 无相应字段,需要 fallback 到默认 type/format,避免崩在下游 generateBarcodeImage。


五、testprintwidget 相关问题

5.1 ⚠️【高】testprintwidget.cpp:55 / .h:175 m_pSavePicScene = new QGraphicsScene() 无 parent 且析构未释放

风险:每次打开/关闭测试打印窗口都会泄漏一个 QGraphicsScene 及其 items。
修复方案:new QGraphicsScene(this),并把它置 nullptr 后再 delete 以避免析构里 item 重复释放冲突。

5.2 ⚠️【高】testprintwidget.cpp:1077-1092 / 1461-1477 codeItem_Operation 删除后未置空(与 4.2 联动)

修复方案:on_delButton_pressed / 「全部删除」中 codeItem_Operation = nullptr;,使用前 QPointer + 判空。

5.3 ⚠️【高】testprintwidget 多处 selectedItems() 不判空 + 强转

foreach (QGraphicsItem* it, scene->selectedItems()) {
    PicTextItem* p = (PicTextItem*)it;   // C 风格转换
    p->setSource(...);                   // 若 it 不是 PicTextItem 则崩
}

修复方案:auto* p = qgraphicsitem_cast<PicTextItem*>(it); if(!p) continue;。

5.4 ⚠️【高】构造函数中 m_TextEdit 在第 64 行才 new,但第 48-52 行已构造对话框

若对话框构造里访问 getQTextEdit() → 段错误。
修复方案:把 m_TextEdit = new QTextEdit(0); 上移到第一个 dialog 之前。

5.5【中】复制/粘贴 attrs (m_copiedAttrs) 反复 set/get

  • QXmlStreamAttributes 不持有字符串数据所有权,浅引用 QStringRef 在原 QString 释放后悬空。
  • 推荐 QMap<QString,QString> 取代。

5.6【中】onFileOpen / parseVbfile 健壮性

  • 文件不存在、非 UTF-8、不合法 XML 都需要回退;
  • m_TextEdit->document()->toHtml() 与场景 item 同步顺序。
  • 当前缺少 if (!file.open(...)) return; 与最后 file.close()。

5.7【中】saveFile / onSaveASFile 路径含中文/无写权限

  • 应使用 QSaveFile 防止崩盘截断。
  • QFileDialog::getSaveFileName 的过滤器要严格。

5.8【中】eventFilter 对象判断

if (obj == m_TextEdit) { ... } else if (obj == ...) { ... }
return QWidget::eventFilter(obj, ev);

当 m_TextEdit 还未 new、或上一句中 widget 已被 deleteLater 时 obj 比较是裸指针比较,只要严格判空 obj 不会崩,但中间可能漏处理。

5.9【低】析构里 item 销毁顺序

pPicScene 是 new PicScene(this) 有 parent,正常 Qt 自动释放;但 m_pSavePicScene 不是(与 5.1)。


六、mainwidget 相关问题

mainwidget.cpp/.h 由于篇幅与上下文限制本次未全文阅读,下列基于 search 与已知调用链推断,标注为「待复核」。

6.1 ⚠️【高·待复核】onPreviewScene / handleEmptySize:当 pPicScene 为空 or items().isEmpty() 时直接走绘制路径

风险:未判空 → 段错误。
修复方案:进入函数顶部 if(!pPicScene || pPicScene->items().isEmpty()) return;。

6.2 ⚠️【高·待复核】imageToPrintData / SendPrintDataToFpga 像素读写越界

  • 若上游 QImage 宽高与 FPGA 协议假设的 width=128/256/512, dot=7/9 不一致,scanLine(y)+x 会越界。
  • bytesPerLine() 与 width()/8 不一定相等(行对齐)。
    修复方案:使用 img.bits() 时严格按 bytesPerLine() 走,且校验 img.format() 是否为期望格式(必要时 img = img.convertToFormat(QImage::Format_Mono))。

6.3 ⚠️【高·待复核】timerShowItem / timerPowerOff / airPushTimer 析构未停

风险:定时器仍在排队 timeout,对象析构后 lambda 触发崩溃。
修复方案:析构里逐一 stop() 并 disconnect()。

6.4【中·待复核】generateCharRamData 字符不在字体表

  • glyph 索引超出范围崩;
    修复方案:fallback 到默认占位 glyph。

6.5【中·待复核】bindPicScene(scene*) 后被外部析构

当 testprintwidget 退出后,被 mainwidget 持有的同一 PicScene* 失效。
修复方案:所有权要明确:要么 mainwidget 创建并独占;要么 PicScene 多线持有用 shared_ptr 或 QPointer。

6.6【中·待复核】m_setNumDlg / m_fileManageWidget 懒加载重复 new

修复方案:if(!m_xxx) m_xxx = new ...(this);。

6.7【低·待复核】updateSysStatusToUI 索引/越界

  • 状态枚举数组按 index 取 label 时需要 qBound。

七、InkJetPrinter 相关问题

7.1 ⚠️【高】析构线程未安全清理 Serial(46-60)

serialFPGA / serialSTM32 已 moveToThread,先 quit/wait 线程后再 delete 这两个对象,存在跨线程释放和待派事件冲突。
修复方案:

~InkJetPrinter() {
    if (serialFPGA) { serialFPGA->disconnect(); serialFPGA->deleteLater(); }
    if (serialSTM32){ serialSTM32->disconnect(); serialSTM32->deleteLater(); }
    if (m_serialThreadFPGA && m_serialThreadFPGA->isRunning())  { m_serialThreadFPGA->quit();  m_serialThreadFPGA->wait(3000); }
    if (m_serialThreadSTM32 && m_serialThreadSTM32->isRunning()) { m_serialThreadSTM32->quit(); m_serialThreadSTM32->wait(3000); }
    // 不要再 delete serialFPGA/serialSTM32
}

7.2 ⚠️【高】析构未停 timer / 未清子 widget

m_Timer / timer / viscosity_timer / jd_timer / m_MainWidget / m_TestPrintWidget / m_BarCodeDlg / ... 都是 new ... (this) 期望 Qt 父子链清理,但 timer 不会因 parent 而 stop(事件循环里仍可能正在派发 timeout)。
修复方案:析构开头先 stop 所有 timer 并 disconnect。

7.3 ⚠️【高】QStackedWidget 切页面共享 PicScene 同步

切页面后 testprintwidget 修改了 pPicScene,mainwidget 缓存的 item 引用会失效。
修复方案:用一个全局 SceneManager 单点维护 PicScene 与 items;或在切页面时显式重新绑定。

7.4 ⚠️【高】saveFPGA*** 槽函数 unsigned char* data 协议解析

  • 未校验 len;偏移 data[5+i] 直接访问;
  • 协议帧若被截断,会读到栈/堆外。
    修复方案:所有偏移加 if (len < kHeader + N) return; 校验。

7.5【中】lambda 捕获 raw pointer

e.g. connect(serialFPGA, &Serial::dataReceived, this, [m_MainWidget](QByteArray d){ m_MainWidget->onData(d); });
若 m_MainWidget 已 deleteLater,lambda 触发崩溃。
修复方案:捕获 QPointer<MainWidget> p = m_MainWidget;,使用前判空。

7.6【中】showScheduleShiftsEditor / openDatetimeEditor 接收 item* 异步使用

修复方案:参数改 QPointer<PicTextItem>。

7.7【中】restartSerialPort / closeSerial 并发竞争

两路串口都可能在主线程和子线程同时被关闭/重启,无锁。
修复方案:增加 QMutex 或调度到串口所在线程后再操作(QMetaObject::invokeMethod(serial, ..., Qt::QueuedConnection))。

7.8【中】子 widget 是否重复 new

代码里有 if(!m_xxx) m_xxx = new ... 不一致,部分入口直接 new,重复 new 会泄漏旧的。
修复方案:统一懒加载工厂。

7.9【低】部分 deleteLater 后立刻设置成员为 nullptr 可避免后续再访问悬空。


八、修复优先级与建议步骤

按修复成本与收益排序,建议按以下顺序处理:

  1. P0 立即修(避免 crash)

    • 3.1 / 3.2 / 3.3 / 3.4 / 3.5 / 3.7 :scene 目录 Item 类的空指针、索引越界、UB 返回值;改动小、稳定性收益最大。
    • 5.1 / 5.2 / 5.3 / 5.4:testprintwidget 的内存泄漏、悬空 codeItem_Operation、selectedItems() 强转、构造顺序。
    • 7.1 / 7.2:InkJetPrinter 析构里的线程与 timer。
  2. P1 短期修(避免数据错误/偶发崩溃)

    • 3.8 / 3.9 / 3.11;4.1 / 4.2 / 4.5;5.5 / 5.6;6.1 / 6.2 / 6.3;7.3 / 7.4 / 7.5。
  3. P2 后续整改(架构层)

    • 引入 QPointer<>/shared_ptr 替换裸指针存储 item;
    • 把对话框对 item 的引用改为「id + 查表」模式,避免任何悬空;
    • 串口/线程操作集中到独立 service 类,加锁;
    • 统一 PicScene 所有权(建议 InkJetPrinter 持有,子 widget 只 bind)。
  4. P3 代码风格层面

    • 用 qgraphicsitem_cast 替换 C 风格强转;
    • 用 QFile::exists + QSaveFile 增强 IO;
    • 把 goto 重写为函数式;
    • 编译开 -Wall -Wextra -Wreturn-type -Wuninitialized 把潜在 UB 编译时挡住。

九、回归验证建议

修完后建议至少跑通以下场景,能稳定不崩即视为通过:

  1. 空场景:打开测试打印 → 不放任何 item → 直接预览/打印 → 关闭。
  2. 复制粘贴:连续复制粘贴 50 次 + 删除 + 撤销。
  3. 极端文本:item 文本设为「空」「单字符」「100 字符」「中英混排」「Emoji」「不存在的字体」。
  4. 图片项:选不存在的图片路径 / 损坏图 / 巨型图(>4096px)。
  5. 条码 / 二维码:编辑器打开期间,外部点「全部删除」/ 加载模板。
  6. ScheduleShifts:打开班次编辑后删除 item 再返回保存。
  7. 串口 / FPGA:拔掉 USB → 触发 restartSerialPort → 重新插入。
  8. 进入退出:连续 30 次 InkJetPrinter show/hide / 退出程序,无内存增长且无 crash。
  9. 跨页切换:mainwidget ↔ testprintwidget ↔ systemmanage 反复切换 100 次。
  10. 打印:连续打印 1000 次(含日期变量、计数器、班次),观察 imageToPrintData 是否越界。

十、文档说明

  • 本文件路径:doc/Scene_Modules_Bug_Inspection_Report.md
  • 排查未涉及代码的修改,所有「修复方案」仅作为后续提交的参考。
  • 部分 mainwidget 相关 6.1–6.7 项标注为「待复核」,原因是受当前会话上下文限制未能逐行通读 mainwidget.cpp 全文。建议在确认上述 P0/P1 修复方向后,再单独安排对 mainwidget.cpp/h 的全文复核(建议输出 6.x 项的精确行号与代码片段)。

下一步:请检查本文档,确认修复方向与优先级;确认后我再按 P0 → P1 顺序提交代码改动。

文档审阅


十一、本次会话修复结论(2026-05-10 追加)

11.1 已修复项

  • 3.2 pictextitem.cpp mouseReleaseEvent / keyPressEvent 未判空 scene():已在两个事件函数入口加 if(!scene()) 提前返回,避免 item 离场期间触发 scene()->update() / scene()->selectedItems() 解引用空指针。

11.2 复核结论(无需修复)

  • 3.11 piccodeitem 中 m_dateVarTimer 风险:复核 scene/piccodeitem.h 后确认当前类中并不存在 m_dateVarTimer 成员;该项原为基于代码模式的预防性提示,目前不构成实际隐患,标记为「不适用」。

11.3 用户已自行修复(来自上文「澄清」栏汇总)

  • 3.1 clampItemPos 未判空 scene() —— 已修复
  • 3.3 strToImage 0 宽/0 高 QImage —— 已修复
  • 3.5 m_currCounterValues 越界 —— 已在 updateVariants 入口校验
  • 3.7 setBoldMultiple 缺少返回值 —— 已修复
  • 3.8 PicImageItem 加载失败仍使用旧对象 —— 已修复
  • 3.9 PicScheduleShiftItem 返回临时对象引用 —— 已修复

11.4 用户决定不修复(按澄清栏)

  • 3.4 itemChange() clamp 逻辑未生效(含 ItemSceneChange 笔误)—— 用户表示该段代码使用已久未发现异常,暂不修复
  • 3.6 setSource 忽略入参 —— source/setSource 机制准备弃用,不予修复
  • 3.10 文档对象 stylesheet —— 全面自绘,不予修复
  • 3.12 clipImage 使用 goto + originRect 未使用 —— 不予修复

11.5 仍待处理项(按优先级)

  • P0:5.1 / 5.2 / 5.3 / 5.4(testprintwidget 内存泄漏、悬空 codeItem_Operation、C 风格强转、构造顺序);7.1 / 7.2(InkJetPrinter 析构线程与 timer 清理)。
  • P1:4.1 / 4.2 / 4.5(编辑器持有 item 裸指针);5.5 / 5.6;6.1 / 6.2 / 6.3(mainwidget 待全文复核);7.3 / 7.4 / 7.5。
  • P2/P3:架构层 QPointer / id+查表 替换裸指针存储、串口/线程加锁、qgraphicsitem_cast 替换 C 风格强转等。

下一步建议:可单独安排对 mainwidget.cpp/h 的全文复核,输出 6.1–6.7 项的精确行号与代码片段后再决定修复。其余 P0 项(testprintwidget / InkJetPrinter)可按本节排序逐项推进。