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不客气!我添加了一个更高效的版本,只是因为我一直在考虑它我敢肯定,还有其他办法。