Qt中如何在paintEvent中使用API视频数据构造的QImage?
问题分析与解决方案
核心问题:QImage浅拷贝导致内存失效
你当前用QImage(pData, width, height, bytesPerRow, QImage::Format_Indexed8)构造的QImage是浅拷贝模式——它不会复制传入的pData内存,只是保存指针引用。当API回调结束后,pData指向的内存很可能被释放或覆盖,等到paintEvent执行时,m_srcImage的内部数据已经失效,表现为isNull()为true。
另外还有两个次要问题:
RenderClass构造函数里初始化m_srcImage(nullptr)错误,QImage是值类型,不是指针,应该用默认初始化。- 信号连接用了
Qt::DirectConnection,如果API回调在非UI线程触发,槽函数会在非UI线程执行,操作QOpenGLWidget相关对象会触发线程安全问题。
修复步骤
1. 改为QImage深拷贝
在槽函数receiveFrameData中,构造临时QImage后调用copy()生成深拷贝的图像,确保m_srcImage拥有独立的内存:
void RenderClass::receiveFrameData(uchar* pData, int width, int height, int bytesPerRow) { // 先构造临时浅拷贝QImage,再调用copy()生成深拷贝 QImage tempImage(pData, width, height, bytesPerRow, QImage::Format_Indexed8); m_srcImage = tempImage.copy(); qDebug() << "Slot is called." << m_srcImage; update(); // 触发paintEvent重绘 }
2. 修正RenderClass构造函数的初始化
把错误的指针初始化改成QImage默认构造:
RenderClass::RenderClass(QWidget* parent) : QOpenGLWidget(parent), m_srcImage() // 值类型默认初始化,替代nullptr { }
3. 调整信号连接类型为队列连接
确保槽函数在UI线程执行,避免跨线程操作UI对象:
const bool connected = QObject::connect(this, &GrabberClass::sendFrameData, m_renderClass, &RenderClass::receiveFrameData, Qt::QueuedConnection); // 替换DirectConnection Q_ASSERT(connected);
4. 可选:确保图像格式匹配
如果是Indexed8格式,需要确认颜色表(color table)是否正确。如果API返回的是灰度图,你可以手动设置颜色表:
void RenderClass::receiveFrameData(uchar* pData, int width, int height, int bytesPerRow) { QImage tempImage(pData, width, height, bytesPerRow, QImage::Format_Indexed8); // 为灰度图生成颜色表 QVector<QRgb> colorTable(256); for (int i = 0; i < 256; ++i) { colorTable[i] = qRgb(i, i, i); } tempImage.setColorTable(colorTable); m_srcImage = tempImage.copy(); update(); }
原理说明
- 深拷贝后的QImage会独立管理图像数据内存,不受原
pData生命周期影响,确保paintEvent调用时数据有效。 Qt::QueuedConnection会把槽函数调用放入UI线程的事件队列,保证所有UI相关操作都在主线程执行,符合Qt的线程安全规则。- QOpenGLWidget的
paintEvent会在主线程触发,此时访问m_srcImage是安全的,只要m_srcImage是有效的深拷贝图像,就能正常绘制。
内容的提问来源于stack exchange,提问作者taathy
相关产品推荐
相关产品推荐

