View Single Post
Old 06-15-2025, 04:48 AM   #100
Terisa de morgan
Grand Sorcerer
Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.Terisa de morgan ought to be getting tired of karma fortunes by now.
 
Terisa de morgan's Avatar
 
Posts: 6,636
Karma: 12595249
Join Date: Jun 2009
Location: Madrid, Spain
Device: Kobo Clara/Aura One/Forma,XiaoMI 5, iPad, Huawei MediaPad, YotaPhone 2
Quote:
Originally Posted by thiago.eec View Post
My modules:
  1. Show all editor actions [module name: editor_actions](by @capink)
    Spoiler:

    Code:
    #!/usr/bin/env python
    # ~*~ coding: utf-8 ~*~
    from __future__ import (unicode_literals, division, absolute_import,
                            print_function)
    
    import re
    
    # python 3 compatibility
    from six import text_type as unicode
    
    from PyQt5.Qt import (QApplication, Qt, QWidget, QGridLayout, QHBoxLayout, QVBoxLayout,
                          QDialog, QDialogButtonBox, QMenu, QPainter, QPoint, QPixmap,
                          QSize, QIcon, QTreeWidgetItem, QTreeWidget, QAbstractItemView,
                          QGroupBox, QCheckBox, QLabel, QAction)
    
    from calibre import prints
    from calibre.constants import DEBUG
    from calibre.gui2 import error_dialog
    
    from calibre_plugins.editor_chains.actions.base import EditorAction
    from calibre_plugins.editor_chains.common_utils import get_icon
    
    try:
        load_translations()
    except NameError:
        prints("EditorChains::actions/editor_actions.py - exception when loading translations")
    
    
    EXCLUDED_GROUPS = [
        _('Editor Chains'),
        _('Editor actions'),
        _('Windows'),
        _('Preview'),
        _('Search')
    ]
    
    ICON_SIZE = 32
    
    
    def group_unique_names(gui, group):
        menu_actions = [x for x in gui.__dict__.values() if isinstance(x, QAction)]
        menu_unique_names = [re.sub(r'^action\-', r'', x.objectName()) for x in menu_actions]
        if group == 'Plugins':
            unique_names = [x for x in gui.keyboard.groups['Plugins']]
        else:
            unique_names = [x for x in gui.keyboard.groups[group] if x in menu_unique_names]
        
        return unique_names
    
    def get_qaction_from_unique_name(gui, unique_name):
        shortcut = gui.keyboard.shortcuts[unique_name]
        ac = shortcut['action']
        return ac
    
    
    def allowed_unique_names(gui):
        unique_names = []
        for group in gui.keyboard.groups.keys():
            unique_names.extend(group_unique_names(gui, group))
        unique_names += [x for x in gui.keyboard.groups['Plugins']]
        return unique_names
    
    class Item(QTreeWidgetItem):
        pass
    
    class ConfigWidget(QWidget):
        def __init__(self, plugin_action):
            QWidget.__init__(self)
            self.plugin_action = plugin_action
            self.gui = plugin_action.gui
            self.all_nodes = {}
            # used to keep track of selected item and allow only one selection
            self.checked_item = None
            
            self._init_controls()
    
        def _init_controls(self):
            self.blank_icon = QIcon(I('blank.png'))
            l = self.l = QVBoxLayout()
            self.setLayout(l)
    
    #        opts_groupbox = QGroupBox()
    #        l.addWidget(opts_groupbox)
    #        opts_groupbox_layout = QVBoxLayout()
    #        opts_groupbox.setLayout(opts_groupbox_layout)
    #        cursor_chk = self.cursor_chk = QCheckBox(_('Disable wait cursor for this action.'))
    #        opts_groupbox_layout.addWidget(cursor_chk)
    #        cursor_chk.setChecked(False)
            
            self.tv = QTreeWidget(self.gui)
            self.tv.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
            self.tv.header().hide()
            self.tv.setSelectionMode(QAbstractItemView.SingleSelection)
            l.addWidget(self.tv, 1)
            self.tv.itemChanged.connect(self._tree_item_changed)
    
            self._populate_actions_tree()
    
        def _get_scaled_icon(self, icon):
            if icon.isNull():
                return self.blank_icon
            # We need the icon scaled to 16x16
            src = icon.pixmap(ICON_SIZE, ICON_SIZE)
            if src.width() == ICON_SIZE and src.height() == ICON_SIZE:
                return icon
            # Need a new version of the icon
            pm = QPixmap(ICON_SIZE, ICON_SIZE)
            pm.fill(Qt.transparent)
            p = QPainter(pm)
            p.drawPixmap(QPoint(int((ICON_SIZE - src.width()) / 2), int((ICON_SIZE - src.height()) / 2)), src)
            p.end()
            return QIcon(pm)
    
        def set_checked_state(self, item, col, state):
            item.setCheckState(col, state)
            if state == Qt.Checked:
                self.checked_item = item
            else:
                self.checked_item = None
    
        def _populate_actions_tree(self):
    
            self.top_level_items_map = {}
            for group in sorted(self.gui.keyboard.groups.keys()):
                if group in EXCLUDED_GROUPS: continue
                
                # Create a node for our top level plugin name
                tl = Item()
                tl.setText(0, group)
              
                # Normal top-level checkable plugin iaction handling
                tl.setFlags(Qt.ItemIsEnabled)
                
                #unique_names = self.gui.keyboard.groups[group]
                unique_names = group_unique_names(self.gui, group)
                
                # Iterate through all the children of this node
                self._populate_action_children(unique_names, tl)
                self.tv.addTopLevelItem(tl)
    
        def _populate_action_children(self, unique_names, parent):
            for unique_name in unique_names:
                ac = get_qaction_from_unique_name(self.gui, unique_name)
                text = ac.text().replace('&', '')
    
                it = Item(parent)
                it.setText(0, text)
                it.setData(0, Qt.UserRole, unique_name)
                it.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                it.setCheckState(0, Qt.Unchecked)
                it.setIcon(0, self._get_scaled_icon(ac.icon()))
    
                self.all_nodes[unique_name] = it
    
        def _tree_item_changed(self, item, column):
            # Checkstate has been changed
    
            is_checked = item.checkState(column) == Qt.Checked
            #text = unicode(item.text(column)).replace('&', '')
            unique_name = item.data(column, Qt.UserRole)
    
            if is_checked:
                # un-check previously checked item if any
                if self.checked_item:
                    self.tv.blockSignals(True)
                    self.checked_item.setCheckState(0, Qt.Unchecked)
                    self.tv.blockSignals(False)
                
                self.checked_item = item
            else:
                self.checked_item = None
    
        def _repopulate_actions_tree(self):
            self.tv.clear()
            self._populate_actions_tree()
    
        def load_settings(self, settings):
            if settings:
                #self.cursor_chk.setChecked(settings.get('disable_busy_cursor', False))
                #self._repopulate_actions_tree()
                self.set_unique_name_checked(settings['unique_name'])
    
        def save_settings(self):
            settings = {}
            settings['unique_name'] = self.checked_item.data(0, Qt.UserRole)
            #settings['disable_busy_cursor'] = self.cursor_chk.isChecked()
            return settings
    
        def set_unique_name_checked(self, unique_name):
            it = self.all_nodes.get(unique_name)
            if it:
                self.set_checked_state(it, 0, Qt.Checked)
                self.tv.setCurrentItem(it)
    
    class EditorActions(EditorAction):
    
        name = 'Editor Actions'
        #_is_builtin = True
    
        def run(self, chain, settings):
            
            ac = get_qaction_from_unique_name(chain.gui, settings['unique_name'])
            if ac:
                if DEBUG:
                    prints('Editor Chains: Editor Actions: Found action: {}'.format(ac.text().replace('&', ''))) 
    
                cursor = Qt.WaitCursor
                if settings.get('disable_busy_cursor'):
                    cursor = Qt.ArrowCursor
                
                QApplication.setOverrideCursor(cursor)
                try:
                    ac.trigger()
                       
                finally:
                    QApplication.restoreOverrideCursor()
                
            if DEBUG:
                prints('Editor Chains: Editor Actions: Finishing run action') 
    
        def validate(self, settings):
            gui = self.plugin_action.gui
            if not settings:
                return (_('Settings Error'), _('You must configure this action before running it'))
            if settings['unique_name'] not in allowed_unique_names(gui):
                return (_('Settings Error'), _('Action cannot be found: {}'.format(settings['unique_name'])))
            return True
    
        def config_widget(self):
            return ConfigWidget
  2. Run 'Check book' and fix all fixable errors [module name: fix_all]
    Spoiler:

    Code:
    from qt.core import (Qt, QApplication)
    
    from calibre.ebooks.oeb.polish.check.main import fix_errors, run_checks
    from calibre_plugins.editor_chains.actions.base import EditorAction
    
    class FixAll(EditorAction):
    
        name = 'Fix all errors'
        headless = True
    
        def run(self, chain, settings, *args, **kwargs):
            container = chain.current_container
            QApplication.setOverrideCursor(Qt.WaitCursor)
            QApplication.processEvents()
            errors = run_checks(container)
            fix_errors(container, errors)
            QApplication.restoreOverrideCursor()
  3. Merge files [module name: merge_files](by @capink)
    Spoiler:

    Code:
    import re
    
    from qt.core import (QWidget, QVBoxLayout, QGroupBox)
    
    from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES
    from calibre.ebooks.oeb.polish.split import merge
    from calibre.ebooks.oeb.polish.replace import rename_files
    from calibre.gui2.tweak_book import editors
    
    from calibre_plugins.editor_chains.actions.base import EditorAction
    from calibre_plugins.editor_chains.scope import scope_names, ScopeWidget, validate_scope, scope_is_headless
    
    
    def spine_index(container, name):
        idx = 0
        for spine_name, is_linear in container.spine_names:
            idx +=1
            if name == spine_name:
                return idx
    
    class ConfigWidget(QWidget):
        def __init__(self, plugin_action):
            QWidget.__init__(self)
            self.plugin_action = plugin_action
            self._init_controls()
    
        def _init_controls(self):
    
            l = self.l = QVBoxLayout()
            self.setLayout(l)
    
            scope_groupbox = QGroupBox(_('Files to merge'))
            l.addWidget(scope_groupbox)
            scope_l = QVBoxLayout()
            scope_groupbox.setLayout(scope_l)
            self.where_box = ScopeWidget(self, self.plugin_action, orientation='vertical',
                                         headless=self.plugin_action.gui is None,
                                         hide=['selected-text','open','current'])
            scope_l.addWidget(self.where_box)
    
            l.addStretch(1)
            self.resize(self.sizeHint())
    
        def load_settings(self, settings):
            if settings:
                self.where_box.where = settings['where']
    
        def save_settings(self):
            settings = {}
            settings['where'] = self.where_box.where
            return settings
    
    class MergeFiles(EditorAction):
    
        name = 'Merge Files'
        headless = True
    
        def run(self, chain, settings, *args, **kwargs):
            names = scope_names(chain, settings['where'])
            print(f'DEBUG: Merging Files\n       names: {names}')
            if len(names) < 2:
                print('Error: You need to select at least 2 files to merge')
                return
            container = chain.current_container
            spine_names = [name for name, is_linear in container.spine_names]
            if names[0] in set(container.manifest_items_of_type(OEB_DOCS)):
                category = 'text'
            elif names[0] in set(container.manifest_items_of_type(OEB_STYLES)):
                category = 'styles'
            else:
                print('Error: Can only merge files of categories: text or styles')
                return
            if category == 'text':
                if not set(names).issubset(set(container.manifest_items_of_type(OEB_DOCS))):
                    print('Error: some files are your trying to merge are not of category text')
                    return
                if not set(names).issubset(set(spine_names)):
                    print('Error: Some files you are trying to merge not in spine')
                    return
                names = sorted(names, key=lambda name: spine_index(container, name))
            else:
                if not set(names).issubset(set(container.manifest_items_of_type(OEB_STYLES))):
                    print('Error: some files are your trying to merge are not of category styles')
                    return
                names = sorted(names)
            master = names[0]
            print(f'DEBUG: Merging Files\n       master: {master}')
    
            # Create groups with the same prefix before merging
            while names:
                master = names[0]
                group = []
                for name in names[:]:
                    master_prefix =  re.search('.*?_split_[^d+]', master)[0]
                    if re.search('.*?_split_[^d+]', name)[0] == master_prefix:
                        group.append(name)
                        names.remove(name)
                merge(container, category, group, master)
                new_name = re.sub('_split_.*?\d+', '', master)
                rename_files(container, {master: new_name})
                #if new_name in editors:
                #    chain.boss.show_editor(new_name)
    
        def validate(self, settings):
            scope_ok = validate_scope(settings['where'])
            if scope_ok is not True:
                return scope_ok
            return True
    
        def config_widget(self):
            return ConfigWidget
    
        def is_headless(self, settings):
            return scope_is_headless(settings['where'])
  4. Pretty print all [module name: pretty_print_all]
    Spoiler:

    Code:
    from qt.core import (Qt, QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QSpinBox, QCheckBox)
    
    from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES
    from calibre.ebooks.oeb.polish import pretty
    from calibre_plugins.editor_chains.actions.base import EditorAction
    
    
    def pretty_block(parent, settings, level=2, indent='  ', indent_level=1):
        ''' Surround block tags with blank lines and recurse into child block tags
        that contain only other block tags '''
        if not parent.text or pretty.isspace(parent.text):
            parent.text = ''
        if settings['block_spaces']:
            nn = '\n' if hasattr(parent.tag, 'strip') and pretty.barename(parent.tag) in {'tr', 'td', 'th'} else '\n\n'
        else:
            nn = '\n'
        parent.text = parent.text + nn + (indent * indent_level * level)
        for i, child in enumerate(parent):
            if pretty.isblock(child) and pretty.has_only_blocks(child):
                pretty_block(child, settings, level=level+1, indent=indent * indent_level)
            elif child.tag == pretty.SVG_TAG:
                pretty_xml_tree(child, level=level, indent=indent, indent_level=indent_level)
            l = level
            if i == len(parent) - 1:
                l -= 1
            if not child.tail or pretty.isspace(child.tail):
                child.tail = ''
            child.tail = child.tail + nn + (indent * indent_level * l)
    
    
    def pretty_xml_tree(elem, level=1, indent='  ', indent_level=1):
        ''' XML beautifier, assumes that elements that have children do not have
        textual content.  Also assumes that there is no text immediately after
        closing tags. These are true for opf/ncx and container.xml files. If either
        of the assumptions are violated, there should be no data loss, but pretty
        printing won't produce optimal results.'''
        if (not elem.text and len(elem) > 0) or (elem.text and pretty.isspace(elem.text)):
            elem.text = '\n' + (indent * indent_level * (level+1))
        for i, child in enumerate(elem):
            pretty_xml_tree(child, level=level+1, indent=indent, indent_level=indent_level)
            if not child.tail or pretty.isspace(child.tail):
                l = level + 1
                if i == len(elem) - 1:
                    l -= 1
                child.tail = '\n' + (indent * indent_level * l)
    
    
    def pretty_html_tree(container, root, settings):
        indent = '  '
        indent_level = settings['indent']
        nn = '\n\n' if settings['root_spaces'] else '\n'
        root.text = nn + indent * indent_level
        for child in root:
            if hasattr(child.tag, 'endswith') and child.tag.endswith('}head'):
                pretty_xml_tree(child, indent_level=indent_level)
                child.tail = nn + indent * indent_level
            else:
                child.tail = nn
    
        for body in root.findall('h:body', namespaces=pretty.XPNSMAP):
            pretty_block(body, settings, indent_level=indent_level)
            # Special case the handling of a body that contains a single block tag
            # with all content. In this case we prettify the containing block tag
            # even if it has non block children.
            if (len(body) == 1 and not callable(body[0].tag) and
            pretty.isblock(body[0]) and
            not pretty.has_only_blocks(body[0]) and
            pretty.barename(body[0].tag) not in ('pre', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6') and
            len(body[0]) > 0):
                pretty_block(body[0], settings, level=2)
    
        if container is not None:
            # Handle <script> and <style> tags
            for child in root.xpath('//*[local-name()="script" or local-name()="style"]'):
                pretty.pretty_script_or_style(container, child)
    
    
    def pretty_all(container, settings):
        ' Pretty print all HTML/CSS/XML files in the container '
        xml_types = {pretty.guess_type('a.ncx'), pretty.guess_type('a.xml'), pretty.guess_type('a.svg')}
        for name, mt in pretty.iteritems(container.mime_map):
            prettied = False
            if mt in OEB_DOCS:
                pretty_html_tree(container, container.parsed(name), settings)
                prettied = True
            elif mt in OEB_STYLES:
                container.parsed(name)
                prettied = True
            elif name == container.opf_name:
                root = container.parsed(name)
                pretty.pretty_opf(root)
                pretty.pretty_xml_tree(root)
                prettied = True
            elif mt in xml_types:
                pretty.pretty_xml_tree(container.parsed(name))
                prettied = True
            if prettied:
                container.dirty(name)
    
    
    class ConfigWidget(QWidget):
        def __init__(self, plugin_action):
            QWidget.__init__(self)
            self.plugin_action = plugin_action
            self._init_controls()
    
        def _init_controls(self):
    
            l = self.l = QVBoxLayout()
            self.setLayout(l)
    
            h = self.h = QHBoxLayout()
            l.addLayout(h)
            self.label = QLabel(_('Indentation level:'))
            h.addWidget(self.label)
    
            self.indent_level = QSpinBox()
            self.indent_level.setRange(1, 10)
            h.addWidget(self.indent_level)
    
            self.root_spaces = QCheckBox(_('Add spaces for head/body'))
            l.addWidget(self.root_spaces)
    
            self.block_spaces = QCheckBox(_('Add spaces between blocks'))
            l.addWidget(self.block_spaces)
    
            #l.addStretch(1)
    
            #self.setMinimumSize(300,110)
            self.resize(self.sizeHint())
    
        def load_settings(self, settings):
            if settings:
                self.indent_level.setValue(settings['indent'])
                self.root_spaces.setChecked(settings['root_spaces'])
                self.block_spaces.setChecked(settings['block_spaces'])
    
        def save_settings(self):
            settings = {}
            settings['indent'] = self.indent_level.value()
            settings['root_spaces'] = self.root_spaces.isChecked()
            settings['block_spaces'] = self.block_spaces.isChecked()
            return settings
    
    
    class BeautifyAll(EditorAction):
    
        name = 'Pretty print ALL'
        headless = True
    
        def run(self, chain, settings, *args, **kwargs):
            container = chain.current_container
            QApplication.setOverrideCursor(Qt.WaitCursor)
            QApplication.processEvents()
            pretty_all(container, settings)
            QApplication.restoreOverrideCursor()
    
        def validate(self, settings):
            if not settings:
                return _('No settings'), _('You must first configure this action')
            return True
    
        def config_widget(self):
            return ConfigWidget
  5. Replace invalid IDs [module name: replace_ids]
    Spoiler:

    Code:
    from qt.core import (Qt, QApplication)
    
    from calibre.ebooks.oeb.polish.check import parsing
    from calibre.ebooks.oeb.polish.check.main import fix_errors
    
    from calibre_plugins.editor_chains.actions.base import EditorAction
    
    class ReplaceIDs(EditorAction):
    
        name = 'Replace IDs'
        headless = True
    
        def run(self, chain, settings, *args, **kwargs):
            container = chain.current_container
            QApplication.setOverrideCursor(Qt.WaitCursor)
            QApplication.processEvents()
            errors = parsing.check_ids(container)
            changed = fix_errors(container, errors)
            QApplication.restoreOverrideCursor()
  6. Select files: this is used to convert Editor Chain's 'escope' to a selection [module name: select_files] (by @capink)
    Spoiler:

    Code:
    import re
    
    from qt.core import (QWidget, QVBoxLayout, QGroupBox)
    
    from calibre_plugins.editor_chains.actions.base import EditorAction
    from calibre_plugins.editor_chains.scope import scope_names, ScopeWidget, validate_scope, scope_is_headless
    
    class ConfigWidget(QWidget):
        def __init__(self, plugin_action):
            QWidget.__init__(self)
            self.plugin_action = plugin_action
            self._init_controls()
    
        def _init_controls(self):
    
            l = self.l = QVBoxLayout()
            self.setLayout(l)
    
            scope_groupbox = QGroupBox(_('Files to select'))
            l.addWidget(scope_groupbox)
            scope_l = QVBoxLayout()
            scope_groupbox.setLayout(scope_l)
            self.where_box = ScopeWidget(self, self.plugin_action, orientation='vertical',
                                         headless=self.plugin_action.gui is None,
                                         hide=['selected-text','open','current'])
            scope_l.addWidget(self.where_box)
    
            l.addStretch(1)
            self.setMinimumSize(400,200)
    
        def load_settings(self, settings):
            if settings:
                self.where_box.where = settings['where']
    
        def save_settings(self):
            settings = {}
            settings['where'] = self.where_box.where
            return settings
    
    class SelectFiles(EditorAction):
    
        name = 'Select Files'
        headless = True
    
        def run(self, chain, settings, *args, **kwargs):
            names = scope_names(chain, settings['where'])
            chain.gui.file_list.select_names(group)
    
        def validate(self, settings):
            scope_ok = validate_scope(settings['where'])
            if scope_ok is not True:
                return scope_ok
            return True
    
        def config_widget(self):
            return ConfigWidget
    
        def is_headless(self, settings):
            return scope_is_headless(settings['where'])
  7. Upgrade to EPUB3 [module name: upgrade]
    Spoiler:

    Code:
    from qt.core import (QApplication, Qt, QWidget, QVBoxLayout,
                         QCheckBox, QLabel)
    
    from calibre import prints
    from calibre.constants import DEBUG
    from calibre.ebooks.oeb.polish.main import tweak_polish
    from calibre.ebooks.oeb.polish import upgrade
    
    from calibre_plugins.editor_chains.actions.base import EditorAction
    
    
    try:
        load_translations()
    except NameError:
        prints("EditorChains::actions/upgrade_internals.py - exception when loading translations")
    
    
    def epub_2_to_3(container, previous_nav=None, remove_ncx=False, remove_guide=False):
        upgrade.upgrade_metadata(container.opf)
        upgrade.collect_properties(container)
        toc = upgrade.get_toc(container)
        toc_name = upgrade.find_existing_ncx_toc(container)
        if toc_name and remove_ncx:
            container.remove_item(toc_name)
            container.opf_xpath('./opf:spine')[0].attrib.pop('toc', None)
        landmarks = upgrade.get_landmarks(container)
        if remove_guide:
            for guide in container.opf_xpath('./opf:guide'):
                guide.getparent().remove(guide)
        upgrade.create_nav(container, toc, landmarks, previous_nav)
        container.opf.set('version', '3.0')
        if upgrade.fix_font_mime_types(container):
            container.refresh_mime_map()
        upgrade.migrate_obfuscated_fonts(container)
        container.dirty(container.opf_name)
    
    
    def upgrade_book(container, remove_ncx=False, remove_guide=False):
        if container.book_type != 'epub' or container.opf_version_parsed.major >= 3:
            return False
        epub_2_to_3(container, remove_ncx=remove_ncx, remove_guide=remove_guide)
        return True
    
    
    class ConfigWidget(QWidget):
        def __init__(self, plugin_action, chain_name, chains_config, *args, **kwargs):
            QWidget.__init__(self)
            self.plugin_action = plugin_action
            self.chain_name = chain_name
            self.chains_config = chains_config
            self._init_controls()
    
        def _init_controls(self):
    
            l = self.l = QVBoxLayout()
            self.setLayout(l)
    
            self.remove_ncx = QCheckBox(_('Remove the legacy Table of Contents in NCX form'))
            l.addWidget(self.remove_ncx)
    
            self.remove_guide = QCheckBox(_('Remove the the OPF Guide element'))
            l.addWidget(self.remove_guide)
    
            l.addStretch(1)
    
            self.resize(self.sizeHint())
    
        def load_settings(self, settings):
            if settings:
                self.remove_ncx.setChecked(settings['remove_ncx'])
                self.remove_guide.setChecked(settings['remove_guide'])
    
        def save_settings(self):
            settings = {}
            settings['remove_ncx'] = self.remove_ncx.isChecked()
            settings['remove_guide'] = self.remove_guide.isChecked()
            return settings
    
    class UpgradeInternals(EditorAction):
    
        name = 'Upgrade to EPUB3'
        _is_builtin_ = True
        headless = True
    
        def run(self, chain, settings, *args, **kwargs):
            if settings is None:
                return
            upgrade_book(chain.current_container, settings['remove_ncx'], settings['remove_guide'])
    
        def validate(self, settings):
            if not settings:
                return _('No settings'), _('You must first configure this action')
            return True
    
        def config_widget(self):
            return ConfigWidget

The chains are attatched.

_______________________
Edit:

I noticed that the regex functions are not included with the chain. Most of them are used to convert items to pt-BR, so not relevant for most users. There is one, though, that is important:
Code:
def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
    if 'alt=' not in match[0]:
        ans = match.group().replace(match.group(1), match.group(1) + ' alt=""')
    else:
        ans = match[0]
    return ans
This is used for fixing missing 'alt' attributes, inside the 'Clean up' chain.
Thank you very much! With this, I'll be able to automate some of the options I do manually! And thanks to capink for the plugin and kovid for the program!
Terisa de morgan is offline   Reply With Quote