Qt模型视图框架:在视图中使用拖放

模型/视图框架完全支持 Qt 的拖放基本操作。列表、表格、树中的项目可以在视图内拖动,数据可以作为 MIME 编码数据导入和导出。 

标准视图自动支持内部拖放,在其中移动项目以更改它们的显示顺序。默认情况下,不为这些视图启用拖放,因为它们被配置为最简单、最常见的用途。要允许拖动项目,需要启用视图的某些属性,并且项目本身也必须允许拖动发生。

1、使用便利类

QListWidget为例,以下代码行在列表小部件中启用拖放:

    QListWidget listWidget;
    listWidget.setSelectionMode(QAbstractItemView::SingleSelection);
    listWidget.setDragEnabled(true);
    listWidget.viewport()->setAcceptDrops(true);
    listWidget.setDropIndicatorShown(true);

    for(int i = 0;i < 5;++i)
        listWidget.insertItem(i,QString("data_%1").arg(i));

    listWidget.show();

结果是列表小部件允许在视图内复制项目,甚至允许用户在包含相同类型数据的视图之间拖动项目。 在这两种情况下,项目都被复制而不是移动。

为了能够在视图内移动项目,必须设置列表小部件的 dragDropMode:

    listWidget.setDragDropMode(QAbstractItemView::InternalMove);

2、使用模型/视图类

设置拖放视图遵循与便利视图相同的方式。例如,QListView 可以像 QListWidget 一样设置:

    QListView listView;
    listView.setSelectionMode(QAbstractItemView::ExtendedSelection);
    listView.setDragEnabled(true);
    listView.setAcceptDrops(true);
    listView.setDropIndicatorShown(true);

由于对视图显示的数据的访问是由模型控制的,因此所使用的模型还必须提供对拖放操作的支持。 模型支持的操作可以通过重新实现 QAbstractItemModel::supportedDropActions() 函数来指定。 例如,使用以下代码启用复制和移动操作:

#include <QStandardItemModel>

class DragDropListModel : public QStandardItemModel
{
public:
    explicit DragDropListModel(QObject *parent = nullptr);
    virtual Qt::DropActions supportedDropActions() const;
};

DragDropListModel::DragDropListModel(QObject *parent) : QStandardItemModel(parent)
{
}

Qt::DropActions DragDropListModel::supportedDropActions() const
{
    return Qt::CopyAction | Qt::MoveAction;
}
    QListView listView;
    listView.setSelectionMode(QAbstractItemView::ExtendedSelection);
    listView.setDragEnabled(true);
    listView.setAcceptDrops(true);
    listView.setDropIndicatorShown(true);

    DragDropListModel * model = new DragDropListModel;
    model->setColumnCount(1);
    model->setRowCount(5);

    for(int i = 0;i < 5;++i)
        model->setData(model->index(i,0),QString("data_%1").arg(i));

    listView.setModel(model);
    listView.show();

3、启用项目的拖放

模型通过重新实现 QAbstractItemModel::flags() 函数以提供合适的标志,向视图指示哪些项目可以拖动,哪些将接受放置。

例如,为行号小于5的项设置Qt::ItemIsDragEnabled 和 Qt::ItemIsDropEnabled 值,即行号小于5的项可拖放:

Qt::ItemFlags DragDropListModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags defaultFlags = QStandardItemModel::flags(index);

    if (index.row() < 5)
        return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
    else
        return Qt::ItemIsEnabled;
}

8.4、拖放的MIME数据

在拖放操作中从模型导出数据项时,它们被编码为与一种或多种 MIME 类型相对应的适当格式。 模型通过重新实现 QAbstractItemModel::mimeTypes() 函数并返回标准 MIME 类型列表来声明它们可用于提供项目的 MIME 类型。

例如,仅提供纯文本的模型将提供以下实现:

QStringList DragDropListModel::mimeTypes() const
{
    QStringList types;
    types << "application/vnd.text.list";
    return types;
}

QAbstractItemModel::mimeData() 函数提供 QMimeData 对象的具体内容。

以下代码将纯文本存储在 QMimeData 对象中:

QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData;
    QByteArray encodedData;

    QDataStream stream(&encodedData, QIODevice::WriteOnly);

    for (const QModelIndex &index : indexes) {
        if (index.isValid()) {
            QString text = data(index, Qt::DisplayRole).toString();
            stream << text;
        }
    }

    mimeData->setData("application/vnd.text.list", encodedData);
    return mimeData;
}

8.5、将拖入的数据插入模型

拖入的数据由模型重新实现的 QAbstractItemModel::dropMimeData() 处理。

模型可以通过重新实现 QAbstractItemModel::canDropMimeData() 来禁止删除某些项目。

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

    if (!data->hasFormat("application/vnd.text.list"))
        return false;

    if (column > 0)
        return false;

    return true;
}

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

    if (action == Qt::IgnoreAction)
        return true;

    int beginRow;

    if (row != -1)
        beginRow = row;
    else if (parent.isValid())
        beginRow = parent.row();
    else
        beginRow = rowCount(QModelIndex());

    QByteArray encodedData = data->data("application/vnd.text.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;
    int rows = 0;

    while (!stream.atEnd()) {
        QString text;
        stream >> text;
        newItems << text;
        ++rows;
    }

    insertRows(beginRow, rows, QModelIndex());
    for (const QString &text : qAsConst(newItems)) {
        QModelIndex idx = index(beginRow, 0, QModelIndex());
        setData(idx, text + "__拖入数据");
        beginRow++;
    }

    return true;
}

汇总:

#include <QStandardItemModel>

class DragDropListModel : public QStandardItemModel
{
public:
    explicit DragDropListModel(QObject *parent = nullptr);
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    QStringList mimeTypes() const override;
    QMimeData *mimeData(const QModelIndexList &indexes) const override;
    bool canDropMimeData(const QMimeData *data, Qt::DropAction action,
                         int row, int column, const QModelIndex &parent) const override;
    bool dropMimeData(const QMimeData *data, Qt::DropAction action,
                      int row, int column, const QModelIndex &parent)override;
};

DragDropListModel::DragDropListModel(QObject *parent) : QStandardItemModel(parent)
{
}

Qt::ItemFlags DragDropListModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags defaultFlags = QStandardItemModel::flags(index);
    return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
}

QStringList DragDropListModel::mimeTypes() const
{
    QStringList types;
    types << "application/vnd.text.list";

    return types;
}

QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData;
    QByteArray encodedData;

    QDataStream stream(&encodedData, QIODevice::WriteOnly);

    for (const QModelIndex &index : indexes)
    {
        if (index.isValid())
        {
            QString text = data(index, Qt::DisplayRole).toString();
            stream << text;
        }
    }

    mimeData->setData("application/vnd.text.list", encodedData);
    return mimeData;
}

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

    if (!data->hasFormat("application/vnd.text.list"))
        return false;

    if (column > 0)
        return false;

    return true;
}

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

    if (action == Qt::IgnoreAction)
        return true;

    int beginRow;

    if (row != -1)
        beginRow = row;
    else if (parent.isValid())
        beginRow = parent.row();
    else
        beginRow = rowCount(QModelIndex());

    QByteArray encodedData = data->data("application/vnd.text.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;
    int rows = 0;

    while (!stream.atEnd()) {
        QString text;
        stream >> text;
        newItems << text;
        ++rows;
    }

    insertRows(beginRow, rows, QModelIndex());
    for (const QString &text : qAsConst(newItems)) {
        QModelIndex idx = index(beginRow, 0, QModelIndex());
        setData(idx, text + "__拖入数据");
        beginRow++;
    }

    return true;
}
    QListView listView;
    listView.setSelectionMode(QAbstractItemView::ExtendedSelection);
    listView.setDragEnabled(true);
    listView.setAcceptDrops(true);
    listView.setDropIndicatorShown(true);

    DragDropListModel * model = new DragDropListModel;
    model->setColumnCount(1);
    model->setRowCount(10);

    for(int i = 0;i < 10;++i)
        model->setData(model->index(i,0),QString("data_%1").arg(i));

    listView.setModel(model);
    listView.show();

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值