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
=== modified file 'openlp/core/display/render.py'
--- openlp/core/display/render.py 2019-04-13 13:00:22 +0000
+++ openlp/core/display/render.py 2019-06-09 20:23:18 +0000
@@ -24,6 +24,7 @@
24"""24"""
25import html25import html
26import logging26import logging
27import mako
27import math28import math
28import os29import os
29import re30import re
@@ -32,8 +33,10 @@
32from PyQt5 import QtWidgets, QtGui33from PyQt5 import QtWidgets, QtGui
3334
34from openlp.core.common import ThemeLevel35from openlp.core.common import ThemeLevel
36from openlp.core.common.i18n import translate
35from openlp.core.common.mixins import LogMixin, RegistryProperties37from openlp.core.common.mixins import LogMixin, RegistryProperties
36from openlp.core.common.registry import Registry, RegistryBase38from openlp.core.common.registry import Registry, RegistryBase
39from openlp.core.common.settings import Settings
37from openlp.core.display.screens import ScreenList40from openlp.core.display.screens import ScreenList
38from openlp.core.display.window import DisplayWindow41from openlp.core.display.window import DisplayWindow
39from openlp.core.lib import ItemCapabilities42from openlp.core.lib import ItemCapabilities
@@ -58,8 +61,10 @@
58 '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \61 '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
59 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'62 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
60VERSE_FOR_LINE_COUNT = '\n'.join(map(str, range(100)))63VERSE_FOR_LINE_COUNT = '\n'.join(map(str, range(100)))
61TITLE = 'Arky Arky (Unknown)'64TITLE = 'Arky Arky'
62FOOTER = ['Public Domain', 'CCLI 123456']65AUTHOR = 'John Doe'
66FOOTER_COPYRIGHT = 'Public Domain'
67CCLI_NO = '123456'
6368
6469
65def remove_tags(text, can_remove_chords=False):70def remove_tags(text, can_remove_chords=False):
@@ -425,7 +430,7 @@
425 return raw_text + ''.join(end_tags), ''.join(start_tags), ''.join(html_tags)430 return raw_text + ''.join(end_tags), ''.join(start_tags), ''.join(html_tags)
426431
427432
428class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow):433class ThemePreviewRenderer(LogMixin, DisplayWindow):
429 """434 """
430 A virtual display used for rendering thumbnails and other offscreen tasks435 A virtual display used for rendering thumbnails and other offscreen tasks
431 """436 """
@@ -435,24 +440,6 @@
435 """440 """
436 super().__init__(*args, **kwargs)441 super().__init__(*args, **kwargs)
437 self.force_page = False442 self.force_page = False
438 for screen in ScreenList():
439 if screen.is_display:
440 self.setGeometry(screen.display_geometry.x(), screen.display_geometry.y(),
441 screen.display_geometry.width(), screen.display_geometry.height())
442 break
443 # If the display is not show'ed and hidden like this webegine will not render
444 self.show()
445 self.hide()
446 self.theme_height = 0
447 self.theme_level = ThemeLevel.Global
448
449 def set_theme_level(self, theme_level):
450 """
451 Sets the theme level.
452
453 :param theme_level: The theme level to be used.
454 """
455 self.theme_level = theme_level
456443
457 def calculate_line_count(self):444 def calculate_line_count(self):
458 """445 """
@@ -466,7 +453,30 @@
466 """453 """
467 return self.run_javascript('Display.clearSlides();')454 return self.run_javascript('Display.clearSlides();')
468455
469 def generate_preview(self, theme_data, force_page=False):456 def generate_footer(self):
457 """
458 """
459 footer_template = Settings().value('songs/footer template')
460 # Keep this in sync with the list in songstab.py
461 vars = {
462 'title': TITLE,
463 'authors_none_label': translate('OpenLP.Ui', 'Written by'),
464 'authors_words_label': translate('SongsPlugin.AuthorType', 'Words',
465 'Author who wrote the lyrics of a song'),
466 'authors_words': [AUTHOR],
467 'copyright': FOOTER_COPYRIGHT,
468 'ccli_license': Settings().value('core/ccli number'),
469 'ccli_license_label': translate('SongsPlugin.MediaItem', 'CCLI License'),
470 'ccli_number': CCLI_NO,
471 }
472 try:
473 footer_html = mako.template.Template(footer_template).render_unicode(**vars).replace('\n', '')
474 except mako.exceptions.SyntaxException:
475 log.error('Failed to render Song footer html:\n' + mako.exceptions.text_error_template().render())
476 footer_html = 'Dummy footer text'
477 return footer_html
478
479 def generate_preview(self, theme_data, force_page=False, generate_screenshot=True):
470 """480 """
471 Generate a preview of a theme.481 Generate a preview of a theme.
472482
@@ -479,14 +489,16 @@
479 if not self.force_page:489 if not self.force_page:
480 self.set_theme(theme_data)490 self.set_theme(theme_data)
481 self.theme_height = theme_data.font_main_height491 self.theme_height = theme_data.font_main_height
482 slides = self.format_slide(render_tags(VERSE), None)492 slides = self.format_slide(VERSE, None)
483 verses = dict()493 verses = dict()
484 verses['title'] = TITLE494 verses['title'] = TITLE
485 verses['text'] = slides[0]495 verses['text'] = render_tags(slides[0])
486 verses['verse'] = 'V1'496 verses['verse'] = 'V1'
497 verses['footer'] = self.generate_footer()
487 self.load_verses([verses])498 self.load_verses([verses])
488 self.force_page = False499 self.force_page = False
489 return self.save_screenshot()500 if generate_screenshot:
501 return self.save_screenshot()
490 self.force_page = False502 self.force_page = False
491 return None503 return None
492504
@@ -515,7 +527,7 @@
515 if item and item.is_capable(ItemCapabilities.CanWordSplit):527 if item and item.is_capable(ItemCapabilities.CanWordSplit):
516 pages = self._paginate_slide_words(text.split('\n'), line_end)528 pages = self._paginate_slide_words(text.split('\n'), line_end)
517 # Songs and Custom529 # Songs and Custom
518 elif item is None or item.is_capable(ItemCapabilities.CanSoftBreak):530 elif item is None or (item and item.is_capable(ItemCapabilities.CanSoftBreak)):
519 pages = []531 pages = []
520 if '[---]' in text:532 if '[---]' in text:
521 # Remove Overflow split if at start of the text533 # Remove Overflow split if at start of the text
@@ -722,7 +734,8 @@
722 :param text: The text to check. It may contain HTML tags.734 :param text: The text to check. It may contain HTML tags.
723 """735 """
724 self.clear_slides()736 self.clear_slides()
725 self.run_javascript('Display.addTextSlide("v1", "{text}", "Dummy Footer");'.format(text=text), is_sync=True)737 self.run_javascript('Display.addTextSlide("v1", "{text}", "Dummy Footer");'
738 .format(text=text.replace('"', '\\"')), is_sync=True)
726 does_text_fits = self.run_javascript('Display.doesContentFit();', is_sync=True)739 does_text_fits = self.run_javascript('Display.doesContentFit();', is_sync=True)
727 return does_text_fits740 return does_text_fits
728741
@@ -745,3 +758,33 @@
745 pixmap.save(fname, ext)758 pixmap.save(fname, ext)
746 else:759 else:
747 return pixmap760 return pixmap
761
762
763class Renderer(RegistryBase, RegistryProperties, ThemePreviewRenderer):
764 """
765 A virtual display used for rendering thumbnails and other offscreen tasks
766 """
767 def __init__(self, *args, **kwargs):
768 """
769 Constructor
770 """
771 super().__init__(*args, **kwargs)
772 self.force_page = False
773 for screen in ScreenList():
774 if screen.is_display:
775 self.setGeometry(screen.display_geometry.x(), screen.display_geometry.y(),
776 screen.display_geometry.width(), screen.display_geometry.height())
777 break
778 # If the display is not show'ed and hidden like this webegine will not render
779 self.show()
780 self.hide()
781 self.theme_height = 0
782 self.theme_level = ThemeLevel.Global
783
784 def set_theme_level(self, theme_level):
785 """
786 Sets the theme level.
787
788 :param theme_level: The theme level to be used.
789 """
790 self.theme_level = theme_level
748791
=== modified file 'openlp/core/ui/media/vlcplayer.py'
--- openlp/core/ui/media/vlcplayer.py 2019-05-31 20:19:15 +0000
+++ openlp/core/ui/media/vlcplayer.py 2019-06-09 20:23:18 +0000
@@ -28,7 +28,6 @@
28import sys28import sys
29import threading29import threading
30from datetime import datetime30from datetime import datetime
31import vlc
3231
33from PyQt5 import QtWidgets32from PyQt5 import QtWidgets
3433
@@ -62,25 +61,27 @@
6261
63 :return: The "vlc" module, or None62 :return: The "vlc" module, or None
64 """63 """
65 if 'vlc' in sys.modules:64 # Import the VLC module if not already done
66 # If VLC has already been imported, no need to do all the stuff below again65 if 'vlc' not in sys.modules:
67 is_vlc_available = False
68 try:66 try:
69 is_vlc_available = bool(sys.modules['vlc'].get_default_instance())67 import vlc # noqa module is not used directly, but is used via sys.modules['vlc']
70 except Exception:68 except ImportError:
71 pass
72 if is_vlc_available:
73 return sys.modules['vlc']
74 else:
75 return None69 return None
76 else:70 # Verify that VLC is also loadable
77 return vlc71 is_vlc_available = False
72 try:
73 is_vlc_available = bool(sys.modules['vlc'].get_default_instance())
74 except Exception:
75 pass
76 if is_vlc_available:
77 return sys.modules['vlc']
78 return None
7879
7980
80# On linux we need to initialise X threads, but not when running tests.81# On linux we need to initialise X threads, but not when running tests.
81# This needs to happen on module load and not in get_vlc(), otherwise it can cause crashes on some DE on some setups82# This needs to happen on module load and not in get_vlc(), otherwise it can cause crashes on some DE on some setups
82# (reported on Gnome3, Unity, Cinnamon, all GTK+ based) when using native filedialogs...83# (reported on Gnome3, Unity, Cinnamon, all GTK+ based) when using native filedialogs...
83if is_linux() and 'nose' not in sys.argv[0] and get_vlc():84if is_linux() and 'pytest' not in sys.argv[0] and get_vlc():
84 try:85 try:
85 try:86 try:
86 x11 = ctypes.cdll.LoadLibrary('libX11.so.6')87 x11 = ctypes.cdll.LoadLibrary('libX11.so.6')
8788
=== modified file 'openlp/core/ui/themeform.py'
--- openlp/core/ui/themeform.py 2019-05-26 20:53:54 +0000
+++ openlp/core/ui/themeform.py 2019-06-09 20:23:18 +0000
@@ -172,16 +172,14 @@
172 if not event:172 if not event:
173 event = QtGui.QResizeEvent(self.size(), self.size())173 event = QtGui.QResizeEvent(self.size(), self.size())
174 QtWidgets.QWizard.resizeEvent(self, event)174 QtWidgets.QWizard.resizeEvent(self, event)
175 if hasattr(self, 'preview_page') and self.currentPage() == self.preview_page:175 try:
176 frame_width = self.preview_box_label.lineWidth()176 self.display_aspect_ratio = self.renderer.width() / self.renderer.height()
177 pixmap_width = self.preview_area.width() - 2 * frame_width177 except ZeroDivisionError:
178 pixmap_height = self.preview_area.height() - 2 * frame_width178 self.display_aspect_ratio = 1
179 aspect_ratio = float(pixmap_width) / pixmap_height179 # Make sure we don't resize before the widgets are actually created
180 if aspect_ratio < self.display_aspect_ratio:180 if hasattr(self, 'preview_area_layout'):
181 pixmap_height = int(pixmap_width / self.display_aspect_ratio + 0.5)181 self.preview_area_layout.set_aspect_ratio(self.display_aspect_ratio)
182 else:182 self.preview_box.set_scale(float(self.preview_box.width()) / self.renderer.width())
183 pixmap_width = int(pixmap_height * self.display_aspect_ratio + 0.5)
184 self.preview_box_label.setFixedSize(pixmap_width + 2 * frame_width, pixmap_height + 2 * frame_width)
185183
186 def validateCurrentPage(self):184 def validateCurrentPage(self):
187 """185 """
@@ -206,11 +204,17 @@
206 self.setOption(QtWidgets.QWizard.HaveCustomButton1, enabled)204 self.setOption(QtWidgets.QWizard.HaveCustomButton1, enabled)
207 if self.page(page_id) == self.preview_page:205 if self.page(page_id) == self.preview_page:
208 self.update_theme()206 self.update_theme()
209 frame = self.theme_manager.generate_image(self.theme)207 self.preview_box.set_theme(self.theme)
210 frame.setDevicePixelRatio(self.devicePixelRatio())208 self.preview_box.clear_slides()
211 self.preview_box_label.setPixmap(frame)209 self.preview_box.set_scale(float(self.preview_box.width()) / self.renderer.width())
212 self.display_aspect_ratio = float(frame.width()) / frame.height()210 try:
211 self.display_aspect_ratio = self.renderer.width() / self.renderer.height()
212 except ZeroDivisionError:
213 self.display_aspect_ratio = 1
214 self.preview_area_layout.set_aspect_ratio(self.display_aspect_ratio)
213 self.resizeEvent()215 self.resizeEvent()
216 self.preview_box.show()
217 self.preview_box.generate_preview(self.theme, False, False)
214218
215 def on_custom_1_button_clicked(self, number):219 def on_custom_1_button_clicked(self, number):
216 """220 """
@@ -398,6 +402,7 @@
398 Handle the display and state of the Preview page.402 Handle the display and state of the Preview page.
399 """403 """
400 self.setField('name', self.theme.theme_name)404 self.setField('name', self.theme.theme_name)
405 self.preview_box.set_theme(self.theme)
401406
402 def on_background_combo_box_current_index_changed(self, index):407 def on_background_combo_box_current_index_changed(self, index):
403 """408 """
@@ -558,5 +563,5 @@
558 source_path = self.theme.background_filename563 source_path = self.theme.background_filename
559 if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):564 if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):
560 return565 return
561 self.theme_manager.save_theme(self.theme, source_path, destination_path)566 self.theme_manager.save_theme(self.theme, source_path, destination_path, self.preview_box.save_screenshot())
562 return QtWidgets.QDialog.accept(self)567 return QtWidgets.QDialog.accept(self)
563568
=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py 2019-05-22 06:47:00 +0000
+++ openlp/core/ui/thememanager.py 2019-06-09 20:23:18 +0000
@@ -476,7 +476,7 @@
476 if not theme_paths:476 if not theme_paths:
477 theme = Theme()477 theme = Theme()
478 theme.theme_name = UiStrings().Default478 theme.theme_name = UiStrings().Default
479 self._write_theme(theme)479 self.save_theme(theme)
480 Settings().setValue(self.settings_section + '/global theme', theme.theme_name)480 Settings().setValue(self.settings_section + '/global theme', theme.theme_name)
481 self.application.set_normal_cursor()481 self.application.set_normal_cursor()
482482
@@ -639,24 +639,14 @@
639 return False639 return False
640 return True640 return True
641641
642 def save_theme(self, theme, image_source_path, image_destination_path):642 def save_theme(self, theme, image_source_path=None, image_destination_path=None, image=None):
643 """
644 Called by theme maintenance Dialog to save the theme and to trigger the reload of the theme list
645
646 :param Theme theme: The theme data object.
647 :param Path image_source_path: Where the theme image is currently located.
648 :param Path image_destination_path: Where the Theme Image is to be saved to
649 :rtype: None
650 """
651 self._write_theme(theme, image_source_path, image_destination_path)
652
653 def _write_theme(self, theme, image_source_path=None, image_destination_path=None):
654 """643 """
655 Writes the theme to the disk and handles the background image if necessary644 Writes the theme to the disk and handles the background image if necessary
656645
657 :param Theme theme: The theme data object.646 :param Theme theme: The theme data object.
658 :param Path image_source_path: Where the theme image is currently located.647 :param Path image_source_path: Where the theme image is currently located.
659 :param Path image_destination_path: Where the Theme Image is to be saved to648 :param Path image_destination_path: Where the Theme Image is to be saved to
649 :param image: The example image of the theme. Optionally.
660 :rtype: None650 :rtype: None
661 """651 """
662 name = theme.theme_name652 name = theme.theme_name
@@ -676,7 +666,15 @@
676 shutil.copyfile(image_source_path, image_destination_path)666 shutil.copyfile(image_source_path, image_destination_path)
677 except OSError:667 except OSError:
678 self.log_exception('Failed to save theme image')668 self.log_exception('Failed to save theme image')
679 self.generate_and_save_image(name, theme)669 if image:
670 sample_path_name = self.theme_path / '{file_name}.png'.format(file_name=name)
671 if sample_path_name.exists():
672 sample_path_name.unlink()
673 image.save(str(sample_path_name), 'png')
674 thumb_path = self.thumb_path / '{name}.png'.format(name=name)
675 create_thumb(sample_path_name, thumb_path, False)
676 else:
677 self.generate_and_save_image(name, theme)
680678
681 def generate_and_save_image(self, theme_name, theme):679 def generate_and_save_image(self, theme_name, theme):
682 """680 """
683681
=== modified file 'openlp/core/ui/themewizard.py'
--- openlp/core/ui/themewizard.py 2019-04-13 13:00:22 +0000
+++ openlp/core/ui/themewizard.py 2019-06-09 20:23:18 +0000
@@ -31,6 +31,8 @@
31from openlp.core.ui.icons import UiIcons31from openlp.core.ui.icons import UiIcons
32from openlp.core.widgets.buttons import ColorButton32from openlp.core.widgets.buttons import ColorButton
33from openlp.core.widgets.edits import PathEdit33from openlp.core.widgets.edits import PathEdit
34from openlp.core.widgets.layouts import AspectRatioLayout
35from openlp.core.display.render import ThemePreviewRenderer
3436
3537
36class Ui_ThemeWizard(object):38class Ui_ThemeWizard(object):
@@ -363,16 +365,13 @@
363 self.preview_layout.addLayout(self.theme_name_layout)365 self.preview_layout.addLayout(self.theme_name_layout)
364 self.preview_area = QtWidgets.QWidget(self.preview_page)366 self.preview_area = QtWidgets.QWidget(self.preview_page)
365 self.preview_area.setObjectName('PreviewArea')367 self.preview_area.setObjectName('PreviewArea')
366 self.preview_area_layout = QtWidgets.QGridLayout(self.preview_area)368 self.preview_area_layout = AspectRatioLayout(self.preview_area, 0.75) # Dummy ratio, will be update
367 self.preview_area_layout.setContentsMargins(0, 0, 0, 0)369 self.preview_area_layout.margin = 8
368 self.preview_area_layout.setColumnStretch(0, 1)370 self.preview_area_layout.setSpacing(0)
369 self.preview_area_layout.setRowStretch(0, 1)371 self.preview_area_layout.setObjectName('preview_web_layout')
370 self.preview_area_layout.setObjectName('preview_area_layout')372 self.preview_box = ThemePreviewRenderer(self)
371 self.preview_box_label = QtWidgets.QLabel(self.preview_area)373 self.preview_box.setObjectName('preview_box')
372 self.preview_box_label.setFrameShape(QtWidgets.QFrame.Box)374 self.preview_area_layout.addWidget(self.preview_box)
373 self.preview_box_label.setScaledContents(True)
374 self.preview_box_label.setObjectName('preview_box_label')
375 self.preview_area_layout.addWidget(self.preview_box_label)
376 self.preview_layout.addWidget(self.preview_area)375 self.preview_layout.addWidget(self.preview_area)
377 theme_wizard.addPage(self.preview_page)376 theme_wizard.addPage(self.preview_page)
378 self.retranslate_ui(theme_wizard)377 self.retranslate_ui(theme_wizard)
379378
=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py 2019-06-01 06:59:45 +0000
+++ openlp/plugins/media/lib/mediaitem.py 2019-06-09 20:23:18 +0000
@@ -173,7 +173,7 @@
173 item = self.list_view.currentItem()173 item = self.list_view.currentItem()
174 if item is None:174 if item is None:
175 return False175 return False
176 filename = item.data(QtCore.Qt.UserRole)176 filename = str(item.data(QtCore.Qt.UserRole))
177 # Special handling if the filename is a optical clip177 # Special handling if the filename is a optical clip
178 if filename.startswith('optical:'):178 if filename.startswith('optical:'):
179 (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(filename)179 (name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(filename)
@@ -259,11 +259,12 @@
259 # TODO needs to be fixed as no idea why this fails259 # TODO needs to be fixed as no idea why this fails
260 # media.sort(key=lambda file_path: get_natural_key(file_path.name))260 # media.sort(key=lambda file_path: get_natural_key(file_path.name))
261 for track in media:261 for track in media:
262 track_info = QtCore.QFileInfo(track)262 track_str = str(track)
263 track_info = QtCore.QFileInfo(track_str)
263 item_name = None264 item_name = None
264 if track.startswith('optical:'):265 if track_str.startswith('optical:'):
265 # Handle optical based item266 # Handle optical based item
266 (file_name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(track)267 (file_name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(track_str)
267 item_name = QtWidgets.QListWidgetItem(clip_name)268 item_name = QtWidgets.QListWidgetItem(clip_name)
268 item_name.setIcon(UiIcons().optical)269 item_name.setIcon(UiIcons().optical)
269 item_name.setData(QtCore.Qt.UserRole, track)270 item_name.setData(QtCore.Qt.UserRole, track)
@@ -272,22 +273,22 @@
272 end=format_milliseconds(end)))273 end=format_milliseconds(end)))
273 elif not os.path.exists(track):274 elif not os.path.exists(track):
274 # File doesn't exist, mark as error.275 # File doesn't exist, mark as error.
275 file_name = os.path.split(str(track))[1]276 file_name = os.path.split(track_str)[1]
276 item_name = QtWidgets.QListWidgetItem(file_name)277 item_name = QtWidgets.QListWidgetItem(file_name)
277 item_name.setIcon(UiIcons().error)278 item_name.setIcon(UiIcons().error)
278 item_name.setData(QtCore.Qt.UserRole, track)279 item_name.setData(QtCore.Qt.UserRole, track)
279 item_name.setToolTip(track)280 item_name.setToolTip(track_str)
280 elif track_info.isFile():281 elif track_info.isFile():
281 # Normal media file handling.282 # Normal media file handling.
282 file_name = os.path.split(str(track))[1]283 file_name = os.path.split(track_str)[1]
283 item_name = QtWidgets.QListWidgetItem(file_name)284 item_name = QtWidgets.QListWidgetItem(file_name)
284 search = file_name.split('.')[-1].lower()285 search = file_name.split('.')[-1].lower()
285 if '*.{text}'.format(text=search) in self.media_controller.audio_extensions_list:286 if search in AUDIO_EXT:
286 item_name.setIcon(UiIcons().audio)287 item_name.setIcon(UiIcons().audio)
287 else:288 else:
288 item_name.setIcon(UiIcons().video)289 item_name.setIcon(UiIcons().video)
289 item_name.setData(QtCore.Qt.UserRole, track)290 item_name.setData(QtCore.Qt.UserRole, track)
290 item_name.setToolTip(track)291 item_name.setToolTip(track_str)
291 if item_name:292 if item_name:
292 self.list_view.addItem(item_name)293 self.list_view.addItem(item_name)
293294
294295
=== modified file 'openlp/plugins/songs/lib/songstab.py'
--- openlp/plugins/songs/lib/songstab.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/songs/lib/songstab.py 2019-06-09 20:23:18 +0000
@@ -88,7 +88,7 @@
88 self.footer_group_box = QtWidgets.QGroupBox(self.left_column)88 self.footer_group_box = QtWidgets.QGroupBox(self.left_column)
89 self.footer_group_box.setObjectName('footer_group_box')89 self.footer_group_box.setObjectName('footer_group_box')
90 self.footer_layout = QtWidgets.QVBoxLayout(self.footer_group_box)90 self.footer_layout = QtWidgets.QVBoxLayout(self.footer_group_box)
91 self.footer_layout.setObjectName('chords_layout')91 self.footer_layout.setObjectName('footer_layout')
92 self.footer_info_label = QtWidgets.QLabel(self.footer_group_box)92 self.footer_info_label = QtWidgets.QLabel(self.footer_group_box)
93 self.footer_layout.addWidget(self.footer_info_label)93 self.footer_layout.addWidget(self.footer_info_label)
94 self.footer_placeholder_info = QtWidgets.QTextEdit(self.footer_group_box)94 self.footer_placeholder_info = QtWidgets.QTextEdit(self.footer_group_box)
9595
=== modified file 'tests/functional/openlp_core/ui/test_thememanager.py'
--- tests/functional/openlp_core/ui/test_thememanager.py 2019-05-22 06:47:00 +0000
+++ tests/functional/openlp_core/ui/test_thememanager.py 2019-06-09 20:23:18 +0000
@@ -83,7 +83,7 @@
8383
84 @patch('openlp.core.ui.thememanager.shutil')84 @patch('openlp.core.ui.thememanager.shutil')
85 @patch('openlp.core.ui.thememanager.create_paths')85 @patch('openlp.core.ui.thememanager.create_paths')
86 def test_write_theme_same_image(self, mocked_create_paths, mocked_shutil):86 def test_save_theme_same_image(self, mocked_create_paths, mocked_shutil):
87 """87 """
88 Test that we don't try to overwrite a theme background image with itself88 Test that we don't try to overwrite a theme background image with itself
89 """89 """
@@ -98,16 +98,16 @@
98 mocked_theme.extract_formatted_xml = MagicMock()98 mocked_theme.extract_formatted_xml = MagicMock()
99 mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()99 mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
100100
101 # WHEN: Calling _write_theme with path to the same image, but the path written slightly different101 # WHEN: Calling save_theme with path to the same image, but the path written slightly different
102 file_path_1 = RESOURCE_PATH / 'church.jpg'102 file_path_1 = RESOURCE_PATH / 'church.jpg'
103 theme_manager._write_theme(mocked_theme, file_path_1, file_path_1)103 theme_manager.save_theme(mocked_theme, file_path_1, file_path_1)
104104
105 # THEN: The mocked_copyfile should not have been called105 # THEN: The mocked_copyfile should not have been called
106 assert mocked_shutil.copyfile.called is False, 'copyfile should not be called'106 assert mocked_shutil.copyfile.called is False, 'copyfile should not be called'
107107
108 @patch('openlp.core.ui.thememanager.shutil')108 @patch('openlp.core.ui.thememanager.shutil')
109 @patch('openlp.core.ui.thememanager.create_paths')109 @patch('openlp.core.ui.thememanager.create_paths')
110 def test_write_theme_diff_images(self, mocked_create_paths, mocked_shutil):110 def test_save_theme_diff_images(self, mocked_create_paths, mocked_shutil):
111 """111 """
112 Test that we do overwrite a theme background image when a new is submitted112 Test that we do overwrite a theme background image when a new is submitted
113 """113 """
@@ -121,15 +121,15 @@
121 mocked_theme.theme_name = 'themename'121 mocked_theme.theme_name = 'themename'
122 mocked_theme.filename = "filename"122 mocked_theme.filename = "filename"
123123
124 # WHEN: Calling _write_theme with path to different images124 # WHEN: Calling save_theme with path to different images
125 file_path_1 = RESOURCE_PATH / 'church.jpg'125 file_path_1 = RESOURCE_PATH / 'church.jpg'
126 file_path_2 = RESOURCE_PATH / 'church2.jpg'126 file_path_2 = RESOURCE_PATH / 'church2.jpg'
127 theme_manager._write_theme(mocked_theme, file_path_1, file_path_2)127 theme_manager.save_theme(mocked_theme, file_path_1, file_path_2)
128128
129 # THEN: The mocked_copyfile should not have been called129 # THEN: The mocked_copyfile should not have been called
130 assert mocked_shutil.copyfile.called is True, 'copyfile should be called'130 assert mocked_shutil.copyfile.called is True, 'copyfile should be called'
131131
132 def test_write_theme_special_char_name(self):132 def test_save_theme_special_char_name(self):
133 """133 """
134 Test that we can save themes with special characters in the name134 Test that we can save themes with special characters in the name
135 """135 """
@@ -142,8 +142,8 @@
142 mocked_theme.theme_name = 'theme 愛 name'142 mocked_theme.theme_name = 'theme 愛 name'
143 mocked_theme.export_theme.return_value = "{}"143 mocked_theme.export_theme.return_value = "{}"
144144
145 # WHEN: Calling _write_theme with a theme with a name with special characters in it145 # WHEN: Calling save_theme with a theme with a name with special characters in it
146 theme_manager._write_theme(mocked_theme)146 theme_manager.save_theme(mocked_theme)
147147
148 # THEN: It should have been created148 # THEN: It should have been created
149 assert os.path.exists(os.path.join(self.temp_folder, 'theme 愛 name', 'theme 愛 name.json')) is True, \149 assert os.path.exists(os.path.join(self.temp_folder, 'theme 愛 name', 'theme 愛 name.json')) is True, \
150150
=== modified file 'tests/openlp_core/ui/test_themeform.py'
--- tests/openlp_core/ui/test_themeform.py 2019-04-13 13:00:22 +0000
+++ tests/openlp_core/ui/test_themeform.py 2019-06-09 20:23:18 +0000
@@ -23,6 +23,7 @@
23Interface tests to test the ThemeWizard class and related methods.23Interface tests to test the ThemeWizard class and related methods.
24"""24"""
25from unittest import TestCase25from unittest import TestCase
26from unittest.mock import patch
2627
27from openlp.core.common.registry import Registry28from openlp.core.common.registry import Registry
28from openlp.core.ui.themeform import ThemeForm29from openlp.core.ui.themeform import ThemeForm
@@ -39,7 +40,8 @@
39 """40 """
40 Registry.create()41 Registry.create()
4142
42 def test_create_theme_wizard(self):43 @patch('openlp.core.display.window.QtWidgets.QVBoxLayout')
44 def test_create_theme_wizard(self, mocked_qvboxlayout):
43 """45 """
44 Test creating a ThemeForm instance46 Test creating a ThemeForm instance
45 """47 """