如何在另一个线程中绘制到QLabel上

时间:2013-04-29 19:58:07

标签: multithreading qt qpixmap drawing2d

我有一个特定的目标:画一个路网。所以我有一些点(x,y),我想连接它们(使用drawLine函数)。因为他们的数量(大约2-3百万)我需要在另一个线程中做,所以有一个问题我应该怎么做?我有一个特殊的绘图区域 - QLabel。我试图通过主线程中的QPixmap来做到这一切都很好,但当我尝试通过另一个线程中的信号/插槽来做它没有图像出现:(

实际上,当我将坐标转换为GUI坐标时,它们变为小数,因此我不知道如何绘制它们,因为drawLine函数具有整数参数:(int x1,int y1,int x2,int y2)。

这就是我创建另一个线程的方式(我只需要运行一个函数,所以这是我认为最好的方法) QtConcurrent::run(this,&MainWindow::parseXML)

希望你能帮助我,因为我会变得疯狂%)

P.S我读过多线程绘图不支持QPixmap。所以现在我不知道该怎么做。
QPainter can be used in a thread to paint onto QImage, QPrinter, and QPicture paint devices. Painting onto QPixmaps and QWidgets is not supported. On Mac OS X the automatic progress dialog will not be displayed if you are printing from outside the GUI thread.

4 个答案:

答案 0 :(得分:7)

如果您需要在Qt GUI线程以外的线程中进行绘制,请执行以下操作:

  1. 在非GUI线程中,创建一个QImage对象
  2. 使用QPainter绘制到QImage对象
  3. 使用QApplication :: postEvent或排队的信号/插槽连接以线程安全的方式将QImage对象传递给主线程
  4. 主线程现在可以将QImage对象转换为QPixmap(这样做比较快),然后照常显示。

答案 1 :(得分:1)

如果你关心表现并且正在使用Qt5,你显然正在寻找QGraphicsView(或者最好是QQuickView。这就是Qt为此目的提供的解决方案。

对于你的问题 - 在Qt中有 no 方式在一个单独的线程中进行绘画;任何窗口小部件类都无法从另一个线程触及。建议的invokeMethod调用实际上是一个异步回调,它在主线程中排队等待执行。您可以生成QImage,将其传递给GUI线程并让GUI使用它,但我认真建议使用场景图(QGraphicsView),因为它的设计和优化正是为了这个目的。

答案 2 :(得分:0)

虽然这是非常糟糕的做法 - 从工作线程中更新GUI线程并且你应该通过信号槽(连接类型为队列)来实现它,你仍然可以通过QMetaObject::invokeMethod()更新GUI

您必须在工作线程中运行每个函数,通过invokeMethod()更新GUI。例如 - 在您的主类中,添加类似void MainWindow::drawLine(int x1, int y1, int x2, int y2)的函数,它将在您的QImage上绘制线条。在你的线程中你可以像这样调用这个函数:

QMetaObject::invokeMethod(this,"drawLine", Q_ARG(int,x1), Q_ARG(int,y1), Q_ARG(int,x2), Q_ARG(int,y2));

答案 3 :(得分:0)

最简单的方法是将图形同时分布在多个图像上,然后(同时)合成图像,最后将它们提交以在gui上绘画。

可以在图像序列上使用QtConcurrent::map完成此操作。地图函子会绘制到特定于当前线程的图像中-例如通过QThreadStorage。分配后,对该图像的引用也可以存储在函子内的列表中。当然,函子必须终止对QtConcurrent::map的调用。 map返回后,函子中列表中的图像可以成对异步组合,直到仅剩下一个图像为止。然后将该图像提交到显示小部件。

如果要避免全尺寸图像合成,则可以使用类似的方法,但是这些线必须分组为空间组,即那些与覆盖要绘制区域的矩形相交的线。为了充分利用所有核心,您需要说矩形区域是QThread::idealThreadCount()的2-3倍。然后,将这些组中的每个组的绘画处理到其子图像上作为并发任务,并提交给QtConcurrent::run。完成所有任务后,图像将提交到显示小部件,该小部件将按顺序在其后备商店上绘画。

后备存储上的图像绘制也可以是多线程的,有关完整示例,请参见this answer。一般而言,图像的宽度必须是(我们使用32位像素的CPU缓存行大小/ 4)的倍数。这些图像在后备存储上的绘制是完全可并行化的。