0

I have a Worker Thread that copes with heavy and long computations (up to tenth of seconds). These computations produce several thousands of QLines, representing the edges of a dynamically-growing tree. These edges can be modified anytime, since they connect the nodes of the trees by checking the cost, represented by the distance. I would like a smooth update of the QGraphicsScene containing the edges. I tried with signal and slots:

  • Worker thread emits a signal, so when the buffer is full this signal gets caught by the main thread, that will cope with the update/drawing of the line
  • This signal gets still caught by the main thread, but it seems it gets emitted very often, so QGraphicsView/QGraphicsScene gets choked with QLine to be added
  • Changing the size of the buffer doesn't matter
  • Is there an alternative approach to this?

The main slot is:

void MainWindow::update_scene(bufferType buffer)
{
  for (int i = 0; i < buffer.size(); ++i)
  {
    if (buffer[i].first < (g_edges.size() - 1))
    {
      delete g_edges[buffer[i].first];
      g_edges[buffer[i].first] = scene->addLine(buffer[i].second);
    }
    else
      g_edges.push_back(scene->addLine(buffer[i].second));
  }
}

The heavy computing part is:

while (T.size() < max_nodes_number && !_stop)
{
    const cnode random_node = rand_conf ();
    const cnode nearest_node = T.nearest_node (random_node);
    cnode new_node = new_conf (nearest_node, random_node);

    if (obstacle_free(nearest_node, new_node))
    {
        QList<cnode*> X_near = T.neighbours (new_node, max_neighbour_radius);
        cnode lowest_cost_node = nearest_node;
        qreal c_min = nearest_node.cost() + T.distance (nearest_node, new_node);

        for (int j = 0; j < X_near.size(); ++j)
        {
            if (obstacle_free(*X_near[j], new_node) && ((X_near[j]->cost() + T.distance (*X_near[j], new_node)) < c_min))
            {
                c_min = X_near[j]->cost() + T.distance (*X_near[j], new_node);
                lowest_cost_node = *X_near[j];
            }
        }

        T.add_node (new_node, lowest_cost_node.id());
        queue (new_node.id(), QLine (new_node.x(), new_node.y(),
                                                                 lowest_cost_node.x(), lowest_cost_node.y()));

        for (int j = 0; j < X_near.size(); ++j)
        {
            if (obstacle_free(*X_near[j], new_node) && (new_node.cost() + T.distance (new_node, *X_near[j])) < X_near[j]->cost())
            {
                queue (X_near[j]->id(), QLine (new_node.x(), new_node.y(),
                                                                             X_near[j]->x(), X_near[j]->y()));

                T.update_parent (*X_near[j], new_node.id());
                T.rewire_tree (X_near[j]->id());
            }
        }
    }
}

Please note that T is a class representing a Tree. It is constituted by some methods allowing to add a node, searching for the nearest one, etc. It has a QList<cnode> as private member, storing the tree's nodes. cnode is a structure constituted of two coordinates, an id, a parent, a cost, a list of its children.

Michael
  • 876
  • 9
  • 29
  • Rather than using multiple QLine items in the scene, just use one custom QGraphicsItem, as described in [this answer](http://stackoverflow.com/questions/18397603/how-to-move-around-1000-items-in-a-qgraphicsscene-without-blocking-the-ui/18400665#18400665) – TheDarkKnight Jan 04 '16 at 15:43
  • @TheDarkKnight I can't use that since I don't know how many objects there will be. They are generated dynamically after heavy computations, so I must use a separate thread. Is there a way to use a single custom QGraphicsItem and to add to it new generated objects? In the example you provided, there is a _schoolOfFish_ : I don't understand how the `QList m_fishPositionList` is drawn... you anyway have to iterate over it in the paint method I suppose, so what advantage do I achieve? – Michael Jan 04 '16 at 16:07
  • Adding and removing QGraphicsItems in the scene is costly, as is rendering lots of them and updating them all at once. By directing you to that answer, I'm suggesting you change your code to stop using QLine, but instead use a single QGraphicsItem, which holds an internal representation of the data you will render. The data can change with your updates and be drawn with each call to the paint function in which you draw the internal model, which is the tree. – TheDarkKnight Jan 04 '16 at 16:22
  • And it gets choked again, since the updates occur too often... – Michael Jan 04 '16 at 16:50
  • 1
    Without viewing code with the changes suggested, I would only be guessing as to advise how to move forward here. I suggest creating an [MCVE](http://stackoverflow.com/help/mcve) that demonstrates the issue. – TheDarkKnight Jan 04 '16 at 16:56
  • @TheDarkKnight - the problem with the single item is if there are 10000 lines and only one changes, you have to redraw 10000 lines. A balance between item count and line count is needed, since the data source is a tree the item structure can simply model after that. – dtech Jan 04 '16 at 18:47
  • A call to draw 10000 lines in one go shouldn't be an issue for Qt, if done in one draw call, especially if you set the viewport to use OGL by calling `setViewport(new QGLWidget)`. Alternatively, you can work out what changed and only redraw a portion of the screen. – TheDarkKnight Jan 05 '16 at 09:01
  • Okay, check this out: `git clone git@bitbucket.org:MichaelDallago/lasttest.git`. With 100.000 `QPoint` even resizing window starts lagging a little. – Michael Jan 05 '16 at 12:37
  • @ddriver @TheDarkKnight My bad, I discovered just now that `scene` gets updated even if I specifically set `NoViewportUpdate`. Perhaps the Worker thread is implied in some way? Perhaps that's because it's a QueuedConnection... for creating worker thread I always follow [this](https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/) guide – Michael Jan 05 '16 at 14:03

0 Answers0