forked from openlp/openlp
Modify themes to work with pathlib
This commit is contained in:
parent
dfe2ae347e
commit
10b13872e5
@ -4,7 +4,7 @@
|
|||||||
"color": "#000000",
|
"color": "#000000",
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"end_color": "#000000",
|
"end_color": "#000000",
|
||||||
"filename": "",
|
"filename": null,
|
||||||
"start_color": "#000000",
|
"start_color": "#000000",
|
||||||
"type": "solid"
|
"type": "solid"
|
||||||
},
|
},
|
||||||
|
@ -26,6 +26,7 @@ from string import Template
|
|||||||
from PyQt5 import QtGui, QtCore, QtWebKitWidgets
|
from PyQt5 import QtGui, QtCore, QtWebKitWidgets
|
||||||
|
|
||||||
from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, RegistryMixin, Settings
|
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, \
|
from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \
|
||||||
build_lyrics_format_css, build_lyrics_outline_css, build_chords_css
|
build_lyrics_format_css, build_lyrics_outline_css, build_chords_css
|
||||||
from openlp.core.common import ThemeLevel
|
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]
|
theme_data, main_rect, footer_rect = self._theme_dimensions[theme_name]
|
||||||
# if No file do not update cache
|
# if No file do not update cache
|
||||||
if theme_data.background_filename:
|
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))
|
ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
|
||||||
|
|
||||||
def pre_render(self, override_theme_data=None):
|
def pre_render(self, override_theme_data=None):
|
||||||
@ -207,8 +208,8 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
|
|||||||
service_item.raw_footer = FOOTER
|
service_item.raw_footer = FOOTER
|
||||||
# if No file do not update cache
|
# if No file do not update cache
|
||||||
if theme_data.background_filename:
|
if theme_data.background_filename:
|
||||||
self.image_manager.add_image(
|
self.image_manager.add_image(path_to_str(theme_data.background_filename),
|
||||||
theme_data.background_filename, ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
|
ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
|
||||||
theme_data, main, footer = self.pre_render(theme_data)
|
theme_data, main, footer = self.pre_render(theme_data)
|
||||||
service_item.theme_data = theme_data
|
service_item.theme_data = theme_data
|
||||||
service_item.main = main
|
service_item.main = main
|
||||||
|
@ -22,13 +22,13 @@
|
|||||||
"""
|
"""
|
||||||
Provide the theme XML and handling functions for OpenLP v2 themes.
|
Provide the theme XML and handling functions for OpenLP v2 themes.
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from lxml import etree, objectify
|
from lxml import etree, objectify
|
||||||
from openlp.core.common import AppLocation, de_hump
|
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
|
from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -160,9 +160,8 @@ class Theme(object):
|
|||||||
# basic theme object with defaults
|
# basic theme object with defaults
|
||||||
json_path = AppLocation.get_directory(AppLocation.AppDir) / 'core' / 'lib' / 'json' / 'theme.json'
|
json_path = AppLocation.get_directory(AppLocation.AppDir) / 'core' / 'lib' / 'json' / 'theme.json'
|
||||||
jsn = get_text_file_string(json_path)
|
jsn = get_text_file_string(json_path)
|
||||||
jsn = json.loads(jsn)
|
self.load_theme(jsn)
|
||||||
self.expand_json(jsn)
|
self.background_filename = None
|
||||||
self.background_filename = ''
|
|
||||||
|
|
||||||
def expand_json(self, var, prev=None):
|
def expand_json(self, var, prev=None):
|
||||||
"""
|
"""
|
||||||
@ -174,8 +173,6 @@ class Theme(object):
|
|||||||
for key, value in var.items():
|
for key, value in var.items():
|
||||||
if prev:
|
if prev:
|
||||||
key = prev + "_" + key
|
key = prev + "_" + key
|
||||||
else:
|
|
||||||
key = key
|
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
self.expand_json(value, key)
|
self.expand_json(value, key)
|
||||||
else:
|
else:
|
||||||
@ -185,13 +182,13 @@ class Theme(object):
|
|||||||
"""
|
"""
|
||||||
Add the path name to the image name so the background can be rendered.
|
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_type == 'image' or self.background_type == 'video':
|
||||||
if self.background_filename and path:
|
if self.background_filename and path:
|
||||||
self.theme_name = self.theme_name.strip()
|
self.theme_name = self.theme_name.strip()
|
||||||
self.background_filename = self.background_filename.strip()
|
self.background_filename = path / self.theme_name / self.background_filename
|
||||||
self.background_filename = os.path.join(path, self.theme_name, self.background_filename)
|
|
||||||
|
|
||||||
def set_default_header_footer(self):
|
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_y = current_screen['size'].height() * 9 / 10
|
||||||
self.font_footer_height = current_screen['size'].height() / 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.
|
Convert the JSON file and expand it.
|
||||||
|
|
||||||
:param theme: the theme string
|
: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)
|
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
|
Loop through the fields and build a dictionary of them
|
||||||
|
|
||||||
@ -223,7 +225,9 @@ class Theme(object):
|
|||||||
theme_data = {}
|
theme_data = {}
|
||||||
for attr, value in self.__dict__.items():
|
for attr, value in self.__dict__.items():
|
||||||
theme_data["{attr}".format(attr=attr)] = value
|
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):
|
def parse(self, xml):
|
||||||
"""
|
"""
|
||||||
|
@ -346,7 +346,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
if not hasattr(self, 'service_item'):
|
if not hasattr(self, 'service_item'):
|
||||||
return False
|
return False
|
||||||
self.override['image'] = path
|
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)
|
self.image(path)
|
||||||
# Update the preview frame.
|
# Update the preview frame.
|
||||||
if self.is_live:
|
if self.is_live:
|
||||||
@ -454,7 +454,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
Registry().execute('video_background_replaced')
|
Registry().execute('video_background_replaced')
|
||||||
self.override = {}
|
self.override = {}
|
||||||
# We have a different theme.
|
# 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')
|
Registry().execute('live_theme_changed')
|
||||||
self.override = {}
|
self.override = {}
|
||||||
else:
|
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_type == 'image':
|
||||||
if self.service_item.theme_data.background_filename:
|
if self.service_item.theme_data.background_filename:
|
||||||
self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
|
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:
|
if image_path:
|
||||||
image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
|
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,
|
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')),
|
path = os.path.join(str(AppLocation.get_section_data_path('themes')),
|
||||||
self.service_item.theme_data.theme_name)
|
self.service_item.theme_data.theme_name)
|
||||||
service_item.add_from_command(path,
|
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')
|
':/media/slidecontroller_multimedia.png')
|
||||||
self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True)
|
self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True)
|
||||||
self._hide_mouse()
|
self._hide_mouse()
|
||||||
|
@ -28,7 +28,6 @@ import os
|
|||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
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 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.theme import BackgroundType, BackgroundGradientType
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.ui import ThemeLayoutForm
|
from openlp.core.ui import ThemeLayoutForm
|
||||||
@ -61,7 +60,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
|||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.registerFields()
|
self.registerFields()
|
||||||
self.update_theme_allowed = True
|
self.update_theme_allowed = True
|
||||||
self.temp_background_filename = ''
|
self.temp_background_filename = None
|
||||||
self.theme_layout_form = ThemeLayoutForm(self)
|
self.theme_layout_form = ThemeLayoutForm(self)
|
||||||
self.background_combo_box.currentIndexChanged.connect(self.on_background_combo_box_current_index_changed)
|
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)
|
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)
|
background_image = BackgroundType.to_string(BackgroundType.Image)
|
||||||
if self.page(self.currentId()) == self.background_page and \
|
if self.page(self.currentId()) == self.background_page and \
|
||||||
self.theme.background_type == background_image and \
|
self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename):
|
||||||
is_not_image_file(Path(self.theme.background_filename)):
|
|
||||||
QtWidgets.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
|
QtWidgets.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
|
||||||
translate('OpenLP.ThemeWizard', 'You have not selected a '
|
translate('OpenLP.ThemeWizard', 'You have not selected a '
|
||||||
'background image. Please select one before continuing.'))
|
'background image. Please select one before continuing.'))
|
||||||
@ -273,7 +271,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
|||||||
Run the wizard.
|
Run the wizard.
|
||||||
"""
|
"""
|
||||||
log.debug('Editing theme {name}'.format(name=self.theme.theme_name))
|
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.update_theme_allowed = False
|
||||||
self.set_defaults()
|
self.set_defaults()
|
||||||
self.update_theme_allowed = True
|
self.update_theme_allowed = True
|
||||||
@ -318,11 +316,11 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
|||||||
self.setField('background_type', 1)
|
self.setField('background_type', 1)
|
||||||
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
|
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
|
||||||
self.image_color_button.color = self.theme.background_border_color
|
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)
|
self.setField('background_type', 2)
|
||||||
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
|
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
|
||||||
self.video_color_button.color = self.theme.background_border_color
|
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)
|
self.setField('background_type', 4)
|
||||||
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
|
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
|
||||||
self.setField('background_type', 3)
|
self.setField('background_type', 3)
|
||||||
@ -402,14 +400,14 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
|||||||
self.theme.background_type = BackgroundType.to_string(index)
|
self.theme.background_type = BackgroundType.to_string(index)
|
||||||
if self.theme.background_type != BackgroundType.to_string(BackgroundType.Image) and \
|
if self.theme.background_type != BackgroundType.to_string(BackgroundType.Image) and \
|
||||||
self.theme.background_type != BackgroundType.to_string(BackgroundType.Video) 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.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
|
if (self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or
|
||||||
self.theme.background_type != BackgroundType.to_string(BackgroundType.Video)) and \
|
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.theme.background_filename = self.temp_background_filename
|
||||||
self.temp_background_filename = ''
|
self.temp_background_filename = None
|
||||||
self.set_background_page_values()
|
self.set_background_page_values()
|
||||||
|
|
||||||
def on_gradient_combo_box_current_index_changed(self, index):
|
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
|
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()
|
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()
|
self.set_background_page_values()
|
||||||
|
|
||||||
def on_main_color_changed(self, color):
|
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', 'Theme Name Invalid'),
|
||||||
translate('OpenLP.ThemeWizard', 'Invalid theme name. Please enter one.'))
|
translate('OpenLP.ThemeWizard', 'Invalid theme name. Please enter one.'))
|
||||||
return
|
return
|
||||||
save_from = None
|
source_path = None
|
||||||
save_to = None
|
destination_path = None
|
||||||
if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \
|
if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \
|
||||||
self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
|
self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
|
||||||
filename = os.path.split(str(self.theme.background_filename))[1]
|
file_name = self.theme.background_filename.name
|
||||||
save_to = os.path.join(self.path, self.theme.theme_name, filename)
|
destination_path = self.path / self.theme.theme_name / file_name
|
||||||
save_from = self.theme.background_filename
|
source_path = self.theme.background_filename
|
||||||
if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):
|
if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):
|
||||||
return
|
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)
|
return QtWidgets.QDialog.accept(self)
|
||||||
|
@ -24,14 +24,14 @@ The Theme Manager manages adding, deleteing and modifying of themes.
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import zipfile
|
import zipfile
|
||||||
import shutil
|
|
||||||
|
|
||||||
from xml.etree.ElementTree import ElementTree, XML
|
from xml.etree.ElementTree import ElementTree, XML
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \
|
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \
|
||||||
UiStrings, check_directory_exists, translate, is_win, get_filesystem_encoding, delete_file
|
UiStrings, check_directory_exists, translate, delete_file
|
||||||
from openlp.core.common.path import Path, path_to_str, str_to_path
|
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, \
|
from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \
|
||||||
check_item_selected, create_thumb, validate_thumb
|
check_item_selected, create_thumb, validate_thumb
|
||||||
from openlp.core.lib.theme import Theme, BackgroundType
|
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 import FileRenameForm, ThemeForm
|
||||||
from openlp.core.ui.lib import OpenLPToolbar
|
from openlp.core.ui.lib import OpenLPToolbar
|
||||||
from openlp.core.ui.lib.filedialog import FileDialog
|
from openlp.core.ui.lib.filedialog import FileDialog
|
||||||
from openlp.core.common.languagemanager import get_locale_key
|
|
||||||
|
|
||||||
|
|
||||||
class Ui_ThemeManager(object):
|
class Ui_ThemeManager(object):
|
||||||
@ -135,7 +134,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
|
|||||||
self.settings_section = 'themes'
|
self.settings_section = 'themes'
|
||||||
# Variables
|
# Variables
|
||||||
self.theme_list = []
|
self.theme_list = []
|
||||||
self.old_background_image = None
|
self.old_background_image_path = None
|
||||||
|
|
||||||
def bootstrap_initialise(self):
|
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.global_theme = Settings().value(self.settings_section + '/global theme')
|
||||||
self.build_theme_path()
|
self.build_theme_path()
|
||||||
self.load_first_time_themes()
|
self.load_first_time_themes()
|
||||||
|
self.upgrade_themes()
|
||||||
|
|
||||||
def bootstrap_post_set_up(self):
|
def bootstrap_post_set_up(self):
|
||||||
"""
|
"""
|
||||||
process the bootstrap post setup request
|
process the bootstrap post setup request
|
||||||
"""
|
"""
|
||||||
self.theme_form = ThemeForm(self)
|
self.theme_form = ThemeForm(self)
|
||||||
self.theme_form.path = self.path
|
self.theme_form.path = self.theme_path
|
||||||
self.file_rename_form = FileRenameForm()
|
self.file_rename_form = FileRenameForm()
|
||||||
Registry().register_function('theme_update_global', self.change_global_from_tab)
|
Registry().register_function('theme_update_global', self.change_global_from_tab)
|
||||||
self.load_themes()
|
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):
|
def build_theme_path(self):
|
||||||
"""
|
"""
|
||||||
Set up the theme path variables
|
Set up the theme path variables
|
||||||
|
|
||||||
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
self.path = str(AppLocation.get_section_data_path(self.settings_section))
|
self.theme_path = AppLocation.get_section_data_path(self.settings_section)
|
||||||
check_directory_exists(Path(self.path))
|
check_directory_exists(self.theme_path)
|
||||||
self.thumb_path = os.path.join(self.path, 'thumbnails')
|
self.thumb_path = self.theme_path / 'thumbnails'
|
||||||
check_directory_exists(Path(self.thumb_path))
|
check_directory_exists(self.thumb_path)
|
||||||
|
|
||||||
def check_list_state(self, item, field=None):
|
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.
|
Takes a theme and makes a new copy of it as well as saving it.
|
||||||
|
|
||||||
:param theme_data: The theme to be used
|
:param Theme theme_data: The theme to be used
|
||||||
:param new_theme_name: The new theme name to save the data to
|
:param str new_theme_name: The new theme name of the theme
|
||||||
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
save_to = None
|
destination_path = None
|
||||||
save_from = None
|
source_path = None
|
||||||
if theme_data.background_type == 'image' or theme_data.background_type == 'video':
|
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])
|
destination_path = self.theme_path / new_theme_name / theme_data.background_filename.name
|
||||||
save_from = theme_data.background_filename
|
source_path = theme_data.background_filename
|
||||||
theme_data.theme_name = new_theme_name
|
theme_data.theme_name = new_theme_name
|
||||||
theme_data.extend_image_filename(self.path)
|
theme_data.extend_image_filename(self.theme_path)
|
||||||
self.save_theme(theme_data, save_from, save_to)
|
self.save_theme(theme_data, source_path, destination_path)
|
||||||
self.load_themes()
|
self.load_themes()
|
||||||
|
|
||||||
def on_edit_theme(self, field=None):
|
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()
|
item = self.theme_list_widget.currentItem()
|
||||||
theme = self.get_theme_data(item.data(QtCore.Qt.UserRole))
|
theme = self.get_theme_data(item.data(QtCore.Qt.UserRole))
|
||||||
if theme.background_type == 'image' or theme.background_type == 'video':
|
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.theme = theme
|
||||||
self.theme_form.exec(True)
|
self.theme_form.exec(True)
|
||||||
self.old_background_image = None
|
self.old_background_image_path = None
|
||||||
self.renderer.update_theme(theme.theme_name)
|
self.renderer.update_theme(theme.theme_name)
|
||||||
self.load_themes()
|
self.load_themes()
|
||||||
|
|
||||||
@ -355,77 +371,76 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
|
|||||||
"""
|
"""
|
||||||
self.theme_list.remove(theme)
|
self.theme_list.remove(theme)
|
||||||
thumb = '{name}.png'.format(name=theme)
|
thumb = '{name}.png'.format(name=theme)
|
||||||
delete_file(Path(self.path, thumb))
|
delete_file(self.theme_path / thumb)
|
||||||
delete_file(Path(self.thumb_path, thumb))
|
delete_file(self.thumb_path / thumb)
|
||||||
try:
|
try:
|
||||||
# Windows is always unicode, so no need to encode filenames
|
rmtree(self.theme_path / theme)
|
||||||
if is_win():
|
except OSError:
|
||||||
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
|
|
||||||
self.log_exception('Error deleting theme {name}'.format(name=theme))
|
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
|
Export the theme to a zip file
|
||||||
:param field:
|
|
||||||
|
:param bool checked: Sent by the QAction.triggered signal. It's not used in this method.
|
||||||
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
item = self.theme_list_widget.currentItem()
|
item = self.theme_list_widget.currentItem()
|
||||||
if item is None:
|
if item is None:
|
||||||
critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.'))
|
critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.'))
|
||||||
return
|
return
|
||||||
theme = item.data(QtCore.Qt.UserRole)
|
theme_name = item.data(QtCore.Qt.UserRole)
|
||||||
export_path, filter_used = \
|
export_path, filter_used = \
|
||||||
FileDialog.getSaveFileName(self.main_window,
|
FileDialog.getSaveFileName(self.main_window,
|
||||||
translate('OpenLP.ThemeManager', 'Save Theme - ({name})').
|
translate('OpenLP.ThemeManager',
|
||||||
format(name=theme),
|
'Save Theme - ({name})').format(name=theme_name),
|
||||||
Settings().value(self.settings_section + '/last directory export'),
|
Settings().value(self.settings_section + '/last directory export'),
|
||||||
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
|
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'),
|
||||||
|
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
if export_path:
|
if export_path:
|
||||||
Settings().setValue(self.settings_section + '/last directory export', export_path.parent)
|
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,
|
QtWidgets.QMessageBox.information(self,
|
||||||
translate('OpenLP.ThemeManager', 'Theme Exported'),
|
translate('OpenLP.ThemeManager', 'Theme Exported'),
|
||||||
translate('OpenLP.ThemeManager',
|
translate('OpenLP.ThemeManager',
|
||||||
'Your theme has been successfully exported.'))
|
'Your theme has been successfully exported.'))
|
||||||
self.application.set_normal_cursor()
|
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.
|
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:
|
try:
|
||||||
theme_zip = zipfile.ZipFile(theme_path, 'w')
|
with zipfile.ZipFile(str(theme_path), 'w') as theme_zip:
|
||||||
source = os.path.join(self.path, theme)
|
source_path = self.theme_path / theme_name
|
||||||
for files in os.walk(source):
|
for file_path in source_path.iterdir():
|
||||||
for name in files[2]:
|
theme_zip.write(str(file_path), os.path.join(theme_name, file_path.name))
|
||||||
theme_zip.write(os.path.join(source, name), os.path.join(theme, name))
|
|
||||||
theme_zip.close()
|
|
||||||
return True
|
return True
|
||||||
except OSError as ose:
|
except OSError as ose:
|
||||||
self.log_exception('Export Theme Failed')
|
self.log_exception('Export Theme Failed')
|
||||||
critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
|
critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
|
||||||
translate('OpenLP.ThemeManager', 'The theme export failed because this error '
|
translate('OpenLP.ThemeManager',
|
||||||
'occurred: {err}').format(err=ose.strerror))
|
'The theme_name export failed because this error occurred: {err}')
|
||||||
if theme_zip:
|
.format(err=ose.strerror))
|
||||||
theme_zip.close()
|
if theme_path.exists():
|
||||||
shutil.rmtree(theme_path, True)
|
rmtree(theme_path, True)
|
||||||
return False
|
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
|
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.
|
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,
|
self,
|
||||||
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
|
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
|
||||||
Settings().value(self.settings_section + '/last directory import'),
|
Settings().value(self.settings_section + '/last directory import'),
|
||||||
@ -435,8 +450,8 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
|
|||||||
return
|
return
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
for file_path in file_paths:
|
for file_path in file_paths:
|
||||||
self.unzip_theme(path_to_str(file_path), self.path)
|
self.unzip_theme(file_path, self.theme_path)
|
||||||
Settings().setValue(self.settings_section + '/last directory import', file_path)
|
Settings().setValue(self.settings_section + '/last directory import', file_path.parent)
|
||||||
self.load_themes()
|
self.load_themes()
|
||||||
self.application.set_normal_cursor()
|
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
|
Imports any themes on start up and makes sure there is at least one theme
|
||||||
"""
|
"""
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
files = AppLocation.get_files(self.settings_section, '.otz')
|
theme_paths = AppLocation.get_files(self.settings_section, '.otz')
|
||||||
for theme_file in files:
|
for theme_path in theme_paths:
|
||||||
theme_file = os.path.join(self.path, str(theme_file))
|
theme_path = self.theme_path / theme_path
|
||||||
self.unzip_theme(theme_file, self.path)
|
self.unzip_theme(theme_path, self.theme_path)
|
||||||
delete_file(Path(theme_file))
|
delete_file(theme_path)
|
||||||
files = AppLocation.get_files(self.settings_section, '.png')
|
theme_paths = AppLocation.get_files(self.settings_section, '.png')
|
||||||
# No themes have been found so create one
|
# No themes have been found so create one
|
||||||
if not files:
|
if not theme_paths:
|
||||||
theme = Theme()
|
theme = Theme()
|
||||||
theme.theme_name = UiStrings().Default
|
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)
|
Settings().setValue(self.settings_section + '/global theme', theme.theme_name)
|
||||||
self.application.set_normal_cursor()
|
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
|
# Sort the themes by its name considering language specific
|
||||||
files.sort(key=lambda file_name: get_locale_key(str(file_name)))
|
files.sort(key=lambda file_name: get_locale_key(str(file_name)))
|
||||||
# now process the file list of png files
|
# now process the file list of png files
|
||||||
for name in files:
|
for file in files:
|
||||||
name = str(name)
|
|
||||||
# check to see file is in theme root directory
|
# check to see file is in theme root directory
|
||||||
theme = os.path.join(self.path, name)
|
theme_path = self.theme_path / file
|
||||||
if os.path.exists(theme):
|
if theme_path.exists():
|
||||||
text_name = os.path.splitext(name)[0]
|
text_name = theme_path.stem
|
||||||
if text_name == self.global_theme:
|
if text_name == self.global_theme:
|
||||||
name = translate('OpenLP.ThemeManager', '{name} (default)').format(name=text_name)
|
name = translate('OpenLP.ThemeManager', '{name} (default)').format(name=text_name)
|
||||||
else:
|
else:
|
||||||
name = text_name
|
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)
|
item_name = QtWidgets.QListWidgetItem(name)
|
||||||
if validate_thumb(Path(theme), Path(thumb)):
|
if validate_thumb(theme_path, thumb):
|
||||||
icon = build_icon(thumb)
|
icon = build_icon(thumb)
|
||||||
else:
|
else:
|
||||||
icon = create_thumb(theme, thumb)
|
icon = create_thumb(str(theme_path), str(thumb))
|
||||||
item_name.setIcon(icon)
|
item_name.setIcon(icon)
|
||||||
item_name.setData(QtCore.Qt.UserRole, text_name)
|
item_name.setData(QtCore.Qt.UserRole, text_name)
|
||||||
self.theme_list_widget.addItem(item_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):
|
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
|
:param str theme_name: Name of the theme to load from file
|
||||||
:return: The theme object.
|
:return: The theme object.
|
||||||
|
:rtype: Theme
|
||||||
"""
|
"""
|
||||||
self.log_debug('get theme data for theme {name}'.format(name=theme_name))
|
theme_name = str(theme_name)
|
||||||
theme_file_path = Path(self.path, str(theme_name), '{file_name}.json'.format(file_name=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)
|
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:
|
if not theme_data:
|
||||||
self.log_debug('No theme data - using default theme')
|
self.log_debug('No theme data - using default theme')
|
||||||
return Theme()
|
return Theme()
|
||||||
else:
|
return self._create_theme_from_json(theme_data, self.theme_path)
|
||||||
if jsn:
|
|
||||||
return self._create_theme_from_json(theme_data, self.path)
|
|
||||||
else:
|
|
||||||
return self._create_theme_from_xml(theme_data, self.path)
|
|
||||||
|
|
||||||
def over_write_message_box(self, theme_name):
|
def over_write_message_box(self, theme_name):
|
||||||
"""
|
"""
|
||||||
@ -543,172 +549,149 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
|
|||||||
defaultButton=QtWidgets.QMessageBox.No)
|
defaultButton=QtWidgets.QMessageBox.No)
|
||||||
return ret == QtWidgets.QMessageBox.Yes
|
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
|
Unzip the theme, remove the preview file if stored. Generate a new preview file. Check the XML theme version
|
||||||
and upgrade if necessary.
|
and upgrade if necessary.
|
||||||
:param file_name:
|
:param openlp.core.common.path.Path file_path:
|
||||||
:param directory:
|
:param openlp.core.common.path.Path directory_path:
|
||||||
"""
|
"""
|
||||||
self.log_debug('Unzipping theme {name}'.format(name=file_name))
|
self.log_debug('Unzipping theme {name}'.format(name=file_path))
|
||||||
theme_zip = None
|
|
||||||
out_file = None
|
|
||||||
file_xml = None
|
file_xml = None
|
||||||
abort_import = True
|
abort_import = True
|
||||||
json_theme = False
|
json_theme = False
|
||||||
theme_name = ""
|
theme_name = ""
|
||||||
try:
|
try:
|
||||||
theme_zip = zipfile.ZipFile(file_name)
|
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']
|
json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json']
|
||||||
if len(json_file) != 1:
|
if len(json_file) != 1:
|
||||||
# TODO: remove XML handling at some point but would need a auto conversion to run first.
|
# 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']
|
xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
|
||||||
if len(xml_file) != 1:
|
if len(xml_file) != 1:
|
||||||
self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))
|
self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))
|
||||||
raise ValidationError
|
raise ValidationError
|
||||||
xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()
|
xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()
|
||||||
theme_version = xml_tree.get('version', default=None)
|
theme_version = xml_tree.get('version', default=None)
|
||||||
if not theme_version or float(theme_version) < 2.0:
|
if not theme_version or float(theme_version) < 2.0:
|
||||||
self.log_error('Theme version is less than 2.0')
|
self.log_error('Theme version is less than 2.0')
|
||||||
raise ValidationError
|
raise ValidationError
|
||||||
theme_name = xml_tree.find('name').text.strip()
|
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)
|
|
||||||
else:
|
else:
|
||||||
out_file = open(full_name, 'wb')
|
new_theme = Theme()
|
||||||
out_file.write(theme_zip.read(name))
|
new_theme.load_theme(theme_zip.read(json_file[0]).decode("utf-8"))
|
||||||
out_file.close()
|
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):
|
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
|
raise ValidationError
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
|
critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
|
||||||
translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
|
translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
|
||||||
finally:
|
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:
|
if not abort_import:
|
||||||
# As all files are closed, we can create the Theme.
|
# As all files are closed, we can create the Theme.
|
||||||
if file_xml:
|
if file_xml:
|
||||||
if json_theme:
|
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:
|
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)
|
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):
|
def check_if_theme_exists(self, theme_name):
|
||||||
"""
|
"""
|
||||||
Check if theme already exists and displays error message
|
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
|
:return: True or False if theme exists
|
||||||
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
theme_dir = os.path.join(self.path, theme_name)
|
if (self.theme_path / theme_name).exists():
|
||||||
if os.path.exists(theme_dir):
|
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('OpenLP.ThemeManager', 'Validation Error'),
|
translate('OpenLP.ThemeManager', 'Validation Error'),
|
||||||
translate('OpenLP.ThemeManager', 'A theme with this name already exists.'))
|
translate('OpenLP.ThemeManager', 'A theme with this name already exists.'))
|
||||||
return False
|
return False
|
||||||
return True
|
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
|
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 Theme theme: The theme data object.
|
||||||
:param image_to: Where the Theme Image is to be saved to
|
: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):
|
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,
|
ImageSource.Theme,
|
||||||
QtGui.QColor(theme.background_border_color))
|
QtGui.QColor(theme.background_border_color))
|
||||||
self.image_manager.process_updates()
|
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
|
Writes the theme to the disk and handles the background image if necessary
|
||||||
|
|
||||||
:param theme: The theme data object.
|
:param Theme theme: The theme data object.
|
||||||
:param image_from: Where the theme image is currently located.
|
:param openlp.core.common.path.Path image_source_path: Where the theme image is currently located.
|
||||||
:param image_to: Where the Theme Image is to be saved to
|
:param openlp.core.common.path.Path image_destination_path: Where the Theme Image is to be saved to
|
||||||
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
name = theme.theme_name
|
name = theme.theme_name
|
||||||
theme_pretty = theme.export_theme()
|
theme_pretty = theme.export_theme(self.theme_path)
|
||||||
theme_dir = os.path.join(self.path, name)
|
theme_dir = self.theme_path / name
|
||||||
check_directory_exists(Path(theme_dir))
|
check_directory_exists(theme_dir)
|
||||||
theme_file = os.path.join(theme_dir, name + '.json')
|
theme_path = theme_dir / '{file_name}.json'.format(file_name=name)
|
||||||
if self.old_background_image and image_to != self.old_background_image:
|
|
||||||
delete_file(Path(self.old_background_image))
|
|
||||||
out_file = None
|
|
||||||
try:
|
try:
|
||||||
out_file = open(theme_file, 'w', encoding='utf-8')
|
theme_path.write_text(theme_pretty)
|
||||||
out_file.write(theme_pretty)
|
|
||||||
except IOError:
|
except IOError:
|
||||||
self.log_exception('Saving theme to file failed')
|
self.log_exception('Saving theme to file failed')
|
||||||
finally:
|
if image_source_path and image_destination_path:
|
||||||
if out_file:
|
if self.old_background_image_path and image_destination_path != self.old_background_image_path:
|
||||||
out_file.close()
|
delete_file(self.old_background_image_path)
|
||||||
if image_from and os.path.abspath(image_from) != os.path.abspath(image_to):
|
if image_source_path != image_destination_path:
|
||||||
try:
|
try:
|
||||||
# Windows is always unicode, so no need to encode filenames
|
copyfile(image_source_path, image_destination_path)
|
||||||
if is_win():
|
except IOError:
|
||||||
shutil.copyfile(image_from, image_to)
|
self.log_exception('Failed to save theme image')
|
||||||
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')
|
|
||||||
self.generate_and_save_image(name, theme)
|
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
|
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.
|
:param theme: The theme data object.
|
||||||
"""
|
"""
|
||||||
frame = self.generate_image(theme)
|
frame = self.generate_image(theme)
|
||||||
sample_path_name = os.path.join(self.path, name + '.png')
|
sample_path_name = self.theme_path / '{file_name}.png'.format(file_name=theme_name)
|
||||||
if os.path.exists(sample_path_name):
|
if sample_path_name.exists():
|
||||||
os.unlink(sample_path_name)
|
sample_path_name.unlink()
|
||||||
frame.save(sample_path_name, 'png')
|
frame.save(str(sample_path_name), 'png')
|
||||||
thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=name))
|
thumb_path = self.thumb_path / '{name}.png'.format(name=theme_name)
|
||||||
create_thumb(sample_path_name, thumb, False)
|
create_thumb(str(sample_path_name), str(thumb_path), False)
|
||||||
|
|
||||||
def update_preview_images(self):
|
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)
|
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
|
@staticmethod
|
||||||
def _create_theme_from_xml(theme_xml, image_path):
|
def _create_theme_from_xml(theme_xml, image_path):
|
||||||
"""
|
"""
|
||||||
Return a theme object using information parsed from XML
|
Return a theme object using information parsed from XML
|
||||||
|
|
||||||
:param theme_xml: The Theme data object.
|
: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.
|
:return: Theme data.
|
||||||
|
:rtype: Theme
|
||||||
"""
|
"""
|
||||||
theme = Theme()
|
theme = Theme()
|
||||||
theme.parse(theme_xml)
|
theme.parse(theme_xml)
|
||||||
theme.extend_image_filename(image_path)
|
theme.extend_image_filename(image_path)
|
||||||
return theme
|
return theme
|
||||||
|
|
||||||
@staticmethod
|
def _create_theme_from_json(self, theme_json, image_path):
|
||||||
def _create_theme_from_json(theme_json, image_path):
|
|
||||||
"""
|
"""
|
||||||
Return a theme object using information parsed from JSON
|
Return a theme object using information parsed from JSON
|
||||||
|
|
||||||
:param theme_json: The Theme data object.
|
: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.
|
:return: Theme data.
|
||||||
|
:rtype: Theme
|
||||||
"""
|
"""
|
||||||
theme = Theme()
|
theme = Theme()
|
||||||
theme.load_theme(theme_json)
|
theme.load_theme(theme_json, self.theme_path)
|
||||||
theme.extend_image_filename(image_path)
|
theme.extend_image_filename(image_path)
|
||||||
return theme
|
return theme
|
||||||
|
|
||||||
|
@ -211,8 +211,8 @@ class ThemesTab(SettingsTab):
|
|||||||
"""
|
"""
|
||||||
Utility method to update the global theme preview image.
|
Utility method to update the global theme preview image.
|
||||||
"""
|
"""
|
||||||
image = self.theme_manager.get_preview_image(self.global_theme)
|
image_path = self.theme_manager.theme_path / '{file_name}.png'.format(file_name=self.global_theme)
|
||||||
preview = QtGui.QPixmap(str(image))
|
preview = QtGui.QPixmap(str(image_path))
|
||||||
if not preview.isNull():
|
if not preview.isNull():
|
||||||
preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
||||||
self.default_list_view.setPixmap(preview)
|
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
|
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')
|
log.debug('Starting upgrade_2 for file_path to JSON')
|
||||||
old_table = Table('image_filenames', metadata, autoload=True)
|
old_table = Table('image_filenames', metadata, autoload=True)
|
||||||
if 'file_path' not in [col.name for col in old_table.c.values()]:
|
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.
|
Package to test the openlp.core.lib.theme package.
|
||||||
"""
|
"""
|
||||||
from unittest import TestCase
|
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
from openlp.core.lib.theme import Theme
|
from openlp.core.lib.theme import Theme
|
||||||
|
|
||||||
@ -79,16 +80,16 @@ class TestTheme(TestCase):
|
|||||||
"""
|
"""
|
||||||
# GIVEN: A theme object
|
# GIVEN: A theme object
|
||||||
theme = Theme()
|
theme = Theme()
|
||||||
theme.theme_name = 'MyBeautifulTheme '
|
theme.theme_name = 'MyBeautifulTheme'
|
||||||
theme.background_filename = ' video.mp4'
|
theme.background_filename = Path('video.mp4')
|
||||||
theme.background_type = 'video'
|
theme.background_type = 'video'
|
||||||
path = os.path.expanduser('~')
|
path = Path.home()
|
||||||
|
|
||||||
# WHEN: Theme.extend_image_filename is run
|
# WHEN: Theme.extend_image_filename is run
|
||||||
theme.extend_image_filename(path)
|
theme.extend_image_filename(path)
|
||||||
|
|
||||||
# THEN: The filename of the background should be correct
|
# 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(expected_filename, theme.background_filename)
|
||||||
self.assertEqual('MyBeautifulTheme', theme.theme_name)
|
self.assertEqual('MyBeautifulTheme', theme.theme_name)
|
||||||
|
|
||||||
|
@ -22,11 +22,10 @@
|
|||||||
"""
|
"""
|
||||||
Package to test the openlp.core.ui.exeptionform package.
|
Package to test the openlp.core.ui.exeptionform package.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from unittest import TestCase
|
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 import Registry
|
||||||
from openlp.core.common.path import Path
|
from openlp.core.common.path import Path
|
||||||
@ -142,15 +141,15 @@ class TestExceptionForm(TestMixin, TestCase):
|
|||||||
test_form = exceptionform.ExceptionForm()
|
test_form = exceptionform.ExceptionForm()
|
||||||
test_form.file_attachment = None
|
test_form.file_attachment = None
|
||||||
|
|
||||||
with patch.object(test_form, '_pyuno_import') as mock_pyuno:
|
with patch.object(test_form, '_pyuno_import') as mock_pyuno, \
|
||||||
with patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback:
|
patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback, \
|
||||||
with patch.object(test_form.description_text_edit, 'toPlainText') as mock_description:
|
patch.object(test_form.description_text_edit, 'toPlainText') as mock_description:
|
||||||
mock_pyuno.return_value = 'UNO Bridge Test'
|
mock_pyuno.return_value = 'UNO Bridge Test'
|
||||||
mock_traceback.return_value = 'openlp: Traceback Test'
|
mock_traceback.return_value = 'openlp: Traceback Test'
|
||||||
mock_description.return_value = 'Description Test'
|
mock_description.return_value = 'Description Test'
|
||||||
|
|
||||||
# WHEN: on_save_report_button_clicked called
|
# WHEN: on_save_report_button_clicked called
|
||||||
test_form.on_send_report_button_clicked()
|
test_form.on_send_report_button_clicked()
|
||||||
|
|
||||||
# THEN: Verify strings were formatted properly
|
# THEN: Verify strings were formatted properly
|
||||||
mocked_add_query_item.assert_called_with('body', MAIL_ITEM_TEXT)
|
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_qt.PYQT_VERSION_STR = 'PyQt5 Test'
|
||||||
mocked_is_linux.return_value = False
|
mocked_is_linux.return_value = False
|
||||||
mocked_application_version.return_value = 'Trunk Test'
|
mocked_application_version.return_value = 'Trunk Test'
|
||||||
mocked_save_filename.return_value = (Path('testfile.txt'), 'filter')
|
|
||||||
|
|
||||||
test_form = exceptionform.ExceptionForm()
|
with patch.object(Path, 'open') as mocked_path_open:
|
||||||
test_form.file_attachment = None
|
x = Path('testfile.txt')
|
||||||
|
mocked_save_filename.return_value = x, 'ext'
|
||||||
|
|
||||||
with patch.object(test_form, '_pyuno_import') as mock_pyuno:
|
test_form = exceptionform.ExceptionForm()
|
||||||
with patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback:
|
test_form.file_attachment = None
|
||||||
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'
|
|
||||||
|
|
||||||
# WHEN: on_save_report_button_clicked called
|
with patch.object(test_form, '_pyuno_import') as mock_pyuno, \
|
||||||
test_form.on_save_report_button_clicked()
|
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
|
# THEN: Verify proper calls to save file
|
||||||
# self.maxDiff = None
|
# self.maxDiff = None
|
||||||
check_text = "call().write({text})".format(text=MAIL_ITEM_TEXT.__repr__())
|
mocked_path_open.assert_has_calls([call().__enter__().write(MAIL_ITEM_TEXT)])
|
||||||
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")
|
|
||||||
|
@ -27,10 +27,10 @@ from unittest.mock import MagicMock, patch
|
|||||||
|
|
||||||
from PyQt5 import QtCore
|
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.lib import ScreenList, PluginManager
|
||||||
from openlp.core.ui import MainDisplay, AudioPlayer
|
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 openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
|
||||||
|
|
||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
@ -184,7 +184,7 @@ class TestMainDisplay(TestCase, TestMixin):
|
|||||||
self.assertEqual(pyobjc_nsview.window().collectionBehavior(), NSWindowCollectionBehaviorManaged,
|
self.assertEqual(pyobjc_nsview.window().collectionBehavior(), NSWindowCollectionBehaviorManaged,
|
||||||
'Window collection behavior should be 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):
|
def test_show_display_startup_logo(self, MockedSettings):
|
||||||
# GIVEN: Mocked show_display, setting for logo visibility
|
# GIVEN: Mocked show_display, setting for logo visibility
|
||||||
display = MagicMock()
|
display = MagicMock()
|
||||||
@ -204,7 +204,7 @@ class TestMainDisplay(TestCase, TestMixin):
|
|||||||
# THEN: setVisible should had been called with "True"
|
# THEN: setVisible should had been called with "True"
|
||||||
main_display.setVisible.assert_called_once_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):
|
def test_show_display_hide_startup_logo(self, MockedSettings):
|
||||||
# GIVEN: Mocked show_display, setting for logo visibility
|
# GIVEN: Mocked show_display, setting for logo visibility
|
||||||
display = MagicMock()
|
display = MagicMock()
|
||||||
@ -224,8 +224,8 @@ class TestMainDisplay(TestCase, TestMixin):
|
|||||||
# THEN: setVisible should had not been called
|
# THEN: setVisible should had not been called
|
||||||
main_display.setVisible.assert_not_called()
|
main_display.setVisible.assert_not_called()
|
||||||
|
|
||||||
@patch(u'openlp.core.ui.maindisplay.Settings')
|
@patch('openlp.core.ui.maindisplay.Settings')
|
||||||
@patch(u'openlp.core.ui.maindisplay.build_html')
|
@patch('openlp.core.ui.maindisplay.build_html')
|
||||||
def test_build_html_no_video(self, MockedSettings, Mocked_build_html):
|
def test_build_html_no_video(self, MockedSettings, Mocked_build_html):
|
||||||
# GIVEN: Mocked display
|
# GIVEN: Mocked display
|
||||||
display = MagicMock()
|
display = MagicMock()
|
||||||
@ -252,8 +252,8 @@ class TestMainDisplay(TestCase, TestMixin):
|
|||||||
self.assertEquals(main_display.media_controller.video.call_count, 0,
|
self.assertEquals(main_display.media_controller.video.call_count, 0,
|
||||||
'Media Controller video should not have been called')
|
'Media Controller video should not have been called')
|
||||||
|
|
||||||
@patch(u'openlp.core.ui.maindisplay.Settings')
|
@patch('openlp.core.ui.maindisplay.Settings')
|
||||||
@patch(u'openlp.core.ui.maindisplay.build_html')
|
@patch('openlp.core.ui.maindisplay.build_html')
|
||||||
def test_build_html_video(self, MockedSettings, Mocked_build_html):
|
def test_build_html_video(self, MockedSettings, Mocked_build_html):
|
||||||
# GIVEN: Mocked display
|
# GIVEN: Mocked display
|
||||||
display = MagicMock()
|
display = MagicMock()
|
||||||
@ -270,7 +270,7 @@ class TestMainDisplay(TestCase, TestMixin):
|
|||||||
service_item.theme_data = MagicMock()
|
service_item.theme_data = MagicMock()
|
||||||
service_item.theme_data.background_type = 'video'
|
service_item.theme_data.background_type = 'video'
|
||||||
service_item.theme_data.theme_name = 'name'
|
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()
|
mocked_plugin = MagicMock()
|
||||||
display.plugin_manager = PluginManager()
|
display.plugin_manager = PluginManager()
|
||||||
display.plugin_manager.plugins = [mocked_plugin]
|
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'))
|
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
|
# 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()
|
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 PyQt5 import QtWidgets
|
||||||
|
|
||||||
from openlp.core.ui import ThemeManager
|
|
||||||
from openlp.core.common import Registry
|
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
|
from tests.utils.constants import TEST_RESOURCES_PATH
|
||||||
|
|
||||||
@ -57,13 +58,13 @@ class TestThemeManager(TestCase):
|
|||||||
"""
|
"""
|
||||||
# GIVEN: A new ThemeManager instance.
|
# GIVEN: A new ThemeManager instance.
|
||||||
theme_manager = ThemeManager()
|
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, \
|
with patch('zipfile.ZipFile.__init__') as mocked_zipfile_init, \
|
||||||
patch('zipfile.ZipFile.write') as mocked_zipfile_write:
|
patch('zipfile.ZipFile.write') as mocked_zipfile_write:
|
||||||
mocked_zipfile_init.return_value = None
|
mocked_zipfile_init.return_value = None
|
||||||
|
|
||||||
# WHEN: The theme is exported
|
# 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
|
# THEN: The zipfile should be created at the given path
|
||||||
mocked_zipfile_init.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w')
|
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
|
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.
|
# theme, check_directory_exists and thememanager-attributes.
|
||||||
with patch('builtins.open') as mocked_open, \
|
with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \
|
||||||
patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
|
|
||||||
patch('openlp.core.ui.thememanager.check_directory_exists'):
|
patch('openlp.core.ui.thememanager.check_directory_exists'):
|
||||||
mocked_open.return_value = MagicMock()
|
|
||||||
theme_manager = ThemeManager(None)
|
theme_manager = ThemeManager(None)
|
||||||
theme_manager.old_background_image = None
|
theme_manager.old_background_image = None
|
||||||
theme_manager.generate_and_save_image = MagicMock()
|
theme_manager.generate_and_save_image = MagicMock()
|
||||||
theme_manager.path = ''
|
theme_manager.theme_path = MagicMock()
|
||||||
mocked_theme = MagicMock()
|
mocked_theme = MagicMock()
|
||||||
mocked_theme.theme_name = 'themename'
|
mocked_theme.theme_name = 'themename'
|
||||||
mocked_theme.extract_formatted_xml = MagicMock()
|
mocked_theme.extract_formatted_xml = MagicMock()
|
||||||
mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
|
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
|
# 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')
|
file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
|
||||||
# Do replacement from end of string to avoid problems with path start
|
theme_manager._write_theme(mocked_theme, file_name1, file_name1)
|
||||||
file_name2 = file_name1[::-1].replace(os.sep, os.sep + os.sep, 2)[::-1]
|
|
||||||
theme_manager._write_theme(mocked_theme, file_name1, file_name2)
|
|
||||||
|
|
||||||
# THEN: The mocked_copyfile should not have been called
|
# 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):
|
def test_write_theme_diff_images(self):
|
||||||
"""
|
"""
|
||||||
Test that we do overwrite a theme background image when a new is submitted
|
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.
|
# theme, check_directory_exists and thememanager-attributes.
|
||||||
with patch('builtins.open') as mocked_open, \
|
with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \
|
||||||
patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
|
|
||||||
patch('openlp.core.ui.thememanager.check_directory_exists'):
|
patch('openlp.core.ui.thememanager.check_directory_exists'):
|
||||||
mocked_open.return_value = MagicMock()
|
|
||||||
theme_manager = ThemeManager(None)
|
theme_manager = ThemeManager(None)
|
||||||
theme_manager.old_background_image = None
|
theme_manager.old_background_image = None
|
||||||
theme_manager.generate_and_save_image = MagicMock()
|
theme_manager.generate_and_save_image = MagicMock()
|
||||||
theme_manager.path = ''
|
theme_manager.theme_path = MagicMock()
|
||||||
mocked_theme = MagicMock()
|
mocked_theme = MagicMock()
|
||||||
mocked_theme.theme_name = 'themename'
|
mocked_theme.theme_name = 'themename'
|
||||||
mocked_theme.filename = "filename"
|
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
|
# WHEN: Calling _write_theme with path to different images
|
||||||
file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg')
|
file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
|
||||||
file_name2 = os.path.join(TEST_RESOURCES_PATH, 'church2.jpg')
|
file_name2 = Path(TEST_RESOURCES_PATH, 'church2.jpg')
|
||||||
theme_manager._write_theme(mocked_theme, file_name1, file_name2)
|
theme_manager._write_theme(mocked_theme, file_name1, file_name2)
|
||||||
|
|
||||||
# THEN: The mocked_copyfile should not have been called
|
# 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):
|
def test_write_theme_special_char_name(self):
|
||||||
"""
|
"""
|
||||||
@ -146,7 +139,7 @@ class TestThemeManager(TestCase):
|
|||||||
theme_manager = ThemeManager(None)
|
theme_manager = ThemeManager(None)
|
||||||
theme_manager.old_background_image = None
|
theme_manager.old_background_image = None
|
||||||
theme_manager.generate_and_save_image = MagicMock()
|
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 = MagicMock()
|
||||||
mocked_theme.theme_name = 'theme 愛 name'
|
mocked_theme.theme_name = 'theme 愛 name'
|
||||||
mocked_theme.export_theme.return_value = "{}"
|
mocked_theme.export_theme.return_value = "{}"
|
||||||
@ -208,17 +201,17 @@ class TestThemeManager(TestCase):
|
|||||||
theme_manager = ThemeManager(None)
|
theme_manager = ThemeManager(None)
|
||||||
theme_manager._create_theme_from_xml = MagicMock()
|
theme_manager._create_theme_from_xml = MagicMock()
|
||||||
theme_manager.generate_and_save_image = MagicMock()
|
theme_manager.generate_and_save_image = MagicMock()
|
||||||
theme_manager.path = ''
|
theme_manager.theme_path = None
|
||||||
folder = mkdtemp()
|
folder = Path(mkdtemp())
|
||||||
theme_file = os.path.join(TEST_RESOURCES_PATH, 'themes', 'Moss_on_tree.otz')
|
theme_file = Path(TEST_RESOURCES_PATH, 'themes', 'Moss_on_tree.otz')
|
||||||
|
|
||||||
# WHEN: We try to unzip it
|
# WHEN: We try to unzip it
|
||||||
theme_manager.unzip_theme(theme_file, folder)
|
theme_manager.unzip_theme(theme_file, folder)
|
||||||
|
|
||||||
# THEN: Files should be unpacked
|
# 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')
|
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):
|
def test_unzip_theme_invalid_version(self):
|
||||||
"""
|
"""
|
||||||
|
@ -26,7 +26,8 @@ from unittest import TestCase
|
|||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
from openlp.core.common import Registry, Settings
|
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
|
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, \
|
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'
|
'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):
|
def test_click_on_new_theme(self):
|
||||||
"""
|
"""
|
||||||
Test the on_add_theme event handler is called by the UI
|
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.themeform.ThemeForm._setup')
|
||||||
@patch('openlp.core.ui.filerenameform.FileRenameForm._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.
|
Test the functions of bootstrap_post_setup are called.
|
||||||
"""
|
"""
|
||||||
# GIVEN:
|
# GIVEN:
|
||||||
self.theme_manager.load_themes = MagicMock()
|
self.theme_manager.load_themes = MagicMock()
|
||||||
self.theme_manager.path = MagicMock()
|
self.theme_manager.theme_path = MagicMock()
|
||||||
|
|
||||||
# WHEN:
|
# WHEN:
|
||||||
self.theme_manager.bootstrap_post_set_up()
|
self.theme_manager.bootstrap_post_set_up()
|
||||||
|
|
||||||
# THEN:
|
# 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")
|
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