Skip to content

Commit

Permalink
Allow controlling which actions are applied in previews
Browse files Browse the repository at this point in the history
  • Loading branch information
khalim19 committed Oct 28, 2022
1 parent a108c5d commit 33962be
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 38 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,8 @@
3.3.4 (upcoming)
================

* Added option to not apply a procedure for previews. This is useful for very slow procedures having little effect on the exported image if you want to keep the preview updated automatically, or procedures that e.g. manipulate the file system (such as export procedures) to avoid saving previewed images.

3.3.3
=====

Expand Down
21 changes: 17 additions & 4 deletions export_layers/actions.py
Expand Up @@ -246,15 +246,13 @@ def _get_values_from_actions(added_data_values_setting, added_actions_group):
added_data_values_setting.reset()

for setting in added_actions_group.walk():
added_data_values_setting.value[
setting.get_path(added_actions_group)] = setting.value
added_data_values_setting.value[setting.get_path(added_actions_group)] = setting.value


def _set_values_for_actions(added_data_values_setting, added_actions_group):
for setting in added_actions_group.walk():
if setting.get_path(added_actions_group) in added_data_values_setting.value:
setting.set_value(
added_data_values_setting.value[setting.get_path(added_actions_group)])
setting.set_value(added_data_values_setting.value[setting.get_path(added_actions_group)])


def _create_action(
Expand All @@ -266,6 +264,8 @@ def _create_action(
description=None,
action_groups=None,
tags=None,
more_options_expanded=False,
enabled_for_previews=True,
**custom_fields):

def _set_display_name_for_enabled_gui(setting_enabled, setting_display_name):
Expand Down Expand Up @@ -323,6 +323,19 @@ def _set_display_name_for_enabled_gui(setting_enabled, setting_display_name):
'default_value': action_groups,
'gui_type': None,
},
{
"type": pg.SettingTypes.boolean,
"name": 'more_options_expanded',
"default_value": more_options_expanded,
"display_name": _('_More options'),
"gui_type": pg.SettingGuiTypes.expander,
},
{
"type": pg.SettingTypes.boolean,
"name": 'enabled_for_previews',
"default_value": enabled_for_previews,
"display_name": _('Enable for previews'),
},
])

orig_name_value = custom_fields.pop('orig_name', name)
Expand Down
59 changes: 39 additions & 20 deletions export_layers/exportlayers.py
Expand Up @@ -95,6 +95,8 @@ def __init__(

self._layer_tree = layer_tree

self._is_preview = False

self.export_context_manager = (
export_context_manager if export_context_manager is not None
else pg.utils.EmptyContext)
Expand Down Expand Up @@ -142,6 +144,10 @@ def __init__(
def layer_tree(self):
return self._layer_tree

@property
def is_preview(self):
return self._is_preview

@property
def exported_layers(self):
return self._exported_layers
Expand Down Expand Up @@ -174,7 +180,8 @@ def file_extension_properties(self):
def invoker(self):
return self._invoker

def export(self, processing_groups=None, layer_tree=None, keep_image_copy=False):
def export(
self, processing_groups=None, layer_tree=None, keep_image_copy=False, is_preview=False):
"""
Export layers as separate images from the specified image.
Expand Down Expand Up @@ -205,8 +212,12 @@ def export(self, processing_groups=None, layer_tree=None, keep_image_copy=False)
copy, pass `True` to `keep_image_copy`. In that case, this method returns
the image copy. If an exception was raised or if no layer was exported, this
method returns `None` and the image copy will be destroyed.
If `is_preview` is `True`, only procedures and constraints that are marked
as enabled for previews will be applied for previews. This has no effect
during real export.
"""
self._init_attributes(processing_groups, layer_tree, keep_image_copy)
self._init_attributes(processing_groups, layer_tree, keep_image_copy, is_preview)
self._preprocess_layers()

exception_occurred = False
Expand Down Expand Up @@ -277,7 +288,7 @@ def reorder_action(self, *args, **kwargs):
"""
self._initial_invoker.reorder(*args, **kwargs)

def _init_attributes(self, processing_groups, layer_tree, keep_image_copy):
def _init_attributes(self, processing_groups, layer_tree, keep_image_copy, is_preview):
self._invoker = pg.invoker.Invoker()
self._add_actions()
self._add_name_only_actions()
Expand All @@ -291,6 +302,7 @@ def _init_attributes(self, processing_groups, layer_tree, keep_image_copy):
self.image, name=pg.config.SOURCE_NAME, is_filtered=True)

self._keep_image_copy = keep_image_copy
self._is_preview = is_preview

self._should_stop = False

Expand Down Expand Up @@ -336,21 +348,19 @@ def _add_actions(self):
self._initial_invoker.list_groups(include_empty_groups=True))

for procedure in actions.walk(self.export_settings['procedures']):
add_action_from_settings(procedure, self._invoker)
add_action_from_settings(procedure, self)

for constraint in actions.walk(self.export_settings['constraints']):
add_action_from_settings(constraint, self._invoker)
add_action_from_settings(constraint, self)

def _add_name_only_actions(self):
for procedure in actions.walk(self.export_settings['procedures']):
add_action_from_settings(
procedure, self._invoker,
[builtin_procedures.NAME_ONLY_TAG], [self._NAME_ONLY_ACTION_GROUP])
procedure, self, [builtin_procedures.NAME_ONLY_TAG], [self._NAME_ONLY_ACTION_GROUP])

for constraint in actions.walk(self.export_settings['constraints']):
add_action_from_settings(
constraint, self._invoker,
[builtin_procedures.NAME_ONLY_TAG], [self._NAME_ONLY_ACTION_GROUP])
constraint, self, [builtin_procedures.NAME_ONLY_TAG], [self._NAME_ONLY_ACTION_GROUP])

def _enable_disable_processing_groups(self, processing_groups):
for functions in self._processing_groups.values():
Expand Down Expand Up @@ -700,7 +710,7 @@ def _copy_non_modifying_parasites(src_image, dest_image):
_LAYER_EXPORTER_ARG_POSITION_IN_CONSTRAINTS = 1


def add_action_from_settings(action, invoker, tags=None, action_groups=None):
def add_action_from_settings(action, layer_exporter, tags=None, action_groups=None):
if action.get_value('is_pdb_procedure', False):
try:
function = pdb[pg.utils.safe_encode_gimp(action['function'].value)]
Expand Down Expand Up @@ -732,12 +742,12 @@ def add_action_from_settings(action, invoker, tags=None, action_groups=None):
if 'constraint' in action.tags:
function = _get_constraint_func(function, subfilter=action['subfilter'].value)

function = _apply_action_only_if_enabled(function, action['enabled'])
function = _apply_action_only_if_enabled(function, action, layer_exporter)

if action_groups is None:
action_groups = action['action_groups'].value

invoker.add(function, action_groups, function_args, function_kwargs)
layer_exporter.invoker.add(function, action_groups, function_args, function_kwargs)


def _has_run_mode_param(pdb_procedure):
Expand All @@ -760,14 +770,23 @@ def _action(image, layer, layer_exporter, *args, **kwargs):
return _action


def _apply_action_only_if_enabled(action, setting_enabled):
def _apply_action(*action_args, **action_kwargs):
if setting_enabled.value:
return action(*action_args, **action_kwargs)
else:
return False

return _apply_action
def _apply_action_only_if_enabled(function, action, layer_exporter):
if layer_exporter.is_preview:
def _apply_action_in_preview(*action_args, **action_kwargs):
if action['enabled'].value and action['enabled_for_previews'].value:
return function(*action_args, **action_kwargs)
else:
return False

return _apply_action_in_preview
else:
def _apply_action(*action_args, **action_kwargs):
if action['enabled'].value:
return function(*action_args, **action_kwargs)
else:
return False

return _apply_action


def _get_constraint_func(rule_func, subfilter=None):
Expand Down
23 changes: 19 additions & 4 deletions export_layers/gui/actions.py
Expand Up @@ -427,6 +427,9 @@ class _ActionEditDialog(gimpui.Dialog):

_PLACEHOLDER_WIDGET_HORIZONTAL_SPACING_BETWEEN_ELEMENTS = 5

_MORE_OPTIONS_SPACING = 4
_MORE_OPTIONS_BORDER_WIDTH = 4

def __init__(self, action, pdb_procedure, *args, **kwargs):
super().__init__(*args, **kwargs)

Expand Down Expand Up @@ -463,6 +466,14 @@ def __init__(self, action, pdb_procedure, *args, **kwargs):
self._table_action_arguments.set_row_spacings(self._TABLE_ROW_SPACING)
self._table_action_arguments.set_col_spacings(self._TABLE_COLUMN_SPACING)

self._vbox_more_options = gtk.VBox()
self._vbox_more_options.set_spacing(self._MORE_OPTIONS_SPACING)
self._vbox_more_options.set_border_width(self._MORE_OPTIONS_BORDER_WIDTH)
self._vbox_more_options.pack_start(
action['enabled_for_previews'].gui.element, expand=False, fill=False)

action['more_options_expanded'].gui.element.add(self._vbox_more_options)

# Put widgets in a custom `VBox` because the action area would otherwise
# have excessively thick borders for some reason.
self._vbox = gtk.VBox()
Expand All @@ -472,6 +483,7 @@ def __init__(self, action, pdb_procedure, *args, **kwargs):
if self._label_procedure_description is not None:
self._vbox.pack_start(self._label_procedure_description, expand=False, fill=False)
self._vbox.pack_start(self._table_action_arguments, expand=True, fill=True)
self._vbox.pack_start(action['more_options_expanded'].gui.element, expand=False, fill=False)

self.vbox.pack_start(self._vbox, expand=False, fill=False)

Expand All @@ -480,7 +492,7 @@ def __init__(self, action, pdb_procedure, *args, **kwargs):
self.set_focus(self._button_ok)

self._button_reset.connect('clicked', self._on_button_reset_clicked, action)
self.connect('response', self._on_action_edit_dialog_response)
self.connect('response', self._on_action_edit_dialog_response, action)

def _create_label_description(self, summary, full_description=None):
label_description = gtk.Label()
Expand Down Expand Up @@ -509,8 +521,7 @@ def _set_arguments(self, action, pdb_procedure):
if not isinstance(setting.gui, pg.setting.SettingGuiTypes.none):
if isinstance(setting, pg.setting.ArraySetting):
if setting.element_type.get_allowed_gui_types():
setting.gui.element.set_property(
'width-request', self._ARRAY_PARAMETER_GUI_WIDTH)
setting.gui.element.set_property('width-request', self._ARRAY_PARAMETER_GUI_WIDTH)
setting.gui.element.max_height = self._ARRAY_PARAMETER_GUI_MAX_HEIGHT
else:
gui_element_to_attach = self._create_placeholder_widget()
Expand All @@ -528,9 +539,13 @@ def _on_label_procedure_name_changed(self, editable_label, action):
editable_label.label.set_markup(
'<b>{}</b>'.format(gobject.markup_escape_text(editable_label.label.get_text())))

def _on_action_edit_dialog_response(self, dialog, response_id):
def _on_action_edit_dialog_response(self, dialog, response_id, action):
for child in list(self._table_action_arguments.get_children()):
self._table_action_arguments.remove(child)

self._vbox_more_options.remove(action['enabled_for_previews'].gui.element)
action['more_options_expanded'].gui.element.remove(self._vbox_more_options)
self._vbox.remove(action['more_options_expanded'].gui.element)

def _create_placeholder_widget(self):
hbox = gtk.HBox()
Expand Down
3 changes: 2 additions & 1 deletion export_layers/gui/preview_image.py
Expand Up @@ -347,7 +347,8 @@ def _get_image_preview(self):
image_preview = self._layer_exporter.export(
processing_groups=['layer_contents'],
layer_tree=layer_tree,
keep_image_copy=True)
keep_image_copy=True,
is_preview=True)
except Exception:
display_image_preview_failure_message(
details=traceback.format_exc(), parent=pg.gui.get_toplevel_window(self))
Expand Down
3 changes: 2 additions & 1 deletion export_layers/gui/preview_name.py
Expand Up @@ -516,7 +516,8 @@ def _process_items(self, reset_items=False):

self._layer_exporter.export(
processing_groups=['layer_name', 'layer_name_for_preview'],
layer_tree=layer_tree)
layer_tree=layer_tree,
is_preview=True)

def _update_items(self):
for layer_elem in self._layer_exporter.layer_tree:
Expand Down
9 changes: 3 additions & 6 deletions export_layers/gui/previews_controller.py
Expand Up @@ -206,8 +206,7 @@ def _on_enabled_changed(constraint_enabled):
'before-clear-actions', _before_clear_constraints)

def _connect_set_image_preview_scaling(self):
def _after_add_action(
actions, action, orig_action_dict, builtin_actions):
def _after_add_action(actions, action, orig_action_dict, builtin_actions):
if action['orig_name'].value not in builtin_actions:
self._custom_actions[action.name] = action

Expand All @@ -223,8 +222,7 @@ def _before_clear_actions(actions):
self._image_preview.set_scaling()

def _set_image_preview_scaling(action_enabled):
if not any(action['enabled'].value
for action in self._custom_actions.values()):
if not any(action['enabled'].value for action in self._custom_actions.values()):
self._image_preview.set_scaling()
else:
self._image_preview.set_scaling(['after_process_layer'], ['after_process_layer'])
Expand Down Expand Up @@ -255,8 +253,7 @@ def _connect_image_preview_menu_setting_changes(self):
self._settings['gui/image_preview_automatic_update'].connect_event(
'value-changed',
lambda setting, update_if_below_setting: update_if_below_setting.set_value(False),
self._settings[
'gui/image_preview_automatic_update_if_below_maximum_duration'])
self._settings['gui/image_preview_automatic_update_if_below_maximum_duration'])

def _connect_toplevel_notify_is_active(self):
toplevel = (
Expand Down
8 changes: 6 additions & 2 deletions export_layers/tests/test_exportlayers.py
Expand Up @@ -66,7 +66,11 @@ def test_add_procedure_added_procedure_is_first_in_action_list(self):
class TestAddActionFromSettings(unittest.TestCase):

def setUp(self):
self.layer_exporter_mock = mock.Mock()
self.invoker = pg.invoker.Invoker()

self.layer_exporter_mock.invoker = self.invoker

self.procedures = actions.create('procedures')

self.procedure_stub = stubs_gimp.PdbProcedureStub(
Expand All @@ -83,7 +87,7 @@ def test_add_action_from_settings(self):
procedure = actions.add(
self.procedures, builtin_procedures.BUILTIN_PROCEDURES['insert_background_layers'])

exportlayers.add_action_from_settings(procedure, self.invoker)
exportlayers.add_action_from_settings(procedure, self.layer_exporter_mock)

added_action_items = self.invoker.list_actions(
group=actions.DEFAULT_PROCEDURES_GROUP)
Expand All @@ -106,7 +110,7 @@ def _test_add_pdb_proc_as_action(self, pdb_procedure, expected_args, expected_kw
with mock.patch('export_layers.exportlayers.pdb') as pdb_mock:
pdb_mock.__getitem__.return_value = pdb_procedure

exportlayers.add_action_from_settings(procedure, self.invoker)
exportlayers.add_action_from_settings(procedure, self.layer_exporter_mock)

added_action_items = self.invoker.list_actions(
group=actions.DEFAULT_PROCEDURES_GROUP)
Expand Down

0 comments on commit 33962be

Please sign in to comment.