QListView内部移动没有序列化

问题描述 投票:1回答:1

我正在尝试支持重新排列QListView中的大对象列表。似乎模型/视图框架坚持序列化项目,在这种情况下,这些项目效率非常低。我试图通过使用shared_ptr来缩短事情,所以只需要传递一点数据,但即便如此,它也会感觉非常混乱并且根本不可靠。

更糟糕的是,在移动过程中我注意到序列化函数被重复调用(几十次/几百次)。当然,他们每次只能被召唤一次。

我确定我错过了一个技巧,实现简单的内部移动,列表排序的正确方法是什么,而没有繁重的序列化开销?

class Big
{
public:
  Big();
  ~Big();

  ...
};

using BigPtr = std::shared_ptr<Big>;

Q_DECLARE_METATYPE(BigPtr) // and qRegisterMetaTypeStreamOperators

// Required serialization operators
// This is very ugly, and what I want rid of...
QDataStream &operator<<(QDataStream &out, const BigPtr &big)
{
    qDebug() << "Out <<";

    out << (quint64)big.get();
    return out;
}
// ...This too...
QDataStream &operator>>(QDataStream &in, BigPtr &big)
{
    qDebug() << "In >>";

    quint64 raw = 0;
    in >> raw;
    li.reset((LayerPtr::element_type *)raw);

    return in;
}

class BigModel : public QAbstractListModel
{
    Q_OBJECT

public:
    BigModel (std::vector<BigPtr> &bigs, QObject *parent = Q_NULLPTR);

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    Qt::ItemFlags flags(const QModelIndex &index) const;  // Qt::ItemIsDragEnabled or Qt::ItemIsDropEnabled depending on valid index
    Qt::DropActions supportedDropActions() const;  // return Qt::MoveAction
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());

private:
    std::vector<BigPtr> &_bigs;
};

...

    // dialog setup...
    ui->listView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->listView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked);
    ui->listView->setDragEnabled(true);
    ui->listView->setDragDropMode(QAbstractItemView::InternalMove);
    ui->listView->setDropIndicatorShown(true);
    ui->listView->viewport()->setAcceptDrops(true);

编辑:

经过更多的头部刮擦,我决定超载mimeDatadropMimeData函数和子类QMimeData直接携带BigPtr

class BigMimeData : public QMimeData
{
    Q_OBJECT

public:
    BigMimeData(const QVariant &_big)
         : big(_big)
    { }
    bool hasFormat(const QString &/*mimeType*/) const { return true; }


    QVariant big;
};

...

QMimeData *BigModel::mimeData(const QModelIndexList &indexes) const
{
    if(indexes.count() > 0) {
        return new BigMimeData(indexes.at(0).data());
    }

    return Q_NULLPTR;
}

bool BigModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
                              int row, int /*column*/, const QModelIndex &parent)
{
    if(!data || !(action == Qt::CopyAction || action == Qt::MoveAction)) {
        return false;
    }

    // start of list
    if(row == -1) {
        row = rowCount(parent);
    }

    auto dragged = static_cast<const BigMimeData *>(data);

    // setData with stored value
    insertRow(row, parent);
    setData(index(row, 0, parent), dragged->big);

    return true;
}

哪个有效,但认真!?只是为了在UI中启用简单的列表重新排序?它肯定不是这么复杂吗?

顺便说一下,很多这些答案似乎都集中在底层数据结构上,这不是问题。问题是,在QListView中,列表控件中的简单内部移动拖放重新排列似乎非常复杂,因为它需要昂贵的序列化。这是一个UI / Widgets / MVC问题。

qt qlistview
1个回答
0
投票

你为什么不使用QVector而不是std::vector

我可能会做的是使用这种结构:

QVector<Big*> _bigs;

然后,如果我想重新排列某个项目,我只会移动其元素为重新排列指针的索引,而setDatadata方法只会为向量中的那些位置发出dataChanged

当你进行重新排列时,你是否检查过你是否在向量中的每个位置发出dataChanged信号?这可能会导致很大的开销。

© www.soinside.com 2019 - 2024. All rights reserved.