Question

I've got a specific target: to draw a road-net. So i have a number of dots (x,y) and I'd like to connect them (using drawLine function). Because of their amount (about 2-3 millions) I need to do in in another thread, so there a problem how should i do it ? I have a special area for drawing - QLabel. I've tried to do it through QPixmap in main thread and everything is fine, but when I try to do it through signal/slot in another thread no image appear :(

Actually, when I transform my coordinates into GUI-coordinates they become fractional so I don't know how to paint them, because drawLine functions has integer argument: (int x1, int y1, int x2, int y2).

This is how i create another thread (I need to run only one function, so it is the best way i think) QtConcurrent::run(this,&MainWindow::parseXML)

Hope you will help me, because I will become mad %)

P.S I've read that QPixmap is not supported in multi-threading drawing. So now i have no idea how to do that.
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.

Was it helpful?

Solution 2

You are apparently looking for a QGraphicsView (or preferably QQuickView if you care about performance and are working with Qt5). That's the solution which Qt offers for exactly this purpose.

To your question -- there is no way in Qt to do the painting in a separate thread; any widget class cannot be touched from another thread. The proposed invokeMethod call is actually an asynchronous callback which gets queued for execution in the main thread. You could generate a QImage, pass it to the GUI thread and let the GUI use it, but I'd seriously suggest working with the scene graph (the QGraphicsView) because it was designed and optimized for precisely this purpose.

OTHER TIPS

If you need to do your painting in a thread other than the Qt GUI thread, do this:

  1. In your non-GUI thread, create a QImage object
  2. Use a QPainter to paint into the QImage object
  3. Use QApplication::postEvent or a queued signal/slot connection to pass the QImage object over to the main thread in a thread-safe manner
  4. The main thread can now convert the QImage object into a QPixmap (this will be relatively quick to do) and then display it as usual.

Though it's really bad practice - to update GUI thread from within worker thread and you should really do it via signal-slot (with connection type -queued) , you still can update GUI via QMetaObject::invokeMethod()

You have to run every function in a worker thread, that updates GUI through invokeMethod(). For example - in your main class, add function like void MainWindow::drawLine(int x1, int y1, int x2, int y2) which will draw line on your QImage. And within your thread you can call that function like this:

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

The simplest approach is to concurrently distribute the drawing across several images, then composite the images (also concurrently), and then finally submit them for painting on the gui.

This can be done using QtConcurrent::map on the sequence of images. The map functor draws into an image that's specific to the current thread - e.g. via QThreadStorage. The reference to that image can also be stored, upon allocation, in a list within the functor. The functor of course has to outlive the call to QtConcurrent::map. Once map returns, the images from the list within the functor can be asynchronously combined pair-wise, until only one image remains. That image is then submitted to the display widget.

If the full-size image compositing is to be avoided, then a similar approach will work, but the lines have to grouped into spatial groups, i.e. those intersecting some rectangles that cover the area to be drawn. To fully utilized all cores, you'd want say 2-3x as many rectangular areas as QThread::idealThreadCount(). Then treat the painting of each of those groups onto its sub-image to be a concurrent task, to be submitted to QtConcurrent::run. When all tasks are done, the images get submitted to the display widget, which paints them on its backing store, in sequence.

The painting of the images on the backing store can also be multi-threaded, see this answer for a complete example. Generally speaking, the images need to have a width the multiple of (CPU cacheline size/4 since we use 32-bit pixels). The painting of those images on the backing store is fully parallelizable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top