The idea of template modifying values that are remembered between invocations is interesting.
I think the action and the display should be separated. We run the action that iterates over selected books, and modify the global dict. After that the display can be handled by the formulas action, which already has access to the global dict. This way, any other action that uses templates, can be preceded by this new action (now that the display is optional and separate), and access whatever values it writes to the global dict.
All this action would need to do is loop over all selected books, and run the template for each one. Here is what I think can be a preliminary implementation for this action:
Code:
# python3 compatibility
from six import text_type as unicode
from calibre import prints
from calibre.constants import DEBUG
from calibre.gui2 import error_dialog
from calibre.ebooks.metadata.book.formatter import SafeFormat
from calibre_plugins.action_chains.actions.base import ChainAction
from calibre_plugins.action_chains.templates import TemplateBox, check_template, get_metadata_object, TEMPLATE_ERROR
class TemplateIteratorDialog(TemplateBox):
def __init__(self, parent, plugin_action, action, name, title):
self.plugin_action = plugin_action
self.action = action
self.gui = plugin_action.gui
self.db = self.gui.current_db
mi = get_metadata_object(self.gui)
TemplateBox.__init__(
self,
parent,
plugin_action,
template_text='',
placeholder_text=_('Enter a template here. It will be iterated over all selected books'),
mi=mi
)
self.setWindowTitle(title)
def load_settings(self, settings):
if settings:
template = settings['template']
self.textbox.insertPlainText(template)
def save_settings(self):
settings = {}
settings['template'] = unicode(self.textbox.toPlainText()).rstrip()
return settings
def accept(self):
self.settings = self.save_settings()
# validate settings
is_valid = self.action.validate(self.settings)
if is_valid is not True:
msg, details = is_valid
error_dialog(
self,
msg,
details,
show=True
)
return
TemplateBox.accept(self)
class TemplateIteratorAction(ChainAction):
name = 'Template Iterator'
def run_template_for_book(self, db, template, book_id, chain_loop):
mi = db.new_api.get_proxy_metadata(book_id)
template_functions = self.plugin_action.template_functions
template_output = SafeFormat().safe_format(template, mi, TEMPLATE_ERROR, mi,
global_vars=chain_loop.chain_vars,
template_functions=template_functions)
return template_output
def run(self, gui, settings, chain_loop):
db = gui.current_db
rows = gui.current_view().selectionModel().selectedRows()
book_ids = [ gui.library_view.model().db.id(row.row()) for row in rows ]
template = settings['template']
for book_id in book_ids:
template_output = self.run_template_for_book(db, template, book_id, chain_loop)
def config_widget(self):
return TemplateIteratorDialog
This action can be tested now as a custom action, before it is included in the plugin.
I still have to ponder on this for sometime, before jumping into final implementation, to get it right first. I am also struggling with name of this new action. Changing it later would break compatibility. Maybe someone can help.
Edit: Maybe make the display part of the same action, and make it optional instead. Not yet sure about this.