qmlpopupshadowpyqt6

Strange border around popup when adding drop shadow with MultiEffect


I'm attempting to add a drop shadow effect around a QML popup, but when I do this with a MultiEffect, it creates an additional border around the popup (beyond the shadow).

Here's a minimal QML example that shows the problem I'm encountering:

import QtQuick
import QtQuick.Controls
import QtQuick.Effects

ApplicationWindow {
    width: 500
    height: 400
    visible: true

    color: "#E0E0E0"

    Button {
        text: "Open Popup"
        anchors.centerIn: parent
        onClicked: popup.open()
    }

    Popup {
        id: popup
        modal: true
        focus: true
        x: (parent.width - width) / 2
        y: (parent.height - height) / 2
        width: 200
        height: 150

        background: Rectangle {
            id: bg
            anchors.fill: parent.item
            color: "white"
            radius: 10
        }

        MultiEffect {
            anchors.fill: parent
            source: bg
            shadowEnabled: true
            shadowHorizontalOffset: 5
            shadowVerticalOffset: 5
        }

        Column {
            anchors.centerIn: parent
            spacing: 10

            Text { text: "This is a popup" }
            Button { text: "Close"; onClicked: popup.close() }
        }
    }
}

106c8606-3132-4a77-b395-3c0c16399637-image.png

In the past, I have used RectangularGlow with great success. This item was much easier to work with. For example:

background: Rectangle {
        anchors.fill: parent
        border.width: 1
        border.color: "black"
        color: root.color

        RectangularGlow {
            z: -1
            anchors.fill: parent
            glowRadius: 12
            spread: 0.1
            color: Colors.black
            opacity: 0.6
        }
}

This works much better. However, unfortunately import Qt5Compat.GraphicalEffects is not available to me on this new project because I am using PyQt6, and Qt5Compat is not available with PyQt6, so I'm forced to resort to MultiEffect that has this strange border.

I'm not attached to using MultiEffect, so if there's a different way to achieve a dropshadow effect (that is compatible with PyQt6) I'd be happy to use that instead, but MultiEffect is the closest thing I've gotten to achieving this effect.


Solution

  • There is no "additional border" here.
    The effect is caused by the background and its shadow, which is rendered inside the background's bounds (not outside it).
    You only need to move the MultiEffect inside the background component, as in the following example:

    Popup {
        width: 200; height: 150
        anchors.centerIn: parent
        modal: true
        visible: true
    
        background: Rectangle {
            radius: 10
            layer.enabled: true
            layer.effect: MultiEffect {
                anchors.fill: parent
                shadowEnabled: true
            }
        }
    
        Text { 
            anchors.centerIn: parent
            text: "This is a popup"
        }
    }
    

    However, I recommend against using MultiEffect for rectangular shapes. Instead, use RectangularGlow or RectangularShadow.
    MultiEffect has a more complex implementation (it uses blur) and is designed for non-rectangular shapes (e.g., transparent PNG or WebP files).
    In contrast, RectangularShadow uses a simple SDF formula, which is significantly faster.

    Example using RectangularShadow:

    Popup {
        // ...
    
        background: Item {
            RectangularShadow {
                anchors.fill: rect
                radius: rect.radius
                blur: 30; spread: 10
                color: "#55000000"
            }
    
            Rectangle {
                id: rect
                anchors.fill: parent
                radius: 10
            }
        }
    
        // ...
    }