该控件使用QScrollArea、QPushButton、QWidget和QVboxLayout模拟树结构。
/*
* 动态多级导航菜单
* 内置5种皮肤、两种折叠/展开图标、顶层节点分割线显隐的功能
* 1、 支持主文本颜色的三态设置
* 2、 支持便签文本颜色的三态设置
* 3、 支持折叠/展开图标颜色的三态设置
* 4、 支持背景颜色的三态设置
* 5、 支持便签背景颜色的三态设置
* 6、 支持导航线颜色的三态设置
* 7、 支持折叠/展开图标的动态效果
* 8、 支持节点切换时的导航线移动效果
* 9、 支持节点折叠时的动态伸缩效果
* 10、支持便签信息单独设置,默认只有父节点显示且显示孩子节点数量
*/
效果图
核心代码
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;
}