Qt 实现 QQ 9.0版 自定义菜单控件

#简述
重新最近开始了QQ最新版 9.0 界面的模仿,前几天搞了一个QQ登陆界面的动画效果详情见 QQ 9.0 新版登录窗口登录特效 ,今晚Qt技术学习班分享了QQ 9.0版本的自定义菜单控件,通过QListWidget 来承载 每个菜单项,通过简单的组合完成菜单项控件,主要如下:

1、自动设置菜单高度,根据当前添加的菜单项自增菜单高度;
2、菜单宽度可根据菜单文字自行设置;
3、菜单的透明度、菜单边框阴影宽度、菜单四角圆角弧度均可设置;
4、其他菜单等样式,菜单项文字颜色,图标等均可自行设置;

这里写图片描述


代码之路

QQCustomMenu.h

/**
* @author : 前行中的小猪;
* @date   : 2018年05月18日
* @version: 1.0
* @note   : 模仿QQ菜单样式,实现自定义菜单,通过ListWidget加载菜单项;
			1、自动设置菜单高度,根据当前添加的菜单项自增菜单高度;
			2、菜单宽度可根据菜单文字自行设置;
			3、菜单的透明度、菜单边框阴影宽度、菜单四角圆角弧度均可设置;
			4、其他菜单等样式,菜单项文字颜色,图标等均可自行设置,
			   这里只给出一个模板,大家可自行发挥拓展;

*@ 博客地址: https://blog.csdn.net/GoForwardToStep ;
**/


#include <QtWidgets/QWidget>
#include <QLabel>
#include <QHBoxLayout>
#include <QIcon>
#include <QPainter>
#include <QListWidget>

#define MENU_ITEM_HEIGHT 30			// 菜单项的高度;

class QQMenuItemWidget : public QWidget
{
	Q_OBJECT

public:
	QQMenuItemWidget()
		: m_isEnter(false)
		, m_itemIndex(0)
	{
		m_iconLabel = new QLabel;
		m_iconLabel->setFixedSize(QSize(20, 20));

		m_textLabel = new QLabel;
		m_textLabel->setStyleSheet("font-family:Microsoft YaHei;font-size:14px;");

		QHBoxLayout* hLayout = new QHBoxLayout(this);
		hLayout->addWidget(m_iconLabel);
		hLayout->addWidget(m_textLabel);
		hLayout->addStretch();
		hLayout->setSpacing(10);
		hLayout->setContentsMargins(10, 0, 0, 0);

		this->setFixedHeight(MENU_ITEM_HEIGHT);
		this->setAttribute(Qt::WA_TranslucentBackground);
	}

	// 设置菜单项文字;
	void setMenuItemText(const QString& text)
	{
		m_textLabel->setText(text);
		m_textLabel->setScaledContents(true);
	}
	
	// 设置菜单项图标;
	void setMenuItemIcon(const QIcon& icon)
	{
		m_iconLabel->setPixmap(icon.pixmap(QSize(20, 20)));
	}

	// 设置菜单项Index;
	void SetMenuItemIndex(const int& index)
	{
		m_itemIndex = index;
	}

private:
	void mouseReleaseEvent(QMouseEvent *event)
	{
		emit signalMenuClicked(m_itemIndex);
	}

	void enterEvent(QEvent *event)
	{
		m_isEnter = true;
		update();
	}

	void leaveEvent(QEvent *event)
	{
		m_isEnter = false;
		update();
	}

	void paintEvent(QPaintEvent *event)
	{
		if (m_isEnter)
		{
			QPainter painter(this);
			painter.fillRect(this->rect(), QColor(215, 215, 215, 150));
		}
	}

signals:
	// 通知菜单项被点击;
	void signalMenuClicked(int);

private:
	QLabel* m_iconLabel;
	QLabel* m_textLabel;
	int m_itemIndex;
	bool m_isEnter;
};

class QQCustomMenu : public QWidget
{
	Q_OBJECT

public:
	QQCustomMenu(QWidget *parent = Q_NULLPTR);
	// 添加菜单项;
	void addMenuItem(const QIcon &icon, const QString &text);
	// 添加分隔项;
	void addSeparator();

private:
	void initWidget();
	void paintEvent(QPaintEvent *event);

private slots:
	// 根据Index判断当前点击了那个菜单项;
	void onMenuItemClicked(int menuItemIndex);

private:
	QListWidget* m_menuItemListWidget;
	// 根据当前添加的菜单项和分隔项自动计算菜单高度;
	int m_currentMenuHeight;
	// 记录菜单项Index;
	int m_menuItemIndex;
};

QQCustomMenu.cpp

#include "QQCustomMenu.h"
#include <QPainter>
#include <QGraphicsDropShadowEffect>

#define SHADOW_WIDTH 15					// 窗口阴影宽度;
#define WINDOW_WIDTH 180				// 菜单宽度;
#define SEPARATOR_HEIGHT 12				// 分割线高度;
#define VMARGIN 10						// QListWidget纵向的边距;
#define BORDER_RADIUS 5					// 菜单四角圆角的弧度;
#define WINDPW_OPACITY 0.9				// 菜单背景透明度;

QQCustomMenu::QQCustomMenu(QWidget *parent)
	: QWidget(parent)
	, m_menuItemIndex(0)
{
	initWidget();

	// 设置窗户属性;
	setWindowFlags(Qt::FramelessWindowHint/* | Qt::Tool*/);		// 正式应用时要加上 Qt::Tool;
	setAttribute(Qt::WA_TranslucentBackground);
	setWindowOpacity(WINDPW_OPACITY);
	setFixedWidth(WINDOW_WIDTH);
	setStyleSheet("QListView{border:none} QListView::item{background:transparent;}");

	// 设置阴影边框;
	auto shadowEffect = new QGraphicsDropShadowEffect(this);
	shadowEffect->setOffset(0, 0);
	shadowEffect->setColor(Qt::gray);
	shadowEffect->setBlurRadius(SHADOW_WIDTH);
	this->setGraphicsEffect(shadowEffect);

	// 计算默认高度;
	m_currentMenuHeight = SHADOW_WIDTH * 2 + VMARGIN * 2;
	this->setFixedHeight(m_currentMenuHeight);
}

void QQCustomMenu::initWidget()
{
	m_menuItemListWidget = new QListWidget;
	m_menuItemListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	m_menuItemListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

	QHBoxLayout* hMainLayout = new QHBoxLayout(this);
	hMainLayout->addWidget(m_menuItemListWidget);
	hMainLayout->setContentsMargins(SHADOW_WIDTH, SHADOW_WIDTH + VMARGIN, SHADOW_WIDTH, SHADOW_WIDTH + VMARGIN);
}

void QQCustomMenu::addMenuItem(const QIcon &icon, const QString &text)
{
	QQMenuItemWidget* menuItem = new QQMenuItemWidget;
	menuItem->setMenuItemIcon(icon);
	menuItem->setMenuItemText(text);
	connect(menuItem, &QQMenuItemWidget::signalMenuClicked, this, &QQCustomMenu::onMenuItemClicked);

	QListWidgetItem* item = new QListWidgetItem;
	item->setSizeHint(QSize(WINDOW_WIDTH - 2 * SHADOW_WIDTH, MENU_ITEM_HEIGHT));
	m_menuItemListWidget->addItem(item);
	m_menuItemListWidget->setItemWidget(item, menuItem);

	m_menuItemIndex++;
	m_currentMenuHeight += MENU_ITEM_HEIGHT;
	this->setFixedHeight(m_currentMenuHeight);
}

void QQCustomMenu::addSeparator()
{
	QFrame* separatorLabel = new QFrame;
	separatorLabel->setFrameShape(QFrame::HLine);
	separatorLabel->setLineWidth(1);
	separatorLabel->setFrameShadow(QFrame::Sunken);
	// 设置分隔线颜色;
	QPalette palette = separatorLabel->palette();
	palette.setColor(QPalette::Dark, QColor(219, 219, 219, 150));
	separatorLabel->setPalette(palette);

	QListWidgetItem* item = new QListWidgetItem;
	item->setSizeHint(QSize(WINDOW_WIDTH - 2 * SHADOW_WIDTH, SEPARATOR_HEIGHT));
	m_menuItemListWidget->addItem(item);
	m_menuItemListWidget->setItemWidget(item, separatorLabel);

	m_currentMenuHeight += SEPARATOR_HEIGHT;
	this->setFixedHeight(m_currentMenuHeight);
}

void QQCustomMenu::paintEvent(QPaintEvent *event)
{
	QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing, true);
	painter.setPen(Qt::NoPen);
	painter.setBrush(Qt::white);
	painter.drawRoundedRect(QRect(SHADOW_WIDTH, SHADOW_WIDTH, \
		width() - SHADOW_WIDTH * 2, height() - SHADOW_WIDTH * 2), BORDER_RADIUS, BORDER_RADIUS);
}

void QQCustomMenu::onMenuItemClicked(int menuItemIndex)
{
	// 根据点击不同的菜单项进行不同操作,这里应发出信号去通知调用菜单的widget;
}

测试代码

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	QQCustomMenu w;
	w.addMenuItem(QIcon(":/Resources/busy.png"), "忙碌");
	w.addMenuItem(QIcon(":/Resources/hide.png"), "隐身");
	w.addMenuItem(QIcon(":/Resources/lock.png"), "锁定");
	w.addSeparator();
	w.addMenuItem(QIcon(":/Resources/offLine.png"), "离线");
	w.addMenuItem(QIcon(":/Resources/message.png"), "消息");
	w.show();
	return a.exec();
}
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页