我有一个特定的目标:画一个路网。所以我有一些点(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.
答案 0 :(得分:7)
如果您需要在Qt GUI线程以外的线程中进行绘制,请执行以下操作:
答案 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)的倍数。这些图像在后备存储上的绘制是完全可并行化的。