forked from openlp/openlp
pathlib changes to the theme code
lp:~phill-ridout/openlp/pathlib7 (revision 2773) [SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/2214/ [SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/2117/ [SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/2003/ [SUCCESS] https://ci.openlp.io/job/Branch-04a-Code_Analysis/1369/ [SUCCESS] https://ci.openlp.io/job/Branch-04b-Test_Coverage/1200/ bzr-revno: 2771
This commit is contained in:
commit
d94138b349
@ -4,7 +4,7 @@
|
||||
"color": "#000000",
|
||||
"direction": "vertical",
|
||||
"end_color": "#000000",
|
||||
"filename": "",
|
||||
"filename": null,
|
||||
"start_color": "#000000",
|
||||
"type": "solid"
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
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,148 @@ 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 +712,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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()]:
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user