QTreeWidget

阅读数:27 评论数:0

跳转到新版页面

分类

C/C++

正文

一、概述

QTreeWidget 是 Qt 框架中的一个控件,用于以树形结构展示数据。它是 QTreeView 的一个高级版本,提供了一个基于项目(item-based)的接口来管理和显示层次数据。使用 QTreeWidget,你可以添加、移除和修改树中的项目(QTreeWidgetItem 对象),每个项目可以有一个或多个列(column)来显示不同的数据。

二、创建和设置QTreeWidget

QTreeWidget *treeWidget = new QTreeWidget(parent);

// 设置列数
treeWidget->setColumnCount(3);

// 设置列标题
QStringList headers;
headers << "Column 1" << "Column 2" << "Column 3";
treeWidget->setHeaderLabels(headers);

三、添加项目

// 创建一个树项目
QTreeWidgetItem *treeItem = new QTreeWidgetItem(treeWidget);

// 设置项目的列数据
treeItem->setText(0, "Item 1 Column 1");
treeItem->setText(1, "Item 1 Column 2");
treeItem->setText(2, "Item 1 Column 3");

// 将项目添加到树中
treeWidget->addTopLevelItem(treeItem);

四、添加子项目

// 创建子项目
QTreeWidgetItem *childItem = new QTreeWidgetItem();

// 设置子项目的列数据
childItem->setText(0, "Child Item Column 1");
childItem->setText(1, "Child Item Column 2");
childItem->setText(2, "Child Item Column 3");

// 将子项目添加到父项目中
treeItem->addChild(childItem);

五、选择和迭代

// 获取当前选中的项目
QTreeWidgetItem *selectedItem = treeWidget->currentItem();

// 遍历所有顶级项目
for (int i = 0; i < treeWidget->topLevelItemCount(); ++i) {
    QTreeWidgetItem *item = treeWidget->topLevelItem(i);
    // 对每个顶级项目做一些处理
}

// 遍历子项目
for (int i = 0; i < treeItem->childCount(); ++i) {
    QTreeWidgetItem *child = treeItem->child(i);
    // 对每个子项目做一些处理
}

六、信号和槽

// 当项目被选中时发出
connect(treeWidget, &QTreeWidget::itemSelectionChanged, this, &YourClass::onItemSelectionChanged);

// 当项目被双击时发出
connect(treeWidget, &QTreeWidget::itemDoubleClicked, this, &YourClass::onItemDoubleClicked);

// ...在 YourClass 中实现相应的槽函数

七、自定义item

你可以通过继承 QTreeWidgetItem 类来创建自定义的树项目,并添加自定义的行为和属性。

class CustomTreeWidgetItem : public QTreeWidgetItem {
public:
    CustomTreeWidgetItem(QTreeWidget *parent) : QTreeWidgetItem(parent) {
        // 自定义初始化代码
    }
    // ... 自定义方法和属性
};

八、树的外观和行为

// 设置选择模式
treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);

// 启用拖放
treeWidget->setDragEnabled(true);
treeWidget->setAcceptDrops(true);
treeWidget->setDropIndicatorShown(true);

设置勾选及选择的样式

    setObjectName("motorTree");
    setStyleSheet("#motorTree {"
                  "border: none;"
                  "font-weight: 400;"
                  "font-size: 20px;"
                  "color: #18181A;"
                  "}"
                  "#motorTree::item {"
                  "border: none;"
                  "}"
                  "#motorTree::item:selected {"
                  "color: #2961B2;"
                  "background-color: rgba(41,91,178,0.12);"
                  "}"
                  "#motorTree::indicator {"
                  "width: 16px;"
                  "height: 16px;"
                  "}"
                  "#motorTree::indicator:checked {"
                  " image: url(:/images/checked.png);"

                  "}"
                  "#motorTree::indicator:unchecked {"
                  "image: url(:/images/unchecked.png);"
                  "}"
                  "");

去掉列标题

QTreeWidget *treeWidget = new QTreeWidget(this);
treeWidget->header()->setVisible(false); // 隐藏列标题

去掉边框

treeWidget->setStyleSheet("QTreeWidget { border: none; }");

设置item上现边距

class TreeItemStyledDelegate:public QStyledItemDelegate
{
public:
    using QStyledItemDelegate::QStyledItemDelegate;

    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        QSize size = QStyledItemDelegate::sizeHint(option, index);
        // 调整高度以包含边距
        size.setHeight(size.height() + 20); // 增加20像素的边距
        return size;
    }
};

#endif

// 设置委托到你的treeWidget
treeWidget->setItemDelegate(new ItemDelegate(treeWidget));

增加可点击的图标

setColumnCount(2);
// 创建一个复选框图标
QIcon checkIcon(":/icons/check_icon.png");

// 创建一个项并设置复选框图标
QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
item->setText(0, "Item 1");
item->setCheckState(0, Qt::Unchecked);  // 设置为未选中状态
item->setIcon(1, checkIcon);  // 在第二列设置复选框图标

// 为复选框图标的点击事件连接一个槽函数
connect(treeWidget, &QTreeWidget::itemClicked, [=](QTreeWidgetItem *item, int column) {
    if (column == 1) { // 第二列的复选框图标被点击
        if (item->checkState(0) == Qt::Unchecked) {
            item->setCheckState(0, Qt::Checked);
        } else {
            item->setCheckState(0, Qt::Unchecked);
        }
    }
});

九、滚动条

int MotorTree::calTotalHeight(QTreeWidgetItem *item){
    int totalHeight = visualItemRect(item).height();
    if(item->isExpanded()){
        for(int i=0;i<item->childCount();++i){
            totalHeight += calTotalHeight(item->child(i));
        }
    }
    return totalHeight;
}


void MotorTree::updateScrollBars(){
    int contentHeight = 0;
    for(int i=0;i<topLevelItemCount();++i){
        contentHeight += calTotalHeight(topLevelItem(i));
    }
    int contentWidth = header()->length();
    setVerticalScrollBarPolicy(contentHeight>maximumHeight()? Qt::ScrollBarAlwaysOn: Qt::ScrollBarAsNeeded);
    setHorizontalScrollBarPolicy(contentWidth>maximumWidth()? Qt::ScrollBarAlwaysOn: Qt::ScrollBarAsNeeded);
}

...
    connect(this,&MotorTree::itemExpanded,this,&MotorTree::updateScrollBars);
    connect(this,&MotorTree::itemCollapsed,this,&MotorTree::updateScrollBars);

QHeaderView 类的 length() 方法返回的是 header 的总长度。这个长度是 header 中所有列的长度之和,不管它们是否可见。因此,如果你的 QTreeWidget 有多列,header()->length() 就会返回所有这些列宽度的总和。

十、使用json文件读写

1、json文件

[
  {
    "type": "folder",
    "id": "1",
    "parentId": "",
    "title": "Root"
  },
  {
    "type": "file",
    "id": "2",
    "parentId": "1",
    "title": "Child 1"
  },
  {
    "type": "file",
    "id": "3",
    "parentId": "1",
    "title": "Child 2"
  },
  {
    "type": "file",
    "id": "4",
    "parentId": "2",
    "title": "Grandchild of Child 1"
  }
]

(2)从json文件读取数据到QTreeWidget

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

void loadTreeWidgetFromJson(QTreeWidget* treeWidget, const QString& fileName) {
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly) {
        qWarning("Cannot open file for reading");
        return;
    }

    QByteArray jsonData = file.readAll();
    file.close();

    QJsonDocument document = QJsonDocument::fromJson(jsonData);
    QJsonArray jsonArray = document.array();

    QMap<QString, QTreeWidgetItem*> itemMap;

    for (const QJsonValue& value : jsonArray) {
        QJsonObject obj = value.toObject();
        QString id = obj["id"].toString();
        QString parentId = obj["parentId"].toString();
        QString title = obj["title"].toString();

        QTreeWidgetItem* parentItem = itemMap.value(parentId, nullptr);
        QTreeWidgetItem* item = new QTreeWidgetItem(parentItem ? parentItem : treeWidget);
        item->setText(0, title);
        item->setData(0, Qt::UserRole, QVariant(id));
        item->setData(0, Qt::UserRole + 1, QVariant(obj["type"].toString()));
        
        if (!parentItem) {
            treeWidget->addTopLevelItem(item);
        }
        
        itemMap[id] = item;
    }
}

在Qt中,当你尝试以QIODevice::WriteOnlyQIODevice::WriteOnly | QIODevice::Truncate模式打开一个文件时,如果文件不存在,QFile::open()方法会自动创建这个文件。

(3)将QTreeWidget写回到json文件

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

void saveTreeWidgetToJson(QTreeWidget* treeWidget, const QString& fileName) {
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qWarning("Cannot open file for writing");
        return;
    }

    QJsonArray jsonArray;

    QTreeWidgetItemIterator it(treeWidget);
    while (*it) {
        QTreeWidgetItem* item = *it;
        QJsonObject obj;
        obj["title"] = item->text(0);
        obj["id"] = item->data(0, Qt::UserRole).toString();
        obj["type"] = item->data(0, Qt::UserRole + 1).toString();
        obj["parentId"] = item->parent() ? item->parent()->data(0, Qt::UserRole).toString() : "";

        jsonArray.append(obj);
        ++it;
    }

    QJsonDocument document(jsonArray);
    file.write(document.toJson());
    file.close();
}

在Qt的模型/视图编程中,每个QTreeWidgetItem可以存储多个与之关联的数据项。每个数据项都与一个角色相关联。角色是一个整数值,用于指定数据项的用途。例如,Qt::DisplayRole用于存储显示给用户的文本,Qt::DecorationRole用于存储图标等。

Qt::UserRole是一个在Qt中预定义的角色,其值比所有预定义角色的值都要大。它的目的是允许开发者存储自定义的数据。从Qt::UserRole开始的角色值被保留用于用户自定义的数据。这意味着你可以使用Qt::UserRole和比它大的值来存储任何你需要的额外信息,而不会与Qt的标准角色冲突。

十一、实现项的拖拽

// 启用拖放
treeWidget->setDragEnabled(true);
treeWidget->setAcceptDrops(true);
treeWidget->setDropIndicatorShown(true);
treeWidget->setDragDropMode(QAbstractItemView::InternalMove);


// 对于每个QTreeWidgetItem,设置拖放标志
QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
item->setText(0, "Item Title");
item->setData(0, Qt::UserRole, QVariant(itemId)); // itemId为该项目的ID
item->setData(0, Qt::UserRole + 1, QVariant(parentId)); // parentId为父项目的ID
item->setFlags(item->flags() | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);

当你使用QAbstractItemView::InternalMove作为参数调用setDragDropMode时,你告诉QTreeWidget启用内部项的拖放,并且只允许在同一控件内移动项(而不是从其他控件拖入或拖出)。这意味着用户可以通过拖放来重新排列项,但不能将它们拖到QTreeWidget外部,也不能从外部拖入项。

void MyTreeWidget::dropEvent(QDropEvent *event)
{
    // 处理拖放事件,确定放置位置,移动项目等
    QTreeWidget::dropEvent(event);
    // 这里可以添加代码来更新项目的parentId
}

void MyTreeWidget::dragMoveEvent(QDragMoveEvent *event)
{
    // 处理拖动事件,决定是否允许移动到某个位置
    QTreeWidget::dragMoveEvent(event);
    // 这里可以添加代码来提供更多自定义行为
}

十二、获取所有勾选项

void MotorTree::getCheckedItemRecursive(QTreeWidgetItem* item){

    if(item->checkState(0)==Qt::Checked){
        QString id = item->data(0,Qt::UserRole).toString();
        checkItemIdList.append(id);
    }
    for(int i=0;i<item->childCount();++i){
        getCheckedItemRecursive(item->child(i));
    }
}

QString MotorTree::getCheckedItemIds(){
    checkItemIdList.clear();
    for(int i=0;i<topLevelItemCount();++i){
        getCheckedItemRecursive(topLevelItem(i));
    }
    return checkItemIdList.join(",");
}



相关推荐