2015-07-26 6 views
3

Я хочу изменить набор изображений в qlistview. Я посмотрел на примеры, и я просто не могу заставить это работать. Когда я перетаскиваю изображение поверх другого, вызывается функция dropomimedata(), однако его «data-> hasImage()» всегда ложь. Когда я бросаю изображение в пустое место, почему-то dropmimedata() не запускается вообще.Qt AbstractItemModel переупорядочивание изображений

Моя модель должна выглядеть следующим образом:

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

QDataStream будет не кодировать данные изображения, как PNG. Почему бы не кодировать его как BMP или GIF, например? Значение mimetype по умолчанию, которое вы кодируете, равно application/x-qabstractitemmodeldatalist - это потому, что вы не переопределили mimeTypes().

Поскольку вы предположительно хотите поддерживать перемещение нескольких элементов одновременно, вы должны придерживаться x-qabstractitemmodeldatalist и соответствующим образом кодировать/декодировать его. См. this question.

Обратите внимание, что этот тип изображения будет не return true для hasImage, так как данные представляют собой список карт значений ролей.

Другие проблемы:

  1. Вы протекающие текстуры повсюду. Вы должны использовать std::shared_ptr или QSharedPointer вместо необработанного указателя.

  2. insertrows помещает тот же экземпляр текстуры в несколько записей. Если вы попытаетесь удалить текстуры, вы не сможете избежать множественных удалений, если не используете общий указатель.

  3. dropMimeData должен отреагировать на row == -1: это означает, что падение произошло непосредственно над изделием, указанным parent.

  4. setContents не добавляет никаких предметов, они полностью перезагружают модель.

Приведенный ниже пример иллюстрирует все точки.

#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(); 
} 
+0

Во-первых, спасибо, что ответили. Ваш пример работает. Однако некоторые из них не то, что мне нужно. Как это сделать, когда я перетаскиваю изображение поверх другого, он вставляет его перед мишенью вместо замены цели? (В идеале я хотел бы нажимать изображения, когда вы зависаете для предварительного просмотра, но не уверены, что это возможно). Также, когда я перетаскиваю изображение в пустое пространство, я хочу, чтобы он переместил его до конца, но я не могу показаться перетащить его туда. – Fundies

+0

@ user3554386 Для меня, под Qt 5.4, он вставляет, а не замены. В любом случае, это не является полным, вам нужно добавить обработку для 'Qt :: DropAction'.В настоящее время он копирует изображения, а не перемещает их. Если вы хотите только поддерживать ходы, вы захотите установить его в представлении. –

+0

У меня есть ser 'setDragDropMode (QListView :: InternalMove);' и 'setDefaultDropAction (Qt :: MoveAction);' но это не повлияло. Также я использую последние Qt – Fundies

Смежные вопросы