QMenu实现不规则形状

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

效果:
请添加图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东方忘忧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值