效果如下:
图片随便找的,可能需要调下样式,代码复制可用,留给有需要的人。
#ifndef CustomTreeWidget_h__
#define CustomTreeWidget_h__#include <QTreeWidget>
#include <QPushButton>class CCustomTreeWidget : public QTreeWidget
{Q_OBJECTpublic:CCustomTreeWidget(QWidget* parent = nullptr);~CCustomTreeWidget();QTreeWidgetItem* AddItem(QTreeWidgetItem* pParent = NULL);void ToggleItem(QTreeWidgetItem* pItem);void ExpandAllNodes();void CollapseAllNodes();protected:void mousePressEvent(QMouseEvent* event) override;void keyPressEvent(QKeyEvent* event) override;private:void UpdateItemWidget(QTreeWidgetItem* pItem);void UpdateAllButtons(const QIcon& icon);void UpdateItemButton(QTreeWidgetItem* pItem, const QIcon& icon);private slots:void SlotToggleNode(QTreeWidgetItem* pItem, QPushButton* pPushButton);};#endif // CustomTreeWidget_h__
#include "CustomTreeWidget.h"
#include <QHeaderView>
#include <QMouseEvent>
#include <QBoxLayout>CCustomTreeWidget::CCustomTreeWidget(QWidget* parent /*= nullptr*/): QTreeWidget(parent)
{setAttribute(Qt::WA_TranslucentBackground, true);setRootIsDecorated(false);setColumnCount(2);header()->hide();header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); // 第一列宽度自适应内容header()->setSectionResizeMode(1, QHeaderView::Fixed); // 第二列宽度固定setColumnWidth(1, 30); // 设置一个初始宽度,实际宽度会在按钮创建后更新// 隐藏默认的展开和折叠按钮setStyleSheet("QTreeView::branch:has-children:!has-siblings:closed,""QTreeView::branch:closed:has-children:has-siblings {""border-image: none; image: none;}""QTreeView::branch:open:has-children:!has-siblings,""QTreeView::branch:open:has-children:has-siblings {""border-image: none; image: none;}""QTreeWidget::item{ height: 20px; }");
}CCustomTreeWidget::~CCustomTreeWidget()
{}QTreeWidgetItem* CCustomTreeWidget::AddItem(QTreeWidgetItem* pParent)
{QTreeWidgetItem* pItem = NULL;if (NULL != pParent){pItem = new QTreeWidgetItem(pParent);pParent->addChild(pItem);UpdateItemWidget(pParent);}else{pItem = new QTreeWidgetItem();addTopLevelItem(pItem);UpdateItemWidget(pItem);}return pItem;
}void CCustomTreeWidget::ToggleItem(QTreeWidgetItem* pItem)
{if (NULL == pItem){return;}if (NULL != itemWidget(pItem, 1)){QPushButton* pPushButton = qobject_cast<QPushButton*>(itemWidget(pItem, 1)->findChild<QPushButton*>());if (pItem->isExpanded()){pPushButton->setIcon(QIcon(":/treeitem-expanded.png"));}else{pPushButton->setIcon(QIcon(":/treeitem-collapsed.png"));}}
}void CCustomTreeWidget::ExpandAllNodes()
{expandAll();UpdateAllButtons(QIcon(":/treeitem-expanded.png"));
}void CCustomTreeWidget::CollapseAllNodes()
{collapseAll();UpdateAllButtons(QIcon(":/treeitem-collapsed.png"));
}void CCustomTreeWidget::mousePressEvent(QMouseEvent* event)
{if (itemAt(event->pos())){event->accept();}else{QTreeWidget::mousePressEvent(event);}
}void CCustomTreeWidget::keyPressEvent(QKeyEvent* event)
{if (event->key() == Qt::Key_Right || event->key() == Qt::Key_Left){event->ignore();}else{QTreeWidget::keyPressEvent(event);}
}void CCustomTreeWidget::UpdateItemWidget(QTreeWidgetItem* pItem)
{if (NULL == pItem){return;}if (pItem->childCount() > 0){QWidget* pWidget = new QWidget();QHBoxLayout* pLayout = new QHBoxLayout(pWidget);pLayout->setContentsMargins(0, 0, 0, 0);pLayout->setAlignment(Qt::AlignRight);QPushButton* pPushButton = new QPushButton();pPushButton->setStyleSheet("background: transparent; border: none;");QIcon icon(":/treeitem-collapsed.png");pPushButton->setIcon(icon);pPushButton->setIconSize(icon.availableSizes().first());pLayout->addWidget(pPushButton);pWidget->setLayout(pLayout);setItemWidget(pItem, 1, pWidget);const int nIconWidth = pPushButton->iconSize().width();setColumnWidth(1, nIconWidth);connect(pPushButton, &QPushButton::clicked, [this, pItem, pPushButton](){SlotToggleNode(pItem, pPushButton);});}else{if (QWidget* pWidget = itemWidget(pItem, 1)){delete pWidget;setItemWidget(pItem, 1, NULL);}}
}void CCustomTreeWidget::UpdateAllButtons(const QIcon& icon)
{for (int i = 0; i < topLevelItemCount(); ++i){UpdateItemButton(topLevelItem(i), icon);}
}void CCustomTreeWidget::UpdateItemButton(QTreeWidgetItem* pItem, const QIcon& icon)
{if (NULL != itemWidget(pItem, 1)){QPushButton* pPushButton = qobject_cast<QPushButton*>(itemWidget(pItem, 1)->findChild<QPushButton*>());if (NULL != pPushButton){pPushButton->setIcon(icon);}for (int i = 0; i < pItem->childCount(); ++i){UpdateItemButton(pItem->child(i), icon);}}
}void CCustomTreeWidget::SlotToggleNode(QTreeWidgetItem* pItem, QPushButton* pPushButton)
{if (pItem->isExpanded()){pItem->setExpanded(false);pPushButton->setIcon(QIcon(":/treeitem-collapsed.png"));}else{pItem->setExpanded(true);pPushButton->setIcon(QIcon(":/treeitem-expanded.png"));}
}
调用代码:
#include "CustomTreeWidget.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);CCustomTreeWidget treeWidget;treeWidget.setWindowTitle("Custom Tree Widget");treeWidget.resize(400, 300);// 添加三个顶级节点QTreeWidgetItem* topLevelItem1 = treeWidget.AddItem();topLevelItem1->setText(0, "Top Level 1");QTreeWidgetItem* topLevelItem2 = treeWidget.AddItem();topLevelItem2->setText(0, "Top Level 2");QTreeWidgetItem* topLevelItem3 = treeWidget.AddItem();topLevelItem3->setText(0, "Top Level 3");// 为每个顶级节点增加三级子节点for (int i = 0; i < 3; ++i){QTreeWidgetItem* child1 = treeWidget.AddItem(topLevelItem1);child1->setText(0, QString("Child 1.%1").arg(i + 1));QTreeWidgetItem* child2 = treeWidget.AddItem(topLevelItem2);child2->setText(0, QString("Child 2.%1").arg(i + 1));QTreeWidgetItem* child3 = treeWidget.AddItem(topLevelItem3);child3->setText(0, QString("Child 3.%1").arg(i + 1));for (int j = 0; j < 3; ++j){QTreeWidgetItem* grandChild1 = treeWidget.AddItem(child1);grandChild1->setText(0, QString("Grandchild 1.%1.%2").arg(i + 1).arg(j + 1));QTreeWidgetItem* grandChild2 = treeWidget.AddItem(child2);grandChild2->setText(0, QString("Grandchild 2.%1.%2").arg(i + 1).arg(j + 1));QTreeWidgetItem* grandChild3 = treeWidget.AddItem(child3);grandChild3->setText(0, QString("Grandchild 3.%1.%2").arg(i + 1).arg(j + 1));}}treeWidget.show();return a.exec();
}