效果如下:
图片随便找的,可能需要调下样式,代码复制可用,留给有需要的人。
#ifndef CustomTreeWidget_h__
#define CustomTreeWidget_h__
#include <QTreeWidget>
#include <QPushButton>
class CCustomTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
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();
}