问题是关于将现有的基于 pthread 的 C 应用程序与 QT GUI 接口。这似乎是一个简单的任务,但是当我搜索(在互联网上和通过试错)时,我没有找到一个可以接受的解决方案。
现在,该应用程序大量使用了预先存在的 C 库,我无权修改其代码。
基本上,预先存在的库实现了一个循环,它以周期性的方式调用一些函数(带有一些数据)。我只能定义那些函数(函数指针)。
在一个完整的应用程序中,预先存在的库循环以下列方式调用这些函数(又名“init”、“process”和“uninit”):
init(f);
process(f);
process(f);
...
process(f);
...
process(f);
uninit(f);
“f”是一种结构,允许在调用之间保存相关数据,“init”和“uninint”函数用于设置和销毁由 f 分配的数据。
现在,process() 函数进行了一些计算,最终结果是一个矩阵,我想在 QT GUI 中将其显示为图像(尽可能简单)。
然而,QT GUI 有它自己的阻塞事件循环,也就是 myApp.exec()。
因此,为了实现这一点,我在“init”函数中创建了一个新线程(pthread),它将首先用相关数据填充“f”字段,然后进入 myApp.exec() 循环。myApp 是一个简单的 QApplication,其中包含一个 QLabel,它显示从 QImage 构造的 QPixmap,我可以用计算的数据填充其“bits()”数据(使用一些与此处无关的映射)。
之后,“进程”函数进行连续计算,更新 QImage 的“位”数据(“位”指针通过 f 结构已知),然后应该强制 GUI 更新自身。
以下是一些代码片段:
init(f){
...
pthread_create(&(f->qt_tid),NULL,&qtimgdisplay_main_thread,f);
...
}
process(f){
...
do_computation();
do_ update(f->bits);
f->myLabel->repaint();
...
}
static void *qtimgdisplay_main_thread(void *arg){
FStructure *f = (FStructure *)arg;
char arg0[] = "programName";
char arg1[] = "arg";
char arg2[] = "another arg";
char* argv[] = { &arg0[0], &arg1[0], &arg2[0], NULL };
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
f->myApp = new QApplication(argc, &argv[0]);
//we create a image
QImage image(f->winwidth,f->winheight,QImage::Format_RGB32);
f->bits=image.bits(); //pointer to the underlying data image
f->myLabel=new QLabel();
f->myLabel->setPixmap(QPixmap::fromImage(image));
f->myLabel->show();
f->myApp->exec();
return NULL;
}
虽然有些工作(排除一些随机分段错误),但该解决方案的问题在于它(正确)显示:
QPixmap:在 GUI 线程之外使用像素图是不安全的
这是完全正确的,因为 QWidgets 不可重入。
因此,然后我尝试以异步方式将 Repaint() 消息发布到 myApp(我仍然必须想象如何从我的 process() 向 QLabel myLabel 发出信号),方法是在“进程”内部替换:
f->myLabel->repaint();
和:
f->myApp->postEvent(f->myLabel,new QPaintEvent(f->myLabel->rect()));
灾难性的结果是 QT GUI 不会自行更新;更多,控制台显示:
QPainter::begin:控件绘画只能作为paintEvent的结果开始
最后,我尝试用“更聪明”的东西(我在这里找到:http ://www.qtforum.org/article/18253/qpaintevent-problem-in-qt4.html )替换上面的同一行,即:
QPaintEvent *pe = new QPaintEvent(f->myLabel->rect());
f->myLabel->setAttribute(Qt::WA_WState_InPaintEvent, true);
f->myApp->sendEvent(s->myLabel,f->myLabel->rect());
f->myLabel->setAttribute(Qt::WA_WState_InPaintEvent, false);
奇妙的结果是它甚至没有编译:
错误:没有匹配函数调用 'QApplication::sendEvent(QLabel*&, QRect)'</p>
我搞不清楚了。有人可以帮忙吗?
这些是我考虑过的一些可能的解决方案:
1) 使 qtimgdisplay_main_thread 成为 QThread 线程而不是 pthread 线程,尽管我不确定这是否会有所帮助;
2)尝试从我的“进程”向 f->myApp 或 f->myLabel 发出一些信号,但我不知道该怎么做,因为“进程”在非 QThread 上运行(并且没有办法访问父线程)
3)创建和销毁一个新的QApplication,所有关联的QLabel和QImage都在“进程”内,但在正常情况下这样做简直是压倒性的和白痴。
我感谢您阅读所有这些内容。如果可能,请尝试通过代码(而不是其他文档)向我提供提示。
编辑:看来,如果我使用:
f->myLabel->update();
代替:
f->myLabel->repaint();
在我的“过程”中。
任何人都可以确认这是正确的解决方案吗?