1

问题是关于将现有的基于 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();

在我的“过程”中。

任何人都可以确认这是正确的解决方案吗?

4

1 回答 1

0

回答我自己的问题:对于任何可能对同一主题感兴趣的人,请参阅此答案:https ://stackoverflow.com/a/10882705/1284631 以获取https://stackoverflow.com/users提供的非常好的解决方案/1329652/kuba-ober(使用该QCoreApplication::postEvent方法)和我自己的解决方案,归结为编写和实例化一个特殊用途的 QObject 派生方法命令的信号发射类,用于与 QT 接口(即,其方法被调用的类就像任何其他 C++ 方法一样,但它们所做的唯一有用的事情是向 QT QObjects 发出信号)。

于 2012-06-21T14:54:26.097 回答