From 1933b035bdc89c25f3260a9db10d8cb219f06e72 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Thu, 15 Jan 2026 15:49:07 +0100 Subject: [PATCH 1/3] Install the dependencies of the Blender addon into the addon folder Blender installs the Mastodon Blender View addon into a specific folder. On linux for example: ~/.config/blender/4.4/scripts/addons/mastodon_blender_view/ This commit changes the addon installation process to install it's dependencies in a "libs" subfolder. For example: ~/.config/blender/4.4/scripts/addons/mastodon_blender_view/libs/ Previously the dependencies where installed into a globel folder. Online discussions suggest that the new behavoir is a better practice. It's still following Blenders best practice for addons, which would deploy wheels with the addon. Using this location hopefully solves two issues. - "Portable Blender" is no longer required. - Installation problems on some Windows machines would be fixed. --- blender-addon/__init__.py | 15 +++++ .../blender-scripts/install_addon.py | 64 +++++++++++-------- src/main/resources/csv/read_csv.py | 3 + 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/blender-addon/__init__.py b/blender-addon/__init__.py index 0c269b3..cc489d4 100644 --- a/blender-addon/__init__.py +++ b/blender-addon/__init__.py @@ -37,6 +37,21 @@ "category": "3D View" } + +def add_addon_libs_dir(): + """ + Add the dependencies of the Mastodon Blender View plugin to the pythons system path. + """ + import sys + import os + addon_dir = os.path.dirname(__file__) + addon_libs_dir = os.path.join(addon_dir, "libs") + if addon_libs_dir not in sys.path: + sys.path.insert(0, addon_libs_dir) + + +add_addon_libs_dir() + from . import mb_panel from . import mb_server diff --git a/src/main/resources/blender-scripts/install_addon.py b/src/main/resources/blender-scripts/install_addon.py index 1e42041..8e04879 100644 --- a/src/main/resources/blender-scripts/install_addon.py +++ b/src/main/resources/blender-scripts/install_addon.py @@ -33,8 +33,22 @@ import sys import addon_utils +# install addon + +print("start installing mastodon blender view addon...") +argv = sys.argv +addon_zip = argv[argv.index('--') + 1] +bpy.ops.preferences.addon_install(filepath=addon_zip) +print("mastodon blender view addon installed") + # install pip +os.environ.pop("PIP_REQ_TRACKER", None) +ensurepip.bootstrap() +os.environ.pop("PIP_REQ_TRACKER", None) + +# get python path + def get_python_path(): try: # 2.92 and older @@ -44,40 +58,40 @@ def get_python_path(): path = sys.executable return os.path.abspath(path) +python_path = get_python_path() -os.environ.pop("PIP_REQ_TRACKER", None) -ensurepip.bootstrap() -os.environ.pop("PIP_REQ_TRACKER", None) +print(f"Blender python binary: {python_path}") -# install dependencies +# get installation path of "mastodon_blender_view" folder -python_path = get_python_path() -packages = {'grpcio', 'bidict', 'grpcio-tools', 'pandas'} -subprocess.check_output([python_path, '-m', 'pip', 'install', *packages]) +filename_init_py = [m.__file__ for m in addon_utils.modules() if m.__name__ == "mastodon_blender_view"][0] +addon_dir = os.path.dirname(filename_init_py) +print(f"Installation directory of the Blender Mastodon plugin: {addon_dir}") -# test if dependencies are installed +# install dependencies -try: - import bidict - import grpc - import google.protobuf - import pandas -except ModuleNotFoundError: - raise Exception("dependency installation failed") +addon_libs_dir = os.path.join(addon_dir, "libs") +print(f"Install dependencies into folder: {addon_libs_dir}") -print("dependencies installed") +packages = ['grpcio', 'bidict', 'grpcio-tools', 'pandas'] +subprocess.check_call([python_path, "-m", "pip", "install", "--target", addon_libs_dir, *packages]) -# install addon +if addon_libs_dir not in sys.path: # add addon libs dir to system path + sys.path.insert(0, addon_libs_dir) -argv = sys.argv -addon_zip = argv[argv.index('--') + 1] -bpy.ops.preferences.addon_install(filepath=addon_zip) +import bidict +import grpc +import google.protobuf +import pandas -print("mastodon blender view addon installed") +print("dependencies installed") -filename_init_py = [m.__file__ for m in addon_utils.modules() if m.__name__ == "mastodon_blender_view"][0] -addon_dir = os.path.dirname(filename_init_py) -subprocess.check_output([python_path, '-m', 'grpc_tools.protoc', '-I.', '--python_out=.', '--grpc_python_out=.', 'mastodon_blender_view/mastodon-blender-view.proto'], cwd=os.path.dirname(addon_dir)) +# install google rpc protocol +cmd = [python_path, '-m', 'grpc_tools.protoc', '-I.', '--python_out=.', '--grpc_python_out=.', 'mastodon_blender_view/mastodon-blender-view.proto'] +cwd = path.dirname(addon_dir) +env = os.environ.copy() +env["PYTHONPATH"] = addon_libs_dir + os.pathsep + env.get("PYTHONPATH") if "PYTHONPATH" in env else addon_libs_dir +subprocess.check_output(cmd, cwd=os.cwd, env=env ) try: from mastodon_blender_view import mastodon_blender_view_pb2 @@ -85,4 +99,4 @@ def get_python_path(): except ModuleNotFoundError: raise Exception("installing google RPC generated code failed") -print("google RPC code compiled") +print("google RPC code compiled") \ No newline at end of file diff --git a/src/main/resources/csv/read_csv.py b/src/main/resources/csv/read_csv.py index 46e5ca9..d88f916 100644 --- a/src/main/resources/csv/read_csv.py +++ b/src/main/resources/csv/read_csv.py @@ -28,6 +28,9 @@ ### import os import bpy + +bpy.ops.preferences.addon_enable(module='mastodon_blender_view') + import pandas as pd import csv from collections import defaultdict From 3110ba62150c7c117b996cef3408fe198cdf7e60 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Thu, 15 Jan 2026 16:20:44 +0100 Subject: [PATCH 2/3] Refactor install_addon.py --- .../blender-scripts/install_addon.py | 109 ++++++++++-------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/src/main/resources/blender-scripts/install_addon.py b/src/main/resources/blender-scripts/install_addon.py index 8e04879..f24c72f 100644 --- a/src/main/resources/blender-scripts/install_addon.py +++ b/src/main/resources/blender-scripts/install_addon.py @@ -33,70 +33,81 @@ import sys import addon_utils -# install addon -print("start installing mastodon blender view addon...") -argv = sys.argv -addon_zip = argv[argv.index('--') + 1] -bpy.ops.preferences.addon_install(filepath=addon_zip) -print("mastodon blender view addon installed") +def main(): + print("start installing mastodon blender view addon...") + install_mastodon_blender_view_addon() + print("mastodon blender view addon installed") + print("install pip if needed...") + install_pip() + print("pip is installed") + addon_dir = get_mastodon_blender_view_addon_dir() + print(f"Installation directory of the Mastodon Blender View addon: {addon_dir}") + install_dependencies(packages=['grpcio', 'bidict', 'grpcio-tools', 'pandas'], target=os.path.join(addon_dir, "libs")) + import_dependencies() + print("dependencies installed") + compile_grpc_protocol(addon_dir) + print("google RPC code compiled") -# install pip -os.environ.pop("PIP_REQ_TRACKER", None) -ensurepip.bootstrap() -os.environ.pop("PIP_REQ_TRACKER", None) +def install_mastodon_blender_view_addon(): + argv = sys.argv + addon_zip = argv[argv.index('--') + 1] + bpy.ops.preferences.addon_install(filepath=addon_zip) -# get python path -def get_python_path(): - try: - # 2.92 and older - path = bpy.app.binary_path_python - except AttributeError: - # 2.93 and later - path = sys.executable - return os.path.abspath(path) +def install_pip(): + os.environ.pop("PIP_REQ_TRACKER", None) + ensurepip.bootstrap() + os.environ.pop("PIP_REQ_TRACKER", None) -python_path = get_python_path() -print(f"Blender python binary: {python_path}") +def get_mastodon_blender_view_addon_dir() -> str: + filename_init_py = [m.__file__ for m in addon_utils.modules() if m.__name__ == "mastodon_blender_view"][0] + addon_dir = os.path.dirname(filename_init_py) + return addon_dir -# get installation path of "mastodon_blender_view" folder -filename_init_py = [m.__file__ for m in addon_utils.modules() if m.__name__ == "mastodon_blender_view"][0] -addon_dir = os.path.dirname(filename_init_py) -print(f"Installation directory of the Blender Mastodon plugin: {addon_dir}") +def install_dependencies(packages: list[str], target: str): + print(f"Install dependencies into folder: {target}") -# install dependencies + subprocess.check_call([get_python_path(), "-m", "pip", "install", "--target", target, *packages]) -addon_libs_dir = os.path.join(addon_dir, "libs") -print(f"Install dependencies into folder: {addon_libs_dir}") + if target not in sys.path: # add addon libs dir to system path + sys.path.insert(0, target) -packages = ['grpcio', 'bidict', 'grpcio-tools', 'pandas'] -subprocess.check_call([python_path, "-m", "pip", "install", "--target", addon_libs_dir, *packages]) -if addon_libs_dir not in sys.path: # add addon libs dir to system path - sys.path.insert(0, addon_libs_dir) +def import_dependencies(): + import bidict + import grpc + import google.protobuf + import pandas -import bidict -import grpc -import google.protobuf -import pandas -print("dependencies installed") +def compile_grpc_protocol(addon_dir): + cmd = [get_python_path(), '-m', 'grpc_tools.protoc', '-I.', '--python_out=.', '--grpc_python_out=.', + 'mastodon_blender_view/mastodon-blender-view.proto'] + cwd = os.path.dirname(addon_dir) + env = os.environ.copy() + addon_libs_dir = os.path.join(addon_dir, "libs") + env["PYTHONPATH"] = addon_libs_dir + os.pathsep + env.get("PYTHONPATH") if "PYTHONPATH" in env else addon_libs_dir + subprocess.check_output(cmd, cwd=cwd, env=env) + + try: + from mastodon_blender_view import mastodon_blender_view_pb2 + from mastodon_blender_view import mastodon_blender_view_pb2_grpc + except ModuleNotFoundError: + raise Exception("installing google RPC generated code failed") -# install google rpc protocol -cmd = [python_path, '-m', 'grpc_tools.protoc', '-I.', '--python_out=.', '--grpc_python_out=.', 'mastodon_blender_view/mastodon-blender-view.proto'] -cwd = path.dirname(addon_dir) -env = os.environ.copy() -env["PYTHONPATH"] = addon_libs_dir + os.pathsep + env.get("PYTHONPATH") if "PYTHONPATH" in env else addon_libs_dir -subprocess.check_output(cmd, cwd=os.cwd, env=env ) -try: - from mastodon_blender_view import mastodon_blender_view_pb2 - from mastodon_blender_view import mastodon_blender_view_pb2_grpc -except ModuleNotFoundError: - raise Exception("installing google RPC generated code failed") +def get_python_path(): + try: + # 2.92 and older + path = bpy.app.binary_path_python + except AttributeError: + # 2.93 and later + path = sys.executable + return os.path.abspath(path) + -print("google RPC code compiled") \ No newline at end of file +main() \ No newline at end of file From 5ceb036415182e4a89cb79ecee799bd54f1f823c Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Thu, 15 Jan 2026 16:27:25 +0100 Subject: [PATCH 3/3] Add comments --- blender-addon/__init__.py | 8 +++----- src/main/resources/csv/read_csv.py | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/blender-addon/__init__.py b/blender-addon/__init__.py index cc489d4..fa82451 100644 --- a/blender-addon/__init__.py +++ b/blender-addon/__init__.py @@ -38,10 +38,8 @@ } -def add_addon_libs_dir(): - """ - Add the dependencies of the Mastodon Blender View plugin to the pythons system path. - """ +def _add_dependencies_to_sys_path(): + """ Add the dependencies of Mastodon Blender View addon to the pythons system path. """ import sys import os addon_dir = os.path.dirname(__file__) @@ -50,7 +48,7 @@ def add_addon_libs_dir(): sys.path.insert(0, addon_libs_dir) -add_addon_libs_dir() +_add_dependencies_to_sys_path() from . import mb_panel from . import mb_server diff --git a/src/main/resources/csv/read_csv.py b/src/main/resources/csv/read_csv.py index d88f916..08e36e7 100644 --- a/src/main/resources/csv/read_csv.py +++ b/src/main/resources/csv/read_csv.py @@ -29,7 +29,7 @@ import os import bpy -bpy.ops.preferences.addon_enable(module='mastodon_blender_view') +bpy.ops.preferences.addon_enable(module='mastodon_blender_view') # "pandas" can only be imported after enabling the addon import pandas as pd import csv