C++ QListView中的上下文相关拖放
在我的一个项目中,我必须管理一个项目列表,这些项目可以通过拖放按顺序重新排列 现在,所有项目都有一个优先级,用户无法更改。列表中元素的顺序有一个限制,即优先级较低的元素必须排在第一位,但具有相同优先级的元素可以互换 例如,以下列表是正常的:C++ QListView中的上下文相关拖放,c++,qt,drag-and-drop,qabstractitemmodel,qlistview,C++,Qt,Drag And Drop,Qabstractitemmodel,Qlistview,在我的一个项目中,我必须管理一个项目列表,这些项目可以通过拖放按顺序重新排列 现在,所有项目都有一个优先级,用户无法更改。列表中元素的顺序有一个限制,即优先级较低的元素必须排在第一位,但具有相同优先级的元素可以互换 例如,以下列表是正常的: (A,1),(B,1),(C,1),(D,2),(E,3) 鉴于以下情况已被打破: (A,1),(B,1),(E,3),(D,2) 以下代码显示了问题的起点: #include <QApplication> #include <QFra
(A,1),(B,1),(C,1),(D,2),(E,3)
鉴于以下情况已被打破:
(A,1),(B,1),(E,3),(D,2)
以下代码显示了问题的起点:
#include <QApplication>
#include <QFrame>
#include <QHBoxLayout>
#include <QListView>
#include <QStandardItemModel>
QStandardItem* create(const QString& text, int priority) {
auto ret = new QStandardItem(text);
ret->setData(priority);
return ret;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
auto frame = new QFrame;
frame->setLayout(new QVBoxLayout);
auto view = new QListView;
frame->layout()->addWidget(view);
auto model = new QStandardItemModel;
view->setModel(model);
model->appendRow(create("1. A", 1));
model->appendRow(create("1. B", 1));
model->appendRow(create("2. X", 2));
model->appendRow(create("2. Y", 2));
model->appendRow(create("2. Z", 2));
view->setDragEnabled(true);
view->viewport()->setAcceptDrops(true);
view->setDropIndicatorShown(true);
view->setDragDropMode(QAbstractItemView::DragDropMode::InternalMove);
view->setDefaultDropAction(Qt::DropAction::MoveAction);
view->setDragDropOverwriteMode(false);
frame->show();
return a.exec();
}
现在,DefaultDropAction必须更改上下文,这取决于要移动的项以及要删除的项
如果这两个元素的优先级相等,那么我有一个动作。如果这两个要素的优先顺序不同,我就不采取行动
如果不在QListView上实现my,是否可以实现此行为
可以通过调整自定义QabstracteModel来实现
一种可能的解决方法是放弃拖放界面,使用上下箭头键移动项目。或者更一般的剪切粘贴操作。但是,我更喜欢使用拖放界面。您可以重新实现QStandardItemModel并覆盖canDropMimeData方法。还有其他方法,不过如果您已经对QStandarItemModel感到满意的话,它们可能会更加复杂。实现您自己的模型可能具有性能优势,特别是当您的数据结构相当简单(如单列列表)时。这还可以让您更全面地自定义拖放行为
请注意,这会完全忽略动作类型QStandardItemModel默认情况下仅允许移动和复制。将一个项目移动到另一个项目将完全删除目标项目-这可能不是您想要的,但是一个单独的问题-请参阅下面代码中的注释
在调用基类方法之前,您也可以在dropMimeData方法中实现相同的逻辑,但我不确定是否看到任何优势。通过使用Candromimedata,用户还可以获得关于什么是有效的、什么是无效的视觉反馈
#include <QStandardItemModel>
class ItemModel : public QStandardItemModel
{
public:
using QStandardItemModel::QStandardItemModel;
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override
{
if (!QStandardItemModel::canDropMimeData(data, action, row, column, parent))
return false;
const int role = Qt::UserRole + 1; // what QStandardItem uses for setData() by default
int originPriority;
int destPriority;
// Find destination item priority.
if (parent.isValid()) {
// dropping onto an item
// Note: if you don't want MoveAction to overwrite items you could:
// if (action == Qt::MoveAction) return false;
destPriority = parent.data(role).toInt();
}
else if (row > -1) {
// dropping between items
destPriority = this->data(index(row, 0), role).toInt();
}
else {
// dropping somewhere else onto the view, treat it as drop after last item in model
destPriority = this->data(index(rowCount() - 1, 0), role).toInt();
}
// Need to find priority of item(s) being dragged (encoded in mime data). Could be several.
// This part decodes the mime data in a way compatible with how QAbstractItemModel encoded it.
// (QStandardItemModel includes it in the mime data alongside its own version)
QByteArray ba = data->data(QAbstractItemModel::mimeTypes().first());
QDataStream ds(&ba, QIODevice::ReadOnly);
while (!ds.atEnd()) {
int r, c;
QMap<int, QVariant> v;
ds >> r >> c >> v;
// If there were multiple columns of data we could also do a
// check on the column number, for example.
originPriority = v.value(role).toInt();
if (originPriority != destPriority)
break; //return false; Could exit here but keep going to print our debug info.
}
qDebug() << "Drop parent:" << parent << "row:" << row <<
"destPriority:" << destPriority << "originPriority:" << originPriority;
if (originPriority != destPriority)
return false;
return true;
}
};
非常感谢你的详尽回答。我会试试看是否能满足我的需要。@Aleph0不客气!我添加了一个更高效的版本,只是因为我一直在考虑它我敢肯定,还有其他办法。