在我的一个项目中,我必须管理一系列项目,这些项目可以通过拖放操作按顺序重新排列。
现在,所有项目都具有优先级,用户无法更改。列表中元素的顺序受到限制,即优先级较低的元素必须排在首位,但优先级相同的元素可以互换。
例如,以下列表是理智的:
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
来实现。
甚至可能的解决方法是放弃拖放界面,并使用向上和向下箭头键在周围移动项目。或什至更一般的剪切和粘贴操作。但是,我真的更喜欢使用拖放界面。
答案 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