Important: Please read the Qt Code of Conduct -

Receiving notification on internal movement of items inside a QTreeWidget

  • I subclassed QTreeWidget and set dragDropMode to InternalMove. Moving items inside the tree works as expected, but I need to react to the items changing their order. Unfortunately, there's no signal that I could connect to that would let me do that.

    I obtained the handle to the underlying model via QTreeWidget::model() and connected to the model's rowsMoved() signal, but unfortunately, it doesn't seem to be emitted for internal moves.

    I also tried reimplementing dropEvent(), but I can't find a way to reliably obtain the destination row index there. From event->pos() I can obtain a QModelIndex of the item under the cursor, but this is useless, because while the drop indicator stays in place between two items, I get the index of the one above if I move the mouse a pixel up, and the one below if I move it down - even though the destination of the move is the same.

    How can I receive notifications of item moves and react to them?

  • I take it you are dynamically changing the order of the QTreeWidget when given certain data, or sorting.

    When you reimplemented the dropEvent() are you emitting a signal? If there isn't a signal you like you can always make your own.

    i'm not sure if this will help you at all:

  • The problem is not that I don't know how to emit my own signals, but that I have trouble finding out which item(s) moved exactly where.

    The way I see it, since QTreeWidget has signals like itemClicked(), itemChanged() etc., it should also have itemMoved(from, to) which would let me react to the move easily.

  • I figured it out:

    void ProjectTree::dropEvent(QDropEvent *event)
    // retrieve the target of the drop
    QTreeWidgetItem *target = itemAt(event->pos());
    DropIndicatorPosition dropPos = dropIndicatorPosition();
    // when the drop indicator points above an item, then this is the
    // correct one, otherwise, replace target item with the one below
    if (dropPos == BelowItem) target = itemBelow(target);

    // get list of selected items from the tree; these are the dragged
    // items
    QList<QTreeWidgetItem*> dragItems = selectedItems();

    // default implementation takes care of the actual move
    // notify subscribers of item move in some user-friendly way
    emit itemsMoved(...)

  • I'm not sure if currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous) gets triggered if you change the item's position, but it is worth a shot.

    You could also extend the QTreeWidget with a class that holds the current QModelIndex (cur) of the current pointer and the previous position (pre).

    When you do the internal movement set pre =cur, cur = indexFromItem(QTreeWidgtItem).
    Do a check to see if pre != cur or if pre!= some default value, then emit(moved_item(pre, cur)) and set pre to cur or a default value.

  • sorry for the late response, but I'm glad you got it

  • currentItemChanged() gets triggered, but it doesn't refer to the item that moved, but the one that occupies the space that it used to hold, so it's useless. Even more so when you move multiple items.

    I also figured out an another way to figure out the row numbers of the moving items from before and after the move, which is based on indexFromItem() like your suggestion:

    void MyTreeWidget::dropEvent(QDropEvent event)
    // get the list of the items that are about to be dropped
    > dragItems = selectedItems();

    // find out their row numbers before the drop
    QList<int> fromRows;
    QTreeWidgetItem *item;
    foreach(item, dragItems) fromRows.append(indexFromItem(item).row());
    // the default implementation takes care of the actual move inside the tree
    // query the indices of the dragged items again after the drop
    QList<int> toRows;
    foreach(item, dragItems) toRows.append(indexFromItem(item).row());
    // notify subscribers in some useful way
    emit itemsMoved(fromRows, toRows);


Log in to reply