Qt之树的使用(QTreeView、QTreeWidget)

QTreeView

(处理复杂的树形结构)
树型控件
  QStringListModel 用于存储简单的QString列表。
  QStandardItemModel 管理复杂的树型结构数据项,每项都可以包含任意数据。
  QDirModel提供本地文件系统中的文件与目录信息。
 QSqlQueryModel,QSqlTableModel,QSqlRelationTableModel用来访问数据库。

自定义model:
  可以处理庞大的数据,和增加特殊功能

QTreeWidget

(处理较为简单的树形结构)

  使用QTreeWidgetItem类添加或设置项目

常用方法:
  insertTopLevelItems() //在视图的顶层索引中引入项目的列表
  expandAll() //展开所有节点的树形节点
  invisibleRootItem() //返回树形控件中不可见的根选项(Root Item)
  selectionItems() //返回所有选定的非隐藏项目的列表内

widgettree.h

#ifndef WIDGETTREE_H
#define WIDGETTREE_H

#include <QWidget>
#include <QTreeWidgetItem>
#include <QMutex>
#include "qctreewidget.h"
#include "qctreeview.h"
#include "qcmtreeview.h"
#include "typedef.h"

typedef struct CUSTOM_TREE_DATA_T
{
    QString sDevno;
    QString sName;
    int number;

}CUSTOM_TREE_DATA;

typedef QHash<QString, CUSTOM_TREE_DATA> QHashTestDev;
typedef QHash<QString, CUSTOM_TREE_DATA>::iterator QHashTestDevIterator;


namespace Ui {
class WidgetTree;
}

class WidgetTree : public QWidget
{
    Q_OBJECT

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

    //初始化自定义的QTreeWidget
    void initCustomTreeWidget();

    //初始化自定义的QTreeView
    void initCustomTreeView();

    //初始化自定义model的QTreeView
    void initCustomModelTreeView();

    //初始化数据
    void initTestDevHash();

    QVector<Department*> initData();

signals:
    void sig_UpdateTreeWidget();
    void sig_UpdateTreeView();

public slots:
    //接收菜单栏操作信号
    void on_refreshOne(QTreeWidgetItem* item);
    void on_refreshAll();
    void on_devOperate(QString devno);
    void on_devSelectItem(QString devno, int nTreeMode);

    void on_refreshOne(QStandardItem* item);
    void on_devOperate(QStandardItem* item);
    void on_devSelectItem(QStandardItem* item, int nTreeMode);

    //更新自定义数据
    void on_UpdateTreeWidget();
    void on_UpdateTreeView();


protected:
    void changeEvent(QEvent* event);

private slots:
    void on_pushButton_Get2_clicked();
    void on_pushButton_Get_clicked();

private:
    void retranslateUi();

private:
    QMutex  m_lock;

    //树数据存储
    QHashTestDev m_testDevHash;


private:
    Ui::WidgetTree *ui;
};

#endif // WIDGETTREE_H


widgettree.cpp

#include "widgettree.h"
#include "ui_widgettree.h"
#include "commondef.h"

#include <QFile>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>


WidgetTree::WidgetTree(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::WidgetTree)
{
    ui->setupUi(this);
    retranslateUi();

    initCustomTreeWidget();
    initCustomTreeView();
    initTestDevHash();

    initCustomModelTreeView();


    connect(this, SIGNAL(sig_UpdateTreeWidget()), this, SLOT(on_UpdateTreeWidget()));
    connect(this, SIGNAL(sig_UpdateTreeView()), this, SLOT(on_UpdateTreeView()));
}

WidgetTree::~WidgetTree()
{
    delete ui;
}


/*************************
 * QTreeWidget-初始化
 * ***********************/
void WidgetTree::initCustomTreeWidget()
{
    connect(ui->treeWidget, SIGNAL(sig_refreshOne(QTreeWidgetItem*)), this, SLOT(on_refreshOne(QTreeWidgetItem*)));
    connect(ui->treeWidget, SIGNAL(sig_refreshAll()), this, SLOT(on_refreshAll()));
    connect(ui->treeWidget, SIGNAL(sig_devOperate(QString)), this, SLOT(on_devOperate(QString)));
    connect(ui->treeWidget, SIGNAL(sig_devSelectItem(QString, int)), this, SLOT(on_devSelectItem(QString, int)));

    ui->treeWidget->setTreeMode(TREE_MODE_DEMO, true);
}

/*************************
 * QTreeWidget-创建节点
 * ***********************/
void WidgetTree::on_UpdateTreeWidget()
{
    //QMutexLocker guard(&m_lock);

    //1.创建节点列表
    QList<QTreeWidgetItem *> devTreeWiditems;
    QStringList strList;

    ui->treeWidget->clear();
    QHashTestDevIterator i = m_testDevHash.begin();
    for(; i != m_testDevHash.end(); ++i)
    {
        CUSTOM_TREE_DATA dev = i.value();
        strList.clear();
        strList << dev.sDevno;

        //2.创建QTreeWidgetItem节点,并设置相关数据
        QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeWidget, strList);
        item->setToolTip(0, strList.first());

        //Qt::ItemIsSelectable表示可选的
        //Qt::ItemIsUserCheckable项目上是否有复选框
        //Qt::ItemIsEnabled 项目上是否没有被禁用(Enabled可用/Disabled禁用)
        //Qt::ItemIsDragEnabled 是否可拖动
        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable
                       | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
        item->setData(0, ROLE_DEVNO, dev.sDevno);
        item->setData(0, ROLE_NAME, dev.sName);
        item->setData(0, ROLE_NUMBER, dev.number);

        for(int j = 0; j < 2; j++)
        {
            QStringList subStrList;
            subStrList << QString("%1_%2").arg(dev.sDevno).arg(j);

            //3.创建QTreeWidgetItem次级节点,并设置相关数据
            QTreeWidgetItem* subItem = new QTreeWidgetItem(item, subStrList);
            subItem->setData(0, Qt::WhatsThisRole, QVariant("subItem"));
            subItem->setData(0, ROLE_SUB_DEVNO, QString("%1_%2").arg(dev.sDevno).arg(j));

            //4.一级节点添加二级节点
            item->addChild(subItem);
        }
        //5.节点列表添加一级节点
        devTreeWiditems.push_back(item);
    }

    //6.统一添加顶层节点
    ui->treeWidget->insertTopLevelItems(0, devTreeWiditems);
}

//

/*************************
 * QTreeView-初始化
 * ***********************/
void WidgetTree::initCustomTreeView()
{
    connect(ui->treeView, SIGNAL(sig_refreshOne(QStandardItem*)), this, SLOT(on_refreshOne(QStandardItem*)));
    connect(ui->treeView, SIGNAL(sig_refreshAll()), this, SLOT(on_refreshAll()));
    connect(ui->treeView, SIGNAL(sig_devOperate(QStandardItem*)), this, SLOT(on_devOperate(QStandardItem*)));
    connect(ui->treeView, SIGNAL(sig_devSelectItem(QStandardItem*, int)), this, SLOT(on_devSelectItem(QStandardItem*, int)));

    ui->treeView->setTreeMode(TREE_VIEW_MODE_DEMO, true);

    //隐藏表头
    ui->treeView->setHeaderHidden(true);

    //设置展开
    //ui->treeView->expandAll();
}

/*************************
 * QTreeView自定义模型-初始化
 * ***********************/
void WidgetTree::initCustomModelTreeView()
{
    QVector<Department*> proList = initData();
    QStringList headers;
    headers << QStringLiteral("Name")
            << QStringLiteral("ID")
            << QStringLiteral("Persons")
            << QStringLiteral("Superior");
    ui->treeView_2->setModelByHeader(headers);

    TreeItem* root = ui->treeView_2->getModel()->root();
    foreach (auto depNode, proList)
    {
        TreeItem* depItem = new TreeItem(root);
        depItem->setPtr(depNode); // 保存数据指针
        root->addChild(depItem);

        foreach (auto subNode, depNode->subList)
        {
            TreeItem* subItem = new TreeItem(depItem);
            subItem->setPtr(subNode);    // 保存数据指针
            depItem->addChild(subItem);
        }
    }
}

/*************************
 * QTreeView-创建节点
 * ***********************/
void WidgetTree::on_UpdateTreeView()
{
    QStandardItem* pStandardItem = nullptr;
    QStandardItem* pStandardChildItem = nullptr;
    ui->treeView->getModel()->clear();
    int row = 0;

    QHashTestDevIterator i = m_testDevHash.begin();
    for(; i != m_testDevHash.end(); ++i)
    {
        CUSTOM_TREE_DATA dev = i.value();

        //1.创建一级复杂数据项节点
        pStandardItem = new QStandardItem(dev.sDevno);

        //2.使用QStandardItemModel添加一级复杂数据项节点
        ui->treeView->getModel()->appendRow(pStandardItem);
        pStandardItem->setData(-1, ROLE_PARENT);
        pStandardItem->setData(dev.sDevno, ROLE_DEVNO);
        pStandardItem->setData(dev.sName, ROLE_NAME);

        //添加子节点
        for(int j = 0; j < 2; j++)
        {
           //3.创建次级复杂数据项节点
           pStandardChildItem = new QStandardItem(QString("%1_%2").arg(dev.sDevno).arg(j));
           pStandardChildItem->setData(row, ROLE_PARENT);
           pStandardChildItem->setData(QString("%1_%2").arg(dev.sName).arg(j), ROLE_NAME);

           //4.一级复杂数据项节点添加次级复杂数据项节点
           pStandardItem->appendRow(pStandardChildItem);
        }
        row++;
    }
}



/*************************
 * 填充数据
 * ***********************/
void WidgetTree::initTestDevHash()
{
    CUSTOM_TREE_DATA testDev[5];
    testDev[0] = {"AAA", "192.168.1.100", 1};
    testDev[1] = {"BBB", "192.168.1.101", 2};
    testDev[2] = {"CCC", "192.168.1.102", 3};
    testDev[3] = {"DDD", "192.168.1.103", 4};
    testDev[4] = {"EEE", "192.168.1.104", 5};

    for(int i = 0; i < 5; i++)
    {
        m_testDevHash.insert(testDev[i].sDevno, testDev[i]);
    }
}

QVector<Department *> WidgetTree::initData()
{
    QFile file(":/Resource/Txt/default.txt");
    file.open(QIODevice::ReadOnly);

    QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
    QJsonObject obj = doc.object();
    QJsonArray array = obj.value("nextLevel").toArray();
    QVector<Department*> depList;
    for(int i = 0; i < array.size(); ++i)
    {
        QJsonObject subObject = array.at(i).toObject();
        QJsonArray subArray = subObject.value("nextLevel").toArray();
        Department *topDepartment = new Department();
        topDepartment->id = subObject.value("id").toInt();
        topDepartment->name = subObject.value("name").toString();
        topDepartment->number = subObject.value("nnt").toInt();
        topDepartment->preOrganizationName = subObject.value("preOrganizationName").toString();
        //第二级
        for(int i = 0; i < subArray.size(); ++i)
        {
            QJsonObject tempObj = subArray.at(i).toObject();
            Department *subDepartment = new Department();
            subDepartment->id = tempObj.value("id").toInt();
            subDepartment->name = tempObj.value("name").toString();
            subDepartment->number = tempObj.value("nnt").toInt();
            subDepartment->preOrganizationName = tempObj.value("preOrganizationName").toString();
            topDepartment->subList.append(subDepartment);
        }

        depList.append(topDepartment);
    }

    return depList;
}

void WidgetTree::on_pushButton_Get2_clicked()
{
    emit sig_UpdateTreeWidget();
}

void WidgetTree::on_pushButton_Get_clicked()
{
    emit sig_UpdateTreeView();
}


/*************************
 * 菜单栏操作
 * ***********************/
void WidgetTree::on_refreshOne(QTreeWidgetItem *item)
{
    MY_DEBUG << "Refresh:" << item->data(0, ROLE_SUB_DEVNO).toString();
}

void WidgetTree::on_refreshAll()
{
    MY_DEBUG << "Refresh all";
}

void WidgetTree::on_devOperate(QString devno)
{
    MY_DEBUG << "devno" << devno;
}

void WidgetTree::on_devSelectItem(QString devno, int nTreeMode)
{
    MY_DEBUG << "devno:" << devno << ", nTreeMode:" << nTreeMode;
}

void WidgetTree::on_refreshOne(QStandardItem *item)
{
    MY_DEBUG << "item->data(ROLE_NAME):" << item->data(ROLE_NAME).toString();
}

void WidgetTree::on_devOperate(QStandardItem *item)
{
    MY_DEBUG << "item->data(ROLE_NAME):" << item->data(ROLE_NAME).toString();
}

void WidgetTree::on_devSelectItem(QStandardItem *item, int nTreeMode)
{
    MY_DEBUG << "item->data(ROLE_NAME):" << item->data(ROLE_NAME) << ", nTreeMode:" << nTreeMode;
}


/*************************
 * 语言翻译
 * ***********************/
void WidgetTree::changeEvent(QEvent *event)
{
    switch(event->type())
    {
        case QEvent::LanguageChange:
            retranslateUi();
            break;
        default:
            QWidget::changeEvent(event);
    }
}

void WidgetTree::retranslateUi()
{
    ui->label->setText(tr("Use Tree View:"));
    ui->label_2->setText(tr("Use Tree Widget:"));
    ui->label_3->setText(tr("Use Tree View(Custom model):"));
}




qctreeview.h

#ifndef QCTREEVIEW_H
#define QCTREEVIEW_H

#include <QTreeView>
#include <QStandardItemModel>
#include <QMoveEvent>
#include <QMouseEvent>
#include <QMenu>

enum TreeViewMode
{
    TREE_VIEW_MODE_DEMO = 0,
    TREE_VIEW_MODE_MAX
};

#define ROLE_DEVNO      (Qt::UserRole)
#define ROLE_PARENT     (Qt::UserRole+1)
#define ROLE_NAME       (Qt::UserRole+2)


class QCTreeView : public QTreeView
{
    Q_OBJECT
public:
    explicit QCTreeView(QWidget *parent = nullptr);
    ~QCTreeView() Q_DECL_OVERRIDE;

    void setTreeMode(int nTreemode, bool bDrag);
    void initMenu();

    QStandardItemModel* getModel();

protected:
    void changeEvent(QEvent* event) Q_DECL_OVERRIDE;

    //鼠标事件
    void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; // first event.
    //void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; // second
    void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE;

private:
    void translateUI();

    //tree mode
    int m_nTreemode;

    //Menu
    QMenu   *m_actMenu;
    QAction *m_actDevRefresh;
    QAction *m_actDevRefreshall;
    QAction *m_actDevOperate;

    //tree model
    QStandardItemModel* m_pModel;
    QPoint m_startpos;

signals:

    //单个刷新
    void sig_refreshOne(QStandardItem *item);
    //全部刷新
    void sig_refreshAll();
    //操作
    void sig_devOperate(QStandardItem *item);

    //选中
    void sig_devSelectItem(QStandardItem *item, int nMode);

public slots:
    void on_showContextMenu(const QPoint& pos);

};

#endif // QCTREEVIEW_H

qctreeview.cpp

#include <QMimeData>
#include <QDrag>
#include <QApplication>
#include "messagebox.h"
#include "qctreeview.h"
#include "commondef.h"

QCTreeView::QCTreeView(QWidget *parent)
    : QTreeView(parent)
{
    m_nTreemode = 0;
    m_pModel = new QStandardItemModel(this);
    setModel(m_pModel);

    //在view内item支持拖和放
    this->setDragDropMode(QTreeView::DragDrop);//NoDragDrop:不支持拖和放

    //在view内item支持正常选择时,一次选择一项;可以通过拖动鼠标选择多项
    this->setSelectionMode(QAbstractItemView::ExtendedSelection);

    //双击禁止展开
    this->setExpandsOnDoubleClick(false);

    //初始化是禁止拖拽
    this->setDragEnabled(false);

    //设置列宽
    //this->setColumnWidth(0, 320);

    //设置最大尺寸宽度
    //this->setMaximumWidth(320);

    initMenu();
}

QCTreeView::~QCTreeView()
{
    SAFE_FREE(m_actDevRefresh);
    SAFE_FREE(m_actDevRefreshall);
    SAFE_FREE(m_actDevOperate);

    SAFE_FREE(m_actMenu);

    SAFE_FREE(m_pModel);
}

void QCTreeView::setTreeMode(int nTreemode, bool bDrag)
{
    m_nTreemode = nTreemode;
    this->setDragEnabled(bDrag);
}

void QCTreeView::initMenu()
{
    m_actMenu = new QMenu(this);
    m_actMenu->setStyleSheet("border:1px solid #4d738b;");

    m_actDevRefresh = new QAction("Refresh", this);
    m_actDevRefreshall = new QAction("Refresh all", this);
    m_actDevOperate = new QAction("Operate", this);

    connect(m_actDevRefresh, &QAction::triggered, [this]()
    {
        QModelIndex curIndex = this->currentIndex();
        int row = curIndex.row();
        int column = curIndex.column();

        if(-1 == row || -1 == column)
        {
            return;
        }

        QStandardItem *curItem;
        int parentRow = curIndex.data(ROLE_PARENT).toInt();
        if(-1 == parentRow)
        {
            curItem = m_pModel->item(row, column);
        }
        else
        {
            QStandardItem *parentItem = m_pModel->item(parentRow, column);
            curItem = parentItem->child(row, column);
        }

        if(curItem)
        {
            emit this->sig_refreshOne(curItem);
        }
    });
    connect(m_actDevRefreshall, &QAction::triggered, [this]()
    {
        emit sig_refreshAll();
    });
    connect(m_actDevOperate, &QAction::triggered, [this]()
    {
        QModelIndex curIndex = this->currentIndex();
        int row = curIndex.row();
        int column = curIndex.column();

        if(-1 == row || -1 == column)
        {
            return;
        }

        QStandardItem *curItem;
        int parentRow = curIndex.data(ROLE_PARENT).toInt();
        if(-1 == parentRow)
        {
            curItem = m_pModel->item(row, column);
        }
        else
        {
            QStandardItem *parentItem = m_pModel->item(parentRow, column);
            curItem = parentItem->child(row, column);
        }

        if(curItem)
        {
            emit this->sig_devOperate(curItem);
        }
    });


    //设置菜单属性方法 右键菜单
    setContextMenuPolicy(Qt::CustomContextMenu);
    connect(this, SIGNAL(customContextMenuRequested(const QPoint& )),
            this, SLOT(on_showContextMenu(const QPoint&)));
}

QStandardItemModel *QCTreeView::getModel()
{
    return m_pModel;
}

void QCTreeView::changeEvent(QEvent *event)
{
    switch(event->type())
    {
        case QEvent::LanguageChange:
            translateUI();
            break;
        default:
            QWidget::changeEvent(event);
    }
}

void QCTreeView::translateUI()
{
    m_actDevRefresh->setText(QObject::tr("Refresh"));
    m_actDevRefreshall->setText(QObject::tr("Refresh All"));
    m_actDevOperate->setText(QObject::tr("Play"));
}

void QCTreeView::on_showContextMenu(const QPoint &pos)
{
    QModelIndex curIndex = indexAt(pos);
    int row = curIndex.row();
    int column = curIndex.column();

    m_actMenu->clear();
    if(-1 == row || -1 == column)
    {
        m_actMenu->addAction(m_actDevRefreshall);
    }
    else
    {
        m_actMenu->addAction(m_actDevRefresh);
        m_actMenu->addAction(m_actDevOperate);
    }

    m_actMenu->exec(mapToGlobal(pos));
}


void QCTreeView::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        m_startpos = event->pos();
    }

    QModelIndex curIndex = indexAt(event->pos());
    if(m_nTreemode == TREE_VIEW_MODE_DEMO)
    {
        int row = curIndex.row();
        int column = curIndex.column();

        if(-1 == row || -1 == column)
        {
            return;
        }

        QStandardItem *curItem;
        int parentRow = curIndex.data(ROLE_PARENT).toInt();
        if(-1 == parentRow)
        {
            curItem = m_pModel->item(row, column);
        }
        else
        {
            QStandardItem *parentItem = m_pModel->item(parentRow, column);
            curItem = parentItem->child(row, column);
        }

        if(curItem)
        {
            emit sig_devSelectItem(curItem, m_nTreemode);
        }
    }

    QTreeView::mousePressEvent(event);
}

void QCTreeView::mouseReleaseEvent(QMouseEvent *event)
{
    QTreeView::mouseReleaseEvent(event);
}

void QCTreeView::mouseDoubleClickEvent(QMouseEvent *event)
{
    QTreeView::mouseDoubleClickEvent(event);
}

qctreewidget.h

#ifndef QCTREEWIDGET_H
#define QCTREEWIDGET_H

#include <QTreeWidget>
#include <QMoveEvent>
#include <QMouseEvent>
#include <QMenu>

enum TreeWidgetMode
{
    TREE_MODE_DEMO = 0,
    TREE_MODE_MAX
};

#define ROLE_DEVNO      (Qt::UserRole)
#define ROLE_NAME       (Qt::UserRole+1)
#define ROLE_NUMBER     (Qt::UserRole+2)
#define ROLE_SUB_DEVNO  (Qt::UserRole+3)



class QCTreeWidget : public QTreeWidget
{
    Q_OBJECT
public:
    explicit QCTreeWidget(QWidget *parent = nullptr);
    ~QCTreeWidget() Q_DECL_OVERRIDE;

    void setTreeMode(int nTreemode, bool bDrag);
    void initMenu();

protected:
    void changeEvent(QEvent* event) Q_DECL_OVERRIDE;

    //右菜单事件
    void contextMenuEvent(QContextMenuEvent *event) Q_DECL_OVERRIDE;

    //拖拽事件
    void startDrag(Qt::DropActions Actions) Q_DECL_OVERRIDE;
    void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE;
    void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE;

    //鼠标事件
    void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; // first event.
    void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; // second
    void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE;

private:
    void translateUI();

    //tree mode
    int m_nTreemode;

    //Menu
    QMenu   *m_actMenu;
    QAction *m_actDevRefresh;
    QAction *m_actDevRefreshall;
    QAction *m_actDevOperate;

    //tree item
    QTreeWidgetItem *m_pItem;
    QPoint m_startpos;
    QTreeWidgetItem *m_curDragItem;

signals:
    //单个刷新
    void sig_refreshOne(QTreeWidgetItem *item);
    //全部刷新
    void sig_refreshAll();
    //操作
    void sig_devOperate(QString sMac);

    //选中
    void sig_devSelectItem(QString sMac, int nMode);

};

#endif // QCTREEWIDGET_H


qctreewidget.cpp

#include <QMimeData>
#include <QDrag>
#include <QApplication>
#include "messagebox.h"
#include "qctreewidget.h"
#include "commondef.h"

QCTreeWidget::QCTreeWidget(QWidget *parent)
    : QTreeWidget(parent)
{
    m_nTreemode = 0;
    this->m_pItem = nullptr;

    //在view内item支持拖和放
    this->setDragDropMode(QTreeWidget::DragDrop);//NoDragDrop:不支持拖和放

    //在view内item支持正常选择时,一次选择一项;可以通过拖动鼠标选择多项
    this->setSelectionMode(QAbstractItemView::ExtendedSelection);

    //双击禁止展开
    this->setExpandsOnDoubleClick(false);

    //初始化是禁止拖拽
    this->setDragEnabled(false);

    //设置列宽
    //this->setColumnWidth(0, 320);

    //设置最大尺寸宽度
    //this->setMaximumWidth(320);

    initMenu();
}

QCTreeWidget::~QCTreeWidget()
{
    SAFE_FREE(m_actDevRefresh);
    SAFE_FREE(m_actDevRefreshall);
    SAFE_FREE(m_actDevOperate);

    SAFE_FREE(m_actMenu);
}

void QCTreeWidget::setTreeMode(int nTreemode, bool bDrag)
{
    m_nTreemode = nTreemode;
    this->setDragEnabled(bDrag);
}

void QCTreeWidget::initMenu()
{
    m_actMenu = new QMenu(this);
    m_actMenu->setStyleSheet("border:1px solid #4d738b;");

    m_actDevRefresh = new QAction("Refresh", this);
    m_actDevRefreshall = new QAction("Refresh all", this);
    m_actDevOperate = new QAction("Operate", this);

    connect(m_actDevRefresh, &QAction::triggered, [this]()
    {
        QTreeWidgetItem *item = currentItem();
        if(item)
        {
            emit this->sig_refreshOne(item);
        }
    });
    connect(m_actDevRefreshall, &QAction::triggered, [this]()
    {
        emit sig_refreshAll();
    });
    connect(m_actDevOperate, &QAction::triggered, [this]()
    {
        QTreeWidgetItem *item = currentItem();
        if(item)
        {
            QString sKey;
            sKey = item->data(0, ROLE_SUB_DEVNO).toString();
            emit this->sig_devOperate(sKey);
        }
    });
}

void QCTreeWidget::changeEvent(QEvent *event)
{
    switch(event->type())
    {
        case QEvent::LanguageChange:
            translateUI();
            break;
        default:
            QWidget::changeEvent(event);
    }
}

void QCTreeWidget::translateUI()
{
    m_actDevRefresh->setText(QObject::tr("Refresh"));
    m_actDevRefreshall->setText(QObject::tr("Refresh All"));
    m_actDevOperate->setText(QObject::tr("Play"));
}

void QCTreeWidget::contextMenuEvent(QContextMenuEvent *event)
{
    m_actMenu->clear();
    QList<QTreeWidgetItem *> Item_list = this->selectedItems();
    if (Item_list.size() > 1)
    {
        m_actMenu->addAction(m_actDevOperate);
        m_actMenu->exec(QCursor::pos());
        return;
    }

    QPoint pos = event->pos();
    QTreeWidgetItem *curitem = itemAt(pos);
    if(curitem) //右键对象菜单
    {
        switch(m_nTreemode)
        {
            case TREE_MODE_DEMO:
                m_actMenu->addAction(m_actDevOperate);
                m_actMenu->addAction(m_actDevRefresh);
                m_actMenu->exec(QCursor::pos());
                break;
        }

        //m_actMenu->exec(QCursor::pos());//空菜单会导致右键卡一下
    }
    else //右键空白菜单
    {
        bool isShowMenu = true;
        switch(m_nTreemode)
        {
            // 禁止视频的一键播放功能
            case TREE_MODE_DEMO:
                m_actMenu->addAction(m_actDevRefreshall);
                break;
            default:
                isShowMenu = false;
                break;
        }
        if(isShowMenu)
        {
            m_actMenu->exec(QCursor::pos());
        }
    }
    event->accept();
}

void QCTreeWidget::startDrag(Qt::DropActions Actions)
{
    m_curDragItem = currentItem();
    if(m_nTreemode == TREE_MODE_DEMO)
    {
        QTreeWidget::startDrag(Actions);
    }
    else
    {
        if(m_curDragItem->parent() != nullptr) //目录不允许拖动
        {
          QTreeWidget::startDrag(Actions);
        }
    }
}

void QCTreeWidget::dragMoveEvent(QDragMoveEvent *event)
{
    if(m_nTreemode == TREE_MODE_DEMO)
    {
        QCTreeWidget *src = qobject_cast<QCTreeWidget *>(event->source());
        if(src && src != this) // 移动到其他地盘
        {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }
        else // 不允许自己拖动到自己地盘
        {
            event->ignore();
        }
    }
    else
    {
        QCTreeWidget *src = qobject_cast<QCTreeWidget *>(event->source());
        if(src && src != this) // 移动到其他地盘,触发移动效果
        {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }
        else // 自己地盘允许自由拖放
        {
            QTreeWidget::dragMoveEvent(event);
        }
    }
}

void QCTreeWidget::dropEvent(QDropEvent *event)
{
    if(m_nTreemode == TREE_MODE_DEMO)
    {
        m_pItem = nullptr;
        QCTreeWidget *src = qobject_cast<QCTreeWidget *>(event->source());
        if(src && src != this) // 移动到其他地盘
        {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }
        else // 不允许自己拖动到自己地盘
        {
            event->ignore();
        }
    }
    else
    {
        QTreeWidgetItem *dst = this->itemAt(event->pos()); //当前位置的item
        if(dst == nullptr || dst->parent() != nullptr)//如果不是目录,忽略
        {
            event->ignore();
            return;
        }

        if(dst == m_curDragItem->parent() || dynamic_cast<void*>(dst) == dynamic_cast<void*>(this))//如果是相同目录,忽略
        {
            event->ignore();
            return;
        }
    }
}

void QCTreeWidget::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        m_startpos = event->pos();
    }

    //增加选中状态更新
    m_pItem = this->itemAt(event->pos());    
    if(m_nTreemode == TREE_MODE_DEMO)
    {
        if(m_pItem && m_pItem->parent() == nullptr)
        {
            QString sMac = m_pItem->data(0, ROLE_DEVNO).toString();
            MY_DEBUG << "TREE_MODE_DEMO sMac:" << sMac;
            emit sig_devSelectItem(sMac, m_nTreemode);
        }
    }
    else
    {
        if(m_pItem && m_pItem->parent() != nullptr)
        {
            QString sMac = m_pItem->data(0, ROLE_DEVNO).toString();
            MY_DEBUG << "Not TREE_MODE_DEMO sMac:" << sMac;
            emit sig_devSelectItem(sMac, m_nTreemode);
        }
    }

    QTreeWidget::mousePressEvent(event);
}

void QCTreeWidget::mouseMoveEvent(QMouseEvent *event)
{
    if(this->dragEnabled())
    {
        QTreeWidgetItem *item = currentItem();
        if(item)
        {
            if(item->flags() & Qt::ItemIsDragEnabled)
            {
                if(event->buttons() & Qt::LeftButton) //确保按住左键移动.
                {
                    if(selectedItems().size() > 1)//多选
                    {
                        return;
                    }

                    int ndis = (event->pos() - m_startpos).manhattanLength();
                    if(ndis > QApplication::startDragDistance())//只有这个长度大于默认的距离,才会被系统认为是形成拖拽的操作.
                    {
                        QTreeWidgetItem *item = this->m_pItem;
                        if(item == nullptr)
                        {
                            return;
                        }

                        QMimeData *mimeData = new QMimeData;
                        if(item->data(0, Qt::WhatsThisRole).toString() == QVariant("subItem"))
                        {
                            QString sDevno = item->data(0, ROLE_DEVNO).toString();
                            mimeData->setProperty("Devno", QVariant(sDevno));
                        }
                        else
                        {
                            return;
                        }

                        QDrag *drag = new QDrag(this);
                        drag->setMimeData(mimeData);
                        drag->setPixmap(item->icon(0).pixmap(28));
                        if(drag->exec(Qt::MoveAction) == Qt::MoveAction){}
                    }
                }
            }
        }
    }
}

void QCTreeWidget::mouseReleaseEvent(QMouseEvent *event)
{
    QTreeWidget::mouseReleaseEvent(event);
}

void QCTreeWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    QTreeWidget::mouseDoubleClickEvent(event);
}


treeitem.h

#ifndef TREEITEM_H
#define TREEITEM_H

#include <QVariant>

class TreeItem
{
public:
    enum Type
    {
        UNKNOWN = -1,
        PROVINCE,
        PERSON
    };

    explicit TreeItem(TreeItem *parent = nullptr);
    ~TreeItem();

    void addChild(TreeItem *item);
    void removeChildren();

    TreeItem *child(int row) { return m_childrenList.value(row); }
    TreeItem *parent() { return m_pParent; }

    int childCount() const { return m_childrenList.count(); }

    QVariant data(int column) const;

    //设置、获取节点存的数据指针
    void setPtr(void* p) { m_ptr = p; }
    void* ptr() const { return m_ptr; }

    // 保存该节点是其父节点的第几个子节点,查询优化所用
    void setRow(int row) { m_nRow = row; }

    //返回本节点位于父节点下第几个子节点
    int row() const { return m_nRow; }

    //类型
    Type getType() const { return m_nType; }
    void setType(const Type &value) { m_nType = value; }

private:
    QList<TreeItem*> m_childrenList;   // 子节点
    TreeItem *m_pParent;          // 父节点
    Type m_nType;      // 此节点保存的数据类型
    void* m_ptr;     // 存储数据的指针
    int m_nRow;       // 此item位于父节点中第几个
};

#endif // TREEITEM_H


treeitem.cpp

#include "treeitem.h"
#include "typedef.h"

TreeItem::TreeItem(TreeItem *parent)
    : m_pParent(parent),
      m_nType(UNKNOWN),
      m_ptr(nullptr),
      m_nRow(-1)
{

}

TreeItem::~TreeItem()
{
    removeChildren();
}

// 在本节点下添加子节点
void TreeItem::addChild(TreeItem *item)
{
    item->setRow(m_childrenList.size());
    m_childrenList.append(item);
}

// 清空所有子节点
void TreeItem::removeChildren()
{
    qDeleteAll(m_childrenList);
    m_childrenList.clear();
}

// 获取本节点第column列的数据
QVariant TreeItem::data(int column) const
{
    Department *department = (Department*)m_ptr;
    switch (column) {
        case COLUMN_NAME: return department->name;
        case COLUMN_ID: return department->id;
        case COLUMN_NUM: return department->number;
        case COLUMN_OrganizationName: return department->preOrganizationName;
    default:return QVariant();
    }

    return QVariant();
}

treemodel.h

#ifndef TREEMODEL_H
#define TREEMODEL_H
#include <QAbstractItemModel>

class TreeItem;

//自定义模型
class TreeModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    explicit TreeModel(const QStringList& headers, QObject *parent = nullptr);
    ~TreeModel() override;

    //根节点
    TreeItem *root();

    //获取表头数据
    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
    //获取index.row行,index.column列数据
    QVariant data(const QModelIndex &index, int role) const override;
    //在parent节点下,第row行,第column列位置上创建索引
    QModelIndex index(int row, int column, const QModelIndex &parent) const override;
    //创建index的父索引
    QModelIndex parent(const QModelIndex &index) const override;
    //获取索引parent下有多少行
    int rowCount(const QModelIndex &parent) const override;
    //返回索引parent下有多少列
    int columnCount(const QModelIndex &parent) const override;

private:
    TreeItem *itemFromIndex(const QModelIndex &index) const;

private:
    QStringList m_pHeaders;
    TreeItem* m_pRootItem;
};

#endif // TREEMODEL_H

treemodel.cpp

#include "treemodel.h"
#include "treeitem.h"
#include "commondef.h"

TreeModel::TreeModel(const QStringList& headers, QObject *parent)
    : QAbstractItemModel(parent)
{
    m_pHeaders = headers;
    m_pRootItem = new TreeItem();
}

TreeModel::~TreeModel()
{
    delete m_pRootItem;
}

TreeItem *TreeModel::itemFromIndex(const QModelIndex &index) const
{
    if (index.isValid())
    {
        TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
        return item;
    }
    return m_pRootItem;
}

TreeItem *TreeModel::root()
{
    return m_pRootItem;
}

// 获取表头数据
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,int role) const
{
    if (orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return m_pHeaders.at(section);
        }
    }
    return QVariant();
}

// 获取index.row行,index.column列数据
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    TreeItem *item = itemFromIndex(index);
    if (role == Qt::DisplayRole)
    {
        return item->data(index.column());
    }
    return QVariant();
}

// 在parent节点下,第row行,第column列位置上创建索引
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    TreeItem *parentItem = itemFromIndex(parent);
    TreeItem *item = parentItem->child(row);
    if (item)
        return createIndex(row, column, item);
    else
        return QModelIndex();
}

// 创建index的父索引
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    TreeItem *item = itemFromIndex(index);
    TreeItem *parentItem = item->parent();

    if (parentItem == m_pRootItem)
        return QModelIndex();
    return createIndex(parentItem->row(), 0, parentItem);
}

// 获取索引parent下有多少行
int TreeModel::rowCount(const QModelIndex &parent) const
{
    if (parent.column() > 0)
        return 0;

    TreeItem* item = itemFromIndex(parent);
    return item->childCount();
}

// 返回索引parent下有多少列
int TreeModel::columnCount(const QModelIndex &parent) const
{
    return m_pHeaders.size();
}


typedef.h

#ifndef TYPEDEF_H
#define TYPEDEF_H

#include <QVector>

// department信息
typedef struct Department_t{
    QString name;                   // 部门名称
    int id;                         // 部门id号
    int number;                     // 部门人数
    QString preOrganizationName;    // 上级组织名

    Department_t()
    {
        id = 0;
        number = 0;
    }
    QVector<Department_t*> subList;
} Department;

// 树列号
enum COLUMN
{
    COLUMN_NAME = 0,
    COLUMN_ID,
    COLUMN_NUM,
    COLUMN_OrganizationName
};

#endif // TYPEDEF_H


实现效果:

在这里插入图片描述

工程下载路径:

https://download.csdn.net/download/linyibin_123/86512679

  • 4
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浅笑一斤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值