Qt动态多级导航菜单

该控件使用QScrollArea、QPushButton、QWidget和QVboxLayout模拟树结构。

/*
 * 动态多级导航菜单
 * 内置5种皮肤、两种折叠/展开图标、顶层节点分割线显隐的功能
 * 1、 支持主文本颜色的三态设置
 * 2、 支持便签文本颜色的三态设置
 * 3、 支持折叠/展开图标颜色的三态设置
 * 4、 支持背景颜色的三态设置
 * 5、 支持便签背景颜色的三态设置
 * 6、 支持导航线颜色的三态设置
 * 7、 支持折叠/展开图标的动态效果
 * 8、 支持节点切换时的导航线移动效果
 * 9、 支持节点折叠时的动态伸缩效果
 * 10、支持便签信息单独设置,默认只有父节点显示且显示孩子节点数量
 */

效果图

Qt动态多级导航菜单_第1张图片
Qt动态多级导航菜单_第2张图片

核心代码

1、ContentWidget绘制部分
该部分负责绘制右侧导航线条

void NavContentWidget::paintEvent(QPaintEvent *event)
{
	Q_UNUSED(event)

	QPainter painter(this);
	painter.setPen(Qt::NoPen);
	painter.setBrush(m_bgColor);
	painter.drawRect(rect());
	painter.setPen(QPen(m_navLineColor, LINE_WIDTH));
	int x = width() - 2;
	painter.drawLine(QPoint(x, m_offsetY), QPoint(x, m_offsetY + NavItem::HEIGHT));
}

2、Item绘制部分
该部分负责绘制Item显示信息,包括折叠/展开图标的旋转动画

void NavItem::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)

    int width = this->width();
    int height = this->height();
 
    QPainter painter(this);
	painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

	// 绘制背景
    painter.setPen((m_view->m_showTopLevelBorder && 0 == m_level) ? QPen(m_selected ? m_view->m_borderSelectedColor : (m_hover ? m_view->m_borderHoverColor : m_view->m_borderColor), m_view->m_borderWidth) : Qt::NoPen);
	painter.setBrush(m_selected ? m_view->m_bgSelectedColor : (m_hover ? m_view->m_bgHoverColor : m_view->m_bgColor));
	
	// 判断是否是第一个顶层节点
	QPoint p = m_view->m_contentWidget->mapFromGlobal(this->mapToGlobal(QPoint(0, 0)));
	bool topFirst = p.y() == 0;
    painter.drawRect(rect().adjusted(-m_view->m_borderWidth, topFirst ? -m_view->m_borderWidth : -m_view->m_borderWidth / 2, m_view->m_borderWidth, 0));

	int iconSize = m_view->m_showIcon ? m_view->m_iconSize : 10;

	// 绘制折叠标志
	if (m_view->m_branchType == QWHNavTreeWidget::TRIANGLE)
	{
		painter.save();
		int offsetY = 1;
		painter.translate(m_view->m_paddingLR + QWHNavTreeWidget::LEVEL_X * m_level + m_view->m_branchSize / 2, height / 2 - offsetY);
		painter.rotate(m_childNum > 0 ? m_angle : -90);
		QPainterPath path;
		path.moveTo(-m_view->m_branchSize / 2, -m_view->m_branchSize * 0.3);
		path.lineTo(m_view->m_branchSize / 2, -m_view->m_branchSize * 0.3);
		path.lineTo(0, m_view->m_branchSize * 0.6);
		path.closeSubpath();
		painter.setPen(Qt::NoPen);
		painter.setBrush(m_selected ? m_view->m_branchSelectedColor : (m_hover ? m_view->m_branchHoverColor : m_view->m_branchColor));
		painter.drawPath(path);
		painter.restore();
	}
	else// m_view->m_branchColor == QWHNavTreeWidget::CROSS
	{
		painter.save();
		painter.translate(m_view->m_paddingLR + QWHNavTreeWidget::LEVEL_X * m_level + m_view->m_branchSize / 2, height / 2);
		painter.rotate(m_childNum > 0 ? m_angle : -90);
		QPainterPath path;
		path.addRect(-m_view->m_branchSize / 2, -NavContentWidget::LINE_WIDTH / 2, m_view->m_branchSize, NavContentWidget::LINE_WIDTH);
		if (0 != m_angle)
			path.addRect(-NavContentWidget::LINE_WIDTH / 2, -m_view->m_branchSize / 2, NavContentWidget::LINE_WIDTH, m_view->m_branchSize);
		painter.setPen(Qt::NoPen);
		painter.setBrush(m_selected ? m_view->m_branchSelectedColor : (m_hover ? m_view->m_branchHoverColor : m_view->m_branchColor));
		painter.drawPath(path);
		painter.restore();
	}

    // 绘制文本
    int offsetX = 6;
    int textW = width - (m_view->m_paddingLR + m_view->m_branchSize + /*m_iconSize*/iconSize + offsetX);
    QRect textRect(m_view->m_paddingLR + m_view->m_branchSize + m_view->m_iconMarginLR * 2 + /*m_iconSize*/iconSize + QWHNavTreeWidget::LEVEL_X * m_level, 0, textW, height);
	QFont font = painter.font();
	font.setPixelSize(20);
	painter.setFont(font);
	painter.setPen(QPen(m_selected ? m_view->m_selectedColor : (m_hover ? m_view->m_hoverColor : m_view->m_color), 2));
    painter.drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, m_text);

	// 绘制便签信息
	if (!m_noteInfo.isEmpty() || m_childNum > 0)
	{
		QPainterPath pathNoteInfo;
		QRect noteInfoRect(width - 50, 10, 40, 20);
		pathNoteInfo.addRoundedRect(noteInfoRect, 10, 10);
		painter.fillPath(pathNoteInfo, m_view->m_noteInfoBgColor);
		
		QString noteInfo;
		if (m_noteInfo.length() > 0)
		{
			noteInfo = m_noteInfo;
			//如果便签信息长度超过3则将多余部分显示成省略号..
			if (noteInfo.length() > 3) {
				noteInfo = noteInfo.mid(0, 3) + "..";
			}
		}
		else
		{
			noteInfo = QString::number(m_childNum);
			//如果便签信息是数字则将超过999的数字部分显示成999+
			if (noteInfo.toInt() > 999) {
				noteInfo = "999+";
			}
		}
		font = painter.font();
		font.setPixelSize(12);
		painter.setFont(font);
		painter.setPen(QPen(m_view->m_noteInfoColor));
		painter.drawText(noteInfoRect, Qt::AlignCenter, noteInfo);
	}

	// 绘制图标
    if (m_view->m_showIcon)
    {
        QRect iconRect(m_view->m_paddingLR + m_view->m_branchSize + m_view->m_iconMarginLR + QWHNavTreeWidget::LEVEL_X * m_level, (height - m_view->m_iconSize) / 2, /*m_iconSize*/iconSize, /*m_iconSize*/iconSize);
        painter.drawPixmap(iconRect, m_icon);
    }
}

调用部分代码

#include "Widget.h"
#include "ui_Widget.h"
#include "QWHNavTreeView.h"

#pragma execution_character_set("UTF-8")

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

	// 顶层节点
	NavItem *item1 = ui->widget->addTopLevelItem("Item1");
	NavItem *item2 = ui->widget->addTopLevelItem("Item2");

	// 二级节点
	NavItem *item11 = ui->widget->addChildItem("Item1-1", item1);
	NavItem *item12 = ui->widget->addChildItem("Item1-2", item1);
	NavItem *item13 = ui->widget->addChildItem("Item1-3", item1);
	NavItem *item14 = ui->widget->addChildItem("Item1-4", item1);

	NavItem *item21 = ui->widget->addChildItem("Item2-1", item2);
	NavItem *item22 = ui->widget->addChildItem("Item2-2", item2);
	NavItem *item23 = ui->widget->addChildItem("Item2-3", item2);
	NavItem *item24 = ui->widget->addChildItem("Item2-4", item2);

	// 三级节点
	NavItem *item111 = ui->widget->addChildItem("Item1-1-1", item11);

	//item1->setIcon(QPixmap("D:/Practice/Qtlianxi/QtControl/QWHNavTreeView/release/Res/camera.png"));

	initView1();
	initView2();
	initView3();
	initView4();
	initView5();
}

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

void Widget::initView1()
{
	// 顶层节点
	NavItem *item1 = ui->widget1->addTopLevelItem("购物平台");
	NavItem *item2 = ui->widget1->addTopLevelItem("网页游戏");
	NavItem *item3 = ui->widget1->addTopLevelItem("军事信息");
	NavItem *item4 = ui->widget1->addTopLevelItem("生活服务");

	// 二级节点
	NavItem *item11 = ui->widget1->addChildItem("京东", item1);
	NavItem *item12 = ui->widget1->addChildItem("淘宝", item1);
	NavItem *item13 = ui->widget1->addChildItem("拼多多", item1);
	NavItem *item14 = ui->widget1->addChildItem("天猫超市", item1);
	NavItem *item15 = ui->widget1->addChildItem("唯品会", item1);

	NavItem *item21 = ui->widget1->addChildItem("英雄联盟", item2);
	NavItem *item22 = ui->widget1->addChildItem("王者荣耀", item2);
	NavItem *item23 = ui->widget1->addChildItem("凡人修真", item2);
	NavItem *item24 = ui->widget1->addChildItem("都市传奇", item2);

	NavItem *item31 = ui->widget1->addChildItem("腾讯军事", item3);
	NavItem *item32 = ui->widget1->addChildItem("军事前沿", item3);
	NavItem *item33 = ui->widget1->addChildItem("米尔军情", item3);
	NavItem *item34 = ui->widget1->addChildItem("环球热点", item3);

	NavItem *item41 = ui->widget1->addChildItem("BOSS直聘", item4);
	NavItem *item42 = ui->widget1->addChildItem("世纪佳缘", item4);
	NavItem *item43 = ui->widget1->addChildItem("中华英才网", item4);
	NavItem *item44 = ui->widget1->addChildItem("特价二手房", item4);

	ui->widget1->setBranchType(QWHNavTreeView::CROSS);
}

void Widget::initView2()
{
	// 顶层节点
	NavItem *item1 = ui->widget2->addTopLevelItem("购物平台");
	NavItem *item2 = ui->widget2->addTopLevelItem("网页游戏");
	NavItem *item3 = ui->widget2->addTopLevelItem("军事信息");
	NavItem *item4 = ui->widget2->addTopLevelItem("生活服务");

	// 二级节点
	NavItem *item11 = ui->widget2->addChildItem("京东", item1);
	NavItem *item12 = ui->widget2->addChildItem("淘宝", item1);
	NavItem *item13 = ui->widget2->addChildItem("拼多多", item1);
	NavItem *item14 = ui->widget2->addChildItem("天猫超市", item1);
	NavItem *item15 = ui->widget2->addChildItem("唯品会", item1);

	NavItem *item21 = ui->widget2->addChildItem("英雄联盟", item2);
	NavItem *item22 = ui->widget2->addChildItem("王者荣耀", item2);
	NavItem *item23 = ui->widget2->addChildItem("凡人修真", item2);
	NavItem *item24 = ui->widget2->addChildItem("都市传奇", item2);

	NavItem *item31 = ui->widget2->addChildItem("腾讯军事", item3);
	NavItem *item32 = ui->widget2->addChildItem("军事前沿", item3);
	NavItem *item33 = ui->widget2->addChildItem("米尔军情", item3);
	NavItem *item34 = ui->widget2->addChildItem("环球热点", item3);

	NavItem *item41 = ui->widget2->addChildItem("BOSS直聘", item4);
	NavItem *item42 = ui->widget2->addChildItem("世纪佳缘", item4);
	NavItem *item43 = ui->widget2->addChildItem("中华英才网", item4);
	NavItem *item44 = ui->widget2->addChildItem("特价二手房", item4);

	ui->widget2->setThemeType(QWHNavTreeView::ThemeWhite);
}

void Widget::initView3()
{
	// 顶层节点
	NavItem *item1 = ui->widget3->addTopLevelItem("购物平台");
	NavItem *item2 = ui->widget3->addTopLevelItem("网页游戏");
	NavItem *item3 = ui->widget3->addTopLevelItem("军事信息");
	NavItem *item4 = ui->widget3->addTopLevelItem("生活服务");

	// 二级节点
	NavItem *item11 = ui->widget3->addChildItem("京东", item1);
	NavItem *item12 = ui->widget3->addChildItem("淘宝", item1);
	NavItem *item13 = ui->widget3->addChildItem("拼多多", item1);
	NavItem *item14 = ui->widget3->addChildItem("天猫超市", item1);
	NavItem *item15 = ui->widget3->addChildItem("唯品会", item1);

	NavItem *item21 = ui->widget3->addChildItem("英雄联盟", item2);
	NavItem *item22 = ui->widget3->addChildItem("王者荣耀", item2);
	NavItem *item23 = ui->widget3->addChildItem("凡人修真", item2);
	NavItem *item24 = ui->widget3->addChildItem("都市传奇", item2);

	NavItem *item31 = ui->widget3->addChildItem("腾讯军事", item3);
	NavItem *item32 = ui->widget3->addChildItem("军事前沿", item3);
	NavItem *item33 = ui->widget3->addChildItem("米尔军情", item3);
	NavItem *item34 = ui->widget3->addChildItem("环球热点", item3);

	NavItem *item41 = ui->widget3->addChildItem("BOSS直聘", item4);
	NavItem *item42 = ui->widget3->addChildItem("世纪佳缘", item4);
	NavItem *item43 = ui->widget3->addChildItem("中华英才网", item4);
	NavItem *item44 = ui->widget3->addChildItem("特价二手房", item4);

	ui->widget3->setThemeType(QWHNavTreeView::ThemeBlueLight);
}

void Widget::initView4()
{
	// 顶层节点
	NavItem *item1 = ui->widget4->addTopLevelItem("购物平台");
	NavItem *item2 = ui->widget4->addTopLevelItem("网页游戏");
	NavItem *item3 = ui->widget4->addTopLevelItem("军事信息");
	NavItem *item4 = ui->widget4->addTopLevelItem("生活服务");

	// 二级节点
	NavItem *item11 = ui->widget4->addChildItem("京东", item1);
	NavItem *item12 = ui->widget4->addChildItem("淘宝", item1);
	NavItem *item13 = ui->widget4->addChildItem("拼多多", item1);
	NavItem *item14 = ui->widget4->addChildItem("天猫超市", item1);
	NavItem *item15 = ui->widget4->addChildItem("唯品会", item1);

	NavItem *item21 = ui->widget4->addChildItem("英雄联盟", item2);
	NavItem *item22 = ui->widget4->addChildItem("王者荣耀", item2);
	NavItem *item23 = ui->widget4->addChildItem("凡人修真", item2);
	NavItem *item24 = ui->widget4->addChildItem("都市传奇", item2);

	NavItem *item31 = ui->widget4->addChildItem("腾讯军事", item3);
	NavItem *item32 = ui->widget4->addChildItem("军事前沿", item3);
	NavItem *item33 = ui->widget4->addChildItem("米尔军情", item3);
	NavItem *item34 = ui->widget4->addChildItem("环球热点", item3);

	NavItem *item41 = ui->widget4->addChildItem("BOSS直聘", item4);
	NavItem *item42 = ui->widget4->addChildItem("世纪佳缘", item4);
	NavItem *item43 = ui->widget4->addChildItem("中华英才网", item4);
	NavItem *item44 = ui->widget4->addChildItem("特价二手房", item4);

	ui->widget4->setThemeType(QWHNavTreeView::ThemeGrayLight);
}

void Widget::initView5()
{
	// 顶层节点
	NavItem *item1 = ui->widget5->addTopLevelItem("购物平台");
	NavItem *item2 = ui->widget5->addTopLevelItem("网页游戏");
	NavItem *item3 = ui->widget5->addTopLevelItem("军事信息");
	NavItem *item4 = ui->widget5->addTopLevelItem("生活服务");

	// 二级节点
	NavItem *item11 = ui->widget5->addChildItem("京东", item1);
	NavItem *item12 = ui->widget5->addChildItem("淘宝", item1);
	NavItem *item13 = ui->widget5->addChildItem("拼多多", item1);
	NavItem *item14 = ui->widget5->addChildItem("天猫超市", item1);
	NavItem *item15 = ui->widget5->addChildItem("唯品会", item1);

	NavItem *item21 = ui->widget5->addChildItem("英雄联盟", item2);
	NavItem *item22 = ui->widget5->addChildItem("王者荣耀", item2);
	NavItem *item23 = ui->widget5->addChildItem("凡人修真", item2);
	NavItem *item24 = ui->widget5->addChildItem("都市传奇", item2);

	NavItem *item31 = ui->widget5->addChildItem("腾讯军事", item3);
	NavItem *item32 = ui->widget5->addChildItem("军事前沿", item3);
	NavItem *item33 = ui->widget5->addChildItem("米尔军情", item3);
	NavItem *item34 = ui->widget5->addChildItem("环球热点", item3);

	NavItem *item41 = ui->widget5->addChildItem("BOSS直聘", item4);
	NavItem *item42 = ui->widget5->addChildItem("世纪佳缘", item4);
	NavItem *item43 = ui->widget5->addChildItem("中华英才网", item4);
	NavItem *item44 = ui->widget5->addChildItem("特价二手房", item4);

	item1->m_noteInfo = "Hot";
	item2->m_noteInfo = "热血";
	item3->m_noteInfo = "Nervous";
	item4->m_noteInfo = "优惠";

	ui->widget5->setBranchType(QWHNavTreeView::CROSS);
	ui->widget5->setThemeType(QWHNavTreeView::ThemeOrange);
}

void Widget::on_btn_clicked()
{
	static bool visible = false;
	ui->widget->setTopItemBorderVisible(visible);
	visible = !visible;
}

void Widget::on_btn1_clicked()
{
	static bool visible = false;
	ui->widget1->setTopItemBorderVisible(visible);
	visible = !visible;
}

void Widget::on_btn2_clicked()
{
	static bool visible = false;
	ui->widget2->setTopItemBorderVisible(visible);
	visible = !visible;
}

void Widget::on_btn3_clicked()
{
	static bool visible = false;
	ui->widget3->setTopItemBorderVisible(visible);
	visible = !visible;
}

void Widget::on_btn4_clicked()
{
	static bool visible = false;
	ui->widget4->setTopItemBorderVisible(visible);
	visible = !visible;
}

void Widget::on_btn5_clicked()
{
	static bool visible = false;
	ui->widget5->setTopItemBorderVisible(visible);
	visible = !visible;
}

你可能感兴趣的:(自定义控件,qt,开发语言)