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

Bob Hood bhood2 at comcast.net
Wed Feb 2 18:41:01 CET 2011


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.  I'd appreciate some additional eyes
and feedback on what I might be doing wrong.

First, I initialize my subclass:

    MyTreeWidget::MyTreeWidgetQWidget *parent)
        : QTreeWidget(parent)
    {
        setDragEnabled(true);
        setAcceptDrops(true);
        setDropIndicatorShown(true);
        setDragDropMode(QAbstractItemView::InternalMove);
        setSelectionMode(QAbstractItemView::SingleSelection);
        setDragDropOverwriteMode(true);
    }

Then I have various method overrides:

    void MyTreeWidget::mousePressEvent(QMouseEvent* event)
    {
        if(event->button() == Qt::LeftButton)
            dragStartPos = event->pos();

        QTreeWidget::mousePressEvent(event);
    }

    void MyTreeWidget::mouseMoveEvent(QMouseEvent *event)
    {
        if(!(event->buttons() & Qt::LeftButton) ||
           (event->pos() - dragStartPos).manhattanLength() <
QApplication::startDragDistance())
        {
            QTreeWidget::mouseMoveEvent(event);
            return;
        }

        QTreeWidgetItem* child = itemAt(event->pos());
        QSize icon_size;
        child->icon(0).actualSize(icon_size);

        QDrag *drag = new QDrag(this);
        QMimeData *mimeData = new QMimeData;

        mimeData->setData("text/mytype", QByteArray());
        drag->setMimeData(mimeData);
        drag->setPixmap(child->icon(0).pixmap(icon_size));
        drag->setHotSpot(event->pos());

        Qt::DropAction dropAction = drag->exec(Qt::MoveAction);
    }

    void MyTreeWidget::dragEnterEvent(QDragEnterEvent *event)
    {
        if(event->mimeData()->hasFormat("text/mytype"))
        {
            selected_items = selectedItems();
            if (event->source() == this)
            {
                event->setDropAction(Qt::MoveAction);
                event->accept();
            }
            else
                event->acceptProposedAction();
        }
        else
            event->ignore();
    }

    void MyTreeWidget::dragMoveEvent(QDragMoveEvent *event)
    {
        if(event->mimeData()->hasFormat("text/mytype"))
        {
            if (event->source() == this)
            {
                event->setDropAction(Qt::MoveAction);
                event->accept();
            }
            else
                event->acceptProposedAction();
        }
        else
            event->ignore();
    }

    void MyTreeWidget::dropEvent(QDropEvent *event)
    {
        if(event->source() != this && !(event->possibleActions() &
Qt::MoveAction))
            return;

        const QMimeData* mime_data = event->mimeData();
        if(!mime_data->hasFormat("text/mytype"))
        {
            event->ignore();
            return;
        }

        QTreeWidgetItem* target = itemAt(event->pos());
        foreach(QTreeWidgetItem* item, selected_items)
        {
            if(target->parent() != item->parent())
                return;     // can only drop within the same parent
        }

        event->setDropAction(Qt::MoveAction);
        event->accept();
    }

There are several issues I'm experiencing with this.

First, I'm not getting the visual indicator I would expect.  I would expect
the item(s) I'm dragging to have some kind of visual feedback (at least the
icon I'm setting).  What I'm getting is just an empty square, and no visual
indicators of where the drop will happen within the tree.

Secondly, I get a bunch of Assert dialogs telling me 'ASSERT failure in
QVariant::save: "Invalid type to save"'.  I haven't a clue what that is
indicating.  Each of my QTreeWidgetItems have Qt::UserRole data assigned (a
structure), and I'm wondering if that's what it is complaining about.  If
that's the case, how do I address this "save type" problem?

And lastly, when I tell the system to accept the action in the dropEvent()
method, nothing actually happens in the tree.  Am I supposed to be moving
things myself, or should I be passing things on by calling
QTreeWidget::dropEvent()?

This seems like a lot of bother just to allow items to be interactively moved
in a tree.  Am I doing too much here, or too little?




More information about the Qt-interest-old mailing list