[Qt-interest] Need help with moving items in a QTreeWidget

Bob Hood bhood2 at comcast.net
Thu Feb 3 03:20:52 CET 2011


On 2/2/2011 10:41 AM, Bob Hood wrote:
> I've been trying to figure this one out, and I'm not having much luck.  I've
> got a QTreeWidget that has top-level parents, and under them are children
> without any other children.  I want to be able to drag children and re-order
> them within their parents, but not allow them to be moved outside their parent
> (i.e., disallow re-parent).  I understand the only way to realize this is to
> subclass QTreeWidget and provide my own drag-and-drop support, however, I
> cannot seem to get it to work correctly.

Ok, after paring down the code I posted, and realizing that I needed to
implement QDataStream operations for the Qt::UserRole-associated data that
each QTreeWidgetItem carries, I have the drag-and-drop operation working
(almost) the way I want and expect.

However, I still need some insight from somebody.  When the dropEvent()
finally occurs, I cannot seem to find a way to determine where the drop will
actually end up.  In other words, the visual cues that are provided during the
drag operation indicate either an insertion, or a re-parenting (dropping the
dragged child onto another child causes re-parenting).  I want to disallow
this re-parenting, but I cannot find a way to determine if the drop point will
be an insert (moved to a new position under the original parent) or if it will
be a re-parent (parented to another child).  In effect, I want the children
under a top-level parent to be moved around like a QListWidget -- no parenting
allowed.

Here's my dropEvent() logic:

    void MyTreeWidget::dropEvent(QDropEvent *e)
    {
        const QMimeData* mime_data = e->mimeData();
        if(!mime_data->hasFormat("application/mytype"))
        {
            e->ignore();
            return;
        }

        if(e->keyboardModifiers() == Qt::NoModifier)
        {
            const QPoint pos = e->pos();
            QTreeWidgetItem* target = itemAt(e->pos());

            bool do_move = true;

            // this is used to determine if the target is itself a child
            if(target->parent() != (QTreeWidgetItem*)0)
                do_move = false;
            else
            {
                foreach(QTreeWidgetItem* item, selected_items)
                {
                    // if target and item don't share the same parent...
                    if(target->parent() != item->parent())
                    {
                        // ...then don't allow the move
                        do_move = false;
                        break;
                    }
                }
            }

            if(!do_move)
                e->setDropAction(Qt::IgnoreAction);
            else
            {
                QTreeWidget::dropEvent(e);
                e->setDropAction(Qt::TargetMoveAction);
            }

            e->accept();
        }
        else if(e->proposedAction() == Qt::TargetMoveAction)
        {
            QTreeWidget::dropEvent(e);
            e->acceptProposedAction();
        }
        else
            QTreeWidget::dropEvent(e);
    }

The problem is, with an insertion, the line:

    QTreeWidgetItem* target = itemAt(e->pos());

still returns a child item, even though the visual feedback says that the drop
will be inserted above (or below) it.  Without knowing this difference, I
cannot disallow re-parenting.



More information about the Qt-interest-old mailing list