在自定义模型中启用拖放

1、实现:需要对以下四个虚函数重写

    Qt::DropActions supportedDropActions() const override;//设置支持放入动作
    QStringList mimeTypes() const override;//设置在拖放操作中导出的条目的数据的编码类型
    QMimeData *mimeData(const QModelIndexList &indexes) const override;//将数据放入QMimeData中
    //将拖放的数据放入模型中
    bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;

2、示例源码:

//widget.h
#ifndef WIDGET_H
#define WIDGET_H
 
#include <QAbstractListModel>
#include <QStringList>
 
//简单的、非层次结构的数据模型
class Widget : public QAbstractListModel
{
    Q_OBJECT
 
public:
    Widget(const QStringList &strings, QObject *parent = 0);
    ~Widget();
 
    //非层次结构,不需要考虑父子关系
    //如果模型是层次结构的,还需要实现index()和parent()函数
    //只读模型,需要实现的函数
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;//返回模型行数
    QVariant data(const QModelIndex &index, int role) const override;//返回指定模型索引的数据项
    //在树和表格视图的标头显示一些内容
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
 
    //将各种操作转换为对具体数据源的操作,从而对外提供一个统一的接口
    //实现界面可编辑,需要实现的函数
    //模型让委托知道项目是可编辑的,这里为每个项目返回一个正确的标识
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    //为委托向模型中设置数据提供一条途径
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
 
    //实现在模型中插入和删除行,需要实现的函数
    bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
    bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;

    //视图中显示数据由模型控制,为使用的模型提供拖放操作的支持
    Qt::DropActions supportedDropActions() const override;//设置支持放入动作
    QStringList mimeTypes() const override;//设置在拖放操作中导出的条目的数据的编码类型
    QMimeData *mimeData(const QModelIndexList &indexes) const override;//将数据放入QMimeData中
    //将拖放的数据放入模型中
    bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
private:
    //QAbstractItemModel本身不存储任何数据,仅提供一些接口来供视图访问数据
    //string_list_作为内部数据源
    QStringList string_list_;
};
#endif // WIDGET_H
 
//widget.cpp
#include "widget.h"
#include <QMimeData>
#include <QDataStream>
 
Widget::Widget(const QStringList &strings, QObject *parent)
    : QAbstractListModel(parent), string_list_(strings)
{
}
 
Widget::~Widget()
{
}
 
int Widget::rowCount(const QModelIndex &parent) const
{
    return string_list_.count();
}
 
QVariant Widget::data(const QModelIndex &index, int role) const
{
    if(!index.isValid()){
        return QVariant();
    }
    if(index.row() >= string_list_.size()){
        return QVariant();
    }
    if(role == Qt::DisplayRole || role == Qt::EditRole){
        return string_list_.at(index.row());
    }else {
        return QVariant();
    }
}
 
QVariant Widget::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role != Qt::DisplayRole){
        return QVariant();
    }
    if(orientation == Qt::Horizontal){
        return QString("Column %1").arg(section);
    }else {
        return QString("Row %1").arg(section);
    }
}
 
Qt::ItemFlags Widget::flags(const QModelIndex &index) const
{
    //如果该索引无效,只支持放入操作
    if(!index.isValid()){
        return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
    }
    //如果该索引有效,那么既支持拖拽操作,也支持放入操纵
    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
 
bool Widget::setData(const QModelIndex &index, const QVariant &value, int role)
{
    //保证索引是有效的、项目是正确的类型、角色是被支持的
    if(index.isValid() && role == Qt::EditRole){
        string_list_.replace(index.row(), value.toString());
        emit dataChanged(index, index);
        return true;
    }
    return false;
}
 
bool Widget::insertRows(int position, int rows, const QModelIndex &index)
{
    //告知其他组件指定的行将要发生改变
    beginInsertRows(QModelIndex(), position, position + rows -1);//父项的模型索引,要插入的第一个、最后一个新行的行号
    for(int row = 0; row < rows; ++row){
        string_list_.insert(position, "");//添加空的字符串
    }
    //告知其他组件该模型大小发生了变化
    endInsertRows();
    return true;
}
 
bool Widget::removeRows(int position, int rows, const QModelIndex &index)
{
    beginRemoveRows(QModelIndex(), position, position + rows -1);
    for(int row = 0; row < rows; ++row){
        string_list_.removeAt(position);
    }
    endRemoveRows();
    return true;
}

Qt::DropActions Widget::supportedDropActions() const
{
    return Qt::CopyAction | Qt::MoveAction;//复制和移动
}

QStringList Widget::mimeTypes() const
{
    //当在拖放操作中的数据项从模型中导出时,要被编码为合适的格式来对应一个或多个MIME类型
    QStringList types;
    //application/vnd.text.list是自定义的类型,后面的函数中要保持一致
    types << "application/vnd.text.list";//此处只支持纯文本类型
    return types;
}

QMimeData *Widget::mimeData(const QModelIndexList &indexes) const
{
    //在进行拖放操作之前,需要将数据放入到一个QMimeData类型的对象中
    QMimeData *mime_data = new QMimeData();
    QByteArray encoded_data;
    QDataStream stream(&encoded_data, QIODevice::WriteOnly);
    foreach(const QModelIndex &index, indexes){
        if(index.isValid()){
            QString text = data(index, Qt::DisplayRole).toString();
            stream << text;
        }
    }
    //将数据放入QMimeData中
    mime_data->setData("application/vnd.text.list", encoded_data);
    return mime_data;
}

bool Widget::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    //如果放入的动作是Qt::IgnoreAction,那么返回true
    if(action == Qt::IgnoreAction){
        return true;
    }
    //如果数据的格式不是指定的格式,返回false
    if(!data->hasFormat("application/vnd.text.list")){
        return false;
    }
    //因为这里是列表,只用一列,,所以列大于0时,返回false
    if(column >0){
        return false;
    }
    //设置开始插入的行
    int begin_row;
    if(row != -1){
        begin_row = row;
    }else if(parent.isValid()){
        begin_row = parent.row();
    }else{
        begin_row = rowCount(QModelIndex());
    }
    //将数据从QMimeData中读出来,然后插入到模型中
    QByteArray encoded_data = data->data("application/vnd.text.list");
    QDataStream stream(&encoded_data, QIODevice::ReadOnly);
    QStringList new_items;
    int rows = 0;
    while(!stream.atEnd()){
        QString text;
        stream >> text;
        new_items << text;
        ++rows;
    }
    insertRows(begin_row, rows, QModelIndex());
    foreach(const QString &text, new_items){
        QModelIndex idx = index(begin_row, 0, QModelIndex());
        setData(idx, text);
        begin_row++;
    }
    return true;
}

 

//main.cpp
#include "widget.h"
 
#include <QApplication>
#include <QTableView>
#include <QListView>
#include <QDebug>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
 
    QStringList list;
    list << "a" << "b" << "c";
    Widget model(list);
    QListView listView;
    listView.setModel(&model);
    listView.setSelectionMode(QAbstractItemView::ExtendedSelection);
    listView.setDragEnabled(true);
    listView.setAcceptDrops(true);
    listView.setDropIndicatorShown(true);
    listView.show();
 
    //添加和删除
    model.insertRows(3,2);
    model.removeRows(0,1);
    return a.exec();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值