输入关键词开始搜索

Qt 常见 Bug 合集 — 高频陷阱与修复

🔴 高频 + 高难度

1. moveToThread 后的对象归属混乱

频率: ⭐⭐⭐⭐⭐ 难度: ⭐⭐⭐⭐

// ❌ 构造时在主线程创建子对象 → 线程亲和性错误
class Worker : public QObject {
    QSerialPort *m_serial;  // 在主线程构造
public:
    Worker() { m_serial = new QSerialPort; }  // 归属主线程!
};
worker->moveToThread(thread);
// m_serial 还在主线程,通信线程里 send → "Cannot send events to objects owned by a different thread"

// ✅ 延迟到槽中创建
class Worker : public QObject {
    QSerialPort *m_serial = nullptr;
public slots:
    void open() { m_serial = new QSerialPort; }  // 在 worker 线程创建
};
QMetaObject::invokeMethod(worker, "open", Qt::QueuedConnection);

2. 信号槽连接后不触发

频率: ⭐⭐⭐⭐⭐ 难度: ⭐⭐⭐

// 原因① 参数签名不匹配(编译期不报错)
connect(sender, SIGNAL(valueChanged(int)),    // Qt4 风格
        receiver, SLOT(onValueChanged(float))); // ❌ int ≠ float

// 原因② 忘记 Q_OBJECT 宏
class MyWidget : public QWidget {
    // Q_OBJECT  ← 少了这行!moc 不生成信号槽代码
signals: void ready();
};

// 原因③ 线程事件循环没跑
QThread thread;
Worker *w = new Worker;
w->moveToThread(&thread);
thread.start();  // ← 如果 Worker 在 start() 前就调了槽,不执行

// 排查方法
QMetaObject::Connection c = connect(...);
qDebug() << c;  // 验证是否连接成功
// 或: connect(..., Qt::QueuedConnection) 强制跨线程

3. QPixmap 导致的内存泄漏

频率: ⭐⭐⭐⭐ 难度: ⭐⭐

// ❌ paintEvent 中每帧新建 QPixmap → 16ms 一次 malloc+free → 堆碎片
void paintEvent(QPaintEvent *) {
    QPixmap offscreen(size());  // 3.8MB * 60fps = 228MB/s 分配
}

// ✅ 成员变量复用
QPixmap m_offscreen;
void resizeEvent(QResizeEvent *e) override {
    m_offscreen = QPixmap(e->size());  // 只在尺寸变化时重建
}
void paintEvent(QPaintEvent *) override {
    QPainter p(&m_offscreen);
    // ...
}

🟡 中频 + 中难度

4. SQLite “database is locked”

频率: ⭐⭐⭐⭐ 难度: ⭐⭐

-- 原因:SQLite 同时只允许一个写者
-- 场景:采集线程在写入,UI 线程同时查询 → UI 线程报 locked

-- ✅ 修复
PRAGMA journal_mode=WAL;       -- 读写可并发
PRAGMA busy_timeout=5000;      -- 获取锁前等待 5 秒

-- ✅ 代码侧:写操作放进同一事务
db.transaction();
for (auto &dp : dataPoints) insert(dp);
db.commit();

5. windeployqt 后程序闪退

频率: ⭐⭐⭐⭐ 难度: ⭐⭐⭐

:: 原因① 缺少 MSVC 运行时
:: 解决: 静态链接 /MT 或附带 VC_redist.x64.exe

:: 原因② 缺少插件 DLL(无任何提示直接退)
set QT_DEBUG_PLUGINS=1
myapp.exe 2> debug.log
:: 查看日志找出缺哪个插件

:: 原因③ windeployqt 不处理非 Qt 依赖
:: 手动检查: depends.exe 或 Dependencies
:: 常见遗漏: OpenSSL DLL, libpq.dll, mysql.dll

6. QTableView 数据不刷新

频率: ⭐⭐⭐ 难度: ⭐⭐

// ❌ 修改了底层数据但没通知 View
model->m_data.append(row);  // QTableView 不知道数据变了

// ✅ 标准流程
model->beginInsertRows(QModelIndex(), model->rowCount(), model->rowCount());
model->m_data.append(row);
model->endInsertRows();

// 或全量刷新(数据量少时)
model->beginResetModel();
model->m_data = newData;
model->endResetModel();

7. 跨线程直接操作 UI 控件

频率: ⭐⭐⭐⭐ 难度: ⭐⭐

// ❌ Worker 线程直接更新 UI → 随机崩溃
void Worker::onDataReady(const Data &d) {
    m_label->setText(d.text);  // ❌ QLabel 在主线程
}

// ✅ 信号槽自动跨线程
connect(worker, &Worker::dataReady,
        label, [label](const Data &d) {
            label->setText(d.text);  // 自动在主线程执行
        }, Qt::QueuedConnection);

🟢 低频 + 低难度(但反复踩)

8. 界面布局错乱

// ❌ setGeometry 硬编码坐标 → 不同 DPI/窗口大小下错乱
button->setGeometry(10, 10, 100, 30);

// ✅ 使用布局管理器
auto *layout = new QVBoxLayout(this);
layout->addWidget(button);
setLayout(layout);

// 自绘控件不写 sizeHint → 布局给 0x0 空间
QSize MyWidget::sizeHint() const override {
    return QSize(400, 300);  // 不写这个,布局不知道怎么分配空间
}

9. 中文/UTF-8 乱码

// ❌ 字符串字面量编码依赖源文件编码
QString s = "中文";  // 源文件是 GBK → 乱码

// ✅ 显式编码
QString s = QString::fromUtf8("中文");
QString s = QStringLiteral("中文");  // 编译期处理
// C++20: QString s = u8"中文";

// QSS 文件也要注意编码,保存为 UTF-8

10. 项目包含路径配置错误

# ❌ 全局 include 而不是 target
include_directories(include/)  # 所有 target 都污染

# ✅ target 级别
target_include_directories(myapp PRIVATE include/)
target_include_directories(mylib PUBLIC include/)

11. QTimer 不触发

// ❌ 在非主线程创建 QTimer 但没事件循环
auto *timer = new QTimer;
timer->start(1000);
connect(timer, &QTimer::timeout, ...);  // 没有事件循环 → 不触发

// ✅ 用 QThread + exec() 启动事件循环
// 或者在主线程创建 QTimer

12. 资源文件 .qrc 路径大小写

<!-- ❌ Windows 开发 → Linux 报错 -->
<file>icons/Save.png</file>
<!-- ✅ 文件名严格匹配(Linux 大小写敏感) -->
<file>icons/save.png</file>