Когда вы хотите реорганизовать элементы в пользовательской модели, вы должны выполнить все необходимые действия: - как вставить и удалить строку - как получить и установить данные - как сериализовать элементы (построить mimedata) - какunserialize items
Пример с пользовательской моделью с QStringList
в качестве источника данных:
Минимальная реализация модели должна быть:
class CustomModel: public QAbstractListModel
{
public:
CustomModel()
{
internalData = QString("abcdefghij").split("");
}
int rowCount(const QModelIndex &parent) const
{
return internalData.length();
}
QVariant data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.parent().isValid())
return QVariant();
if (role != Qt::DisplayRole)
return QVariant();
return internalData.at(index.row());
}
private:
QStringList internalData;
};
Мы должныдобавьте способ вставки / удаления строк и задайте данные:
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole)
{
if (role != Qt::DisplayRole)
return false;
internalData[index.row()] = value.toString();
return true;
}
bool insertRows(int row, int count, const QModelIndex &parent)
{
if (parent.isValid())
return false;
for (int i = 0; i != count; ++i)
internalData.insert(row + i, "");
return true;
}
bool removeRows(int row, int count, const QModelIndex &parent)
{
if (parent.isValid())
return false;
beginRemoveRows(parent, row, row + count - 1);
for (int i = 0; i != count; ++i)
internalData.removeAt(row);
endRemoveRows();
return true;
}
Для части перетаскивания:
Сначала нам нужно определить тип MIME, чтобы определить способ, которым мы будемдесериализовать данные:
QStringList mimeTypes() const
{
QStringList types;
types << CustomModel::MimeType;
return types;
}
Где CustomModel::MimeType
- постоянная строка, такая как "application/my.custom.model"
. Метод canDropMimeData
будет использоваться для проверки того, являются ли отброшенные данные допустимыми или нет.Итак, мы можем отбросить внешние данные:
bool canDropMimeData(const QMimeData *data,
Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex& /*parent*/)
{
if ( action != Qt::MoveAction || !data->hasFormat(CustomModel::MimeType))
return false;
return true;
}
Затем мы можем создать наши данные MIME на основе внутренних данных:
QMimeData* mimeData(const QModelIndexList &indexes) const
{
QMimeData* mimeData = new QMimeData;
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
for (const QModelIndex &index : indexes) {
if (index.isValid()) {
QString text = data(index, Qt::DisplayRole).toString();
stream << text;
}
}
mimeData->setData(CustomModel::MimeType, encodedData);
return mimeData;
}
Теперь нам нужно обработать пропущенные данные.Мы должны десериализовать данные MIME, вставить новую строку, чтобы установить данные в нужном месте (для Qt::MoveAction
старая строка будет автоматически удалена. Вот почему нам пришлось реализовать removeRows
):
bool dropMimeData(const QMimeData *data,
Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (!canDropMimeData(data, action, row, column, parent))
return false;
if (action == Qt::IgnoreAction)
return true;
else if (action != Qt::MoveAction)
return false;
QByteArray encodedData = data->data("application/my.custom.model");
QDataStream stream(&encodedData, QIODevice::ReadOnly);
QStringList newItems;
int rows = 0;
while (!stream.atEnd()) {
QString text;
stream >> text;
newItems << text;
++rows;
}
insertRows(row, rows, QModelIndex());
for (const QString &text : qAsConst(newItems))
{
QModelIndex idx = index(row, 0, QModelIndex());
setData(idx, text);
row++;
}
return true;
}
Если вам нужна дополнительная информация о системе перетаскивания в Qt, посмотрите документацию .