Qt AbstractItemModel重新排列图像

时间:2015-07-26 10:43:20

标签: qt drag-and-drop qlistview qabstractitemmodel

我想在qlistview中重新排列一组图像。我看过这些例子,但我无法让它发挥作用。当我将图像拖到另一个图像上时,会执行dropomimedata(),但其“data-> hasImage()”始终为false。当我出于某种原因在空白区域中放置Image时,根本没有触发dropmimedata()。

我的模型应如下所示:

enter image description here

然而,在拖入空白区后,它看起来像这样:

enter image description here

当我将图像拖到另一个图像上时,没有任何更改,因为hasImage始终为false。我究竟做错了什么?我错过了什么?

#include "spritemodel.h"

#include <QDebug>
#include <QMimeData>

SpriteModel::SpriteModel() : QAbstractListModel()
{
}

void SpriteModel::setContents(QList<QPair<QImage, QOpenGLTexture*>> &newList)
{
    beginInsertRows(QModelIndex(), 0, newList.size());
    imageList = newList;
    endInsertRows();
}

int SpriteModel::rowCount(const QModelIndex & parent) const
{
    Q_UNUSED(parent);
    return imageList.size();
}

QVariant SpriteModel::data(const QModelIndex & index, int role) const
{
    if (role == Qt::DecorationRole)
        return imageList[index.row()].first;
    else if (role == Qt::DisplayRole)
        return "";
    else
        return QVariant();
}

Qt::DropActions SpriteModel::supportedDropActions() const
{
    return Qt::CopyAction | Qt::MoveAction;
}

Qt::ItemFlags SpriteModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);

    if (index.isValid())
        return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
    else
        return Qt::ItemIsDropEnabled | defaultFlags;
}

bool SpriteModel::removeRows(int position, int rows, const QModelIndex &parent)
{
    beginRemoveRows(QModelIndex(), position, position+rows-1);

    for (int row = 0; row < rows; ++row) {
        imageList.removeAt(position);
    }

    endRemoveRows();
    return true;
}

bool SpriteModel::insertRows(int position, int rows, const QModelIndex &parent)
{
    beginInsertRows(QModelIndex(), position, position+rows-1);

    QImage img(imageList[0].first.width(), imageList[0].first.height(), imageList[0].first.format());
    QOpenGLTexture *texture = new QOpenGLTexture(img);

    for (int row = 0; row < rows; ++row) {
        imageList.insert(position, qMakePair(img, texture));
    }

    endInsertRows();
    return true;
}

bool SpriteModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    QImage img = value.value<QImage>();
    QOpenGLTexture *texture = new QOpenGLTexture(img);

    if (index.isValid() && role == Qt::EditRole) {

        imageList.replace(index.row(), qMakePair(img, texture));
        emit dataChanged(index, index);
        return true;
    }
    return false;
}

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

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

    foreach (const QModelIndex &index, indexes) {
        if (index.isValid()) {
            QVariant img = data(index, Qt::DecorationRole);
            stream << img;
        }
    }

    mimeData->setData("image/png", encodedData);
    return mimeData;
}

bool SpriteModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    if (data->hasImage()) {
        qDebug() << "wut";
        QImage img = qvariant_cast<QImage>(data->imageData());
        QOpenGLTexture *texture = new QOpenGLTexture(img);

        beginInsertRows(parent, 0, 1); // test
        imageList.insert(row, qMakePair(img, texture));
        endInsertRows();

        emit dataChanged(QModelIndex(),QModelIndex());
        return true;
    }
    return false;
}

1 个答案:

答案 0 :(得分:1)

QDataStream将您的图像数据编码为png。例如,为什么不将它编码为bmp或gif?您要编码的默认mimetype为application/x-qabstractitemmodeldatalist - 因为您没有覆盖mimeTypes()

由于您可能希望支持一次移动多个项目,因此您应该坚持使用x-qabstractitemmodeldatalist,并对其进行适当的编码/解码。有关详细信息,请参阅this question

请注意,对于hasImage,此mimetype not 将返回true,因为数据是角色值映射列表。

其他问题:

  1. 你在整个地方泄漏了纹理。您应该使用std::shared_ptrQSharedPointer而不是原始指针。

  2. insertrows将相同的纹理实例放入多个条目中。如果您尝试删除纹理,除非使用共享指针,否则将无法避免多次删除。

  3. dropMimeData必须对row == -1作出反应:这表示直接发生在parent指示的项目上。

  4. setContents未添加任何商品,他们完全重置了模型。

  5. 以下示例说明了所有要点。

    #include <QtWidgets>
    
    const auto mimeType = QStringLiteral("application/x-qabstractitemmodeldatalist");
    
    class SpriteModel : public QAbstractListModel {
    public:
       typedef QSharedPointer<QOpenGLTexture> TexturePtr;
       typedef QPair<QImage, TexturePtr> Item;
    private:
       QList<Item> m_imageList;
    public:
       SpriteModel(QObject * parent = 0) : QAbstractListModel(parent) {}
    
       static Item makeItem(const QImage & image) {
          return qMakePair(image, TexturePtr(new QOpenGLTexture(image)));
       }
    
       void setContents(QList<Item> &newList) {
          beginResetModel();
          m_imageList = newList;
          endResetModel();
       }
    
       int rowCount(const QModelIndex &) const Q_DECL_OVERRIDE {
          return m_imageList.size();
       }
    
       QVariant data(const QModelIndex & index, int role) const Q_DECL_OVERRIDE {
          if (role == Qt::DecorationRole)
             return m_imageList[index.row()].first;
          else if (role == Qt::DisplayRole)
             return "";
          else
             return QVariant();
       }
    
       Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE {
          return Qt::CopyAction | Qt::MoveAction;
       }
    
       Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE {
          auto defaultFlags = QAbstractListModel::flags(index);
          if (index.isValid())
             return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
          else
             return Qt::ItemIsDropEnabled | defaultFlags;
       }
    
       bool removeRows(int position, int rows, const QModelIndex &) Q_DECL_OVERRIDE {
          beginRemoveRows(QModelIndex(), position, position+rows-1);
          for (int row = 0; row < rows; ++row) {
             m_imageList.removeAt(position);
          }
          endRemoveRows();
          return true;
       }
    
       bool insertRows(int position, int rows, const QModelIndex &) Q_DECL_OVERRIDE {
          beginInsertRows(QModelIndex(), position, position+rows-1);
          auto size = m_imageList.isEmpty() ? QSize(10, 10) : m_imageList.at(0).first.size();
          QImage img(size, m_imageList[0].first.format());
          for (int row = 0; row < rows; ++row) {
             m_imageList.insert(position, makeItem(img));
          }
          endInsertRows();
          return true;
       }
    
       bool setData(const QModelIndex &index, const QVariant &value, int role) Q_DECL_OVERRIDE {
          if (index.isValid() && role == Qt::EditRole) {
             m_imageList.replace(index.row(), makeItem(value.value<QImage>()));
             emit dataChanged(index, index);
             return true;
          }
          return false;
       }
    
       QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE {
          auto mimeData = new QMimeData();
          QByteArray encodedData;
          QDataStream stream(&encodedData, QIODevice::WriteOnly);
    
          qDebug() << "mimeData" << indexes;
    
          for (const auto & index : indexes) {
             if (! index.isValid()) continue;
             QMap<int, QVariant> roleDataMap;
             roleDataMap[Qt::DecorationRole] = data(index, Qt::DecorationRole);
             stream << index.row() << index.column() << roleDataMap;
          }
          mimeData->setData(mimeType, encodedData);
          return mimeData;
       }
    
       bool canDropMimeData(const QMimeData *data,
                            Qt::DropAction, int, int column, const QModelIndex & parent) const Q_DECL_OVERRIDE
       {
          return data->hasFormat(mimeType) && (column == 0 || (column == -1 && parent.column() == 0));
       }
    
       bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) Q_DECL_OVERRIDE {
          Q_UNUSED(column);
          qDebug() << "drop" << action << row << column << parent;
          if (! data->hasFormat(mimeType)) return false;
    
          auto encoded = data->data(mimeType);
          QDataStream stream(&encoded, QIODevice::ReadOnly);
          QList<QImage> images;
          while (! stream.atEnd()) {
             int row, col;
             QMap<int, QVariant> roleDataMap;
             stream >> row >> col >> roleDataMap;
             auto it = roleDataMap.find(Qt::DecorationRole);
             if (it != roleDataMap.end()) {
                images << it.value().value<QImage>();
             }
          }
          if (row == -1) row = parent.row();
          if (! images.isEmpty()) {
             beginInsertRows(parent, row, row+images.size() - 1);
             qDebug() << "inserting" << images.count();
             for (auto & image : images)
                m_imageList.insert(row ++, makeItem(image));
             endInsertRows();
             return true;
          }
          return false;
       }
    };
    
    QImage makeImage(int n) {
       QImage img(64, 128, QImage::Format_RGBA8888);
       img.fill(Qt::transparent);
       QPainter p(&img);
       p.setFont(QFont("Helvetica", 32));
       p.drawText(img.rect(), Qt::AlignCenter, QString::number(n));
       return img;
    }
    
    int main(int argc, char *argv[])
    {
       QApplication a(argc, argv);
       QList<SpriteModel::Item> items;
       for (int i = 0; i < 5; ++i) items << SpriteModel::makeItem(makeImage(i));
       SpriteModel model;
       model.setContents(items);
       QListView view;
       view.setModel(&model);
       view.setViewMode(QListView::IconMode);
       view.setSelectionMode(QAbstractItemView::ExtendedSelection);
       view.setDragEnabled(true);
       view.setAcceptDrops(true);
       view.setDropIndicatorShown(true);
       view.show();
       qDebug() << model.mimeTypes();
       return a.exec();
    }