From 3a3e664ae2a30f91245e43b94ec7c2aef3812d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 13 Jul 2023 12:07:02 +0200 Subject: [PATCH] Add option to make a job setting auto-evaluatable by the user Add a new job setting option `autoevalLockable`. Setting this to `true` in the job compiler's `JOB_TYPE` settings has the following effect: - By default, the setting will not be editable in Blender's job submission interface. Instead, a toggle button with a 'car' icon will be shown. - When the 'car' button is toggled off, the setting becomes editable again. In its default, uneditable state, the setting will be auto-evaluated before submission. This makes it possible to 'lock in' auto-evaluation. The main use case is for the frame range of the render job. By default this will be locked to the scene frame range, but it can still be overridden if a different range is wanted. --- addon/flamenco/gui.py | 30 ++++++++++++++++- addon/flamenco/job_types.py | 24 ++++++++++++++ addon/flamenco/job_types_propgroup.py | 32 +++++++++++++++++-- .../scripts/simple_blender_render.js | 1 + 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/addon/flamenco/gui.py b/addon/flamenco/gui.py index d9697f75..fc913044 100644 --- a/addon/flamenco/gui.py +++ b/addon/flamenco/gui.py @@ -95,8 +95,12 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel): return row = layout.row(align=True) + if setting.get("editable", True): - self.draw_setting_editable(row, propgroup, setting) + if job_types.setting_can_autoeval(setting): + self.draw_setting_autoeval(row, propgroup, setting) + else: + self.draw_setting_editable(row, propgroup, setting) else: self.draw_setting_readonly(context, row, propgroup, setting) @@ -132,6 +136,30 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel): ) -> None: layout.prop(propgroup, setting.key) + def draw_setting_autoeval( + self, + layout: bpy.types.UILayout, + propgroup: JobTypePropertyGroup, + setting: _AvailableJobSetting, + ) -> None: + autoeval_enabled = job_types.setting_should_autoeval(propgroup, setting) + if autoeval_enabled: + label = propgroup.bl_rna.properties[setting.key].name + layout.prop( + propgroup, + job_types.setting_autoeval_propname(setting), + text=label, + icon="AUTO", + ) + else: + self.draw_setting_editable(layout, propgroup, setting) + layout.prop( + propgroup, + job_types.setting_autoeval_propname(setting), + text="", + icon="AUTO", + ) + def draw_flamenco_status( self, context: bpy.types.Context, layout: bpy.types.UILayout ) -> None: diff --git a/addon/flamenco/job_types.py b/addon/flamenco/job_types.py index db60e8a7..fe34411f 100644 --- a/addon/flamenco/job_types.py +++ b/addon/flamenco/job_types.py @@ -68,6 +68,30 @@ def setting_is_visible(setting: _AvailableJobSetting) -> bool: return str(visibility) in {"visible", "submission"} +def setting_should_autoeval( + propgroup: job_types_propgroup.JobTypePropertyGroup, + setting: _AvailableJobSetting, +) -> bool: + if not setting_is_visible(setting): + # Invisible settings are there purely to be auto-evaluated. + return True + + propname = setting_autoeval_propname(setting) + return getattr(propgroup, propname, False) + + +def setting_can_autoeval(setting: _AvailableJobSetting) -> bool: + # Note that this uses the Pythonified name; that's done by the OpenAPI code generator. + can: bool = setting.get("autoeval_lockable", False) + print(f"setting_can_autoeval({setting.key}: {can})") + return can + + +def setting_autoeval_propname(setting: _AvailableJobSetting) -> str: + """Return the property name of the 'auto-eval' state for this setting.""" + return f"autoeval_{setting.key}" + + def _store_available_job_types(available_job_types: _AvailableJobTypes) -> None: global _available_job_types global _job_type_enum_items diff --git a/addon/flamenco/job_types_propgroup.py b/addon/flamenco/job_types_propgroup.py index 348d71e6..84e42c36 100644 --- a/addon/flamenco/job_types_propgroup.py +++ b/addon/flamenco/job_types_propgroup.py @@ -131,8 +131,7 @@ class JobTypePropertyGroup: setting value. Otherwise the default is used. """ for setting in self.job_type.settings: - if job_types.setting_is_visible(setting): - # Skip those settings that will be visible in the GUI. + if not job_types.setting_should_autoeval(self, setting): continue setting_eval = setting.get("eval", "") @@ -253,10 +252,16 @@ def generate(job_type: _AvailableJobType) -> type[JobTypePropertyGroup]: ) pg_type.__annotations__ = {} + # Add RNA properties for the settings. for setting in job_type.settings: prop = _create_property(job_type, setting) pg_type.__annotations__[setting.key] = prop + if job_types.setting_can_autoeval(setting): + # Add RNA property for the 'auto-eval' toggle. + propname, prop = _create_autoeval_property(setting) + pg_type.__annotations__[propname] = prop + assert issubclass(pg_type, JobTypePropertyGroup), "did not expect type %r" % type( pg_type ) @@ -304,6 +309,29 @@ def _create_property(job_type: _AvailableJobType, setting: _AvailableJobSetting) return prop +def _create_autoeval_property( + setting: _AvailableJobSetting, +) -> tuple[str, Any]: + from flamenco.manager.model.available_job_setting import AvailableJobSetting + + assert isinstance(setting, AvailableJobSetting) + + setting_name = _job_setting_key_to_label(setting.key) + prop_descr = ( + "Automatically determine the value for %r when the job gets submitted" + % setting_name + ) + + prop = bpy.props.BoolProperty( + name="Auto Evaluate %s" % setting_name, + description=prop_descr, + default=True, + ) + + prop_name = job_types.setting_autoeval_propname(setting) + return prop_name, prop + + def _find_prop_type( job_type: _AvailableJobType, setting: _AvailableJobSetting ) -> tuple[Any, dict[str, Any]]: diff --git a/internal/manager/job_compilers/scripts/simple_blender_render.js b/internal/manager/job_compilers/scripts/simple_blender_render.js index d980991d..411477e4 100644 --- a/internal/manager/job_compilers/scripts/simple_blender_render.js +++ b/internal/manager/job_compilers/scripts/simple_blender_render.js @@ -5,6 +5,7 @@ const JOB_TYPE = { settings: [ // Settings for artists to determine: { key: "frames", type: "string", required: true, eval: "f'{C.scene.frame_start}-{C.scene.frame_end}'", + autoevalLockable: true, description: "Frame range to render. Examples: '47', '1-30', '3, 5-10, 47-327'" }, { key: "chunk_size", type: "int32", default: 1, description: "Number of frames to render in one Blender render task", visible: "submission" },