QMenu更改窗口形状的方法有三种,分别是qss,painter和QStyle。这边介绍的是QStyle。
使用到Qstyle来修改控件的样式,一般需要查看这个控件painter的源码。源码相关的这边不做主要介绍,你只需要知道一般标准控件的painter源码中绘制控件的几个接口,分别有:
void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override;
void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override;
void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = nullptr) const override;
void drawItemText(QPainter *painter, const QRect &rect, int flags, const QPalette &pal, bool enabled,
const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const override;
virtual void drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, const QPixmap &pixmap) const override;
drawPrimitive //绘制主窗体的。
drawControl //各控件中的结构
drawComplexControl //
drawItemText //
drawItemPixmap //
至于它们分别是什么作用,这个具体我也介绍不清楚,涉及的源码太多,并且各个控件也不一样。
接下来:直接上代码。
1、继承QMenu重写部分操作和设置style。
2、继承style代理QProxyStyle,实现相关的方法。
这边重写drawPrimitive即可改变窗口形状。
```cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "umenu.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QPushButton* btn = new QPushButton(this);
btn->setObjectName("btn");
UMenu* menu = new UMenu(btn);
btn->setMenu(menu);
menu->addSection("start");
menu->addAction("1");
menu->addSeparator();
menu->addAction("2");
menu->addSeparator();
menu->addAction("3");
btn->move((width()-btn->width())/2,(height()-btn->height())/2);
btn->setStyleSheet("QPushButton#btn{border-image:url(:/1.jpeg);}QPushButton#btn:hover{border-image:url(:/2.jpeg);}QPushButton#btn:pressed{border-image:url(:/2.jpeg);}");
}
MainWindow::~MainWindow()
{
delete ui;
}
#ifndef UMENU_H
#define UMENU_H
#include <QtWidgets>
#define EFFECT_NUMBER 30 //阴影大小
#define TRIANGLE_HEIGHT 10 //三角图形高度
class UMenu : public QMenu
{
Q_OBJECT
public:
UMenu(QWidget *parent = 0);
void setBackground(QColor color); //设置QMenu背景颜色
void setRadius(int xradius, int yradius);
int getXradius();
int getYradius();
void setPopUpheight(int height); //设置菜单弹出的相对高度
signals:
void sigCloseMenu();
protected:
void showEvent(QShowEvent *event);
void closeEvent(QCloseEvent *event);
private:
QWidget* m_parent;
int m_xradius; //x轴圆角
int m_yradius; //y轴圆角
int m_popupheight; //菜单弹出的相对高度
};
class UStyle : public QProxyStyle
{
public:
UStyle(QStyle *style = nullptr);
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const;
#if 0
virtual void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const;
#endif
};
#endif // UMENU_H
#include "umenu.h"
#include <QDebug>
UMenu::UMenu(QWidget *parent) : m_parent(parent)
{
m_xradius = 5;
m_yradius = 5;
m_popupheight = 4;
this->setAttribute(Qt::WA_TranslucentBackground);
this->setContentsMargins(EFFECT_NUMBER,EFFECT_NUMBER+TRIANGLE_HEIGHT,EFFECT_NUMBER,EFFECT_NUMBER); //设置边距,空出阴影位置和顶部三角图形位置
//添加阴影
QGraphicsDropShadowEffect *shadowEffect =new QGraphicsDropShadowEffect(this);
shadowEffect->setOffset(0,0);
shadowEffect->setColor(QColor(0,0,0,177));
shadowEffect->setBlurRadius(EFFECT_NUMBER);
this->setGraphicsEffect(shadowEffect);
QPalette p = this->palette();
p.setColor(QPalette::Background,QColor("#FFFFFF")); //QMenu背景颜色
this->setPalette(p);
UStyle* style = new UStyle;
this->setStyle(style);
}
void UMenu::showEvent(QShowEvent* event)
{
if (m_parent)
{
QPoint pos;
pos.setX(EFFECT_NUMBER/2+(-this->sizeHint().width())*3.0/4+(m_parent->width())/2);
pos.setY(m_parent->height()+m_popupheight-EFFECT_NUMBER);
this->move(m_parent->mapToGlobal(pos)); //计算弹出位置
}
}
void UMenu::closeEvent(QCloseEvent *event)
{
emit sigCloseMenu();
}
//设置菜单弹出的相对高度
void UMenu::setPopUpheight(int height)
{
m_popupheight = height;
}
void UMenu::setBackground(QColor color)
{
QPalette p = this->palette();
p.setColor(QPalette::Background,color); //QMenu背景颜色
this->setPalette(p);
}
void UMenu::setRadius(int xradius,int yradius)
{
m_xradius = xradius;
m_yradius = yradius;
}
int UMenu::getXradius(){return m_xradius;}
int UMenu::getYradius(){return m_yradius;}
/*-----------------------------------------------UStyle-----------------------------------------------------*/
UStyle::UStyle(QStyle *style)
{
}
void UStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
switch (element) {
case PE_FrameMenu: //整个菜单widget的边框色
qDrawShadeRect(painter,option->rect,option->palette,option->state & State_Sunken,1);
break;
case PE_PanelMenu: //整个菜单widget的背景色
{
// qDrawShadePanel(painter,option->rect,option->palette,option->state & State_Sunken,0,&option->palette.brush(QPalette::Background));
QRect rect = QRect(option->rect.x()+EFFECT_NUMBER,option->rect.y()+EFFECT_NUMBER+TRIANGLE_HEIGHT,option->rect.width()-EFFECT_NUMBER*2,option->rect.height()-EFFECT_NUMBER*2-TRIANGLE_HEIGHT);
UMenu* menu = (UMenu*)widget;
painter->setPen(option->palette.color(QPalette::Background));
painter->setBrush(option->palette.color(QPalette::Background));
painter->setRenderHint(QPainter::Antialiasing);
// 绘制矩形
painter->drawRoundedRect(rect,menu->getXradius(),menu->getYradius());
// 绘制多边形
QPainterPath path;
double realwidth = option->rect.width()-EFFECT_NUMBER*2;
double startx = EFFECT_NUMBER+4/6.0*realwidth;
double endx = EFFECT_NUMBER+5/6.0*realwidth;
double twidth = 1/6.0*realwidth;
double theight = TRIANGLE_HEIGHT;
path.moveTo(startx,EFFECT_NUMBER+theight);
path.lineTo(startx+2/5.0*twidth,EFFECT_NUMBER+1/5.0*theight);
path.arcTo(startx+2/5.0*twidth,EFFECT_NUMBER+1/10.0*theight,1/5.0*twidth,1/5.0*twidth,-180,-180);
path.lineTo(startx+3/5.0*twidth,EFFECT_NUMBER+1/5.0*theight);
path.lineTo(endx,EFFECT_NUMBER+theight);
path.lineTo(startx,EFFECT_NUMBER+theight);
painter->drawPath(path);
}
break;
default:
QProxyStyle::drawPrimitive(element,option,painter,widget);
break;
}
}
#if 0
void UStyle::drawControl(ControlElement element, const QStyleOption *option,QPainter *painter, const QWidget *widget) const
{
switch (element) {
case CE_MenuEmptyArea: //空白区域不处理,看源码可看见最后恢得成了整个菜单区域,有需要的可以处理如蒙上一层半透明
break;
case CE_MenuItem: //画菜单项
if(const QStyleOptionMenuItem * menuItem = qstyleoption_cast<const QStyleOptionMenuItem*>(option))
{
painter->save();
if(menuItem->menuItemType == QStyleOptionMenuItem::Separator)
{ //画分隔线
painter->setPen(QColor("#DDDDDD"));
painter->drawLine(0,menuItem->rect.center().y(),menuItem->rect.right(),menuItem->rect.center().y());
}
else if(menuItem->menuItemType == QStyleOptionMenuItem::SubMenu)
{ //画子菜单
}
else
{ //文字菜单项
painter->setPen(QColor("#7D86A9"));
QFont font = menuItem->font;
painter->setFont(font);
int text_flags = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
painter->drawText(menuItem->rect,text_flags,menuItem->text);
}
bool hover = menuItem->state & State_Selected && menuItem->state & State_Enabled;
if(hover)
{ //鼠标滑过,先画一个矩形,使后面的文字不会被覆盖
painter->setPen(Qt::red);
QFont font = menuItem->font;
painter->setFont(font);
int text_flags = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
painter->drawText(menuItem->rect,text_flags,menuItem->text);
}
painter->restore();
}
break;
default:
QProxyStyle::drawControl(element,option,painter,widget);
break;
}
}
#endif
效果: