← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~alisonken1/openlp/strings-lib into lp:openlp

 

Ken Roberts has proposed merging lp:~alisonken1/openlp/strings-lib into lp:openlp.

Commit message:
openlp/core/lib/*.py files convert strings to python3 format

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~alisonken1/openlp/strings-lib/+merge/294810

Convert strings from python2 to python3 format

- Strings converted except as noted
- Updated projector pjlink test

--------------------------------
lp:~alisonken1/openlp/strings-lib (revision 2662)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/1555/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/1466/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/1404/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Windows_Functional_Tests/1184/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Windows_Interface_Tests/774/
[SUCCESS] https://ci.openlp.io/job/Branch-05a-Code_Analysis/842/
[SUCCESS] https://ci.openlp.io/job/Branch-05b-Test_Coverage/710/

-- 
Your team OpenLP Core is requested to review the proposed merge of lp:~alisonken1/openlp/strings-lib into lp:openlp.
=== modified file 'openlp/core/lib/db.py'
--- openlp/core/lib/db.py	2016-04-05 17:10:51 +0000
+++ openlp/core/lib/db.py	2016-05-16 13:50:39 +0000
@@ -82,10 +82,10 @@
     :return: None
     """
     db_path = get_db_path(plugin_name, db_file_name)
-    log.exception('Error loading database: %s', db_path)
+    log.exception('Error loading database: {db}'.format(db=db_path))
     critical_error_message_box(translate('OpenLP.Manager', 'Database Error'),
-                               translate('OpenLP.Manager', 'OpenLP cannot load your database.\n\nDatabase: %s')
-                               % db_path)
+                               translate('OpenLP.Manager',
+                                         'OpenLP cannot load your database.\n\nDatabase: {db}').format(db=db_path))
 
 
 def init_url(plugin_name, db_file_name=None):
@@ -157,10 +157,10 @@
         return version, upgrade.__version__
     version += 1
     try:
-        while hasattr(upgrade, 'upgrade_%d' % version):
-            log.debug('Running upgrade_%d', version)
+        while hasattr(upgrade, 'upgrade_{version:d}'.format(version=version)):
+            log.debug('Running upgrade_{version:d}'.format(version=version))
             try:
-                upgrade_func = getattr(upgrade, 'upgrade_%d' % version)
+                upgrade_func = getattr(upgrade, 'upgrade_{version:d}'.format(version=version))
                 upgrade_func(session, metadata)
                 session.commit()
                 # Update the version number AFTER a commit so that we are sure the previous transaction happened
@@ -168,8 +168,8 @@
                 session.commit()
                 version += 1
             except (SQLAlchemyError, DBAPIError):
-                log.exception('Could not run database upgrade script "upgrade_%s", upgrade process has been halted.',
-                              version)
+                log.exception('Could not run database upgrade script '
+                              '"upgrade_{version:d}", upgrade process has been halted.'.format(version=version))
                 break
     except (SQLAlchemyError, DBAPIError):
         version_meta = Metadata.populate(key='version', value=int(upgrade.__version__))
@@ -242,9 +242,10 @@
                 critical_error_message_box(
                     translate('OpenLP.Manager', 'Database Error'),
                     translate('OpenLP.Manager', 'The database being loaded was created in a more recent version of '
-                              'OpenLP. The database is version %d, while OpenLP expects version %d. The database will '
-                              'not be loaded.\n\nDatabase: %s') % (db_ver, up_ver, self.db_url)
-                )
+                              'OpenLP. The database is version {db_ver}, while OpenLP expects version {db_up}. '
+                              'The database will not be loaded.\n\nDatabase: {db_name}').format(db_ver=db_ver,
+                                                                                                db_up=up_ver,
+                                                                                                db_name=self.db_url))
                 return
         if not session:
             try:
@@ -460,7 +461,7 @@
                     raise
             except InvalidRequestError:
                 self.session.rollback()
-                log.exception('Failed to delete %s records', object_class.__name__)
+                log.exception('Failed to delete {name} records'.format(name=object_class.__name__))
                 return False
             except:
                 self.session.rollback()

=== modified file 'openlp/core/lib/filedialog.py'
--- openlp/core/lib/filedialog.py	2015-12-31 22:46:06 +0000
+++ openlp/core/lib/filedialog.py	2016-05-16 13:50:39 +0000
@@ -50,7 +50,8 @@
                 log.info('File not found. Attempting to unquote.')
                 file = parse.unquote(file)
                 if not os.path.exists(file):
-                    log.error('File %s not found.' % file)
+                    log.error('File {text} not found.'.format(text=file))
+                    # TODO: Test with UiStrings() before converting to python3 strings
                     QtWidgets.QMessageBox.information(parent, UiStrings().FileNotFound,
                                                       UiStrings().FileNotFoundMessage % file)
                     continue

=== modified file 'openlp/core/lib/htmlbuilder.py'
--- openlp/core/lib/htmlbuilder.py	2015-12-31 22:46:06 +0000
+++ openlp/core/lib/htmlbuilder.py	2016-05-16 13:50:39 +0000
@@ -396,6 +396,7 @@
 
 log = logging.getLogger(__name__)
 
+# TODO: Verify where this is used before converting to python3
 HTMLSRC = """
 <!DOCTYPE html>
 <html>
@@ -564,13 +565,13 @@
     theme_data = item.theme_data
     # Image generated and poked in
     if background:
-        bgimage_src = 'src="data:image/png;base64,%s"' % background
+        bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=background)
     elif item.bg_image_bytes:
-        bgimage_src = 'src="data:image/png;base64,%s"' % item.bg_image_bytes
+        bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=item.bg_image_bytes)
     else:
         bgimage_src = 'style="display:none;"'
     if image:
-        image_src = 'src="data:image/png;base64,%s"' % image
+        image_src = 'src="data:image/png;base64,{image}"'.format(image=image)
     else:
         image_src = 'style="display:none;"'
     css_additions = ''
@@ -601,7 +602,7 @@
     """
     try:
         webkit_ver = float(QtWebKit.qWebKitVersion())
-        log.debug('Webkit version = %s' % webkit_ver)
+        log.debug('Webkit version = {version}'.format(version=webkit_ver))
     except AttributeError:
         webkit_ver = 0
     return webkit_ver
@@ -621,23 +622,25 @@
         if theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
             background = ''
         elif theme.background_type == BackgroundType.to_string(BackgroundType.Solid):
-            background = 'background-color: %s' % theme.background_color
+            background = 'background-color: {theme}'.format(theme=theme.background_color)
         else:
             if theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
-                background = 'background: -webkit-gradient(linear, left top, left bottom, from(%s), to(%s)) fixed' \
-                    % (theme.background_start_color, theme.background_end_color)
+                background = 'background: -webkit-gradient(linear, left top, left bottom, from({start}), to({end})) ' \
+                    'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
             elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftTop):
-                background = 'background: -webkit-gradient(linear, left top, right bottom, from(%s), to(%s)) fixed' \
-                    % (theme.background_start_color, theme.background_end_color)
+                background = 'background: -webkit-gradient(linear, left top, right bottom, from({start}), to({end})) ' \
+                    'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
             elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftBottom):
-                background = 'background: -webkit-gradient(linear, left bottom, right top, from(%s), to(%s)) fixed' \
-                    % (theme.background_start_color, theme.background_end_color)
+                background = 'background: -webkit-gradient(linear, left bottom, right top, from({start}), to({end})) ' \
+                    'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
             elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Vertical):
-                background = 'background: -webkit-gradient(linear, left top, right top, from(%s), to(%s)) fixed' % \
-                    (theme.background_start_color, theme.background_end_color)
+                background = 'background: -webkit-gradient(linear, left top, right top, from({start}), to({end})) ' \
+                    'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
             else:
-                background = 'background: -webkit-gradient(radial, %s 50%%, 100, %s 50%%, %s, from(%s), to(%s)) fixed'\
-                    % (width, width, width, theme.background_start_color, theme.background_end_color)
+                background = 'background: -webkit-gradient(radial, {width1} 50%, 100, {width2} 50%, {width3}, ' \
+                    'from({start}), to({end})) fixed'.format(width1=width, width2=width, width3=width,
+                                                             start=theme.background_start_color,
+                                                             end=theme.background_end_color)
     return background
 
 
@@ -647,6 +650,7 @@
 
     :param item: Service Item containing theme and location information
     """
+    # TODO: Verify this before converting to python3
     style = """
 .lyricstable {
     z-index: 5;
@@ -669,12 +673,14 @@
     lyrics = ''
     lyricsmain = ''
     if theme_data and item.main:
-        lyricstable = 'left: %spx; top: %spx;' % (item.main.x(), item.main.y())
+        lyricstable = 'left: {left}px; top: {top}px;'.format(left=item.main.x(), top=item.main.y())
         lyrics = build_lyrics_format_css(theme_data, item.main.width(), item.main.height())
         lyricsmain += build_lyrics_outline_css(theme_data)
         if theme_data.font_main_shadow:
-            lyricsmain += ' text-shadow: %s %spx %spx;' % \
-                (theme_data.font_main_shadow_color, theme_data.font_main_shadow_size, theme_data.font_main_shadow_size)
+            lyricsmain += ' text-shadow: {theme} {shadow1}px ' \
+                '{shadow2}px;'.format(theme=theme_data.font_main_shadow_color,
+                                      shadow1=theme_data.font_main_shadow_size,
+                                      shadow2=theme_data.font_main_shadow_size)
     lyrics_css = style % (lyricstable, lyrics, lyricsmain)
     return lyrics_css
 
@@ -689,7 +695,9 @@
         size = float(theme_data.font_main_outline_size) / 16
         fill_color = theme_data.font_main_color
         outline_color = theme_data.font_main_outline_color
-        return ' -webkit-text-stroke: %sem %s; -webkit-text-fill-color: %s; ' % (size, outline_color, fill_color)
+        return ' -webkit-text-stroke: {size}em {color}; -webkit-text-fill-color: {fill}; '.format(size=size,
+                                                                                                  color=outline_color,
+                                                                                                  fill=fill_color)
     return ''
 
 
@@ -715,13 +723,21 @@
         padding_bottom = '0.5em'
     else:
         padding_bottom = '0'
-    lyrics = '%s word-wrap: break-word; ' \
-             'text-align: %s; vertical-align: %s; font-family: %s; ' \
-             'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \
-             'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx; height: %spx; ' % \
-        (justify, align, valign, theme_data.font_main_name, theme_data.font_main_size,
-         theme_data.font_main_color, 100 + int(theme_data.font_main_line_adjustment), padding_bottom,
-         left_margin, width, height)
+    lyrics = '{justify} word-wrap: break-word; ' \
+        'text-align: {align}; vertical-align: {valign}; font-family: {font}; ' \
+        'font-size: {size}pt; color: {color}; line-height: {line:d}%; margin: 0;' \
+        'padding: 0; padding-bottom: {bottom}; padding-left: {left}px; width: {width}px; ' \
+        'height: {height}px; '.format(justify=justify,
+                                      align=align,
+                                      valign=valign,
+                                      font=theme_data.font_main_name,
+                                      size=theme_data.font_main_size,
+                                      color=theme_data.font_main_color,
+                                      line=100 + int(theme_data.font_main_line_adjustment),
+                                      bottom=padding_bottom,
+                                      left=left_margin,
+                                      width=width,
+                                      height=height)
     if theme_data.font_main_italics:
         lyrics += 'font-style:italic; '
     if theme_data.font_main_bold:
@@ -737,20 +753,21 @@
     :param height:
     """
     style = """
-    left: %spx;
-    bottom: %spx;
-    width: %spx;
-    font-family: %s;
-    font-size: %spt;
-    color: %s;
+    left: {left}px;
+    bottom: {bottom}px;
+    width: {width}px;
+    font-family: {family};
+    font-size: {size}pt;
+    color: {color};
     text-align: left;
-    white-space: %s;
+    white-space: {space};
     """
     theme = item.theme_data
     if not theme or not item.footer:
         return ''
     bottom = height - int(item.footer.y()) - int(item.footer.height())
     whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
-    lyrics_html = style % (item.footer.x(), bottom, item.footer.width(),
-                           theme.font_footer_name, theme.font_footer_size, theme.font_footer_color, whitespace)
+    lyrics_html = style.format(left=item.footer.x(), bottom=bottom, width=item.footer.width(),
+                               family=theme.font_footer_name, size=theme.font_footer_size,
+                               color=theme.font_footer_color, space=whitespace)
     return lyrics_html

=== modified file 'openlp/core/lib/imagemanager.py'
--- openlp/core/lib/imagemanager.py	2015-12-31 22:46:06 +0000
+++ openlp/core/lib/imagemanager.py	2016-05-16 13:50:39 +0000
@@ -236,7 +236,7 @@
         """
         Return the ``QImage`` from the cache. If not present wait for the background thread to process it.
         """
-        log.debug('getImage %s' % path)
+        log.debug('getImage {path}'.format(path=path))
         image = self._cache[(path, source, width, height)]
         if image.image is None:
             self._conversion_queue.modify_priority(image, Priority.High)
@@ -256,7 +256,7 @@
         """
         Returns the byte string for an image. If not present wait for the background thread to process it.
         """
-        log.debug('get_image_bytes %s' % path)
+        log.debug('get_image_bytes {path}'.format(path=path))
         image = self._cache[(path, source, width, height)]
         if image.image_bytes is None:
             self._conversion_queue.modify_priority(image, Priority.Urgent)
@@ -271,7 +271,7 @@
         """
         Add image to cache if it is not already there.
         """
-        log.debug('add_image %s' % path)
+        log.debug('add_image {path}'.format(path=path))
         if not (path, source, width, height) in self._cache:
             image = Image(path, source, background, width, height)
             self._cache[(path, source, width, height)] = image

=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py	2016-04-17 19:32:15 +0000
+++ openlp/core/lib/mediamanageritem.py	2016-05-16 13:50:39 +0000
@@ -186,7 +186,7 @@
         for action in toolbar_actions:
             if action[0] == StringContent.Preview:
                 self.toolbar.addSeparator()
-            self.toolbar.add_toolbar_action('%s%sAction' % (self.plugin.name, action[0]),
+            self.toolbar.add_toolbar_action('{name}{action}Action'.format(name=self.plugin.name, action=action[0]),
                                             text=self.plugin.get_string(action[1])['title'], icon=action[2],
                                             tooltip=self.plugin.get_string(action[1])['tooltip'],
                                             triggers=action[3])
@@ -200,7 +200,7 @@
         self.list_view.setSpacing(1)
         self.list_view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
         self.list_view.setAlternatingRowColors(True)
-        self.list_view.setObjectName('%sListView' % self.plugin.name)
+        self.list_view.setObjectName('{name}ListView'.format(name=self.plugin.name))
         # Add to page_layout
         self.page_layout.addWidget(self.list_view)
         # define and add the context menu
@@ -212,19 +212,22 @@
                                  triggers=self.on_edit_click)
             create_widget_action(self.list_view, separator=True)
         create_widget_action(self.list_view,
-                             'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()),
+                             'listView{plugin}{preview}Item'.format(plugin=self.plugin.name.title(),
+                                                                    preview=StringContent.Preview.title()),
                              text=self.plugin.get_string(StringContent.Preview)['title'],
                              icon=':/general/general_preview.png',
                              can_shortcuts=True,
                              triggers=self.on_preview_click)
         create_widget_action(self.list_view,
-                             'listView%s%sItem' % (self.plugin.name.title(), StringContent.Live.title()),
+                             'listView{plugin}{live}Item'.format(plugin=self.plugin.name.title(),
+                                                                 live=StringContent.Live.title()),
                              text=self.plugin.get_string(StringContent.Live)['title'],
                              icon=':/general/general_live.png',
                              can_shortcuts=True,
                              triggers=self.on_live_click)
         create_widget_action(self.list_view,
-                             'listView%s%sItem' % (self.plugin.name.title(), StringContent.Service.title()),
+                             'listView{plugin}{service}Item'.format(plugin=self.plugin.name.title(),
+                                                                    service=StringContent.Service.title()),
                              can_shortcuts=True,
                              text=self.plugin.get_string(StringContent.Service)['title'],
                              icon=':/general/general_add.png',
@@ -232,7 +235,8 @@
         if self.has_delete_icon:
             create_widget_action(self.list_view, separator=True)
             create_widget_action(self.list_view,
-                                 'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()),
+                                 'listView{plugin}{delete}Item'.format(plugin=self.plugin.name.title(),
+                                                                       delete=StringContent.Delete.title()),
                                  text=self.plugin.get_string(StringContent.Delete)['title'],
                                  icon=':/general/general_delete.png',
                                  can_shortcuts=True, triggers=self.on_delete_click)
@@ -313,7 +317,7 @@
         files = FileDialog.getOpenFileNames(self, self.on_new_prompt,
                                             Settings().value(self.settings_section + '/last directory'),
                                             self.on_new_file_masks)
-        log.info('New files(s) %s' % files)
+        log.info('New files(s) {files}'.format(files=files))
         if files:
             self.application.set_busy_cursor()
             self.validate_and_load(files)
@@ -333,7 +337,8 @@
                 if not error_shown:
                     critical_error_message_box(translate('OpenLP.MediaManagerItem', 'Invalid File Type'),
                                                translate('OpenLP.MediaManagerItem',
-                                                         'Invalid File %s.\nSuffix not supported') % file_name)
+                                                         'Invalid File {name}.\n'
+                                                         'Suffix not supported').format(name=file_name))
                     error_shown = True
             else:
                 new_files.append(file_name)
@@ -375,7 +380,9 @@
             self.load_list(full_list, target_group)
             last_dir = os.path.split(files[0])[0]
             Settings().setValue(self.settings_section + '/last directory', last_dir)
-            Settings().setValue('%s/%s files' % (self.settings_section, self.settings_section), self.get_file_list())
+            Settings().setValue('{section1}/{section2} files'.format(section1=self.settings_section,
+                                                                     section2=self.settings_section),
+                                self.get_file_list())
         if duplicates_found:
             critical_error_message_box(UiStrings().Duplicate,
                                        translate('OpenLP.MediaManagerItem',
@@ -550,7 +557,7 @@
             # Is it possible to process multiple list items to generate
             # multiple service items?
             if self.single_service_item:
-                log.debug('%s Add requested', self.plugin.name)
+                log.debug('{plugin} Add requested'.format(plugin=self.plugin.name))
                 self.add_to_service(replace=self.remote_triggered)
             else:
                 items = self.list_view.selectedIndexes()
@@ -591,7 +598,7 @@
                                               translate('OpenLP.MediaManagerItem',
                                                         'You must select one or more items.'))
         else:
-            log.debug('%s Add requested', self.plugin.name)
+            log.debug('{plugin} Add requested'.format(plugin=self.plugin.name))
             service_item = self.service_manager.get_service_item()
             if not service_item:
                 QtWidgets.QMessageBox.information(self, UiStrings().NISs,
@@ -604,7 +611,8 @@
                 # Turn off the remote edit update message indicator
                 QtWidgets.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'Invalid Service Item'),
                                                   translate('OpenLP.MediaManagerItem',
-                                                            'You must select a %s service item.') % self.title)
+                                                            'You must select a {title} '
+                                                            'service item.').format(title=self.title))
 
     def build_service_item(self, item=None, xml_version=False, remote=False, context=ServiceItemContext.Live):
         """

=== modified file 'openlp/core/lib/plugin.py'
--- openlp/core/lib/plugin.py	2016-04-04 19:53:54 +0000
+++ openlp/core/lib/plugin.py	2016-05-16 13:50:39 +0000
@@ -130,7 +130,7 @@
         :param settings_tab_class: The class name of the plugin's settings tab.
         :param version: Defaults to *None*, which means that the same version number is used as OpenLP's version number.
         """
-        log.debug('Plugin %s initialised' % name)
+        log.debug('Plugin {plugin} initialised'.format(plugin=name))
         super(Plugin, self).__init__()
         self.name = name
         self.text_strings = {}
@@ -154,11 +154,11 @@
         # Append a setting for files in the mediamanager (note not all plugins
         # which have a mediamanager need this).
         if media_item_class is not None:
-            default_settings['%s/%s files' % (name, name)] = []
+            default_settings['{name1}/{name2} files'.format(name1=name, name2=name)] = []
         # Add settings to the dict of all settings.
         Settings.extend_default_settings(default_settings)
-        Registry().register_function('%s_add_service_item' % self.name, self.process_add_service_event)
-        Registry().register_function('%s_config_updated' % self.name, self.config_update)
+        Registry().register_function('{name}_add_service_item'.format(name=self.name), self.process_add_service_event)
+        Registry().register_function('{name}_config_updated'.format(name=self.name), self.config_update)
 
     def check_pre_conditions(self):
         """
@@ -256,7 +256,7 @@
         """
         Generic Drag and drop handler triggered from service_manager.
         """
-        log.debug('process_add_service_event event called for plugin %s' % self.name)
+        log.debug('process_add_service_event event called for plugin {name}'.format(name=self.name))
         if replace:
             self.media_item.on_add_edit_click()
         else:

=== modified file 'openlp/core/lib/pluginmanager.py'
--- openlp/core/lib/pluginmanager.py	2015-12-31 22:46:06 +0000
+++ openlp/core/lib/pluginmanager.py	2016-05-16 13:50:39 +0000
@@ -43,7 +43,7 @@
         super(PluginManager, self).__init__(parent)
         self.log_info('Plugin manager Initialising')
         self.base_path = os.path.abspath(AppLocation.get_directory(AppLocation.PluginsDir))
-        self.log_debug('Base path %s ' % self.base_path)
+        self.log_debug('Base path {path}'.format(path=self.base_path))
         self.plugins = []
         self.log_info('Plugin manager Initialised')
 
@@ -73,7 +73,7 @@
         """
         start_depth = len(os.path.abspath(self.base_path).split(os.sep))
         present_plugin_dir = os.path.join(self.base_path, 'presentations')
-        self.log_debug('finding plugins in %s at depth %d' % (self.base_path, start_depth))
+        self.log_debug('finding plugins in {path} at depth {depth:d}'.format(path=self.base_path, depth=start_depth))
         for root, dirs, files in os.walk(self.base_path):
             for name in files:
                 if name.endswith('.py') and not name.startswith('__'):
@@ -84,7 +84,9 @@
                         break
                     module_name = name[:-3]
                     # import the modules
-                    self.log_debug('Importing %s from %s. Depth %d' % (module_name, root, this_depth))
+                    self.log_debug('Importing {name} from {root}. Depth {depth:d}'.format(name=module_name,
+                                                                                          root=root,
+                                                                                          depth=this_depth))
                     try:
                         # Use the "imp" library to try to get around a problem with the PyUNO library which
                         # monkey-patches the __import__ function to do some magic. This causes issues with our tests.
@@ -93,21 +95,21 @@
                         # Then load the module (do the actual import) using the details from find_module()
                         imp.load_module(module_name, fp, path_name, description)
                     except ImportError as e:
-                        self.log_exception('Failed to import module %s on path %s: %s'
-                                           % (module_name, path, e.args[0]))
+                        self.log_exception('Failed to import module {name} on path {path}: '
+                                           '{args}'.format(name=module_name, path=path, args=e.args[0]))
         plugin_classes = Plugin.__subclasses__()
         plugin_objects = []
         for p in plugin_classes:
             try:
                 plugin = p()
-                self.log_debug('Loaded plugin %s' % str(p))
+                self.log_debug('Loaded plugin {plugin}'.format(plugin=str(p)))
                 plugin_objects.append(plugin)
             except TypeError:
-                self.log_exception('Failed to load plugin %s' % str(p))
+                self.log_exception('Failed to load plugin {plugin}'.format(plugin=str(p)))
         plugins_list = sorted(plugin_objects, key=lambda plugin: plugin.weight)
         for plugin in plugins_list:
             if plugin.check_pre_conditions():
-                self.log_debug('Plugin %s active' % str(plugin.name))
+                self.log_debug('Plugin {plugin} active'.format(plugin=str(plugin.name)))
                 plugin.set_status()
             else:
                 plugin.status = PluginStatus.Disabled
@@ -175,10 +177,11 @@
         Loop through all the plugins and give them an opportunity to initialise themselves.
         """
         for plugin in self.plugins:
-            self.log_info('initialising plugins %s in a %s state' % (plugin.name, plugin.is_active()))
+            self.log_info('initialising plugins {plugin} in a {state} state'.format(plugin=plugin.name,
+                                                                                    state=plugin.is_active()))
             if plugin.is_active():
                 plugin.initialise()
-                self.log_info('Initialisation Complete for %s ' % plugin.name)
+                self.log_info('Initialisation Complete for {plugin}'.format(plugin=plugin.name))
 
     def finalise_plugins(self):
         """
@@ -187,7 +190,7 @@
         for plugin in self.plugins:
             if plugin.is_active():
                 plugin.finalise()
-                self.log_info('Finalisation Complete for %s ' % plugin.name)
+                self.log_info('Finalisation Complete for {plugin}'.format(plugin=plugin.name))
 
     def get_plugin_by_name(self, name):
         """

=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2016-01-23 12:38:08 +0000
+++ openlp/core/lib/renderer.py	2016-05-16 13:50:39 +0000
@@ -107,7 +107,7 @@
 
         :param theme_name: The theme name
         """
-        self.log_debug("_set_theme with theme %s" % theme_name)
+        self.log_debug("_set_theme with theme {theme}".format(theme=theme_name))
         if theme_name not in self._theme_dimensions:
             theme_data = self.theme_manager.get_theme_data(theme_name)
             main_rect = self.get_main_rectangle(theme_data)
@@ -183,7 +183,7 @@
 
         :param item_theme_name: The item theme's name.
         """
-        self.log_debug("set_item_theme with theme %s" % item_theme_name)
+        self.log_debug("set_item_theme with theme {theme}".format(theme=item_theme_name))
         self._set_theme(item_theme_name)
         self.item_theme_name = item_theme_name
 
@@ -317,7 +317,7 @@
         self.width = screen_size.width()
         self.height = screen_size.height()
         self.screen_ratio = self.height / self.width
-        self.log_debug('_calculate default %s, %f' % (screen_size, self.screen_ratio))
+        self.log_debug('_calculate default {size}, {ratio:f}'.format(size=screen_size, ratio=self.screen_ratio))
         # 90% is start of footer
         self.footer_start = int(self.height * 0.90)
 
@@ -354,7 +354,7 @@
         :param rect_main: The main text block.
         :param rect_footer: The footer text block.
         """
-        self.log_debug('_set_text_rectangle %s , %s' % (rect_main, rect_footer))
+        self.log_debug('_set_text_rectangle {main} , {footer}'.format(main=rect_main, footer=rect_footer))
         self._rect = rect_main
         self._rect_footer = rect_footer
         self.page_width = self._rect.width()
@@ -370,6 +370,7 @@
         self.web.resize(self.page_width, self.page_height)
         self.web_frame = self.web.page().mainFrame()
         # Adjust width and height to account for shadow. outline done in css.
+        # TODO: Verify before converting to python3 strings
         html = """<!DOCTYPE html><html><head><script>
             function show_text(newtext) {
                 var main = document.getElementById('main');
@@ -518,7 +519,8 @@
 
         :param text:  The text to check. It may contain HTML tags.
         """
-        self.web_frame.evaluateJavaScript('show_text("%s")' % text.replace('\\', '\\\\').replace('\"', '\\\"'))
+        self.web_frame.evaluateJavaScript('show_text'
+                                          '("{text}")'.format(text=text.replace('\\', '\\\\').replace('\"', '\\\"')))
         return self.web_frame.contentsSize().height() <= self.empty_height
 
 

=== modified file 'openlp/core/lib/screen.py'
--- openlp/core/lib/screen.py	2016-01-10 16:00:05 +0000
+++ openlp/core/lib/screen.py	2016-05-16 13:50:39 +0000
@@ -78,7 +78,7 @@
         ``number``
             The number of the screen, which size has changed.
         """
-        log.info('screen_resolution_changed %d' % number)
+        log.info('screen_resolution_changed {number:d}'.format(number=number))
         for screen in self.screen_list:
             if number == screen['number']:
                 new_screen = {
@@ -105,7 +105,7 @@
         """
         # Do not log at start up.
         if changed_screen != -1:
-            log.info('screen_count_changed %d' % self.desktop.screenCount())
+            log.info('screen_count_changed {count:d}'.format(count=self.desktop.screenCount()))
         # Remove unplugged screens.
         for screen in copy.deepcopy(self.screen_list):
             if screen['number'] == self.desktop.screenCount():
@@ -132,9 +132,11 @@
         """
         screen_list = []
         for screen in self.screen_list:
-            screen_name = '%s %d' % (translate('OpenLP.ScreenList', 'Screen'), screen['number'] + 1)
+            screen_name = '{name} {number:d}'.format(name=translate('OpenLP.ScreenList', 'Screen'),
+                                                     number=screen['number'] + 1)
             if screen['primary']:
-                screen_name = '%s (%s)' % (screen_name, translate('OpenLP.ScreenList', 'primary'))
+                screen_name = '{name} ({primary})'.format(name=screen_name,
+                                                          primary=translate('OpenLP.ScreenList', 'primary'))
             screen_list.append(screen_name)
         return screen_list
 
@@ -152,7 +154,7 @@
                     'size': PyQt5.QtCore.QRect(0, 0, 1024, 768)
                 }
         """
-        log.info('Screen %d found with resolution %s' % (screen['number'], screen['size']))
+        log.info('Screen {number:d} found with resolution {size}'.format(number=screen['number'], size=screen['size']))
         if screen['primary']:
             self.current = screen
             self.override = copy.deepcopy(self.current)
@@ -165,7 +167,7 @@
 
         :param number: The screen number (int).
         """
-        log.info('remove_screen %d' % number)
+        log.info('remove_screen {number:d}'.forma(number=number))
         for screen in self.screen_list:
             if screen['number'] == number:
                 self.screen_list.remove(screen)
@@ -189,7 +191,7 @@
 
         :param number: The screen number (int).
         """
-        log.debug('set_current_display %s' % number)
+        log.debug('set_current_display {number}'.format(number=number))
         if number + 1 > self.display_count:
             self.current = self.screen_list[0]
         else:

=== modified file 'openlp/core/lib/searchedit.py'
--- openlp/core/lib/searchedit.py	2015-12-31 22:46:06 +0000
+++ openlp/core/lib/searchedit.py	2016-05-16 13:50:39 +0000
@@ -62,9 +62,10 @@
         right_padding = self.clear_button.width() + frame_width
         if hasattr(self, 'menu_button'):
             left_padding = self.menu_button.width()
-            stylesheet = 'QLineEdit { padding-left: %spx; padding-right: %spx; } ' % (left_padding, right_padding)
+            stylesheet = 'QLineEdit {{ padding-left:{left}px; padding-right: {right}px; }} '.format(left=left_padding,
+                                                                                                    right=right_padding)
         else:
-            stylesheet = 'QLineEdit { padding-right: %spx; } ' % right_padding
+            stylesheet = 'QLineEdit {{ padding-right: {right}px; }} '.format(right=right_padding)
         self.setStyleSheet(stylesheet)
         msz = self.minimumSizeHint()
         self.setMinimumSize(max(msz.width(), self.clear_button.width() + (frame_width * 2) + 2),

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2016-02-14 17:53:16 +0000
+++ openlp/core/lib/serviceitem.py	2016-05-16 13:50:39 +0000
@@ -247,7 +247,7 @@
             self.renderer.set_item_theme(self.theme)
             self.theme_data, self.main, self.footer = self.renderer.pre_render()
         if self.service_item_type == ServiceItemType.Text:
-            log.debug('Formatting slides: %s' % self.title)
+            log.debug('Formatting slides: {title}'.format(title=self.title))
             # Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
             # the dict instead of rendering them again.
             previous_pages = {}
@@ -270,7 +270,7 @@
         elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:
             pass
         else:
-            log.error('Invalid value renderer: %s' % self.service_item_type)
+            log.error('Invalid value renderer: {item}'.format(item=self.service_item_type))
         self.title = clean_tags(self.title)
         # The footer should never be None, but to be compatible with a few
         # nightly builds between 1.9.4 and 1.9.5, we have to correct this to
@@ -325,7 +325,8 @@
         self.service_item_type = ServiceItemType.Command
         # If the item should have a display title but this frame doesn't have one, we make one up
         if self.is_capable(ItemCapabilities.HasDisplayTitle) and not display_title:
-            display_title = translate('OpenLP.ServiceItem', '[slide %d]') % (len(self._raw_frames) + 1)
+            display_title = translate('OpenLP.ServiceItem',
+                                      '[slide {frame:d}]').format(frame=len(self._raw_frames) + 1)
         # Update image path to match servicemanager location if file was loaded from service
         if image and not self.has_original_files and self.name == 'presentations':
             file_location = os.path.join(path, file_name)
@@ -390,7 +391,7 @@
         :param path: Defaults to *None*. This is the service manager path for things which have their files saved
             with them or None when the saved service is lite and the original file paths need to be preserved.
         """
-        log.debug('set_from_service called with path %s' % path)
+        log.debug('set_from_service called with path {path}'.format(path=path))
         header = service_item['serviceitem']['header']
         self.title = header['title']
         self.name = header['name']
@@ -606,11 +607,13 @@
         start = None
         end = None
         if self.start_time != 0:
-            start = translate('OpenLP.ServiceItem', '<strong>Start</strong>: %s') % \
-                str(datetime.timedelta(seconds=self.start_time))
+            time = str(datetime.timedelta(seconds=self.start_time))
+            start = translate('OpenLP.ServiceItem',
+                              '<strong>Start</strong>: {start}').format(start=time)
         if self.media_length != 0:
-            end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: %s') % \
-                str(datetime.timedelta(seconds=self.media_length // 1000))
+            length = str(datetime.timedelta(seconds=self.media_length // 1000))
+            end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: {length}').format(length=length)
+
         if not start and not end:
             return ''
         elif start and not end:
@@ -618,7 +621,7 @@
         elif not start and end:
             return end
         else:
-            return '%s <br>%s' % (start, end)
+            return '{start} <br>{end}'.format(start=start, end=end)
 
     def update_theme(self, theme):
         """

=== modified file 'openlp/core/lib/theme.py'
--- openlp/core/lib/theme.py	2016-04-30 15:40:23 +0000
+++ openlp/core/lib/theme.py	2016-05-16 13:50:39 +0000
@@ -427,7 +427,7 @@
         try:
             theme_xml = objectify.fromstring(xml)
         except etree.XMLSyntaxError:
-            log.exception('Invalid xml %s', xml)
+            log.exception('Invalid xml {text}'.format(text=xml))
             return
         xml_iter = theme_xml.getiterator()
         for element in xml_iter:
@@ -513,6 +513,7 @@
         theme_strings = []
         for key in dir(self):
             if key[0:1] != '_':
+                # TODO: Verify spacing format before converting to python3 string
                 theme_strings.append('%30s: %s' % (key, getattr(self, key)))
         return '\n'.join(theme_strings)
 

=== modified file 'openlp/core/lib/ui.py'
--- openlp/core/lib/ui.py	2016-03-31 16:34:22 +0000
+++ openlp/core/lib/ui.py	2016-05-16 13:50:39 +0000
@@ -165,7 +165,7 @@
             kwargs.setdefault('icon', ':/services/service_down.png')
             kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.'))
         else:
-            log.warning('The role "%s" is not defined in create_push_button().', role)
+            log.warning('The role "{role}" is not defined in create_push_button().'.format(role=role))
     if kwargs.pop('btn_class', '') == 'toolbutton':
         button = QtWidgets.QToolButton(parent)
     else:
@@ -183,7 +183,7 @@
         button.clicked.connect(kwargs.pop('click'))
     for key in list(kwargs.keys()):
         if key not in ['text', 'icon', 'tooltip', 'click']:
-            log.warning('Parameter %s was not consumed in create_button().', key)
+            log.warning('Parameter {key} was not consumed in create_button().'.format(key=key))
     return button
 
 
@@ -270,7 +270,7 @@
         action.triggered.connect(kwargs.pop('triggers'))
     for key in list(kwargs.keys()):
         if key not in ['text', 'icon', 'tooltip', 'statustip', 'checked', 'can_shortcuts', 'category', 'triggers']:
-            log.warning('Parameter %s was not consumed in create_action().' % key)
+            log.warning('Parameter {key} was not consumed in create_action().'.format(key=key))
     return action
 
 

=== modified file 'openlp/core/lib/webpagereader.py'
--- openlp/core/lib/webpagereader.py	2016-04-05 18:33:50 +0000
+++ openlp/core/lib/webpagereader.py	2016-05-16 13:50:39 +0000
@@ -133,37 +133,37 @@
         time.sleep(0.1)
         try:
             page = urllib.request.urlopen(req, timeout=CONNECTION_TIMEOUT)
-            log.debug('Downloaded page {}'.format(page.geturl()))
+            log.debug('Downloaded page {text}'.format(text=page.geturl()))
             break
         except urllib.error.URLError as err:
-            log.exception('URLError on {}'.format(url))
-            log.exception('URLError: {}'.format(err.reason))
+            log.exception('URLError on {text}'.format(text=url))
+            log.exception('URLError: {text}'.format(text=err.reason))
             page = None
             if retries > CONNECTION_RETRIES:
                 raise
         except socket.timeout:
-            log.exception('Socket timeout: {}'.format(url))
+            log.exception('Socket timeout: {text}'.format(text=url))
             page = None
             if retries > CONNECTION_RETRIES:
                 raise
         except socket.gaierror:
-            log.exception('Socket gaierror: {}'.format(url))
+            log.exception('Socket gaierror: {text}'.format(text=url))
             page = None
             if retries > CONNECTION_RETRIES:
                 raise
         except ConnectionRefusedError:
-            log.exception('ConnectionRefused: {}'.format(url))
+            log.exception('ConnectionRefused: {text}'.format(text=url))
             page = None
             if retries > CONNECTION_RETRIES:
                 raise
             break
         except ConnectionError:
-            log.exception('Connection error: {}'.format(url))
+            log.exception('Connection error: {text}'.format(text=url))
             page = None
             if retries > CONNECTION_RETRIES:
                 raise
         except HTTPException:
-            log.exception('HTTPException error: {}'.format(url))
+            log.exception('HTTPException error: {text}'.format(text=url))
             page = None
             if retries > CONNECTION_RETRIES:
                 raise
@@ -173,7 +173,7 @@
     if update_openlp:
         Registry().get('application').process_events()
     if not page:
-        log.exception('{} could not be downloaded'.format(url))
+        log.exception('{text} could not be downloaded'.format(text=url))
         return None
     log.debug(page)
     return page

=== modified file 'tests/functional/openlp_core_lib/test_projector_pjlink1.py'
--- tests/functional/openlp_core_lib/test_projector_pjlink1.py	2016-04-24 11:22:04 +0000
+++ tests/functional/openlp_core_lib/test_projector_pjlink1.py	2016-05-16 13:50:39 +0000
@@ -112,7 +112,7 @@
     @patch.object(pjlink_test, 'projectorReceivedData')
     def projector_process_lamp_test(self, mock_projectorReceivedData):
         """
-        Test setting lamp on/off and hours
+        Test status lamp on/off and hours
         """
         # GIVEN: Test object
         pjlink = pjlink_test
@@ -129,7 +129,7 @@
     @patch.object(pjlink_test, 'projectorReceivedData')
     def projector_process_multiple_lamp_test(self, mock_projectorReceivedData):
         """
-        Test setting multiple lamp on/off and hours
+        Test status multiple lamp on/off and hours
         """
         # GIVEN: Test object
         pjlink = pjlink_test
@@ -156,7 +156,7 @@
     @patch.object(pjlink_test, 'projectorReceivedData')
     def projector_process_power_on_test(self, mock_projectorReceivedData):
         """
-        Test setting power to ON
+        Test status power to ON
         """
         # GIVEN: Test object and preset
         pjlink = pjlink_test
@@ -171,7 +171,7 @@
     @patch.object(pjlink_test, 'projectorReceivedData')
     def projector_process_power_off_test(self, mock_projectorReceivedData):
         """
-        Test setting power to STANDBY
+        Test status power to STANDBY
         """
         # GIVEN: Test object and preset
         pjlink = pjlink_test
@@ -182,3 +182,71 @@
 
         # THEN: Power should be set to STANDBY
         self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY')
+
+    @patch.object(pjlink_test, 'projectorUpdateIcons')
+    def projector_process_avmt_closed_unmuted_test(self, mock_projectorReceivedData):
+        """
+        Test avmt status shutter closed and audio muted
+        """
+        # GIVEN: Test object
+        pjlink = pjlink_test
+        pjlink.shutter = False
+        pjlink.mute = True
+
+        # WHEN: Called with setting shutter closed and mute off
+        pjlink.process_avmt('11')
+
+        # THEN: Shutter should be True and mute should be False
+        self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
+        self.assertFalse(pjlink.mute, 'Audio should be off')
+
+    @patch.object(pjlink_test, 'projectorUpdateIcons')
+    def projector_process_avmt_open_muted_test(self, mock_projectorReceivedData):
+        """
+        Test avmt status shutter open and mute on
+        """
+        # GIVEN: Test object
+        pjlink = pjlink_test
+        pjlink.shutter = True
+        pjlink.mute = False
+
+        # WHEN: Called with setting shutter closed and mute on
+        pjlink.process_avmt('21')
+
+        # THEN: Shutter should be closed and mute should be True
+        self.assertFalse(pjlink.shutter, 'Shutter should have been set to closed')
+        self.assertTrue(pjlink.mute, 'Audio should be off')
+
+    @patch.object(pjlink_test, 'projectorUpdateIcons')
+    def projector_process_avmt_open_unmuted_test(self, mock_projectorReceivedData):
+        """
+        Test avmt status shutter open and mute off off
+        """
+        # GIVEN: Test object
+        pjlink = pjlink_test
+        pjlink.shutter = True
+        pjlink.mute = True
+
+        # WHEN: Called with setting shutter to closed and mute on
+        pjlink.process_avmt('30')
+
+        # THEN: Shutter should be closed and mute should be True
+        self.assertFalse(pjlink.shutter, 'Shutter should have been set to open')
+        self.assertFalse(pjlink.mute, 'Audio should be on')
+
+    @patch.object(pjlink_test, 'projectorUpdateIcons')
+    def projector_process_avmt_closed_muted_test(self, mock_projectorReceivedData):
+        """
+        Test avmt status shutter closed and mute off
+        """
+        # GIVEN: Test object
+        pjlink = pjlink_test
+        pjlink.shutter = False
+        pjlink.mute = False
+
+        # WHEN: Called with setting shutter to closed and mute on
+        pjlink.process_avmt('31')
+
+        # THEN: Shutter should be closed and mute should be True
+        self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
+        self.assertTrue(pjlink.mute, 'Audio should be on')


Follow ups