2010-01-27 31 views
2

閱讀默認mimeData默認情況下,QAbstractTableModel類具有mimeData()函數返回一個QMimeData對象,其具有它的數據集合作爲經編碼QModelIndexList(見here)。我想解壓在一個重載dropMimeData()功能這個數據,但無法弄清楚如何轉換這種QMimeData回一個QModelIndexList.我嘗試了很明顯的:的Qt4:從QAbstractTableModel

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沒有>>運算符。

注意:這個問題是this one更加集中的版本。對不起,如果這打破了SO ettiquete,我在這裏有點新。

回答

2

明白了,在這個老問題的鏈接感謝卡萊布·彼得森:

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; 
} 
1

6年較晚,但萬一有人打的問題,在未來,這裏有一個更完整的解決方案。它也處理有鍵入的情況,但無效的QVariants(因爲它們不是可序列化的)。在我的(相關的,但不完全相同的)用例中,我主要關心源和目標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; 
}