← Back to team overview

openlp-core team mailing list archive

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

 

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

Commit message:
Convert strings in openlp/core/ui files to python3 format

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~alisonken1/openlp/strings-core-ui/+merge/295343

- Convert strings in openlp/core/ui files
- Updated projectordb test

--------------------------------
lp:~alisonken1/openlp/strings-core-ui (revision 2667)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/1566/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/1477/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/1415/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Windows_Functional_Tests/1195/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Windows_Interface_Tests/785/
[SUCCESS] https://ci.openlp.io/job/Branch-05a-Code_Analysis/853/
[SUCCESS] https://ci.openlp.io/job/Branch-05b-Test_Coverage/721/

-- 
Your team OpenLP Core is requested to review the proposed merge of lp:~alisonken1/openlp/strings-core-ui into lp:openlp.
=== modified file 'openlp/core/lib/projector/db.py'
--- openlp/core/lib/projector/db.py	2016-04-16 21:32:56 +0000
+++ openlp/core/lib/projector/db.py	2016-05-20 16:33:06 +0000
@@ -131,7 +131,7 @@
         """
         Return basic representation of Source table entry.
         """
-        return '<Source(pjlink_name="{name}", pjlink_code="{code}", text="{Text}")>'.format(name=self.pjlink_name,
+        return '<Source(pjlink_name="{name}", pjlink_code="{code}", text="{text}")>'.format(name=self.pjlink_name,
                                                                                             code=self.pjlink_code,
                                                                                             text=self.text)
     model_id = Column(Integer, ForeignKey('model.id'))

=== modified file 'openlp/core/ui/aboutdialog.py'
--- openlp/core/ui/aboutdialog.py	2015-12-31 22:46:06 +0000
+++ openlp/core/ui/aboutdialog.py	2016-05-20 16:33:06 +0000
@@ -88,7 +88,7 @@
 
         :param about_dialog: The QDialog object to translate
         """
-        about_dialog.setWindowTitle('%s OpenLP' % UiStrings().About)
+        about_dialog.setWindowTitle('{about} OpenLP'.format(about=UiStrings().About))
         self.about_text_edit.setPlainText(
             translate('OpenLP.AboutForm',
                       'OpenLP <version><revision> - Open Source Lyrics Projection\n'
@@ -200,115 +200,115 @@
                                  '    bring this software to you for free because\n'
                                  '    He has set us free.')
         self.credits_text_edit.setPlainText(
-            '%s\n'
-            '    %s\n'
-            '\n'
-            '%s\n'
-            '    %s\n'
-            '\n'
-            '%s\n'
-            '    %s\n'
-            '\n'
-            '%s\n'
-            '    %s\n'
-            '\n'
-            '%s\n'
-            '    %s\n'
-            '\n'
-            '%s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '    %s\n'
-            '        %s\n'
-            '\n'
-            '%s\n'
-            '    %s\n'
-            '\n'
-            '%s\n%s' %
-            (project_lead, lead,
-             devs, '\n    '.join(developers),
-             cons, '\n    '.join(contributors),
-             tests, '\n    '.join(testers),
-             packs, '\n    '.join(packagers),
-             laters,
-             af, '\n        '.join(translators['af']),
-             cs, '\n        '.join(translators['cs']),
-             da, '\n        '.join(translators['da']),
-             de, '\n        '.join(translators['de']),
-             el, '\n        '.join(translators['el']),
-             gb, '\n        '.join(translators['en_GB']),
-             enza, '\n        '.join(translators['en_ZA']),
-             es, '\n        '.join(translators['es']),
-             et, '\n        '.join(translators['et']),
-             fi, '\n        '.join(translators['fi']),
-             fr, '\n        '.join(translators['fr']),
-             hu, '\n        '.join(translators['hu']),
-             ind, '\n        '.join(translators['id']),
-             ja, '\n        '.join(translators['ja']),
-             nb, '\n        '.join(translators['nb']),
-             nl, '\n        '.join(translators['nl']),
-             pl, '\n        '.join(translators['pl']),
-             ptbr, '\n        '.join(translators['pt_BR']),
-             ru, '\n        '.join(translators['ru']),
-             sv, '\n        '.join(translators['sv']),
-             talk, '\n        '.join(translators['ta_LK']),
-             zhcn, '\n        '.join(translators['zh_CN']),
-             documentation, '\n    '.join(documentors),
-             built_with, final_credit))
+            '{titleLead}\n'
+            '    {nameLead}\n'
+            '\n'
+            '{titleDevs}\n'
+            '    {nameDevs}\n'
+            '\n'
+            '{titleContrib}\n'
+            '    {nameContrib}\n'
+            '\n'
+            '{titleTesters}\n'
+            '    {nameTesters}\n'
+            '\n'
+            '{titlePackagers}\n'
+            '    {namePackagers}\n'
+            '\n'
+            '{titleTranslators}\n'
+            '    {titleAF}\n'
+            '        {nameAF}\n'
+            '    {titleCS}\n'
+            '        {nameCS}\n'
+            '    {titleDA}\n'
+            '        {nameDA}\n'
+            '    {titleDE}\n'
+            '        {nameDE}\n'
+            '    {titleEL}\n'
+            '        {nameEL}\n'
+            '    {titleGB}\n'
+            '        {nameGB}\n'
+            '    {titleENZA}\n'
+            '        {nameENZA}\n'
+            '    {titleES}\n'
+            '        {nameES}\n'
+            '    {titleET}\n'
+            '        {nameET}\n'
+            '    {titleFI}\n'
+            '        {nameFI}\n'
+            '    {titleFR}\n'
+            '        {nameFR}\n'
+            '    {titleHU}\n'
+            '        {nameHU}\n'
+            '    {titleIND}\n'
+            '        {nameIND}\n'
+            '    {titleJA}\n'
+            '        {nameJA}\n'
+            '    {titleNB}\n'
+            '        {nameNB}\n'
+            '    {titleNL}\n'
+            '        {nameNL}\n'
+            '    {titlePL}\n'
+            '        {namePL}\n'
+            '    {titlePTBR}\n'
+            '        {namePTBR}\n'
+            '    {titleRU}\n'
+            '        {nameRU}\n'
+            '    {titleSV}\n'
+            '        {nameSV}\n'
+            '    {titleTALK}\n'
+            '        {nameTALK}\n'
+            '    {titleZHCN}\n'
+            '        {nameZHCN}\n'
+            '\n'
+            '{titleDOCS}\n'
+            '    {nameDOCS}\n'
+            '\n'
+            '{build}\n{final}'.format(titleLead=project_lead, nameLead=lead,
+                                      titleDevs=devs, nameDevs='\n    '.join(developers),
+                                      titleContrib=cons, nameContrib='\n    '.join(contributors),
+                                      titleTesters=tests, nameTesters='\n    '.join(testers),
+                                      titlePackagers=packs, namePackagers='\n    '.join(packagers),
+                                      titleTranslators=laters,
+                                      titleAF=af, nameAF='\n        '.join(translators['af']),
+                                      titleCS=cs, nameCS='\n        '.join(translators['cs']),
+                                      titleDA=da, nameDA='\n        '.join(translators['da']),
+                                      titleDE=de, nameDE='\n        '.join(translators['de']),
+                                      titleEL=el, nameEL='\n        '.join(translators['el']),
+                                      titleGB=gb, nameGB='\n        '.join(translators['en_GB']),
+                                      titleENZA=enza, nameENZA='\n        '.join(translators['en_ZA']),
+                                      titleES=es, nameES='\n        '.join(translators['es']),
+                                      titleET=et, nameET='\n        '.join(translators['et']),
+                                      titleFI=fi, nameFI='\n        '.join(translators['fi']),
+                                      titleFR=fr, nameFR='\n        '.join(translators['fr']),
+                                      titleHU=hu, nameHU='\n        '.join(translators['hu']),
+                                      titleIND=ind, nameIND='\n        '.join(translators['id']),
+                                      titleJA=ja, nameJA='\n        '.join(translators['ja']),
+                                      titleNB=nb, nameNB='\n        '.join(translators['nb']),
+                                      titleNL=nl, nameNL='\n        '.join(translators['nl']),
+                                      titlePL=pl, namePL='\n        '.join(translators['pl']),
+                                      titlePTBR=ptbr, namePTBR='\n        '.join(translators['pt_BR']),
+                                      titleRU=ru, nameRU='\n        '.join(translators['ru']),
+                                      titleSV=sv, nameSV='\n        '.join(translators['sv']),
+                                      titleTALK=talk, nameTALK='\n        '.join(translators['ta_LK']),
+                                      titleZHCN=zhcn, nameZHCN='\n        '.join(translators['zh_CN']),
+                                      titleDOCS=documentation, nameDOCS='\n    '.join(documentors),
+                                      build=built_with,
+                                      final=final_credit))
         self.about_notebook.setTabText(self.about_notebook.indexOf(self.credits_tab),
                                        translate('OpenLP.AboutForm', 'Credits'))
+        cr_others = ('Tim Bentley, Gerald Britton, Jonathan Corwin, Samuel Findlay, '
+                     'Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, '
+                     'Armin K\xf6hler, Erik Lundin, Edwin Lunando, Joshua Miller, '
+                     'Brian T. Meyer, Stevan Pettit, Andreas Preikschat, '
+                     'Mattias P\xf5ldaru, Christian Richter, Philip Ridout, '
+                     'Ken Roberts, Simon Scudder, Jeffrey Smith, Maikel Stuivenberg, '
+                     'Martin Thompson, Jon Tibble, Dave Warnock, Frode Woldsund, '
+                     'Martin Zibricky, Patrick Zimmermann')
         copyright_note = translate('OpenLP.AboutForm',
-                                   'Copyright \xa9 2004-2016 %s\n'
-                                   'Portions copyright \xa9 2004-2016 %s') % \
-            ('Raoul Snyman',
-             'Tim Bentley, Gerald Britton, Jonathan Corwin, Samuel Findlay, '
-             'Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, '
-             'Armin K\xf6hler, Erik Lundin, Edwin Lunando, Joshua Miller, '
-             'Brian T. Meyer, Stevan Pettit, Andreas Preikschat, '
-             'Mattias P\xf5ldaru, Christian Richter, '
-             'Philip Ridout, Simon Scudder, Jeffrey Smith, Maikel Stuivenberg, '
-             'Martin Thompson, Jon Tibble, Dave Warnock, Frode Woldsund, '
-             'Martin Zibricky, Patrick Zimmermann')
+                                   'Copyright \xa9 2004-2016 {cr}\n\n'
+                                   'Portions copyright \xa9 2004-2016 {others}').format(cr='Raoul Snyman',
+                                                                                        others=cr_others)
         licence = translate('OpenLP.AboutForm',
                             'This program is free software; you can redistribute it and/or '
                             'modify it under the terms of the GNU General Public License as '
@@ -690,7 +690,11 @@
                     'linking proprietary applications with the library. If this is '
                     'what you want to do, use the GNU Lesser General Public License '
                     'instead of this License.')
-        self.license_text_edit.setPlainText('%s\n\n%s\n\n%s\n\n\n%s' % (copyright_note, licence, disclaimer, gpl_text))
+        self.license_text_edit.setPlainText('{crnote}\n\n{license}\n\n{disclaimer}'
+                                            '\n\n\n{gpl}'.format(crnote=copyright_note,
+                                                                 license=licence,
+                                                                 disclaimer=disclaimer,
+                                                                 gpl=gpl_text))
         self.about_notebook.setTabText(self.about_notebook.indexOf(self.license_tab),
                                        translate('OpenLP.AboutForm', 'License'))
         self.volunteer_button.setText(translate('OpenLP.AboutForm', 'Volunteer'))

=== modified file 'openlp/core/ui/aboutform.py'
--- openlp/core/ui/aboutform.py	2016-04-04 19:53:54 +0000
+++ openlp/core/ui/aboutform.py	2016-05-20 16:33:06 +0000
@@ -52,7 +52,7 @@
         about_text = self.about_text_edit.toPlainText()
         about_text = about_text.replace('<version>', application_version['version'])
         if application_version['build']:
-            build_text = translate('OpenLP.AboutForm', ' build %s') % application_version['build']
+            build_text = translate('OpenLP.AboutForm', ' build {version}').format(version=application_version['build'])
         else:
             build_text = ''
         about_text = about_text.replace('<revision>', build_text)

=== modified file 'openlp/core/ui/advancedtab.py'
--- openlp/core/ui/advancedtab.py	2016-04-16 21:01:22 +0000
+++ openlp/core/ui/advancedtab.py	2016-05-20 16:33:06 +0000
@@ -308,8 +308,8 @@
         self.service_name_label.setText(translate('OpenLP.AdvancedTab', 'Name:'))
         self.service_name_edit.setToolTip(translate('OpenLP.AdvancedTab', 'Consult the OpenLP manual for usage.'))
         self.service_name_revert_button.setToolTip(
-            translate('OpenLP.AdvancedTab', 'Revert to the default service name "%s".') %
-            UiStrings().DefaultServiceName)
+            translate('OpenLP.AdvancedTab',
+                      'Revert to the default service name "{name}".').format(name=UiStrings().DefaultServiceName))
         self.service_name_example_label.setText(translate('OpenLP.AdvancedTab', 'Example:'))
         self.hide_mouse_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Mouse Cursor'))
         self.hide_mouse_check_box.setText(translate('OpenLP.AdvancedTab', 'Hide mouse cursor when over display window'))
@@ -391,16 +391,16 @@
         # Since data location can be changed, make sure the path is present.
         self.current_data_path = AppLocation.get_data_path()
         if not os.path.exists(self.current_data_path):
-            log.error('Data path not found %s' % self.current_data_path)
+            log.error('Data path not found {path}'.format(path=self.current_data_path))
             answer = QtWidgets.QMessageBox.critical(
                 self, translate('OpenLP.AdvancedTab', 'Data Directory Error'),
-                translate('OpenLP.AdvancedTab', 'OpenLP data directory was not found\n\n%s\n\n'
+                translate('OpenLP.AdvancedTab', 'OpenLP data directory was not found\n\n{path}\n\n'
                           'This data directory was previously changed from the OpenLP '
                           'default location.  If the new location was on removable '
                           'media, that media needs to be made available.\n\n'
                           'Click "No" to stop loading OpenLP. allowing you to fix the the problem.\n\n'
                           'Click "Yes" to reset the data directory to the default '
-                          'location.').replace('%s', self.current_data_path),
+                          'location.').format(path=self.current_data_path),
                 QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
                 QtWidgets.QMessageBox.No)
             if answer == QtWidgets.QMessageBox.No:
@@ -410,7 +410,7 @@
             # Set data location to default.
             settings.remove('advanced/data path')
             self.current_data_path = AppLocation.get_data_path()
-            log.warning('User requested data path set to default %s' % self.current_data_path)
+            log.warning('User requested data path set to default {path}'.format(path=self.current_data_path))
         self.data_directory_label.setText(os.path.abspath(self.current_data_path))
         # Don't allow data directory move if running portable.
         if settings.value('advanced/is portable'):
@@ -542,9 +542,9 @@
         # Make sure they want to change the data.
         answer = QtWidgets.QMessageBox.question(self, translate('OpenLP.AdvancedTab', 'Confirm Data Directory Change'),
                                                 translate('OpenLP.AdvancedTab', 'Are you sure you want to change the '
-                                                          'location of the OpenLP data directory to:\n\n%s\n\nThe data '
-                                                          'directory will be changed when OpenLP is closed.').
-                                                replace('%s', new_data_path),
+                                                          'location of the OpenLP data directory to:\n\n{path}'
+                                                          '\n\nThe data directory will be changed when OpenLP is '
+                                                          'closed.').format(path=new_data_path),
                                                 QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
                                                                                       QtWidgets.QMessageBox.No),
                                                 QtWidgets.QMessageBox.No)
@@ -608,10 +608,10 @@
             answer = QtWidgets.QMessageBox.warning(self,
                                                    translate('OpenLP.AdvancedTab', 'Overwrite Existing Data'),
                                                    translate('OpenLP.AdvancedTab',
-                                                             'WARNING: \n\nThe location you have selected \n\n%s\n\n'
-                                                             'appears to contain OpenLP data files. Do you wish to '
-                                                             'replace these files with the current data files?').
-                                                   replace('%s', os.path.abspath(data_path,)),
+                                                             'WARNING: \n\nThe location you have selected \n\n{path}'
+                                                             '\n\nappears to contain OpenLP data files. Do you wish to '
+                                                             'replace these files with the current data '
+                                                             'files?').format(path=os.path.abspath(data_path,)),
                                                    QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
                                                                                          QtWidgets.QMessageBox.No),
                                                    QtWidgets.QMessageBox.No)

=== modified file 'openlp/core/ui/exceptionform.py'
--- openlp/core/ui/exceptionform.py	2016-04-04 19:53:54 +0000
+++ openlp/core/ui/exceptionform.py	2016-05-20 16:33:06 +0000
@@ -91,6 +91,7 @@
         super(ExceptionForm, self).__init__(None, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
         self.setupUi(self)
         self.settings_section = 'crashreport'
+        # TODO: Need to see how to format strings when string with tags is actually a variable
         self.report_text = '**OpenLP Bug Report**\n' \
             'Version: %s\n\n' \
             '--- Details of the Exception. ---\n\n%s\n\n ' \
@@ -114,21 +115,17 @@
         openlp_version = get_application_version()
         description = self.description_text_edit.toPlainText()
         traceback = self.exception_text_edit.toPlainText()
-        system = translate('OpenLP.ExceptionForm', 'Platform: %s\n') % platform.platform()
-        libraries = 'Python: %s\n' % platform.python_version() + \
-            'Qt5: %s\n' % Qt.qVersion() + \
-            'PyQt5: %s\n' % Qt.PYQT_VERSION_STR + \
-            'QtWebkit: %s\n' % WEBKIT_VERSION + \
-            'SQLAlchemy: %s\n' % sqlalchemy.__version__ + \
-            'SQLAlchemy Migrate: %s\n' % MIGRATE_VERSION + \
-            'BeautifulSoup: %s\n' % bs4.__version__ + \
-            'lxml: %s\n' % etree.__version__ + \
-            'Chardet: %s\n' % CHARDET_VERSION + \
-            'PyEnchant: %s\n' % ENCHANT_VERSION + \
-            'Mako: %s\n' % MAKO_VERSION + \
-            'pyICU: %s\n' % ICU_VERSION + \
-            'pyUNO bridge: %s\n' % self._pyuno_import() + \
-            'VLC: %s\n' % VLC_VERSION
+        system = translate('OpenLP.ExceptionForm', 'Platform: {platform}\n').format(platform=platform.platform())
+        libraries = ('Python: {python}\nQt5: {qt5}\nPyQt5: {pyqt5}\nQtWebkit: {qtwebkit}\nSQLAlchemy: {sqalchemy}\n'
+                     'SQLAlchemy Migrate: {migrate}\nBeautifulSoup: {soup}\nlxml: {etree}\nChardet: {chardet}\n'
+                     'PyEnchant: {enchant}\nMako: {mako}\npyICU: {icu}\npyUNO bridge: {uno}\n'
+                     'VLC: {vlc}\n').format(python=platform.python_version(), qt5=Qt.qVersion(),
+                                            pyqt5=Qt.PYQT_VERSION_STR, qtwebkit=WEBKIT_VERSION,
+                                            sqalchemy=sqlalchemy.__version__, migrate=MIGRATE_VERSION,
+                                            soup=bs4.__version__, etree=etree.__version__, chardet=CHARDET_VERSION,
+                                            enchant=ENCHANT_VERSION, mako=MAKO_VERSION, icu=ICU_VERSION,
+                                            uno=self._pyuno_import(), vlc=VLC_VERSION)
+
         if is_linux():
             if os.environ.get('KDE_FULL_SESSION') == 'true':
                 system += 'Desktop: KDE SC\n'
@@ -178,9 +175,10 @@
                 source = re.sub(r'.*[/\\]openlp[/\\](.*)".*', r'\1', line)
             if ':' in line:
                 exception = line.split('\n')[-1].split(':')[0]
-        subject = 'Bug report: %s in %s' % (exception, source)
+        subject = 'Bug report: {error} in {source}'.format(error=exception, source=source)
         mail_urlquery = QtCore.QUrlQuery()
         mail_urlquery.addQueryItem('subject', subject)
+        # TODO: Find out how to format() text that is in a variable
         mail_urlquery.addQueryItem('body', self.report_text % content)
         if self.file_attachment:
             mail_urlquery.addQueryItem('attach', self.file_attachment)
@@ -199,7 +197,7 @@
         else:
             self.__button_state(False)
         self.description_word_count.setText(
-            translate('OpenLP.ExceptionDialog', 'Description characters to enter : %s') % count)
+            translate('OpenLP.ExceptionDialog', 'Description characters to enter : {count}').format(count=count))
 
     def on_attach_file_button_clicked(self):
         """
@@ -210,7 +208,7 @@
                                                                              'Select Attachment'),
                                                                    Settings().value(self.settings_section +
                                                                                     '/last directory'),
-                                                                   '%s (*)' % UiStrings().AllFiles)
+                                                                   '{text} (*)'.format(text=UiStrings().AllFiles))
         log.info('New files(s) %s', str(files))
         if files:
             self.file_attachment = str(files)

=== modified file 'openlp/core/ui/firsttimeform.py'
--- openlp/core/ui/firsttimeform.py	2016-04-15 16:06:14 +0000
+++ openlp/core/ui/firsttimeform.py	2016-05-20 16:33:06 +0000
@@ -72,7 +72,7 @@
         if self.was_download_cancelled:
             return
         try:
-            urllib.request.urlretrieve('%s%s' % (self.themes_url, self.screenshot),
+            urllib.request.urlretrieve('{host}{name}'.format(host=self.themes_url, name=self.screenshot),
                                        os.path.join(gettempdir(), 'openlp', self.screenshot))
             # Signal that the screenshot has been downloaded
             self.screenshot_downloaded.emit(self.title, self.filename, self.sha256)
@@ -180,11 +180,13 @@
         user_agent = 'OpenLP/' + Registry().get('application').applicationVersion()
         self.application.process_events()
         try:
-            web_config = get_web_page('%s%s' % (self.web, 'download.cfg'), header=('User-Agent', user_agent))
+            web_config = get_web_page('{host}{name}'.format(host=self.web, name='download.cfg'),
+                                      header=('User-Agent', user_agent))
         except (urllib.error.URLError, ConnectionError) as err:
             msg = QtWidgets.QMessageBox()
             title = translate('OpenLP.FirstTimeWizard', 'Network Error')
-            msg.setText('{} {}'.format(title, err.code if hasattr(err, 'code') else ''))
+            msg.setText('{title} {error}'.format(title=title,
+                                                 error=err.code if hasattr(err, 'code') else ''))
             msg.setInformativeText(translate('OpenLP.FirstTimeWizard',
                                              'There was a network error attempting to '
                                              'connect to retrieve initial configuration information'))
@@ -205,6 +207,7 @@
                 trace_error_handler(log)
         self.update_screen_list_combo()
         self.application.process_events()
+        # TODO: Figure out how to use a variable with format()
         self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading %s...')
         if self.has_run_wizard:
             self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active())
@@ -223,9 +226,9 @@
             songs = songs.split(',')
             for song in songs:
                 self.application.process_events()
-                title = self.config.get('songs_%s' % song, 'title')
-                filename = self.config.get('songs_%s' % song, 'filename')
-                sha256 = self.config.get('songs_%s' % song, 'sha256', fallback='')
+                title = self.config.get('songs_{song}'.format(song=song), 'title')
+                filename = self.config.get('songs_{song}'.format(song=song), 'filename')
+                sha256 = self.config.get('songs_{song}'.format(song=song), 'sha256', fallback='')
                 item = QtWidgets.QListWidgetItem(title, self.songs_list_widget)
                 item.setData(QtCore.Qt.UserRole, (filename, sha256))
                 item.setCheckState(QtCore.Qt.Unchecked)
@@ -234,15 +237,15 @@
             bible_languages = bible_languages.split(',')
             for lang in bible_languages:
                 self.application.process_events()
-                language = self.config.get('bibles_%s' % lang, 'title')
+                language = self.config.get('bibles_{lang}'.format(lang=lang), 'title')
                 lang_item = QtWidgets.QTreeWidgetItem(self.bibles_tree_widget, [language])
-                bibles = self.config.get('bibles_%s' % lang, 'translations')
+                bibles = self.config.get('bibles_{lang}'.format(lang=lang), 'translations')
                 bibles = bibles.split(',')
                 for bible in bibles:
                     self.application.process_events()
-                    title = self.config.get('bible_%s' % bible, 'title')
-                    filename = self.config.get('bible_%s' % bible, 'filename')
-                    sha256 = self.config.get('bible_%s' % bible, 'sha256', fallback='')
+                    title = self.config.get('bible_{bible}'.format(bible=bible), 'title')
+                    filename = self.config.get('bible_{bible}'.format(bible=bible), 'filename')
+                    sha256 = self.config.get('bible_{bible}'.format(bible=bible), 'sha256', fallback='')
                     item = QtWidgets.QTreeWidgetItem(lang_item, [title])
                     item.setData(0, QtCore.Qt.UserRole, (filename, sha256))
                     item.setCheckState(0, QtCore.Qt.Unchecked)
@@ -252,10 +255,10 @@
             # Download the theme screenshots
             themes = self.config.get('themes', 'files').split(',')
             for theme in themes:
-                title = self.config.get('theme_%s' % theme, 'title')
-                filename = self.config.get('theme_%s' % theme, 'filename')
-                sha256 = self.config.get('theme_%s' % theme, 'sha256', fallback='')
-                screenshot = self.config.get('theme_%s' % theme, 'screenshot')
+                title = self.config.get('theme_{theme}'.format(theme=theme), 'title')
+                filename = self.config.get('theme_{theme}'.format(theme=theme), 'filename')
+                sha256 = self.config.get('theme_{theme}'.format(theme=theme), 'sha256', fallback='')
+                screenshot = self.config.get('theme_{theme}'.format(theme=theme), 'screenshot')
                 worker = ThemeScreenshotWorker(self.themes_url, title, filename, sha256, screenshot)
                 self.theme_screenshot_workers.append(worker)
                 worker.screenshot_downloaded.connect(self.on_screenshot_downloaded)
@@ -421,7 +424,7 @@
                     self._download_progress(block_count, block_size)
                 filename.close()
                 if sha256 and hasher.hexdigest() != sha256:
-                    log.error('sha256 sums did not match for file: {}'.format(f_path))
+                    log.error('sha256 sums did not match for file: {file}'.format(file=f_path))
                     os.remove(f_path)
                     return False
             except (urllib.error.URLError, socket.timeout) as err:
@@ -447,7 +450,7 @@
         themes = self.config.get('themes', 'files')
         themes = themes.split(',')
         for index, theme in enumerate(themes):
-            screenshot = self.config.get('theme_%s' % theme, 'screenshot')
+            screenshot = self.config.get('theme_{theme}'.format(theme=theme), 'screenshot')
             item = self.themes_list_widget.item(index)
             if item:
                 item.setIcon(build_icon(os.path.join(gettempdir(), 'openlp', screenshot)))
@@ -507,7 +510,7 @@
                 item = self.songs_list_widget.item(i)
                 if item.checkState() == QtCore.Qt.Checked:
                     filename, sha256 = item.data(QtCore.Qt.UserRole)
-                    size = self._get_file_size('%s%s' % (self.songs_url, filename))
+                    size = self._get_file_size('{path}{name}'.format(path=self.songs_url, name=filename))
                     self.max_progress += size
             # Loop through the Bibles list and increase for each selected item
             iterator = QtWidgets.QTreeWidgetItemIterator(self.bibles_tree_widget)
@@ -516,7 +519,7 @@
                 item = iterator.value()
                 if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
                     filename, sha256 = item.data(0, QtCore.Qt.UserRole)
-                    size = self._get_file_size('%s%s' % (self.bibles_url, filename))
+                    size = self._get_file_size('{path}{name}'.format(path=self.bibles_url, name=filename))
                     self.max_progress += size
                 iterator += 1
             # Loop through the themes list and increase for each selected item
@@ -525,7 +528,7 @@
                 item = self.themes_list_widget.item(i)
                 if item.checkState() == QtCore.Qt.Checked:
                     filename, sha256 = item.data(QtCore.Qt.UserRole)
-                    size = self._get_file_size('%s%s' % (self.themes_url, filename))
+                    size = self._get_file_size('{path}{name}'.format(path=self.themes_url, name=filename))
                     self.max_progress += size
         except urllib.error.URLError:
             trace_error_handler(log)
@@ -560,22 +563,26 @@
         if self.max_progress:
             self.progress_bar.setValue(self.progress_bar.maximum())
             if self.has_run_wizard:
-                self.progress_label.setText(translate('OpenLP.FirstTimeWizard',
-                                            'Download complete. Click the %s button to return to OpenLP.') %
-                                            clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
+                text = translate('OpenLP.FirstTimeWizard',
+                                 'Download complete. Click the {button} button to return to OpenLP.'
+                                 ).format(text=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
+                self.progress_label.setText(text)
             else:
-                self.progress_label.setText(translate('OpenLP.FirstTimeWizard',
-                                            'Download complete. Click the %s button to start OpenLP.') %
-                                            clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
+                text = translate('OpenLP.FirstTimeWizard',
+                                 'Download complete. Click the {button} button to start OpenLP.'
+                                 ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
+                self.progress_label.setText()
         else:
             if self.has_run_wizard:
-                self.progress_label.setText(translate('OpenLP.FirstTimeWizard',
-                                            'Click the %s button to return to OpenLP.') %
-                                            clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
+                text = translate('OpenLP.FirstTimeWizard',
+                                 'Click the {button} button to return to OpenLP.'
+                                 ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
+                self.progress_label.setText(text)
             else:
-                self.progress_label.setText(translate('OpenLP.FirstTimeWizard',
-                                            'Click the %s button to start OpenLP.') %
-                                            clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
+                text = translate('OpenLP.FirstTimeWizard',
+                                 'Click the {button} button to start OpenLP.'
+                                 ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
+                self.progress_label.setText()
         self.finish_button.setVisible(True)
         self.finish_button.setEnabled(True)
         self.cancel_button.setVisible(False)
@@ -628,8 +635,9 @@
                 self._increment_progress_bar(self.downloading % filename, 0)
                 self.previous_size = 0
                 destination = os.path.join(songs_destination, str(filename))
-                if not self.url_get_file('%s%s' % (self.songs_url, filename), destination, sha256):
-                    missed_files.append('Song: {}'.format(filename))
+                if not self.url_get_file('{path}{name}'.format(path=self.songs_url, name=filename),
+                                         destination, sha256):
+                    missed_files.append('Song: {name}'.format(name=filename))
         # Download Bibles
         bibles_iterator = QtWidgets.QTreeWidgetItemIterator(self.bibles_tree_widget)
         while bibles_iterator.value():
@@ -638,31 +646,34 @@
                 bible, sha256 = item.data(0, QtCore.Qt.UserRole)
                 self._increment_progress_bar(self.downloading % bible, 0)
                 self.previous_size = 0
-                if not self.url_get_file('%s%s' % (self.bibles_url, bible), os.path.join(bibles_destination, bible),
+                if not self.url_get_file('{path}{name}'.format(path=self.bibles_url, name=bible),
+                                         os.path.join(bibles_destination, bible),
                                          sha256):
-                    missed_files.append('Bible: {}'.format(bible))
+                    missed_files.append('Bible: {name}'.format(name=bible))
             bibles_iterator += 1
         # Download themes
         for i in range(self.themes_list_widget.count()):
             item = self.themes_list_widget.item(i)
             if item.checkState() == QtCore.Qt.Checked:
                 theme, sha256 = item.data(QtCore.Qt.UserRole)
+                # TODO: Verify how to use format() with strings in a variable
                 self._increment_progress_bar(self.downloading % theme, 0)
                 self.previous_size = 0
-                if not self.url_get_file('%s%s' % (self.themes_url, theme), os.path.join(themes_destination, theme),
+                if not self.url_get_file('{path}{name}'.format(path=self.themes_url, name=theme),
+                                         os.path.join(themes_destination, theme),
                                          sha256):
-                    missed_files.append('Theme: {}'.format(theme))
+                    missed_files.append('Theme: {name}'.format(name=theme))
         if missed_files:
             file_list = ''
             for entry in missed_files:
-                file_list += '{}<br \>'.format(entry)
+                file_list += '{text}<br \>'.format(text=entry)
             msg = QtWidgets.QMessageBox()
             msg.setIcon(QtWidgets.QMessageBox.Warning)
             msg.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'Network Error'))
             msg.setText(translate('OpenLP.FirstTimeWizard', 'Unable to download some files'))
             msg.setInformativeText(translate('OpenLP.FirstTimeWizard',
                                              'The following files were not able to be '
-                                             'downloaded:<br \>{}'.format(file_list)))
+                                             'downloaded:<br \>{text}'.format(text=file_list)))
             msg.setStandardButtons(msg.Ok)
             ans = msg.exec()
         return True

=== modified file 'openlp/core/ui/firsttimewizard.py'
--- openlp/core/ui/firsttimewizard.py	2016-04-16 13:58:28 +0000
+++ openlp/core/ui/firsttimewizard.py	2016-05-20 16:33:06 +0000
@@ -228,12 +228,13 @@
         :param first_time_wizard: The wizard form
         """
         first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
-        first_time_wizard.title_label.setText('<span style="font-size:14pt; font-weight:600;">%s</span>' %
-                                              translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard'))
+        text = translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard')
+        first_time_wizard.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}'
+                                              '</span>'.format(text=text))
+        button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.NextButton))
         first_time_wizard.information_label.setText(
             translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. '
-                                                'Click the %s button below to start.') %
-            clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.NextButton)))
+                                                'Click the {button} button below to start.').format(button=button))
         self.download_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading Resource Index'))
         self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while the resource index is '
                                                                            'downloaded.'))
@@ -264,18 +265,19 @@
         self.no_internet_page.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection'))
         self.no_internet_page.setSubTitle(
             translate('OpenLP.FirstTimeWizard', 'Unable to detect an Internet connection.'))
+        button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton))
         self.no_internet_text = translate('OpenLP.FirstTimeWizard',
                                           'No Internet connection was found. The First Time Wizard needs an Internet '
                                           'connection in order to be able to download sample songs, Bibles and themes.'
-                                          '  Click the %s button now to start OpenLP with initial settings and '
+                                          '  Click the {button} button now to start OpenLP with initial settings and '
                                           'no sample data.\n\nTo re-run the First Time Wizard and import this sample '
                                           'data at a later time, check your Internet connection and re-run this '
-                                          'wizard by selecting "Tools/Re-run First Time Wizard" from OpenLP.') % \
-            clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton))
+                                          'wizard by selecting "Tools/Re-run First Time Wizard" from OpenLP.'
+                                          ).format(button=button)
+        button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton))
         self.cancel_wizard_text = translate('OpenLP.FirstTimeWizard',
                                             '\n\nTo cancel the First Time Wizard completely (and not start OpenLP), '
-                                            'click the %s button now.') % \
-            clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton))
+                                            'click the {button} button now.').format(button=button)
         self.songs_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Songs'))
         self.songs_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download public domain songs.'))
         self.bibles_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Bibles'))

=== modified file 'openlp/core/ui/formattingtagcontroller.py'
--- openlp/core/ui/formattingtagcontroller.py	2015-12-31 22:46:06 +0000
+++ openlp/core/ui/formattingtagcontroller.py	2016-05-20 16:33:06 +0000
@@ -72,19 +72,19 @@
         """
         for line_number, html1 in enumerate(self.protected_tags):
             if self._strip(html1['start tag']) == tag:
-                return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag
+                return translate('OpenLP.FormattingTagForm', 'Tag {tag} already defined.').format(tag=tag)
             if self._strip(html1['desc']) == desc:
-                return translate('OpenLP.FormattingTagForm', 'Description %s already defined.') % tag
+                return translate('OpenLP.FormattingTagForm', 'Description {tag} already defined.').format(tag=tag)
         for line_number, html1 in enumerate(self.custom_tags):
             if self._strip(html1['start tag']) == tag:
-                return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag
+                return translate('OpenLP.FormattingTagForm', 'Tag {tag} already defined.').format(tag=tag)
             if self._strip(html1['desc']) == desc:
-                return translate('OpenLP.FormattingTagForm', 'Description %s already defined.') % tag
+                return translate('OpenLP.FormattingTagForm', 'Description {tag} already defined.').format(tag=tag)
         tag = {
             'desc': desc,
-            'start tag': '{%s}' % tag,
+            'start tag': '{{{tag}}}'.format(tag=tag),
             'start html': start_html,
-            'end tag': '{/%s}' % tag,
+            'end tag': '{/{tag}}}'.format(tag=tag),
             'end html': end_html,
             'protected': False,
             'temporary': False
@@ -130,6 +130,7 @@
                     elif not match.group('empty'):
                         end_tags.append(tag)
                 match = self.html_tag_regex.search(start_html, match.end())
+            # TODO: Verify format() works with lambda
             return ''.join(map(lambda tag: '</%s>' % tag, reversed(end_tags)))
 
     def start_tag_changed(self, start_html, end_html):
@@ -146,7 +147,8 @@
         end = self.start_html_to_end_html(start_html)
         if not end_html:
             if not end:
-                return translate('OpenLP.FormattingTagForm', 'Start tag %s is not valid HTML') % start_html, None
+                return translate('OpenLP.FormattingTagForm',
+                                 'Start tag {tag} is not valid HTML').format(tag=start_html), None
             return None, end
         return None, None
 
@@ -165,7 +167,8 @@
         if not end_html:
             return None, end
         if end and end != end_html:
-            return translate('OpenLP.FormattingTagForm',
-                             'End tag %(end)s does not match end tag for start tag %(start)s') % \
-                {'end': end, 'start': start_html}, None
+            return (translate('OpenLP.FormattingTagForm',
+                              'End tag {end} does not match end tag for start tag {start}').format(end=end,
+                                                                                                   start=start_html),
+                    None)
         return None, None

=== modified file 'openlp/core/ui/formattingtagform.py'
--- openlp/core/ui/formattingtagform.py	2016-01-09 16:26:14 +0000
+++ openlp/core/ui/formattingtagform.py	2016-05-20 16:33:06 +0000
@@ -90,9 +90,10 @@
         """
         new_row = self.tag_table_widget.rowCount()
         self.tag_table_widget.insertRow(new_row)
-        self.tag_table_widget.setItem(new_row, 0, QtWidgets.QTableWidgetItem(translate('OpenLP.FormattingTagForm',
-                                                                                       'New Tag %d' % new_row)))
-        self.tag_table_widget.setItem(new_row, 1, QtWidgets.QTableWidgetItem('n%d' % new_row))
+        self.tag_table_widget.setItem(new_row, 0,
+                                      QtWidgets.QTableWidgetItem(translate('OpenLP.FormattingTagForm',
+                                                                           'New Tag {row:d}').format(row=new_row)))
+        self.tag_table_widget.setItem(new_row, 1, QtWidgets.QTableWidgetItem('n{row:d}'.format(row=new_row)))
         self.tag_table_widget.setItem(new_row, 2,
                                       QtWidgets.QTableWidgetItem(translate('OpenLP.FormattingTagForm', '<HTML here>')))
         self.tag_table_widget.setItem(new_row, 3, QtWidgets.QTableWidgetItem(''))

=== modified file 'openlp/core/ui/generaltab.py'
--- openlp/core/ui/generaltab.py	2016-04-17 18:57:03 +0000
+++ openlp/core/ui/generaltab.py	2016-05-20 16:33:06 +0000
@@ -400,7 +400,7 @@
         """
         Select the logo file
         """
-        file_filters = '%s;;%s (*.*)' % (get_images_filter(), UiStrings().AllFiles)
+        file_filters = '{text};;{names} (*.*)'.format(text=get_images_filter(), names=UiStrings().AllFiles)
         filename, filter_used = QtWidgets.QFileDialog.getOpenFileName(self,
                                                                       translate('OpenLP.AdvancedTab', 'Open File'), '',
                                                                       file_filters)

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2016-04-29 20:25:12 +0000
+++ openlp/core/ui/mainwindow.py	2016-05-20 16:33:06 +0000
@@ -471,7 +471,8 @@
         self.web_site_item.setText(translate('OpenLP.MainWindow', '&Web Site'))
         for item in self.language_group.actions():
             item.setText(item.objectName())
-            item.setStatusTip(translate('OpenLP.MainWindow', 'Set the interface language to %s') % item.objectName())
+            item.setStatusTip(translate('OpenLP.MainWindow',
+                                        'Set the interface language to {name}').format(name=item.objectName()))
         self.auto_language_item.setText(translate('OpenLP.MainWindow', '&Autodetect'))
         self.auto_language_item.setStatusTip(translate('OpenLP.MainWindow', 'Use the system language, if available.'))
         self.tools_add_tool_item.setText(translate('OpenLP.MainWindow', 'Add &Tool...'))
@@ -1334,8 +1335,10 @@
         for file_id, filename in enumerate(recent_files_to_display):
             log.debug('Recent file name: {name}'.format(name=filename))
             # TODO: Verify ''.format() before committing
-            action = create_action(self, '', text='&%d %s' % (file_id + 1,
-                                   os.path.splitext(os.path.basename(str(filename)))[0]), data=filename,
+            action = create_action(self, '',
+                                   text='&{n} {name}'.format(n=file_id + 1,
+                                                             name=os.path.splitext(os.path.basename(str(filename)))[0]),
+                                   data=filename,
                                    triggers=self.service_manager_contents.on_recent_service_clicked)
             self.recent_files_menu.addAction(action)
         clear_recent_files_action = create_action(self, '',

=== modified file 'openlp/core/ui/plugindialog.py'
--- openlp/core/ui/plugindialog.py	2016-02-22 14:51:58 +0000
+++ openlp/core/ui/plugindialog.py	2016-05-20 16:33:06 +0000
@@ -74,6 +74,6 @@
         """
         plugin_view_dialog.setWindowTitle(translate('OpenLP.PluginForm', 'Manage Plugins'))
         self.plugin_info_group_box.setTitle(translate('OpenLP.PluginForm', 'Plugin Details'))
-        self.about_label.setText('%s:' % UiStrings().About)
+        self.about_label.setText('{about}:'.format(about=UiStrings().About))
         self.status_label.setText(translate('OpenLP.PluginForm', 'Status:'))
         self.status_checkbox.setText(translate('OpenLP.PluginForm', 'Active'))

=== modified file 'openlp/core/ui/pluginform.py'
--- openlp/core/ui/pluginform.py	2016-02-22 14:51:58 +0000
+++ openlp/core/ui/pluginform.py	2016-05-20 16:33:06 +0000
@@ -60,6 +60,7 @@
         self._clear_details()
         self.programatic_change = True
         plugin_list_width = 0
+        # TODO: See how to use format() with variables
         for plugin in self.plugin_manager.plugins:
             item = QtWidgets.QListWidgetItem(self.plugin_list_widget)
             # We do this just to make 100% sure the status is an integer as
@@ -94,7 +95,7 @@
         """
         Set the details of the currently selected plugin
         """
-        log.debug('PluginStatus: %s', str(self.active_plugin.status))
+        log.debug('PluginStatus: {status}'.format(status=str(self.active_plugin.status)))
         self.about_text_browser.setHtml(self.active_plugin.about())
         self.programatic_change = True
         if self.active_plugin.status != PluginStatus.Disabled:
@@ -136,6 +137,7 @@
             self.active_plugin.app_startup()
         else:
             self.active_plugin.toggle_status(PluginStatus.Inactive)
+        # TODO: Verify using format() with a variable
         status_text = translate('OpenLP.PluginForm', '%s (Inactive)')
         if self.active_plugin.status == PluginStatus.Active:
             status_text = translate('OpenLP.PluginForm', '%s (Active)')

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2016-05-18 17:06:25 +0000
+++ openlp/core/ui/servicemanager.py	2016-05-20 16:33:06 +0000
@@ -118,7 +118,7 @@
                                         tooltip=translate('OpenLP.ServiceManager', 'Save this service.'),
                                         triggers=self.decide_save_method)
         self.toolbar.addSeparator()
-        self.theme_label = QtWidgets.QLabel('%s:' % UiStrings().Theme, widget)
+        self.theme_label = QtWidgets.QLabel('{theme}:'.format(theme=UiStrings().Theme), widget)
         self.theme_label.setContentsMargins(3, 3, 3, 3)
         self.theme_label.setObjectName('theme_label')
         self.toolbar.add_toolbar_widget(self.theme_label)
@@ -503,8 +503,8 @@
         path_file_name = str(self.file_name())
         path, file_name = os.path.split(path_file_name)
         base_name = os.path.splitext(file_name)[0]
-        service_file_name = '%s.osj' % base_name
-        self.log_debug('ServiceManager.save_file - %s' % path_file_name)
+        service_file_name = '{name}.osj'.format(name=base_name)
+        self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
         Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
         service = self.create_basic_service()
         write_list = []
@@ -530,8 +530,9 @@
             self.application.set_normal_cursor()
             title = translate('OpenLP.ServiceManager', 'Service File(s) Missing')
             message = translate('OpenLP.ServiceManager',
-                                'The following file(s) in the service are missing: %s\n\n'
-                                'These files will be removed if you continue to save.') % "\n\t".join(missing_list)
+                                'The following file(s) in the service are missing: {name}\n\n'
+                                'These files will be removed if you continue to save.'
+                                ).format(name="\n\t".join(missing_list))
             answer = QtWidgets.QMessageBox.critical(self, title, message,
                                                     QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok |
                                                                                           QtWidgets.QMessageBox.Cancel))
@@ -561,7 +562,7 @@
         service_content = json.dumps(service)
         # Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be extracted using unzip in UNIX.
         allow_zip_64 = (total_size > 2147483648 + len(service_content))
-        self.log_debug('ServiceManager.save_file - allowZip64 is %s' % allow_zip_64)
+        self.log_debug('ServiceManager.save_file - allowZip64 is {text}'.format(text=allow_zip_64))
         zip_file = None
         success = True
         self.main_window.increment_progress_bar()
@@ -584,7 +585,7 @@
                     shutil.copy(audio_from, save_file)
                 zip_file.write(audio_from, audio_to)
         except IOError:
-            self.log_exception('Failed to save service to disk: %s' % temp_file_name)
+            self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))
             self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
                                            translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
             success = False
@@ -601,7 +602,7 @@
             except OSError as ose:
                 QtWidgets.QMessageBox.critical(self, translate('OpenLP.ServiceManager', 'Error Saving File'),
                                                translate('OpenLP.ServiceManager', 'An error occurred while writing the '
-                                                         'service file: %s') % ose.strerror,
+                                                         'service file: {error}').format(error=ose.strerror),
                                                QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
                 success = False
             self.main_window.add_recent_file(path_file_name)
@@ -623,8 +624,8 @@
         path_file_name = str(self.file_name())
         path, file_name = os.path.split(path_file_name)
         base_name = os.path.splitext(file_name)[0]
-        service_file_name = '%s.osj' % base_name
-        self.log_debug('ServiceManager.save_file - %s' % path_file_name)
+        service_file_name = '{name}.osj'.format(name=base_name)
+        self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
         Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
         service = self.create_basic_service()
         self.application.set_busy_cursor()
@@ -645,7 +646,7 @@
             # First we add service contents.
             zip_file.writestr(service_file_name, service_content)
         except IOError:
-            self.log_exception('Failed to save service to disk: %s', temp_file_name)
+            self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))
             self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
                                            translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
             success = False
@@ -740,13 +741,13 @@
                 try:
                     ucs_file = zip_info.filename
                 except UnicodeDecodeError:
-                    self.log_exception('file_name "%s" is not valid UTF-8' % zip_info.file_name)
+                    self.log_exception('file_name "{name}" is not valid UTF-8'.format(name=zip_info.file_name))
                     critical_error_message_box(message=translate('OpenLP.ServiceManager',
                                                'File is not a valid service.\n The content encoding is not UTF-8.'))
                     continue
                 os_file = ucs_file.replace('/', os.path.sep)
                 os_file = os.path.basename(os_file)
-                self.log_debug('Extract file: %s' % os_file)
+                self.log_debug('Extract file: {name}'.format(name=os_file))
                 zip_info.filename = os_file
                 zip_file.extract(zip_info, self.service_path)
                 if os_file.endswith('osj') or os_file.endswith('osd'):
@@ -774,18 +775,18 @@
                 critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
                 self.log_error('File contains no service data')
         except (IOError, NameError, zipfile.BadZipfile):
-            self.log_exception('Problem loading service file %s' % file_name)
+            self.log_exception('Problem loading service file {name}'.format(name=file_name))
             critical_error_message_box(message=translate('OpenLP.ServiceManager',
                                        'File could not be opened because it is corrupt.'))
         except zipfile.BadZipfile:
             if os.path.getsize(file_name) == 0:
-                self.log_exception('Service file is zero sized: %s' % file_name)
+                self.log_exception('Service file is zero sized: {name}'.format(name=file_name))
                 QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
                                                   translate('OpenLP.ServiceManager',
                                                             'This service file does not contain '
                                                             'any data.'))
             else:
-                self.log_exception('Service file is cannot be extracted as zip: %s' % file_name)
+                self.log_exception('Service file is cannot be extracted as zip: {name}'.format(name=file_name))
                 QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'),
                                                   translate('OpenLP.ServiceManager',
                                                             'This file is either corrupt or it is not an OpenLP 2 '
@@ -874,7 +875,7 @@
             self.auto_play_slides_loop.setChecked(service_item['service_item'].auto_play_slides_loop)
             self.timed_slide_interval.setChecked(service_item['service_item'].timed_slide_interval > 0)
             if service_item['service_item'].timed_slide_interval > 0:
-                delay_suffix = ' %s s' % str(service_item['service_item'].timed_slide_interval)
+                delay_suffix = ' {text} s'.format(text=str(service_item['service_item'].timed_slide_interval))
             else:
                 delay_suffix = ' ...'
             self.timed_slide_interval.setText(
@@ -1268,14 +1269,17 @@
             tree_widget_item.setText(0, service_item_from_item.get_display_title())
             tips = []
             if service_item_from_item.temporary_edit:
-                tips.append('<strong>%s:</strong> <em>%s</em>' % (translate('OpenLP.ServiceManager', 'Edit'),
-                            (translate('OpenLP.ServiceManager', 'Service copy only'))))
+                text1 = translate('OpenLP.ServiceManager', 'Edit')
+                text2 = translate('OpenLP.ServiceManager', 'Service copy only')
+                tips.append('<strong>{text1}:</strong> <em>{text2}</em>'.format(text1=text1, text2=text2))
             if service_item_from_item.theme and service_item_from_item.theme != -1:
-                tips.append('<strong>%s:</strong> <em>%s</em>' %
-                            (translate('OpenLP.ServiceManager', 'Slide theme'), service_item_from_item.theme))
+                text = translate('OpenLP.ServiceManager', 'Slide theme')
+                tips.append('<strong>{text1}:</strong> <em>{text2}</em>'.format(text1=text,
+                                                                                text2=service_item_from_item.theme))
             if service_item_from_item.notes:
-                tips.append('<strong>%s: </strong> %s' %
-                            (translate('OpenLP.ServiceManager', 'Notes'), html.escape(service_item_from_item.notes)))
+                text1 = translate('OpenLP.ServiceManager', 'Notes')
+                text2 = html.escape(service_item_from_item.notes)
+                tips.append('<strong>{text1}: </strong> {text2}'.format(text1=text1, text2=text2))
             if item['service_item'].is_capable(ItemCapabilities.HasVariableStartTime):
                 tips.append(item['service_item'].get_media_time())
             tree_widget_item.setToolTip(0, '<br>'.join(tips))
@@ -1637,7 +1641,7 @@
                             replace = True
                     else:
                         self.drop_position = get_parent_item_data(item) - 1
-                Registry().execute('%s_add_service_item' % plugin, replace)
+                Registry().execute('{plugin}_add_service_item'.format(plugin=plugin), replace)
 
     def update_theme_list(self, theme_list):
         """

=== modified file 'openlp/core/ui/settingsform.py'
--- openlp/core/ui/settingsform.py	2016-03-29 16:55:33 +0000
+++ openlp/core/ui/settingsform.py	2016-05-20 16:33:06 +0000
@@ -85,7 +85,7 @@
         :param tab_widget: The widget to add
         :param is_visible: If this tab should be visible
         """
-        log.debug('Inserting %s tab' % tab_widget.tab_title)
+        log.debug('Inserting {text} tab'.format(text=tab_widget.tab_title))
         # add the tab to get it to display in the correct part of the screen
         self.stacked_layout.addWidget(tab_widget)
         if is_visible:

=== modified file 'openlp/core/ui/shortcutlistform.py'
--- openlp/core/ui/shortcutlistform.py	2016-03-31 16:34:22 +0000
+++ openlp/core/ui/shortcutlistform.py	2016-05-20 16:33:06 +0000
@@ -425,11 +425,12 @@
                 if changing_action.shortcutContext() in [QtCore.Qt.WindowShortcut, QtCore.Qt.ApplicationShortcut]:
                     is_valid = False
         if not is_valid:
+            text = translate('OpenLP.ShortcutListDialog',
+                             'The shortcut "{key}" is already assigned to another action, please'
+                             ' use a different shortcut.'
+                             ).format(key=self.get_shortcut_string(key_sequence))
             self.main_window.warning_message(translate('OpenLP.ShortcutListDialog', 'Duplicate Shortcut'),
-                                             translate('OpenLP.ShortcutListDialog',
-                                                       'The shortcut "%s" is already assigned to another action, please'
-                                                       ' use a different shortcut.') %
-                                             self.get_shortcut_string(key_sequence, for_display=True))
+                                             text, for_display=True)
             self.dialog_was_shown = True
         return is_valid
 

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2016-05-05 03:57:04 +0000
+++ openlp/core/ui/slidecontroller.py	2016-05-20 16:33:06 +0000
@@ -98,7 +98,7 @@
         """
         sender = self.sender().objectName() if self.sender().objectName() else self.sender().text()
         controller = self
-        Registry().execute('%s' % sender, [controller, args])
+        Registry().execute('{text}'.format(text=sender), [controller, args])
 
 
 class InfoLabel(QtWidgets.QLabel):
@@ -395,7 +395,7 @@
                 {'key': 'O', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Other"')}
             ]
             shortcuts.extend([{'key': str(number)} for number in range(10)])
-            self.controller.addActions([create_action(self, 'shortcutAction_%s' % s['key'],
+            self.controller.addActions([create_action(self, 'shortcutAction_{key}'.format(key=s['key']),
                                                       text=s.get('text'),
                                                       can_shortcuts=True,
                                                       context=QtCore.Qt.WidgetWithChildrenShortcut,
@@ -417,14 +417,20 @@
             self.preview_widget.doubleClicked.connect(self.on_preview_double_click)
             self.toolbar.set_widget_visible(['editSong'], False)
             self.controller.addActions([self.next_item, self.previous_item])
-        Registry().register_function('slidecontroller_%s_stop_loop' % self.type_prefix, self.on_stop_loop)
-        Registry().register_function('slidecontroller_%s_change' % self.type_prefix, self.on_slide_change)
-        Registry().register_function('slidecontroller_%s_blank' % self.type_prefix, self.on_slide_blank)
-        Registry().register_function('slidecontroller_%s_unblank' % self.type_prefix, self.on_slide_unblank)
+        Registry().register_function('slidecontroller_{text}_stop_loop'.format(text=self.type_prefix),
+                                     self.on_stop_loop)
+        Registry().register_function('slidecontroller_{text}_change'.format(text=self.type_prefix),
+                                     self.on_slide_change)
+        Registry().register_function('slidecontroller_{text}_blank'.format(text=self.type_prefix),
+                                     self.on_slide_blank)
+        Registry().register_function('slidecontroller_{text}_unblank'.format(text=self.type_prefix),
+                                     self.on_slide_unblank)
         Registry().register_function('slidecontroller_update_slide_limits', self.update_slide_limits)
-        getattr(self, 'slidecontroller_%s_set' % self.type_prefix).connect(self.on_slide_selected_index)
-        getattr(self, 'slidecontroller_%s_next' % self.type_prefix).connect(self.on_slide_selected_next)
-        getattr(self, 'slidecontroller_%s_previous' % self.type_prefix).connect(self.on_slide_selected_previous)
+        getattr(self, 'slidecontroller_{text}_set'.format(text=self.type_prefix)).connect(self.on_slide_selected_index)
+        getattr(self, 'slidecontroller_{text}_next'.format(text=self.type_prefix)).connect(self.on_slide_selected_next)
+        # NOTE: {t} used to keep line length < maxline
+        getattr(self,
+                'slidecontroller_{t}_previous'.format(t=self.type_prefix)).connect(self.on_slide_selected_previous)
 
     def _slide_shortcut_activated(self):
         """
@@ -841,7 +847,8 @@
         self.service_item = copy.copy(service_item)
         if self.service_item.is_command():
             Registry().execute(
-                '%s_start' % service_item.name.lower(), [self.service_item, self.is_live, self.hide_mode(), slide_no])
+                '{text}_start'.format(text=service_item.name.lower()),
+                [self.service_item, self.is_live, self.hide_mode(), slide_no])
         # Reset blanking if needed
         if old_item and self.is_live and (old_item.is_capable(ItemCapabilities.ProvidesOwnDisplay) or
                                           self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay)):
@@ -879,8 +886,8 @@
                 if frame['verseTag']:
                     # These tags are already translated.
                     verse_def = frame['verseTag']
-                    verse_def = '%s%s' % (verse_def[0], verse_def[1:])
-                    two_line_def = '%s\n%s' % (verse_def[0], verse_def[1:])
+                    verse_def = '{def1}{def2}'.format(def1=verse_def[0], def2=verse_def[1:])
+                    two_line_def = '{def1}\n{def2}'.format(def1=verse_def[0], def2=verse_def[1:])
                     row = two_line_def
                     if verse_def not in self.slide_list:
                         self.slide_list[verse_def] = frame_number
@@ -915,10 +922,10 @@
             # close the previous, so make sure we don't close the new one.
             if old_item.is_command() and not self.service_item.is_command() or \
                     old_item.is_command() and not old_item.is_media() and self.service_item.is_media():
-                Registry().execute('%s_stop' % old_item.name.lower(), [old_item, self.is_live])
+                Registry().execute('{name}_stop'.format(name=old_item.name.lower()), [old_item, self.is_live])
             if old_item.is_media() and not self.service_item.is_media():
                 self.on_media_close()
-        Registry().execute('slidecontroller_%s_started' % self.type_prefix, [self.service_item])
+        Registry().execute('slidecontroller_{item}_started'.format(item=self.type_prefix), [self.service_item])
 
     def on_slide_selected_index(self, message):
         """
@@ -930,7 +937,8 @@
         if not self.service_item:
             return
         if self.service_item.is_command():
-            Registry().execute('%s_slide' % self.service_item.name.lower(), [self.service_item, self.is_live, index])
+            Registry().execute('{name}_slide'.format(name=self.service_item.name.lower()),
+                               [self.service_item, self.is_live, index])
             self.update_preview()
             self.selected_row = index
         else:
@@ -975,7 +983,7 @@
         """
         if checked is None:
             checked = self.blank_screen.isChecked()
-        self.log_debug('on_blank_display %s' % checked)
+        self.log_debug('on_blank_display {text}'.format(text=checked))
         self.hide_menu.setDefaultAction(self.blank_screen)
         self.blank_screen.setChecked(checked)
         self.theme_screen.setChecked(False)
@@ -996,7 +1004,7 @@
         """
         if checked is None:
             checked = self.theme_screen.isChecked()
-        self.log_debug('on_theme_display %s' % checked)
+        self.log_debug('on_theme_display {text}'.format(text=checked))
         self.hide_menu.setDefaultAction(self.theme_screen)
         self.blank_screen.setChecked(False)
         self.theme_screen.setChecked(checked)
@@ -1017,7 +1025,7 @@
         """
         if checked is None:
             checked = self.desktop_screen.isChecked()
-        self.log_debug('on_hide_display %s' % checked)
+        self.log_debug('on_hide_display {text}'.format(text=checked))
         self.hide_menu.setDefaultAction(self.desktop_screen)
         self.blank_screen.setChecked(False)
         self.theme_screen.setChecked(False)
@@ -1035,17 +1043,18 @@
         Blank/Hide the display screen within a plugin if required.
         """
         hide_mode = self.hide_mode()
-        self.log_debug('blank_plugin %s ' % hide_mode)
+        self.log_debug('blank_plugin {text}'.format(text=hide_mode))
         if self.service_item is not None:
             if hide_mode:
                 if not self.service_item.is_command():
                     Registry().execute('live_display_hide', hide_mode)
-                Registry().execute('%s_blank' %
-                                   self.service_item.name.lower(), [self.service_item, self.is_live, hide_mode])
+                Registry().execute('{text}_blank'.format(text=self.service_item.name.lower()),
+                                   [self.service_item, self.is_live, hide_mode])
             else:
                 if not self.service_item.is_command():
                     Registry().execute('live_display_show')
-                Registry().execute('%s_unblank' % self.service_item.name.lower(), [self.service_item, self.is_live])
+                Registry().execute('{text}_unblank'.format(text=self.service_item.name.lower()),
+                                   [self.service_item, self.is_live])
         else:
             if hide_mode:
                 Registry().execute('live_display_hide', hide_mode)
@@ -1056,15 +1065,17 @@
         """
         Tell the plugin to hide the display screen.
         """
-        self.log_debug('hide_plugin %s ' % hide)
+        self.log_debug('hide_plugin {text}'.format(text=hide))
         if self.service_item is not None:
             if hide:
                 Registry().execute('live_display_hide', HideMode.Screen)
-                Registry().execute('%s_hide' % self.service_item.name.lower(), [self.service_item, self.is_live])
+                Registry().execute('{text}_hide'.format(text=self.service_item.name.lower()),
+                                   [self.service_item, self.is_live])
             else:
                 if not self.service_item.is_command():
                     Registry().execute('live_display_show')
-                Registry().execute('%s_unblank' % self.service_item.name.lower(), [self.service_item, self.is_live])
+                Registry().execute('{text}_unblank'.format(text=self.service_item.name.lower()),
+                                   [self.service_item, self.is_live])
         else:
             if hide:
                 Registry().execute('live_display_hide', HideMode.Screen)
@@ -1099,8 +1110,8 @@
         if -1 < row < self.preview_widget.slide_count():
             if self.service_item.is_command():
                 if self.is_live and not start:
-                    Registry().execute('%s_slide' %
-                                       self.service_item.name.lower(), [self.service_item, self.is_live, row])
+                    Registry().execute('{text}_slide'.format(text=self.service_item.name.lower()),
+                                       [self.service_item, self.is_live, row])
             else:
                 to_display = self.service_item.get_rendered_frame(row)
                 if self.service_item.is_text():
@@ -1133,7 +1144,7 @@
         """
         This updates the preview frame, for example after changing a slide or using *Blank to Theme*.
         """
-        self.log_debug('update_preview %s ' % self.screens.current['primary'])
+        self.log_debug('update_preview {text} '.format(text=self.screens.current['primary']))
         if self.service_item and self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
             if self.is_live:
                 # If live, grab screen-cap of main display now
@@ -1185,7 +1196,8 @@
         if not self.service_item:
             return
         if self.service_item.is_command():
-            Registry().execute('%s_next' % self.service_item.name.lower(), [self.service_item, self.is_live])
+            Registry().execute('{text}_next'.format(text=self.service_item.name.lower()),
+                               [self.service_item, self.is_live])
             if self.is_live:
                 self.update_preview()
         else:
@@ -1213,7 +1225,8 @@
         if not self.service_item:
             return
         if self.service_item.is_command():
-            Registry().execute('%s_previous' % self.service_item.name.lower(), [self.service_item, self.is_live])
+            Registry().execute('{text}_previous'.format(text=self.service_item.name.lower()),
+                               [self.service_item, self.is_live])
             if self.is_live:
                 self.update_preview()
         else:
@@ -1265,7 +1278,7 @@
             checked = self.play_slides_loop.isChecked()
         else:
             self.play_slides_loop.setChecked(checked)
-        self.log_debug('on_play_slides_loop %s' % checked)
+        self.log_debug('on_play_slides_loop {text}'.format(text=checked))
         if checked:
             self.play_slides_loop.setIcon(build_icon(':/media/media_stop.png'))
             self.play_slides_loop.setText(UiStrings().StopPlaySlidesInLoop)
@@ -1288,7 +1301,7 @@
             checked = self.play_slides_once.isChecked()
         else:
             self.play_slides_once.setChecked(checked)
-        self.log_debug('on_play_slides_once %s' % checked)
+        self.log_debug('on_play_slides_once {text}'.format(text=checked))
         if checked:
             self.play_slides_once.setIcon(build_icon(':/media/media_stop.png'))
             self.play_slides_once.setText(UiStrings().StopPlaySlidesToEnd)
@@ -1354,7 +1367,8 @@
                 # Live and Preview have issues if we have video or presentations
                 # playing in both at the same time.
                 if self.service_item.is_command():
-                    Registry().execute('%s_stop' % self.service_item.name.lower(), [self.service_item, self.is_live])
+                    Registry().execute('{text}_stop'.format(text=self.service_item.name.lower()),
+                                       [self.service_item, self.is_live])
                 if self.service_item.is_media():
                     self.on_media_close()
                 self.on_go_live()

=== modified file 'openlp/core/ui/starttimeform.py'
--- openlp/core/ui/starttimeform.py	2016-01-09 16:26:14 +0000
+++ openlp/core/ui/starttimeform.py	2016-05-20 16:33:06 +0000
@@ -56,9 +56,9 @@
         self.hour_finish_spin_box.setValue(hours)
         self.minute_finish_spin_box.setValue(minutes)
         self.second_finish_spin_box.setValue(seconds)
-        self.hour_finish_label.setText('%s%s' % (str(hour), UiStrings().Hours))
-        self.minute_finish_label.setText('%s%s' % (str(minutes), UiStrings().Minutes))
-        self.second_finish_label.setText('%s%s' % (str(seconds), UiStrings().Seconds))
+        self.hour_finish_label.setText('{val:d}{text}'.format(val=hour, text=UiStrings().Hours))
+        self.minute_finish_label.setText('{val:d}{text}'.format(val=minutes, text=UiStrings().Minutes))
+        self.second_finish_label.setText('{val:d}{text}'.format(val=seconds, text=UiStrings().Seconds))
         return QtWidgets.QDialog.exec(self)
 
     def accept(self):

=== modified file 'openlp/core/ui/themeform.py'
--- openlp/core/ui/themeform.py	2016-05-02 08:12:39 +0000
+++ openlp/core/ui/themeform.py	2016-05-20 16:33:06 +0000
@@ -263,7 +263,7 @@
         """
         Run the wizard.
         """
-        log.debug('Editing theme %s' % self.theme.theme_name)
+        log.debug('Editing theme {name}'.format(name=self.theme.theme_name))
         self.temp_background_filename = ''
         self.update_theme_allowed = False
         self.set_defaults()
@@ -272,7 +272,8 @@
         self.theme_name_edit.setVisible(not edit)
         self.edit_mode = edit
         if edit:
-            self.setWindowTitle(translate('OpenLP.ThemeWizard', 'Edit Theme - %s') % self.theme.theme_name)
+            self.setWindowTitle(translate('OpenLP.ThemeWizard', 'Edit Theme - {name}'
+                                          ).format(name=self.theme.theme_name))
             self.next()
         else:
             self.setWindowTitle(UiStrings().NewTheme)
@@ -282,7 +283,7 @@
         """
         Set up the pages for Initial run through dialog
         """
-        log.debug('initializePage %s' % page_id)
+        log.debug('initializePage {page}'.format(page=page_id))
         wizard_page = self.page(page_id)
         if wizard_page == self.background_page:
             self.set_background_page_values()
@@ -445,7 +446,7 @@
         Background Image button pushed.
         """
         images_filter = get_images_filter()
-        images_filter = '%s;;%s (*.*)' % (images_filter, UiStrings().AllFiles)
+        images_filter = '{name};;{text} (*.*)'.format(name=images_filter, text=UiStrings().AllFiles)
         filename, filter_used = QtWidgets.QFileDialog.getOpenFileName(
             self, translate('OpenLP.ThemeWizard', 'Select Image'),
             self.image_file_edit.text(), images_filter)
@@ -463,6 +464,7 @@
         """
         Background video button pushed.
         """
+        # TODO: Check this before converting
         visible_formats = '(%s)' % '; '.join(VIDEO_EXT)
         actual_formats = '(%s)' % ' '.join(VIDEO_EXT)
         video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'),

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2016-04-30 15:40:23 +0000
+++ openlp/core/ui/thememanager.py	2016-05-20 16:33:06 +0000
@@ -203,7 +203,7 @@
         Change the global theme when it is changed through the Themes settings tab
         """
         self.global_theme = Settings().value(self.settings_section + '/global theme')
-        self.log_debug('change_global_from_tab %s' % self.global_theme)
+        self.log_debug('change_global_from_tab {text}'.format(text=self.global_theme))
         for count in range(0, self.theme_list_widget.count()):
             # reset the old name
             item = self.theme_list_widget.item(count)
@@ -213,7 +213,7 @@
                 self.theme_list_widget.item(count).setText(new_name)
             # Set the new name
             if self.global_theme == new_name:
-                name = translate('OpenLP.ThemeManager', '%s (default)') % new_name
+                name = translate('OpenLP.ThemeManager', '{text} (default)').format(text=new_name)
                 self.theme_list_widget.item(count).setText(name)
                 self.delete_toolbar_action.setVisible(item not in self.theme_list_widget.selectedItems())
 
@@ -233,7 +233,7 @@
             # Set the new name
             if count == selected_row:
                 self.global_theme = self.theme_list_widget.item(count).text()
-                name = translate('OpenLP.ThemeManager', '%s (default)') % self.global_theme
+                name = translate('OpenLP.ThemeManager', '{text} (default)').format(text=self.global_theme)
                 self.theme_list_widget.item(count).setText(name)
                 Settings().setValue(self.settings_section + '/global theme', self.global_theme)
                 Registry().execute('theme_update_global')
@@ -256,6 +256,7 @@
         Renames an existing theme to a new name
         :param field:
         """
+        # TODO: Check for delayed format() conversions
         if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to rename.'),
                                        translate('OpenLP.ThemeManager', 'Rename Confirmation'),
                                        translate('OpenLP.ThemeManager', 'Rename %s theme?'), False, False):
@@ -284,7 +285,8 @@
         item = self.theme_list_widget.currentItem()
         old_theme_name = item.data(QtCore.Qt.UserRole)
         self.file_rename_form.file_name_edit.setText(translate('OpenLP.ThemeManager',
-                                                     'Copy of %s', 'Copy of <theme name>') % old_theme_name)
+                                                               'Copy of {name}',
+                                                               'Copy of <theme name>').format(name=old_theme_name))
         if self.file_rename_form.exec(True):
             new_theme_name = self.file_rename_form.file_name_edit.text()
             if self.check_if_theme_exists(new_theme_name):
@@ -331,6 +333,7 @@
         Delete a theme triggered by the UI.
         :param field:
         """
+        # TODO: Verify delayed format() conversions
         if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to delete.'),
                                        translate('OpenLP.ThemeManager', 'Delete Confirmation'),
                                        translate('OpenLP.ThemeManager', 'Delete %s theme?')):
@@ -351,7 +354,7 @@
         :param theme: The theme to delete.
         """
         self.theme_list.remove(theme)
-        thumb = '%s.png' % theme
+        thumb = '{name}.png'.format(name=theme)
         delete_file(os.path.join(self.path, thumb))
         delete_file(os.path.join(self.thumb_path, thumb))
         try:
@@ -363,7 +366,7 @@
                 shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
         except OSError as os_error:
             shutil.Error = os_error
-            self.log_exception('Error deleting theme %s' % theme)
+            self.log_exception('Error deleting theme {name}'.format(name=theme))
 
     def on_export_theme(self, field=None):
         """
@@ -376,7 +379,8 @@
             return
         theme = item.data(QtCore.Qt.UserRole)
         path = QtWidgets.QFileDialog.getExistingDirectory(self,
-                                                          translate('OpenLP.ThemeManager', 'Save Theme - (%s)') % theme,
+                                                          translate('OpenLP.ThemeManager',
+                                                                    'Save Theme - ({name})').format(name=theme),
                                                           Settings().value(self.settings_section +
                                                                            '/last directory export'))
         self.application.set_busy_cursor()
@@ -409,7 +413,7 @@
             self.log_exception('Export Theme Failed')
             critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
                                        translate('OpenLP.ThemeManager', 'The theme export failed because this error '
-                                                                        'occurred: %s') % ose.strerror)
+                                                                        'occurred: {err}').format(err=ose.strerror))
             if theme_zip:
                 theme_zip.close()
                 shutil.rmtree(theme_path, True)
@@ -425,7 +429,7 @@
                                             translate('OpenLP.ThemeManager', 'Select Theme Import File'),
                                             Settings().value(self.settings_section + '/last directory import'),
                                             translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
-        self.log_info('New Themes %s' % str(files))
+        self.log_info('New Themes {name}'.format(name=str(files)))
         if not files:
             return
         self.application.set_busy_cursor()
@@ -472,10 +476,10 @@
             if os.path.exists(theme):
                 text_name = os.path.splitext(name)[0]
                 if text_name == self.global_theme:
-                    name = translate('OpenLP.ThemeManager', '%s (default)') % text_name
+                    name = translate('OpenLP.ThemeManager', '{name} (default)').format(name=text_name)
                 else:
                     name = text_name
-                thumb = os.path.join(self.thumb_path, '%s.png' % text_name)
+                thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=text_name))
                 item_name = QtWidgets.QListWidgetItem(name)
                 if validate_thumb(theme, thumb):
                     icon = build_icon(thumb)
@@ -506,7 +510,7 @@
         :param theme_name: Name of the theme to load from file
         :return: The theme object.
         """
-        self.log_debug('get theme data for theme %s' % theme_name)
+        self.log_debug('get theme data for theme {name}'.format(name=theme_name))
         xml_file = os.path.join(self.path, str(theme_name), str(theme_name) + '.xml')
         xml = get_text_file_string(xml_file)
         if not xml:
@@ -524,8 +528,8 @@
         """
         ret = QtWidgets.QMessageBox.question(self, translate('OpenLP.ThemeManager', 'Theme Already Exists'),
                                              translate('OpenLP.ThemeManager',
-                                                       'Theme %s already exists. Do you want to replace it?')
-                                             .replace('%s', theme_name),
+                                                       'Theme {name} already exists. '
+                                                       'Do you want to replace it?').format(name=theme_name),
                                              QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
                                                                                    QtWidgets.QMessageBox.No),
                                              QtWidgets.QMessageBox.No)
@@ -538,7 +542,7 @@
         :param file_name:
         :param directory:
         """
-        self.log_debug('Unzipping theme %s' % file_name)
+        self.log_debug('Unzipping theme {name}'.format(name=file_name))
         theme_zip = None
         out_file = None
         file_xml = None
@@ -547,7 +551,7 @@
             theme_zip = zipfile.ZipFile(file_name)
             xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
             if len(xml_file) != 1:
-                self.log_error('Theme contains "%s" XML files' % len(xml_file))
+                self.log_error('Theme contains "{val:d}" XML files'.format(val=len(xml_file)))
                 raise ValidationError
             xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()
             theme_version = xml_tree.get('version', default=None)
@@ -579,7 +583,7 @@
                     out_file.write(theme_zip.read(name))
                 out_file.close()
         except (IOError, zipfile.BadZipfile):
-            self.log_exception('Importing theme from zip failed %s' % file_name)
+            self.log_exception('Importing theme from zip failed {name|'.format(name=file_name))
             raise ValidationError
         except ValidationError:
             critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
@@ -601,7 +605,7 @@
                     critical_error_message_box(
                         translate('OpenLP.ThemeManager', 'Validation Error'),
                         translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
-                    self.log_error('Theme file does not contain XML data %s' % file_name)
+                    self.log_error('Theme file does not contain XML data {name}'.format(name=file_name))
 
     def check_if_theme_exists(self, theme_name):
         """
@@ -682,7 +686,7 @@
         if os.path.exists(sample_path_name):
             os.unlink(sample_path_name)
         frame.save(sample_path_name, 'png')
-        thumb = os.path.join(self.thumb_path, '%s.png' % name)
+        thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=name))
         create_thumb(sample_path_name, thumb, False)
 
     def update_preview_images(self):
@@ -760,14 +764,17 @@
                 for plugin in self.plugin_manager.plugins:
                     used_count = plugin.uses_theme(theme)
                     if used_count:
-                        plugin_usage = "%s%s" % (plugin_usage, (translate('OpenLP.ThemeManager',
-                                                                          '%(count)s time(s) by %(plugin)s') %
-                                                                {'count': used_count, 'plugin': plugin.name}))
+                        plugin_usage = "{plug}{text}".format(plug=plugin_usage,
+                                                             text=(translate('OpenLP.ThemeManager',
+                                                                             '{count} time(s) by {plugin}'
+                                                                             ).format(name=used_count,
+                                                                                      plugin=plugin.name)))
                         plugin_usage = "%s\n" % plugin_usage
                 if plugin_usage:
                     critical_error_message_box(translate('OpenLP.ThemeManager', 'Unable to delete theme'),
-                                               translate('OpenLP.ThemeManager', 'Theme is currently used \n\n%s') %
-                                               plugin_usage)
+                                               translate('OpenLP.ThemeManager',
+                                                         'Theme is currently used \n\n{text}'
+                                                         ).format(text=plugin_usage))
 
                     return False
             return True

=== modified file 'openlp/core/ui/themewizard.py'
--- openlp/core/ui/themewizard.py	2016-04-30 17:19:55 +0000
+++ openlp/core/ui/themewizard.py	2016-05-20 16:33:06 +0000
@@ -405,8 +405,8 @@
         Translate the UI on the fly
         """
         theme_wizard.setWindowTitle(translate('OpenLP.ThemeWizard', 'Theme Wizard'))
-        self.title_label.setText('<span style="font-size:14pt; font-weight:600;">%s</span>' %
-                                 translate('OpenLP.ThemeWizard', 'Welcome to the Theme Wizard'))
+        text = translate('OpenLP.ThemeWizard', 'Welcome to the Theme Wizard')
+        self.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}</span>'.format(text=text))
         self.information_label.setText(
             translate('OpenLP.ThemeWizard', 'This wizard will help you to create and edit your themes. Click the next '
                       'button below to start the process by setting up your background.'))
@@ -435,9 +435,9 @@
         self.gradient_combo_box.setItemText(BackgroundGradientType.LeftBottom,
                                             translate('OpenLP.ThemeWizard', 'Bottom Left - Top Right'))
         self.image_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
-        self.image_label.setText('%s:' % UiStrings().Image)
+        self.image_label.setText('{text}:'.format(text=UiStrings().Image))
         self.video_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:'))
-        self.video_label.setText('%s:' % UiStrings().Video)
+        self.video_label.setText('{text}:'.format(text=UiStrings().Video))
         self.main_area_page.setTitle(translate('OpenLP.ThemeWizard', 'Main Area Font Details'))
         self.main_area_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Define the font and display '
                                                   'characteristics for the Display text'))

=== modified file 'tests/functional/openlp_core_lib/test_projectordb.py'
--- tests/functional/openlp_core_lib/test_projectordb.py	2016-05-15 17:32:04 +0000
+++ tests/functional/openlp_core_lib/test_projectordb.py	2016-05-20 16:33:06 +0000
@@ -28,7 +28,7 @@
 import os
 from unittest import TestCase
 
-from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource
+from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source
 
 from tests.functional import MagicMock, patch
 from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
@@ -220,3 +220,19 @@
         # THEN: __repr__ should return a proper string
         self.assertEqual(str(model), '<Model(name='"OpenLP Test"')>',
                          'Model.__repr__() should have returned a proper representation string')
+
+    def source_repr_test(self):
+        """
+        Test source.__repr__ text
+        """
+        # GIVEN: Test object
+        source = Source()
+
+        # WHEN: Source() information is set
+        source.pjlink_name = 'Test object'
+        source.pjlink_code = '11'
+        source.text = 'Input text'
+
+        # THEN: __repr__ should return a proper string
+        self.assertEqual(str(source), '<Source(pjlink_name="Test object", pjlink_code="11", text="Input text")>',
+                         'Source.__repr__() should have returned a proper representation string')


Follow ups