From 7c6c48b4b5f418a3a594e805cce823fd42c59921 Mon Sep 17 00:00:00 2001 From: Isamu Mogi Date: Sat, 20 Jul 2024 05:34:07 +0900 Subject: [PATCH] feat: show add-on compatibility warning --- src/io_scene_vrm/common/ops/vrm.py | 16 +++++- src/io_scene_vrm/editor/handler.py | 1 + src/io_scene_vrm/editor/migration.py | 76 +++++++++++++++++++++++++--- src/io_scene_vrm/editor/ops.py | 60 ++++++++++++---------- src/io_scene_vrm/locale/ja_jp.py | 24 ++++++--- src/io_scene_vrm/registration.py | 2 +- 6 files changed, 136 insertions(+), 43 deletions(-) diff --git a/src/io_scene_vrm/common/ops/vrm.py b/src/io_scene_vrm/common/ops/vrm.py index 44da1d015..a16af0d9d 100644 --- a/src/io_scene_vrm/common/ops/vrm.py +++ b/src/io_scene_vrm/common/ops/vrm.py @@ -23,19 +23,31 @@ def model_validate( ) -def show_blend_file_vrm_addon_compatibility_warning( +def show_blend_file_addon_compatibility_warning( execution_context: str = "EXEC_DEFAULT", + /, + *, + file_addon_version: str = "", + installed_addon_version: str = "", ) -> set[str]: - return bpy.ops.vrm.show_blend_file_vrm_addon_compatibility_warning( # type: ignore[attr-defined, no-any-return] + return bpy.ops.vrm.show_blend_file_addon_compatibility_warning( # type: ignore[attr-defined, no-any-return] execution_context, + file_addon_version=file_addon_version, + installed_addon_version=installed_addon_version, ) def show_blend_file_compatibility_warning( execution_context: str = "EXEC_DEFAULT", + /, + *, + file_version: str = "", + app_version: str = "", ) -> set[str]: return bpy.ops.vrm.show_blend_file_compatibility_warning( # type: ignore[attr-defined, no-any-return] execution_context, + file_version=file_version, + app_version=app_version, ) diff --git a/src/io_scene_vrm/editor/handler.py b/src/io_scene_vrm/editor/handler.py index 9ecdee1aa..b203c24d5 100644 --- a/src/io_scene_vrm/editor/handler.py +++ b/src/io_scene_vrm/editor/handler.py @@ -6,3 +6,4 @@ @persistent def load_post(_unsed: object) -> None: migration.state.blend_file_compatibility_warning_shown = False + migration.state.blend_file_addon_compatibility_warning_shown = False diff --git a/src/io_scene_vrm/editor/migration.py b/src/io_scene_vrm/editor/migration.py index bd42c222c..645ea08e5 100644 --- a/src/io_scene_vrm/editor/migration.py +++ b/src/io_scene_vrm/editor/migration.py @@ -29,6 +29,7 @@ @dataclass class State: blend_file_compatibility_warning_shown: bool = False + blend_file_addon_compatibility_warning_shown: bool = False state: Final = State() @@ -124,7 +125,8 @@ def migrate_all_objects( context, context.scene.name ) mtoon1_migration.migrate(context) - validate_blend_file_version_compatibility(context) + validate_blend_file_compatibility(context) + validate_blend_file_addon_compatibility(context) preferences = get_preferences(context) @@ -137,7 +139,7 @@ def migrate_all_objects( preferences.addon_version = updated_addon_version -def validate_blend_file_version_compatibility(context: Context) -> None: +def validate_blend_file_compatibility(context: Context) -> None: """新しいBlenderで作成されたファイルを古いBlenderで編集しようとした場合に警告をする. アドオンの対応バージョンの事情で新しいBlenderで編集されたファイルを古いBlenderで編集しようとし、 @@ -156,23 +158,83 @@ def validate_blend_file_version_compatibility(context: Context) -> None: if blend_file_major_minor_version <= current_major_minor_version: return + file_version_str = ".".join(map(str, blend_file_major_minor_version)) + app_version_str = ".".join(map(str, current_major_minor_version)) + logger.error( "Opening incompatible file: file_blender_version=%s running_blender_version=%s", - context.blend_data.version, - bpy.app.version, + file_version_str, + app_version_str, + ) + + if not state.blend_file_compatibility_warning_shown: + state.blend_file_compatibility_warning_shown = True + # Blender 4.2.0ではtimerで実行しないとダイアログが自動で消えるのでタイマーを使う + bpy.app.timers.register( + functools.partial( + show_blend_file_compatibility_warning, + file_version_str, + app_version_str, + ), + first_interval=0.1, + ) + + +def show_blend_file_compatibility_warning(file_version: str, app_version: str) -> None: + ops.vrm.show_blend_file_compatibility_warning( + "INVOKE_DEFAULT", + file_version=file_version, + app_version=app_version, + ) + + +def validate_blend_file_addon_compatibility(context: Context) -> None: + """新しいVRMアドオンで作成されたファイルを古いVRMアドオンで編集しようとした場合に警告をする.""" + if not context.blend_data.filepath: + return + installed_addon_version = addon_version() + + # TODO: これはSceneあたりにバージョンを生やしたほうが良いかも + up_to_date = True + file_addon_version: tuple[int, ...] = (0, 0, 0) + for armature in context.blend_data.armatures: + file_addon_version = tuple(get_armature_extension(armature).addon_version) + if file_addon_version > installed_addon_version: + up_to_date = False + break + if up_to_date: + return + + file_addon_version_str = ".".join(map(str, file_addon_version)) + installed_addon_version_str = ".".join(map(str, installed_addon_version)) + + logger.error( + "Opening incompatible VRM add-on version: file=%s installed=%s", + file_addon_version_str, + installed_addon_version_str, ) if not state.blend_file_compatibility_warning_shown: state.blend_file_compatibility_warning_shown = True # Blender 4.2.0ではtimerで実行しないとダイアログが自動で消えるのでタイマーを使う bpy.app.timers.register( - show_blend_file_compatibility_warning, + functools.partial( + show_blend_file_addon_compatibility_warning, + file_addon_version_str, + installed_addon_version_str, + ), first_interval=0.1, ) -def show_blend_file_compatibility_warning() -> None: - ops.vrm.show_blend_file_compatibility_warning("INVOKE_DEFAULT") +def show_blend_file_addon_compatibility_warning( + file_addon_version: str, installed_addon_version: str +) -> None: + ops.vrm.show_blend_file_addon_compatibility_warning( + "INVOKE_DEFAULT", + file_addon_version=file_addon_version, + installed_addon_version=installed_addon_version, + ) def have_vrm_model(context: Context) -> bool: diff --git a/src/io_scene_vrm/editor/ops.py b/src/io_scene_vrm/editor/ops.py index 299b3e943..6f392b1a8 100644 --- a/src/io_scene_vrm/editor/ops.py +++ b/src/io_scene_vrm/editor/ops.py @@ -358,28 +358,25 @@ class VRM_OT_show_blend_file_compatibility_warning(Operator): bl_description = "Show Blend File Compatibility Warning" bl_options: AbstractSet[str] = {"REGISTER"} + file_version: StringProperty(options={"HIDDEN"}) # type: ignore[valid-type] + app_version: StringProperty(options={"HIDDEN"}) # type: ignore[valid-type] + def execute(self, _context: Context) -> set[str]: return {"FINISHED"} def invoke(self, context: Context, _event: Event) -> set[str]: return context.window_manager.invoke_props_dialog(self, width=500) - def draw(self, context: Context) -> None: - app_version = str(bpy.app.version[0]) + "." + str(bpy.app.version[1]) - file_version = ( - str(context.blend_data.version[0]) - + "." - + str(context.blend_data.version[1]) - ) + def draw(self, _context: Context) -> None: column = self.layout.row(align=True).column() text = pgettext( "The current file is not compatible with the running Blender.\n" - + "The file was created in Blender {file_version}, but the running Blender" - + " version is {app_version}.\n" + + "The current file was created in Blender {file_version}, but the running" + + " Blender version is {app_version}.\n" + "So it is not compatible. As a result some data may be lost or corrupted." ).format( - app_version=app_version, - file_version=file_version, + app_version=self.app_version, + file_version=self.file_version, ) description_outer_column = column.column() description_outer_column.emboss = "NONE" @@ -395,35 +392,40 @@ def draw(self, context: Context) -> None: ) open_url.url = "https://developer.blender.org/docs/handbook/guidelines/compatibility_handling_for_blend_files/#forward-compatibility" + if TYPE_CHECKING: + # This code is auto generated. + # `poetry run python tools/property_typing.py` + file_version: str # type: ignore[no-redef] + app_version: str # type: ignore[no-redef] + -class VRM_OT_show_blend_file_vrm_addon_compatibility_warning(Operator): - bl_idname = "vrm.show_blend_file_vrm_addon_compatibility_warning" +class VRM_OT_show_blend_file_addon_compatibility_warning(Operator): + bl_idname = "vrm.show_blend_file_addon_compatibility_warning" bl_label = "VRM Add-on Compatibility Warning" bl_description = "Show Blend File and VRM Add-on Compatibility Warning" bl_options: AbstractSet[str] = {"REGISTER"} + file_addon_version: StringProperty(options={"HIDDEN"}) # type: ignore[valid-type] + installed_addon_version: StringProperty(options={"HIDDEN"}) # type: ignore[valid-type] + def execute(self, _context: Context) -> set[str]: return {"FINISHED"} def invoke(self, context: Context, _event: Event) -> set[str]: return context.window_manager.invoke_props_dialog(self, width=500) - def draw(self, context: Context) -> None: - app_version = str(bpy.app.version[0]) + "." + str(bpy.app.version[1]) - file_version = ( - str(context.blend_data.version[0]) - + "." - + str(context.blend_data.version[1]) - ) + def draw(self, _context: Context) -> None: column = self.layout.row(align=True).column() text = pgettext( - "The current file is not compatible with the current VRM Add-on.\n" - + "The file was created in VRM Add-on {file_version}, but the current" - + " VRM Add-on version is {app_version}.\n" - + "So it is not compatible. As a result some data may be lost or corrupted." + "The current file is not compatible with the installed VRM Add-on.\n" + + "The current file was created in VRM Add-on {file_addon_version}, but the" + + " installed\n" + + "VRM Add-on version is {installed_addon_version}. So it is not" + + " compatible. As a result some\n" + + "data may be lost or corrupted." ).format( - current_version=app_version, - file_version=file_version, + file_addon_version=self.file_addon_version, + installed_addon_version=self.installed_addon_version, ) description_outer_column = column.column() description_outer_column.emboss = "NONE" @@ -432,6 +434,12 @@ def draw(self, context: Context) -> None: icon = "ERROR" if i == 0 else "NONE" description_column.label(text=line, translate=False, icon=icon) + if TYPE_CHECKING: + # This code is auto generated. + # `poetry run python tools/property_typing.py` + file_addon_version: str # type: ignore[no-redef] + installed_addon_version: str # type: ignore[no-redef] + __Operator = TypeVar("__Operator", bound=Operator) diff --git a/src/io_scene_vrm/locale/ja_jp.py b/src/io_scene_vrm/locale/ja_jp.py index 2e8cb3e9b..c4de55f68 100644 --- a/src/io_scene_vrm/locale/ja_jp.py +++ b/src/io_scene_vrm/locale/ja_jp.py @@ -597,8 +597,8 @@ ( "*", "The current file is not compatible with the running Blender.\n" - + "The file was created in Blender {file_version}, but the running Blender" - + " version is {app_version}.\n" + + "The current file was created in Blender {file_version}, but the running" + + " Blender version is {app_version}.\n" + "So it is not compatible. As a result some data may be lost or corrupted.", ): "現在のファイルは実行中のBlenderと互換性がありません。\n" + "現在のファイルはBlender {file_version}で作られたファイルですが、" @@ -609,9 +609,19 @@ ("VrmAddon", "Open Documentation"): "関連ドキュメントを開く", ( "*", - "The current file is not compatible with the current VRM Add-on.\n" - + "The file was created in VRM Add-on {file_version}, but the current" - + " VRM Add-on version is {app_version}.\n" - + "So it is not compatible. As a result some data may be lost or corrupted.", - ): "VRMアドオンのバージョンが合ってないっす {file_version} vs {app_version}", + "The current file is not compatible with the installed VRM Add-on.\n" + + "The current file was created in VRM Add-on {file_addon_version}, but the" + + " installed\n" + + "VRM Add-on version is {installed_addon_version}. So it is not" + + " compatible. As a result some\n" + + "data may be lost or corrupted.", + ): "現在のファイルはインストール済みのVRMアドオンと互換性がありません。\n" + + "現在のファイルはVRMアドオンのバージョン{file_addon_version}で作られた" + + "ファイルですが、\nインストール済みのVRMアドオンのバージョンは" + + "{installed_addon_version}のため互換性がありません。\n" + + "そのため、一部のデータが消えたり壊れたりすることがあります。", + ( + "Operator", + "VRM Add-on Compatibility Warning", + ): "VRMアドオンの互換性の警告", } diff --git a/src/io_scene_vrm/registration.py b/src/io_scene_vrm/registration.py index 9332f34f0..ed1852c6e 100644 --- a/src/io_scene_vrm/registration.py +++ b/src/io_scene_vrm/registration.py @@ -419,7 +419,7 @@ def save_pre(_unused: object) -> None: ops.VRM_OT_save_human_bone_mappings, ops.VRM_OT_load_human_bone_mappings, ops.VRM_OT_show_blend_file_compatibility_warning, - ops.VRM_OT_show_blend_file_vrm_addon_compatibility_warning, + ops.VRM_OT_show_blend_file_addon_compatibility_warning, validation.VrmValidationError, validation.WM_OT_vrm_validator, export_scene.WM_OT_vrm_export_human_bones_assignment,