QListView 外部放置不起作用

QListView external drop doesn't work

我正在尝试实现从一个 QListView 到另一个 QListView 的拖放项目 (plain/text)。拖动开始很好(我什至能够将项目拖放到另一个接受文本拖放的应用程序),但我的第二个 QListView 出于某种原因不接受拖放。 以下是列表视图的配置方式:

ui->lessonsListView->setAcceptDrops(true);
ui->lessonsListView->setDropIndicatorShown(true);
ui->lessonsListView->setDragDropMode(QAbstractItemView::DropOnly);
ui->lessonsListView->setDragDropOverwriteMode(true);

此 listView 的代理模型实现了以下方法:

Qt::ItemFlags LessonsProxyModel::flags(const QModelIndex &index) const
{
    qDebug() << __FUNCTION__;
    return Qt::ItemIsDropEnabled | QSortFilterProxyModel::flags(index);
}

Qt::DropActions LessonsProxyModel::supportedDropActions() const
{
    qDebug() << __FUNCTION__;
    return Qt::MoveAction;
}

bool LessonsProxyModel::canDropMimeData(const QMimeData *data,
    Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    qDebug() << __FUNCTION__;
    Q_UNUSED(action);
    Q_UNUSED(row);
    Q_UNUSED(column);

    if (!data->hasFormat("text/plain") || !parent.isValid())
        return false;

    return true;
}

bool LessonsProxyModel::dropMimeData(const QMimeData *data,
    Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    qDebug() << __FUNCTION__;
    if (!canDropMimeData(data, action, row, column, parent))
        return false;

    emit dataDropped(data, parent);

    return true;
}

从应用程序输出中我看到只有 supportedDropActions()flags() 被调用。 canDropMimeData()dropMimeData() 都没有调用过。我究竟做错了什么? 任何提示将不胜感激。

谢谢!

已编辑:

以防万一:下面是 listView 的源代码,并且启动了那些拖动的模型: 列表视图设置:

ui->abonsListView->setDragEnabled(true);

代理型号代码:

Qt::ItemFlags AbonsProxyModel::flags(const QModelIndex &index) const
{
    return Qt::ItemIsDragEnabled | QSortFilterProxyModel::flags(index);
}

Qt::DropActions AbonsProxyModel::supportedDragActions() const
{
    qDebug() << __FUNCTION__;
    return Qt::MoveAction;
}

QStringList AbonsProxyModel::mimeTypes() const
{
    qDebug() << __FUNCTION__;
    QStringList types;
    types << "text/plain";
    return types;
}

QMimeData *AbonsProxyModel::mimeData(const QModelIndexList &indexes) const
{
    qDebug() << __FUNCTION__;
    QMimeData *mimeData = new QMimeData();

    foreach (const QModelIndex &index, indexes)
        if (index.isValid())
        {
            mimeData->setText(data(index, AbonsModel::Id).toString());
            qDebug() << __FUNCTION__;
            return mimeData;
        }

    return mimeData;
}

我对代理模型的经验不多。通过文档,我想我对这两个功能是什么有一个大概的了解。这些函数不会自动调用。你必须 "call" 从你的角度来看它们。

首先,检查您是否拥有正确的数据是模型的工作。因此,这是模型中的两个便利功能,可以简化您的工作。这些是特定于模型的函数而不是通用函数,因此永远不会为抽象模型定义。

考虑这个场景。从某个地方,一个 QMimeData 对象被放入您的视图中。您如何知道此数据的类型是否正确?由谁来决定数据的类型是否正确?

Model-View框架中,View只做绘画工作。由 Model 决定诸如如何处理数据、显示什么、什么是可以接受的以及什么不是。所以当在 View 上丢了一个数据,你需要将它发送到 Model 来检查丢下的数据是否满足 Model 的要求。完成这项工作的钩子是 bool Model::canDropMimeData(...).

如果可以接受,那么数据的处理也需要Model自己完成。同样,您需要将数据发送到 Model。钩子是 bool Model::dropMimeData(...).

如何知道数据是否处理成功?检查 return 值!如果数据处理成功,那么您很可能需要更新 View.

因此,除了上面的代码之外,您必须 覆盖接收 dragEnterEventdragMoveEventdropEvent View 这样你就可以调用 canDropMimeData(...)dropMimeData(...)

/* You need to allow the drag to enter the widget/view. */
/* This is a must. */
void TargetView::dragEnterEvent( QDragEnterEvent *deEvent ) {

    deEvent->acceptProposedAction();
};

/* This is optional. Use this to check if the data can be */
/* dropped at the current mouse position. Example: In a */
/* FileSystem View, it makes no sense to drop a bunch of */
/* files on top of a file, but makes sense it you drop it */
/* in an empty space or on a folder */
void TargetView::dragMoveEvent( QDragMoveEvent *dmEvent ) {
    /* You can do something like this */
    if ( indexAt( dmEvent->pos() ).isValid() ) {
        /* You cannot drop it on an existing index */
        /* If you ignore it, the cursor changes to */
        /* 'don't drop' image */
        dmEvent->ignore();
    }

    else {
        /* Empty space. Tell the user to feel free */
        /* to perform the drop. We accept the event */
        /* Cursor shows the drag+drop icon */
        dmEvent->accept();
    }
};

void TargetView::dropEvent( QDropEvent *dpEvent ) {
    /* Here is where you call canDropMimeData and dropMimeData */
    QMimeData *mData = dpEvent->mimeData();
    if ( model->canDropMimeData( mData, ..., ..., ..., ... ) ) {
        /* Add the data to the model */
        bool updated = model->dropMimeData( mData, ..., ..., ..., ... );
        if ( updated ) {
            /* Intimate the view to update itself */
            update();
        }
    }
};

我终于找到答案了!当我开始编写这段代码时,我从 Qt 文档 Using Drag and Drop with Item Views 中复制粘贴了一些片段,在这篇文章中,他们只是错过了 const 说明符以重新实现 canDropMimeData()。因此,我的 canDropMimeData() 版本变成了非虚拟的,而 QListView 只是从基础 class QAbstractProxyModel 调用了方法。我添加了 const - 没有任何子 classing,一切都像魅力一样工作。