Modify themes to work with pathlib

This commit is contained in:
Philip Ridout 2017-09-26 17:39:13 +01:00
parent dfe2ae347e
commit 10b13872e5
14 changed files with 326 additions and 333 deletions

View File

@ -4,7 +4,7 @@
"color": "#000000",
"direction": "vertical",
"end_color": "#000000",
"filename": "",
"filename": null,
"start_color": "#000000",
"type": "solid"
},

View File

@ -26,6 +26,7 @@ from string import Template
from PyQt5 import QtGui, QtCore, QtWebKitWidgets
from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, RegistryMixin, Settings
from openlp.core.common.path import path_to_str
from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \
build_lyrics_format_css, build_lyrics_outline_css, build_chords_css
from openlp.core.common import ThemeLevel
@ -118,7 +119,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
theme_data, main_rect, footer_rect = self._theme_dimensions[theme_name]
# if No file do not update cache
if theme_data.background_filename:
self.image_manager.add_image(theme_data.background_filename,
self.image_manager.add_image(path_to_str(theme_data.background_filename),
ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
def pre_render(self, override_theme_data=None):
@ -207,8 +208,8 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
service_item.raw_footer = FOOTER
# if No file do not update cache
if theme_data.background_filename:
self.image_manager.add_image(
theme_data.background_filename, ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
self.image_manager.add_image(path_to_str(theme_data.background_filename),
ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
theme_data, main, footer = self.pre_render(theme_data)
service_item.theme_data = theme_data
service_item.main = main

View File

@ -22,13 +22,13 @@
"""
Provide the theme XML and handling functions for OpenLP v2 themes.
"""
import os
import logging
import json
import logging
from lxml import etree, objectify
from openlp.core.common import AppLocation, de_hump
from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
from openlp.core.common.path import Path, str_to_path
from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string
log = logging.getLogger(__name__)
@ -160,9 +160,8 @@ class Theme(object):
# basic theme object with defaults
json_path = AppLocation.get_directory(AppLocation.AppDir) / 'core' / 'lib' / 'json' / 'theme.json'
jsn = get_text_file_string(json_path)
jsn = json.loads(jsn)
self.expand_json(jsn)
self.background_filename = ''
self.load_theme(jsn)
self.background_filename = None
def expand_json(self, var, prev=None):
"""
@ -174,8 +173,6 @@ class Theme(object):
for key, value in var.items():
if prev:
key = prev + "_" + key
else:
key = key
if isinstance(value, dict):
self.expand_json(value, key)
else:
@ -185,13 +182,13 @@ class Theme(object):
"""
Add the path name to the image name so the background can be rendered.
:param path: The path name to be added.
:param openlp.core.common.path.Path path: The path name to be added.
:rtype: None
"""
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()
self.background_filename = os.path.join(path, self.theme_name, self.background_filename)
self.background_filename = path / self.theme_name / self.background_filename
def set_default_header_footer(self):
"""
@ -206,16 +203,21 @@ class Theme(object):
self.font_footer_y = current_screen['size'].height() * 9 / 10
self.font_footer_height = current_screen['size'].height() / 10
def load_theme(self, theme):
def load_theme(self, theme, theme_path=None):
"""
Convert the JSON file and expand it.
:param theme: the theme string
:param openlp.core.common.path.Path theme_path: The path to the theme
:rtype: None
"""
jsn = json.loads(theme)
if theme_path:
jsn = json.loads(theme, cls=OpenLPJsonDecoder, base_path=theme_path)
else:
jsn = json.loads(theme, cls=OpenLPJsonDecoder)
self.expand_json(jsn)
def export_theme(self):
def export_theme(self, theme_path=None):
"""
Loop through the fields and build a dictionary of them
@ -223,7 +225,9 @@ class Theme(object):
theme_data = {}
for attr, value in self.__dict__.items():
theme_data["{attr}".format(attr=attr)] = value
return json.dumps(theme_data)
if theme_path:
return json.dumps(theme_data, cls=OpenLPJsonEncoder, base_path=theme_path)
return json.dumps(theme_data, cls=OpenLPJsonEncoder)
def parse(self, xml):
"""

View File

@ -346,7 +346,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
if not hasattr(self, 'service_item'):
return False
self.override['image'] = path
self.override['theme'] = self.service_item.theme_data.background_filename
self.override['theme'] = path_to_str(self.service_item.theme_data.background_filename)
self.image(path)
# Update the preview frame.
if self.is_live:
@ -454,7 +454,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
Registry().execute('video_background_replaced')
self.override = {}
# We have a different theme.
elif self.override['theme'] != service_item.theme_data.background_filename:
elif self.override['theme'] != path_to_str(service_item.theme_data.background_filename):
Registry().execute('live_theme_changed')
self.override = {}
else:
@ -466,7 +466,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
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)
path_to_str(self.service_item.theme_data.background_filename), ImageSource.Theme)
if image_path:
image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
created_html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,
@ -488,7 +488,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
path = os.path.join(str(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,
path_to_str(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()

View File

@ -28,7 +28,6 @@ import os
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, UiStrings, translate, get_images_filter, is_not_image_file
from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui import ThemeLayoutForm
@ -61,7 +60,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.setupUi(self)
self.registerFields()
self.update_theme_allowed = True
self.temp_background_filename = ''
self.temp_background_filename = None
self.theme_layout_form = ThemeLayoutForm(self)
self.background_combo_box.currentIndexChanged.connect(self.on_background_combo_box_current_index_changed)
self.gradient_combo_box.currentIndexChanged.connect(self.on_gradient_combo_box_current_index_changed)
@ -188,8 +187,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
"""
background_image = BackgroundType.to_string(BackgroundType.Image)
if self.page(self.currentId()) == self.background_page and \
self.theme.background_type == background_image and \
is_not_image_file(Path(self.theme.background_filename)):
self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename):
QtWidgets.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
translate('OpenLP.ThemeWizard', 'You have not selected a '
'background image. Please select one before continuing.'))
@ -273,7 +271,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
Run the wizard.
"""
log.debug('Editing theme {name}'.format(name=self.theme.theme_name))
self.temp_background_filename = ''
self.temp_background_filename = None
self.update_theme_allowed = False
self.set_defaults()
self.update_theme_allowed = True
@ -318,11 +316,11 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.setField('background_type', 1)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
self.image_color_button.color = self.theme.background_border_color
self.image_path_edit.path = str_to_path(self.theme.background_filename)
self.image_path_edit.path = 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_path_edit.path = str_to_path(self.theme.background_filename)
self.video_path_edit.path = self.theme.background_filename
self.setField('background_type', 4)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
self.setField('background_type', 3)
@ -402,14 +400,14 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
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 is None:
self.temp_background_filename = self.theme.background_filename
self.theme.background_filename = ''
self.theme.background_filename = None
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.temp_background_filename is not None:
self.theme.background_filename = self.temp_background_filename
self.temp_background_filename = ''
self.temp_background_filename = None
self.set_background_page_values()
def on_gradient_combo_box_current_index_changed(self, index):
@ -450,18 +448,24 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
"""
self.theme.background_end_color = color
def on_image_path_edit_path_changed(self, file_path):
def on_image_path_edit_path_changed(self, new_path):
"""
Background Image button pushed.
Handle the `pathEditChanged` signal from image_path_edit
:param openlp.core.common.path.Path new_path: Path to the new image
:rtype: None
"""
self.theme.background_filename = path_to_str(file_path)
self.theme.background_filename = new_path
self.set_background_page_values()
def on_video_path_edit_path_changed(self, file_path):
def on_video_path_edit_path_changed(self, new_path):
"""
Background video button pushed.
Handle the `pathEditChanged` signal from video_path_edit
:param openlp.core.common.path.Path new_path: Path to the new video
:rtype: None
"""
self.theme.background_filename = path_to_str(file_path)
self.theme.background_filename = new_path
self.set_background_page_values()
def on_main_color_changed(self, color):
@ -537,14 +541,14 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
translate('OpenLP.ThemeWizard', 'Theme Name Invalid'),
translate('OpenLP.ThemeWizard', 'Invalid theme name. Please enter one.'))
return
save_from = None
save_to = None
source_path = None
destination_path = None
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
file_name = self.theme.background_filename.name
destination_path = self.path / self.theme.theme_name / file_name
source_path = self.theme.background_filename
if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):
return
self.theme_manager.save_theme(self.theme, save_from, save_to)
self.theme_manager.save_theme(self.theme, source_path, destination_path)
return QtWidgets.QDialog.accept(self)

View File

@ -24,14 +24,14 @@ The Theme Manager manages adding, deleteing and modifying of themes.
"""
import os
import zipfile
import shutil
from xml.etree.ElementTree import ElementTree, XML
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \
UiStrings, check_directory_exists, translate, is_win, get_filesystem_encoding, delete_file
from openlp.core.common.path import Path, path_to_str, str_to_path
UiStrings, check_directory_exists, translate, delete_file
from openlp.core.common.languagemanager import get_locale_key
from openlp.core.common.path import Path, copyfile, path_to_str, rmtree
from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \
check_item_selected, create_thumb, validate_thumb
from openlp.core.lib.theme import Theme, BackgroundType
@ -39,7 +39,6 @@ from openlp.core.lib.ui import critical_error_message_box, create_widget_action
from openlp.core.ui import FileRenameForm, ThemeForm
from openlp.core.ui.lib import OpenLPToolbar
from openlp.core.ui.lib.filedialog import FileDialog
from openlp.core.common.languagemanager import get_locale_key
class Ui_ThemeManager(object):
@ -135,7 +134,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
self.settings_section = 'themes'
# Variables
self.theme_list = []
self.old_background_image = None
self.old_background_image_path = None
def bootstrap_initialise(self):
"""
@ -145,25 +144,41 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
self.global_theme = Settings().value(self.settings_section + '/global theme')
self.build_theme_path()
self.load_first_time_themes()
self.upgrade_themes()
def bootstrap_post_set_up(self):
"""
process the bootstrap post setup request
"""
self.theme_form = ThemeForm(self)
self.theme_form.path = self.path
self.theme_form.path = self.theme_path
self.file_rename_form = FileRenameForm()
Registry().register_function('theme_update_global', self.change_global_from_tab)
self.load_themes()
def upgrade_themes(self):
"""
Upgrade the xml files to json.
:rtype: None
"""
xml_file_paths = AppLocation.get_section_data_path('themes').glob('*/*.xml')
for xml_file_path in xml_file_paths:
theme_data = get_text_file_string(xml_file_path)
theme = self._create_theme_from_xml(theme_data, self.theme_path)
self._write_theme(theme)
xml_file_path.unlink()
def build_theme_path(self):
"""
Set up the theme path variables
:rtype: None
"""
self.path = str(AppLocation.get_section_data_path(self.settings_section))
check_directory_exists(Path(self.path))
self.thumb_path = os.path.join(self.path, 'thumbnails')
check_directory_exists(Path(self.thumb_path))
self.theme_path = AppLocation.get_section_data_path(self.settings_section)
check_directory_exists(self.theme_path)
self.thumb_path = self.theme_path / 'thumbnails'
check_directory_exists(self.thumb_path)
def check_list_state(self, item, field=None):
"""
@ -298,17 +313,18 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
"""
Takes a theme and makes a new copy of it as well as saving it.
:param theme_data: The theme to be used
:param new_theme_name: The new theme name to save the data to
:param Theme theme_data: The theme to be used
:param str new_theme_name: The new theme name of the theme
:rtype: None
"""
save_to = None
save_from = None
destination_path = None
source_path = None
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
destination_path = self.theme_path / new_theme_name / theme_data.background_filename.name
source_path = theme_data.background_filename
theme_data.theme_name = new_theme_name
theme_data.extend_image_filename(self.path)
self.save_theme(theme_data, save_from, save_to)
theme_data.extend_image_filename(self.theme_path)
self.save_theme(theme_data, source_path, destination_path)
self.load_themes()
def on_edit_theme(self, field=None):
@ -322,10 +338,10 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
item = self.theme_list_widget.currentItem()
theme = self.get_theme_data(item.data(QtCore.Qt.UserRole))
if theme.background_type == 'image' or theme.background_type == 'video':
self.old_background_image = theme.background_filename
self.old_background_image_path = theme.background_filename
self.theme_form.theme = theme
self.theme_form.exec(True)
self.old_background_image = None
self.old_background_image_path = None
self.renderer.update_theme(theme.theme_name)
self.load_themes()
@ -355,77 +371,76 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
"""
self.theme_list.remove(theme)
thumb = '{name}.png'.format(name=theme)
delete_file(Path(self.path, thumb))
delete_file(Path(self.thumb_path, thumb))
delete_file(self.theme_path / thumb)
delete_file(self.thumb_path / thumb)
try:
# Windows is always unicode, so no need to encode filenames
if is_win():
shutil.rmtree(os.path.join(self.path, theme))
else:
encoding = get_filesystem_encoding()
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
except OSError as os_error:
shutil.Error = os_error
rmtree(self.theme_path / theme)
except OSError:
self.log_exception('Error deleting theme {name}'.format(name=theme))
def on_export_theme(self, field=None):
def on_export_theme(self, checked=None):
"""
Export the theme in a zip file
:param field:
Export the theme to a zip file
:param bool checked: Sent by the QAction.triggered signal. It's not used in this method.
:rtype: None
"""
item = self.theme_list_widget.currentItem()
if item is None:
critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.'))
return
theme = item.data(QtCore.Qt.UserRole)
theme_name = item.data(QtCore.Qt.UserRole)
export_path, filter_used = \
FileDialog.getSaveFileName(self.main_window,
translate('OpenLP.ThemeManager', 'Save Theme - ({name})').
format(name=theme),
Settings().value(self.settings_section + '/last directory export'),
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
translate('OpenLP.ThemeManager',
'Save Theme - ({name})').format(name=theme_name),
Settings().value(self.settings_section + '/last directory export'),
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'),
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
self.application.set_busy_cursor()
if export_path:
Settings().setValue(self.settings_section + '/last directory export', export_path.parent)
if self._export_theme(str(export_path), theme):
if self._export_theme(export_path.with_suffix('.otz'), theme_name):
QtWidgets.QMessageBox.information(self,
translate('OpenLP.ThemeManager', 'Theme Exported'),
translate('OpenLP.ThemeManager',
'Your theme has been successfully exported.'))
self.application.set_normal_cursor()
def _export_theme(self, theme_path, theme):
def _export_theme(self, theme_path, theme_name):
"""
Create the zipfile with the theme contents.
:param theme_path: Location where the zip file will be placed
:param theme: The name of the theme to be exported
:param openlp.core.common.path.Path theme_path: Location where the zip file will be placed
:param str theme_name: The name of the theme to be exported
:return: The success of creating the zip file
:rtype: bool
"""
theme_zip = None
try:
theme_zip = zipfile.ZipFile(theme_path, 'w')
source = os.path.join(self.path, theme)
for files in os.walk(source):
for name in files[2]:
theme_zip.write(os.path.join(source, name), os.path.join(theme, name))
theme_zip.close()
with zipfile.ZipFile(str(theme_path), 'w') as theme_zip:
source_path = self.theme_path / theme_name
for file_path in source_path.iterdir():
theme_zip.write(str(file_path), os.path.join(theme_name, file_path.name))
return True
except OSError as ose:
self.log_exception('Export Theme Failed')
critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
translate('OpenLP.ThemeManager', 'The theme export failed because this error '
'occurred: {err}').format(err=ose.strerror))
if theme_zip:
theme_zip.close()
shutil.rmtree(theme_path, True)
translate('OpenLP.ThemeManager',
'The theme_name export failed because this error occurred: {err}')
.format(err=ose.strerror))
if theme_path.exists():
rmtree(theme_path, True)
return False
def on_import_theme(self, field=None):
def on_import_theme(self, checked=None):
"""
Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from
those files. This process will only load version 2 themes.
:param field:
:param bool checked: Sent by the QAction.triggered signal. It's not used in this method.
:rtype: None
"""
file_paths, selected_filter = FileDialog.getOpenFileNames(
file_paths, filter_used = FileDialog.getOpenFileNames(
self,
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
Settings().value(self.settings_section + '/last directory import'),
@ -435,8 +450,8 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
return
self.application.set_busy_cursor()
for file_path in file_paths:
self.unzip_theme(path_to_str(file_path), self.path)
Settings().setValue(self.settings_section + '/last directory import', file_path)
self.unzip_theme(file_path, self.theme_path)
Settings().setValue(self.settings_section + '/last directory import', file_path.parent)
self.load_themes()
self.application.set_normal_cursor()
@ -445,17 +460,17 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
Imports any themes on start up and makes sure there is at least one theme
"""
self.application.set_busy_cursor()
files = AppLocation.get_files(self.settings_section, '.otz')
for theme_file in files:
theme_file = os.path.join(self.path, str(theme_file))
self.unzip_theme(theme_file, self.path)
delete_file(Path(theme_file))
files = AppLocation.get_files(self.settings_section, '.png')
theme_paths = AppLocation.get_files(self.settings_section, '.otz')
for theme_path in theme_paths:
theme_path = self.theme_path / theme_path
self.unzip_theme(theme_path, self.theme_path)
delete_file(theme_path)
theme_paths = AppLocation.get_files(self.settings_section, '.png')
# No themes have been found so create one
if not files:
if not theme_paths:
theme = Theme()
theme.theme_name = UiStrings().Default
self._write_theme(theme, None, None)
self._write_theme(theme)
Settings().setValue(self.settings_section + '/global theme', theme.theme_name)
self.application.set_normal_cursor()
@ -471,22 +486,21 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
# Sort the themes by its name considering language specific
files.sort(key=lambda file_name: get_locale_key(str(file_name)))
# now process the file list of png files
for name in files:
name = str(name)
for file in files:
# check to see file is in theme root directory
theme = os.path.join(self.path, name)
if os.path.exists(theme):
text_name = os.path.splitext(name)[0]
theme_path = self.theme_path / file
if theme_path.exists():
text_name = theme_path.stem
if text_name == self.global_theme:
name = translate('OpenLP.ThemeManager', '{name} (default)').format(name=text_name)
else:
name = text_name
thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=text_name))
thumb = self.thumb_path / '{name}.png'.format(name=text_name)
item_name = QtWidgets.QListWidgetItem(name)
if validate_thumb(Path(theme), Path(thumb)):
if validate_thumb(theme_path, thumb):
icon = build_icon(thumb)
else:
icon = create_thumb(theme, thumb)
icon = create_thumb(str(theme_path), str(thumb))
item_name.setIcon(icon)
item_name.setData(QtCore.Qt.UserRole, text_name)
self.theme_list_widget.addItem(item_name)
@ -507,27 +521,19 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
def get_theme_data(self, theme_name):
"""
Returns a theme object from an XML or JSON file
Returns a theme object from a JSON file
:param theme_name: Name of the theme to load from file
:return: The theme object.
:param str theme_name: Name of the theme to load from file
:return: The theme object.
:rtype: Theme
"""
self.log_debug('get theme data for theme {name}'.format(name=theme_name))
theme_file_path = Path(self.path, str(theme_name), '{file_name}.json'.format(file_name=theme_name))
theme_name = str(theme_name)
theme_file_path = self.theme_path / theme_name / '{file_name}.json'.format(file_name=theme_name)
theme_data = get_text_file_string(theme_file_path)
jsn = True
if not theme_data:
theme_file_path = theme_file_path.with_suffix('.xml')
theme_data = get_text_file_string(theme_file_path)
jsn = False
if not theme_data:
self.log_debug('No theme data - using default theme')
return Theme()
else:
if jsn:
return self._create_theme_from_json(theme_data, self.path)
else:
return self._create_theme_from_xml(theme_data, self.path)
return self._create_theme_from_json(theme_data, self.theme_path)
def over_write_message_box(self, theme_name):
"""
@ -543,172 +549,149 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
defaultButton=QtWidgets.QMessageBox.No)
return ret == QtWidgets.QMessageBox.Yes
def unzip_theme(self, file_name, directory):
def unzip_theme(self, file_path, directory_path):
"""
Unzip the theme, remove the preview file if stored. Generate a new preview file. Check the XML theme version
and upgrade if necessary.
:param file_name:
:param directory:
:param openlp.core.common.path.Path file_path:
:param openlp.core.common.path.Path directory_path:
"""
self.log_debug('Unzipping theme {name}'.format(name=file_name))
theme_zip = None
out_file = None
self.log_debug('Unzipping theme {name}'.format(name=file_path))
file_xml = None
abort_import = True
json_theme = False
theme_name = ""
try:
theme_zip = zipfile.ZipFile(file_name)
json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json']
if len(json_file) != 1:
# TODO: remove XML handling at some point but would need a auto conversion to run first.
xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
if len(xml_file) != 1:
self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))
raise ValidationError
xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()
theme_version = xml_tree.get('version', default=None)
if not theme_version or float(theme_version) < 2.0:
self.log_error('Theme version is less than 2.0')
raise ValidationError
theme_name = xml_tree.find('name').text.strip()
else:
new_theme = Theme()
new_theme.load_theme(theme_zip.read(json_file[0]).decode("utf-8"))
theme_name = new_theme.theme_name
json_theme = True
theme_folder = os.path.join(directory, theme_name)
theme_exists = os.path.exists(theme_folder)
if theme_exists and not self.over_write_message_box(theme_name):
abort_import = True
return
else:
abort_import = False
for name in theme_zip.namelist():
out_name = name.replace('/', os.path.sep)
split_name = out_name.split(os.path.sep)
if split_name[-1] == '' or len(split_name) == 1:
# is directory or preview file
continue
full_name = os.path.join(directory, out_name)
check_directory_exists(Path(os.path.dirname(full_name)))
if os.path.splitext(name)[1].lower() == '.xml' or os.path.splitext(name)[1].lower() == '.json':
file_xml = str(theme_zip.read(name), 'utf-8')
out_file = open(full_name, 'w', encoding='utf-8')
out_file.write(file_xml)
with zipfile.ZipFile(str(file_path)) as theme_zip:
json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json']
if len(json_file) != 1:
# TODO: remove XML handling after the 2.6 release.
xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
if len(xml_file) != 1:
self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))
raise ValidationError
xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()
theme_version = xml_tree.get('version', default=None)
if not theme_version or float(theme_version) < 2.0:
self.log_error('Theme version is less than 2.0')
raise ValidationError
theme_name = xml_tree.find('name').text.strip()
else:
out_file = open(full_name, 'wb')
out_file.write(theme_zip.read(name))
out_file.close()
new_theme = Theme()
new_theme.load_theme(theme_zip.read(json_file[0]).decode("utf-8"))
theme_name = new_theme.theme_name
json_theme = True
theme_folder = directory_path / theme_name
if theme_folder.exists() and not self.over_write_message_box(theme_name):
abort_import = True
return
else:
abort_import = False
for zipped_file in theme_zip.namelist():
zipped_file_rel_path = Path(zipped_file)
split_name = zipped_file_rel_path.parts
if split_name[-1] == '' or len(split_name) == 1:
# is directory or preview file
continue
full_name = directory_path / zipped_file_rel_path
check_directory_exists(full_name.parent)
if zipped_file_rel_path.suffix.lower() == '.xml' or zipped_file_rel_path.suffix.lower() == '.json':
file_xml = str(theme_zip.read(zipped_file), 'utf-8')
with full_name.open('w', encoding='utf-8') as out_file:
out_file.write(file_xml)
else:
with full_name.open('wb') as out_file:
out_file.write(theme_zip.read(zipped_file))
except (IOError, zipfile.BadZipfile):
self.log_exception('Importing theme from zip failed {name}'.format(name=file_name))
self.log_exception('Importing theme from zip failed {name}'.format(name=file_path))
raise ValidationError
except ValidationError:
critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
finally:
# Close the files, to be able to continue creating the theme.
if theme_zip:
theme_zip.close()
if out_file:
out_file.close()
if not abort_import:
# As all files are closed, we can create the Theme.
if file_xml:
if json_theme:
theme = self._create_theme_from_json(file_xml, self.path)
theme = self._create_theme_from_json(file_xml, self.theme_path)
else:
theme = self._create_theme_from_xml(file_xml, self.path)
theme = self._create_theme_from_xml(file_xml, self.theme_path)
self.generate_and_save_image(theme_name, theme)
# Only show the error message, when IOError was not raised (in
# this case the error message has already been shown).
elif theme_zip is not None:
critical_error_message_box(
translate('OpenLP.ThemeManager', 'Validation Error'),
translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
self.log_error('Theme file does not contain XML data {name}'.format(name=file_name))
def check_if_theme_exists(self, theme_name):
"""
Check if theme already exists and displays error message
:param theme_name: Name of the Theme to test
:param str theme_name: Name of the Theme to test
:return: True or False if theme exists
:rtype: bool
"""
theme_dir = os.path.join(self.path, theme_name)
if os.path.exists(theme_dir):
if (self.theme_path / theme_name).exists():
critical_error_message_box(
translate('OpenLP.ThemeManager', 'Validation Error'),
translate('OpenLP.ThemeManager', 'A theme with this name already exists.'))
return False
return True
def save_theme(self, theme, image_from, image_to):
def save_theme(self, theme, image_source_path, image_destination_path):
"""
Called by theme maintenance Dialog to save the theme and to trigger the reload of the theme list
:param theme: The theme data object.
:param image_from: Where the theme image is currently located.
:param image_to: Where the Theme Image is to be saved to
:param Theme theme: The theme data object.
:param openlp.core.common.path.Path image_source_path: Where the theme image is currently located.
:param openlp.core.common.path.Path image_destination_path: Where the Theme Image is to be saved to
:rtype: None
"""
self._write_theme(theme, image_from, image_to)
self._write_theme(theme, image_source_path, image_destination_path)
if theme.background_type == BackgroundType.to_string(BackgroundType.Image):
self.image_manager.update_image_border(theme.background_filename,
self.image_manager.update_image_border(path_to_str(theme.background_filename),
ImageSource.Theme,
QtGui.QColor(theme.background_border_color))
self.image_manager.process_updates()
def _write_theme(self, theme, image_from, image_to):
def _write_theme(self, theme, image_source_path=None, image_destination_path=None):
"""
Writes the theme to the disk and handles the background image if necessary
:param theme: The theme data object.
:param image_from: Where the theme image is currently located.
:param image_to: Where the Theme Image is to be saved to
:param Theme theme: The theme data object.
:param openlp.core.common.path.Path image_source_path: Where the theme image is currently located.
:param openlp.core.common.path.Path image_destination_path: Where the Theme Image is to be saved to
:rtype: None
"""
name = theme.theme_name
theme_pretty = theme.export_theme()
theme_dir = os.path.join(self.path, name)
check_directory_exists(Path(theme_dir))
theme_file = os.path.join(theme_dir, name + '.json')
if self.old_background_image and image_to != self.old_background_image:
delete_file(Path(self.old_background_image))
out_file = None
theme_pretty = theme.export_theme(self.theme_path)
theme_dir = self.theme_path / name
check_directory_exists(theme_dir)
theme_path = theme_dir / '{file_name}.json'.format(file_name=name)
try:
out_file = open(theme_file, 'w', encoding='utf-8')
out_file.write(theme_pretty)
theme_path.write_text(theme_pretty)
except IOError:
self.log_exception('Saving theme to file failed')
finally:
if out_file:
out_file.close()
if image_from and os.path.abspath(image_from) != os.path.abspath(image_to):
try:
# Windows is always unicode, so no need to encode filenames
if is_win():
shutil.copyfile(image_from, image_to)
else:
encoding = get_filesystem_encoding()
shutil.copyfile(image_from.encode(encoding), image_to.encode(encoding))
except IOError as xxx_todo_changeme:
shutil.Error = xxx_todo_changeme
self.log_exception('Failed to save theme image')
if image_source_path and image_destination_path:
if self.old_background_image_path and image_destination_path != self.old_background_image_path:
delete_file(self.old_background_image_path)
if image_source_path != image_destination_path:
try:
copyfile(image_source_path, image_destination_path)
except IOError:
self.log_exception('Failed to save theme image')
self.generate_and_save_image(name, theme)
def generate_and_save_image(self, name, theme):
def generate_and_save_image(self, theme_name, theme):
"""
Generate and save a preview image
:param name: The name of the theme.
:param str theme_name: The name of the theme.
:param theme: The theme data object.
"""
frame = self.generate_image(theme)
sample_path_name = os.path.join(self.path, name + '.png')
if os.path.exists(sample_path_name):
os.unlink(sample_path_name)
frame.save(sample_path_name, 'png')
thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=name))
create_thumb(sample_path_name, thumb, False)
sample_path_name = self.theme_path / '{file_name}.png'.format(file_name=theme_name)
if sample_path_name.exists():
sample_path_name.unlink()
frame.save(str(sample_path_name), 'png')
thumb_path = self.thumb_path / '{name}.png'.format(name=theme_name)
create_thumb(str(sample_path_name), str(thumb_path), False)
def update_preview_images(self):
"""
@ -730,39 +713,32 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
"""
return self.renderer.generate_preview(theme_data, force_page)
def get_preview_image(self, theme):
"""
Return an image representing the look of the theme
:param theme: The theme to return the image for.
"""
return os.path.join(self.path, theme + '.png')
@staticmethod
def _create_theme_from_xml(theme_xml, image_path):
"""
Return a theme object using information parsed from XML
:param theme_xml: The Theme data object.
:param image_path: Where the theme image is stored
:param openlp.core.common.path.Path image_path: Where the theme image is stored
:return: Theme data.
:rtype: Theme
"""
theme = Theme()
theme.parse(theme_xml)
theme.extend_image_filename(image_path)
return theme
@staticmethod
def _create_theme_from_json(theme_json, image_path):
def _create_theme_from_json(self, theme_json, image_path):
"""
Return a theme object using information parsed from JSON
:param theme_json: The Theme data object.
:param image_path: Where the theme image is stored
:param openlp.core.common.path.Path image_path: Where the theme image is stored
:return: Theme data.
:rtype: Theme
"""
theme = Theme()
theme.load_theme(theme_json)
theme.load_theme(theme_json, self.theme_path)
theme.extend_image_filename(image_path)
return theme

View File

@ -211,8 +211,8 @@ class ThemesTab(SettingsTab):
"""
Utility method to update the global theme preview image.
"""
image = self.theme_manager.get_preview_image(self.global_theme)
preview = QtGui.QPixmap(str(image))
image_path = self.theme_manager.theme_path / '{file_name}.png'.format(file_name=self.global_theme)
preview = QtGui.QPixmap(str(image_path))
if not preview.isNull():
preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
self.default_list_view.setPixmap(preview)

View File

@ -48,7 +48,6 @@ def upgrade_2(session, metadata):
"""
Version 2 upgrade - Move file path from old db to JSON encoded path to new db. Added during 2.5 dev
"""
# TODO: Update tests
log.debug('Starting upgrade_2 for file_path to JSON')
old_table = Table('image_filenames', metadata, autoload=True)
if 'file_path' not in [col.name for col in old_table.c.values()]:

View File

@ -22,8 +22,9 @@
"""
Package to test the openlp.core.lib.theme package.
"""
from unittest import TestCase
import os
from pathlib import Path
from unittest import TestCase
from openlp.core.lib.theme import Theme
@ -79,16 +80,16 @@ class TestTheme(TestCase):
"""
# GIVEN: A theme object
theme = Theme()
theme.theme_name = 'MyBeautifulTheme '
theme.background_filename = ' video.mp4'
theme.theme_name = 'MyBeautifulTheme'
theme.background_filename = Path('video.mp4')
theme.background_type = 'video'
path = os.path.expanduser('~')
path = Path.home()
# WHEN: Theme.extend_image_filename is run
theme.extend_image_filename(path)
# THEN: The filename of the background should be correct
expected_filename = os.path.join(path, 'MyBeautifulTheme', 'video.mp4')
expected_filename = path / 'MyBeautifulTheme' / 'video.mp4'
self.assertEqual(expected_filename, theme.background_filename)
self.assertEqual('MyBeautifulTheme', theme.theme_name)

View File

@ -22,11 +22,10 @@
"""
Package to test the openlp.core.ui.exeptionform package.
"""
import os
import tempfile
from unittest import TestCase
from unittest.mock import mock_open, patch
from unittest.mock import call, patch
from openlp.core.common import Registry
from openlp.core.common.path import Path
@ -142,15 +141,15 @@ class TestExceptionForm(TestMixin, TestCase):
test_form = exceptionform.ExceptionForm()
test_form.file_attachment = None
with patch.object(test_form, '_pyuno_import') as mock_pyuno:
with patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback:
with patch.object(test_form.description_text_edit, 'toPlainText') as mock_description:
mock_pyuno.return_value = 'UNO Bridge Test'
mock_traceback.return_value = 'openlp: Traceback Test'
mock_description.return_value = 'Description Test'
with patch.object(test_form, '_pyuno_import') as mock_pyuno, \
patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback, \
patch.object(test_form.description_text_edit, 'toPlainText') as mock_description:
mock_pyuno.return_value = 'UNO Bridge Test'
mock_traceback.return_value = 'openlp: Traceback Test'
mock_description.return_value = 'Description Test'
# WHEN: on_save_report_button_clicked called
test_form.on_send_report_button_clicked()
# WHEN: on_save_report_button_clicked called
test_form.on_send_report_button_clicked()
# THEN: Verify strings were formatted properly
mocked_add_query_item.assert_called_with('body', MAIL_ITEM_TEXT)
@ -182,25 +181,24 @@ class TestExceptionForm(TestMixin, TestCase):
mocked_qt.PYQT_VERSION_STR = 'PyQt5 Test'
mocked_is_linux.return_value = False
mocked_application_version.return_value = 'Trunk Test'
mocked_save_filename.return_value = (Path('testfile.txt'), 'filter')
test_form = exceptionform.ExceptionForm()
test_form.file_attachment = None
with patch.object(Path, 'open') as mocked_path_open:
x = Path('testfile.txt')
mocked_save_filename.return_value = x, 'ext'
with patch.object(test_form, '_pyuno_import') as mock_pyuno:
with patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback:
with patch.object(test_form.description_text_edit, 'toPlainText') as mock_description:
with patch("openlp.core.ui.exceptionform.open", mock_open(), create=True) as mocked_open:
mock_pyuno.return_value = 'UNO Bridge Test'
mock_traceback.return_value = 'openlp: Traceback Test'
mock_description.return_value = 'Description Test'
test_form = exceptionform.ExceptionForm()
test_form.file_attachment = None
# WHEN: on_save_report_button_clicked called
test_form.on_save_report_button_clicked()
with patch.object(test_form, '_pyuno_import') as mock_pyuno, \
patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback, \
patch.object(test_form.description_text_edit, 'toPlainText') as mock_description:
mock_pyuno.return_value = 'UNO Bridge Test'
mock_traceback.return_value = 'openlp: Traceback Test'
mock_description.return_value = 'Description Test'
# WHEN: on_save_report_button_clicked called
test_form.on_save_report_button_clicked()
# THEN: Verify proper calls to save file
# self.maxDiff = None
check_text = "call().write({text})".format(text=MAIL_ITEM_TEXT.__repr__())
write_text = "{text}".format(text=mocked_open.mock_calls[1])
mocked_open.assert_called_with('testfile.txt', 'w')
self.assertEquals(check_text, write_text, "Saved information should match test text")
mocked_path_open.assert_has_calls([call().__enter__().write(MAIL_ITEM_TEXT)])

View File

@ -27,10 +27,10 @@ from unittest.mock import MagicMock, patch
from PyQt5 import QtCore
from openlp.core.common import Registry, is_macosx, Settings
from openlp.core.common import Registry, is_macosx
from openlp.core.common.path import Path
from openlp.core.lib import ScreenList, PluginManager
from openlp.core.ui import MainDisplay, AudioPlayer
from openlp.core.ui.media import MediaController
from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
from tests.helpers.testmixin import TestMixin
@ -184,7 +184,7 @@ class TestMainDisplay(TestCase, TestMixin):
self.assertEqual(pyobjc_nsview.window().collectionBehavior(), NSWindowCollectionBehaviorManaged,
'Window collection behavior should be NSWindowCollectionBehaviorManaged')
@patch(u'openlp.core.ui.maindisplay.Settings')
@patch('openlp.core.ui.maindisplay.Settings')
def test_show_display_startup_logo(self, MockedSettings):
# GIVEN: Mocked show_display, setting for logo visibility
display = MagicMock()
@ -204,7 +204,7 @@ class TestMainDisplay(TestCase, TestMixin):
# THEN: setVisible should had been called with "True"
main_display.setVisible.assert_called_once_with(True)
@patch(u'openlp.core.ui.maindisplay.Settings')
@patch('openlp.core.ui.maindisplay.Settings')
def test_show_display_hide_startup_logo(self, MockedSettings):
# GIVEN: Mocked show_display, setting for logo visibility
display = MagicMock()
@ -224,8 +224,8 @@ 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')
@patch('openlp.core.ui.maindisplay.Settings')
@patch('openlp.core.ui.maindisplay.build_html')
def test_build_html_no_video(self, MockedSettings, Mocked_build_html):
# GIVEN: Mocked display
display = MagicMock()
@ -252,8 +252,8 @@ class TestMainDisplay(TestCase, TestMixin):
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')
@patch('openlp.core.ui.maindisplay.Settings')
@patch('openlp.core.ui.maindisplay.build_html')
def test_build_html_video(self, MockedSettings, Mocked_build_html):
# GIVEN: Mocked display
display = MagicMock()
@ -270,7 +270,7 @@ class TestMainDisplay(TestCase, TestMixin):
service_item.theme_data = MagicMock()
service_item.theme_data.background_type = 'video'
service_item.theme_data.theme_name = 'name'
service_item.theme_data.background_filename = 'background_filename'
service_item.theme_data.background_filename = Path('background_filename')
mocked_plugin = MagicMock()
display.plugin_manager = PluginManager()
display.plugin_manager.plugins = [mocked_plugin]

View File

@ -49,5 +49,5 @@ class TestThemeManager(TestCase):
self.instance.on_image_path_edit_path_changed(Path('/', 'new', 'pat.h'))
# THEN: The theme background file should be set and `set_background_page_values` should have been called
self.assertEqual(self.instance.theme.background_filename, '/new/pat.h')
self.assertEqual(self.instance.theme.background_filename, Path('/', 'new', 'pat.h'))
mocked_set_background_page_values.assert_called_once_with()

View File

@ -30,8 +30,9 @@ from unittest.mock import ANY, MagicMock, patch
from PyQt5 import QtWidgets
from openlp.core.ui import ThemeManager
from openlp.core.common import Registry
from openlp.core.common.path import Path
from openlp.core.ui import ThemeManager
from tests.utils.constants import TEST_RESOURCES_PATH
@ -57,13 +58,13 @@ class TestThemeManager(TestCase):
"""
# GIVEN: A new ThemeManager instance.
theme_manager = ThemeManager()
theme_manager.path = os.path.join(TEST_RESOURCES_PATH, 'themes')
theme_manager.theme_path = Path(TEST_RESOURCES_PATH, 'themes')
with patch('zipfile.ZipFile.__init__') as mocked_zipfile_init, \
patch('zipfile.ZipFile.write') as mocked_zipfile_write:
mocked_zipfile_init.return_value = None
# WHEN: The theme is exported
theme_manager._export_theme(os.path.join('some', 'path', 'Default.otz'), 'Default')
theme_manager._export_theme(Path('some', 'path', 'Default.otz'), 'Default')
# THEN: The zipfile should be created at the given path
mocked_zipfile_init.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w')
@ -86,57 +87,49 @@ class TestThemeManager(TestCase):
"""
Test that we don't try to overwrite a theme background image with itself
"""
# GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile,
# GIVEN: A new theme manager instance, with mocked builtins.open, copyfile,
# theme, check_directory_exists and thememanager-attributes.
with patch('builtins.open') as mocked_open, \
patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \
patch('openlp.core.ui.thememanager.check_directory_exists'):
mocked_open.return_value = MagicMock()
theme_manager = ThemeManager(None)
theme_manager.old_background_image = None
theme_manager.generate_and_save_image = MagicMock()
theme_manager.path = ''
theme_manager.theme_path = MagicMock()
mocked_theme = MagicMock()
mocked_theme.theme_name = 'themename'
mocked_theme.extract_formatted_xml = MagicMock()
mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
# WHEN: Calling _write_theme with path to the same image, but the path written slightly different
file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg')
# Do replacement from end of string to avoid problems with path start
file_name2 = file_name1[::-1].replace(os.sep, os.sep + os.sep, 2)[::-1]
theme_manager._write_theme(mocked_theme, file_name1, file_name2)
file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
theme_manager._write_theme(mocked_theme, file_name1, file_name1)
# THEN: The mocked_copyfile should not have been called
self.assertFalse(mocked_copyfile.called, 'shutil.copyfile should not be called')
self.assertFalse(mocked_copyfile.called, 'copyfile should not be called')
def test_write_theme_diff_images(self):
"""
Test that we do overwrite a theme background image when a new is submitted
"""
# GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile,
# GIVEN: A new theme manager instance, with mocked builtins.open, copyfile,
# theme, check_directory_exists and thememanager-attributes.
with patch('builtins.open') as mocked_open, \
patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \
patch('openlp.core.ui.thememanager.check_directory_exists'):
mocked_open.return_value = MagicMock()
theme_manager = ThemeManager(None)
theme_manager.old_background_image = None
theme_manager.generate_and_save_image = MagicMock()
theme_manager.path = ''
theme_manager.theme_path = MagicMock()
mocked_theme = MagicMock()
mocked_theme.theme_name = 'themename'
mocked_theme.filename = "filename"
# mocked_theme.extract_formatted_xml = MagicMock()
# mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
# WHEN: Calling _write_theme with path to different images
file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg')
file_name2 = os.path.join(TEST_RESOURCES_PATH, 'church2.jpg')
file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
file_name2 = Path(TEST_RESOURCES_PATH, 'church2.jpg')
theme_manager._write_theme(mocked_theme, file_name1, file_name2)
# THEN: The mocked_copyfile should not have been called
self.assertTrue(mocked_copyfile.called, 'shutil.copyfile should be called')
self.assertTrue(mocked_copyfile.called, 'copyfile should be called')
def test_write_theme_special_char_name(self):
"""
@ -146,7 +139,7 @@ class TestThemeManager(TestCase):
theme_manager = ThemeManager(None)
theme_manager.old_background_image = None
theme_manager.generate_and_save_image = MagicMock()
theme_manager.path = self.temp_folder
theme_manager.theme_path = Path(self.temp_folder)
mocked_theme = MagicMock()
mocked_theme.theme_name = 'theme 愛 name'
mocked_theme.export_theme.return_value = "{}"
@ -208,17 +201,17 @@ class TestThemeManager(TestCase):
theme_manager = ThemeManager(None)
theme_manager._create_theme_from_xml = MagicMock()
theme_manager.generate_and_save_image = MagicMock()
theme_manager.path = ''
folder = mkdtemp()
theme_file = os.path.join(TEST_RESOURCES_PATH, 'themes', 'Moss_on_tree.otz')
theme_manager.theme_path = None
folder = Path(mkdtemp())
theme_file = Path(TEST_RESOURCES_PATH, 'themes', 'Moss_on_tree.otz')
# WHEN: We try to unzip it
theme_manager.unzip_theme(theme_file, folder)
# THEN: Files should be unpacked
self.assertTrue(os.path.exists(os.path.join(folder, 'Moss on tree', 'Moss on tree.xml')))
self.assertTrue((folder / 'Moss on tree' / 'Moss on tree.xml').exists())
self.assertEqual(mocked_critical_error_message_box.call_count, 0, 'No errors should have happened')
shutil.rmtree(folder)
shutil.rmtree(str(folder))
def test_unzip_theme_invalid_version(self):
"""

View File

@ -26,7 +26,8 @@ from unittest import TestCase
from unittest.mock import patch, MagicMock
from openlp.core.common import Registry, Settings
from openlp.core.ui import ThemeManager, ThemeForm, FileRenameForm
from openlp.core.common.path import Path
from openlp.core.ui import ThemeManager
from tests.helpers.testmixin import TestMixin
@ -91,6 +92,23 @@ class TestThemeManager(TestCase, TestMixin):
assert self.theme_manager.thumb_path.startswith(self.theme_manager.path) is True, \
'The thumb path and the main path should start with the same value'
def test_build_theme_path(self):
"""
Test the thememanager build_theme_path - basic test
"""
# GIVEN: A new a call to initialise
with patch('openlp.core.common.AppLocation.get_section_data_path', return_value=Path('test/path')):
Settings().setValue('themes/global theme', 'my_theme')
self.theme_manager.theme_form = MagicMock()
self.theme_manager.load_first_time_themes = MagicMock()
# WHEN: the build_theme_path is run
self.theme_manager.build_theme_path()
# THEN: The thumbnail path should be a sub path of the test path
self.assertEqual(self.theme_manager.thumb_path, Path('test/path/thumbnails'))
def test_click_on_new_theme(self):
"""
Test the on_add_theme event handler is called by the UI
@ -109,17 +127,16 @@ class TestThemeManager(TestCase, TestMixin):
@patch('openlp.core.ui.themeform.ThemeForm._setup')
@patch('openlp.core.ui.filerenameform.FileRenameForm._setup')
def test_bootstrap_post(self, mocked_theme_form, mocked_rename_form):
def test_bootstrap_post(self, mocked_rename_form, mocked_theme_form):
"""
Test the functions of bootstrap_post_setup are called.
"""
# GIVEN:
self.theme_manager.load_themes = MagicMock()
self.theme_manager.path = MagicMock()
self.theme_manager.theme_path = MagicMock()
# WHEN:
self.theme_manager.bootstrap_post_set_up()
# THEN:
self.assertEqual(self.theme_manager.path, self.theme_manager.theme_form.path)
self.assertEqual(1, self.theme_manager.load_themes.call_count, "load_themes should have been called once")