一如既往,先上图
动画效果参考下面的是视频
20220316-182247
摘要描述:
1、支持嵌入面板和弹出按钮两种,目前仅完成嵌入面板,后期会实现弹出实现
2、支持非常多的自定义功能,有关自定义功能定义参考下面的风格数据结构体代码,结构体中的功能均支持自定义
风格数据结构体代码如下:
/// 圆形菜单控件风格样式数据结构
///
typedef struct LNCFROUNDMENU_STYLE_{
QList<DGRADIENTCOLOR_DATA> vMenuCtlBkgClr; //菜单渐变背景颜色集合列表
QColor cMenuCtlBkgClr = QColor(88,88,88,44); //菜单控件背景颜色
QColor cMenuBtnBkgClr = QColor(77,77,77,44); //菜单按钮默认背景色
QColor cMenuBtnHovers = QColor(192,192,192,144);//菜单按钮悬停背景色
QColor cMenuBtnTxtClr = QColor(202,202,202); //菜单按钮文字颜色
QColor cMenuTextHover = QColor(255,255,255); //菜单按钮文字悬停颜色
QColor cSwicthBnColor = QColor(144,133,227); //切换按钮默认颜色
QColor cSwicthExpands = QColor(250,142,85); //切换按钮菜单展开时背景颜色
QColor cSwicthBnHover = QColor(250,85,101); //切换按钮鼠标悬停颜色
QColor cBkgBorderClrs = QColor(11,11,21,99); //背景边框颜色
QColor cItemBorderClr = QColor(11,11,21,99); //菜单项边框颜色
qreal dFrameOutScale = 0.75; //菜单外边框距离窗口边界比例
qreal dFrameInnScale = 0.40; //菜单内边框距离窗口边界比例
qreal dCenterBnScale = 1.5; //菜单中心按钮大小比例,相对于菜单窗口大小的缩小比例
qreal dItemsBtnScale = 2.0; //菜单按钮图标大小比例,相对于菜单按钮扇形区域大小缩放比例
qreal dItemsBkgScale = 1.25; //菜单按钮圆形背景大小比例,相对于菜单按钮扇形区域大小缩放比例
qreal dItemBorderWid = 1; //菜单按钮边框宽度
qreal dBnRoundRadius = 0.5; //菜单按钮圆角比例,0-0.5之间
qreal dBkgBordersWid = 1; //菜单背景宽度
qreal dStartDrawAngle = 180.0; //起始绘制角度
uint uCenterBtnSize = 64; //菜单中心按钮绝对大小尺寸
uint uAnimatedTimer = 400; //菜单展开和关闭动画周期时长
bool bShowCtrlBkgnd = false; //是否显示控件背景
bool bShowItemsLine = false; //是否绘制分割线
bool bRoundMenuItem = true; //是否绘制圆形菜单项,默认是,否则显示扇形
bool bDrawCenterBtn = true; //绘制中心按钮
bool bMenuBtnsScale = true; //中心按钮大小比例类型,true表示绝对比例,false绝对大小
bool bHasRandomClrs = false; //是否使用随机按钮背景颜色
bool bRotateAnimate = true; //是否启用旋转动画,如果使用旋转动画至少将菜单展开和关闭周期设置到400或以上
bool bShowBkgBorder = false; //是否显示背景边框
bool bHasItemBorder = false; //是否显示菜单按钮边框
bool bNoIcoFullName = true; //无图标按钮是否显示菜单全名
bool operator == (const LNCFROUNDMENU_STYLE_& rhs) // == 操作运算符重载
{
if(vMenuCtlBkgClr.count()!= rhs.vMenuCtlBkgClr.count())
return false;
if(vMenuCtlBkgClr.count()== rhs.vMenuCtlBkgClr.count()&&rhs.vMenuCtlBkgClr.count()>0){
for(int i=0;i<rhs.vMenuCtlBkgClr.count();i++)
{
if(QString("%1").arg(vMenuCtlBkgClr[i].fBkgndAngleVal) !=QString("%1").arg(rhs.vMenuCtlBkgClr[i].fBkgndAngleVal))
return false;
if(vMenuCtlBkgClr[i].cBkgndAngleClr != rhs.vMenuCtlBkgClr[i].cBkgndAngleClr)
return false;
}
}
return (cMenuCtlBkgClr == rhs.cMenuCtlBkgClr)
&& (cMenuBtnBkgClr == rhs.cMenuBtnBkgClr)
&& (cMenuBtnHovers == rhs.cMenuBtnHovers)
&& (cMenuBtnTxtClr == rhs.cMenuBtnTxtClr)
&& (bHasRandomClrs == rhs.bHasRandomClrs)
&& (cMenuTextHover == rhs.cMenuTextHover)
&& (cSwicthBnColor == rhs.cSwicthBnColor)
&& (cSwicthExpands == rhs.cSwicthExpands)
&& (cSwicthBnHover == rhs.cSwicthBnHover)
&& (QString("%1").arg(dFrameOutScale) == QString("%1").arg(rhs.dFrameOutScale))
&& (QString("%1").arg(dFrameInnScale) == QString("%1").arg(rhs.dFrameInnScale))
&& (QString("%1").arg(dCenterBnScale) == QString("%1").arg(rhs.dCenterBnScale))
&& (QString("%1").arg(dItemsBtnScale) == QString("%1").arg(rhs.dItemsBtnScale))
&& (QString("%1").arg(dItemsBkgScale) == QString("%1").arg(rhs.dItemsBkgScale))
&& (QString("%1").arg(dStartDrawAngle) == QString("%1").arg(rhs.dStartDrawAngle))
&& (uCenterBtnSize == rhs.uCenterBtnSize)
&& (uAnimatedTimer == rhs.uAnimatedTimer)
&& (bShowCtrlBkgnd == rhs.bShowCtrlBkgnd)
&& (bShowItemsLine == rhs.bShowItemsLine)
&& (bRoundMenuItem == rhs.bRoundMenuItem)
&& (bDrawCenterBtn == rhs.bDrawCenterBtn)
&& (bMenuBtnsScale == rhs.bMenuBtnsScale)
&& (bHasRandomClrs == rhs.bHasRandomClrs)
&& (bRotateAnimate == rhs.bRotateAnimate)
&& (cBkgBorderClrs == rhs.cBkgBorderClrs)
&& (bShowBkgBorder == rhs.bShowBkgBorder)
&& (cItemBorderClr == rhs.cItemBorderClr)
&& (QString("%1").arg(dItemBorderWid) == QString("%1").arg(rhs.dItemBorderWid))
&& (bHasItemBorder == rhs.bHasItemBorder)
&& (dBnRoundRadius == rhs.dBnRoundRadius)
&& (QString("%1").arg(dBkgBordersWid) == QString("%1").arg(rhs.dBkgBordersWid))
&& (bNoIcoFullName == rhs.bNoIcoFullName); //无图标按钮是否显示菜单全名
}
bool operator != (const LNCFROUNDMENU_STYLE_& rhs) // != 操作运算符重载
{
return !(*this == rhs);
}
}LNCFROUNDMENU_STYLE,*PLNCFROUNDMENU_STYLE;
除了风格自定义还支持自定义图标等功能,参考下面的变量
///菜单展开图标路径
std::u16string sExpandImgPath;
///菜单闭合图标路径
std::u16string sClosedImgPath;
头文件主要代码
//默认构造函数
explicit Lncf_QRoundMenuCtl(QWidget *parent = nullptr);
/// 标准构造函数
/// \brief Lncf_QRoundMenuCtl
/// \param bPopup :是否为popup模式
/// \param uBtnSize :中心按钮尺寸
/// \param uItemType :菜单项类型,0圆角矩形,1扇形
/// \param parent :父窗口句柄
///
explicit Lncf_QRoundMenuCtl(bool bPopup,uint uBtnSize,uint uItemType = 0,QWidget *parent = nullptr);
//标准析构
~Lncf_QRoundMenuCtl();
private:
/// 初始化圆形菜单控件
/// \brief InitRoundMenuCtl
///
void InitRoundMenuCtl();
protected:
// 重写系统事件
bool event(QEvent *ev) override;
// 重写系统绘制事件
void paintEvent(QPaintEvent *event) override;
/// 绘制控件圆形背景
/// \brief DrawBackgroundCtl
/// \param painter
///
void DrawBackgroundCtl(QPainter *painter);
/// 绘制菜单项圆形外边框
/// \brief DrawItemOutBorder
/// \param painter
///
void DrawItemOutBorder(QPainter *painter);
/// 绘制菜单项圆形内边框
/// \brief DrawItemsInBorder
/// \param painter
///
void DrawItemsInBorder(QPainter *painter);
/// 绘制菜单中心按钮
/// \brief DrawSwitchsButton
/// \param painter
///
void DrawSwitchsButton(QPainter *painter);
/// 绘制菜单项按钮
/// \brief DrawMenuCtlButton
/// \param painter
///
void DrawMenuCtlButton(QPainter *painter);
// 重写系统鼠标松开事件
void mouseReleaseEvent(QMouseEvent *event) override;
private:
/// 初始化菜单动画
/// \brief InitMenuAnimation
///
void InitMenuAnimation();
/// 获取菜单项外圆半径
/// \brief GetItemsOutRadius
/// \return
///
qreal GetItemsOutRadius();
/// 获取菜单项内圆半径
/// \brief GetItemsInnRadius
/// \return
///
qreal GetItemsInnRadius();
/// 获取菜单项目绘制开始角度
/// \brief GetItemStartAngle
/// \return
///
qreal GetItemStartAngle();
/// 获取菜单控件绘制弧度
/// \brief GetFrmStartRadian
/// \return
///
qreal GetFrmStartRadian();
/// 获取菜单切换按钮菜单绘图路径
/// \brief GetSwitchBtnsPath
/// \return
///
QPainterPath GetSwitchBtnsPath();
/// 获取菜单按钮绘图路径
/// \brief GetMenuButtonPath
/// \param index
/// \param hover
/// \return
///
QPainterPath GetMenuButtonPath(int index, bool hover = false);
private slots:
/// 切换菜单按钮单击
/// \brief MenuSwitchClicked
///
void MenuSwitchClicked();
/// 菜单项展开或收起
/// \brief MenuItemBtnExpand
/// \param bExpand
///
void MenuItemBtnExpand(const bool& bExpand);
/// 菜单项单击槽函数
/// \brief MenuItemsBtnClick
/// \param uIndex
///
void MenuItemsBtnClick(const uint& uIndex);
public:
/// 添加菜单按钮项
/// \brief AddMenuButtonItem
/// \param tMenuItem
/// 注意在插入和添加菜单时图标和title必须提供一个
///
void AddMenuButtonItem(const LQROUNDMENUBTN_ITEM& tMenuItem);
void AddMenuButtonItem(const QPixmap &icon, const QString &title, const QString &tooltip);
void AddMenuButtonItem(const QPixmap &icon, const QString &title, const QString &tooltip,
const QColor &background);
void AddMenuButtonItem(const QPixmap &icon, const std::u16string &title, const std::u16string &tooltip,
const QColor &background);
/// 插入一个菜单按钮
/// \brief InsertMenuBtnItem
/// \param index
/// \param tMenuItem
///
void InsertMenuBtnItem(int index,LQROUNDMENUBTN_ITEM tMenuItem);
/// 插入一个按钮项目
/// \brief InsertMenuBtnItem
/// \param index
/// \param icon
/// \param title
/// \param tooltip
/// \param background
///
void InsertMenuBtnItem(int index, const QPixmap &icon, const QString &title, const QString &tooltip,
const QColor &background = QColor());
void InsertMenuBtnItem(int index, const QPixmap &icon, const std::u16string &title, const std::u16string &tooltip,
const QColor &background = QColor());
/// 获取菜单展开图标路径
/// \brief GetExpandImgPath
/// \return
///
std::u16string GetExpandImgPath() const;
/// 获取菜单闭合图标路径
/// \brief GetClosedImgPath
/// \return
///
std::u16string GetClosedImgPath() const;
/// 设置菜单展开图标路径
/// \brief SetExpandImgPath
/// \param sImage
///
void SetExpandImgPath(const std::u16string& sImage);
/// 设置菜单闭合图标路径
/// \brief SetClosedImgPath
/// \param sImage
///
void SetClosedImgPath(const std::u16string& sImage);
核心绘图代码如下
// 重写系统绘制事件
void Lncf_QRoundMenuCtl::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
//绘制背景
if(this->tMenuCtrlStyles.bShowCtrlBkgnd)
DrawBackgroundCtl(&painter);
//绘制菜单按钮
DrawMenuCtlButton(&painter);
//绘制中心按钮
if(this->tMenuCtrlStyles.bDrawCenterBtn)
DrawSwitchsButton(&painter);
}
/// 绘制控件圆形背景
/// \brief DrawBackgroundCtl
/// \param painter
///
void Lncf_QRoundMenuCtl::DrawBackgroundCtl(QPainter *painter)
{
painter->setPen(this->tMenuCtrlStyles.bShowBkgBorder?QPen(tMenuCtrlStyles.cBkgBorderClrs,tMenuCtrlStyles.dBkgBordersWid):Qt::NoPen);
if(this->tMenuCtrlStyles.vMenuCtlBkgClr.count()>0){
QLinearGradient gradient(QPoint(0, 0), QPoint(width(), height()));
for(int i=0;i<tMenuCtrlStyles.vMenuCtlBkgClr.count();i++)
gradient.setColorAt(tMenuCtrlStyles.vMenuCtlBkgClr[i].fBkgndAngleVal, tMenuCtrlStyles.vMenuCtlBkgClr[i].cBkgndAngleClr);
painter->setBrush(gradient);
}
else{
painter->setBrush(this->tMenuCtrlStyles.cMenuCtlBkgClr);
}
QRectF rect = this->rect();
uint uRectMinVal = qMin(this->width(),this->height());
rect.setSize(QSize(uRectMinVal*tMenuCtrlStyles.dFrameOutScale,uRectMinVal*tMenuCtrlStyles.dFrameOutScale));
rect.moveCenter(QPointF(width() / 2.0, height() / 2.0));
painter->drawEllipse(rect);
}
/// 绘制菜单项圆形外边框
/// \brief DrawItemOutBorder
/// \param painter
///
void Lncf_QRoundMenuCtl::DrawItemOutBorder(QPainter *painter)
{
QRectF rect(0.0, 0.0, 2.0 * GetItemsOutRadius(), 2.0 * GetItemsOutRadius());
rect.moveCenter(QPointF(width() / 2.0, height() / 2.0));
painter->save();
painter->setPen(QPen(QBrush(this->tMenuCtrlStyles.cItemBorderClr), tMenuCtrlStyles.dItemBorderWid));
painter->drawEllipse(rect);
painter->restore();
}
/// 绘制菜单项圆形内边框
/// \brief DrawItemsInBorder
/// \param painter
///
void Lncf_QRoundMenuCtl::DrawItemsInBorder(QPainter *painter)
{
QRectF rect(0.0, 0.0, 2.0 * GetItemsInnRadius(), 2.0 * GetItemsInnRadius());
rect.moveCenter(QPointF(width() / 2.0, height() / 2.0));
painter->save();
painter->setPen(QPen(QBrush(this->tMenuCtrlStyles.cItemBorderClr), tMenuCtrlStyles.dItemBorderWid));
painter->drawEllipse(rect);
painter->restore();
}
int imageRotate =0;
/// 绘制菜单中心按钮
/// \brief DrawSwitchsButton
/// \param painter
///
void Lncf_QRoundMenuCtl::DrawSwitchsButton(QPainter *painter)
{
auto path = GetSwitchBtnsPath();
painter->setBrush(bSwitchBnHover?tMenuCtrlStyles.cSwicthBnHover:(bHasExpandMenu ? tMenuCtrlStyles.cSwicthExpands : tMenuCtrlStyles.cSwicthBnColor));
painter->setPen(tMenuCtrlStyles.bHasItemBorder?QPen(tMenuCtrlStyles.cItemBorderClr,tMenuCtrlStyles.dItemBorderWid):Qt::NoPen);
painter->drawPath(path);
auto pixmap = bHasExpandMenu ? QPixmap(QString::fromStdU16String(sClosedImgPath)) :
QPixmap(QString::fromStdU16String(sExpandImgPath));
auto rect = path.boundingRect();
if(!pixmap.isNull()){
rect = rect.marginsRemoved(QMargins(rect.width() / 4.0,
rect.height() / 4.0,
rect.width() / 4.0,
rect.height() / 4.0));
painter->drawPixmap(rect, pixmap, pixmap.rect());
}
else{
rect = rect.marginsRemoved(QMargins(rect.width() / 4.0,
rect.height() / 4.0,
rect.width() / 4.0,
rect.height() / 4.0));
std::u16string sSwicthText = bHasExpandMenu ? u"MENU":
u"CLOSE";
QFont fontObj = this->font();
fontObj.setPointSizeF(rect.height()/4.0);
painter->setFont(fontObj);
painter->setPen(bSwitchBnHover?tMenuCtrlStyles.cMenuBtnTxtClr:tMenuCtrlStyles.cMenuTextHover);
painter->drawText(rect, Qt::AlignCenter|Qt::TextWordWrap,QString::fromStdU16String(sSwicthText));
}
}
/// 绘制菜单项按钮
/// \brief DrawMenuCtlButton
/// \param painter
///
void Lncf_QRoundMenuCtl::DrawMenuCtlButton(QPainter *painter)
{
if(pMenuAnimeGroup->state() != QAbstractAnimation::Running){
if(!bHasExpandMenu||vMenuButtonList.count()<=0){
return;
}
}
if(this->tMenuCtrlStyles.bShowItemsLine&&!this->tMenuCtrlStyles.bRoundMenuItem){
DrawItemOutBorder(painter);
DrawItemsInBorder(painter);
}
auto count = vMenuButtonList.count();
if(count == 0) return;
auto radian = 2 * PI / count;
auto outer_radius = GetItemsOutRadius();
auto inner_radius = GetItemsInnRadius();
QFont fontObj = this->font();
for (int i = 0 ; i< count; ++i)
{
auto path = GetMenuButtonPath(i);
//绘制背景
auto bgColor = this->tMenuCtrlStyles.bHasRandomClrs?QColor::fromHsl(rand()%360,rand()%256,rand()%200):vMenuButtonList.at(i)->cCustomBkgClr;
if(!bgColor.isValid()){
bgColor = this->tMenuCtrlStyles.cMenuBtnBkgClr;
}
auto iconRectWidth = outer_radius - inner_radius;
if(!this->tMenuCtrlStyles.bRoundMenuItem){
painter->fillPath(path,vMenuButtonList.at(i)->bIsHoverState?this->tMenuCtrlStyles.cMenuBtnHovers:bgColor);
}
else{
QRectF rectBtnBkg(0, 0, iconRectWidth / this->tMenuCtrlStyles.dItemsBkgScale, iconRectWidth / this->tMenuCtrlStyles.dItemsBkgScale);
rectBtnBkg.moveCenter(path.boundingRect().center());
painter->setBrush(vMenuButtonList.at(i)->bIsHoverState?this->tMenuCtrlStyles.cMenuBtnHovers:bgColor);
painter->setPen(tMenuCtrlStyles.bHasItemBorder?QPen(tMenuCtrlStyles.cItemBorderClr,tMenuCtrlStyles.dItemBorderWid):Qt::NoPen);
painter->drawRoundedRect(rectBtnBkg,rectBtnBkg.width()*tMenuCtrlStyles.dBnRoundRadius,rectBtnBkg.height()*tMenuCtrlStyles.dBnRoundRadius);
}
auto pixmapIcon = vMenuButtonList.at(i)->pMenuItemIcon;
if(!pixmapIcon.isNull()){
QRectF rectIcon(0, 0, iconRectWidth / this->tMenuCtrlStyles.dItemsBtnScale, iconRectWidth / this->tMenuCtrlStyles.dItemsBtnScale);
rectIcon.moveCenter(path.boundingRect().center());
painter->drawPixmap(rectIcon, pixmapIcon, pixmapIcon.rect());
}
else{
QRectF rectText(0, 0, iconRectWidth / this->tMenuCtrlStyles.dItemsBtnScale*0.95, iconRectWidth / this->tMenuCtrlStyles.dItemsBtnScale*0.95);
rectText.moveCenter(path.boundingRect().center());
fontObj.setPointSizeF(rectText.height()/4.0);
painter->setFont(fontObj);
painter->setPen(vMenuButtonList.at(i)->bIsHoverState?tMenuCtrlStyles.cMenuTextHover:tMenuCtrlStyles.cMenuBtnTxtClr);
painter->drawText(rectText, Qt::AlignCenter|Qt::TextWordWrap, QString::fromStdU16String(vMenuButtonList.at(i)->uItemTitleVal));
}
/***************************
* draw seperator
* */
if(this->tMenuCtrlStyles.bShowItemsLine&&!this->tMenuCtrlStyles.bRoundMenuItem){
QPointF point1(width() / 2.0 + outer_radius * std::cos(radian * i + GetFrmStartRadian()),
height() / 2.0 - outer_radius * std::sin(radian * i + GetFrmStartRadian()));
QPointF point2(width() / 2.0 + inner_radius * std::cos(radian * i + GetFrmStartRadian()),
height() / 2.0 - inner_radius * std::sin(radian * i + GetFrmStartRadian()));
painter->save();
painter->setPen(QPen(QBrush(tMenuCtrlStyles.cItemBorderClr), tMenuCtrlStyles.dBkgBordersWid));
painter->drawLine(point1, point2);
painter->restore();
}
}
}
大半夜刚封装完,困的实在类,先写到这。