msrd0
msrd0

Reputation: 8371

QGraphicsItem disappears when calling setPos from a different thread

I have two types of ­­­­­­­­­­QGraphicsItem­­­­­­s on a QGraphicsView, one of those two types are in the scene like grid with z-index 1, the other ones, ants, are on top of them with z-index 2. When starting the program, I set all ants to position 0,0 and add them to the scene. But then I start moving those ants from another Thread by calling setPos() on them - and then my computer eats the ants! They disapper at their old position, but don't appear at their new position. The new position is inside the scene.

Here is the code of Ant class (that inherits QGraphicsItem):

#include "ant.h"
#include "constants.h"

#include <QPainter>

Ant::Ant()
{
    setZValue(2);
}

QRectF Ant::boundingRect() const
{
    return QRect(QPoint(0,0), G_FIELD_RECT_SIZE);
}

void Ant::paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget)
{
    Q_UNUSED(item)
    Q_UNUSED(widget)

    QBrush b = painter->brush();
    if (food())
        painter->setBrush(Qt::blue);
    else
        painter->setBrush(Qt::black);
    painter->drawEllipse(2,3, G_FIELD_RECT_WIDTH - 4, G_FIELD_RECT_HEIGHT - 6);
    painter->setBrush(b);
}

After a bit more testing, I found out that everything is working as long as I call setPos from the Qt Event Thread. As soon as I call it in an custom thread, the ants disappear. Any idea how I can solve this?

Upvotes: 2

Views: 761

Answers (1)

jpo38
jpo38

Reputation: 21514

You must go back to the main thread to do the setPos. As commented by ddriver, you should not modify GUI from a thread (you usually get qDebug messages when doing so, didn't you get any in your debugger window?).

You just need to:

  • Add a new signal to your Ant class (like signalSetPos( QPoint pos ))
  • Add a new slot to your Ant class (like doSetPos( QPoint pos )). This slot implementation simply calls setPos(pos).
  • Connect them using Qt::QueuedConnection or Qt::BlockingQueuedConnection (fifth parameter of the connect function, for GUI update, Qt::QueuedConnection may be preferable because it won't block your thread).
  • From the thread where you used to do setPos( newPos ), just do emit signalSetPos( newPos ). Then, doSetPos will be executed (later if you used Qt::QueuedConnection, right away if you used Qt::BlockingQueuedConnection) from the main thread.

Check this post for more information about emitting signals from threads: Qt - emit a signal from a c++ thread

Upvotes: 2

Related Questions