pythonpython-3.xpyqt5ctypespyside2

user32.SetWindowCompositionAttribute with PyQt5 causes ctypes.ArgumentError


Recently I installed a python package called BlurWindow via

pip install BlurWindow

This python package helps to achieve a transparent blur background to the application window. Although it says that this module works with tkinter, qt and many other packages, but in my case it only works with tkinter and PySide2 but not with PyQt5.

My code can be seen below:

import sys

from PySide2.QtWidgets import *
from PySide2.QtCore import *

# from PyQt5.QtWidgets import *
# from PyQt5.QtCore import *

from BlurWindow.blurWindow import GlobalBlur


class MainWindow(QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setAttribute(Qt.WA_TranslucentBackground)
        # self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowMinMaxButtonsHint)   # set window flags
        self.resize(500, 400)

        l = QLabel('How do you like the blurry window?', self)

        GlobalBlur(self.winId(), Dark=False, Acrylic=False, QWidget=self)

        self.setStyleSheet("color: white; background-color: rgba(0, 0, 0, 0)")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec_())

This code works fine. But when I comment these two lines (line 3 and 4):

from PySide2.QtWidgets import *
from PySide2.QtCore import *

and uncomment these two lines (line 6 and 7):

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

this error occurs:

Traceback (most recent call last):
  File "E:\PythonProjects\Zambo\lib\runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "E:\PythonProjects\Zambo\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "E:\PythonProjects\Zambo\lib\site-packages\BlurWindow\blurWindow.py", line 191, in <module>
    mw = MainWindow()
  File "E:\PythonProjects\Zambo\lib\site-packages\BlurWindow\blurWindow.py", line 185, in __init__
    GlobalBlur(hWnd,Dark=True,QWidget=self)
  File "E:\PythonProjects\Zambo\lib\site-packages\BlurWindow\blurWindow.py", line 160, in GlobalBlur
    blur(HWND,hexColor,Acrylic,Dark)
  File "E:\PythonProjects\Zambo\lib\site-packages\BlurWindow\blurWindow.py", line 136, in blur
    user32.SetWindowCompositionAttribute(HWND, data)
ctypes.ArgumentError: argument 1: <class 'TypeError'>: Don't know how to convert parameter 1

I don't know why this code works with PySide2 but doesn't work with PyQt5!

This is the blurWindow.py module that I'm using in the shown code.

I am on windows 10 and using Python 3.9.5 on an anaconda virtual environment. But I have tested this code on Python 3.6 and 3.8 but the PyQt5 library doesn't work anywhere, instead shows that same error.

I have almost finished my project in which I will implement this blur effect. And since I've used PyQt5 library in my project, replacing PyQt5 library with PySide2 will be a big trouble for me. Any help would be highly appreciated!


Solution

  • At the beginning, I thought it might be the same thing as [SO]: ctypes.ArgumentError when using kivy with pywinauto, but it turns out it's much simpler.

    It's a bug in BlurWindow. Running blurWindow.py yields the same behavior (with PyQt5.12.3).

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q068651351]> sopr.bat
    ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
    
    [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.08.07_test0\Scripts\python.exe" BlurWindow\blurWindow.py
    Traceback (most recent call last):
      File "BlurWindow\blurWindow.py", line 188, in <module>
        mw = MainWindow()
      File "BlurWindow\blurWindow.py", line 182, in __init__
        GlobalBlur(hWnd,Dark=True,QWidget=self)
      File "BlurWindow\blurWindow.py", line 157, in GlobalBlur
        blur(HWND,hexColor,Acrylic,Dark)
      File "BlurWindow\blurWindow.py", line 133, in blur
        user32.SetWindowCompositionAttribute(HWND, data)
    ctypes.ArgumentError: argument 1: <class 'TypeError'>: Don't know how to convert parameter 1
    
    [prompt]> :: Test the 2 modules
    [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.08.07_test0\Scripts\python.exe" -c "import PyQt5.QtWidgets as pqqw; help(pqqw.QWidget.winId)"
    Help on built-in function winId:
    
    winId(...)
        winId(self) -> sip.voidptr
    
    
    [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.08.07_test0\Scripts\python.exe" -c "import PySide2.QtWidgets as psqw; help(psqw.QWidget.winId)"
    Help on method_descriptor:
    
    winId(self) -> int
        winId(self) -> int
    

    As seen, there's a "small" difference between the 2 modules QtWidgets.QWidget.winId's return value. CTypes doesn't know to convert PyQt5's.

    Short story

    Since you copied the MainWindow code here, the fix is simple, replace:

    GlobalBlur(self.winId(), Dark=False, Acrylic=False, QWidget=self)
    

    by:

    GlobalBlur(int(self.winId()), Dark=False, Acrylic=False, QWidget=self)
    

    and it should work fine. But check the URL at the next section's beginning to see what you might run into.

    Long story

    This is more about BlurWindow. The error is when calling (!!!undocumented!!!) user32.SetWindowCompositionAttribute via CTypes.
    An important point here: [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer). Note that it's Undefined Behavior, and it only works by luck.

    Submitted [GitHub]: Peticali/PythonBlurBehind - Fix SetWindowCompositionAttribute failure (merged on 210805). It contains a proper fix to the current issue.

    Check [SO]: How to change username of job in print queue using python & win32print (@CristiFati's answer) (at the end) for possible ways to go further.
    You could download the patch, and apply the changes locally. Check [SO]: Run / Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (@CristiFati's answer) (Patching utrunner section) for how to apply patches on Win (basically, every line that starts with one "+" sign goes in, and every line that starts with one "-" sign goes out). I am using Cygwin, btw.
    But since it's just one file, you could download it and overwrite your existing one.
    In any case, back it up first!