Bug - Opacity does not seem to work for Qt6 apps in Wayland

Background: I want to port my app from GTK3 to Qt6. The only blocker is, Wayland does not seem to allow app opacity to be controlled by the app itself for Qt apps. But it works for GTK3 or GTK4 apps.

Since my app is a beautiful weather widget, I want to control its opacity from within the app (this has been a long time feature), and it is possible with GTK3+wayland (and instead of switching to GTK4, I want to switch to Qt).

I am using KDE 6.6.0 with Wayland.

Here are two examples. Just run python wayland_opacity_gtk4.py and python wayland_opacity_qt6.py: The GTK4 app works flawlessly. The Qt6 app prints

This plugin does not support setting window opacity

Here is the GTK4 example

#!/usr/bin/env python3
"""Minimal GTK4 opacity demo for Wayland/X11 bug reports."""

import gi

gi.require_version("Gtk", "4.0")
from gi.repository import Gdk, Gtk


class OpacityDemo(Gtk.ApplicationWindow):
    def __init__(self, app):
        super().__init__(application=app, title="GTK4 Opacity Demo")
        self.set_default_size(420, 220)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
        box.set_margin_top(12)
        box.set_margin_bottom(12)
        box.set_margin_start(12)
        box.set_margin_end(12)
        self.set_child(box)

        self.label = Gtk.Label(label="Opacity: 1.00")
        self.slider = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 0.10, 1.00, 0.01)
        self.slider.set_value(1.00)
        self.slider.connect("value-changed", self.on_change)

        box.append(self.label)
        box.append(self.slider)

    def on_change(self, _slider):
        opacity = self.slider.get_value()
        self.label.set_text(f"Opacity: {opacity:.2f}")
        self.set_opacity(opacity)


class DemoApp(Gtk.Application):
    def __init__(self):
        super().__init__(application_id="io.github.archisman.typhoon.gtk4opacitydemo")

    def do_activate(self):
        display = Gdk.Display.get_default()
        backend = display.get_name() if display else "unknown"
        print(f"GTK4 display backend: {backend}")

        win = OpacityDemo(self)
        win.present()


def main():
    app = DemoApp()
    app.run(None)


if __name__ == "__main__":
    main()

Here is the Qt6 example

#!/usr/bin/env python3
"""Minimal Qt opacity demo for Wayland/X11 bug reports."""

import sys

try:
    from PyQt6.QtCore import Qt
    from PyQt6.QtWidgets import QApplication, QLabel, QSlider, QVBoxLayout, QWidget
except ImportError:
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import QApplication, QLabel, QSlider, QVBoxLayout, QWidget


class OpacityDemo(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Qt Opacity Demo")
        self.resize(420, 220)

        self.label = QLabel("Opacity: 1.00")
        self.slider = QSlider(Qt.Orientation.Horizontal)
        self.slider.setMinimum(10)
        self.slider.setMaximum(100)
        self.slider.setValue(100)
        self.slider.valueChanged.connect(self.on_change)

        layout = QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.slider)

    def on_change(self, value: int):
        opacity = value / 100.0
        self.label.setText(f"Opacity: {opacity:.2f}")
        self.setWindowOpacity(opacity)


def main():
    app = QApplication(sys.argv)
    print(f"Qt platform: {app.platformName()}")
    window = OpacityDemo()
    window.show()
    if hasattr(app, "exec"):
        sys.exit(app.exec())
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Does anyone know any workaround?

The Qt6 code does work in X11.

I am happy to send a bug report. Someone please tell me which package I should file the bug report against

I am not sure what you get in X11, but here is an example (albeit C++) what works with KWin Wayland.

The highlights are:

  • setAttribute(Qt::WA_TranslucentBackground, true);
  • Using QGraphicsOpacityEffect::setOpacity(...) instead of QWindow::setWindowOpacity(...)

Though I think, your result will end up being a little different from what you are doing with setWindowOpacity(...)

#include "Widget.h"
#include "ui_Widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    goe = new QGraphicsOpacityEffect;
    setGraphicsEffect(goe);
    setAttribute(Qt::WA_TranslucentBackground, true);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    opacity += 0.2;
    if (opacity > 1) {
        opacity = 0.2;
    }
    ui->textBrowser->append("Current Opacity: " + QString::number(opacity));
    goe->setOpacity(opacity);
    // setWindowOpacity(opacity);
}


The bug report would most probably go to https://bugreports.qt-project.org/.
In case this is a valid bug though, expect there to already be a similar report.