Merge lp:~phill-ridout/openlp/fixes-IV into lp:openlp

Proposed by Phill
Status: Merged
Merged at revision: 2889
Proposed branch: lp:~phill-ridout/openlp/fixes-IV
Merge into: lp:openlp
Diff against target: 852 lines (+172/-161)
25 files modified
openlp/core/common/__init__.py (+16/-1)
openlp/core/common/actions.py (+1/-1)
openlp/core/common/i18n.py (+4/-14)
openlp/core/common/registry.py (+2/-23)
openlp/core/display/html/display.js (+29/-22)
openlp/core/display/render.py (+1/-1)
openlp/core/display/screens.py (+2/-10)
openlp/core/lib/theme.py (+1/-0)
openlp/core/state.py (+2/-11)
openlp/core/ui/advancedtab.py (+1/-1)
openlp/core/ui/icons.py (+4/-14)
openlp/plugins/bibles/lib/__init__.py (+23/-29)
openlp/plugins/bibles/lib/db.py (+22/-25)
openlp/plugins/bibles/lib/manager.py (+4/-2)
openlp/plugins/custom/forms/editcustomdialog.py (+2/-0)
openlp/plugins/songs/forms/editsongdialog.py (+2/-0)
openlp/plugins/songs/lib/db.py (+3/-1)
openlp/plugins/songs/lib/importers/cclifile.py (+2/-0)
openlp/plugins/songs/lib/importers/dreambeam.py (+2/-1)
openlp/plugins/songs/lib/importers/easyslides.py (+2/-0)
openlp/plugins/songs/lib/importers/easyworship.py (+1/-1)
openlp/plugins/songs/lib/importers/songbeamer.py (+1/-1)
tests/functional/openlp_core/common/test_common.py (+43/-1)
tests/functional/openlp_core/lib/test_theme.py (+1/-1)
tests/functional/openlp_core/ui/test_icons.py (+1/-1)
To merge this branch: bzr merge lp:~phill-ridout/openlp/fixes-IV
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Review via email: mp+371023@code.launchpad.net

Commit message

Refactors and fixes

Description of the change

Refactor `singleton` classes to use a `singleton` metaclass
Fixes for a number of reported issues
Tidy ups, and improvements as suggested by pycharm

To post a comment you must log in.
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/common/__init__.py'
--- openlp/core/common/__init__.py 2019-07-19 18:43:14 +0000
+++ openlp/core/common/__init__.py 2019-08-06 21:48:11 +0000
@@ -172,6 +172,21 @@
172 Next = 3172 Next = 3
173173
174174
175class Singleton(type):
176 """
177 Provide a `Singleton` metaclass https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python
178 """
179 _instances = {}
180
181 def __call__(cls, *args, **kwargs):
182 """
183 Create a new instance if one does not already exist.
184 """
185 if cls not in cls._instances:
186 cls._instances[cls] = super().__call__(*args, **kwargs)
187 return cls._instances[cls]
188
189
175def de_hump(name):190def de_hump(name):
176 """191 """
177 Change any Camel Case string to python string192 Change any Camel Case string to python string
@@ -385,7 +400,7 @@
385 global IMAGES_FILTER400 global IMAGES_FILTER
386 if not IMAGES_FILTER:401 if not IMAGES_FILTER:
387 log.debug('Generating images filter.')402 log.debug('Generating images filter.')
388 formats = list(map(bytes.decode, list(map(bytes, QtGui.QImageReader.supportedImageFormats()))))403 formats = list(map(bytes.decode, map(bytes, QtGui.QImageReader.supportedImageFormats())))
389 visible_formats = '(*.{text})'.format(text='; *.'.join(formats))404 visible_formats = '(*.{text})'.format(text='; *.'.join(formats))
390 actual_formats = '(*.{text})'.format(text=' *.'.join(formats))405 actual_formats = '(*.{text})'.format(text=' *.'.join(formats))
391 IMAGES_FILTER = '{text} {visible} {actual}'.format(text=translate('OpenLP', 'Image Files'),406 IMAGES_FILTER = '{text} {visible} {actual}'.format(text=translate('OpenLP', 'Image Files'),
392407
=== modified file 'openlp/core/common/actions.py'
--- openlp/core/common/actions.py 2019-04-13 13:00:22 +0000
+++ openlp/core/common/actions.py 2019-08-06 21:48:11 +0000
@@ -260,7 +260,7 @@
260 return260 return
261 # We have to do this to ensure that the loaded shortcut list e. g. STRG+O (German) is converted to CTRL+O,261 # We have to do this to ensure that the loaded shortcut list e. g. STRG+O (German) is converted to CTRL+O,
262 # which is only done when we convert the strings in this way (QKeySequencet -> uncode).262 # which is only done when we convert the strings in this way (QKeySequencet -> uncode).
263 shortcuts = list(map(QtGui.QKeySequence.toString, list(map(QtGui.QKeySequence, shortcuts))))263 shortcuts = list(map(QtGui.QKeySequence.toString, map(QtGui.QKeySequence, shortcuts)))
264 # Check the alternate shortcut first, to avoid problems when the alternate shortcut becomes the primary shortcut264 # Check the alternate shortcut first, to avoid problems when the alternate shortcut becomes the primary shortcut
265 # after removing the (initial) primary shortcut due to conflicts.265 # after removing the (initial) primary shortcut due to conflicts.
266 if len(shortcuts) == 2:266 if len(shortcuts) == 2:
267267
=== modified file 'openlp/core/common/i18n.py'
--- openlp/core/common/i18n.py 2019-07-03 13:23:23 +0000
+++ openlp/core/common/i18n.py 2019-08-06 21:48:11 +0000
@@ -29,7 +29,7 @@
2929
30from PyQt5 import QtCore, QtWidgets30from PyQt5 import QtCore, QtWidgets
3131
32from openlp.core.common import is_macosx, is_win32from openlp.core.common import Singleton, is_macosx, is_win
33from openlp.core.common.applocation import AppLocation33from openlp.core.common.applocation import AppLocation
34from openlp.core.common.settings import Settings34from openlp.core.common.settings import Settings
3535
@@ -327,22 +327,11 @@
327 return LanguageManager.__qm_list__327 return LanguageManager.__qm_list__
328328
329329
330class UiStrings(object):330class UiStrings(metaclass=Singleton):
331 """331 """
332 Provide standard strings for objects to use.332 Provide standard strings for objects to use.
333 """333 """
334 __instance__ = None334 def __init__(self):
335
336 def __new__(cls):
337 """
338 Override the default object creation method to return a single instance.
339 """
340 if not cls.__instance__:
341 cls.__instance__ = super().__new__(cls)
342 cls.__instance__.load()
343 return cls.__instance__
344
345 def load(self):
346 """335 """
347 These strings should need a good reason to be retranslated elsewhere.336 These strings should need a good reason to be retranslated elsewhere.
348 Should some/more/less of these have an & attached?337 Should some/more/less of these have an & attached?
@@ -436,6 +425,7 @@
436 self.ResetLiveBG = translate('OpenLP.Ui', 'Reset live background.')425 self.ResetLiveBG = translate('OpenLP.Ui', 'Reset live background.')
437 self.RequiredShowInFooter = translate('OpenLP.Ui', 'Required, this will be displayed in footer.')426 self.RequiredShowInFooter = translate('OpenLP.Ui', 'Required, this will be displayed in footer.')
438 self.Seconds = translate('OpenLP.Ui', 's', 'The abbreviated unit for seconds')427 self.Seconds = translate('OpenLP.Ui', 's', 'The abbreviated unit for seconds')
428 self.SaveAndClose = translate('OpenLP.ui', translate('SongsPlugin.EditSongForm', '&Save && Close'))
439 self.SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview')429 self.SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview')
440 self.Search = translate('OpenLP.Ui', 'Search')430 self.Search = translate('OpenLP.Ui', 'Search')
441 self.SearchThemes = translate('OpenLP.Ui', 'Search Themes...', 'Search bar place holder text ')431 self.SearchThemes = translate('OpenLP.Ui', 'Search Themes...', 'Search bar place holder text ')
442432
=== modified file 'openlp/core/common/registry.py'
--- openlp/core/common/registry.py 2019-05-05 18:41:17 +0000
+++ openlp/core/common/registry.py 2019-08-06 21:48:11 +0000
@@ -23,29 +23,19 @@
23Provide Registry Services23Provide Registry Services
24"""24"""
25import logging25import logging
26import sys
2726
28from openlp.core.common import de_hump, trace_error_handler27from openlp.core.common import Singleton, de_hump, trace_error_handler
2928
3029
31log = logging.getLogger(__name__)30log = logging.getLogger(__name__)
3231
3332
34class Registry(object):33class Registry(metaclass=Singleton):
35 """34 """
36 This is the Component Registry. It is a singleton object and is used to provide a look up service for common35 This is the Component Registry. It is a singleton object and is used to provide a look up service for common
37 objects.36 objects.
38 """37 """
39 log.info('Registry loaded')38 log.info('Registry loaded')
40 __instance__ = None
41
42 def __new__(cls):
43 """
44 Re-implement the __new__ method to make sure we create a true singleton.
45 """
46 if not cls.__instance__:
47 cls.__instance__ = object.__new__(cls)
48 return cls.__instance__
4939
50 @classmethod40 @classmethod
51 def create(cls):41 def create(cls):
@@ -57,20 +47,9 @@
57 registry.service_list = {}47 registry.service_list = {}
58 registry.functions_list = {}48 registry.functions_list = {}
59 registry.working_flags = {}49 registry.working_flags = {}
60 # Allow the tests to remove Registry entries but not the live system
61 registry.running_under_test = 'nose' in sys.argv[0] or 'pytest' in sys.argv[0]
62 registry.initialising = True50 registry.initialising = True
63 return registry51 return registry
6452
65 @classmethod
66 def destroy(cls):
67 """
68 Destroy the Registry.
69 """
70 if cls.__instance__.running_under_test:
71 del cls.__instance__
72 cls.__instance__ = None
73
74 def get(self, key):53 def get(self, key):
75 """54 """
76 Extracts the registry value from the list based on the key passed in55 Extracts the registry value from the list based on the key passed in
7756
=== modified file 'openlp/core/display/html/display.js'
--- openlp/core/display/html/display.js 2019-06-21 20:53:42 +0000
+++ openlp/core/display/html/display.js 2019-08-06 21:48:11 +0000
@@ -281,8 +281,9 @@
281 * Checks if the present slide content fits within the slide281 * Checks if the present slide content fits within the slide
282 */282 */
283 doesContentFit: function () {283 doesContentFit: function () {
284 console.debug("scrollHeight: " + $(".slides")[0].scrollHeight + ", clientHeight: " + $(".slides")[0].clientHeight);284 var currSlide = $(".slides")[0];
285 return $(".slides")[0].clientHeight >= $(".slides")[0].scrollHeight;285 console.debug("scrollHeight: " + currSlide.scrollHeight + ", clientHeight: " + currSlide.clientHeight);
286 return currSlide.clientHeight >= currSlide.scrollHeight;
286 },287 },
287 /**288 /**
288 * Generate the OpenLP startup splashscreen289 * Generate the OpenLP startup splashscreen
@@ -333,7 +334,7 @@
333 /**334 /**
334 * Set fullscreen image from base64 data335 * Set fullscreen image from base64 data
335 * @param {string} bg_color - The background color336 * @param {string} bg_color - The background color
336 * @param {string} image - Path to the image337 * @param {string} image_data - base64 encoded image data
337 */338 */
338 setFullscreenImageFromData: function(bg_color, image_data) {339 setFullscreenImageFromData: function(bg_color, image_data) {
339 Display.clearSlides();340 Display.clearSlides();
@@ -372,7 +373,6 @@
372 * @param {string} verse - The verse number, e.g. "v1"373 * @param {string} verse - The verse number, e.g. "v1"
373 * @param {string} text - The HTML for the verse, e.g. "line1<br>line2"374 * @param {string} text - The HTML for the verse, e.g. "line1<br>line2"
374 * @param {string} footer_text - The HTML for the footer"375 * @param {string} footer_text - The HTML for the footer"
375 * @param {bool} [reinit=true] - Re-initialize Reveal. Defaults to true.
376 */376 */
377 addTextSlide: function (verse, text, footer_text) {377 addTextSlide: function (verse, text, footer_text) {
378 var html = _prepareText(text);378 var html = _prepareText(text);
@@ -476,25 +476,28 @@
476 * Play a video476 * Play a video
477 */477 */
478 playVideo: function () {478 playVideo: function () {
479 if ($("#video").length == 1) {479 var videoElem = $("#video");
480 $("#video")[0].play();480 if (videoElem.length == 1) {
481 videoElem[0].play();
481 }482 }
482 },483 },
483 /**484 /**
484 * Pause a video485 * Pause a video
485 */486 */
486 pauseVideo: function () {487 pauseVideo: function () {
487 if ($("#video").length == 1) {488 var videoElem = $("#video");
488 $("#video")[0].pause();489 if (videoElem.length == 1) {
490 videoElem[0].pause();
489 }491 }
490 },492 },
491 /**493 /**
492 * Stop a video494 * Stop a video
493 */495 */
494 stopVideo: function () {496 stopVideo: function () {
495 if ($("#video").length == 1) {497 var videoElem = $("#video");
496 $("#video")[0].pause();498 if (videoElem.length == 1) {
497 $("#video")[0].currentTime = 0.0;499 videoElem[0].pause();
500 videoElem[0].currentTime = 0.0;
498 }501 }
499 },502 },
500 /**503 /**
@@ -502,8 +505,9 @@
502 * @param seconds The position in seconds to seek to505 * @param seconds The position in seconds to seek to
503 */506 */
504 seekVideo: function (seconds) {507 seekVideo: function (seconds) {
505 if ($("#video").length == 1) {508 var videoElem = $("#video");
506 $("#video")[0].currentTime = seconds;509 if (videoElem.length == 1) {
510 videoElem[0].currentTime = seconds;
507 }511 }
508 },512 },
509 /**513 /**
@@ -511,8 +515,9 @@
511 * @param rate A Double of the rate. 1.0 => 100% speed, 0.75 => 75% speed, 1.25 => 125% speed, etc.515 * @param rate A Double of the rate. 1.0 => 100% speed, 0.75 => 75% speed, 1.25 => 125% speed, etc.
512 */516 */
513 setPlaybackRate: function (rate) {517 setPlaybackRate: function (rate) {
514 if ($("#video").length == 1) {518 var videoElem = $("#video");
515 $("#video")[0].playbackRate = rate;519 if (videoElem.length == 1) {
520 videoElem[0].playbackRate = rate;
516 }521 }
517 },522 },
518 /**523 /**
@@ -520,24 +525,27 @@
520 * @param level The volume level from 0 to 100.525 * @param level The volume level from 0 to 100.
521 */526 */
522 setVideoVolume: function (level) {527 setVideoVolume: function (level) {
523 if ($("#video").length == 1) {528 var videoElem = $("#video");
524 $("#video")[0].volume = level / 100.0;529 if (videoElem.length == 1) {
530 videoElem[0].volume = level / 100.0;
525 }531 }
526 },532 },
527 /**533 /**
528 * Mute the volume534 * Mute the volume
529 */535 */
530 toggleVideoMute: function () {536 toggleVideoMute: function () {
531 if ($("#video").length == 1) {537 var videoElem = $("#video");
532 $("#video")[0].muted = !$("#video")[0].muted;538 if (videoElem.length == 1) {
539 videoElem[0].muted = !videoElem[0].muted;
533 }540 }
534 },541 },
535 /**542 /**
536 * Clear the background audio playlist543 * Clear the background audio playlist
537 */544 */
538 clearPlaylist: function () {545 clearPlaylist: function () {
539 if ($("#background-audio").length == 1) {546 var backgroundAudoElem = $("#background-audio");
540 var audio = $("#background-audio")[0];547 if (backgroundAudoElem.length == 1) {
548 var audio = backgroundAudoElem[0];
541 /* audio.playList */549 /* audio.playList */
542 }550 }
543 },551 },
@@ -619,7 +627,6 @@
619 },627 },
620 setTheme: function (theme) {628 setTheme: function (theme) {
621 this._theme = theme;629 this._theme = theme;
622 var slidesDiv = $(".slides")
623 // Set the background630 // Set the background
624 var globalBackground = $("#global-background")[0];631 var globalBackground = $("#global-background")[0];
625 var backgroundStyle = {};632 var backgroundStyle = {};
626633
=== modified file 'openlp/core/display/render.py'
--- openlp/core/display/render.py 2019-07-28 15:56:28 +0000
+++ openlp/core/display/render.py 2019-08-06 21:48:11 +0000
@@ -47,7 +47,7 @@
4747
48SLIM_CHARS = 'fiíIÍjlĺľrtť.,;/ ()|"\'!:\\'48SLIM_CHARS = 'fiíIÍjlĺľrtť.,;/ ()|"\'!:\\'
49CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)'49CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)'
50 r'([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?')50 r'([\u0080-\uFFFF\w\s\.\,\!\?\;\:\|\"\'\-\_]*)(\Z)?')
51CHORD_TEMPLATE = '<span class="chordline">{chord}</span>'51CHORD_TEMPLATE = '<span class="chordline">{chord}</span>'
52FIRST_CHORD_TEMPLATE = '<span class="chordline firstchordline">{chord}</span>'52FIRST_CHORD_TEMPLATE = '<span class="chordline firstchordline">{chord}</span>'
53CHORD_LINE_TEMPLATE = '<span class="chord"><span><strong>{chord}</strong></span></span>{tail}{whitespace}{remainder}'53CHORD_LINE_TEMPLATE = '<span class="chord"><span><strong>{chord}</strong></span></span>{tail}{whitespace}{remainder}'
5454
=== modified file 'openlp/core/display/screens.py'
--- openlp/core/display/screens.py 2019-08-04 14:06:00 +0000
+++ openlp/core/display/screens.py 2019-08-06 21:48:11 +0000
@@ -28,6 +28,7 @@
2828
29from PyQt5 import QtCore, QtWidgets29from PyQt5 import QtCore, QtWidgets
3030
31from openlp.core.common import Singleton
31from openlp.core.common.i18n import translate32from openlp.core.common.i18n import translate
32from openlp.core.common.registry import Registry33from openlp.core.common.registry import Registry
33from openlp.core.common.settings import Settings34from openlp.core.common.settings import Settings
@@ -147,24 +148,15 @@
147 screen_dict['custom_geometry']['height'])148 screen_dict['custom_geometry']['height'])
148149
149150
150class ScreenList(object):151class ScreenList(metaclass=Singleton):
151 """152 """
152 Wrapper to handle the parameters of the display screen.153 Wrapper to handle the parameters of the display screen.
153154
154 To get access to the screen list call ``ScreenList()``.155 To get access to the screen list call ``ScreenList()``.
155 """156 """
156 log.info('Screen loaded')157 log.info('Screen loaded')
157 __instance__ = None
158 screens = []158 screens = []
159159
160 def __new__(cls):
161 """
162 Re-implement __new__ to create a true singleton.
163 """
164 if not cls.__instance__:
165 cls.__instance__ = object.__new__(cls)
166 return cls.__instance__
167
168 def __iter__(self):160 def __iter__(self):
169 """161 """
170 Convert this object into an iterable, so that we can iterate over it instead of the inner list162 Convert this object into an iterable, so that we can iterate over it instead of the inner list
171163
=== modified file 'openlp/core/lib/theme.py'
--- openlp/core/lib/theme.py 2019-06-21 22:09:36 +0000
+++ openlp/core/lib/theme.py 2019-08-06 21:48:11 +0000
@@ -170,6 +170,7 @@
170 jsn = get_text_file_string(json_path)170 jsn = get_text_file_string(json_path)
171 self.load_theme(jsn)171 self.load_theme(jsn)
172 self.background_filename = None172 self.background_filename = None
173 self.version = 2
173174
174 def expand_json(self, var, prev=None):175 def expand_json(self, var, prev=None):
175 """176 """
176177
=== modified file 'openlp/core/state.py'
--- openlp/core/state.py 2019-04-13 13:00:22 +0000
+++ openlp/core/state.py 2019-08-06 21:48:11 +0000
@@ -28,6 +28,7 @@
28"""28"""
29import logging29import logging
3030
31from openlp.core.common import Singleton
31from openlp.core.common.registry import Registry32from openlp.core.common.registry import Registry
32from openlp.core.common.mixins import LogMixin33from openlp.core.common.mixins import LogMixin
33from openlp.core.lib.plugin import PluginStatus34from openlp.core.lib.plugin import PluginStatus
@@ -52,17 +53,7 @@
52 self.text = None53 self.text = None
5354
5455
55class State(LogMixin):56class State(LogMixin, metaclass=Singleton):
56
57 __instance__ = None
58
59 def __new__(cls):
60 """
61 Re-implement the __new__ method to make sure we create a true singleton.
62 """
63 if not cls.__instance__:
64 cls.__instance__ = object.__new__(cls)
65 return cls.__instance__
6657
67 def load_settings(self):58 def load_settings(self):
68 self.modules = {}59 self.modules = {}
6960
=== modified file 'openlp/core/ui/advancedtab.py'
--- openlp/core/ui/advancedtab.py 2019-05-22 06:47:00 +0000
+++ openlp/core/ui/advancedtab.py 2019-08-06 21:48:11 +0000
@@ -81,7 +81,7 @@
81 self.ui_layout.addRow(self.media_plugin_check_box)81 self.ui_layout.addRow(self.media_plugin_check_box)
82 self.hide_mouse_check_box = QtWidgets.QCheckBox(self.ui_group_box)82 self.hide_mouse_check_box = QtWidgets.QCheckBox(self.ui_group_box)
83 self.hide_mouse_check_box.setObjectName('hide_mouse_check_box')83 self.hide_mouse_check_box.setObjectName('hide_mouse_check_box')
84 self.ui_layout.addWidget(self.hide_mouse_check_box)84 self.ui_layout.addRow(self.hide_mouse_check_box)
85 self.double_click_live_check_box = QtWidgets.QCheckBox(self.ui_group_box)85 self.double_click_live_check_box = QtWidgets.QCheckBox(self.ui_group_box)
86 self.double_click_live_check_box.setObjectName('double_click_live_check_box')86 self.double_click_live_check_box.setObjectName('double_click_live_check_box')
87 self.ui_layout.addRow(self.double_click_live_check_box)87 self.ui_layout.addRow(self.double_click_live_check_box)
8888
=== modified file 'openlp/core/ui/icons.py'
--- openlp/core/ui/icons.py 2019-07-03 13:23:23 +0000
+++ openlp/core/ui/icons.py 2019-08-06 21:48:11 +0000
@@ -27,6 +27,7 @@
27import qtawesome as qta27import qtawesome as qta
28from PyQt5 import QtGui, QtWidgets28from PyQt5 import QtGui, QtWidgets
2929
30from openlp.core.common import Singleton
30from openlp.core.common.applocation import AppLocation31from openlp.core.common.applocation import AppLocation
31from openlp.core.lib import build_icon32from openlp.core.lib import build_icon
3233
@@ -34,22 +35,11 @@
34log = logging.getLogger(__name__)35log = logging.getLogger(__name__)
3536
3637
37class UiIcons(object):38class UiIcons(metaclass=Singleton):
38 """39 """
39 Provide standard icons for objects to use.40 Provide standard icons for objects to use.
40 """41 """
41 __instance__ = None42 def __init__(self):
42
43 def __new__(cls):
44 """
45 Override the default object creation method to return a single instance.
46 """
47 if not cls.__instance__:
48 cls.__instance__ = super().__new__(cls)
49 cls.__instance__.load()
50 return cls.__instance__
51
52 def load(self):
53 """43 """
54 These are the font icons used in the code.44 These are the font icons used in the code.
55 """45 """
@@ -165,6 +155,7 @@
165 'volunteer': {'icon': 'fa.group'}155 'volunteer': {'icon': 'fa.group'}
166 }156 }
167 self.load_icons(icon_list)157 self.load_icons(icon_list)
158 self.main_icon = build_icon(':/icon/openlp-logo.svg')
168159
169 def load_icons(self, icon_list):160 def load_icons(self, icon_list):
170 """161 """
@@ -184,7 +175,6 @@
184 setattr(self, key, qta.icon('fa.plus-circle', color='red'))175 setattr(self, key, qta.icon('fa.plus-circle', color='red'))
185 except Exception:176 except Exception:
186 setattr(self, key, qta.icon('fa.plus-circle', color='red'))177 setattr(self, key, qta.icon('fa.plus-circle', color='red'))
187 self.main_icon = build_icon(':/icon/openlp-logo.svg')
188178
189 @staticmethod179 @staticmethod
190 def _print_icons():180 def _print_icons():
191181
=== modified file 'openlp/plugins/bibles/lib/__init__.py'
--- openlp/plugins/bibles/lib/__init__.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/bibles/lib/__init__.py 2019-08-06 21:48:11 +0000
@@ -26,6 +26,7 @@
26import logging26import logging
27import re27import re
2828
29from openlp.core.common import Singleton
29from openlp.core.common.i18n import translate30from openlp.core.common.i18n import translate
30from openlp.core.common.settings import Settings31from openlp.core.common.settings import Settings
3132
@@ -64,20 +65,10 @@
64 English = 265 English = 2
6566
6667
67class BibleStrings(object):68class BibleStrings(metaclass=Singleton):
68 """69 """
69 Provide standard strings for objects to use.70 Provide standard strings for objects to use.
70 """71 """
71 __instance__ = None
72
73 def __new__(cls):
74 """
75 Override the default object creation method to return a single instance.
76 """
77 if not cls.__instance__:
78 cls.__instance__ = object.__new__(cls)
79 return cls.__instance__
80
81 def __init__(self):72 def __init__(self):
82 """73 """
83 These strings should need a good reason to be retranslated elsewhere.74 These strings should need a good reason to be retranslated elsewhere.
@@ -336,11 +327,13 @@
336 log.debug('Matched reference {text}'.format(text=reference))327 log.debug('Matched reference {text}'.format(text=reference))
337 book = match.group('book')328 book = match.group('book')
338 if not book_ref_id:329 if not book_ref_id:
339 book_ref_id = bible.get_book_ref_id_by_localised_name(book, language_selection)330 book_ref_ids = bible.get_book_ref_id_by_localised_name(book, language_selection)
340 elif not bible.get_book_by_book_ref_id(book_ref_id):331 elif not bible.get_book_by_book_ref_id(book_ref_id):
341 return []332 return []
333 else:
334 book_ref_ids = [book_ref_id]
342 # We have not found the book so do not continue335 # We have not found the book so do not continue
343 if not book_ref_id:336 if not book_ref_ids:
344 return []337 return []
345 ranges = match.group('ranges')338 ranges = match.group('ranges')
346 range_list = get_reference_match('range_separator').split(ranges)339 range_list = get_reference_match('range_separator').split(ranges)
@@ -381,22 +374,23 @@
381 to_chapter = to_verse374 to_chapter = to_verse
382 to_verse = None375 to_verse = None
383 # Append references to the list376 # Append references to the list
384 if has_range:377 for book_ref_id in book_ref_ids:
385 if not from_verse:378 if has_range:
386 from_verse = 1379 if not from_verse:
387 if not to_verse:380 from_verse = 1
388 to_verse = -1381 if not to_verse:
389 if to_chapter and to_chapter > from_chapter:382 to_verse = -1
390 ref_list.append((book_ref_id, from_chapter, from_verse, -1))383 if to_chapter and to_chapter > from_chapter:
391 for i in range(from_chapter + 1, to_chapter):384 ref_list.append((book_ref_id, from_chapter, from_verse, -1))
392 ref_list.append((book_ref_id, i, 1, -1))385 for i in range(from_chapter + 1, to_chapter):
393 ref_list.append((book_ref_id, to_chapter, 1, to_verse))386 ref_list.append((book_ref_id, i, 1, -1))
394 elif to_verse >= from_verse or to_verse == -1:387 ref_list.append((book_ref_id, to_chapter, 1, to_verse))
395 ref_list.append((book_ref_id, from_chapter, from_verse, to_verse))388 elif to_verse >= from_verse or to_verse == -1:
396 elif from_verse:389 ref_list.append((book_ref_id, from_chapter, from_verse, to_verse))
397 ref_list.append((book_ref_id, from_chapter, from_verse, from_verse))390 elif from_verse:
398 else:391 ref_list.append((book_ref_id, from_chapter, from_verse, from_verse))
399 ref_list.append((book_ref_id, from_chapter, 1, -1))392 else:
393 ref_list.append((book_ref_id, from_chapter, 1, -1))
400 return ref_list394 return ref_list
401 else:395 else:
402 log.debug('Invalid reference: {text}'.format(text=reference))396 log.debug('Invalid reference: {text}'.format(text=reference))
403397
=== modified file 'openlp/plugins/bibles/lib/db.py'
--- openlp/plugins/bibles/lib/db.py 2019-05-22 06:47:00 +0000
+++ openlp/plugins/bibles/lib/db.py 2019-08-06 21:48:11 +0000
@@ -281,13 +281,14 @@
281 log.debug('BibleDB.get_book("{book}")'.format(book=book))281 log.debug('BibleDB.get_book("{book}")'.format(book=book))
282 return self.get_object_filtered(Book, Book.name.like(book + '%'))282 return self.get_object_filtered(Book, Book.name.like(book + '%'))
283283
284 def get_books(self):284 def get_books(self, book=None):
285 """285 """
286 A wrapper so both local and web bibles have a get_books() method that286 A wrapper so both local and web bibles have a get_books() method that
287 manager can call. Used in the media manager advanced search tab.287 manager can call. Used in the media manager advanced search tab.
288 """288 """
289 log.debug('BibleDB.get_books()')289 log.debug('BibleDB.get_books("{book}")'.format(book=book))
290 return self.get_all_objects(Book, order_by_ref=Book.id)290 filter = Book.name.like(book + '%') if book else None
291 return self.get_all_objects(Book, filter_clause=filter, order_by_ref=Book.id)
291292
292 def get_book_by_book_ref_id(self, ref_id):293 def get_book_by_book_ref_id(self, ref_id):
293 """294 """
@@ -300,39 +301,35 @@
300301
301 def get_book_ref_id_by_localised_name(self, book, language_selection):302 def get_book_ref_id_by_localised_name(self, book, language_selection):
302 """303 """
303 Return the id of a named book.304 Return the ids of a matching named book.
304305
305 :param book: The name of the book, according to the selected language.306 :param book: The name of the book, according to the selected language.
306 :param language_selection: The language selection the user has chosen in the settings section of the Bible.307 :param language_selection: The language selection the user has chosen in the settings section of the Bible.
308 :rtype: list[int]
307 """309 """
308 log.debug('get_book_ref_id_by_localised_name("{book}", "{lang}")'.format(book=book, lang=language_selection))310 log.debug('get_book_ref_id_by_localised_name("{book}", "{lang}")'.format(book=book, lang=language_selection))
309 from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings311 from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings
310 book_names = BibleStrings().BookNames312 book_names = BibleStrings().BookNames
311 # escape reserved characters313 # escape reserved characters
312 book_escaped = book
313 for character in RESERVED_CHARACTERS:314 for character in RESERVED_CHARACTERS:
314 book_escaped = book_escaped.replace(character, '\\' + character)315 book_escaped = book.replace(character, '\\' + character)
315 regex_book = re.compile('\\s*{book}\\s*'.format(book='\\s*'.join(book_escaped.split())), re.IGNORECASE)316 regex_book = re.compile('\\s*{book}\\s*'.format(book='\\s*'.join(book_escaped.split())), re.IGNORECASE)
316 if language_selection == LanguageSelection.Bible:317 if language_selection == LanguageSelection.Bible:
317 db_book = self.get_book(book)318 db_books = self.get_books(book)
318 if db_book:319 return [db_book.book_reference_id for db_book in db_books]
319 return db_book.book_reference_id320 else:
320 elif language_selection == LanguageSelection.Application:321 book_list = []
321 books = [key for key in list(book_names.keys()) if regex_book.match(str(book_names[key]))]322 if language_selection == LanguageSelection.Application:
322 books = [_f for _f in map(BiblesResourcesDB.get_book, books) if _f]323 books = [key for key in list(book_names.keys()) if regex_book.match(book_names[key])]
323 for value in books:324 book_list = [_f for _f in map(BiblesResourcesDB.get_book, books) if _f]
324 if self.get_book_by_book_ref_id(value['id']):325 elif language_selection == LanguageSelection.English:
325 return value['id']326 books = BiblesResourcesDB.get_books_like(book)
326 elif language_selection == LanguageSelection.English:327 if books:
327 books = BiblesResourcesDB.get_books_like(book)328 book_list = [value for value in books if regex_book.match(value['name'])]
328 if books:329 if not book_list:
329 book_list = [value for value in books if regex_book.match(value['name'])]330 book_list = books
330 if not book_list:331 return [value['id'] for value in book_list if self.get_book_by_book_ref_id(value['id'])]
331 book_list = books332 return []
332 for value in book_list:
333 if self.get_book_by_book_ref_id(value['id']):
334 return value['id']
335 return False
336333
337 def get_verses(self, reference_list, show_error=True):334 def get_verses(self, reference_list, show_error=True):
338 """335 """
339336
=== modified file 'openlp/plugins/bibles/lib/manager.py'
--- openlp/plugins/bibles/lib/manager.py 2019-07-03 13:23:23 +0000
+++ openlp/plugins/bibles/lib/manager.py 2019-08-06 21:48:11 +0000
@@ -240,8 +240,10 @@
240 book=book,240 book=book,
241 chapter=chapter))241 chapter=chapter))
242 language_selection = self.get_language_selection(bible)242 language_selection = self.get_language_selection(bible)
243 book_ref_id = self.db_cache[bible].get_book_ref_id_by_localised_name(book, language_selection)243 book_ref_ids = self.db_cache[bible].get_book_ref_id_by_localised_name(book, language_selection)
244 return self.db_cache[bible].get_verse_count(book_ref_id, chapter)244 if book_ref_ids:
245 return self.db_cache[bible].get_verse_count(book_ref_ids[0], chapter)
246 return 0
245247
246 def get_verse_count_by_book_ref_id(self, bible, book_ref_id, chapter):248 def get_verse_count_by_book_ref_id(self, bible, book_ref_id, chapter):
247 """249 """
248250
=== modified file 'openlp/plugins/custom/forms/editcustomdialog.py'
--- openlp/plugins/custom/forms/editcustomdialog.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/custom/forms/editcustomdialog.py 2019-08-06 21:48:11 +0000
@@ -97,6 +97,7 @@
97 self.preview_button = QtWidgets.QPushButton()97 self.preview_button = QtWidgets.QPushButton()
98 self.button_box = create_button_box(custom_edit_dialog, 'button_box', ['cancel', 'save'],98 self.button_box = create_button_box(custom_edit_dialog, 'button_box', ['cancel', 'save'],
99 [self.preview_button])99 [self.preview_button])
100 self.save_button = self.button_box.button(QtWidgets.QDialogButtonBox.Save)
100 self.dialog_layout.addWidget(self.button_box)101 self.dialog_layout.addWidget(self.button_box)
101 self.retranslate_ui(custom_edit_dialog)102 self.retranslate_ui(custom_edit_dialog)
102103
@@ -112,3 +113,4 @@
112 self.theme_label.setText(translate('CustomPlugin.EditCustomForm', 'The&me:'))113 self.theme_label.setText(translate('CustomPlugin.EditCustomForm', 'The&me:'))
113 self.credit_label.setText(translate('CustomPlugin.EditCustomForm', '&Credits:'))114 self.credit_label.setText(translate('CustomPlugin.EditCustomForm', '&Credits:'))
114 self.preview_button.setText(UiStrings().SaveAndPreview)115 self.preview_button.setText(UiStrings().SaveAndPreview)
116 self.save_button.setText(UiStrings().SaveAndClose)
115117
=== modified file 'openlp/plugins/songs/forms/editsongdialog.py'
--- openlp/plugins/songs/forms/editsongdialog.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/songs/forms/editsongdialog.py 2019-08-06 21:48:11 +0000
@@ -291,6 +291,7 @@
291 self.warning_label.setObjectName('warning_label')291 self.warning_label.setObjectName('warning_label')
292 self.bottom_layout.addWidget(self.warning_label)292 self.bottom_layout.addWidget(self.warning_label)
293 self.button_box = create_button_box(edit_song_dialog, 'button_box', ['cancel', 'save'])293 self.button_box = create_button_box(edit_song_dialog, 'button_box', ['cancel', 'save'])
294 self.save_button = self.button_box.button(QtWidgets.QDialogButtonBox.Save)
294 self.bottom_layout.addWidget(self.button_box)295 self.bottom_layout.addWidget(self.button_box)
295 self.dialog_layout.addLayout(self.bottom_layout)296 self.dialog_layout.addLayout(self.bottom_layout)
296 self.retranslate_ui(edit_song_dialog)297 self.retranslate_ui(edit_song_dialog)
@@ -341,6 +342,7 @@
341 translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> Not all of the verses are in use.')342 translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> Not all of the verses are in use.')
342 self.no_verse_order_entered_warning = \343 self.no_verse_order_entered_warning = \
343 translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> You have not entered a verse order.')344 translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> You have not entered a verse order.')
345 self.save_button.setText(UiStrings().SaveAndPreview)
344346
345347
346def create_combo_box(parent, name, editable=True):348def create_combo_box(parent, name, editable=True):
347349
=== modified file 'openlp/plugins/songs/lib/db.py'
--- openlp/plugins/songs/lib/db.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/songs/lib/db.py 2019-08-06 21:48:11 +0000
@@ -374,7 +374,9 @@
374 mapper(SongBookEntry, songs_songbooks_table, properties={374 mapper(SongBookEntry, songs_songbooks_table, properties={
375 'songbook': relation(Book)375 'songbook': relation(Book)
376 })376 })
377 mapper(Book, song_books_table)377 mapper(Book, song_books_table, properties={
378 'songs': relation(Song, secondary=songs_songbooks_table)
379 })
378 mapper(MediaFile, media_files_table)380 mapper(MediaFile, media_files_table)
379 mapper(Song, songs_table, properties={381 mapper(Song, songs_table, properties={
380 # Use the authors_songs relation when you need access to the 'author_type' attribute382 # Use the authors_songs relation when you need access to the 'author_type' attribute
381383
=== modified file 'openlp/plugins/songs/lib/importers/cclifile.py'
--- openlp/plugins/songs/lib/importers/cclifile.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/songs/lib/importers/cclifile.py 2019-08-06 21:48:11 +0000
@@ -146,7 +146,9 @@
146 """146 """
147 log.debug('USR file text: {text}'.format(text=text_list))147 log.debug('USR file text: {text}'.format(text=text_list))
148 song_author = ''148 song_author = ''
149 song_fields = ''
149 song_topics = ''150 song_topics = ''
151 song_words = ''
150 for line in text_list:152 for line in text_list:
151 if line.startswith('[S '):153 if line.startswith('[S '):
152 ccli, line = line.split(']', 1)154 ccli, line = line.split(']', 1)
153155
=== modified file 'openlp/plugins/songs/lib/importers/dreambeam.py'
--- openlp/plugins/songs/lib/importers/dreambeam.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/songs/lib/importers/dreambeam.py 2019-08-06 21:48:11 +0000
@@ -87,6 +87,7 @@
87 if self.stop_import_flag:87 if self.stop_import_flag:
88 return88 return
89 self.set_defaults()89 self.set_defaults()
90 author_copyright = ''
90 parser = etree.XMLParser(remove_blank_text=True)91 parser = etree.XMLParser(remove_blank_text=True)
91 try:92 try:
92 with file_path.open('r') as xml_file:93 with file_path.open('r') as xml_file:
@@ -142,7 +143,7 @@
142 author_copyright = song_xml.Text2.Text.text143 author_copyright = song_xml.Text2.Text.text
143 if author_copyright:144 if author_copyright:
144 author_copyright = str(author_copyright)145 author_copyright = str(author_copyright)
145 if author_copyright.find(str(SongStrings.CopyrightSymbol)) >= 0:146 if author_copyright.find(SongStrings.CopyrightSymbol) >= 0:
146 self.add_copyright(author_copyright)147 self.add_copyright(author_copyright)
147 else:148 else:
148 self.parse_author(author_copyright)149 self.parse_author(author_copyright)
149150
=== modified file 'openlp/plugins/songs/lib/importers/easyslides.py'
--- openlp/plugins/songs/lib/importers/easyslides.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/songs/lib/importers/easyslides.py 2019-08-06 21:48:11 +0000
@@ -137,9 +137,11 @@
137 except UnicodeDecodeError:137 except UnicodeDecodeError:
138 log.exception('Unicode decode error while decoding Contents')138 log.exception('Unicode decode error while decoding Contents')
139 self._success = False139 self._success = False
140 return
140 except AttributeError:141 except AttributeError:
141 log.exception('no Contents')142 log.exception('no Contents')
142 self._success = False143 self._success = False
144 return
143 lines = lyrics.split('\n')145 lines = lyrics.split('\n')
144 # we go over all lines first, to determine information,146 # we go over all lines first, to determine information,
145 # which tells us how to parse verses later147 # which tells us how to parse verses later
146148
=== modified file 'openlp/plugins/songs/lib/importers/easyworship.py'
--- openlp/plugins/songs/lib/importers/easyworship.py 2019-07-03 13:23:23 +0000
+++ openlp/plugins/songs/lib/importers/easyworship.py 2019-08-06 21:48:11 +0000
@@ -268,13 +268,13 @@
268 self.db_set_record_struct(field_descriptions)268 self.db_set_record_struct(field_descriptions)
269 # Pick out the field description indexes we will need269 # Pick out the field description indexes we will need
270 try:270 try:
271 success = True
272 fi_title = self.db_find_field(b'Title')271 fi_title = self.db_find_field(b'Title')
273 fi_author = self.db_find_field(b'Author')272 fi_author = self.db_find_field(b'Author')
274 fi_copy = self.db_find_field(b'Copyright')273 fi_copy = self.db_find_field(b'Copyright')
275 fi_admin = self.db_find_field(b'Administrator')274 fi_admin = self.db_find_field(b'Administrator')
276 fi_words = self.db_find_field(b'Words')275 fi_words = self.db_find_field(b'Words')
277 fi_ccli = self.db_find_field(b'Song Number')276 fi_ccli = self.db_find_field(b'Song Number')
277 success = True
278 except IndexError:278 except IndexError:
279 # This is the wrong table279 # This is the wrong table
280 success = False280 success = False
281281
=== modified file 'openlp/plugins/songs/lib/importers/songbeamer.py'
--- openlp/plugins/songs/lib/importers/songbeamer.py 2019-05-22 06:47:00 +0000
+++ openlp/plugins/songs/lib/importers/songbeamer.py 2019-08-06 21:48:11 +0000
@@ -128,7 +128,7 @@
128 # The encoding should only be ANSI (cp1252), UTF-8, Unicode, Big-Endian-Unicode.128 # The encoding should only be ANSI (cp1252), UTF-8, Unicode, Big-Endian-Unicode.
129 # So if it doesn't start with 'u' we default to cp1252. See:129 # So if it doesn't start with 'u' we default to cp1252. See:
130 # https://forum.songbeamer.com/viewtopic.php?p=419&sid=ca4814924e37c11e4438b7272a98b6f2130 # https://forum.songbeamer.com/viewtopic.php?p=419&sid=ca4814924e37c11e4438b7272a98b6f2
131 if not self.input_file_encoding.lower().startswith('u'):131 if self.input_file_encoding and not self.input_file_encoding.lower().startswith('u'):
132 self.input_file_encoding = 'cp1252'132 self.input_file_encoding = 'cp1252'
133 with file_path.open(encoding=self.input_file_encoding) as song_file:133 with file_path.open(encoding=self.input_file_encoding) as song_file:
134 song_data = song_file.readlines()134 song_data = song_file.readlines()
135135
=== modified file 'tests/functional/openlp_core/common/test_common.py'
--- tests/functional/openlp_core/common/test_common.py 2019-05-22 06:47:00 +0000
+++ tests/functional/openlp_core/common/test_common.py 2019-08-06 21:48:11 +0000
@@ -26,7 +26,7 @@
26from unittest import TestCase26from unittest import TestCase
27from unittest.mock import MagicMock, call, patch27from unittest.mock import MagicMock, call, patch
2828
29from openlp.core.common import clean_button_text, de_hump, extension_loader, is_linux, is_macosx, is_win, \29from openlp.core.common import Singleton, clean_button_text, de_hump, extension_loader, is_linux, is_macosx, is_win, \
30 normalize_str, path_to_module, trace_error_handler30 normalize_str, path_to_module, trace_error_handler
3131
3232
@@ -163,6 +163,48 @@
163 mocked_logger.error.assert_called_with(163 mocked_logger.error.assert_called_with(
164 'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test')164 'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test')
165165
166 def test_singleton_metaclass_multiple_init(self):
167 """
168 Test that a class using the Singleton Metaclass is only initialised once despite being called several times and
169 that the same instance is returned each time..
170 """
171 # GIVEN: The Singleton Metaclass and a test class using it
172 class SingletonClass(metaclass=Singleton):
173 def __init__(self):
174 pass
175
176 with patch.object(SingletonClass, '__init__', return_value=None) as patched_init:
177
178 # WHEN: Initialising the class multiple times
179 inst_1 = SingletonClass()
180 inst_2 = SingletonClass()
181
182 # THEN: The __init__ method of the SingletonClass should have only been called once, and both returned values
183 # should be the same instance.
184 assert inst_1 is inst_2
185 assert patched_init.call_count == 1
186
187 def test_singleton_metaclass_multiple_classes(self):
188 """
189 Test that multiple classes using the Singleton Metaclass return the different an appropriate instances.
190 """
191 # GIVEN: Two different classes using the Singleton Metaclass
192 class SingletonClass1(metaclass=Singleton):
193 def __init__(self):
194 pass
195
196 class SingletonClass2(metaclass=Singleton):
197 def __init__(self):
198 pass
199
200 # WHEN: Initialising both classes
201 s_c1 = SingletonClass1()
202 s_c2 = SingletonClass2()
203
204 # THEN: The instances should be an instance of the appropriate class
205 assert isinstance(s_c1, SingletonClass1)
206 assert isinstance(s_c2, SingletonClass2)
207
166 def test_is_win(self):208 def test_is_win(self):
167 """209 """
168 Test the is_win() function210 Test the is_win() function
169211
=== modified file 'tests/functional/openlp_core/lib/test_theme.py'
--- tests/functional/openlp_core/lib/test_theme.py 2019-07-18 19:14:58 +0000
+++ tests/functional/openlp_core/lib/test_theme.py 2019-08-06 21:48:11 +0000
@@ -182,4 +182,4 @@
182 assert 0 == theme.display_vertical_align, 'display_vertical_align should be 0'182 assert 0 == theme.display_vertical_align, 'display_vertical_align should be 0'
183 assert theme.font_footer_bold is False, 'font_footer_bold should be False'183 assert theme.font_footer_bold is False, 'font_footer_bold should be False'
184 assert 'Arial' == theme.font_main_name, 'font_main_name should be "Arial"'184 assert 'Arial' == theme.font_main_name, 'font_main_name should be "Arial"'
185 assert 47 == len(theme.__dict__), 'The theme should have 47 attributes'185 assert 48 == len(theme.__dict__), 'The theme should have 48 attributes'
186186
=== modified file 'tests/functional/openlp_core/ui/test_icons.py'
--- tests/functional/openlp_core/ui/test_icons.py 2019-04-13 13:00:22 +0000
+++ tests/functional/openlp_core/ui/test_icons.py 2019-08-06 21:48:11 +0000
@@ -33,7 +33,7 @@
3333
34class TestIcons(TestCase, TestMixin):34class TestIcons(TestCase, TestMixin):
3535
36 @patch('openlp.core.ui.icons.UiIcons.load')36 @patch('openlp.core.ui.icons.UiIcons.__init__', return_value=None)
37 def test_simple_icon(self, _):37 def test_simple_icon(self, _):
38 # GIVEN: an basic set of icons38 # GIVEN: an basic set of icons
39 icons = UiIcons()39 icons = UiIcons()