From 6d0e006b09feac02e1e85c70fe68aff7a8741433 Mon Sep 17 00:00:00 2001 From: Simon Hanna Date: Sun, 3 Jan 2016 14:03:29 +0100 Subject: [PATCH 01/43] Change Combobox to Checkbox for plugin states --- openlp/core/ui/plugindialog.py | 10 ++++------ openlp/core/ui/pluginform.py | 19 ++++++++----------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py index 5d36dd72c..273d20383 100644 --- a/openlp/core/ui/plugindialog.py +++ b/openlp/core/ui/plugindialog.py @@ -53,10 +53,9 @@ class Ui_PluginViewDialog(object): self.plugin_info_layout.setObjectName('plugin_info_layout') self.status_label = QtWidgets.QLabel(self.plugin_info_group_box) self.status_label.setObjectName('status_label') - self.status_combo_box = QtWidgets.QComboBox(self.plugin_info_group_box) - self.status_combo_box.addItems(('', '')) - self.status_combo_box.setObjectName('status_combo_box') - self.plugin_info_layout.addRow(self.status_label, self.status_combo_box) + self.status_checkbox = QtWidgets.QCheckBox(self.plugin_info_group_box) + self.status_checkbox.setObjectName('status_checkbox') + self.plugin_info_layout.addRow(self.status_label, self.status_checkbox) self.version_label = QtWidgets.QLabel(self.plugin_info_group_box) self.version_label.setObjectName('version_label') self.version_number_label = QtWidgets.QLabel(self.plugin_info_group_box) @@ -83,5 +82,4 @@ class Ui_PluginViewDialog(object): self.version_label.setText('%s:' % UiStrings().Version) self.about_label.setText('%s:' % UiStrings().About) self.status_label.setText(translate('OpenLP.PluginForm', 'Status:')) - self.status_combo_box.setItemText(0, translate('OpenLP.PluginForm', 'Active')) - self.status_combo_box.setItemText(1, translate('OpenLP.PluginForm', 'Inactive')) + self.status_checkbox.setText(translate('OpenLP.PluginForm', 'Active')) diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 7d71e8ff4..040e9f3a1 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -49,7 +49,7 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): self._clear_details() # Right, now let's put some signals and slots together! self.plugin_list_widget.itemSelectionChanged.connect(self.on_plugin_list_widget_selection_changed) - self.status_combo_box.currentIndexChanged.connect(self.on_status_combo_box_changed) + self.status_checkbox.stateChanged.connect(self.on_status_checkbox_changed) def load(self): """ @@ -86,10 +86,10 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): """ Clear the plugin details widgets """ - self.status_combo_box.setCurrentIndex(-1) + self.status_checkbox.setChecked(False) self.version_number_label.setText('') self.about_text_browser.setHtml('') - self.status_combo_box.setEnabled(False) + self.status_checkbox.setEnabled(False) def _set_details(self): """ @@ -99,11 +99,8 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): self.version_number_label.setText(self.active_plugin.version) self.about_text_browser.setHtml(self.active_plugin.about()) self.programatic_change = True - status = PluginStatus.Active - if self.active_plugin.status == PluginStatus.Active: - status = PluginStatus.Inactive - self.status_combo_box.setCurrentIndex(status) - self.status_combo_box.setEnabled(True) + self.status_checkbox.setChecked(self.active_plugin.status == PluginStatus.Active) + self.status_checkbox.setEnabled(True) self.programatic_change = False def on_plugin_list_widget_selection_changed(self): @@ -125,13 +122,13 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): else: self._clear_details() - def on_status_combo_box_changed(self, status): + def on_status_checkbox_changed(self, status): """ If the status of a plugin is altered, apply the change """ - if self.programatic_change or status == PluginStatus.Disabled: + if self.programatic_change: return - if status == PluginStatus.Inactive: + if status: self.application.set_busy_cursor() self.active_plugin.toggle_status(PluginStatus.Active) self.application.set_normal_cursor() From ed0b739d97c153820a83323ecfc80b9259abebd9 Mon Sep 17 00:00:00 2001 From: Simon Hanna Date: Sun, 3 Jan 2016 15:00:23 +0100 Subject: [PATCH 02/43] Add .coveragerc --- .bzrignore | 1 + .coveragerc | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 .coveragerc diff --git a/.bzrignore b/.bzrignore index 272c37e88..68fa826de 100644 --- a/.bzrignore +++ b/.bzrignore @@ -43,3 +43,4 @@ __pycache__ .coverage cover *.kdev4 +coverage diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..f8f529f44 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +source = openlp + +[html] +directory = coverage From 480a2d8512bf17d2088e500bac951a535681b2fb Mon Sep 17 00:00:00 2001 From: Simon Hanna Date: Sun, 3 Jan 2016 15:50:41 +0100 Subject: [PATCH 03/43] Make about() method of SongUsagePlugin static and add a test for it --- openlp/plugins/songusage/songusageplugin.py | 5 +-- .../openlp_plugins/songusage/__init__.py | 24 ++++++++++++++ .../songusage/test_songusage.py | 31 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/functional/openlp_plugins/songusage/__init__.py create mode 100644 tests/functional/openlp_plugins/songusage/test_songusage.py diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index 0bf8ed9a5..b433d96e0 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -226,8 +226,9 @@ class SongUsagePlugin(Plugin): """ self.song_usage_detail_form.initialise() self.song_usage_detail_form.exec() - - def about(self): + + @staticmethod + def about(): """ The plugin about text diff --git a/tests/functional/openlp_plugins/songusage/__init__.py b/tests/functional/openlp_plugins/songusage/__init__.py new file mode 100644 index 000000000..917bf47cd --- /dev/null +++ b/tests/functional/openlp_plugins/songusage/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2016 OpenLP Developers # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +Tests for the Songusage plugin +""" diff --git a/tests/functional/openlp_plugins/songusage/test_songusage.py b/tests/functional/openlp_plugins/songusage/test_songusage.py new file mode 100644 index 000000000..7b940bc20 --- /dev/null +++ b/tests/functional/openlp_plugins/songusage/test_songusage.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2016 OpenLP Developers # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +This module contains tests for the Songusage plugin. +""" +from unittest import TestCase +from openlp.plugins.songusage.songusageplugin import SongUsagePlugin + +class TestSongUsage(TestCase): + + def test_about_text(self): + self.assertNotEquals(len(SongUsagePlugin.about()), 0) From 9ced5436bae39514014661b3545d4f8469fec0a8 Mon Sep 17 00:00:00 2001 From: Simon Hanna Date: Mon, 22 Feb 2016 15:34:00 +0100 Subject: [PATCH 04/43] Fix exception caused when plugins are disabled --- openlp/core/ui/pluginform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index d7bd1d1d6..d67e7ea1e 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -126,7 +126,7 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): """ If the status of a plugin is altered, apply the change """ - if self.programatic_change: + if self.programatic_change or self.active_plugin is None: return if status: self.application.set_busy_cursor() From 9356d990acf5f061cc7ae198e8c6eb80e6e34ad0 Mon Sep 17 00:00:00 2001 From: Simon Hanna Date: Mon, 22 Feb 2016 15:51:11 +0100 Subject: [PATCH 05/43] Show plugin information even if the plugin is disabled --- openlp/core/ui/pluginform.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index d67e7ea1e..f43757b44 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -99,8 +99,12 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): self.version_number_label.setText(self.active_plugin.version) self.about_text_browser.setHtml(self.active_plugin.about()) self.programatic_change = True - self.status_checkbox.setChecked(self.active_plugin.status == PluginStatus.Active) - self.status_checkbox.setEnabled(True) + if self.active_plugin.status != PluginStatus.Disabled: + self.status_checkbox.setChecked(self.active_plugin.status == PluginStatus.Active) + self.status_checkbox.setEnabled(True) + else: + self.status_checkbox.setChecked(False) + self.status_checkbox.setEnabled(False) self.programatic_change = False def on_plugin_list_widget_selection_changed(self): @@ -113,10 +117,9 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): plugin_name_singular = self.plugin_list_widget.currentItem().text().split('(')[0][:-1] self.active_plugin = None for plugin in self.plugin_manager.plugins: - if plugin.status != PluginStatus.Disabled: - if plugin.name_strings['singular'] == plugin_name_singular: - self.active_plugin = plugin - break + if plugin.name_strings['singular'] == plugin_name_singular: + self.active_plugin = plugin + break if self.active_plugin: self._set_details() else: From 1d51939308070471702d7e745dae028b009bf77e Mon Sep 17 00:00:00 2001 From: Simon Hanna Date: Mon, 22 Feb 2016 15:51:58 +0100 Subject: [PATCH 06/43] Do not show version numbers for plugins --- openlp/core/ui/plugindialog.py | 6 ------ openlp/core/ui/pluginform.py | 2 -- 2 files changed, 8 deletions(-) diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py index 273d20383..583728fcd 100644 --- a/openlp/core/ui/plugindialog.py +++ b/openlp/core/ui/plugindialog.py @@ -56,11 +56,6 @@ class Ui_PluginViewDialog(object): self.status_checkbox = QtWidgets.QCheckBox(self.plugin_info_group_box) self.status_checkbox.setObjectName('status_checkbox') self.plugin_info_layout.addRow(self.status_label, self.status_checkbox) - self.version_label = QtWidgets.QLabel(self.plugin_info_group_box) - self.version_label.setObjectName('version_label') - self.version_number_label = QtWidgets.QLabel(self.plugin_info_group_box) - self.version_number_label.setObjectName('version_number_label') - self.plugin_info_layout.addRow(self.version_label, self.version_number_label) self.about_label = QtWidgets.QLabel(self.plugin_info_group_box) self.about_label.setObjectName('about_label') self.about_text_browser = QtWidgets.QTextBrowser(self.plugin_info_group_box) @@ -79,7 +74,6 @@ class Ui_PluginViewDialog(object): """ plugin_view_dialog.setWindowTitle(translate('OpenLP.PluginForm', 'Manage Plugins')) self.plugin_info_group_box.setTitle(translate('OpenLP.PluginForm', 'Plugin Details')) - self.version_label.setText('%s:' % UiStrings().Version) self.about_label.setText('%s:' % UiStrings().About) self.status_label.setText(translate('OpenLP.PluginForm', 'Status:')) self.status_checkbox.setText(translate('OpenLP.PluginForm', 'Active')) diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index f43757b44..cb6dd70aa 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -87,7 +87,6 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): Clear the plugin details widgets """ self.status_checkbox.setChecked(False) - self.version_number_label.setText('') self.about_text_browser.setHtml('') self.status_checkbox.setEnabled(False) @@ -96,7 +95,6 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): Set the details of the currently selected plugin """ log.debug('PluginStatus: %s', str(self.active_plugin.status)) - self.version_number_label.setText(self.active_plugin.version) self.about_text_browser.setHtml(self.active_plugin.about()) self.programatic_change = True if self.active_plugin.status != PluginStatus.Disabled: From 2433efdfd064443d6adbd3327012a982c2f8e43c Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 29 Mar 2016 16:45:59 +0100 Subject: [PATCH 07/43] Update themes in service manager when themes are set in settings --- openlp/core/ui/servicemanager.py | 1 + openlp/core/ui/themestab.py | 1 + .../openlp_core_ui/test_themetab.py | 84 +++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 tests/functional/openlp_core_ui/test_themetab.py diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index fdae5c069..067fd86c7 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1326,6 +1326,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa visible = self.renderer.theme_level == ThemeLevel.Global self.theme_label.setVisible(visible) self.theme_combo_box.setVisible(visible) + self.regenerate_service_items() def regenerate_service_items(self, changed=False): """ diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index a953ae013..07474ca74 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -160,6 +160,7 @@ class ThemesTab(SettingsTab): settings.endGroup() self.renderer.set_theme_level(self.theme_level) if self.tab_visited: + print("processed") self.settings_form.register_post_process('theme_update_global') self.tab_visited = False diff --git a/tests/functional/openlp_core_ui/test_themetab.py b/tests/functional/openlp_core_ui/test_themetab.py new file mode 100644 index 000000000..8896ae8f0 --- /dev/null +++ b/tests/functional/openlp_core_ui/test_themetab.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2016 OpenLP Developers # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +Package to test the openlp.core.ui.ThemeTab package. +""" +from unittest import TestCase + +from openlp.core.common import Registry +from openlp.core.ui.themestab import ThemesTab +from openlp.core.ui.settingsform import SettingsForm + +from tests.helpers.testmixin import TestMixin +from tests.functional import MagicMock + + +class TestThemeTab(TestCase, TestMixin): + + def setUp(self): + """ + Set up a few things for the tests + """ + Registry.create() + + def test_creation(self): + """ + Test that Themes Tab is created. + """ + # GIVEN: A new Advanced Tab + settings_form = SettingsForm(None) + + # WHEN: I create an advanced tab + themes_tab = ThemesTab(settings_form) + + # THEN: + self.assertEqual("Themes", themes_tab.tab_title, 'The tab title should be Theme') + + def test_save_triggers_processes_true(self): + """ + Test that the global theme event is triggered when the tab is visited. + """ + # GIVEN: A new Advanced Tab + settings_form = SettingsForm(None) + themes_tab = ThemesTab(settings_form) + Registry().register('renderer', MagicMock()) + themes_tab.tab_visited = True + # WHEN: I change search as type check box + themes_tab.save() + + # THEN: we should have two post save processed to run + self.assertEqual(1, len(settings_form.processes), 'One post save processes should be created') + + def test_save_triggers_processes_false(self): + """ + Test that the global theme event is not triggered when the tab is not visited. + """ + # GIVEN: A new Advanced Tab + settings_form = SettingsForm(None) + themes_tab = ThemesTab(settings_form) + Registry().register('renderer', MagicMock()) + themes_tab.tab_visited = False + # WHEN: I change search as type check box + themes_tab.save() + + # THEN: we should have two post save processed to run + self.assertEqual(0, len(settings_form.processes), 'No post save processes should be created') \ No newline at end of file From be8bdcf1f34afda94c182f5025894d9a86841360 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 29 Mar 2016 17:00:55 +0100 Subject: [PATCH 08/43] remove print --- openlp/core/ui/themestab.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 07474ca74..a953ae013 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -160,7 +160,6 @@ class ThemesTab(SettingsTab): settings.endGroup() self.renderer.set_theme_level(self.theme_level) if self.tab_visited: - print("processed") self.settings_form.register_post_process('theme_update_global') self.tab_visited = False From 77fca59e5711c651e2ff540e8a2b2f864ed081ef Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 29 Mar 2016 17:07:40 +0100 Subject: [PATCH 09/43] add new line --- tests/functional/openlp_core_ui/test_themetab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_ui/test_themetab.py b/tests/functional/openlp_core_ui/test_themetab.py index 8896ae8f0..c3bb6282d 100644 --- a/tests/functional/openlp_core_ui/test_themetab.py +++ b/tests/functional/openlp_core_ui/test_themetab.py @@ -81,4 +81,4 @@ class TestThemeTab(TestCase, TestMixin): themes_tab.save() # THEN: we should have two post save processed to run - self.assertEqual(0, len(settings_form.processes), 'No post save processes should be created') \ No newline at end of file + self.assertEqual(0, len(settings_form.processes), 'No post save processes should be created') From d11cb1d6222efc232606068e016b9913f9839063 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 29 Mar 2016 17:55:33 +0100 Subject: [PATCH 10/43] Only set tab_visited when the tab is visited and not on loading --- openlp/core/lib/settingstab.py | 2 +- openlp/core/ui/settingsform.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/settingstab.py b/openlp/core/lib/settingstab.py index fb7da0e14..a236dbdf9 100644 --- a/openlp/core/lib/settingstab.py +++ b/openlp/core/lib/settingstab.py @@ -135,4 +135,4 @@ class SettingsTab(QtWidgets.QWidget, RegistryProperties): """ Tab has just been made visible to the user """ - self.tab_visited = True + pass diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index f8c3cbbc2..82d72694c 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -60,7 +60,8 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties): """ Execute the form """ - # load all the settings + # load all the + self.setting_list_widget.blockSignals(True) self.setting_list_widget.clear() while self.stacked_layout.count(): # take at 0 and the rest shuffle up. @@ -74,6 +75,7 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties): if plugin.settings_tab: self.insert_tab(plugin.settings_tab, plugin.is_active()) self.setting_list_widget.setCurrentRow(0) + self.setting_list_widget.blockSignals(False) return QtWidgets.QDialog.exec(self) def insert_tab(self, tab_widget, is_visible=True): @@ -177,6 +179,7 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties): # Check that the title of the tab (i.e. plugin name) is the same as the data in the list item if tab_widget.tab_title == list_item.data(QtCore.Qt.UserRole): # Make the matching tab visible + tab_widget.tab_visited = True self.stacked_layout.setCurrentIndex(tab_index) self.stacked_layout.currentWidget().tab_visible() From c1222dc2bb8dd3845c606bc9a6726deffddddb22 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Fri, 22 Apr 2016 17:40:59 -0700 Subject: [PATCH 11/43] Convert strings to python3 in __init__ files --- openlp/core/__init__.py | 17 +++++++++-------- openlp/core/common/__init__.py | 23 ++++++++++++++--------- openlp/core/lib/__init__.py | 4 +++- openlp/core/ui/media/__init__.py | 11 +++++++---- openlp/plugins/bibles/lib/__init__.py | 22 +++++++++++++--------- tests/utils/__init__.py | 2 +- 6 files changed, 47 insertions(+), 32 deletions(-) diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index db135ef10..5ed016309 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -222,10 +222,11 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication): QtWidgets.QMessageBox.warning(None, translate('OpenLP', 'Backup'), translate('OpenLP', 'Backup of the data folder failed!')) return - QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), - translate('OpenLP', - 'A backup of the data folder has been created at %s') - % data_folder_backup_path) + message = translate('OpenLP', + 'A backup of the data folder has been created' + 'at {text}'.format(text=data_folder_backup_path)) + QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message) + # Update the version in the settings Settings().setValue('core/application version', openlp_version) @@ -257,7 +258,7 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication): """ if event.type() == QtCore.QEvent.FileOpen: file_name = event.file() - log.debug('Got open file event for %s!', file_name) + log.debug('Got open file event for {name}!'.format(name=file_name)) self.args.insert(0, file_name) return True # Mac OS X should restore app window when user clicked on the OpenLP icon @@ -311,7 +312,7 @@ def set_up_logging(log_path): logfile.setFormatter(logging.Formatter('%(asctime)s %(name)-55s %(levelname)-8s %(message)s')) log.addHandler(logfile) if log.isEnabledFor(logging.DEBUG): - print('Logging to: %s' % filename) + print('Logging to: {name}'.format(name=filename)) def main(args=None): @@ -351,12 +352,12 @@ def main(args=None): log.info('Running portable') portable_settings_file = os.path.abspath(os.path.join(application_path, '..', '..', 'Data', 'OpenLP.ini')) # Make this our settings file - log.info('INI file: %s', portable_settings_file) + log.info('INI file: {name}'.format(name=portable_settings_file)) Settings.set_filename(portable_settings_file) portable_settings = Settings() # Set our data path data_path = os.path.abspath(os.path.join(application_path, '..', '..', 'Data',)) - log.info('Data path: %s', data_path) + log.info('Data path: {name}'.format(name=data_path)) # Point to our data path portable_settings.setValue('advanced/data path', data_path) portable_settings.setValue('advanced/is portable', True) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index b8a1a4d2e..6a111d97d 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -53,7 +53,10 @@ def trace_error_handler(logger): """ log_string = "OpenLP Error trace" for tb in traceback.extract_stack(): - log_string = '%s\n File %s at line %d \n\t called %s' % (log_string, tb[0], tb[1], tb[3]) + log_string = '{text}\n File {file} at line {line} \n\t called {data}'.format(text=log_string, + file=tb[0], + line=tb[1], + data=tb[3]) logger.error(log_string) @@ -65,7 +68,7 @@ def check_directory_exists(directory, do_not_log=False): :param do_not_log: To not log anything. This is need for the start up, when the log isn't ready. """ if not do_not_log: - log.debug('check_directory_exists %s' % directory) + log.debug('check_directory_exists {text}'.format(text=directory)) try: if not os.path.exists(directory): os.makedirs(directory) @@ -200,13 +203,13 @@ def md5_hash(salt, data=None): :param data: OPTIONAL Data to hash :returns: str """ - log.debug('md5_hash(salt="%s")' % salt) + log.debug('md5_hash(salt="{text}")'.format(text=salt)) hash_obj = hashlib.new('md5') hash_obj.update(salt) if data: hash_obj.update(data) hash_value = hash_obj.hexdigest() - log.debug('md5_hash() returning "%s"' % hash_value) + log.debug('md5_hash() returning "{text}"'.format(text=hash_value)) return hash_value @@ -219,12 +222,12 @@ def qmd5_hash(salt, data=None): :param data: OPTIONAL Data to hash :returns: str """ - log.debug('qmd5_hash(salt="%s"' % salt) + log.debug('qmd5_hash(salt="{text}"'.format(text=salt)) hash_obj = QHash(QHash.Md5) hash_obj.addData(salt) hash_obj.addData(data) hash_value = hash_obj.result().toHex() - log.debug('qmd5_hash() returning "%s"' % hash_value) + log.debug('qmd5_hash() returning "{text}"'.format(text=hash_value)) return hash_value.data() @@ -340,9 +343,11 @@ def get_images_filter(): if not IMAGES_FILTER: log.debug('Generating images filter.') formats = list(map(bytes.decode, list(map(bytes, QtGui.QImageReader.supportedImageFormats())))) - visible_formats = '(*.%s)' % '; *.'.join(formats) - actual_formats = '(*.%s)' % ' *.'.join(formats) - IMAGES_FILTER = '%s %s %s' % (translate('OpenLP', 'Image Files'), visible_formats, actual_formats) + visible_formats = '(*.{text})'.format(text='; *.'.join(formats)) + actual_formats = '(*.{text})'.format(text=' *.'.join(formats)) + IMAGES_FILTER = '{text} {visible} {actual}'.format(text=translate('OpenLP', 'Image Files'), + visible=visible_formats, + actual=actual_formats) return IMAGES_FILTER diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 6e62bbf9c..5769a3626 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -97,7 +97,7 @@ def get_text_file_string(text_file): file_handle.seek(0) content = file_handle.read() except (IOError, UnicodeError): - log.exception('Failed to open text file %s' % text_file) + log.exception('Failed to open text file {text}'.format(text=text_file)) finally: if file_handle: file_handle.close() @@ -300,6 +300,8 @@ def create_separated_list(string_list): return '' elif len(string_list) == 1: return string_list[0] + # TODO: + # Cannot convert these strings to python3 yet until I can figure out how to mock translate() with the new format elif len(string_list) == 2: return translate('OpenLP.core.lib', '%s and %s', 'Locale list separator: 2 items') % (string_list[0], string_list[1]) diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 07e2a73fb..9e2f61ae8 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -83,7 +83,7 @@ def get_media_players(): reg_ex = QtCore.QRegExp(".*\[(.*)\].*") if Settings().value('media/override player') == QtCore.Qt.Checked: if reg_ex.exactMatch(saved_players): - overridden_player = '%s' % reg_ex.cap(1) + overridden_player = '{text}'.format(text=reg_ex.cap(1)) else: overridden_player = 'auto' else: @@ -102,7 +102,7 @@ def set_media_players(players_list, overridden_player='auto'): log.debug('set_media_players') players = ','.join(players_list) if Settings().value('media/override player') == QtCore.Qt.Checked and overridden_player != 'auto': - players = players.replace(overridden_player, '[%s]' % overridden_player) + players = players.replace(overridden_player, '[{text}]'.format(text=overridden_player)) Settings().setValue('media/players', players) @@ -113,7 +113,7 @@ def parse_optical_path(input_string): :param input_string: The string to parse :return: The elements extracted from the string: filename, title, audio_track, subtitle_track, start, end """ - log.debug('parse_optical_path, about to parse: "%s"' % input_string) + log.debug('parse_optical_path, about to parse: "{text}"'.format(text=input_string)) clip_info = input_string.split(sep=':') title = int(clip_info[1]) audio_track = int(clip_info[2]) @@ -137,7 +137,10 @@ def format_milliseconds(milliseconds): seconds, millis = divmod(milliseconds, 1000) minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) - return "%02d:%02d:%02d,%03d" % (hours, minutes, seconds, millis) + return "{hours:02d}:{minutes:02d}:{seconds:02d},{millis:03d}".format(hours=hours, + minutes=minutes, + seconds=seconds, + millis=millis) from .mediacontroller import MediaController from .playertab import PlayerTab diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 0bba1e387..52f400c24 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -211,22 +211,24 @@ def update_reference_separators(): while '||' in source_string: source_string = source_string.replace('||', '|') if role != 'e': - REFERENCE_SEPARATORS['sep_%s_display' % role] = source_string.split('|')[0] + REFERENCE_SEPARATORS['sep_{text}_display'.format(text=role)] = source_string.split('|')[0] # escape reserved characters for character in '\\.^$*+?{}[]()': source_string = source_string.replace(character, '\\' + character) # add various unicode alternatives source_string = source_string.replace('-', '(?:[-\u00AD\u2010\u2011\u2012\u2014\u2014\u2212\uFE63\uFF0D])') source_string = source_string.replace(',', '(?:[,\u201A])') - REFERENCE_SEPARATORS['sep_%s' % role] = '\s*(?:%s)\s*' % source_string - REFERENCE_SEPARATORS['sep_%s_default' % role] = default_separators[index] + REFERENCE_SEPARATORS['sep_{text}'.format(text=role)] = '\s*(?:{text})\s*'.format(text=source_string) + REFERENCE_SEPARATORS['sep_{text}_default'.format(text=role)] = default_separators[index] # verse range match: (:)?(-((:)?|end)?)? - range_regex = '(?:(?P[0-9]+)%(sep_v)s)?' \ - '(?P[0-9]+)(?P%(sep_r)s(?:(?:(?P' \ - '[0-9]+)%(sep_v)s)?(?P[0-9]+)|%(sep_e)s)?)?' % REFERENCE_SEPARATORS - REFERENCE_MATCHES['range'] = re.compile('^\s*%s\s*$' % range_regex, re.UNICODE) + range_regex = '(?:(?P[0-9]+){sep_v})?' \ + '(?P[0-9]+)(?P{sep_r}(?:(?:(?P' \ + '[0-9]+){sep_v})?(?P[0-9]+)|{sep_e})?)?'.format(**REFERENCE_SEPARATORS) + REFERENCE_MATCHES['range'] = re.compile('^\s*{text}\s*$'.format(text=range_regex), re.UNICODE) REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE) # full reference match: ((,(?!$)|(?=$)))+ + # NOTE: + # Need to research a little more before converting this to python3 string format REFERENCE_MATCHES['full'] = \ re.compile('^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$' @@ -331,10 +333,12 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): separator. """ + # TODO: + # Verify convertsion here before committing format change log.debug('parse_reference("%s")', reference) match = get_reference_match('full').match(reference) if match: - log.debug('Matched reference %s' % reference) + log.debug('Matched reference {text}'.format(text=reference)) book = match.group('book') if not book_ref_id: book_ref_id = bible.get_book_ref_id_by_localised_name(book, language_selection) @@ -400,7 +404,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): ref_list.append((book_ref_id, from_chapter, 1, -1)) return ref_list else: - log.debug('Invalid reference: %s' % reference) + log.warn('Invalid reference: {text}'.format(text=reference)) return None diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 86d73bf85..9c106a728 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -26,7 +26,7 @@ import json def assert_length(expected, iterable, msg=None): if len(iterable) != expected: if not msg: - msg = 'Expected length %s, got %s' % (expected, len(iterable)) + msg = 'Expected length {expected}, got {got}'.format(expected=expected, got=len(iterable)) raise AssertionError(msg) From d5b98b73081130626428f69b4be70d1718236a7f Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sat, 23 Apr 2016 12:55:47 -0700 Subject: [PATCH 12/43] Added projector power test --- openlp/core/lib/projector/constants.py | 6 +++- .../openlp_core_lib/test_projector_pjlink1.py | 33 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/projector/constants.py b/openlp/core/lib/projector/constants.py index 6bfc88a6f..5c0d6c6c2 100644 --- a/openlp/core/lib/projector/constants.py +++ b/openlp/core/lib/projector/constants.py @@ -297,7 +297,11 @@ PJLINK_ERST_STATUS = {'0': ERROR_STRING[E_OK], PJLINK_POWR_STATUS = {'0': S_STANDBY, '1': S_ON, '2': S_COOLDOWN, - '3': S_WARMUP} + '3': S_WARMUP, + S_STANDBY: '0', + S_ON: '1', + S_COOLDOWN: '2', + S_WARMUP: '3'} PJLINK_DEFAULT_SOURCES = {'1': translate('OpenLP.DB', 'RGB'), '2': translate('OpenLP.DB', 'Video'), diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink1.py b/tests/functional/openlp_core_lib/test_projector_pjlink1.py index 5d0d26ceb..95d86efe9 100644 --- a/tests/functional/openlp_core_lib/test_projector_pjlink1.py +++ b/tests/functional/openlp_core_lib/test_projector_pjlink1.py @@ -26,7 +26,8 @@ Package to test the openlp.core.lib.projector.pjlink1 package. from unittest import TestCase from openlp.core.lib.projector.pjlink1 import PJLink1 -from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING +from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_WARMUP, S_ON, \ + S_COOLDOWN, PJLINK_POWR_STATUS from tests.functional import patch from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE @@ -151,3 +152,33 @@ class TestPJLink(TestCase): 'Lamp 3 power status should have been set to TRUE') self.assertEquals(pjlink.lamp[2]['Hours'], 33333, 'Lamp 3 hours should have been set to 33333') + + @patch.object(pjlink_test, 'projectorReceivedData') + def projector_process_power_on_test(self, mock_projectorReceivedData): + """ + Test setting power on + """ + # GIVEN: Test object and preset + pjlink = pjlink_test + pjlink.power = S_STANDBY + + # WHEN: Call process_command with turn power on command + pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON]) + + # THEN: Power should be set to ON + self.assertEquals(pjlink.power, S_ON, 'Power should have been set to ON') + + @patch.object(pjlink_test, 'projectorReceivedData') + def projector_process_power_off_test(self, mock_projectorReceivedData): + """ + Test setting power off + """ + # GIVEN: Test object and preset + pjlink = pjlink_test + pjlink.power = S_ON + + # WHEN: Call process_command with turn power on command + pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_STANDBY]) + + # THEN: Power should be set to ON + self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY') From 3e0769d946bfe8ce81f31a1cb9b0a1b78ac0aa01 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sat, 23 Apr 2016 14:12:28 -0700 Subject: [PATCH 13/43] Fix extraneous print() in media plugin --- openlp/plugins/media/mediaplugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 1d5529084..bb8a4f6a5 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -160,7 +160,8 @@ def process_check_binary(program_path): """ program_type = None runlog = check_binary_exists(program_path) - print(runlog, type(runlog)) + # NOTE: Unneeded print statement - let originator clear it out if needed + # print(runlog, type(runlog)) # Analyse the output to see it the program is mediainfo for line in runlog.splitlines(): decoded_line = line.decode() From 292606ba38ac7007426c4409cc5ca4373a7fd22a Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sat, 23 Apr 2016 14:38:43 -0700 Subject: [PATCH 14/43] Fix translate() string format and some missed conversions --- openlp/core/__init__.py | 4 ++-- openlp/core/common/__init__.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index 5ed016309..68dd772be 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -223,8 +223,8 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication): translate('OpenLP', 'Backup of the data folder failed!')) return message = translate('OpenLP', - 'A backup of the data folder has been created' - 'at {text}'.format(text=data_folder_backup_path)) + 'A backup of the data folder has been created ' + 'at {text}').format(text=data_folder_backup_path) QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message) # Update the version in the settings diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 1f5ca0c30..3946ee0b0 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -286,7 +286,7 @@ def get_uno_command(connection_type='pipe'): CONNECTION = '"--accept=pipe,name=openlp_pipe;urp;"' else: CONNECTION = '"--accept=socket,host=localhost,port=2002;urp;"' - return '%s %s %s' % (command, OPTIONS, CONNECTION) + return '{cmd} {opt} {conn}'.format(cmd=command, opt=OPTIONS, conn=CONNECTION) def get_uno_instance(resolver, connection_type='pipe'): @@ -336,7 +336,7 @@ def delete_file(file_path_name): os.remove(file_path_name) return True except (IOError, OSError): - log.exception("Unable to delete file %s" % file_path_name) + log.exception("Unable to delete file {name}".format(name=file_path_name)) return False @@ -390,7 +390,7 @@ def check_binary_exists(program_path): :param program_path:The full path to the binary to check. :return: program output to be parsed """ - log.debug('testing program_path: %s', program_path) + log.debug('testing program_path: {text}'.format(text=program_path)) try: # Setup startupinfo options for check_output to avoid console popping up on windows if is_win(): @@ -404,5 +404,5 @@ def check_binary_exists(program_path): except Exception: trace_error_handler(log) runlog = '' - log.debug('check_output returned: %s' % runlog) + log.debug('check_output returned: {text}'.format(text=runlog)) return runlog From 28e14012a8c25d9838dd8e14fa194b3d221280d6 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sun, 24 Apr 2016 04:04:32 -0700 Subject: [PATCH 15/43] Remove unneeded print statement --- openlp/core/__init__.py | 4 ++-- openlp/core/common/__init__.py | 4 ++-- openlp/plugins/media/mediaplugin.py | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index 68dd772be..5ed016309 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -223,8 +223,8 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication): translate('OpenLP', 'Backup of the data folder failed!')) return message = translate('OpenLP', - 'A backup of the data folder has been created ' - 'at {text}').format(text=data_folder_backup_path) + 'A backup of the data folder has been created' + 'at {text}'.format(text=data_folder_backup_path)) QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message) # Update the version in the settings diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 3946ee0b0..8b6a935c4 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -336,7 +336,7 @@ def delete_file(file_path_name): os.remove(file_path_name) return True except (IOError, OSError): - log.exception("Unable to delete file {name}".format(name=file_path_name)) + log.exception("Unable to delete file {text}".format(text=file_path_name)) return False @@ -404,5 +404,5 @@ def check_binary_exists(program_path): except Exception: trace_error_handler(log) runlog = '' - log.debug('check_output returned: {text}'.format(text=runlog)) + log.debug('check_output returned: {text}'.format(text=runlog) return runlog diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index bb8a4f6a5..e258b5809 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -160,8 +160,6 @@ def process_check_binary(program_path): """ program_type = None runlog = check_binary_exists(program_path) - # NOTE: Unneeded print statement - let originator clear it out if needed - # print(runlog, type(runlog)) # Analyse the output to see it the program is mediainfo for line in runlog.splitlines(): decoded_line = line.decode() From 884f060dea2795be1387dd0b0f7561787cad398d Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sun, 24 Apr 2016 04:22:04 -0700 Subject: [PATCH 16/43] Oops and text in projector test --- openlp/core/common/__init__.py | 2 +- tests/functional/openlp_core_lib/test_projector_pjlink1.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 8b6a935c4..17fdf027b 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -404,5 +404,5 @@ def check_binary_exists(program_path): except Exception: trace_error_handler(log) runlog = '' - log.debug('check_output returned: {text}'.format(text=runlog) + log.debug('check_output returned: {text}'.format(text=runlog)) return runlog diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink1.py b/tests/functional/openlp_core_lib/test_projector_pjlink1.py index 95d86efe9..4f81b2660 100644 --- a/tests/functional/openlp_core_lib/test_projector_pjlink1.py +++ b/tests/functional/openlp_core_lib/test_projector_pjlink1.py @@ -156,7 +156,7 @@ class TestPJLink(TestCase): @patch.object(pjlink_test, 'projectorReceivedData') def projector_process_power_on_test(self, mock_projectorReceivedData): """ - Test setting power on + Test setting power to ON """ # GIVEN: Test object and preset pjlink = pjlink_test @@ -171,7 +171,7 @@ class TestPJLink(TestCase): @patch.object(pjlink_test, 'projectorReceivedData') def projector_process_power_off_test(self, mock_projectorReceivedData): """ - Test setting power off + Test setting power to STANDBY """ # GIVEN: Test object and preset pjlink = pjlink_test @@ -180,5 +180,5 @@ class TestPJLink(TestCase): # WHEN: Call process_command with turn power on command pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_STANDBY]) - # THEN: Power should be set to ON + # THEN: Power should be set to STANDBY self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY') From d3d6e6c620933803eed5435576e80321cc813820 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Wed, 27 Apr 2016 05:49:55 -0700 Subject: [PATCH 17/43] Missed translate() string format --- openlp/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index 5ed016309..6f6addbbd 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -224,7 +224,7 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication): return message = translate('OpenLP', 'A backup of the data folder has been created' - 'at {text}'.format(text=data_folder_backup_path)) + 'at {text}').format(text=data_folder_backup_path) QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message) # Update the version in the settings From 6158a0672c389cd8bb5d3bc1f9a9f26c62e101fd Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 30 Apr 2016 10:34:37 +0100 Subject: [PATCH 18/43] background videos --- openlp/core/ui/maindisplay.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 079235c2d..7e7929fd6 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -31,13 +31,15 @@ Some of the code for this form is based on the examples at: import html import logging +import os from PyQt5 import QtCore, QtWidgets, QtWebKit, QtWebKitWidgets, QtOpenGL, QtGui, QtMultimedia -from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, Settings, translate, is_macosx, is_win +from openlp.core.common import AppLocation, Registry, RegistryProperties, OpenLPMixin, Settings, translate,\ + is_macosx, is_win from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte from openlp.core.lib.theme import BackgroundType -from openlp.core.ui import HideMode, AlertLocation +from openlp.core.ui import HideMode, AlertLocation, DisplayControllerType if is_macosx(): from ctypes import pythonapi, c_void_p, c_char_p, py_object @@ -437,6 +439,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): :param service_item: The Service item to be used :param image_path: Where the image resides. + :param is_love: Are we the live controller assume not. """ self.web_loaded = False self.initial_fame = None @@ -457,13 +460,13 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): background = self.image_manager.get_image_bytes(self.override['image'], ImageSource.ImagePlugin) self.set_transparency(self.service_item.theme_data.background_type == BackgroundType.to_string(BackgroundType.Transparent)) - if self.service_item.theme_data.background_filename: - self.service_item.bg_image_bytes = self.image_manager.get_image_bytes( - self.service_item.theme_data.background_filename, ImageSource.Theme) - if image_path: - image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin) - else: - image_bytes = None + image_bytes = None + if self.service_item.theme_data.background_type == 'image': + if self.service_item.theme_data.background_filename: + self.service_item.bg_image_bytes = self.image_manager.get_image_bytes( + self.service_item.theme_data.background_filename, ImageSource.Theme) + if image_path: + image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin) html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes, plugins=self.plugin_manager.plugins) self.web_view.setHtml(html) @@ -475,6 +478,17 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): Registry().execute('slidecontroller_live_unblank') else: self.hide_display(self.hide_mode) + if self.service_item.theme_data.background_type == 'video' and self.is_live: + if self.service_item.theme_data.background_filename: + service_item = ServiceItem() + service_item.title = 'webkit' + service_item.processor = 'webkit' + path = os.path.join(AppLocation.get_section_data_path('themes'), + self.service_item.theme_data.theme_name) + service_item.add_from_command(path, + self.service_item.theme_data.background_filename, + ':/media/slidecontroller_multimedia.png') + self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True) self._hide_mouse() def footer(self, text): From c80f45320b84a4b56559500adf2437aaf4c20814 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 30 Apr 2016 16:40:23 +0100 Subject: [PATCH 19/43] video theme creation --- openlp/core/common/uistrings.py | 1 + openlp/core/lib/theme.py | 25 +++++++++++++++- openlp/core/ui/media/webkitplayer.py | 3 -- openlp/core/ui/themeform.py | 43 ++++++++++++++++++++++++++-- openlp/core/ui/thememanager.py | 4 +-- openlp/core/ui/themewizard.py | 37 ++++++++++++++++++++---- 6 files changed, 99 insertions(+), 14 deletions(-) diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py index f26b41fc5..c7e29415d 100644 --- a/openlp/core/common/uistrings.py +++ b/openlp/core/common/uistrings.py @@ -152,3 +152,4 @@ class UiStrings(object): self.Version = translate('OpenLP.Ui', 'Version') self.View = translate('OpenLP.Ui', 'View') self.ViewMode = translate('OpenLP.Ui', 'View Mode') + self.Video = translate('OpenLP.Ui', 'Video') diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 0ef0065a7..bd35c4bdb 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -44,6 +44,7 @@ class BackgroundType(object): Gradient = 1 Image = 2 Transparent = 3 + Video = 4 @staticmethod def to_string(background_type): @@ -58,6 +59,8 @@ class BackgroundType(object): return 'image' elif background_type == BackgroundType.Transparent: return 'transparent' + elif background_type == BackgroundType.Video: + return 'video' @staticmethod def from_string(type_string): @@ -72,6 +75,8 @@ class BackgroundType(object): return BackgroundType.Image elif type_string == 'transparent': return BackgroundType.Transparent + elif type_string == 'video': + return BackgroundType.Video class BackgroundGradientType(object): @@ -184,7 +189,7 @@ class ThemeXML(object): :param path: The path name to be added. """ - if self.background_type == 'image': + if self.background_type == 'image' or self.background_type == 'video': if self.background_filename and path: self.theme_name = self.theme_name.strip() self.background_filename = self.background_filename.strip() @@ -255,6 +260,21 @@ class ThemeXML(object): # Create endColor element self.child_element(background, 'borderColor', str(border_color)) + def add_background_video(self, filename, border_color): + """ + Add a video background. + + :param filename: The file name of the video. + :param border_color: + """ + background = self.theme_xml.createElement('background') + background.setAttribute('type', 'video') + self.theme.appendChild(background) + # Create Filename element + self.child_element(background, 'filename', filename) + # Create endColor element + self.child_element(background, 'borderColor', str(border_color)) + def add_font(self, name, color, size, override, fonttype='main', bold='False', italics='False', line_adjustment=0, xpos=0, ypos=0, width=0, height=0, outline='False', outline_color='#ffffff', outline_pixel=2, shadow='False', shadow_color='#ffffff', shadow_pixel=5): @@ -512,6 +532,9 @@ class ThemeXML(object): elif self.background_type == BackgroundType.to_string(BackgroundType.Image): filename = os.path.split(self.background_filename)[1] self.add_background_image(filename, self.background_border_color) + elif self.background_type == BackgroundType.to_string(BackgroundType.Video): + filename = os.path.split(self.background_filename)[1] + self.add_background_video(filename, self.background_border_color) elif self.background_type == BackgroundType.to_string(BackgroundType.Transparent): self.add_background_transparent() self.add_font( diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index cc8c7f55c..0b9b90a6e 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -55,9 +55,6 @@ VIDEO_JS = """ switch(state){ case 'load': video.src = 'file:///' + path; - if(loop == true) { - video.loop = true; - } video.load(); break; case 'play': diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index fc231a859..d18914411 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -31,7 +31,7 @@ from openlp.core.common import Registry, RegistryProperties, UiStrings, translat from openlp.core.lib.theme import BackgroundType, BackgroundGradientType from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui import ThemeLayoutForm -from openlp.core.ui.lib.colorbutton import ColorButton +from openlp.core.ui.media.webkitplayer import VIDEO_EXT from .themewizard import Ui_ThemeWizard log = logging.getLogger(__name__) @@ -66,10 +66,13 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): self.gradient_combo_box.currentIndexChanged.connect(self.on_gradient_combo_box_current_index_changed) self.color_button.colorChanged.connect(self.on_color_changed) self.image_color_button.colorChanged.connect(self.on_image_color_changed) + self.video_color_button.colorChanged.connect(self.on_video_color_changed) self.gradient_start_button.colorChanged.connect(self.on_gradient_start_color_changed) self.gradient_end_button.colorChanged.connect(self.on_gradient_end_color_changed) self.image_browse_button.clicked.connect(self.on_image_browse_button_clicked) self.image_file_edit.editingFinished.connect(self.on_image_file_edit_editing_finished) + self.video_browse_button.clicked.connect(self.on_video_browse_button_clicked) + self.video_file_edit.editingFinished.connect(self.on_video_file_edit_editing_finished) self.main_color_button.colorChanged.connect(self.on_main_color_changed) self.outline_color_button.colorChanged.connect(self.on_outline_color_changed) self.shadow_color_button.colorChanged.connect(self.on_shadow_color_changed) @@ -307,6 +310,10 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): self.image_color_button.color = self.theme.background_border_color self.image_file_edit.setText(self.theme.background_filename) self.setField('background_type', 2) + elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): + self.video_color_button.color = self.theme.background_border_color + self.video_file_edit.setText(self.theme.background_filename) + self.setField('background_type', 4) elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent): self.setField('background_type', 3) if self.theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal): @@ -384,10 +391,12 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): if self.update_theme_allowed: self.theme.background_type = BackgroundType.to_string(index) if self.theme.background_type != BackgroundType.to_string(BackgroundType.Image) and \ + self.theme.background_type != BackgroundType.to_string(BackgroundType.Video) and \ self.temp_background_filename == '': self.temp_background_filename = self.theme.background_filename self.theme.background_filename = '' - if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) and \ + if (self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or + self.theme.background_type != BackgroundType.to_string(BackgroundType.Video)) and \ self.temp_background_filename != '': self.theme.background_filename = self.temp_background_filename self.temp_background_filename = '' @@ -413,6 +422,12 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): """ self.theme.background_border_color = color + def on_video_color_changed(self, color): + """ + Background / Gradient 1 _color button pushed. + """ + self.theme.background_border_color = color + def on_gradient_start_color_changed(self, color): """ Gradient 2 _color button pushed. @@ -444,6 +459,27 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): """ self.theme.background_filename = str(self.image_file_edit.text()) + def on_video_browse_button_clicked(self): + """ + Background video button pushed. + """ + visible_formats = '(%s)' % '; '.join(VIDEO_EXT) + actual_formats = '(%s)' % ' '.join(VIDEO_EXT) + video_filter = '%s %s %s' % (translate('OpenLP', 'Video Files'), visible_formats, actual_formats) + video_filter = '%s;;%s (*.*)' % (video_filter, UiStrings().AllFiles) + filename, filter_used = QtWidgets.QFileDialog.getOpenFileName( + self, translate('OpenLP.ThemeWizard', 'Select Video'), + self.video_file_edit.text(), video_filter) + if filename: + self.theme.background_filename = filename + self.set_background_page_values() + + def on_video_file_edit_editing_finished(self): + """ + Background video path edited + """ + self.theme.background_filename = str(self.image_file_edit.text()) + def on_main_color_changed(self, color): """ Set the main colour value @@ -519,7 +555,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): return save_from = None save_to = None - if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image): + if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \ + self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): filename = os.path.split(str(self.theme.background_filename))[1] save_to = os.path.join(self.path, self.theme.theme_name, filename) save_from = self.theme.background_filename diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 32975e9aa..14a4663f4 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -300,7 +300,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage """ save_to = None save_from = None - if theme_data.background_type == 'image': + if theme_data.background_type == 'image' or theme_data.background_type == 'video': save_to = os.path.join(self.path, new_theme_name, os.path.split(str(theme_data.background_filename))[1]) save_from = theme_data.background_filename theme_data.theme_name = new_theme_name @@ -318,7 +318,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage translate('OpenLP.ThemeManager', 'You must select a theme to edit.')): item = self.theme_list_widget.currentItem() theme = self.get_theme_data(item.data(QtCore.Qt.UserRole)) - if theme.background_type == 'image': + if theme.background_type == 'image' or theme.background_type == 'video': self.old_background_image = theme.background_filename self.theme_form.theme = theme self.theme_form.exec(True) diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index ab8854ef2..ac596440c 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -62,7 +62,7 @@ class Ui_ThemeWizard(object): self.background_label = QtWidgets.QLabel(self.background_page) self.background_label.setObjectName('background_label') self.background_combo_box = QtWidgets.QComboBox(self.background_page) - self.background_combo_box.addItems(['', '', '', '']) + self.background_combo_box.addItems(['', '', '', '', '']) self.background_combo_box.setObjectName('background_combo_box') self.background_type_layout.addRow(self.background_label, self.background_combo_box) self.background_type_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer) @@ -135,6 +135,32 @@ class Ui_ThemeWizard(object): self.transparent_layout.setObjectName('Transparent_layout') self.background_stack.addWidget(self.transparent_widget) self.background_layout.addLayout(self.background_stack) + + self.video_widget = QtWidgets.QWidget(self.background_page) + self.video_widget.setObjectName('video_widget') + self.video_layout = QtWidgets.QFormLayout(self.video_widget) + self.video_layout.setContentsMargins(0, 0, 0, 0) + self.video_layout.setObjectName('video_layout') + self.video_color_label = QtWidgets.QLabel(self.color_widget) + self.video_color_label.setObjectName('video_color_label') + self.video_color_button = ColorButton(self.color_widget) + self.video_color_button.setObjectName('video_color_button') + self.video_layout.addRow(self.video_color_label, self.video_color_button) + self.video_label = QtWidgets.QLabel(self.video_widget) + self.video_label.setObjectName('video_label') + self.video_file_layout = QtWidgets.QHBoxLayout() + self.video_file_layout.setObjectName('video_file_layout') + self.video_file_edit = QtWidgets.QLineEdit(self.video_widget) + self.video_file_edit.setObjectName('video_file_edit') + self.video_file_layout.addWidget(self.video_file_edit) + self.video_browse_button = QtWidgets.QToolButton(self.video_widget) + self.video_browse_button.setObjectName('video_browse_button') + self.video_browse_button.setIcon(build_icon(':/general/general_open.png')) + self.video_file_layout.addWidget(self.video_browse_button) + self.video_layout.addRow(self.video_label, self.video_file_layout) + self.video_layout.setItem(2, QtWidgets.QFormLayout.LabelRole, self.spacer) + self.background_stack.addWidget(self.video_widget) + theme_wizard.addPage(self.background_page) # Main Area Page self.main_area_page = QtWidgets.QWizardPage() @@ -390,11 +416,10 @@ class Ui_ThemeWizard(object): self.background_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Set up your theme\'s background ' 'according to the parameters below.')) self.background_label.setText(translate('OpenLP.ThemeWizard', 'Background type:')) - self.background_combo_box.setItemText(BackgroundType.Solid, - translate('OpenLP.ThemeWizard', 'Solid color')) - self.background_combo_box.setItemText(BackgroundType.Gradient, - translate('OpenLP.ThemeWizard', 'Gradient')) + self.background_combo_box.setItemText(BackgroundType.Solid, translate('OpenLP.ThemeWizard', 'Solid color')) + self.background_combo_box.setItemText(BackgroundType.Gradient, translate('OpenLP.ThemeWizard', 'Gradient')) self.background_combo_box.setItemText(BackgroundType.Image, UiStrings().Image) + self.background_combo_box.setItemText(BackgroundType.Video, UiStrings().Video) self.background_combo_box.setItemText(BackgroundType.Transparent, translate('OpenLP.ThemeWizard', 'Transparent')) self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:')) @@ -413,6 +438,8 @@ class Ui_ThemeWizard(object): 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.video_color_label.setText(translate('OpenLP.ThemeWizard', 'Background color:')) + self.video_label.setText('%s:' % 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')) From 162f1ecbe9efd79fd6523d91dab8e71c44001984 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 30 Apr 2016 16:49:06 +0100 Subject: [PATCH 20/43] remove old loop --- openlp/core/ui/media/webkitplayer.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index 0b9b90a6e..4524d4cc7 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -45,7 +45,7 @@ VIDEO_CSS = """ """ VIDEO_JS = """ - function show_video(state, path, volume, loop, variable_value){ + function show_video(state, path, volume, variable_value){ // Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent! var video = document.getElementById('video'); @@ -177,12 +177,8 @@ class WebkitPlayer(MediaPlayer): else: vol = 0 path = controller.media_info.file_info.absoluteFilePath() - if controller.media_info.is_background: - loop = 'true' - else: - loop = 'false' display.web_view.setVisible(True) - js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol), loop) + js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol)) display.frame.evaluateJavaScript(js) return True From 38cd4b1f67dcc5ab8c116b3dbe7d41f9a82abd88 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 30 Apr 2016 18:12:40 +0100 Subject: [PATCH 21/43] with tests --- scripts/translation_utils.py | 4 +- .../openlp_core_ui/test_maindisplay.py | 61 ++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index 221948acc..c355089fd 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -60,7 +60,7 @@ import webbrowser from PyQt5 import QtCore from lxml import etree, objectify -SERVER_URL = 'http://www.transifex.net/api/2/project/openlp/resource/openlp-24x/' +SERVER_URL = 'http://www.transifex.net/api/2/project/openlp/resource/openlp-26x/' IGNORED_PATHS = ['scripts'] IGNORED_FILES = ['setup.py'] @@ -270,7 +270,7 @@ def update_translations(): return else: os.chdir(os.path.abspath('..')) - run('pylupdate4 -verbose -noobsolete openlp.pro') + run('pylupdate5 -verbose -noobsolete openlp.pro') os.chdir(os.path.abspath('scripts')) diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index fc7ae4910..1eb92e93e 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -27,8 +27,9 @@ from unittest import TestCase, skipUnless from PyQt5 import QtCore from openlp.core.common import Registry, is_macosx, Settings -from openlp.core.lib import ScreenList +from openlp.core.lib import ScreenList, PluginManager from openlp.core.ui import MainDisplay +from openlp.core.ui.media import MediaController from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET from tests.helpers.testmixin import TestMixin @@ -223,3 +224,61 @@ class TestMainDisplay(TestCase, TestMixin): # THEN: setVisible should had not been called main_display.setVisible.assert_not_called() + + @patch(u'openlp.core.ui.maindisplay.Settings') + @patch(u'openlp.core.ui.maindisplay.build_html') + def build_html_no_video_test(self, MockedSettings, Mocked_build_html): + # GIVEN: Mocked display + display = MagicMock() + mocked_media_controller = MagicMock() + Registry.create() + Registry().register('media_controller', mocked_media_controller) + main_display = MainDisplay(display) + main_display.frame = MagicMock() + mocked_settings = MagicMock() + mocked_settings.value.return_value = False + MockedSettings.return_value = mocked_settings + main_display.shake_web_view = MagicMock() + service_item = MagicMock() + mocked_plugin = MagicMock() + display.plugin_manager = PluginManager() + display.plugin_manager.plugins = [mocked_plugin] + main_display.web_view = MagicMock() + + # WHEN: build_html is called with a normal service item and a non video theme. + main_display.build_html(service_item) + + # THEN: the following should had not been called + self.assertEquals(main_display.web_view.setHtml.call_count, 1, 'setHTML should be called once') + self.assertEquals(main_display.media_controller.video.call_count, 0, + 'Media Controller video should not have been called') + + @patch(u'openlp.core.ui.maindisplay.Settings') + @patch(u'openlp.core.ui.maindisplay.build_html') + def build_html_video_test(self, MockedSettings, Mocked_build_html): + # GIVEN: Mocked display + display = MagicMock() + mocked_media_controller = MagicMock() + Registry.create() + Registry().register('media_controller', mocked_media_controller) + main_display = MainDisplay(display) + main_display.frame = MagicMock() + mocked_settings = MagicMock() + mocked_settings.value.return_value = False + MockedSettings.return_value = mocked_settings + main_display.shake_web_view = MagicMock() + service_item = MagicMock() + service_item.theme_data = MagicMock() + service_item.theme_data.background_type = 'video' + mocked_plugin = MagicMock() + display.plugin_manager = PluginManager() + display.plugin_manager.plugins = [mocked_plugin] + main_display.web_view = MagicMock() + + # WHEN: build_html is called with a normal service item and a video theme. + main_display.build_html(service_item) + + # THEN: the following should had not been called + self.assertEquals(main_display.web_view.setHtml.call_count, 1, 'setHTML should be called once') + self.assertEquals(main_display.media_controller.video.call_count, 1, + 'Media Controller video should have been called once') From 6d7039ed41786a43f1665816fad614dcf722bbd6 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 30 Apr 2016 18:19:55 +0100 Subject: [PATCH 22/43] minor fixes --- openlp/core/ui/maindisplay.py | 1 - openlp/core/ui/themewizard.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 7e7929fd6..ccad0071e 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -439,7 +439,6 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): :param service_item: The Service item to be used :param image_path: Where the image resides. - :param is_love: Are we the live controller assume not. """ self.web_loaded = False self.initial_fame = None diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index ac596440c..b546c1872 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -135,7 +135,6 @@ class Ui_ThemeWizard(object): self.transparent_layout.setObjectName('Transparent_layout') self.background_stack.addWidget(self.transparent_widget) self.background_layout.addLayout(self.background_stack) - self.video_widget = QtWidgets.QWidget(self.background_page) self.video_widget.setObjectName('video_widget') self.video_layout = QtWidgets.QFormLayout(self.video_widget) @@ -160,7 +159,6 @@ class Ui_ThemeWizard(object): self.video_layout.addRow(self.video_label, self.video_file_layout) self.video_layout.setItem(2, QtWidgets.QFormLayout.LabelRole, self.spacer) self.background_stack.addWidget(self.video_widget) - theme_wizard.addPage(self.background_page) # Main Area Page self.main_area_page = QtWidgets.QWizardPage() From 51613fd32d500270c62629a6c9c25e52cf600ee1 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 2 May 2016 09:12:39 +0100 Subject: [PATCH 23/43] fix strings --- openlp/core/ui/media/webkitplayer.py | 2 +- openlp/core/ui/themeform.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index 4524d4cc7..19221ace0 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -178,7 +178,7 @@ class WebkitPlayer(MediaPlayer): vol = 0 path = controller.media_info.file_info.absoluteFilePath() display.web_view.setVisible(True) - js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol)) + js = 'show_video("load", "{path}", {vol});'.format(path=path.replace('\\', '\\\\'), vol=str(vol)) display.frame.evaluateJavaScript(js) return True diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index d18914411..2ec733629 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -465,8 +465,9 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): """ visible_formats = '(%s)' % '; '.join(VIDEO_EXT) actual_formats = '(%s)' % ' '.join(VIDEO_EXT) - video_filter = '%s %s %s' % (translate('OpenLP', 'Video Files'), visible_formats, actual_formats) - video_filter = '%s;;%s (*.*)' % (video_filter, UiStrings().AllFiles) + video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'), + visible=visible_formats, actual=actual_formats) + video_filter = '{video};;{ui} (*.*)'.format(video=video_filter, ui=UiStrings().AllFiles) filename, filter_used = QtWidgets.QFileDialog.getOpenFileName( self, translate('OpenLP.ThemeWizard', 'Select Video'), self.video_file_edit.text(), video_filter) From f0d7cf7f36e6942c8aa795c180edae8a9112dd37 Mon Sep 17 00:00:00 2001 From: Ian Knight Date: Wed, 4 May 2016 21:40:42 +0930 Subject: [PATCH 24/43] Corrected aspect ratio on slide previews --- openlp/core/lib/__init__.py | 20 ++++++++++++++++++- openlp/core/lib/serviceitem.py | 1 + openlp/core/ui/listpreviewwidget.py | 13 ++++++------ openlp/core/ui/slidecontroller.py | 13 +++++++++--- .../lib/presentationcontroller.py | 4 ++-- 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 6e62bbf9c..4e85fe7e4 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -55,9 +55,13 @@ class ImageSource(object): ``Theme`` This says, that the image is used by a theme. + + ``PresentationPlugin`` + This states that an image is being used by the presentation plugin. """ ImagePlugin = 1 Theme = 2 + PresentationPlugin = 3 class MediaType(object): @@ -174,10 +178,24 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None): ext = os.path.splitext(thumb_path)[1].lower() reader = QtGui.QImageReader(image_path) if size is None: + # No size given; use default height of 88 ratio = reader.size().width() / reader.size().height() reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88)) - else: + elif size.isValid(): + # Complete size given reader.setScaledSize(size) + else: + # Invalid size given + ratio = reader.size().width() / reader.size().height() + if size.width() >= 0: + # Valid width; scale height + reader.setScaledSize(QtCore.QSize(size.width(), int(size.width() / ratio))) + elif size.height() >= 0: + # Valid height; scale width + reader.setScaledSize(QtCore.QSize(int(ratio * size.height()), size.height())) + else: + # Invalid; use default height of 88 + reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88)) thumb = reader.read() thumb.save(thumb_path, ext[1:]) if not return_icon: diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index e45aa6e61..32afbfe4f 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -334,6 +334,7 @@ class ServiceItem(RegistryProperties): file_location_hash, ntpath.basename(image)) self._raw_frames.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title, 'notes': notes}) + self.image_manager.add_image(image, ImageSource.PresentationPlugin, '#000000') self._new_item() def get_service_repr(self, lite_save): diff --git a/openlp/core/ui/listpreviewwidget.py b/openlp/core/ui/listpreviewwidget.py index 88aef818a..4d62e3173 100644 --- a/openlp/core/ui/listpreviewwidget.py +++ b/openlp/core/ui/listpreviewwidget.py @@ -152,14 +152,15 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): else: label.setScaledContents(True) if self.service_item.is_command(): - pixmap = QtGui.QPixmap(frame['image']) - pixmap.setDevicePixelRatio(label.devicePixelRatio()) - label.setPixmap(pixmap) + #pixmap = QtGui.QPixmap(frame['image']) + #pixmap.setDevicePixelRatio(label.devicePixelRatio()) + #label.setPixmap(pixmap) + image = self.image_manager.get_image(frame['image'], ImageSource.PresentationPlugin) else: image = self.image_manager.get_image(frame['path'], ImageSource.ImagePlugin) - pixmap = QtGui.QPixmap.fromImage(image) - pixmap.setDevicePixelRatio(label.devicePixelRatio()) - label.setPixmap(pixmap) + pixmap = QtGui.QPixmap.fromImage(image) + pixmap.setDevicePixelRatio(label.devicePixelRatio()) + label.setPixmap(pixmap) slide_height = width // self.screen_ratio # Setup and validate row height cap if in use. max_img_row_height = Settings().value('advanced/slide max height') diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 96ce82868..f04bff8fa 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -1132,9 +1132,16 @@ class SlideController(DisplayController, RegistryProperties): """ self.log_debug('update_preview %s ' % self.screens.current['primary']) if self.service_item and self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay): - # Grab now, but try again in a couple of seconds if slide change is slow - QtCore.QTimer.singleShot(500, self.grab_maindisplay) - QtCore.QTimer.singleShot(2500, self.grab_maindisplay) + if self.is_live: + # If live, grab screen-cap of main display now + QtCore.QTimer.singleShot(500, self.grab_maindisplay) + # but take another in a couple of seconds in case slide change is slow + QtCore.QTimer.singleShot(2500, self.grab_maindisplay) + else: + # If not live, use the slide's thumbnail instead + self.slide_image = QtGui.QPixmap.fromImage(self.image_manager.get_image(self.service_item.get_rendered_frame(self.selected_row), ImageSource.PresentationPlugin)) #QtGui.QPixmap(self.service_item.get_rendered_frame(self.selected_row)) + self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio()) + self.slide_preview.setPixmap(self.slide_image) else: self.slide_image = self.display.preview() self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio()) diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index abc71f867..fc90ddb0d 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -242,13 +242,13 @@ class PresentationDocument(object): def convert_thumbnail(self, file, idx): """ - Convert the slide image the application made to a standard 320x240 .png image. + Convert the slide image the application made to a scaled 360px height .png image. """ if self.check_thumbnails(): return if os.path.isfile(file): thumb_path = self.get_thumbnail_path(idx, False) - create_thumb(file, thumb_path, False, QtCore.QSize(320, 240)) + create_thumb(file, thumb_path, False, QtCore.QSize(-1, 360)) def get_thumbnail_path(self, slide_no, check_exists): """ From bc6253a6279f7fed580eb4b8802ceb811ce82883 Mon Sep 17 00:00:00 2001 From: Ian Knight Date: Thu, 5 May 2016 13:27:04 +0930 Subject: [PATCH 25/43] Housekeeping, added check for thumbnails to avoid icon scaling issues --- openlp/core/lib/__init__.py | 6 +++--- openlp/core/lib/serviceitem.py | 3 ++- openlp/core/ui/lib/listpreviewwidget.py | 10 +++++++--- openlp/core/ui/slidecontroller.py | 9 +++++++-- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 5a1006893..1fee2fe5a 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -56,12 +56,12 @@ class ImageSource(object): ``Theme`` This says, that the image is used by a theme. - ``PresentationPlugin`` - This states that an image is being used by the presentation plugin. + ``CommandPlugins`` + This states that an image is being used by a command plugin. """ ImagePlugin = 1 Theme = 2 - PresentationPlugin = 3 + CommandPlugins = 3 class MediaType(object): diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 32afbfe4f..2cbbb98e9 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -334,7 +334,8 @@ class ServiceItem(RegistryProperties): file_location_hash, ntpath.basename(image)) self._raw_frames.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title, 'notes': notes}) - self.image_manager.add_image(image, ImageSource.PresentationPlugin, '#000000') + if self.is_capable(ItemCapabilities.HasThumbnails): + self.image_manager.add_image(image, ImageSource.CommandPlugins, '#000000') self._new_item() def get_service_repr(self, lite_save): diff --git a/openlp/core/ui/lib/listpreviewwidget.py b/openlp/core/ui/lib/listpreviewwidget.py index e18292129..2383cc35f 100644 --- a/openlp/core/ui/lib/listpreviewwidget.py +++ b/openlp/core/ui/lib/listpreviewwidget.py @@ -27,7 +27,7 @@ It is based on a QTableWidget but represents its contents in list form. from PyQt5 import QtCore, QtGui, QtWidgets from openlp.core.common import RegistryProperties, Settings -from openlp.core.lib import ImageSource, ServiceItem +from openlp.core.lib import ImageSource, ItemCapabilities, ServiceItem class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): @@ -152,10 +152,14 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): else: label.setScaledContents(True) if self.service_item.is_command(): - image = self.image_manager.get_image(frame['image'], ImageSource.PresentationPlugin) + if self.service_item.is_capable(ItemCapabilities.HasThumbnails): + image = self.image_manager.get_image(frame['image'], ImageSource.CommandPlugins) + pixmap = QtGui.QPixmap.fromImage(image) + else: + pixmap = QtGui.QPixmap(frame['image']) else: image = self.image_manager.get_image(frame['path'], ImageSource.ImagePlugin) - pixmap = QtGui.QPixmap.fromImage(image) + pixmap = QtGui.QPixmap.fromImage(image) pixmap.setDevicePixelRatio(label.devicePixelRatio()) label.setPixmap(pixmap) slide_height = width // self.screen_ratio diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index db4e01210..c06860057 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -1141,8 +1141,13 @@ class SlideController(DisplayController, RegistryProperties): # but take another in a couple of seconds in case slide change is slow QtCore.QTimer.singleShot(2500, self.grab_maindisplay) else: - # If not live, use the slide's thumbnail instead - self.slide_image = QtGui.QPixmap.fromImage(self.image_manager.get_image(self.service_item.get_rendered_frame(self.selected_row), ImageSource.PresentationPlugin)) #QtGui.QPixmap(self.service_item.get_rendered_frame(self.selected_row)) + # If not live, use the slide's thumbnail/icon instead + image_path = self.service_item.get_rendered_frame(self.selected_row) + if self.service_item.is_capable(ItemCapabilities.HasThumbnails): + image = self.image_manager.get_image(image_path, ImageSource.CommandPlugins) + self.slide_image = QtGui.QPixmap.fromImage(image) + else: + self.slide_image = QtGui.QPixmap(image_path) self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio()) self.slide_preview.setPixmap(self.slide_image) else: From 16c0ebfcdac1c6fee563c860fbebf0c62df5ef95 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Thu, 5 May 2016 08:41:48 -0700 Subject: [PATCH 26/43] Revert regex string conversion until more testing --- openlp/plugins/bibles/lib/__init__.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 52f400c24..804755d18 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -211,24 +211,24 @@ def update_reference_separators(): while '||' in source_string: source_string = source_string.replace('||', '|') if role != 'e': - REFERENCE_SEPARATORS['sep_{text}_display'.format(text=role)] = source_string.split('|')[0] + REFERENCE_SEPARATORS['sep_{role}_display'.format(role=role)] = source_string.split('|')[0] # escape reserved characters for character in '\\.^$*+?{}[]()': source_string = source_string.replace(character, '\\' + character) # add various unicode alternatives source_string = source_string.replace('-', '(?:[-\u00AD\u2010\u2011\u2012\u2014\u2014\u2212\uFE63\uFF0D])') source_string = source_string.replace(',', '(?:[,\u201A])') - REFERENCE_SEPARATORS['sep_{text}'.format(text=role)] = '\s*(?:{text})\s*'.format(text=source_string) - REFERENCE_SEPARATORS['sep_{text}_default'.format(text=role)] = default_separators[index] + REFERENCE_SEPARATORS['sep_{role}'.format(role=role)] = '\s*(?:{source})\s*'.format(source=source_string) + REFERENCE_SEPARATORS['sep_{role}_default'.format(role=role)] = default_separators[index] # verse range match: (:)?(-((:)?|end)?)? - range_regex = '(?:(?P[0-9]+){sep_v})?' \ - '(?P[0-9]+)(?P{sep_r}(?:(?:(?P' \ - '[0-9]+){sep_v})?(?P[0-9]+)|{sep_e})?)?'.format(**REFERENCE_SEPARATORS) - REFERENCE_MATCHES['range'] = re.compile('^\s*{text}\s*$'.format(text=range_regex), re.UNICODE) + # TODO: Check before converting this string + range_regex = '(?:(?P[0-9]+)%(sep_v)s)?' \ + '(?P[0-9]+)(?P%(sep_r)s(?:(?:(?P' \ + '[0-9]+)%(sep_v)s)?(?P[0-9]+)|%(sep_e)s)?)?' % REFERENCE_SEPARATORS + # TODO: Test before converting re.compile strings + REFERENCE_MATCHES['range'] = re.compile('^\s*%s\s*$' % range_regex, re.UNICODE) REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE) # full reference match: ((,(?!$)|(?=$)))+ - # NOTE: - # Need to research a little more before converting this to python3 string format REFERENCE_MATCHES['full'] = \ re.compile('^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$' @@ -333,9 +333,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): separator. """ - # TODO: - # Verify convertsion here before committing format change - log.debug('parse_reference("%s")', reference) + log.debug('parse_reference("{text}")'.format(text=reference)) match = get_reference_match('full').match(reference) if match: log.debug('Matched reference {text}'.format(text=reference)) @@ -404,7 +402,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): ref_list.append((book_ref_id, from_chapter, 1, -1)) return ref_list else: - log.warn('Invalid reference: {text}'.format(text=reference)) + log.debug('Invalid reference: {text}'.format(text=reference)) return None From 55002518efe6ff33865f23c9d37b5730e9e5a643 Mon Sep 17 00:00:00 2001 From: Ian Knight Date: Fri, 6 May 2016 04:27:32 +0930 Subject: [PATCH 27/43] Added Functional Tests, cleaned PEP8 errors --- openlp/core/lib/__init__.py | 2 +- tests/functional/openlp_core_lib/test_lib.py | 143 ++++++++++++++- .../openlp_core_lib/test_serviceitem.py | 7 +- .../openlp_core_ui/test_slidecontroller.py | 171 +++++++++++++++++- .../test_listpreviewwidget.py | 56 +++++- 5 files changed, 372 insertions(+), 7 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 1fee2fe5a..d96b43148 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -55,7 +55,7 @@ class ImageSource(object): ``Theme`` This says, that the image is used by a theme. - + ``CommandPlugins`` This states that an image is being used by a command plugin. """ diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 1bd42bec4..8f94276a6 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -250,7 +250,7 @@ class TestLib(TestCase): def create_thumb_with_size_test(self): """ - Test the create_thumb() function + Test the create_thumb() function with a given size. """ # GIVEN: An image to create a thumb of. image_path = os.path.join(TEST_PATH, 'church.jpg') @@ -270,7 +270,7 @@ class TestLib(TestCase): # WHEN: Create the thumb. icon = create_thumb(image_path, thumb_path, size=thumb_size) - # THEN: Check if the thumb was created. + # THEN: Check if the thumb was created and scaled to the given size. self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists') self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon') self.assertFalse(icon.isNull(), 'The icon should not be null') @@ -282,6 +282,145 @@ class TestLib(TestCase): except: pass + def create_thumb_no_size_test(self): + """ + Test the create_thumb() function with no size specified. + """ + # GIVEN: An image to create a thumb of. + image_path = os.path.join(TEST_PATH, 'church.jpg') + thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg') + expected_size = QtCore.QSize(63, 88) + + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + os.remove(thumb_path) + except: + pass + + # Only continue when the thumb does not exist. + self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.') + + # WHEN: Create the thumb. + icon = create_thumb(image_path, thumb_path) + + # THEN: Check if the thumb was created, retaining its aspect ratio. + self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists') + self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon') + self.assertFalse(icon.isNull(), 'The icon should not be null') + self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size') + + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + os.remove(thumb_path) + except: + pass + + def create_thumb_invalid_size_test(self): + """ + Test the create_thumb() function with invalid size specified. + """ + # GIVEN: An image to create a thumb of. + image_path = os.path.join(TEST_PATH, 'church.jpg') + thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg') + thumb_size = QtCore.QSize(-1, -1) + expected_size = QtCore.QSize(63, 88) + + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + os.remove(thumb_path) + except: + pass + + # Only continue when the thumb does not exist. + self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.') + + # WHEN: Create the thumb. + icon = create_thumb(image_path, thumb_path) + + # THEN: Check if the thumb was created, retaining its aspect ratio. + self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists') + self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon') + self.assertFalse(icon.isNull(), 'The icon should not be null') + self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size') + + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + os.remove(thumb_path) + except: + pass + + def create_thumb_width_only_test(self): + """ + Test the create_thumb() function with a size of only width specified. + """ + # GIVEN: An image to create a thumb of. + image_path = os.path.join(TEST_PATH, 'church.jpg') + thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg') + thumb_size = QtCore.QSize(100, -1) + expected_size = QtCore.QSize(100, 137) + + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + os.remove(thumb_path) + except: + pass + + # Only continue when the thumb does not exist. + self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.') + + # WHEN: Create the thumb. + icon = create_thumb(image_path, thumb_path, size=thumb_size) + + # THEN: Check if the thumb was created, retaining its aspect ratio. + self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists') + self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon') + self.assertFalse(icon.isNull(), 'The icon should not be null') + self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size') + + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + os.remove(thumb_path) + except: + pass + + def create_thumb_height_only_test(self): + """ + Test the create_thumb() function with a size of only height specified. + """ + # GIVEN: An image to create a thumb of. + image_path = os.path.join(TEST_PATH, 'church.jpg') + thumb_path = os.path.join(TEST_PATH, 'church_thumb.jpg') + thumb_size = QtCore.QSize(-1, 100) + expected_size = QtCore.QSize(72, 100) + + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + os.remove(thumb_path) + except: + pass + + # Only continue when the thumb does not exist. + self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.') + + # WHEN: Create the thumb. + icon = create_thumb(image_path, thumb_path, size=thumb_size) + + # THEN: Check if the thumb was created, retaining its aspect ratio. + self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists') + self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon') + self.assertFalse(icon.isNull(), 'The icon should not be null') + self.assertEqual(expected_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size') + + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + os.remove(thumb_path) + except: + pass + def check_item_selected_true_test(self): """ Test that the check_item_selected() function returns True when there are selected indexes diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index 449746e12..cda7ad91b 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -244,14 +244,16 @@ class TestServiceItem(TestCase): self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command') self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match') + @patch(u'openlp.core.lib.serviceitem.ServiceItem.image_manager') @patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') - def add_from_command_for_a_presentation_thumb_test(self, mocked_get_section_data_path): + def add_from_command_for_a_presentation_thumb_test(self, mocked_get_section_data_path, mocked_image_manager): """ - Test the Service Item - adding a presentation, and updating the thumb path + Test the Service Item - adding a presentation, updating the thumb path & adding the thumb to image_manager """ # GIVEN: A service item, a mocked AppLocation and presentation data mocked_get_section_data_path.return_value = os.path.join('mocked', 'section', 'path') service_item = ServiceItem(None) + service_item.add_capability(ItemCapabilities.HasThumbnails) service_item.has_original_files = False service_item.name = 'presentations' presentation_name = 'test.pptx' @@ -270,6 +272,7 @@ class TestServiceItem(TestCase): # THEN: verify that it is setup as a Command and that the frame data matches self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command') self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match') + self.assertEqual(1, mocked_image_manager.add_image.call_count, 'image_manager should be used') def service_item_load_optical_media_from_service_test(self): """ diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py index 20e48cbe0..65cce0a45 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core_ui/test_slidecontroller.py @@ -26,7 +26,7 @@ from PyQt5 import QtCore, QtGui from unittest import TestCase from openlp.core import Registry -from openlp.core.lib import ServiceItemAction +from openlp.core.lib import ImageSource, ServiceItemAction from openlp.core.ui import SlideController, LiveController, PreviewController from openlp.core.ui.slidecontroller import InfoLabel, WIDE_MENU, NON_TEXT_MENU @@ -713,6 +713,175 @@ class TestSlideController(TestCase): slide_controller.theme_screen, slide_controller.blank_screen ]) + @patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager') + @patch(u'PyQt5.QtCore.QTimer.singleShot') + def update_preview_test_live(self, mocked_singleShot, mocked_image_manager): + """ + Test that the preview screen is updated with the correct preview for different service items + """ + # GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute + # and a slide controller with many mocks. + # Mocked Live Item + mocked_live_item = MagicMock() + mocked_live_item.get_rendered_frame.return_value = '' + mocked_live_item.is_capable = MagicMock() + mocked_live_item.is_capable.side_effect = [True, True] + # Mock image_manager + mocked_image_manager.get_image.return_value = QtGui.QImage() + # Mock Registry + Registry.create() + mocked_main_window = MagicMock() + Registry().register('main_window', mocked_main_window) + # Mock SlideController + slide_controller = SlideController(None) + slide_controller.service_item = mocked_live_item + slide_controller.is_live = True + slide_controller.log_debug = MagicMock() + slide_controller.selected_row = MagicMock() + slide_controller.screens = MagicMock() + slide_controller.screens.current = {'primary': ''} + slide_controller.display = MagicMock() + slide_controller.display.preview.return_value = QtGui.QImage() + slide_controller.grab_maindisplay = MagicMock() + slide_controller.slide_preview = MagicMock() + slide_controller.slide_count = 0 + + # WHEN: update_preview is called + slide_controller.update_preview() + + # THEN: Registry.execute should have been called to stop the presentation + self.assertEqual(0, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should not be called') + self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called') + self.assertEqual(2, mocked_singleShot.call_count, + 'Timer to grab_maindisplay should have been called 2 times') + self.assertEqual(0, mocked_image_manager.get_image.call_count, 'image_manager not be called') + + @patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager') + @patch(u'PyQt5.QtCore.QTimer.singleShot') + def update_preview_test_pres(self, mocked_singleShot, mocked_image_manager): + """ + Test that the preview screen is updated with the correct preview for different service items + """ + # GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute + # and a slide controller with many mocks. + # Mocked Presentation Item + mocked_pres_item = MagicMock() + mocked_pres_item.get_rendered_frame.return_value = '' + mocked_pres_item.is_capable = MagicMock() + mocked_pres_item.is_capable.side_effect = [True, True] + # Mock image_manager + mocked_image_manager.get_image.return_value = QtGui.QImage() + # Mock Registry + Registry.create() + mocked_main_window = MagicMock() + Registry().register('main_window', mocked_main_window) + # Mock SlideController + slide_controller = SlideController(None) + slide_controller.service_item = mocked_pres_item + slide_controller.is_live = False + slide_controller.log_debug = MagicMock() + slide_controller.selected_row = MagicMock() + slide_controller.screens = MagicMock() + slide_controller.screens.current = {'primary': ''} + slide_controller.display = MagicMock() + slide_controller.display.preview.return_value = QtGui.QImage() + slide_controller.grab_maindisplay = MagicMock() + slide_controller.slide_preview = MagicMock() + slide_controller.slide_count = 0 + + # WHEN: update_preview is called + slide_controller.update_preview() + + # THEN: Registry.execute should have been called to stop the presentation + self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called') + self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called') + self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called') + self.assertEqual(1, mocked_image_manager.get_image.call_count, 'image_manager should be called') + + @patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager') + @patch(u'PyQt5.QtCore.QTimer.singleShot') + def update_preview_test_media(self, mocked_singleShot, mocked_image_manager): + """ + Test that the preview screen is updated with the correct preview for different service items + """ + # GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute + # and a slide controller with many mocks. + # Mocked Media Item + mocked_media_item = MagicMock() + mocked_media_item.get_rendered_frame.return_value = '' + mocked_media_item.is_capable = MagicMock() + mocked_media_item.is_capable.side_effect = [True, False] + # Mock image_manager + mocked_image_manager.get_image.return_value = QtGui.QImage() + # Mock Registry + Registry.create() + mocked_main_window = MagicMock() + Registry().register('main_window', mocked_main_window) + # Mock SlideController + slide_controller = SlideController(None) + slide_controller.service_item = mocked_media_item + slide_controller.is_live = False + slide_controller.log_debug = MagicMock() + slide_controller.selected_row = MagicMock() + slide_controller.screens = MagicMock() + slide_controller.screens.current = {'primary': ''} + slide_controller.display = MagicMock() + slide_controller.display.preview.return_value = QtGui.QImage() + slide_controller.grab_maindisplay = MagicMock() + slide_controller.slide_preview = MagicMock() + slide_controller.slide_count = 0 + + # WHEN: update_preview is called + slide_controller.update_preview() + + # THEN: Registry.execute should have been called to stop the presentation + self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called') + self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called') + self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called') + self.assertEqual(0, mocked_image_manager.get_image.call_count, 'image_manager should not be called') + + @patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager') + @patch(u'PyQt5.QtCore.QTimer.singleShot') + def update_preview_test_image(self, mocked_singleShot, mocked_image_manager): + """ + Test that the preview screen is updated with the correct preview for different service items + """ + # GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute + # and a slide controller with many mocks. + # Mocked Image Item + mocked_img_item = MagicMock() + mocked_img_item.get_rendered_frame.return_value = '' + mocked_img_item.is_capable = MagicMock() + mocked_img_item.is_capable.side_effect = [False, True] + # Mock image_manager + mocked_image_manager.get_image.return_value = QtGui.QImage() + # Mock Registry + Registry.create() + mocked_main_window = MagicMock() + Registry().register('main_window', mocked_main_window) + # Mock SlideController + slide_controller = SlideController(None) + slide_controller.service_item = mocked_img_item + slide_controller.is_live = False + slide_controller.log_debug = MagicMock() + slide_controller.selected_row = MagicMock() + slide_controller.screens = MagicMock() + slide_controller.screens.current = {'primary': ''} + slide_controller.display = MagicMock() + slide_controller.display.preview.return_value = QtGui.QImage() + slide_controller.grab_maindisplay = MagicMock() + slide_controller.slide_preview = MagicMock() + slide_controller.slide_count = 0 + + # WHEN: update_preview is called + slide_controller.update_preview() + + # THEN: Registry.execute should have been called to stop the presentation + self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called') + self.assertEqual(1, slide_controller.display.preview.call_count, 'display.preview() should be called') + self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called') + self.assertEqual(0, mocked_image_manager.get_image.call_count, 'image_manager should not be called') + class TestInfoLabel(TestCase): diff --git a/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py b/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py index 0ed88cc88..275c90af6 100644 --- a/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py +++ b/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py @@ -24,9 +24,11 @@ Package to test the openlp.core.ui.lib.listpreviewwidget package. """ from unittest import TestCase +from PyQt5 import QtGui + from openlp.core.common import Settings from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget -from openlp.core.lib import ServiceItem +from openlp.core.lib import ImageSource, ServiceItem from tests.functional import MagicMock, patch, call @@ -72,6 +74,54 @@ class TestListPreviewWidget(TestCase): self.assertIsNotNone(list_preview_widget, 'The ListPreviewWidget object should not be None') self.assertEquals(list_preview_widget.screen_ratio, 1, 'Should not be called') + @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.image_manager') + @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents') + @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight') + def replace_service_item_test_thumbs(self, mocked_setRowHeight, mocked_resizeRowsToContents, + mocked_image_manager): + """ + Test that thubmails for different slides are loaded properly in replace_service_item. + """ + # GIVEN: A setting to adjust "Max height for non-text slides in slide controller", + # different ServiceItem(s) and a ListPreviewWidget. + + # Mock Settings().value('advanced/slide max height') + self.mocked_Settings_obj.value.return_value = 0 + # Mock self.viewport().width() + self.mocked_viewport_obj.width.return_value = 200 + # Mock Image service item + mocked_img_service_item = MagicMock() + mocked_img_service_item.is_text.return_value = False + mocked_img_service_item.is_media.return_value = False + mocked_img_service_item.is_command.return_value = False + mocked_img_service_item.is_capable.return_value = False + mocked_img_service_item.get_frames.return_value = [{'title': None, 'path': 'TEST1', 'image': 'FAIL'}, + {'title': None, 'path': 'TEST2', 'image': 'FAIL'}] + # Mock Command service item + mocked_cmd_service_item = MagicMock() + mocked_cmd_service_item.is_text.return_value = False + mocked_cmd_service_item.is_media.return_value = False + mocked_cmd_service_item.is_command.return_value = True + mocked_cmd_service_item.is_capable.return_value = True + mocked_cmd_service_item.get_frames.return_value = [{'title': None, 'path': 'FAIL', 'image': 'TEST3'}, + {'title': None, 'path': 'FAIL', 'image': 'TEST4'}] + # Mock image_manager + mocked_image_manager.get_image.return_value = QtGui.QImage() + + # init ListPreviewWidget and load service item + list_preview_widget = ListPreviewWidget(None, 1) + + # WHEN: replace_service_item is called + list_preview_widget.replace_service_item(mocked_img_service_item, 200, 0) + list_preview_widget.replace_service_item(mocked_cmd_service_item, 200, 0) + + # THEN: resizeRowsToContents() should not be called, while setRowHeight() should be called + # twice for each slide. + self.assertEquals(mocked_image_manager.get_image.call_count, 4, 'Should be called once for each slide') + calls = [call('TEST1', ImageSource.ImagePlugin), call('TEST2', ImageSource.ImagePlugin), + call('TEST3', ImageSource.CommandPlugins), call('TEST4', ImageSource.CommandPlugins)] + mocked_image_manager.get_image.assert_has_calls(calls) + @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents') @patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight') def replace_recalculate_layout_test_text(self, mocked_setRowHeight, mocked_resizeRowsToContents): @@ -120,6 +170,7 @@ class TestListPreviewWidget(TestCase): # Mock image service item service_item = MagicMock() service_item.is_text.return_value = False + service_item.is_capable.return_value = False service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None}, {'title': None, 'path': None, 'image': None}] # init ListPreviewWidget and load service item @@ -156,6 +207,7 @@ class TestListPreviewWidget(TestCase): # Mock image service item service_item = MagicMock() service_item.is_text.return_value = False + service_item.is_capable.return_value = False service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None}, {'title': None, 'path': None, 'image': None}] # init ListPreviewWidget and load service item @@ -225,6 +277,7 @@ class TestListPreviewWidget(TestCase): # Mock image service item service_item = MagicMock() service_item.is_text.return_value = False + service_item.is_capable.return_value = False service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None}, {'title': None, 'path': None, 'image': None}] # Mock self.cellWidget().children().setMaximumWidth() @@ -261,6 +314,7 @@ class TestListPreviewWidget(TestCase): # Mock image service item service_item = MagicMock() service_item.is_text.return_value = False + service_item.is_capable.return_value = False service_item.get_frames.return_value = [{'title': None, 'path': None, 'image': None}, {'title': None, 'path': None, 'image': None}] # Mock self.cellWidget().children().setMaximumWidth() From aac09c0afbc08319a54bace4aee5f20097387cb9 Mon Sep 17 00:00:00 2001 From: Ian Knight Date: Fri, 6 May 2016 04:33:12 +0930 Subject: [PATCH 28/43] Fixed create_thumb_invalid_size_test --- tests/functional/openlp_core_lib/test_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 8f94276a6..a792e3aee 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -337,7 +337,7 @@ class TestLib(TestCase): self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.') # WHEN: Create the thumb. - icon = create_thumb(image_path, thumb_path) + icon = create_thumb(image_path, thumb_path, size=thumb_size) # THEN: Check if the thumb was created, retaining its aspect ratio. self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists') From 9ac95cf66553659d893bf69ddff56c2e62043a2b Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Thu, 5 May 2016 12:37:48 -0700 Subject: [PATCH 29/43] Skip crosswalk.com test while server down --- openlp/core/common/__init__.py | 7 +++---- tests/interfaces/openlp_plugins/bibles/test_lib_http.py | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 17fdf027b..f1abd8fe6 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -55,10 +55,9 @@ def trace_error_handler(logger): """ log_string = "OpenLP Error trace" for tb in traceback.extract_stack(): - log_string = '{text}\n File {file} at line {line} \n\t called {data}'.format(text=log_string, - file=tb[0], - line=tb[1], - data=tb[3]) + log_string += '\n File {file} at line {line} \n\t called {data}'.format(file=tb[0], + line=tb[1], + data=tb[3]) logger.error(log_string) diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py index ca2269aa4..5d73d01e8 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py @@ -22,7 +22,7 @@ """ Package to test the openlp.plugin.bible.lib.https package. """ -from unittest import TestCase +from unittest import TestCase, skip from openlp.core.common import Registry from openlp.plugins.bibles.lib.http import BGExtract, CWExtract, BSExtract @@ -146,6 +146,7 @@ class TestBibleHTTP(TestCase): self.assertIsNotNone(bibles) self.assertIn(('Holman Christian Standard Bible', 'HCSB', 'en'), bibles) + @skip("Waiting for Crosswalk to fix their server") def crosswalk_get_bibles_test(self): """ Test getting list of bibles from Crosswalk.com From 3cdbaff2a65678936b4fed83c296acff83221955 Mon Sep 17 00:00:00 2001 From: Ian Knight Date: Fri, 6 May 2016 11:16:49 +0930 Subject: [PATCH 30/43] Housekeeping --- .../openlp_core_ui/test_slidecontroller.py | 24 +++++++++---------- .../test_listpreviewwidget.py | 5 ++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py index 65cce0a45..f47601bbd 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core_ui/test_slidecontroller.py @@ -717,9 +717,9 @@ class TestSlideController(TestCase): @patch(u'PyQt5.QtCore.QTimer.singleShot') def update_preview_test_live(self, mocked_singleShot, mocked_image_manager): """ - Test that the preview screen is updated with the correct preview for different service items + Test that the preview screen is updated with a screen grab for live service items """ - # GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute + # GIVEN: A mocked live service item, a mocked image_manager, a mocked Registry, # and a slide controller with many mocks. # Mocked Live Item mocked_live_item = MagicMock() @@ -749,7 +749,7 @@ class TestSlideController(TestCase): # WHEN: update_preview is called slide_controller.update_preview() - # THEN: Registry.execute should have been called to stop the presentation + # THEN: A screen_grab should have been called self.assertEqual(0, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should not be called') self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called') self.assertEqual(2, mocked_singleShot.call_count, @@ -760,9 +760,9 @@ class TestSlideController(TestCase): @patch(u'PyQt5.QtCore.QTimer.singleShot') def update_preview_test_pres(self, mocked_singleShot, mocked_image_manager): """ - Test that the preview screen is updated with the correct preview for different service items + Test that the preview screen is updated with the correct preview for presentation service items """ - # GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute + # GIVEN: A mocked presentation service item, a mocked image_manager, a mocked Registry, # and a slide controller with many mocks. # Mocked Presentation Item mocked_pres_item = MagicMock() @@ -792,7 +792,7 @@ class TestSlideController(TestCase): # WHEN: update_preview is called slide_controller.update_preview() - # THEN: Registry.execute should have been called to stop the presentation + # THEN: setPixmap and the image_manager should have been called self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called') self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called') self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called') @@ -802,9 +802,9 @@ class TestSlideController(TestCase): @patch(u'PyQt5.QtCore.QTimer.singleShot') def update_preview_test_media(self, mocked_singleShot, mocked_image_manager): """ - Test that the preview screen is updated with the correct preview for different service items + Test that the preview screen is updated with the correct preview for media service items """ - # GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute + # GIVEN: A mocked media service item, a mocked image_manager, a mocked Registry, # and a slide controller with many mocks. # Mocked Media Item mocked_media_item = MagicMock() @@ -834,7 +834,7 @@ class TestSlideController(TestCase): # WHEN: update_preview is called slide_controller.update_preview() - # THEN: Registry.execute should have been called to stop the presentation + # THEN: setPixmap should have been called self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called') self.assertEqual(0, slide_controller.display.preview.call_count, 'display.preview() should not be called') self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called') @@ -844,9 +844,9 @@ class TestSlideController(TestCase): @patch(u'PyQt5.QtCore.QTimer.singleShot') def update_preview_test_image(self, mocked_singleShot, mocked_image_manager): """ - Test that the preview screen is updated with the correct preview for different service items + Test that the preview screen is updated with the correct preview for image service items """ - # GIVEN: A mocked presentation service item, a mocked media service item, a mocked Registry.execute + # GIVEN: A mocked image service item, a mocked image_manager, a mocked Registry, # and a slide controller with many mocks. # Mocked Image Item mocked_img_item = MagicMock() @@ -876,7 +876,7 @@ class TestSlideController(TestCase): # WHEN: update_preview is called slide_controller.update_preview() - # THEN: Registry.execute should have been called to stop the presentation + # THEN: setPixmap and display.preview should have been called self.assertEqual(1, slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should be called') self.assertEqual(1, slide_controller.display.preview.call_count, 'display.preview() should be called') self.assertEqual(0, mocked_singleShot.call_count, 'Timer to grab_maindisplay should not be called') diff --git a/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py b/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py index 275c90af6..704acc544 100644 --- a/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py +++ b/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py @@ -83,7 +83,7 @@ class TestListPreviewWidget(TestCase): Test that thubmails for different slides are loaded properly in replace_service_item. """ # GIVEN: A setting to adjust "Max height for non-text slides in slide controller", - # different ServiceItem(s) and a ListPreviewWidget. + # different ServiceItem(s), an ImageManager, and a ListPreviewWidget. # Mock Settings().value('advanced/slide max height') self.mocked_Settings_obj.value.return_value = 0 @@ -115,8 +115,7 @@ class TestListPreviewWidget(TestCase): list_preview_widget.replace_service_item(mocked_img_service_item, 200, 0) list_preview_widget.replace_service_item(mocked_cmd_service_item, 200, 0) - # THEN: resizeRowsToContents() should not be called, while setRowHeight() should be called - # twice for each slide. + # THEN: The ImageManager should be called in the appriopriate manner for each service item. self.assertEquals(mocked_image_manager.get_image.call_count, 4, 'Should be called once for each slide') calls = [call('TEST1', ImageSource.ImagePlugin), call('TEST2', ImageSource.ImagePlugin), call('TEST3', ImageSource.CommandPlugins), call('TEST4', ImageSource.CommandPlugins)] From df654d72fb1ca4c1a69f0f37da83845ee6eaab7e Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 10 May 2016 05:36:25 +0100 Subject: [PATCH 31/43] change net to com --- openlp/core/ui/servicemanager.py | 2 +- scripts/translation_utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 82b489344..a59f94377 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1323,7 +1323,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa """ The theme may have changed in the settings dialog so make sure the theme combo box is in the correct state. """ - visible = self.renderer.theme_level == ThemeLevel.Global + visible = not self.renderer.theme_level == ThemeLevel.Global self.theme_label.setVisible(visible) self.theme_combo_box.setVisible(visible) diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index c355089fd..a82ac44cf 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -60,7 +60,7 @@ import webbrowser from PyQt5 import QtCore from lxml import etree, objectify -SERVER_URL = 'http://www.transifex.net/api/2/project/openlp/resource/openlp-26x/' +SERVER_URL = 'http://www.transifex.com/api/2/project/openlp/resource/openlp-26x/' IGNORED_PATHS = ['scripts'] IGNORED_FILES = ['setup.py'] From 33eb1ba274110b9023623b54ff30959538746bc0 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 10 May 2016 05:40:59 +0100 Subject: [PATCH 32/43] remove rogue print --- openlp/plugins/media/mediaplugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 1d5529084..e258b5809 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -160,7 +160,6 @@ def process_check_binary(program_path): """ program_type = None runlog = check_binary_exists(program_path) - print(runlog, type(runlog)) # Analyse the output to see it the program is mediainfo for line in runlog.splitlines(): decoded_line = line.decode() From f51132e876faa6c321d179ea6087203a5ce4e846 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Fri, 13 May 2016 21:24:46 -0700 Subject: [PATCH 33/43] core/common convert strings to python3 format --- openlp/core/common/actions.py | 16 +++++++++------- openlp/core/common/languagemanager.py | 11 ++++++----- openlp/core/common/openlpmixin.py | 5 +++-- openlp/core/common/registry.py | 12 ++++++------ openlp/core/common/settings.py | 10 +++++----- openlp/core/common/uistrings.py | 6 ++++-- openlp/core/common/versionchecker.py | 17 ++++++++++------- .../openlp_core_lib/test_projectordb.py | 14 ++++++++++++++ 8 files changed, 57 insertions(+), 34 deletions(-) diff --git a/openlp/core/common/actions.py b/openlp/core/common/actions.py index cb5b9ccaf..7f8c90f86 100644 --- a/openlp/core/common/actions.py +++ b/openlp/core/common/actions.py @@ -114,7 +114,7 @@ class CategoryActionList(object): if item[1] == action: self.actions.remove(item) return - raise ValueError('Action "%s" does not exist.' % action) + raise ValueError('Action "{action}" does not exist.'.format(action=action)) class CategoryList(object): @@ -138,7 +138,7 @@ class CategoryList(object): for category in self.categories: if category.name == key: return category - raise KeyError('Category "%s" does not exist.' % key) + raise KeyError('Category "{keY}" does not exist.'.format(key=key)) def __len__(self): """ @@ -203,7 +203,7 @@ class CategoryList(object): if category.name == name: self.categories.remove(category) return - raise ValueError('Category "%s" does not exist.' % name) + raise ValueError('Category "{name}" does not exist.'.format(name=name)) class ActionList(object): @@ -272,8 +272,9 @@ class ActionList(object): actions.append(action) ActionList.shortcut_map[shortcuts[1]] = actions else: - log.warning('Shortcut "%s" is removed from "%s" because another action already uses this shortcut.' % - (shortcuts[1], action.objectName())) + log.warning('Shortcut "{shortcut}" is removed from "{action}" because another ' + 'action already uses this shortcut.'.format(shortcut=shortcuts[1], + action=action.objectName())) shortcuts.remove(shortcuts[1]) # Check the primary shortcut. existing_actions = ActionList.shortcut_map.get(shortcuts[0], []) @@ -283,8 +284,9 @@ class ActionList(object): actions.append(action) ActionList.shortcut_map[shortcuts[0]] = actions else: - log.warning('Shortcut "%s" is removed from "%s" because another action already uses this shortcut.' % - (shortcuts[0], action.objectName())) + log.warning('Shortcut "{shortcut}" is removed from "{action}" '' + because another action already uses this shortcut.'.format(shortcut=shortcuts[0], + action=action.objectName())) shortcuts.remove(shortcuts[0]) action.setShortcuts([QtGui.QKeySequence(shortcut) for shortcut in shortcuts]) diff --git a/openlp/core/common/languagemanager.py b/openlp/core/common/languagemanager.py index 52e9e9f13..58262ffb5 100644 --- a/openlp/core/common/languagemanager.py +++ b/openlp/core/common/languagemanager.py @@ -68,7 +68,7 @@ class LanguageManager(object): """ Find all available language files in this OpenLP install """ - log.debug('Translation files: %s', AppLocation.get_directory(AppLocation.LanguageDir)) + log.debug('Translation files: {files}'.format(files=AppLocation.get_directory(AppLocation.LanguageDir))) trans_dir = QtCore.QDir(AppLocation.get_directory(AppLocation.LanguageDir)) file_names = trans_dir.entryList(['*.qm'], QtCore.QDir.Files, QtCore.QDir.Name) # Remove qm files from the list which start with "qt_". @@ -93,7 +93,7 @@ class LanguageManager(object): """ language = Settings().value('core/language') language = str(language) - log.info('Language file: \'%s\' Loaded from conf file' % language) + log.info("Language file: '{language}' Loaded from conf file".format(language=language)) if re.match(r'[[].*[]]', language): LanguageManager.auto_language = True language = re.sub(r'[\[\]]', '', language) @@ -117,9 +117,9 @@ class LanguageManager(object): qm_list = LanguageManager.get_qm_list() language = str(qm_list[action_name]) if LanguageManager.auto_language: - language = '[%s]' % language + language = '[{language}]'.format(language=language) Settings().setValue('core/language', language) - log.info('Language file: \'%s\' written to conf file' % language) + log.info("Language file: '{language}' written to conf file".format(language=language)) if message: QtWidgets.QMessageBox.information(None, translate('OpenLP.LanguageManager', 'Language'), @@ -136,7 +136,8 @@ class LanguageManager(object): for counter, qmf in enumerate(qm_files): reg_ex = QtCore.QRegExp("^.*i18n/(.*).qm") if reg_ex.exactMatch(qmf): - name = '%s' % reg_ex.cap(1) + name = '{regex}'.format(regex=reg_ex.cap(1)) + # TODO: Test before converting to python3 string format LanguageManager.__qm_list__['%#2i %s' % (counter + 1, LanguageManager.language_name(qmf))] = name @staticmethod diff --git a/openlp/core/common/openlpmixin.py b/openlp/core/common/openlpmixin.py index d17f36bd6..94505b86b 100644 --- a/openlp/core/common/openlpmixin.py +++ b/openlp/core/common/openlpmixin.py @@ -49,12 +49,13 @@ class OpenLPMixin(object): Code to added debug wrapper to work on called functions within a decorated class. """ def wrapped(*args, **kwargs): - parent.logger.debug("Entering %s" % func.__name__) + parent.logger.debug("Entering {function}".format(function=func.__name__)) try: return func(*args, **kwargs) except Exception as e: if parent.logger.getEffectiveLevel() <= logging.ERROR: - parent.logger.error('Exception in %s : %s' % (func.__name__, e)) + parent.logger.error('Exception in {function} : {error}'.format(function=func.__name__, + error=e)) raise e return wrapped diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index adf495a36..b904d627c 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -71,8 +71,8 @@ class Registry(object): else: if not self.initialising: trace_error_handler(log) - log.error('Service %s not found in list' % key) - raise KeyError('Service %s not found in list' % key) + log.error('Service {key} not found in list'.format(key=key)) + raise KeyError('Service {key} not found in list'.format(key=key)) def register(self, key, reference): """ @@ -83,8 +83,8 @@ class Registry(object): """ if key in self.service_list: trace_error_handler(log) - log.error('Duplicate service exception %s' % key) - raise KeyError('Duplicate service exception %s' % key) + log.error('Duplicate service exception {key}'.format(key=key)) + raise KeyError('Duplicate service exception {key}'.format(key=key)) else: self.service_list[key] = reference @@ -140,8 +140,8 @@ class Registry(object): except TypeError: # Who has called me can help in debugging trace_error_handler(log) - log.exception('Exception for function %s', function) + log.exception('Exception for function {function}'.format(function=function)) else: trace_error_handler(log) - log.error("Event %s called but not registered" % event) + log.error("Event {event} called but not registered".format(event=event)) return results diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 84fc6db96..07449ff64 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -487,16 +487,16 @@ class Settings(QtCore.QSettings): # Do NOT do this anywhere else! settings = QtCore.QSettings(self.fileName(), Settings.IniFormat) settings.beginGroup(plugin.settings_section) - if settings.contains('%s count' % plugin.name): + if settings.contains('{name} count'.format(name=plugin.name)): # Get the count. - list_count = int(settings.value('%s count' % plugin.name, 0)) + list_count = int(settings.value('{name} count'.format(name=plugin.name), 0)) if list_count: for counter in range(list_count): # The keys were named e. g.: "image 0" - item = settings.value('%s %d' % (plugin.name, counter), '') + item = settings.value('{name} {counter:d}'.format(name=plugin.name, counter=counter), '') if item: files_list.append(item) - settings.remove('%s %d' % (plugin.name, counter)) - settings.remove('%s count' % plugin.name) + settings.remove('{name} {counter:d}'.format=(plugin.name, counter)) + settings.remove('{name} count'.format(name=plugin.name)) settings.endGroup() return files_list diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py index c7e29415d..91db10fcf 100644 --- a/openlp/core/common/uistrings.py +++ b/openlp/core/common/uistrings.py @@ -80,6 +80,7 @@ class UiStrings(object): self.Export = translate('OpenLP.Ui', 'Export') self.File = translate('OpenLP.Ui', 'File') self.FileNotFound = translate('OpenLP.Ui', 'File Not Found') + # TODO: Check before converting to python3 string self.FileNotFoundMessage = translate('OpenLP.Ui', 'File %s not found.\nPlease try selecting it individually.') self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit') self.Help = translate('OpenLP.Ui', 'Help') @@ -110,8 +111,8 @@ class UiStrings(object): self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular') self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural') self.OLP = translate('OpenLP.Ui', 'OpenLP') - self.OLPV2 = "%s %s" % (self.OLP, "2") - self.OLPV2x = "%s %s" % (self.OLP, "2.4") + self.OLPV2 = "{name} {version}".format(name=self.OLP, version="2") + self.OLPV2x = "{name} {version}".format(name=self.OLP, version="2.4") self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?') self.OpenService = translate('OpenLP.Ui', 'Open service.') self.PlaySlidesInLoop = translate('OpenLP.Ui', 'Play Slides in Loop') @@ -139,6 +140,7 @@ class UiStrings(object): self.Split = translate('OpenLP.Ui', 'Optional &Split') self.SplitToolTip = translate('OpenLP.Ui', 'Split a slide into two only if it does not fit on the screen as one slide.') + # TODO: Check before converting to python3 string self.StartTimeCode = translate('OpenLP.Ui', 'Start %s') self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop') self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End') diff --git a/openlp/core/common/versionchecker.py b/openlp/core/common/versionchecker.py index 136405607..fb706968b 100644 --- a/openlp/core/common/versionchecker.py +++ b/openlp/core/common/versionchecker.py @@ -44,9 +44,10 @@ class VersionThread(QtCore.QThread): log.debug('Version thread - run') app_version = get_application_version() version = check_latest_version(app_version) - log.debug("Versions %s and %s " % (LooseVersion(str(version)), LooseVersion(str(app_version['full'])))) + log.debug("Versions {version1} and {version2} ".format(version1=LooseVersion(str(version)), + version2=LooseVersion(str(app_version['full'])))) if LooseVersion(str(version)) > LooseVersion(str(app_version['full'])): - self.main_window.openlp_version_check.emit('%s' % version) + self.main_window.openlp_version_check.emit('{version}'.format(version=version)) def get_application_version(): @@ -91,7 +92,7 @@ def get_application_version(): if tree_revision == tag_revision: full_version = tag_version.strip() else: - full_version = '%s-bzr%s' % (tag_version.strip(), tree_revision.strip()) + full_version = '{tag}-bzr{tree}'.format(tag=tag_version.strip(), tree=tree_revision.strip()) else: # We're not running the development version, let's use the file. file_path = AppLocation.get_directory(AppLocation.VersionDir) @@ -113,9 +114,10 @@ def get_application_version(): 'build': bits[1] if len(bits) > 1 else None } if APPLICATION_VERSION['build']: - log.info('Openlp version %s build %s', APPLICATION_VERSION['version'], APPLICATION_VERSION['build']) + log.info('Openlp version {version} build {build}'.format(version=APPLICATION_VERSION['version'], + build=APPLICATION_VERSION['build'])) else: - log.info('Openlp version %s' % APPLICATION_VERSION['version']) + log.info('Openlp version {version}'.format(version=APPLICATION_VERSION['version'])) return APPLICATION_VERSION @@ -149,8 +151,9 @@ def check_latest_version(current_version): req = urllib.request.Request('http://www.openlp.org/files/dev_version.txt') else: req = urllib.request.Request('http://www.openlp.org/files/version.txt') - req.add_header('User-Agent', 'OpenLP/%s %s/%s; ' % (current_version['full'], platform.system(), - platform.release())) + req.add_header('User-Agent', 'OpenLP/{version} {system}/{release}; '.format(version=current_version['full'], + system=platform.system(), + release=platform.release())) remote_version = None retries = 0 while True: diff --git a/tests/functional/openlp_core_lib/test_projectordb.py b/tests/functional/openlp_core_lib/test_projectordb.py index f6d52e476..158cb5fb6 100644 --- a/tests/functional/openlp_core_lib/test_projectordb.py +++ b/tests/functional/openlp_core_lib/test_projectordb.py @@ -206,3 +206,17 @@ class TestProjectorDB(TestCase): # THEN: __repr__ should return a proper string self.assertEqual(str(manufacturer), '', 'Manufacturer.__repr__() should have returned a proper representation string') + + def model_repr_test(self): + """ + Test model class __repr__ text + """ + # GIVEN: Test object + model = Model() + + # WHEN: Name is set + model.name = 'OpenLP Test' + + # THEN: __repr__ should return a proper string + self.assertEqual(str(model), '', + 'Model.__repr__() should have returned a proper representation string') From 07468f0093d587c4b31f7e7e7419a460be8d1c8f Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sun, 15 May 2016 10:32:04 -0700 Subject: [PATCH 34/43] Fix oops --- openlp/core/common/actions.py | 6 +++--- openlp/core/common/settings.py | 2 +- tests/functional/openlp_core_lib/test_projectordb.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/common/actions.py b/openlp/core/common/actions.py index 7f8c90f86..d22ef8fd1 100644 --- a/openlp/core/common/actions.py +++ b/openlp/core/common/actions.py @@ -284,9 +284,9 @@ class ActionList(object): actions.append(action) ActionList.shortcut_map[shortcuts[0]] = actions else: - log.warning('Shortcut "{shortcut}" is removed from "{action}" '' - because another action already uses this shortcut.'.format(shortcut=shortcuts[0], - action=action.objectName())) + log.warning('Shortcut "{shortcut}" is removed from "{action}" ' + 'because another action already uses this shortcut.'.format(shortcut=shortcuts[0], + action=action.objectName())) shortcuts.remove(shortcuts[0]) action.setShortcuts([QtGui.QKeySequence(shortcut) for shortcut in shortcuts]) diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 07449ff64..7bbd4349d 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -496,7 +496,7 @@ class Settings(QtCore.QSettings): item = settings.value('{name} {counter:d}'.format(name=plugin.name, counter=counter), '') if item: files_list.append(item) - settings.remove('{name} {counter:d}'.format=(plugin.name, counter)) + settings.remove('{name} {counter:d}'.format(name=plugin.name, counter=counter)) settings.remove('{name} count'.format(name=plugin.name)) settings.endGroup() return files_list diff --git a/tests/functional/openlp_core_lib/test_projectordb.py b/tests/functional/openlp_core_lib/test_projectordb.py index 158cb5fb6..c1b9ce3d1 100644 --- a/tests/functional/openlp_core_lib/test_projectordb.py +++ b/tests/functional/openlp_core_lib/test_projectordb.py @@ -218,5 +218,5 @@ class TestProjectorDB(TestCase): model.name = 'OpenLP Test' # THEN: __repr__ should return a proper string - self.assertEqual(str(model), '', + self.assertEqual(str(model), '', 'Model.__repr__() should have returned a proper representation string') From 7c4671b67665634ed3b5b47356d7706ba3182d65 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sun, 15 May 2016 10:33:42 -0700 Subject: [PATCH 35/43] Convert strings to python3 in core.common.lib --- openlp/core/lib/db.py | 25 ++--- openlp/core/lib/filedialog.py | 3 +- openlp/core/lib/htmlbuilder.py | 94 +++++++++++-------- openlp/core/lib/imagemanager.py | 6 +- openlp/core/lib/mediamanageritem.py | 32 ++++--- openlp/core/lib/plugin.py | 10 +- openlp/core/lib/pluginmanager.py | 25 ++--- openlp/core/lib/renderer.py | 12 ++- openlp/core/lib/screen.py | 16 ++-- openlp/core/lib/searchedit.py | 5 +- openlp/core/lib/serviceitem.py | 21 +++-- openlp/core/lib/theme.py | 3 +- openlp/core/lib/ui.py | 6 +- openlp/core/lib/webpagereader.py | 20 ++-- .../openlp_core_lib/test_projector_pjlink1.py | 76 ++++++++++++++- 15 files changed, 230 insertions(+), 124 deletions(-) diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 7ae3cdc6f..812758a4b 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -82,10 +82,10 @@ def handle_db_error(plugin_name, db_file_name): :return: None """ db_path = get_db_path(plugin_name, db_file_name) - log.exception('Error loading database: %s', db_path) + log.exception('Error loading database: {db}'.format(db=db_path)) critical_error_message_box(translate('OpenLP.Manager', 'Database Error'), - translate('OpenLP.Manager', 'OpenLP cannot load your database.\n\nDatabase: %s') - % db_path) + translate('OpenLP.Manager', + 'OpenLP cannot load your database.\n\nDatabase: {db}').format(db=db_path)) def init_url(plugin_name, db_file_name=None): @@ -157,10 +157,10 @@ def upgrade_db(url, upgrade): return version, upgrade.__version__ version += 1 try: - while hasattr(upgrade, 'upgrade_%d' % version): - log.debug('Running upgrade_%d', version) + while hasattr(upgrade, 'upgrade_{version:d}'.format(version=version)): + log.debug('Running upgrade_{version:d}'.format(version=version)) try: - upgrade_func = getattr(upgrade, 'upgrade_%d' % version) + upgrade_func = getattr(upgrade, 'upgrade_{version:d}'.format(version=version)) upgrade_func(session, metadata) session.commit() # Update the version number AFTER a commit so that we are sure the previous transaction happened @@ -168,8 +168,8 @@ def upgrade_db(url, upgrade): session.commit() version += 1 except (SQLAlchemyError, DBAPIError): - log.exception('Could not run database upgrade script "upgrade_%s", upgrade process has been halted.', - version) + log.exception('Could not run database upgrade script ' + '"upgrade_{version:d}", upgrade process has been halted.'.format(version=version)) break except (SQLAlchemyError, DBAPIError): version_meta = Metadata.populate(key='version', value=int(upgrade.__version__)) @@ -242,9 +242,10 @@ class Manager(object): critical_error_message_box( translate('OpenLP.Manager', 'Database Error'), translate('OpenLP.Manager', 'The database being loaded was created in a more recent version of ' - 'OpenLP. The database is version %d, while OpenLP expects version %d. The database will ' - 'not be loaded.\n\nDatabase: %s') % (db_ver, up_ver, self.db_url) - ) + 'OpenLP. The database is version {db_ver}, while OpenLP expects version {db_up}. ' + 'The database will not be loaded.\n\nDatabase: {db_name}').format(db_ver=db_ver, + db_up=up_ver, + db_name=self.db_url)) return if not session: try: @@ -460,7 +461,7 @@ class Manager(object): raise except InvalidRequestError: self.session.rollback() - log.exception('Failed to delete %s records', object_class.__name__) + log.exception('Failed to delete {name} records'.format(name=object_class.__name__)) return False except: self.session.rollback() diff --git a/openlp/core/lib/filedialog.py b/openlp/core/lib/filedialog.py index 823567a37..249f7959d 100644 --- a/openlp/core/lib/filedialog.py +++ b/openlp/core/lib/filedialog.py @@ -50,7 +50,8 @@ class FileDialog(QtWidgets.QFileDialog): log.info('File not found. Attempting to unquote.') file = parse.unquote(file) if not os.path.exists(file): - log.error('File %s not found.' % file) + log.error('File {text} not found.'.format(text=file)) + # TODO: Test with UiStrings() before converting to python3 strings QtWidgets.QMessageBox.information(parent, UiStrings().FileNotFound, UiStrings().FileNotFoundMessage % file) continue diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 812680027..265aabe2c 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -396,6 +396,7 @@ from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, Vertic log = logging.getLogger(__name__) +# TODO: Verify where this is used before converting to python3 HTMLSRC = """ @@ -564,13 +565,13 @@ def build_html(item, screen, is_live, background, image=None, plugins=None): theme_data = item.theme_data # Image generated and poked in if background: - bgimage_src = 'src="data:image/png;base64,%s"' % background + bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=background) elif item.bg_image_bytes: - bgimage_src = 'src="data:image/png;base64,%s"' % item.bg_image_bytes + bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=item.bg_image_bytes) else: bgimage_src = 'style="display:none;"' if image: - image_src = 'src="data:image/png;base64,%s"' % image + image_src = 'src="data:image/png;base64,{image}"'.format(image=image) else: image_src = 'style="display:none;"' css_additions = '' @@ -601,7 +602,7 @@ def webkit_version(): """ try: webkit_ver = float(QtWebKit.qWebKitVersion()) - log.debug('Webkit version = %s' % webkit_ver) + log.debug('Webkit version = {version}'.format(version=webkit_ver)) except AttributeError: webkit_ver = 0 return webkit_ver @@ -621,23 +622,25 @@ def build_background_css(item, width): if theme.background_type == BackgroundType.to_string(BackgroundType.Transparent): background = '' elif theme.background_type == BackgroundType.to_string(BackgroundType.Solid): - background = 'background-color: %s' % theme.background_color + background = 'background-color: {theme}'.format(theme=theme.background_color) else: if theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal): - background = 'background: -webkit-gradient(linear, left top, left bottom, from(%s), to(%s)) fixed' \ - % (theme.background_start_color, theme.background_end_color) + background = 'background: -webkit-gradient(linear, left top, left bottom, from({start}), to({end})) ' \ + 'fixed'.format(start=theme.background_start_color, end=theme.background_end_color) elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftTop): - background = 'background: -webkit-gradient(linear, left top, right bottom, from(%s), to(%s)) fixed' \ - % (theme.background_start_color, theme.background_end_color) + background = 'background: -webkit-gradient(linear, left top, right bottom, from({start}), to({end})) ' \ + 'fixed'.format(start=theme.background_start_color, end=theme.background_end_color) elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftBottom): - background = 'background: -webkit-gradient(linear, left bottom, right top, from(%s), to(%s)) fixed' \ - % (theme.background_start_color, theme.background_end_color) + background = 'background: -webkit-gradient(linear, left bottom, right top, from({start}), to({end})) ' \ + 'fixed'.format(start=theme.background_start_color, end=theme.background_end_color) elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Vertical): - background = 'background: -webkit-gradient(linear, left top, right top, from(%s), to(%s)) fixed' % \ - (theme.background_start_color, theme.background_end_color) + background = 'background: -webkit-gradient(linear, left top, right top, from({start}), to({end})) ' \ + 'fixed'.format(start=theme.background_start_color, end=theme.background_end_color) else: - background = 'background: -webkit-gradient(radial, %s 50%%, 100, %s 50%%, %s, from(%s), to(%s)) fixed'\ - % (width, width, width, theme.background_start_color, theme.background_end_color) + background = 'background: -webkit-gradient(radial, {width1} 50%%, 100, {width2} 50%%, {width3}, ' \ + 'from({start}), to({end})) fixed'.format(width1=width, width2=width, width3=width, + start=theme.background_start_color, + end=theme.background_end_color) return background @@ -652,16 +655,16 @@ def build_lyrics_css(item): z-index: 5; position: absolute; display: table; - %s + {stable} } .lyricscell { display: table-cell; word-wrap: break-word; -webkit-transition: opacity 0.4s ease; - %s + {lyrics} } .lyricsmain { - %s + {main} } """ theme_data = item.theme_data @@ -669,13 +672,15 @@ def build_lyrics_css(item): lyrics = '' lyricsmain = '' if theme_data and item.main: - lyricstable = 'left: %spx; top: %spx;' % (item.main.x(), item.main.y()) + lyricstable = 'left: {left}px; top: {top}px;'.format(left=item.main.x(), top=item.main.y()) lyrics = build_lyrics_format_css(theme_data, item.main.width(), item.main.height()) lyricsmain += build_lyrics_outline_css(theme_data) if theme_data.font_main_shadow: - lyricsmain += ' text-shadow: %s %spx %spx;' % \ - (theme_data.font_main_shadow_color, theme_data.font_main_shadow_size, theme_data.font_main_shadow_size) - lyrics_css = style % (lyricstable, lyrics, lyricsmain) + lyricsmain += ' text-shadow: {theme} {shadow1}px ' \ + '{shadow2}px;'.format(theme=theme_data.font_main_shadow_color, + shadow1=theme_data.font_main_shadow_size, + shadow2=theme_data.font_main_shadow_size) + lyrics_css = style.format(stable=lyricstable, lyrics=lyrics, main=lyricsmain) return lyrics_css @@ -689,7 +694,9 @@ def build_lyrics_outline_css(theme_data): size = float(theme_data.font_main_outline_size) / 16 fill_color = theme_data.font_main_color outline_color = theme_data.font_main_outline_color - return ' -webkit-text-stroke: %sem %s; -webkit-text-fill-color: %s; ' % (size, outline_color, fill_color) + return ' -webkit-text-stroke: {size}em {color}; -webkit-text-fill-color: {fill}; '.format(size=size, + color=outline_color, + fill=fill_color) return '' @@ -715,13 +722,21 @@ def build_lyrics_format_css(theme_data, width, height): padding_bottom = '0.5em' else: padding_bottom = '0' - lyrics = '%s word-wrap: break-word; ' \ - 'text-align: %s; vertical-align: %s; font-family: %s; ' \ - 'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \ - 'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx; height: %spx; ' % \ - (justify, align, valign, theme_data.font_main_name, theme_data.font_main_size, - theme_data.font_main_color, 100 + int(theme_data.font_main_line_adjustment), padding_bottom, - left_margin, width, height) + lyrics = '{justify} word-wrap: break-word; ' \ + 'text-align: {align}; vertical-align: {valign}; font-family: {font); ' \ + 'font-size: {size}pt; color: {color}; line-height: {line:d}%%; margin: 0;' \ + 'padding: 0; padding-bottom: {bottom}; padding-left: {left}px; width: {width}px; ' \ + 'height: {height}px; '.format(justify=justify, + align=align, + valign=valign, + font=theme_data.font_main_name, + size=theme_data.font_main_size, + color=theme_data.font_main_color, + line=100 + int(theme_data.font_main_line_adjustment), + bottom=padding_bottom, + left=left_margin, + width=width, + height=height) if theme_data.font_main_italics: lyrics += 'font-style:italic; ' if theme_data.font_main_bold: @@ -737,20 +752,21 @@ def build_footer_css(item, height): :param height: """ style = """ - left: %spx; - bottom: %spx; - width: %spx; - font-family: %s; - font-size: %spt; - color: %s; + left: {left}px; + bottom: {bottom}px; + width: {width}px; + font-family: {family}; + font-size: {size}pt; + color: {color}; text-align: left; - white-space: %s; + white-space: {space}; """ theme = item.theme_data if not theme or not item.footer: return '' bottom = height - int(item.footer.y()) - int(item.footer.height()) whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap' - lyrics_html = style % (item.footer.x(), bottom, item.footer.width(), - theme.font_footer_name, theme.font_footer_size, theme.font_footer_color, whitespace) + lyrics_html = style.format(left=item.footer.x(), bottom=bottom, width=item.footer.width(), + family=theme.font_footer_name, size=theme.font_footer_size, + color=theme.font_footer_color, space=whitespace) return lyrics_html diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index 0d0903a3b..1c25fca25 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -236,7 +236,7 @@ class ImageManager(QtCore.QObject): """ Return the ``QImage`` from the cache. If not present wait for the background thread to process it. """ - log.debug('getImage %s' % path) + log.debug('getImage {path}'.format(path=path)) image = self._cache[(path, source, width, height)] if image.image is None: self._conversion_queue.modify_priority(image, Priority.High) @@ -256,7 +256,7 @@ class ImageManager(QtCore.QObject): """ Returns the byte string for an image. If not present wait for the background thread to process it. """ - log.debug('get_image_bytes %s' % path) + log.debug('get_image_bytes {path}'.format(path=path)) image = self._cache[(path, source, width, height)] if image.image_bytes is None: self._conversion_queue.modify_priority(image, Priority.Urgent) @@ -271,7 +271,7 @@ class ImageManager(QtCore.QObject): """ Add image to cache if it is not already there. """ - log.debug('add_image %s' % path) + log.debug('add_image {path}'.format(path=path)) if not (path, source, width, height) in self._cache: image = Image(path, source, background, width, height) self._cache[(path, source, width, height)] = image diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 5af90c1b7..6e8327365 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -186,7 +186,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): for action in toolbar_actions: if action[0] == StringContent.Preview: self.toolbar.addSeparator() - self.toolbar.add_toolbar_action('%s%sAction' % (self.plugin.name, action[0]), + self.toolbar.add_toolbar_action('{name}{action}Action'.format(name=self.plugin.name, action=action[0]), text=self.plugin.get_string(action[1])['title'], icon=action[2], tooltip=self.plugin.get_string(action[1])['tooltip'], triggers=action[3]) @@ -200,7 +200,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): self.list_view.setSpacing(1) self.list_view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.list_view.setAlternatingRowColors(True) - self.list_view.setObjectName('%sListView' % self.plugin.name) + self.list_view.setObjectName('{name}ListView'.format(name=self.plugin.name)) # Add to page_layout self.page_layout.addWidget(self.list_view) # define and add the context menu @@ -212,19 +212,22 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): triggers=self.on_edit_click) create_widget_action(self.list_view, separator=True) create_widget_action(self.list_view, - 'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()), + 'listView{plugin}{preview}Item'.format(plugin=self.plugin.name.title(), + preview=StringContent.Preview.title()), text=self.plugin.get_string(StringContent.Preview)['title'], icon=':/general/general_preview.png', can_shortcuts=True, triggers=self.on_preview_click) create_widget_action(self.list_view, - 'listView%s%sItem' % (self.plugin.name.title(), StringContent.Live.title()), + 'listView{plugin}{live}Item'.format(plugin=self.plugin.name.title(), + live=StringContent.Live.title()), text=self.plugin.get_string(StringContent.Live)['title'], icon=':/general/general_live.png', can_shortcuts=True, triggers=self.on_live_click) create_widget_action(self.list_view, - 'listView%s%sItem' % (self.plugin.name.title(), StringContent.Service.title()), + 'listView{plugin}{service}Item'.format(plugin=self.plugin.name.title(), + service=StringContent.Service.title()), can_shortcuts=True, text=self.plugin.get_string(StringContent.Service)['title'], icon=':/general/general_add.png', @@ -232,7 +235,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): if self.has_delete_icon: create_widget_action(self.list_view, separator=True) create_widget_action(self.list_view, - 'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()), + 'listView{plugin}{delete}Item'.format(plugin=self.plugin.name.title(), + delete=StringContent.Delete.title()), text=self.plugin.get_string(StringContent.Delete)['title'], icon=':/general/general_delete.png', can_shortcuts=True, triggers=self.on_delete_click) @@ -313,7 +317,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): files = FileDialog.getOpenFileNames(self, self.on_new_prompt, Settings().value(self.settings_section + '/last directory'), self.on_new_file_masks) - log.info('New files(s) %s' % files) + log.info('New files(s) {files}'.format(files=files)) if files: self.application.set_busy_cursor() self.validate_and_load(files) @@ -333,7 +337,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): if not error_shown: critical_error_message_box(translate('OpenLP.MediaManagerItem', 'Invalid File Type'), translate('OpenLP.MediaManagerItem', - 'Invalid File %s.\nSuffix not supported') % file_name) + 'Invalid File {name}.\n' + 'Suffix not supported').format(name=file_name)) error_shown = True else: new_files.append(file_name) @@ -375,7 +380,9 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): self.load_list(full_list, target_group) last_dir = os.path.split(files[0])[0] Settings().setValue(self.settings_section + '/last directory', last_dir) - Settings().setValue('%s/%s files' % (self.settings_section, self.settings_section), self.get_file_list()) + Settings().setValue('{section1}/{section2} files'.format(section1=self.settings_section, + section2=self.settings_section), + self.get_file_list()) if duplicates_found: critical_error_message_box(UiStrings().Duplicate, translate('OpenLP.MediaManagerItem', @@ -550,7 +557,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): # Is it possible to process multiple list items to generate # multiple service items? if self.single_service_item: - log.debug('%s Add requested', self.plugin.name) + log.debug('{plugin} Add requested'.format(plugin=self.plugin.name)) self.add_to_service(replace=self.remote_triggered) else: items = self.list_view.selectedIndexes() @@ -591,7 +598,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): translate('OpenLP.MediaManagerItem', 'You must select one or more items.')) else: - log.debug('%s Add requested', self.plugin.name) + log.debug('{plugin} Add requested'.format(plugin=self.plugin.name)) service_item = self.service_manager.get_service_item() if not service_item: QtWidgets.QMessageBox.information(self, UiStrings().NISs, @@ -604,7 +611,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties): # Turn off the remote edit update message indicator QtWidgets.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'Invalid Service Item'), translate('OpenLP.MediaManagerItem', - 'You must select a %s service item.') % self.title) + 'You must select a {title} ' + 'service item.').format(title=self.title)) def build_service_item(self, item=None, xml_version=False, remote=False, context=ServiceItemContext.Live): """ diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index b9c8ca5f0..493cc2f45 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -130,7 +130,7 @@ class Plugin(QtCore.QObject, RegistryProperties): :param settings_tab_class: The class name of the plugin's settings tab. :param version: Defaults to *None*, which means that the same version number is used as OpenLP's version number. """ - log.debug('Plugin %s initialised' % name) + log.debug('Plugin {plugin} initialised'.format(plugin=name)) super(Plugin, self).__init__() self.name = name self.text_strings = {} @@ -154,11 +154,11 @@ class Plugin(QtCore.QObject, RegistryProperties): # Append a setting for files in the mediamanager (note not all plugins # which have a mediamanager need this). if media_item_class is not None: - default_settings['%s/%s files' % (name, name)] = [] + default_settings['{name1}/{name2} files'.format(name1=name, name2=name)] = [] # Add settings to the dict of all settings. Settings.extend_default_settings(default_settings) - Registry().register_function('%s_add_service_item' % self.name, self.process_add_service_event) - Registry().register_function('%s_config_updated' % self.name, self.config_update) + Registry().register_function('{name}_add_service_item'.format(name=self.name), self.process_add_service_event) + Registry().register_function('{name}_config_updated'.format(name=self.name), self.config_update) def check_pre_conditions(self): """ @@ -256,7 +256,7 @@ class Plugin(QtCore.QObject, RegistryProperties): """ Generic Drag and drop handler triggered from service_manager. """ - log.debug('process_add_service_event event called for plugin %s' % self.name) + log.debug('process_add_service_event event called for plugin {name}'.format(name=self.name)) if replace: self.media_item.on_add_edit_click() else: diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 598c7c5d1..455d0deed 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -43,7 +43,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties): super(PluginManager, self).__init__(parent) self.log_info('Plugin manager Initialising') self.base_path = os.path.abspath(AppLocation.get_directory(AppLocation.PluginsDir)) - self.log_debug('Base path %s ' % self.base_path) + self.log_debug('Base path {path}'.forma(path=self.base_path)) self.plugins = [] self.log_info('Plugin manager Initialised') @@ -73,7 +73,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties): """ start_depth = len(os.path.abspath(self.base_path).split(os.sep)) present_plugin_dir = os.path.join(self.base_path, 'presentations') - self.log_debug('finding plugins in %s at depth %d' % (self.base_path, start_depth)) + self.log_debug('finding plugins in {path} at depth {depth:d}'.format(path=self.base_path, depth=start_depth)) for root, dirs, files in os.walk(self.base_path): for name in files: if name.endswith('.py') and not name.startswith('__'): @@ -84,7 +84,9 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties): break module_name = name[:-3] # import the modules - self.log_debug('Importing %s from %s. Depth %d' % (module_name, root, this_depth)) + self.log_debug('Importing {name} from {root}. Depth {depth:d}'.format(name=module_name, + root=root, + depth=this_depth)) try: # Use the "imp" library to try to get around a problem with the PyUNO library which # monkey-patches the __import__ function to do some magic. This causes issues with our tests. @@ -93,21 +95,21 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties): # Then load the module (do the actual import) using the details from find_module() imp.load_module(module_name, fp, path_name, description) except ImportError as e: - self.log_exception('Failed to import module %s on path %s: %s' - % (module_name, path, e.args[0])) + self.log_exception('Failed to import module {name} on path {path}: ' + '{args}'.format(name=module_name, path=path, args=e.args[0])) plugin_classes = Plugin.__subclasses__() plugin_objects = [] for p in plugin_classes: try: plugin = p() - self.log_debug('Loaded plugin %s' % str(p)) + self.log_debug('Loaded plugin {plugin}'.format(plugin=str(p))) plugin_objects.append(plugin) except TypeError: - self.log_exception('Failed to load plugin %s' % str(p)) + self.log_exception('Failed to load plugin {plugin}'.format(plugin=str(p))) plugins_list = sorted(plugin_objects, key=lambda plugin: plugin.weight) for plugin in plugins_list: if plugin.check_pre_conditions(): - self.log_debug('Plugin %s active' % str(plugin.name)) + self.log_debug('Plugin {plugin} active'.format(plugin=str(plugin.name))) plugin.set_status() else: plugin.status = PluginStatus.Disabled @@ -175,10 +177,11 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties): Loop through all the plugins and give them an opportunity to initialise themselves. """ for plugin in self.plugins: - self.log_info('initialising plugins %s in a %s state' % (plugin.name, plugin.is_active())) + self.log_info('initialising plugins {plugin} in a {state} state'.format(plugin=plugin.name, + state=plugin.is_active())) if plugin.is_active(): plugin.initialise() - self.log_info('Initialisation Complete for %s ' % plugin.name) + self.log_info('Initialisation Complete for {plugin}'.format(plugin=plugin.name)) def finalise_plugins(self): """ @@ -187,7 +190,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties): for plugin in self.plugins: if plugin.is_active(): plugin.finalise() - self.log_info('Finalisation Complete for %s ' % plugin.name) + self.log_info('Finalisation Complete for {plugin}'.format(plugin=plugin.name)) def get_plugin_by_name(self, name): """ diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index f76d28853..0d233a9c4 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -107,7 +107,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): :param theme_name: The theme name """ - self.log_debug("_set_theme with theme %s" % theme_name) + self.log_debug("_set_theme with theme {theme}".format(theme=theme_name)) if theme_name not in self._theme_dimensions: theme_data = self.theme_manager.get_theme_data(theme_name) main_rect = self.get_main_rectangle(theme_data) @@ -183,7 +183,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): :param item_theme_name: The item theme's name. """ - self.log_debug("set_item_theme with theme %s" % item_theme_name) + self.log_debug("set_item_theme with theme {theme}".format(theme=item_theme_name)) self._set_theme(item_theme_name) self.item_theme_name = item_theme_name @@ -317,7 +317,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): self.width = screen_size.width() self.height = screen_size.height() self.screen_ratio = self.height / self.width - self.log_debug('_calculate default %s, %f' % (screen_size, self.screen_ratio)) + self.log_debug('_calculate default {size}, {ratio:f}'.format(size=screen_size, ratio=self.screen_ratio)) # 90% is start of footer self.footer_start = int(self.height * 0.90) @@ -354,7 +354,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): :param rect_main: The main text block. :param rect_footer: The footer text block. """ - self.log_debug('_set_text_rectangle %s , %s' % (rect_main, rect_footer)) + self.log_debug('_set_text_rectangle {main} , {footer}'.format(main=rect_main, footer=rect_footer)) self._rect = rect_main self._rect_footer = rect_footer self.page_width = self._rect.width() @@ -370,6 +370,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): self.web.resize(self.page_width, self.page_height) self.web_frame = self.web.page().mainFrame() # Adjust width and height to account for shadow. outline done in css. + # TODO: Verify before converting to python3 strings html = """