Search code examples
pythonpyqtpyqt6

How do I make Qt window receive clicks again after setting `WA_TransparentForMouseEvents` attribute in PyQt?


According to documentation, in order to unset an attribute, I need to pass False as the second parameter to setAttribute method of a widget. I am already doing that, but for some reason window stays transparent for mouse events.

Here is a minimal reproducible example:

import sys

from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
from PyQt6.QtCore import Qt


class Main(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(400, 400)
        self.setWindowTitle('Main')

        self.state = False

        self.button = QPushButton(f'Secondary window can be clicked through: {self.state}')
        self.button.clicked.connect(self.button_click)

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.button)
        self.setLayout(self.layout)

        self.secondary = Secondary()

    def button_click(self):
        self.state = not self.state
        self.button.setText(f'Secondary window can be clicked through: {self.state}')
        self.secondary.set_click_through_state(self.state)


class Secondary(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(400, 400)
        self.setWindowOpacity(0.8)
        self.setWindowTitle('Secondary')

    def set_click_through_state(self, state: bool):
        print('Received new state:', state)
        self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, on=state)

        self.hide()
        if state:
            self.setWindowFlags(
                self.windowFlags()
                | Qt.WindowType.FramelessWindowHint
                | Qt.WindowType.WindowStaysOnTopHint
            )
        else:
            self.setWindowFlags(
                self.windowFlags()
                & ~Qt.WindowType.FramelessWindowHint
                & ~Qt.WindowType.WindowStaysOnTopHint
            )
        self.show()

        print('Attribute is set:', self.testAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents))


if __name__ == '__main__':
    app = QApplication(sys.argv)

    main = Main()
    main.show()
    main.secondary.show()

    sys.exit(app.exec())

Tested on Windows 10 / Python 3.10 / PyQt6.


Solution

  • Turns out that setting WA_TransparentForMouseEvents attribute to a widget also adds a WindowTransparentForInput flag to it. I am not sure why, but this flag is not removed when unsetting an attribute.

    Issue resolved by simply removing WindowTransparentForInput flag from window:

    widget.setWindowFlags(
        widget.windowFlags() & ~Qt.WindowType.WindowTransparentForInput
    )