Merge lp:~tomasgroth/openlp/fix-theme-thumb into lp:openlp

Proposed by Tomas Groth
Status: Merged
Merged at revision: 2877
Proposed branch: lp:~tomasgroth/openlp/fix-theme-thumb
Merge into: lp:openlp
Diff against target: 541 lines (+148/-99)
9 files modified
openlp/core/display/render.py (+70/-27)
openlp/core/ui/media/vlcplayer.py (+14/-13)
openlp/core/ui/themeform.py (+20/-15)
openlp/core/ui/thememanager.py (+12/-14)
openlp/core/ui/themewizard.py (+9/-10)
openlp/plugins/media/lib/mediaitem.py (+10/-9)
openlp/plugins/songs/lib/songstab.py (+1/-1)
tests/functional/openlp_core/ui/test_thememanager.py (+9/-9)
tests/openlp_core/ui/test_themeform.py (+3/-1)
To merge this branch: bzr merge lp:~tomasgroth/openlp/fix-theme-thumb
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Raoul Snyman Pending
Review via email: mp+368596@code.launchpad.net

This proposal supersedes a proposal from 2019-06-09.

Commit message

Add a webengine view for previewing themes.
Made VLC loading more robust.
A few minor fixes.

To post a comment you must log in.
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/176/ for more details

Revision history for this message
Tim Bentley (trb143) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

I like it, but you'll just need to fix the tests.

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/177/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linting failed, please see https://ci.openlp.io/job/MP-03-Linting/116/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linting passed!

Revision history for this message
Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/display/render.py'
2--- openlp/core/display/render.py 2019-04-13 13:00:22 +0000
3+++ openlp/core/display/render.py 2019-06-09 20:23:18 +0000
4@@ -24,6 +24,7 @@
5 """
6 import html
7 import logging
8+import mako
9 import math
10 import os
11 import re
12@@ -32,8 +33,10 @@
13 from PyQt5 import QtWidgets, QtGui
14
15 from openlp.core.common import ThemeLevel
16+from openlp.core.common.i18n import translate
17 from openlp.core.common.mixins import LogMixin, RegistryProperties
18 from openlp.core.common.registry import Registry, RegistryBase
19+from openlp.core.common.settings import Settings
20 from openlp.core.display.screens import ScreenList
21 from openlp.core.display.window import DisplayWindow
22 from openlp.core.lib import ItemCapabilities
23@@ -58,8 +61,10 @@
24 '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
25 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
26 VERSE_FOR_LINE_COUNT = '\n'.join(map(str, range(100)))
27-TITLE = 'Arky Arky (Unknown)'
28-FOOTER = ['Public Domain', 'CCLI 123456']
29+TITLE = 'Arky Arky'
30+AUTHOR = 'John Doe'
31+FOOTER_COPYRIGHT = 'Public Domain'
32+CCLI_NO = '123456'
33
34
35 def remove_tags(text, can_remove_chords=False):
36@@ -425,7 +430,7 @@
37 return raw_text + ''.join(end_tags), ''.join(start_tags), ''.join(html_tags)
38
39
40-class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow):
41+class ThemePreviewRenderer(LogMixin, DisplayWindow):
42 """
43 A virtual display used for rendering thumbnails and other offscreen tasks
44 """
45@@ -435,24 +440,6 @@
46 """
47 super().__init__(*args, **kwargs)
48 self.force_page = False
49- for screen in ScreenList():
50- if screen.is_display:
51- self.setGeometry(screen.display_geometry.x(), screen.display_geometry.y(),
52- screen.display_geometry.width(), screen.display_geometry.height())
53- break
54- # If the display is not show'ed and hidden like this webegine will not render
55- self.show()
56- self.hide()
57- self.theme_height = 0
58- self.theme_level = ThemeLevel.Global
59-
60- def set_theme_level(self, theme_level):
61- """
62- Sets the theme level.
63-
64- :param theme_level: The theme level to be used.
65- """
66- self.theme_level = theme_level
67
68 def calculate_line_count(self):
69 """
70@@ -466,7 +453,30 @@
71 """
72 return self.run_javascript('Display.clearSlides();')
73
74- def generate_preview(self, theme_data, force_page=False):
75+ def generate_footer(self):
76+ """
77+ """
78+ footer_template = Settings().value('songs/footer template')
79+ # Keep this in sync with the list in songstab.py
80+ vars = {
81+ 'title': TITLE,
82+ 'authors_none_label': translate('OpenLP.Ui', 'Written by'),
83+ 'authors_words_label': translate('SongsPlugin.AuthorType', 'Words',
84+ 'Author who wrote the lyrics of a song'),
85+ 'authors_words': [AUTHOR],
86+ 'copyright': FOOTER_COPYRIGHT,
87+ 'ccli_license': Settings().value('core/ccli number'),
88+ 'ccli_license_label': translate('SongsPlugin.MediaItem', 'CCLI License'),
89+ 'ccli_number': CCLI_NO,
90+ }
91+ try:
92+ footer_html = mako.template.Template(footer_template).render_unicode(**vars).replace('\n', '')
93+ except mako.exceptions.SyntaxException:
94+ log.error('Failed to render Song footer html:\n' + mako.exceptions.text_error_template().render())
95+ footer_html = 'Dummy footer text'
96+ return footer_html
97+
98+ def generate_preview(self, theme_data, force_page=False, generate_screenshot=True):
99 """
100 Generate a preview of a theme.
101
102@@ -479,14 +489,16 @@
103 if not self.force_page:
104 self.set_theme(theme_data)
105 self.theme_height = theme_data.font_main_height
106- slides = self.format_slide(render_tags(VERSE), None)
107+ slides = self.format_slide(VERSE, None)
108 verses = dict()
109 verses['title'] = TITLE
110- verses['text'] = slides[0]
111+ verses['text'] = render_tags(slides[0])
112 verses['verse'] = 'V1'
113+ verses['footer'] = self.generate_footer()
114 self.load_verses([verses])
115 self.force_page = False
116- return self.save_screenshot()
117+ if generate_screenshot:
118+ return self.save_screenshot()
119 self.force_page = False
120 return None
121
122@@ -515,7 +527,7 @@
123 if item and item.is_capable(ItemCapabilities.CanWordSplit):
124 pages = self._paginate_slide_words(text.split('\n'), line_end)
125 # Songs and Custom
126- elif item is None or item.is_capable(ItemCapabilities.CanSoftBreak):
127+ elif item is None or (item and item.is_capable(ItemCapabilities.CanSoftBreak)):
128 pages = []
129 if '[---]' in text:
130 # Remove Overflow split if at start of the text
131@@ -722,7 +734,8 @@
132 :param text: The text to check. It may contain HTML tags.
133 """
134 self.clear_slides()
135- self.run_javascript('Display.addTextSlide("v1", "{text}", "Dummy Footer");'.format(text=text), is_sync=True)
136+ self.run_javascript('Display.addTextSlide("v1", "{text}", "Dummy Footer");'
137+ .format(text=text.replace('"', '\\"')), is_sync=True)
138 does_text_fits = self.run_javascript('Display.doesContentFit();', is_sync=True)
139 return does_text_fits
140
141@@ -745,3 +758,33 @@
142 pixmap.save(fname, ext)
143 else:
144 return pixmap
145+
146+
147+class Renderer(RegistryBase, RegistryProperties, ThemePreviewRenderer):
148+ """
149+ A virtual display used for rendering thumbnails and other offscreen tasks
150+ """
151+ def __init__(self, *args, **kwargs):
152+ """
153+ Constructor
154+ """
155+ super().__init__(*args, **kwargs)
156+ self.force_page = False
157+ for screen in ScreenList():
158+ if screen.is_display:
159+ self.setGeometry(screen.display_geometry.x(), screen.display_geometry.y(),
160+ screen.display_geometry.width(), screen.display_geometry.height())
161+ break
162+ # If the display is not show'ed and hidden like this webegine will not render
163+ self.show()
164+ self.hide()
165+ self.theme_height = 0
166+ self.theme_level = ThemeLevel.Global
167+
168+ def set_theme_level(self, theme_level):
169+ """
170+ Sets the theme level.
171+
172+ :param theme_level: The theme level to be used.
173+ """
174+ self.theme_level = theme_level
175
176=== modified file 'openlp/core/ui/media/vlcplayer.py'
177--- openlp/core/ui/media/vlcplayer.py 2019-05-31 20:19:15 +0000
178+++ openlp/core/ui/media/vlcplayer.py 2019-06-09 20:23:18 +0000
179@@ -28,7 +28,6 @@
180 import sys
181 import threading
182 from datetime import datetime
183-import vlc
184
185 from PyQt5 import QtWidgets
186
187@@ -62,25 +61,27 @@
188
189 :return: The "vlc" module, or None
190 """
191- if 'vlc' in sys.modules:
192- # If VLC has already been imported, no need to do all the stuff below again
193- is_vlc_available = False
194+ # Import the VLC module if not already done
195+ if 'vlc' not in sys.modules:
196 try:
197- is_vlc_available = bool(sys.modules['vlc'].get_default_instance())
198- except Exception:
199- pass
200- if is_vlc_available:
201- return sys.modules['vlc']
202- else:
203+ import vlc # noqa module is not used directly, but is used via sys.modules['vlc']
204+ except ImportError:
205 return None
206- else:
207- return vlc
208+ # Verify that VLC is also loadable
209+ is_vlc_available = False
210+ try:
211+ is_vlc_available = bool(sys.modules['vlc'].get_default_instance())
212+ except Exception:
213+ pass
214+ if is_vlc_available:
215+ return sys.modules['vlc']
216+ return None
217
218
219 # On linux we need to initialise X threads, but not when running tests.
220 # This needs to happen on module load and not in get_vlc(), otherwise it can cause crashes on some DE on some setups
221 # (reported on Gnome3, Unity, Cinnamon, all GTK+ based) when using native filedialogs...
222-if is_linux() and 'nose' not in sys.argv[0] and get_vlc():
223+if is_linux() and 'pytest' not in sys.argv[0] and get_vlc():
224 try:
225 try:
226 x11 = ctypes.cdll.LoadLibrary('libX11.so.6')
227
228=== modified file 'openlp/core/ui/themeform.py'
229--- openlp/core/ui/themeform.py 2019-05-26 20:53:54 +0000
230+++ openlp/core/ui/themeform.py 2019-06-09 20:23:18 +0000
231@@ -172,16 +172,14 @@
232 if not event:
233 event = QtGui.QResizeEvent(self.size(), self.size())
234 QtWidgets.QWizard.resizeEvent(self, event)
235- if hasattr(self, 'preview_page') and self.currentPage() == self.preview_page:
236- frame_width = self.preview_box_label.lineWidth()
237- pixmap_width = self.preview_area.width() - 2 * frame_width
238- pixmap_height = self.preview_area.height() - 2 * frame_width
239- aspect_ratio = float(pixmap_width) / pixmap_height
240- if aspect_ratio < self.display_aspect_ratio:
241- pixmap_height = int(pixmap_width / self.display_aspect_ratio + 0.5)
242- else:
243- pixmap_width = int(pixmap_height * self.display_aspect_ratio + 0.5)
244- self.preview_box_label.setFixedSize(pixmap_width + 2 * frame_width, pixmap_height + 2 * frame_width)
245+ try:
246+ self.display_aspect_ratio = self.renderer.width() / self.renderer.height()
247+ except ZeroDivisionError:
248+ self.display_aspect_ratio = 1
249+ # Make sure we don't resize before the widgets are actually created
250+ if hasattr(self, 'preview_area_layout'):
251+ self.preview_area_layout.set_aspect_ratio(self.display_aspect_ratio)
252+ self.preview_box.set_scale(float(self.preview_box.width()) / self.renderer.width())
253
254 def validateCurrentPage(self):
255 """
256@@ -206,11 +204,17 @@
257 self.setOption(QtWidgets.QWizard.HaveCustomButton1, enabled)
258 if self.page(page_id) == self.preview_page:
259 self.update_theme()
260- frame = self.theme_manager.generate_image(self.theme)
261- frame.setDevicePixelRatio(self.devicePixelRatio())
262- self.preview_box_label.setPixmap(frame)
263- self.display_aspect_ratio = float(frame.width()) / frame.height()
264+ self.preview_box.set_theme(self.theme)
265+ self.preview_box.clear_slides()
266+ self.preview_box.set_scale(float(self.preview_box.width()) / self.renderer.width())
267+ try:
268+ self.display_aspect_ratio = self.renderer.width() / self.renderer.height()
269+ except ZeroDivisionError:
270+ self.display_aspect_ratio = 1
271+ self.preview_area_layout.set_aspect_ratio(self.display_aspect_ratio)
272 self.resizeEvent()
273+ self.preview_box.show()
274+ self.preview_box.generate_preview(self.theme, False, False)
275
276 def on_custom_1_button_clicked(self, number):
277 """
278@@ -398,6 +402,7 @@
279 Handle the display and state of the Preview page.
280 """
281 self.setField('name', self.theme.theme_name)
282+ self.preview_box.set_theme(self.theme)
283
284 def on_background_combo_box_current_index_changed(self, index):
285 """
286@@ -558,5 +563,5 @@
287 source_path = self.theme.background_filename
288 if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):
289 return
290- self.theme_manager.save_theme(self.theme, source_path, destination_path)
291+ self.theme_manager.save_theme(self.theme, source_path, destination_path, self.preview_box.save_screenshot())
292 return QtWidgets.QDialog.accept(self)
293
294=== modified file 'openlp/core/ui/thememanager.py'
295--- openlp/core/ui/thememanager.py 2019-05-22 06:47:00 +0000
296+++ openlp/core/ui/thememanager.py 2019-06-09 20:23:18 +0000
297@@ -476,7 +476,7 @@
298 if not theme_paths:
299 theme = Theme()
300 theme.theme_name = UiStrings().Default
301- self._write_theme(theme)
302+ self.save_theme(theme)
303 Settings().setValue(self.settings_section + '/global theme', theme.theme_name)
304 self.application.set_normal_cursor()
305
306@@ -639,24 +639,14 @@
307 return False
308 return True
309
310- def save_theme(self, theme, image_source_path, image_destination_path):
311- """
312- Called by theme maintenance Dialog to save the theme and to trigger the reload of the theme list
313-
314- :param Theme theme: The theme data object.
315- :param Path image_source_path: Where the theme image is currently located.
316- :param Path image_destination_path: Where the Theme Image is to be saved to
317- :rtype: None
318- """
319- self._write_theme(theme, image_source_path, image_destination_path)
320-
321- def _write_theme(self, theme, image_source_path=None, image_destination_path=None):
322+ def save_theme(self, theme, image_source_path=None, image_destination_path=None, image=None):
323 """
324 Writes the theme to the disk and handles the background image if necessary
325
326 :param Theme theme: The theme data object.
327 :param Path image_source_path: Where the theme image is currently located.
328 :param Path image_destination_path: Where the Theme Image is to be saved to
329+ :param image: The example image of the theme. Optionally.
330 :rtype: None
331 """
332 name = theme.theme_name
333@@ -676,7 +666,15 @@
334 shutil.copyfile(image_source_path, image_destination_path)
335 except OSError:
336 self.log_exception('Failed to save theme image')
337- self.generate_and_save_image(name, theme)
338+ if image:
339+ sample_path_name = self.theme_path / '{file_name}.png'.format(file_name=name)
340+ if sample_path_name.exists():
341+ sample_path_name.unlink()
342+ image.save(str(sample_path_name), 'png')
343+ thumb_path = self.thumb_path / '{name}.png'.format(name=name)
344+ create_thumb(sample_path_name, thumb_path, False)
345+ else:
346+ self.generate_and_save_image(name, theme)
347
348 def generate_and_save_image(self, theme_name, theme):
349 """
350
351=== modified file 'openlp/core/ui/themewizard.py'
352--- openlp/core/ui/themewizard.py 2019-04-13 13:00:22 +0000
353+++ openlp/core/ui/themewizard.py 2019-06-09 20:23:18 +0000
354@@ -31,6 +31,8 @@
355 from openlp.core.ui.icons import UiIcons
356 from openlp.core.widgets.buttons import ColorButton
357 from openlp.core.widgets.edits import PathEdit
358+from openlp.core.widgets.layouts import AspectRatioLayout
359+from openlp.core.display.render import ThemePreviewRenderer
360
361
362 class Ui_ThemeWizard(object):
363@@ -363,16 +365,13 @@
364 self.preview_layout.addLayout(self.theme_name_layout)
365 self.preview_area = QtWidgets.QWidget(self.preview_page)
366 self.preview_area.setObjectName('PreviewArea')
367- self.preview_area_layout = QtWidgets.QGridLayout(self.preview_area)
368- self.preview_area_layout.setContentsMargins(0, 0, 0, 0)
369- self.preview_area_layout.setColumnStretch(0, 1)
370- self.preview_area_layout.setRowStretch(0, 1)
371- self.preview_area_layout.setObjectName('preview_area_layout')
372- self.preview_box_label = QtWidgets.QLabel(self.preview_area)
373- self.preview_box_label.setFrameShape(QtWidgets.QFrame.Box)
374- self.preview_box_label.setScaledContents(True)
375- self.preview_box_label.setObjectName('preview_box_label')
376- self.preview_area_layout.addWidget(self.preview_box_label)
377+ self.preview_area_layout = AspectRatioLayout(self.preview_area, 0.75) # Dummy ratio, will be update
378+ self.preview_area_layout.margin = 8
379+ self.preview_area_layout.setSpacing(0)
380+ self.preview_area_layout.setObjectName('preview_web_layout')
381+ self.preview_box = ThemePreviewRenderer(self)
382+ self.preview_box.setObjectName('preview_box')
383+ self.preview_area_layout.addWidget(self.preview_box)
384 self.preview_layout.addWidget(self.preview_area)
385 theme_wizard.addPage(self.preview_page)
386 self.retranslate_ui(theme_wizard)
387
388=== modified file 'openlp/plugins/media/lib/mediaitem.py'
389--- openlp/plugins/media/lib/mediaitem.py 2019-06-01 06:59:45 +0000
390+++ openlp/plugins/media/lib/mediaitem.py 2019-06-09 20:23:18 +0000
391@@ -173,7 +173,7 @@
392 item = self.list_view.currentItem()
393 if item is None:
394 return False
395- filename = item.data(QtCore.Qt.UserRole)
396+ filename = str(item.data(QtCore.Qt.UserRole))
397 # Special handling if the filename is a optical clip
398 if filename.startswith('optical:'):
399 (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(filename)
400@@ -259,11 +259,12 @@
401 # TODO needs to be fixed as no idea why this fails
402 # media.sort(key=lambda file_path: get_natural_key(file_path.name))
403 for track in media:
404- track_info = QtCore.QFileInfo(track)
405+ track_str = str(track)
406+ track_info = QtCore.QFileInfo(track_str)
407 item_name = None
408- if track.startswith('optical:'):
409+ if track_str.startswith('optical:'):
410 # Handle optical based item
411- (file_name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(track)
412+ (file_name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(track_str)
413 item_name = QtWidgets.QListWidgetItem(clip_name)
414 item_name.setIcon(UiIcons().optical)
415 item_name.setData(QtCore.Qt.UserRole, track)
416@@ -272,22 +273,22 @@
417 end=format_milliseconds(end)))
418 elif not os.path.exists(track):
419 # File doesn't exist, mark as error.
420- file_name = os.path.split(str(track))[1]
421+ file_name = os.path.split(track_str)[1]
422 item_name = QtWidgets.QListWidgetItem(file_name)
423 item_name.setIcon(UiIcons().error)
424 item_name.setData(QtCore.Qt.UserRole, track)
425- item_name.setToolTip(track)
426+ item_name.setToolTip(track_str)
427 elif track_info.isFile():
428 # Normal media file handling.
429- file_name = os.path.split(str(track))[1]
430+ file_name = os.path.split(track_str)[1]
431 item_name = QtWidgets.QListWidgetItem(file_name)
432 search = file_name.split('.')[-1].lower()
433- if '*.{text}'.format(text=search) in self.media_controller.audio_extensions_list:
434+ if search in AUDIO_EXT:
435 item_name.setIcon(UiIcons().audio)
436 else:
437 item_name.setIcon(UiIcons().video)
438 item_name.setData(QtCore.Qt.UserRole, track)
439- item_name.setToolTip(track)
440+ item_name.setToolTip(track_str)
441 if item_name:
442 self.list_view.addItem(item_name)
443
444
445=== modified file 'openlp/plugins/songs/lib/songstab.py'
446--- openlp/plugins/songs/lib/songstab.py 2019-04-13 13:00:22 +0000
447+++ openlp/plugins/songs/lib/songstab.py 2019-06-09 20:23:18 +0000
448@@ -88,7 +88,7 @@
449 self.footer_group_box = QtWidgets.QGroupBox(self.left_column)
450 self.footer_group_box.setObjectName('footer_group_box')
451 self.footer_layout = QtWidgets.QVBoxLayout(self.footer_group_box)
452- self.footer_layout.setObjectName('chords_layout')
453+ self.footer_layout.setObjectName('footer_layout')
454 self.footer_info_label = QtWidgets.QLabel(self.footer_group_box)
455 self.footer_layout.addWidget(self.footer_info_label)
456 self.footer_placeholder_info = QtWidgets.QTextEdit(self.footer_group_box)
457
458=== modified file 'tests/functional/openlp_core/ui/test_thememanager.py'
459--- tests/functional/openlp_core/ui/test_thememanager.py 2019-05-22 06:47:00 +0000
460+++ tests/functional/openlp_core/ui/test_thememanager.py 2019-06-09 20:23:18 +0000
461@@ -83,7 +83,7 @@
462
463 @patch('openlp.core.ui.thememanager.shutil')
464 @patch('openlp.core.ui.thememanager.create_paths')
465- def test_write_theme_same_image(self, mocked_create_paths, mocked_shutil):
466+ def test_save_theme_same_image(self, mocked_create_paths, mocked_shutil):
467 """
468 Test that we don't try to overwrite a theme background image with itself
469 """
470@@ -98,16 +98,16 @@
471 mocked_theme.extract_formatted_xml = MagicMock()
472 mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
473
474- # WHEN: Calling _write_theme with path to the same image, but the path written slightly different
475+ # WHEN: Calling save_theme with path to the same image, but the path written slightly different
476 file_path_1 = RESOURCE_PATH / 'church.jpg'
477- theme_manager._write_theme(mocked_theme, file_path_1, file_path_1)
478+ theme_manager.save_theme(mocked_theme, file_path_1, file_path_1)
479
480 # THEN: The mocked_copyfile should not have been called
481 assert mocked_shutil.copyfile.called is False, 'copyfile should not be called'
482
483 @patch('openlp.core.ui.thememanager.shutil')
484 @patch('openlp.core.ui.thememanager.create_paths')
485- def test_write_theme_diff_images(self, mocked_create_paths, mocked_shutil):
486+ def test_save_theme_diff_images(self, mocked_create_paths, mocked_shutil):
487 """
488 Test that we do overwrite a theme background image when a new is submitted
489 """
490@@ -121,15 +121,15 @@
491 mocked_theme.theme_name = 'themename'
492 mocked_theme.filename = "filename"
493
494- # WHEN: Calling _write_theme with path to different images
495+ # WHEN: Calling save_theme with path to different images
496 file_path_1 = RESOURCE_PATH / 'church.jpg'
497 file_path_2 = RESOURCE_PATH / 'church2.jpg'
498- theme_manager._write_theme(mocked_theme, file_path_1, file_path_2)
499+ theme_manager.save_theme(mocked_theme, file_path_1, file_path_2)
500
501 # THEN: The mocked_copyfile should not have been called
502 assert mocked_shutil.copyfile.called is True, 'copyfile should be called'
503
504- def test_write_theme_special_char_name(self):
505+ def test_save_theme_special_char_name(self):
506 """
507 Test that we can save themes with special characters in the name
508 """
509@@ -142,8 +142,8 @@
510 mocked_theme.theme_name = 'theme 愛 name'
511 mocked_theme.export_theme.return_value = "{}"
512
513- # WHEN: Calling _write_theme with a theme with a name with special characters in it
514- theme_manager._write_theme(mocked_theme)
515+ # WHEN: Calling save_theme with a theme with a name with special characters in it
516+ theme_manager.save_theme(mocked_theme)
517
518 # THEN: It should have been created
519 assert os.path.exists(os.path.join(self.temp_folder, 'theme 愛 name', 'theme 愛 name.json')) is True, \
520
521=== modified file 'tests/openlp_core/ui/test_themeform.py'
522--- tests/openlp_core/ui/test_themeform.py 2019-04-13 13:00:22 +0000
523+++ tests/openlp_core/ui/test_themeform.py 2019-06-09 20:23:18 +0000
524@@ -23,6 +23,7 @@
525 Interface tests to test the ThemeWizard class and related methods.
526 """
527 from unittest import TestCase
528+from unittest.mock import patch
529
530 from openlp.core.common.registry import Registry
531 from openlp.core.ui.themeform import ThemeForm
532@@ -39,7 +40,8 @@
533 """
534 Registry.create()
535
536- def test_create_theme_wizard(self):
537+ @patch('openlp.core.display.window.QtWidgets.QVBoxLayout')
538+ def test_create_theme_wizard(self, mocked_qvboxlayout):
539 """
540 Test creating a ThemeForm instance
541 """