C++ Qt4:从QAbstractTableModel读取默认mimeData

C++ Qt4:从QAbstractTableModel读取默认mimeData,c++,user-interface,qt,qt4,C++,User Interface,Qt,Qt4,默认情况下,QAbstractTableModel类有一个mimeData()函数,该函数返回一个QMimeData对象,该对象的数据设置为编码的QModelIndexList(请参阅)。我想在重载的dropMimeData()函数中解压此数据,但不知道如何将此QMimeData转换回QModelIndexList。我尝试了明显的方法: bool myTableModel::dropMimeData(const QMimeData * mimeData, Qt::DropAction actio

默认情况下,
QAbstractTableModel
类有一个
mimeData()
函数,该函数返回一个
QMimeData
对象,该对象的数据设置为编码的
QModelIndexList
(请参阅)。我想在重载的
dropMimeData()
函数中解压此数据,但不知道如何将此
QMimeData
转换回
QModelIndexList。
我尝试了明显的方法:

bool myTableModel::dropMimeData(const QMimeData * mimeData, Qt::DropAction action, int row, int column, const QModelIndex & parent)
{
  QStringList formats = mimeData->formats();

  QByteArray encodedData = mimeData->data(formats[0]);
  QDataStream stream(&encodedData, QIODevice::ReadOnly);
  QModelIndexList list;
  stream >> index;
}
但是得到错误:

 no match for ‘operator>>’ in ‘stream >> ((myTableModel*)this)->QAbstractTableModel::index’
因为QModelIndex没有>>运算符


注意:这个问题是一个更加集中的版本。很抱歉,如果这个问题这么简单,我对这里有点陌生。

明白了,多亏卡莱布·彼得森在老问题的链接上:

bool ObjectAnimation::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
{
  QStringList formats = data->formats();
  QByteArray encodedData = data->data(formats[0]);
  QDataStream stream(&encodedData, QIODevice::ReadOnly);

  int row, column;
  stream >> row >> column;

  qDebug() << "row: " << row << " column:" << column;

  return false;
}
bool ObjectAnimation::dromimedata(常量qimedata*数据,Qt::DropAction动作,int行,int列,常量QModelIndex&parent)
{
QStringList formats=数据->格式();
QByteArray encodedData=数据->数据(格式[0]);
QDataStream(encodedData和QIODevice::ReadOnly);
int行,列;
流>>行>>列;

qDebug()6年后,但如果将来有人碰到这个问题,这里有一个更完整的解决方案。它还可以处理有类型但无效的QVariants(因为它们不可序列化)的情况。在my中(相关,但不完全相同)在用例中,我最关心的是源和目标QVariants的类型兼容,我不太关心它的值,但是代码应该可以工作

#include <QtCore/QVariant>
#include <QtCore/QPair>

class QMimeData;

class QModelDataListDecoderPrivate;

/*
* The default model implementation adds the `application/x-qabstractitemmodeldatalist`
* MIME Type. This things is totally undocumented, but some reverse engineering
* of it's encoder indicate it is (as of Qt 5.6) an array of:
*
*     Tuple<int, int, QMap<int, QVaritant>>
*
* pushed in a `QDataStream`. This format suck, as it's not really able to
* express the source `QModelIndex`. However, it does contain the QVariant
* of some of its role. From them, even the invalid ones, the QMetaType id
* should be pushed into the stream. It should have been easy, but there
* is another problem. QVariant::load exits early when decoding a QMetaType
* that cannot be encoder. While it prints the QMetaType on stderr, it doesn't
* export it. This, in turn, causes another problem where the QMap will be empty
* if a single element fail to be deserialized. This little class implements a
* serializable Qt type that mimics the QVariant decoder to be able to extract
* the correct type.
*
* The QVariant data is encoded as (it is stable and documented):
*
*  * The type of the data (quint32)
*  * The null flag (qint8)
*  * The data of the specified type
* 
* Reference:
*
*  * http://doc.qt.io/qt-5/datastreamformat.html
*  * qvariant.cpp
*  * qabstractitemmodel.cpp
*/
class QModelDataListDecoder
{
public:
    explicit QModelDataListDecoder(const QMimeData* data);
    virtual ~QModelDataListDecoder();

    bool canConvert(quint32 typeId, int role = Qt::EditRole, int row = -1, int column = -1) const;

    template<typename T>
    bool canConvert(int role = Qt::EditRole) const {
        return canConvert(qMetaTypeId<T>(), role);
    }

    QVariant data(int role, int row = -1, int column = -1) const;

    int count();

    QPair<int, int> firstElement() const;

    quint32 typeId(int role = Qt::EditRole, int row = -1, int column = -1) const;

private:
    QModelDataListDecoderPrivate* d_ptr;
};

#include <QtCore/QMimeData>
#include <QtCore/QDataStream>
#include <QtCore/QDebug>

// Be less strict about unserializable values that are part of the stream.
class QLousyVariantDecoder
{
public:
    class QVariantExt : public QVariant {
    public:
        inline void createProxy(int typeId) { create(typeId, Q_NULLPTR); }
    };

    quint32     metaType;
    qint8       isNull;
    QVariantExt variant ;
    QByteArray  userType;
    bool        isLoaded;
};

class QModelDataListDecoderPrivate
{
public:
    QHash<QPair<int, int>, QMap<int, QLousyVariantDecoder> > m_Data;
};

QDebug operator<<(QDebug debug, const QLousyVariantDecoder &v)
{
    return debug << QStringLiteral("<<<")
        << QStringLiteral("Type:"      ) << v.metaType << v.userType
        << QStringLiteral(", Is NULL:" ) << v.isNull
        << QStringLiteral(", Is valid:") << v.isLoaded
        << QStringLiteral(", Value:"   ) << v.variant
        << QStringLiteral(">>>");
}

QDataStream &operator<<(QDataStream &s, const QLousyVariantDecoder &self)
{
    return s << self.variant;
}

QDataStream &operator>>(QDataStream &s, QLousyVariantDecoder &self)
{
    // There is no Qt ways to doing this before 5.7 beside creating it by hand
    // Qt5.7 support transactions, but replicating the exact behavior of the
    // following code is longer than not using transactions.
    s >> self.metaType;
    s >> self.isNull;

    if (self.metaType == QVariant::UserType) {
        s >> self.userType;
        self.metaType = QMetaType::type(self.userType.constData());

        if (self.metaType == QMetaType::UnknownType) {
            s.setStatus(QDataStream::ReadCorruptData);
            return s;
        }
    }
    else
        self.userType = QMetaType::typeName(self.metaType);

    if (!self.isNull) {
        self.variant.createProxy(self.metaType);

        void* data = const_cast<void *>(self.variant.constData());

        // Ignore errors, as the way it is implemented, the field is empty,
        // so the streams remains valid. However dropping the data wont work.
        self.isLoaded = QMetaType::load(s, self.variant.type(), data);
    }

    return s;
}

// hack to execute code at a RANDOM moment during initialization.
static auto _DUMMY = ([]()->bool {
    qRegisterMetaType               <QLousyVariantDecoder>("QLousyVariantDecoder");
    qRegisterMetaTypeStreamOperators<QLousyVariantDecoder>("QLousyVariantDecoder");
    return true;
})();

QModelDataListDecoder::QModelDataListDecoder(const QMimeData* data)
    : d_ptr(new QModelDataListDecoderPrivate)
{
    if (!data)
        return;

    // Check all payloads if one can be converted to the right QMetaType
    auto buf = data->data("application/x-qabstractitemmodeldatalist");

    if (buf.isEmpty())
        return;

    QDataStream s(buf);

    while (!s.atEnd()) {
        int r, c;
        QMap<int, QLousyVariantDecoder> v;
        s >> r >> c >> v;

        // only add valid items
        if (r+1 && c+1)
            d_ptr->m_Data[{r, c}] = std::move(v);
    }
}

QModelDataListDecoder::~QModelDataListDecoder()
{
    delete d_ptr;
}

QPair<int, int> QModelDataListDecoder::firstElement() const
{
    if (d_ptr->m_Data.isEmpty())
        return {-1,-1};

    return d_ptr->m_Data.begin().key();
}

bool QModelDataListDecoder::canConvert(quint32 typeId, int role, int row, int col) const
{
    auto v = data(role, row, col);

    if (v.isValid())
        return v.canConvert(typeId);

    const auto pair = (row+1&&col+1) ? QPair<int,int>(row, col) : firstElement();

    if (!d_ptr->m_Data.contains(pair))
        return false;

    auto info = d_ptr->m_Data[pair][role];

    if (info.metaType == typeId)
        return true;

    QLousyVariantDecoder::QVariantExt var;
    var.createProxy(info.metaType);

    return var.canConvert(typeId);;
}

QVariant QModelDataListDecoder::data(int role, int row, int col) const
{
    const auto pair = (row+1&&col+1) ? QPair<int,int>(row, col) : firstElement();

    if (!d_ptr->m_Data.contains(pair))
        return {};

    return d_ptr->m_Data[pair][role].variant;
}

quint32 QModelDataListDecoder::typeId(int role, int row, int col) const
{
    const auto pair = (row+1&&col+1) ? QPair<int,int>(row, col) : firstElement();

    if (!d_ptr->m_Data.contains(pair))
        return QMetaType::UnknownType;

    const auto data = d_ptr->m_Data[pair];

    if (!data.contains(role))
        return QMetaType::UnknownType;

    return data[role].metaType;
}
#包括
#包括
类qimedata;
类QModelDataListDecoderPrivate;
/*
*默认模型实现添加了`application/x-qabstractemmodeldatalist`
*MIME类型。这些东西完全没有文档记录,但有些是逆向工程
*它的编码器表示它(从Qt 5.6开始)是以下数组:
*
*元组
*
*在“QDataStream”中推送。这种格式很糟糕,因为它实际上无法
*表示源“QModelIndex”。但是,它确实包含QVariant
*从它们,甚至是无效的,QMetaType id
*应该被推到溪流中。本来应该很容易,但是没有
*是另一个问题。QVariant::load在解码QMetaType时提前退出
*这不能是编码器。当它在stderr上打印QMetaType时,它不会
*导出它。这反过来会导致另一个问题,QMap将为空
*如果单个元素反序列化失败,这个小类实现
*可串行化Qt类型,模拟QVariant解码器,以便能够提取
*正确的类型。
*
*QVariant数据编码为(稳定且记录在案):
*
**数据类型(quint32)
**空标志(qint8)
**指定类型的数据
* 
*参考:
*
*  * http://doc.qt.io/qt-5/datastreamformat.html
**qvariant.cpp
**qabstractemmodel.cpp
*/
类QModelDataListDecoder
{
公众:
显式qmodeldatalist解码器(常量QMimeData*data);
虚拟~QModelDataListDecoder();
boolcanconvert(quint32 typeId,int-role=Qt::EditRole,int-row=-1,int-column=-1)常量;
模板
布尔canConvert(int-role=Qt::EditRole)常量{
返回canConvert(qMetaTypeId(),角色);
}
QVariant数据(int-role,int-row=-1,int-column=-1)常量;
int count();
QPair firstElement()常量;
quint32 typeId(int-role=Qt::EditRole,int-row=-1,int-column=-1)常量;
私人:
QModelDataListDecoderPrivate*d_ptr;
};
#包括
#包括
#包括
//对于属于流一部分的不可变量化值,不要太严格。
类QLousyVariantDecoder
{
公众:
类别QVariant文本:公共QVariant{
公众:
内联void createProxy(int-typeId){create(typeId,Q_-NULLPTR);}
};
第32亚型;
qint8为空;
文本变体;
QByteArray用户类型;
布尔孤岛;
};
类QModelDataListDecoderPrivate
{
公众:
QHash m_数据;
};
QDebug操作符>c>>v;
//仅添加有效项
如果(r+1&&c+1)
d_ptr->m_Data[{r,c}]=std::move(v);
}
}
QModelDataListDecoder::~QModelDataListDecoder()
{
删除d_ptr;
}
QPair QModelDataListDecoder::firstElement()常量
{
if(d_ptr->m_Data.isEmpty())
返回{-1,-1};
返回d_ptr->m_Data.begin().key();
}
boolqmodeldatalist解码器::canConvert(quint32类型ID,int角色,int行,int列)常量
{
自动v=数据(角色、行、列);
if(v.isValid())
返回v.canConvert(typeId);
常量自动对=(行+1和列+1)?QPair(行,列):firstElement();
如果(!d_ptr->m_Data.contains(pair))
返回false;
自动信息=d_ptr->m_数据[对][角色];
if(info.metaType==typeId)
返回true;
QLousyVariantDecoder::QvariantText变量;
var.createProxy(info.metaType);
返回变量canConvert(typeId);;
}
QVariant QModelDataListDecoder::数据(int角色、int行、int列)常量
{
常量自动对=(行+1和列+1)?QPair(行,列):firstElement();
如果(!d_ptr->m_Data.contains(pair))
返回{};
返回d_ptr->m_Data[pair][role]。变量;
}
quint32 QModelDataListDecoder::typeId(int角色、int行、int列)常量
{
常量自动对=(行+1和列+1)?QPair(行,列):firstElement();
如果(!d_ptr->m_Data.contains(pair))
返回QMetaType::UnknownType;
const auto data=d_ptr->m_data[pair];
如果(!data.contains(角色))
返回QMetaType::UnknownType;
返回数据[角色]。元类型;
}