Qt自定义TreeWidget,实现展开折叠按钮在右侧,且一条竖直线上对齐

效果如下:

图片随便找的,可能需要调下样式,代码复制可用,留给有需要的人。

cpp 复制代码
#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__
cpp 复制代码
#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"));
	}
}

调用代码:

cpp 复制代码
#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();
}
相关推荐
WG_173 分钟前
C++多态
开发语言·c++·面试
Charles Ray2 小时前
C++学习笔记 —— 内存分配 new
c++·笔记·学习
重生之我在20年代敲代码2 小时前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记
铁匠匠匠6 小时前
从零开始学数据结构系列之第六章《排序简介》
c语言·数据结构·经验分享·笔记·学习·开源·课程设计
迷迭所归处7 小时前
C++ —— 关于vector
开发语言·c++·算法
CV工程师小林8 小时前
【算法】BFS 系列之边权为 1 的最短路问题
数据结构·c++·算法·leetcode·宽度优先
white__ice9 小时前
2024.9.19
c++
天玑y9 小时前
算法设计与分析(背包问题
c++·经验分享·笔记·学习·算法·leetcode·蓝桥杯
锦亦之22339 小时前
QT+OSG+OSG-earth如何在窗口显示一个地球
开发语言·qt
姜太公钓鲸2339 小时前
c++ static(详解)
开发语言·c++