![](/img/trans.png)
[英]How to modify the behavior of Drag'n'Drop in QListWidget (or QListView)
[英]Context depending drag and drop in QListView
在我的一個項目中,我必須管理一個項目列表,這些項目列表可以通過拖放來按其順序重新排列。
現在,所有項目都具有優先級,用戶無法更改。 列表中元素的順序受到限制,即優先級較低的元素必須先行,但優先級相同的元素可以互換。
例如,以下列表是理智的:
(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
。
是否可以在QListView
上不實現my的情況下實現此行為,並且可以通過改編自定義的QAbstractItemModel
來實現?
可能的解決方法甚至是放棄拖放界面,並使用向上和向下箭頭鍵在周圍移動項目。 或什至更一般的剪切和粘貼操作。 但是,我確實更喜歡使用拖放界面。
您可以重新實現QStandardItemModel
並重寫canDropMimeData()
方法。 還有其他方法,但是如果您已經對QStandardItemModel
滿意的話,它們可能會更多地參與其中。 實現自己的模型可能會帶來性能優勢,尤其是在您的數據結構非常簡單的情況下(例如單列列表)。 通常,這還使您可以更自定義拖放行為。
請注意,這將完全忽略操作類型(默認情況下, QStandardItemModel
僅允許移動和復制)。 將一個項目移至另一個項目將完全刪除目標項目-這可能不是您想要的,但是一個單獨的問題(請參見下面代碼中的注釋)。
您也可以在dropMimeData()
方法中實現相同的邏輯(在調用基類方法之前),但是我不確定我是否看到任何優點。 而且,通過使用canDropMimeData()
,用戶還可以獲得有關什么是canDropMimeData()
和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;
}
};
作為參考,下面是QAbstractItemModel
如何編碼數據 (並在下一個方法中對其進行解碼)。
添加 :好的,這讓我有些煩惱,所以這是一個更有效的版本... :-)通過在拖動開始時將拖動項的優先級直接嵌入到mime數據中,可以節省很多解碼時間。
#include <QStandardItemModel>
#define PRIORITY_MIME_TYPE QStringLiteral("application/x-priority-data")
class ItemModel : public QStandardItemModel
{
public:
using QStandardItemModel::QStandardItemModel;
QMimeData *mimeData(const QModelIndexList &indexes) const override
{
QMimeData *mdata = QStandardItemModel::mimeData(indexes);
if (!mdata)
return nullptr;
// Add our own priority data for more efficient evaluation in canDropMimeData()
const int role = Qt::UserRole + 1; // data role for priority value
int priority = -1;
bool ok;
for (const QModelIndex &idx : indexes) {
// Priority of selected item
const int thisPriority = idx.data(role).toInt(&ok);
// When dragging multiple items, check that the priorities of all selected items are the same.
if (!ok || (priority > -1 && thisPriority != priority))
return nullptr; // Cannot drag items with different priorities;
priority = thisPriority;
}
if (priority < 0)
return nullptr; // couldn't find a priority, cancel the drag.
// Encode the priority data
QByteArray ba;
ba.setNum(priority);
mdata->setData(PRIORITY_MIME_TYPE, ba);
return mdata;
}
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;
if (!data->hasFormat(PRIORITY_MIME_TYPE))
return false;
const int role = Qt::UserRole + 1; // what QStandardItem uses for setData() by default
int destPriority = -1;
bool ok = false;
// Find destination item priority.
if (parent.isValid()) {
// dropping onto an item
destPriority = parent.data(role).toInt(&ok);
}
else if (row > -1) {
// dropping between items
destPriority = this->data(index(row, 0), role).toInt(&ok);
}
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(&ok);
}
if (!ok || destPriority < 0)
return false;
// Get priority of item(s) being dragged which we encoded in mimeData() method.
const int originPriority = data->data(PRIORITY_MIME_TYPE).toInt(&ok);
qDebug() << "Drop parent:" << parent << "row:" << row
<< "destPriority:" << destPriority << "originPriority:" << originPriority;
if (!ok || originPriority != destPriority)
return false;
return true;
}
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.