Skip to content

pre_install_pyside

Pre-launch hook for installing Qt bindings in Marvelous Designer.

This module provides: - InstallQtBinding: Pre-launch hook that installs PySide6 to MD's Python environment to enable Qt-based functionality.

InstallQtBinding

Bases: PreLaunchHook

Install Qt binding to unreal's python packages.

Source code in client/ayon_marvelousdesigner/hooks/pre_install_pyside.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
class InstallQtBinding(PreLaunchHook):
    """Install Qt binding to unreal's python packages."""

    app_groups: ClassVar = {"marvelousdesigner"}
    order = 11
    launch_types: ClassVar = {LaunchTypes.local}

    def execute(self) -> None:
        """Execute the pre-launch hook to install PySide6."""
        current_platform = platform.system().lower()
        python_exe = (
            "python"
            if current_platform != "windows"
            else "python.exe"
        )
        python_executable = Path(shutil.which(python_exe)).as_posix()
        md_setting = self.data["project_settings"]["marvelous_designer"]
        qt_binding_dir = md_setting["prelaunch_settings"].get(
            "qt_binding_dir", "")
        qt_binding_dir = Path(qt_binding_dir)
        if not qt_binding_dir.exists():
            self.log.warning(
                "Qt binding directory '%s' does not exist.", qt_binding_dir
            )
            return

        return_code = self.install_pyside(python_executable, qt_binding_dir)
        if return_code:
            self.log.info("PySide6 installed successfully.")
            self.launch_context.env["QtDir"] = qt_binding_dir.as_posix()

    def install_pyside(
            self, python_executable: str,
            qt_binding_dir: Path) -> Union[int, None]:
        """Install PySide6 python module to marvelous designer's python.

        Installation requires administration rights that's why it is required
        to use "pywin32" module which can execute command's and ask for
        administration rights.

        Note:
            This is asking for administrative right always, no matter if
            it is actually needed or not. Unfortunately getting
            correct permissions for directory on Windows isn't that trivial.
            You can either use `win32security` module or run `icacls` command
            in subprocess and parse its output.

        Returns:
            Return code from the pip install process, or None
            if installation fails.

        """
        args = [
            python_executable,
            "-m",
            "pip",
            "install",
            "PySide6",
            "--target",
            qt_binding_dir.as_posix(),
            "--ignore-installed",
        ]

        return self.pip_install(args)

    def pip_install(self, args: list) -> Union[int, None]:
        """Execute pip install command in subprocess.

        Args:
            args (list): List of command line arguments for pip install.

        Returns:
            bool or None: True if pip install was successful (return code 0),
            None if an exception occurred during execution.

        """
        try:
            # Parameters
            # - use "-m pip" as module pip to install PySide2/6 and argument
            #   "--ignore-installed" is to force install module to unreal
            #   site-packages and make sure it is binary compatible

            process = subprocess.Popen(
                args, stdout=subprocess.PIPE,
                stderr=subprocess.PIPE, universal_newlines=True
            )
            process.communicate()

        except PermissionError:
            self.log.warning(
                'Permission denied with command: "%s".', " ".join(args),
                exc_info=True)
        except OSError as error:
            self.log.warning(
                'OS error has occurred: "%s".', error, exc_info=True)
        except subprocess.SubprocessError:
            pass
        else:
            return process.returncode == 0

        return None

execute()

Execute the pre-launch hook to install PySide6.

Source code in client/ayon_marvelousdesigner/hooks/pre_install_pyside.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def execute(self) -> None:
    """Execute the pre-launch hook to install PySide6."""
    current_platform = platform.system().lower()
    python_exe = (
        "python"
        if current_platform != "windows"
        else "python.exe"
    )
    python_executable = Path(shutil.which(python_exe)).as_posix()
    md_setting = self.data["project_settings"]["marvelous_designer"]
    qt_binding_dir = md_setting["prelaunch_settings"].get(
        "qt_binding_dir", "")
    qt_binding_dir = Path(qt_binding_dir)
    if not qt_binding_dir.exists():
        self.log.warning(
            "Qt binding directory '%s' does not exist.", qt_binding_dir
        )
        return

    return_code = self.install_pyside(python_executable, qt_binding_dir)
    if return_code:
        self.log.info("PySide6 installed successfully.")
        self.launch_context.env["QtDir"] = qt_binding_dir.as_posix()

install_pyside(python_executable, qt_binding_dir)

Install PySide6 python module to marvelous designer's python.

Installation requires administration rights that's why it is required to use "pywin32" module which can execute command's and ask for administration rights.

Note

This is asking for administrative right always, no matter if it is actually needed or not. Unfortunately getting correct permissions for directory on Windows isn't that trivial. You can either use win32security module or run icacls command in subprocess and parse its output.

Returns:

Type Description
Union[int, None]

Return code from the pip install process, or None

Union[int, None]

if installation fails.

Source code in client/ayon_marvelousdesigner/hooks/pre_install_pyside.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def install_pyside(
        self, python_executable: str,
        qt_binding_dir: Path) -> Union[int, None]:
    """Install PySide6 python module to marvelous designer's python.

    Installation requires administration rights that's why it is required
    to use "pywin32" module which can execute command's and ask for
    administration rights.

    Note:
        This is asking for administrative right always, no matter if
        it is actually needed or not. Unfortunately getting
        correct permissions for directory on Windows isn't that trivial.
        You can either use `win32security` module or run `icacls` command
        in subprocess and parse its output.

    Returns:
        Return code from the pip install process, or None
        if installation fails.

    """
    args = [
        python_executable,
        "-m",
        "pip",
        "install",
        "PySide6",
        "--target",
        qt_binding_dir.as_posix(),
        "--ignore-installed",
    ]

    return self.pip_install(args)

pip_install(args)

Execute pip install command in subprocess.

Parameters:

Name Type Description Default
args list

List of command line arguments for pip install.

required

Returns:

Type Description
Union[int, None]

bool or None: True if pip install was successful (return code 0),

Union[int, None]

None if an exception occurred during execution.

Source code in client/ayon_marvelousdesigner/hooks/pre_install_pyside.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def pip_install(self, args: list) -> Union[int, None]:
    """Execute pip install command in subprocess.

    Args:
        args (list): List of command line arguments for pip install.

    Returns:
        bool or None: True if pip install was successful (return code 0),
        None if an exception occurred during execution.

    """
    try:
        # Parameters
        # - use "-m pip" as module pip to install PySide2/6 and argument
        #   "--ignore-installed" is to force install module to unreal
        #   site-packages and make sure it is binary compatible

        process = subprocess.Popen(
            args, stdout=subprocess.PIPE,
            stderr=subprocess.PIPE, universal_newlines=True
        )
        process.communicate()

    except PermissionError:
        self.log.warning(
            'Permission denied with command: "%s".', " ".join(args),
            exc_info=True)
    except OSError as error:
        self.log.warning(
            'OS error has occurred: "%s".', error, exc_info=True)
    except subprocess.SubprocessError:
        pass
    else:
        return process.returncode == 0

    return None