C++ QT defined a CustomTreeView, 可选择多个node,并且可拖动这些node,改变父节点

To create a custom QTreeView in C++ using Qt, where users can select multiple nodes and drag them to change their parent nodes, you'll need to leverage Qt's model-view framework along with the drag-and-drop feature. Below is a step-by-step guide to achieving this.

Step 1: Subclass QTreeView and QAbstractItemModel

You'll need to subclass both QTreeView (the view) and QAbstractItemModel (the model). The QTreeView will handle the user interface, and the QAbstractItemModel will manage the data.

Step 2: Create the Custom QTreeView

The custom QTreeView will allow multiple selections and handle drag-and-drop actions.

  1. Subclass QTreeView to enable multi-selection and drag-and-drop:
复制代码

cpp

复制代码
`#include <QTreeView>
#include <QMimeData>
#include <QDrag>
#include <QDropEvent>
#include <QStandardItemModel>
#include <QStandardItem>

class CustomTreeView : public QTreeView {
    Q_OBJECT

public:
    CustomTreeView(QWidget *parent = nullptr) : QTreeView(parent) {
        setSelectionMode(QAbstractItemView::ExtendedSelection); // Allow multi-selection
        setDragDropMode(QAbstractItemView::InternalMove); // Enable drag-and-drop
        setDropIndicatorShown(true); // Show drop indicator
    }

    void dropEvent(QDropEvent *event) override {
        // Handle drop event here if needed, like checking validity of drop
        QTreeView::dropEvent(event);
    }
};
`

cpp

Step 3: Create the Model (Subclass QAbstractItemModel)

Next, subclass QAbstractItemModel to represent your data structure. Here's an example of how you might implement it:

复制代码

cpp

复制代码
`#include <QAbstractItemModel>
#include <QVector>
#include <QVariant>

class CustomTreeModel : public QAbstractItemModel {
    Q_OBJECT

public:
    CustomTreeModel(QObject *parent = nullptr) : QAbstractItemModel(parent) {
        // Sample data: (root, child1, child2)
        rootItem = new QStandardItem("Root");
        QStandardItem *child1 = new QStandardItem("Child 1");
        QStandardItem *child2 = new QStandardItem("Child 2");
        rootItem->appendRow(child1);
        rootItem->appendRow(child2);
    }

    ~CustomTreeModel() {
        delete rootItem;
    }

    int rowCount(const QModelIndex &parent = QModelIndex()) const override {
        if (!parent.isValid()) {
            return rootItem->rowCount();
        }
        QStandardItem *parentItem = static_cast<QStandardItem*>(parent.internalPointer());
        return parentItem->rowCount();
    }

    int columnCount(const QModelIndex &parent = QModelIndex()) const override {
        return parent.isValid() ? static_cast<QStandardItem*>(parent.internalPointer())->columnCount() : 1;
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
        if (role == Qt::DisplayRole) {
            QStandardItem *item = static_cast<QStandardItem*>(index.internalPointer());
            return item->text();
        }
        return QVariant();
    }

    Qt::ItemFlags flags(const QModelIndex &index) const override {
        Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
        return defaultFlags | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
    }

    QModelIndex parent(const QModelIndex &index) const override {
        if (!index.isValid()) {
            return QModelIndex();
        }

        QStandardItem *childItem = static_cast<QStandardItem*>(index.internalPointer());
        QStandardItem *parentItem = childItem->parent();
        if (parentItem == rootItem) {
            return QModelIndex(); // Root node has no parent
        }
        return createIndex(parentItem->row(), 0, parentItem);
    }

    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override {
        if (!parent.isValid()) {
            return createIndex(row, column, rootItem->child(row));
        }
        QStandardItem *parentItem = static_cast<QStandardItem*>(parent.internalPointer());
        return createIndex(row, column, parentItem->child(row));
    }

private:
    QStandardItem *rootItem;
};
`

cpp

Step 4: Integrating Model and View

Now that you have the custom view (CustomTreeView) and model (CustomTreeModel), set them up in your MainWindow or wherever you want to display the tree.

复制代码

cpp

复制代码
`#include <QMainWindow>
#include <QTreeView>
#include <QStandardItemModel>

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        // Create the custom tree view
        CustomTreeView *treeView = new CustomTreeView(this);
        treeView->setModel(new CustomTreeModel(this)); // Set the custom model
        setCentralWidget(treeView);
    }
};

`

cpp

Step 5: Enable Dragging and Dropping Logic

To support dragging and dropping, the CustomTreeView handles the dragMoveEvent, dragEnterEvent, and dropEvent in the dropEvent method. You may need to add additional logic to handle the rearrangement of nodes.

Step 6: Running the Application

Now, set up the application entry point:

复制代码

cpp

复制代码
`#include <QApplication>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
`

Summary:

  • QTreeView: Subclassed to handle multi-selection and drag-and-drop functionality.
  • QAbstractItemModel: Subclassed to represent the tree data structure.
  • Drag and Drop : Supported by setting the appropriate flags and handling the events in CustomTreeView.

By subclassing QTreeView and QAbstractItemModel, you create a flexible, custom tree view that allows for multi-selection and drag-and-drop functionality. You can further extend the logic to handle additional features like validation, custom item rendering, etc.

//

//

split to .h and .cpp

Sure! Here's the code split into header (.h) and implementation (.cpp) files for a better structure.

CustomTreeView.h

复制代码

cpp

复制代码
`#ifndef CUSTOMTREEVIEW_H
#define CUSTOMTREEVIEW_H

#include <QTreeView>
#include <QDropEvent>

class CustomTreeView : public QTreeView {
    Q_OBJECT

public:
    explicit CustomTreeView(QWidget *parent = nullptr);

protected:
    void dropEvent(QDropEvent *event) override;
};

#endif // CUSTOMTREEVIEW_H
`

cpp

CustomTreeView.cpp

复制代码

cpp

复制代码
`#include "CustomTreeView.h"
#include <QMimeData>
#include <QDrag>

CustomTreeView::CustomTreeView(QWidget *parent) : QTreeView(parent) {
    setSelectionMode(QAbstractItemView::ExtendedSelection);  // Allow multi-selection
    setDragDropMode(QAbstractItemView::InternalMove);        // Enable drag-and-drop
    setDropIndicatorShown(true);                              // Show drop indicator
}

void CustomTreeView::dropEvent(QDropEvent *event) {
    // Handle drop event here if needed (e.g., validate drop)
    QTreeView::dropEvent(event);
}
`

cpp

CustomTreeModel.h

复制代码

cpp

复制代码
`#ifndef CUSTOMTREEMODEL_H
#define CUSTOMTREEMODEL_H

#include <QAbstractItemModel>
#include <QStandardItem>

class CustomTreeModel : public QAbstractItemModel {
    Q_OBJECT

public:
    explicit CustomTreeModel(QObject *parent = nullptr);
    ~CustomTreeModel();

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    QModelIndex parent(const QModelIndex &index) const override;
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;

private:
    QStandardItem *rootItem;
};

#endif // CUSTOMTREEMODEL_H
`

cpp

CustomTreeModel.cpp

复制代码

cpp

复制代码
`#include "CustomTreeModel.h"

CustomTreeModel::CustomTreeModel(QObject *parent) : QAbstractItemModel(parent) {
    // Sample data: (root, child1, child2)
    rootItem = new QStandardItem("Root");
    QStandardItem *child1 = new QStandardItem("Child 1");
    QStandardItem *child2 = new QStandardItem("Child 2");
    rootItem->appendRow(child1);
    rootItem->appendRow(child2);
}

CustomTreeModel::~CustomTreeModel() {
    delete rootItem;
}

int CustomTreeModel::rowCount(const QModelIndex &parent) const {
    if (!parent.isValid()) {
        return rootItem->rowCount();
    }
    QStandardItem *parentItem = static_cast<QStandardItem*>(parent.internalPointer());
    return parentItem->rowCount();
}

int CustomTreeModel::columnCount(const QModelIndex &parent) const {
    return parent.isValid() ? static_cast<QStandardItem*>(parent.internalPointer())->columnCount() : 1;
}

QVariant CustomTreeModel::data(const QModelIndex &index, int role) const {
    if (role == Qt::DisplayRole) {
        QStandardItem *item = static_cast<QStandardItem*>(index.internalPointer());
        return item->text();
    }
    return QVariant();
}

Qt::ItemFlags CustomTreeModel::flags(const QModelIndex &index) const {
    Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
    return defaultFlags | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}

QModelIndex CustomTreeModel::parent(const QModelIndex &index) const {
    if (!index.isValid()) {
        return QModelIndex();
    }

    QStandardItem *childItem = static_cast<QStandardItem*>(index.internalPointer());
    QStandardItem *parentItem = childItem->parent();
    if (parentItem == rootItem) {
        return QModelIndex();  // Root node has no parent
    }
    return createIndex(parentItem->row(), 0, parentItem);
}

QModelIndex CustomTreeModel::index(int row, int column, const QModelIndex &parent) const {
    if (!parent.isValid()) {
        return createIndex(row, column, rootItem->child(row));
    }
    QStandardItem *parentItem = static_cast<QStandardItem*>(parent.internalPointer());
    return createIndex(row, column, parentItem->child(row));
}
`

cpp

MainWindow.h

复制代码

cpp

复制代码
`#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class CustomTreeView;
class CustomTreeModel;

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow() override;

private:
    CustomTreeView *treeView;
    CustomTreeModel *model;
};

#endif // MAINWINDOW_H
`

cpp

MainWindow.cpp

复制代码

cpp

复制代码
`#include "MainWindow.h"
#include "CustomTreeView.h"
#include "CustomTreeModel.h"

#include <QVBoxLayout>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      treeView(new CustomTreeView(this)),
      model(new CustomTreeModel(this)) {
    
    treeView->setModel(model);  // Set the custom model to the tree view

    // Set up the main window layout
    setCentralWidget(treeView);
}

MainWindow::~MainWindow() {
    // Any cleanup if necessary (not strictly needed as Qt handles it)
}
`

cpp

main.cpp

复制代码

cpp

复制代码
`#include <QApplication>
#include "MainWindow.h"

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
`

Explanation:

  1. CustomTreeView.h & CustomTreeView.cpp:

    • The view class (CustomTreeView) is set up to allow multi-selection and internal drag-and-drop. The dropEvent method is overridden to handle drops (if needed).
  2. CustomTreeModel.h & CustomTreeModel.cpp:

    • The model class (CustomTreeModel) is a subclass of QAbstractItemModel that provides data for the tree. It manages a root item and its child nodes.
  3. MainWindow.h & MainWindow.cpp:

    • This class initializes the application window and connects the custom tree view with the model.
  4. main.cpp:

    • This file sets up the Qt application and shows the main window.

Compiling and Running:

  • Include the necessary Qt modules (e.g., QtCore, QtGui, QtWidgets) in your .pro file or CMake setup.

  • If you use a .pro file, it might look like this:

    复制代码
    QT += core gui widgets
    SOURCES += main.cpp MainWindow.cpp CustomTreeView.cpp CustomTreeModel.cpp
    HEADERS += MainWindow.h CustomTreeView.h CustomTreeModel.h

Now you have a well-structured Qt application that uses a custom tree view with drag-and-drop functionality. Let me know if you need further details or have questions about customization!

相关推荐
爱和冰阔落20 分钟前
【Linux】Ctrl+C 到底做了什么?从键盘、kill 到 alarm,一次讲清信号的产生
linux·运维·c++·安卓
水饺编程38 分钟前
第5章,[Win32 章节] :贝塞尔样条曲线(一)
c语言·c++·windows·visual studio
QQ_21696290961 小时前
【项目编号:project95315】SpringBoot公共自习室管理系统:座位预约、房间管理、签到核销、公告规则完整实战
java·spring boot·后端
zlinear数据采集卡1 小时前
数据采集卡从入门到精通(38):上位机开发实战——Python/QT/LabVIEW的技术选型与分层架构
python·单片机·嵌入式硬件·qt·fpga开发·开源·labview
嵌入式阿蔡1 小时前
面试高频考点 01:volatile / 中断 / 堆栈 八股精讲
java·面试·职场和发展·嵌入式实时数据库
程序员打怪兽2 小时前
C++:类与对象,封装,继承,多态,构造/析构,重载/重写
c++
秋饼2 小时前
LangChain4j + Java 实现企业级 Text-to-SQL 智能问数系统:从自然语言到安全可控的数据洞察
java·ai·技术分享·后端开发
Escalating_xu2 小时前
【C++ STL简介】从六大组件到容器、迭代器与算法协作
java·c++·算法
何以解忧,唯有..2 小时前
Python os模块详解:文件与目录操作指南
java·服务器·python
Brilliantwxx2 小时前
【Linux】 进程(3)深度解析:从查看进程到进程状态
linux·服务器·网络·c++