我正在尝试支持重新排列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);
编辑:
经过更多的头部刮擦,我决定超载mimeData
和dropMimeData
函数和子类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问题。
你为什么不使用QVector
而不是std::vector
?
我可能会做的是使用这种结构:
QVector<Big*> _bigs;
然后,如果我想重新排列某个项目,我只会移动其元素为重新排列指针的索引,而setData
和data
方法只会为向量中的那些位置发出dataChanged
。
当你进行重新排列时,你是否检查过你是否在向量中的每个位置发出dataChanged
信号?这可能会导致很大的开销。