Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/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: http://qt-project.org/wiki/Qt_for_beginners_Signals_and_slots_2
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
QList<QTreeWidgetItem*> dragItems = selectedItems();
// default implementation takes care of the actual move
// notify subscribers of item move in some user-friendly way
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
QList<QTreeWidgetItem> 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 QTreeWidget::dropEvent(event); // 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);