QMemu 阴影实现

5/30/2022 Qt

# 菜单阴影

Qt自带的菜单太单调了,要自己定制,所以简单修改了Qss

QString qss = "QMenu {\
     background-color : rgb(255,255,255);\
     padding : 5px;\
     margin : 5px;\
     border : 1px solid gray;\
     border-radius:10px;\
    }\
    QMenu::item {\
        font-size:10pt;\
        color: rgb(0,0,0);\
        background-color: transparent;\
        padding: 8px 25px 6px 10px;\
        margin: 4px 1px;\
    }\
    QMenu::item:selected {\
        background-color : lightblue;\
        border-radius:5px;\
    }\
    QMenu::icon:checked {\
        background: rgb(253,253,254);\
        position: absolute;\
        top: 1px;\
        right: 1px;\
        bottom: 1px;\
        left: 1px;\
    }\
    QMenu::icon:checked:selected {\
        background-color : rgb(236,236,237);\
    }\
    QMenu::separator {\
        height: 2px;\
        background: rgb(235,235,236);\
        margin-left: 5px;\
        margin-right: 5px;\
    }\
";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

设置四周圆角,所以需要吧原先的背景透明

    // pop menu的基本属性,不加这个menu点其他位置不消失
    // framelesswindowhint 如果设置圆角需要这个,让他变成无边框, 显示圆角
    // nodropshawWindowhint 不显示阴影,(在某些平台下)因为默认右侧与下侧有阴影,所以导致右下角不显示圆角
    setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);
    // 设置背景透明,让四个角只显示圆角,不显示其他黑的部分
    setAttribute(Qt::WA_TranslucentBackground);
1
2
3
4
5
6

# QGraphicsDropShadowEffect

上面设置了不显示自带阴影,但是还是想要菜单有阴影的,所以理所当然的,想到了无边框窗口的常用方法,使用 QGraphicsDropShadowEffect

    
	// 注意在Qss中留出margin,给阴影显示的空间
	QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this);
    shadow->setOffset(0, 0);
    shadow->setColor(QColor("#444444"));
    shadow->setBlurRadius(10);
    setGraphicsEffect(shadow);
1
2
3
4
5
6
7

但是一般无边框窗口我们是在widget里面嵌套一个子Widget留出margin,这个子widget才是真正的显示窗口,setGraphicsEffect作用于这个子widget。

而上面的写法是直接作用于QMenu的主Widget上了。

所以当窗体重新刷新的时候会报error

UpdateLayeredWindowIndirect failed for ptDst=(1367, 153), size=(199x219), dirty=(219x239 -10, -10) (参数错误。)

大概就是画到了QMenu窗口外面。

虽然看起来没什么大问题,因为程序不会崩溃,菜单与阴影也正常显示。

但是当你把QMenu作为类的成员变量时,选择一个QMenu的Action后,再次唤出菜单时,会发现,你上次的选择作为残影留在了上面。没有刷新。

当然,解法也很简单。

出现上面的的原因是QMenu还是同一个menu,因为error没有正常刷新,所以第二次出来时,会留有第一次操作的残影。

那我们用完就销毁好了。每次用的都是新的Menu,作为一个临时变量使用,选择完就销毁。

# QPainter

但是如果还是想要优雅一点。

那么就使用另一种阴影方式,用QPainter画出来

#define SHADOW_WIDTH 10		// 阴影边框宽度;

MyShadowWidget::MyShadowWidget(QWidget *parent)
    : QWidget(parent)
{
    
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_TranslucentBackground);
}

void MyShadowWidget::paintEvent(QPaintEvent *event)
{
    
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.fillRect(QRect(SHADOW_WIDTH, SHADOW_WIDTH, this->width() - 2 * SHADOW_WIDTH, this->height() - 2 * SHADOW_WIDTH), QBrush(Qt::white));

    QColor color(50, 50, 50, 30);
    for (int i = 0; i < SHADOW_WIDTH; i++)
    {
    
        color.setAlpha(120 - qSqrt(i) * 40);
        painter.setPen(color);
        // 方角阴影边框;
     //   painter.drawRect(SHADOW_WIDTH - i, SHADOW_WIDTH - i, this->width() - (SHADOW_WIDTH - i) * 2, this->height() - (SHADOW_WIDTH - i) * 2);
        // 圆角阴影边框;
        painter.drawRoundedRect(SHADOW_WIDTH - i, SHADOW_WIDTH - i, this->width() - (SHADOW_WIDTH - i) * 2, this->height() - (SHADOW_WIDTH - i) * 2, 4, 4);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

但是这种方式毕竟是画出来的,效率可能不太高,效果也可能没有那么好,但是只是一个QMenu,问题不大

# 图片

据说还有用阴影图片的方法,但是我没使用过。放个连接

https://www.i4k.xyz/article/GoForwardToStep/99549750