在QListView中取决于上下文的拖放

时间:2019-09-03 09:44:02

标签: c++ qt drag-and-drop qabstractitemmodel qlistview

在我的一个项目中,我必须管理一系列项目,这些项目可以通过拖放操作按顺序重新排列。

现在,所有项目都具有优先级,用户无法更改。列表中元素的顺序受到限制,即优先级较低的元素必须排在首位,但优先级相同的元素可以互换。

例如,以下列表是理智的:

From q In qItems Where q.qName = "Name1" And q.qStrings.Contains("f3") And q.Id = 1

以下内容已损坏:

(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。如果两个元素的优先级不同,我有一个MoveAction

如果不在IgnoreAction上实现my,可以实现此行为吗? 可以通过改编自定义QListView来实现。

甚至可能的解决方法是放弃拖放界面,并使用向上和向下箭头键在周围移动项目。或什至更一般的剪切和粘贴操作。但是,我真的更喜欢使用拖放界面。

1 个答案:

答案 0 :(得分:1)

您可以重新实现this.$emit("foo"); 并覆盖QStandardItemModel方法。还有其他方法,但是如果您对canDropMimeData()很满意的话,可能会更多地参与其中。实现自己的模型可能会带来性能优势,尤其是在您的数据结构非常简单的情况下(例如单列列表)。这样一来,您通常还可以更自定义拖放行为。

请注意,这会完全忽略操作类型(QStandardItemModel仅在默认情况下允许移动和复制)。将一个项目移至另一个项目将完全删除目标项目-这可能不是您想要的,但是是一个单独的问题(请参见下面代码中的注释)。

您也可以在QStandardItemModel方法中实现相同的逻辑(在调用基类方法之前),但是我不确定我是否有任何优势。而且,通过使用dropMimeData(),用户还可以得到关于什么是有效和什么无效的视觉反馈。

canDropMimeData()

作为参考,这是 #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; } }; encodes data的方式(并在下一个方法中对其进行解码)。

已添加: 好的,这让我有些烦恼,所以这是一个更有效的版本... :-)通过在拖动开始时将拖动项的优先级直接嵌入到mime数据中,可以节省大量解码时间。

QAbstractItemModel