From 31e44bc245ab782a8680632a24b6995e6c9ef7b0 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 3 Aug 2024 16:54:28 -0500 Subject: [PATCH 1/2] feat: create manifest, fix typo --- compositor/operator.py | 2 ++ geometry/operator.py | 2 ++ ntp_operator.py | 28 +++++++++++++++++++++++++--- options.py | 25 +++++++++++++++++++++++++ shader/operator.py | 2 ++ utils.py | 3 +++ 6 files changed, 59 insertions(+), 3 deletions(-) diff --git a/compositor/operator.py b/compositor/operator.py index f7ff414..92ba873 100644 --- a/compositor/operator.py +++ b/compositor/operator.py @@ -241,6 +241,8 @@ def execute(self, context): self._create_register_func() self._create_unregister_func() self._create_main_func() + if bpy.app.version >= (4, 2, 0): + self._create_manifest() else: context.window_manager.clipboard = self._file.getvalue() diff --git a/geometry/operator.py b/geometry/operator.py index 508fd08..a19c098 100644 --- a/geometry/operator.py +++ b/geometry/operator.py @@ -211,6 +211,8 @@ def execute(self, context): self._create_register_func() self._create_unregister_func() self._create_main_func() + if bpy.app.version >= (4, 2, 0): + self._create_manifest() else: context.window_manager.clipboard = self._file.getvalue() self._file.close() diff --git a/ntp_operator.py b/ntp_operator.py index adee2b8..4968cda 100644 --- a/ntp_operator.py +++ b/ntp_operator.py @@ -132,6 +132,7 @@ def _setup_options(self, options: NTPOptions) -> bool: self._author_name = options.author_name self._version = options.version self._location = options.location + self._license = options.license self._category = options.category self._custom_category = options.custom_category if options.menu_id in dir(bpy.types): @@ -176,11 +177,12 @@ def _create_header(self, name: str) -> None: """ self._write("bl_info = {", "") + self._name = name if self._name_override and self._name_override != "": - name = self._name_override - self._write(f"\t\"name\" : {str_to_py_str(name)},", "") + self._name = self._name_override + self._write(f"\t\"name\" : {str_to_py_str(self._name)},", "") if self._description and self._description != "": - self.write(f"\t\"description\" : {str_to_py_str(self._description)}," "") + self._write(f"\t\"description\" : {str_to_py_str(self._description)}," "") self._write(f"\t\"author\" : {str_to_py_str(self._author_name)},", "") self._write(f"\t\"version\" : {vec3_to_py_str(self._version)},", "") self._write(f"\t\"blender\" : {bpy.app.version},", "") @@ -204,6 +206,7 @@ def _init_operator(self, idname: str, label: str) -> None: idname (str): name for the operator label (str): appearence inside Blender """ + self._idname = idname self._write(f"class {self._class_name}(bpy.types.Operator):", "") self._write(f"\tbl_idname = \"node.{idname}\"", "") self._write(f"\tbl_label = {str_to_py_str(label)}", "") @@ -1375,6 +1378,25 @@ def _create_main_func(self) -> None: self._write("if __name__ == \"__main__\":", "") self._write("register()", "\t") + if bpy.app.version >= (4, 2, 0): + def _create_manifest(self) -> None: + manifest = open(f"{self._addon_dir}/blender_manifest.toml", "w") + manifest.write("schema_version = \"1.0.0\"\n\n") + manifest.write(f"id = {str_to_py_str(self._idname)}\n") + + manifest.write(f"version = {version_to_manifest_str(self._version)}\n") + manifest.write(f"name = {str_to_py_str(self._name)}\n") + manifest.write(f"tagline = {str_to_py_str(self._description)}\n") + manifest.write(f"maintainer = {str_to_py_str(self._author_name)}\n") + manifest.write("type = \"add-on\"\n") + manifest.write(f"blender_version_min = {version_to_manifest_str(bpy.app.version)}\n") + if self._license != 'OTHER': + manifest.write(f"license = [{str_to_py_str(self._license)}]\n") + else: + self.report({'WARNING'}, "No license selected. Please add a license to the manifest file") + + manifest.close() + def _zip_addon(self) -> None: """ Zips up the addon and removes the directory diff --git a/options.py b/options.py index 47871df..2df6a81 100644 --- a/options.py +++ b/options.py @@ -35,6 +35,7 @@ class NTPOptions(bpy.types.PropertyGroup): description="Generate necessary import statements", default = True ) + # Addon properties dir_path : bpy.props.StringProperty( name = "Save Location", @@ -75,6 +76,26 @@ class NTPOptions(bpy.types.PropertyGroup): "hovering over the desired menu", default="NODE_MT_add" ) + license: bpy.props.EnumProperty( + name="License", + items = [ + ('SPDX:GPL-2.0-or-later', "GNU General Public License v2.0 or later", ""), + ('SPDX:GPL-3.0-or-later', "GNU General Public License v3.0 or later", ""), + ('SPDX:LGPL-2.1-or-later', "GNU Lesser General Public License v2.1 or later", ""), + ('SPDX:LGPL-3.0-or-later', "GNU Lesser General Public License v3.0 or later", ""), + ('SPDX:BSD-1-Clause', "BSD 1-Clause \"Simplified\" License", ""), + ('SPDX:BSD-2-Clause', "BSD 2-Clause \"Simplified\" License", ""), + ('SPDX:BSD-3-Clause', "BSD 3-Clause “New” or “Revised” License", ""), + ('SPDX:BSL-1.0', "Boost Software License 1.0", ""), + ('SPDX:MIT', "MIT License", ""), + ('SPDX:MIT-0', "MIT No Attribution", ""), + ('SPDX:MPL-2.0', "Mozilla Public License 2.0", ""), + ('SPDX:Pixar', "Pixar License", ""), + ('SPDX:Zlib', "Zlib License", ""), + ('OTHER', "Other", "") + ], + default = 'OTHER' + ) category: bpy.props.EnumProperty( name = "Category", items = [ @@ -117,6 +138,7 @@ class NTPOptions(bpy.types.PropertyGroup): description="Custom category", default = "" ) + class NTPOptionsPanel(bpy.types.Panel): bl_label = "Options" bl_idname = "NODE_PT_ntp_options" @@ -149,10 +171,13 @@ def draw(self, context): elif ntp_options.mode == 'ADDON': addon_options = [ "dir_path", + "name_override", + "description", "author_name", "version", "location", "menu_id", + "license", "category" ] option_list += addon_options diff --git a/shader/operator.py b/shader/operator.py index bd136fe..2825fe8 100644 --- a/shader/operator.py +++ b/shader/operator.py @@ -176,6 +176,8 @@ def execute(self, context): self._create_register_func() self._create_unregister_func() self._create_main_func() + if bpy.app.version >= (4, 2, 0): + self._create_manifest() else: context.window_manager.clipboard = self._file.getvalue() diff --git a/utils.py b/utils.py index cbf8b91..b7ddcae 100644 --- a/utils.py +++ b/utils.py @@ -88,6 +88,9 @@ def vec3_to_py_str(vec3) -> str: """ return f"({vec3[0]}, {vec3[1]}, {vec3[2]})" +def version_to_manifest_str(version) -> str: + return f"\"{version[0]}.{version[1]}.{version[2]}\"" + def vec4_to_py_str(vec4) -> str: """ Converts a 4D vector to a string usable by the add-on From 8fda601ff930f29696730b0ece3011971b2403c4 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 3 Aug 2024 16:58:02 -0500 Subject: [PATCH 2/2] fix: don't try to load invalid images --- ntp_operator.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ntp_operator.py b/ntp_operator.py index 4968cda..106f796 100644 --- a/ntp_operator.py +++ b/ntp_operator.py @@ -394,8 +394,8 @@ def _set_settings_defaults(self, node: Node) -> None: elif st == ST.IMAGE: if self._addon_dir is not None and attr is not None: if attr.source in {'FILE', 'GENERATED', 'TILED'}: - self._save_image(attr) - self._load_image(attr, f"{node_var}.{attr_name}") + if self._save_image(attr): + self._load_image(attr, f"{node_var}.{attr_name}") elif st == ST.IMAGE_USER: self._image_user_settings(attr, f"{node_var}.{attr_name}") elif st == ST.SIM_OUTPUT_ITEMS: @@ -782,8 +782,8 @@ def _set_input_defaults(self, node: Node) -> None: elif input.bl_idname == 'NodeSocketImage': img = input.default_value if img is not None and self._addon_dir != None: # write in a better way - self._save_image(img) - self._load_image(img, f"{socket_var}.default_value") + if self._save_image(img): + self._load_image(img, f"{socket_var}.default_value") default_val = None # materials @@ -1040,7 +1040,7 @@ def _node_tree_settings(self, node: Node, attr_name: str) -> None: self.report({'WARNING'}, (f"NodeToPython: Node tree dependency graph " f"wasn't properly initialized")) - def _save_image(self, img: bpy.types.Image) -> None: + def _save_image(self, img: bpy.types.Image) -> bool: """ Saves an image to an image directory of the add-on @@ -1049,13 +1049,13 @@ def _save_image(self, img: bpy.types.Image) -> None: """ if img is None: - return + return False img_str = img_to_py_str(img) if not img.has_data: self.report({'WARNING'}, f"{img_str} has no data") - return + return False # create image dir if one doesn't exist img_dir = os.path.join(self._addon_dir, IMAGE_DIR_NAME) @@ -1067,6 +1067,7 @@ def _save_image(self, img: bpy.types.Image) -> None: img_path = f"{img_dir}/{img_str}" if not os.path.exists(img_path): img.save_render(img_path) + return True def _load_image(self, img: bpy.types.Image, img_var: str) -> None: """