mirror of https://gitlab.com/openlp/openlp.git
Head
This commit is contained in:
commit
82ca72471e
|
@ -67,6 +67,7 @@ class UiStrings(object):
|
|||
self.Browse = translate('OpenLP.Ui', 'Browse...')
|
||||
self.Cancel = translate('OpenLP.Ui', 'Cancel')
|
||||
self.CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:')
|
||||
self.CCLISongNumberLabel = translate('OpenLP.Ui', 'CCLI song number:')
|
||||
self.CreateService = translate('OpenLP.Ui', 'Create a new service.')
|
||||
self.ConfirmDelete = translate('OpenLP.Ui', 'Confirm Delete')
|
||||
self.Continuous = translate('OpenLP.Ui', 'Continuous')
|
||||
|
@ -115,7 +116,7 @@ class UiStrings(object):
|
|||
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
|
||||
self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
|
||||
self.OLPV2 = translate('OpenLP.Ui', 'OpenLP 2')
|
||||
self.OLPV2x = translate('OpenLP.Ui', 'OpenLP 2.1')
|
||||
self.OLPV2x = translate('OpenLP.Ui', 'OpenLP 2.2')
|
||||
self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?')
|
||||
self.OpenService = translate('OpenLP.Ui', 'Open service.')
|
||||
self.PlaySlidesInLoop = translate('OpenLP.Ui', 'Play Slides in Loop')
|
||||
|
|
|
@ -217,6 +217,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
|
|||
if self.current_media_players[source].state != MediaState.Paused:
|
||||
display = self._define_display(self.display_controllers[source])
|
||||
display.controller.seek_slider.setSliderPosition(0)
|
||||
display.controller.mediabar.actions['playbackPlay'].setVisible(True)
|
||||
display.controller.mediabar.actions['playbackPause'].setVisible(False)
|
||||
self.timer.stop()
|
||||
|
||||
def get_media_display_css(self):
|
||||
|
|
|
@ -883,7 +883,8 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
|
|||
# TODO for future: make group explains itself more visually
|
||||
else:
|
||||
self.auto_play_slides_menu.menuAction().setVisible(False)
|
||||
if service_item['service_item'].is_capable(ItemCapabilities.HasVariableStartTime):
|
||||
if service_item['service_item'].is_capable(ItemCapabilities.HasVariableStartTime) and \
|
||||
not service_item['service_item'].is_capable(ItemCapabilities.IsOptical):
|
||||
self.time_action.setVisible(True)
|
||||
if service_item['service_item'].is_capable(ItemCapabilities.CanAutoStartForLive):
|
||||
self.auto_start_action.setVisible(True)
|
||||
|
@ -1478,8 +1479,6 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
|
|||
if self.service_items and item < len(self.service_items) and \
|
||||
self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanPreview):
|
||||
self.preview_controller.add_service_manager_item(self.service_items[item]['service_item'], 0)
|
||||
next_item = self.service_manager_list.topLevelItem(item)
|
||||
self.service_manager_list.setCurrentItem(next_item)
|
||||
self.live_controller.preview_widget.setFocus()
|
||||
else:
|
||||
critical_error_message_box(translate('OpenLP.ServiceManager', 'Missing Display Handler'),
|
||||
|
|
|
@ -33,6 +33,7 @@ The :mod:`slidecontroller` module contains the most important part of OpenLP - t
|
|||
import os
|
||||
import copy
|
||||
from collections import deque
|
||||
from threading import Lock
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
|
@ -131,6 +132,8 @@ class SlideController(DisplayController, RegistryProperties):
|
|||
self.ratio = self.screens.current['size'].width() / self.screens.current['size'].height()
|
||||
except ZeroDivisionError:
|
||||
self.ratio = 1
|
||||
self.process_queue_lock = Lock()
|
||||
self.slide_selected_lock = Lock()
|
||||
self.timer_id = 0
|
||||
self.song_edit = False
|
||||
self.selected_row = 0
|
||||
|
@ -531,9 +534,9 @@ class SlideController(DisplayController, RegistryProperties):
|
|||
Process the service item request queue. The key presses can arrive
|
||||
faster than the processing so implement a FIFO queue.
|
||||
"""
|
||||
if self.keypress_queue:
|
||||
while len(self.keypress_queue) and not self.keypress_loop:
|
||||
self.keypress_loop = True
|
||||
# Make sure only one thread get in here. Just return if already locked.
|
||||
if self.keypress_queue and self.process_queue_lock.acquire(False):
|
||||
while len(self.keypress_queue):
|
||||
keypress_command = self.keypress_queue.popleft()
|
||||
if keypress_command == ServiceItemAction.Previous:
|
||||
self.service_manager.previous_item()
|
||||
|
@ -542,7 +545,7 @@ class SlideController(DisplayController, RegistryProperties):
|
|||
self.service_manager.previous_item(last_slide=True)
|
||||
else:
|
||||
self.service_manager.next_item()
|
||||
self.keypress_loop = False
|
||||
self.process_queue_lock.release()
|
||||
|
||||
def screen_size_changed(self):
|
||||
"""
|
||||
|
@ -1039,6 +1042,10 @@ class SlideController(DisplayController, RegistryProperties):
|
|||
|
||||
:param start:
|
||||
"""
|
||||
# Only one thread should be in here at the time. If already locked just skip, since the update will be
|
||||
# done by the thread holding the lock. If it is a "start" slide, we must wait for the lock.
|
||||
if not self.slide_selected_lock.acquire(start):
|
||||
return
|
||||
row = self.preview_widget.current_slide_number()
|
||||
self.selected_row = 0
|
||||
if -1 < row < self.preview_widget.slide_count():
|
||||
|
@ -1061,6 +1068,8 @@ class SlideController(DisplayController, RegistryProperties):
|
|||
self.update_preview()
|
||||
self.preview_widget.change_slide(row)
|
||||
self.display.setFocus()
|
||||
# Release lock
|
||||
self.slide_selected_lock.release()
|
||||
|
||||
def on_slide_change(self, row):
|
||||
"""
|
||||
|
@ -1405,7 +1414,6 @@ class LiveController(RegistryMixin, OpenLPMixin, SlideController):
|
|||
self.split = 1
|
||||
self.type_prefix = 'live'
|
||||
self.keypress_queue = deque()
|
||||
self.keypress_loop = False
|
||||
self.category = UiStrings().LiveToolbar
|
||||
ActionList.get_instance().add_category(str(self.category), CategoryOrder.standard_toolbar)
|
||||
|
||||
|
|
|
@ -56,7 +56,9 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog, RegistryProperties):
|
|||
self.hour_spin_box.setValue(hour)
|
||||
self.minute_spin_box.setValue(minutes)
|
||||
self.second_spin_box.setValue(seconds)
|
||||
hours, minutes, seconds = self._time_split(self.item['service_item'].media_length)
|
||||
hours, minutes, seconds = self._time_split(self.item['service_item'].end_time)
|
||||
if hours == 0 and minutes == 0 and seconds == 0:
|
||||
hours, minutes, seconds = self._time_split(self.item['service_item'].media_length)
|
||||
self.hour_finish_spin_box.setValue(hours)
|
||||
self.minute_finish_spin_box.setValue(minutes)
|
||||
self.second_finish_spin_box.setValue(seconds)
|
||||
|
|
|
@ -93,10 +93,10 @@ class BGExtract(RegistryProperties):
|
|||
"""
|
||||
if isinstance(tag, NavigableString):
|
||||
return None, str(tag)
|
||||
elif tag.get('class')[0] == "versenum" or tag.get('class')[0] == 'versenum mid-line':
|
||||
elif tag.get('class') and (tag.get('class')[0] == 'versenum' or tag.get('class')[0] == 'versenum mid-line'):
|
||||
verse = str(tag.string).replace('[', '').replace(']', '').strip()
|
||||
return verse, None
|
||||
elif tag.get('class')[0] == 'chapternum':
|
||||
elif tag.get('class') and tag.get('class')[0] == 'chapternum':
|
||||
verse = '1'
|
||||
return verse, None
|
||||
else:
|
||||
|
|
|
@ -96,7 +96,6 @@ class CustomMediaItem(MediaManagerItem):
|
|||
def retranslateUi(self):
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
self.search_text_label.setText('%s:' % UiStrings().Search)
|
||||
self.search_text_button.setText(UiStrings().Search)
|
||||
|
@ -134,6 +133,7 @@ class CustomMediaItem(MediaManagerItem):
|
|||
# Called to redisplay the custom list screen edith from a search
|
||||
# or from the exit of the Custom edit dialog. If remote editing is
|
||||
# active trigger it and clean up so it will not update again.
|
||||
self.check_search_result()
|
||||
|
||||
def on_new_click(self):
|
||||
"""
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
import logging
|
||||
import os
|
||||
from datetime import time
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
|
@ -126,18 +125,18 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
|||
Adds buttons to the start of the header bar.
|
||||
"""
|
||||
if 'vlc' in get_media_players()[0]:
|
||||
diable_optical_button_text = False
|
||||
disable_optical_button_text = False
|
||||
optical_button_text = translate('MediaPlugin.MediaItem', 'Load CD/DVD')
|
||||
optical_button_tooltip = translate('MediaPlugin.MediaItem', 'Load CD/DVD')
|
||||
else:
|
||||
diable_optical_button_text = True
|
||||
disable_optical_button_text = True
|
||||
optical_button_text = translate('MediaPlugin.MediaItem', 'Load CD/DVD')
|
||||
optical_button_tooltip = translate('MediaPlugin.MediaItem',
|
||||
'Load CD/DVD - only supported when VLC is installed and enabled')
|
||||
self.load_optical = self.toolbar.add_toolbar_action('load_optical', icon=OPTICAL_ICON, text=optical_button_text,
|
||||
tooltip=optical_button_tooltip,
|
||||
triggers=self.on_load_optical)
|
||||
if diable_optical_button_text:
|
||||
if disable_optical_button_text:
|
||||
self.load_optical.setDisabled(True)
|
||||
|
||||
def add_end_header_bar(self):
|
||||
|
@ -282,7 +281,6 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
|||
Initialize media item.
|
||||
"""
|
||||
self.list_view.clear()
|
||||
self.list_view.setIconSize(QtCore.QSize(88, 50))
|
||||
self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
|
||||
check_directory_exists(self.service_path)
|
||||
self.load_list(Settings().value(self.settings_section + '/media files'))
|
||||
|
|
|
@ -306,9 +306,9 @@ class HttpRouter(RegistryProperties):
|
|||
Translate various strings in the mobile app.
|
||||
"""
|
||||
self.template_vars = {
|
||||
'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Remote'),
|
||||
'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Stage View'),
|
||||
'live_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Live View'),
|
||||
'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.2 Remote'),
|
||||
'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.2 Stage View'),
|
||||
'live_title': translate('RemotePlugin.Mobile', 'OpenLP 2.2 Live View'),
|
||||
'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
|
||||
'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
|
||||
'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
|
||||
|
|
|
@ -323,7 +323,7 @@ class Ui_EditSongDialog(object):
|
|||
self.theme_add_button.setText(translate('SongsPlugin.EditSongForm', 'New &Theme'))
|
||||
self.rights_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Copyright Information'))
|
||||
self.copyright_insert_button.setText(SongStrings.CopyrightSymbol)
|
||||
self.ccli_label.setText(UiStrings().CCLINumberLabel)
|
||||
self.ccli_label.setText(UiStrings().CCLISongNumberLabel)
|
||||
self.comments_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Comments'))
|
||||
self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.theme_tab),
|
||||
translate('SongsPlugin.EditSongForm', 'Theme, Copyright Info && Comments'))
|
||||
|
|
|
@ -265,7 +265,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||
return False
|
||||
return True
|
||||
|
||||
def _validate_tags(self, tags):
|
||||
def _validate_tags(self, tags, first_time=True):
|
||||
"""
|
||||
Validates a list of tags
|
||||
Deletes the first affiliated tag pair which is located side by side in the list
|
||||
|
@ -277,6 +277,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||
:param tags: A list of tags
|
||||
:return: True if the function can't find any mismatched tags. Else False.
|
||||
"""
|
||||
if first_time:
|
||||
fixed_tags = []
|
||||
for i in range(len(tags)):
|
||||
if tags[i] != '{br}':
|
||||
fixed_tags.append(tags[i])
|
||||
tags = fixed_tags
|
||||
if len(tags) == 0:
|
||||
return True
|
||||
if len(tags) % 2 != 0:
|
||||
|
@ -284,7 +290,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||
for i in range(len(tags)-1):
|
||||
if tags[i+1] == "{/" + tags[i][1:]:
|
||||
del tags[i:i+2]
|
||||
return self._validate_tags(tags)
|
||||
return self._validate_tags(tags, False)
|
||||
return False
|
||||
|
||||
def _process_lyrics(self):
|
||||
|
|
|
@ -84,13 +84,15 @@ class AuthorType(object):
|
|||
NoType,
|
||||
Words,
|
||||
Music,
|
||||
WordsAndMusic
|
||||
WordsAndMusic,
|
||||
Translation
|
||||
]
|
||||
TranslatedTypes = [
|
||||
Types[NoType],
|
||||
Types[Words],
|
||||
Types[Music],
|
||||
Types[WordsAndMusic]
|
||||
Types[WordsAndMusic],
|
||||
Types[Translation]
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -382,7 +382,7 @@ class TestUtils(TestCase):
|
|||
mocked_page_object = MagicMock()
|
||||
mock_urlopen.return_value = mocked_page_object
|
||||
fake_url = 'this://is.a.fake/url'
|
||||
user_agent_header = ('User-Agent', 'OpenLP/2.1.0')
|
||||
user_agent_header = ('User-Agent', 'OpenLP/2.2.0')
|
||||
|
||||
# WHEN: The get_web_page() method is called
|
||||
returned_page = get_web_page(fake_url, header=user_agent_header)
|
||||
|
|
|
@ -158,9 +158,9 @@ class TestDB(TestCase):
|
|||
# THEN: It should return only the name
|
||||
self.assertEqual("John Doe", display_name)
|
||||
|
||||
def test_author_get_display_name_with_type(self):
|
||||
def test_author_get_display_name_with_type_words(self):
|
||||
"""
|
||||
Test that the display name of an author with a type is correct
|
||||
Test that the display name of an author with a type is correct (Words)
|
||||
"""
|
||||
# GIVEN: An author
|
||||
author = Author()
|
||||
|
@ -172,6 +172,20 @@ class TestDB(TestCase):
|
|||
# THEN: It should return the name with the type in brackets
|
||||
self.assertEqual("John Doe (Words)", display_name)
|
||||
|
||||
def test_author_get_display_name_with_type_translation(self):
|
||||
"""
|
||||
Test that the display name of an author with a type is correct (Translation)
|
||||
"""
|
||||
# GIVEN: An author
|
||||
author = Author()
|
||||
author.display_name = "John Doe"
|
||||
|
||||
# WHEN: We call the get_display_name() function
|
||||
display_name = author.get_display_name(AuthorType.Translation)
|
||||
|
||||
# THEN: It should return the name with the type in brackets
|
||||
self.assertEqual("John Doe (Translation)", display_name)
|
||||
|
||||
def test_upgrade_old_song_db(self):
|
||||
"""
|
||||
Test that we can upgrade an old song db to the current schema
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
"""
|
||||
This module contains tests for the lib submodule of the Songs plugin.
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.common import Registry, Settings
|
||||
from openlp.core.lib import ServiceItem
|
||||
from openlp.plugins.songs.forms.editsongform import EditSongForm
|
||||
from openlp.plugins.songs.lib.db import AuthorType
|
||||
from tests.functional import patch, MagicMock
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
class TestEditSongForm(TestCase, TestMixin):
|
||||
"""
|
||||
Test the functions in the :mod:`lib` module.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the components need for all tests.
|
||||
"""
|
||||
Registry.create()
|
||||
Registry().register('service_list', MagicMock())
|
||||
Registry().register('main_window', MagicMock())
|
||||
with patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__', return_value=None):
|
||||
self.edit_song_form = EditSongForm(None, MagicMock(), MagicMock())
|
||||
self.setup_application()
|
||||
self.build_settings()
|
||||
QtCore.QLocale.setDefault(QtCore.QLocale('en_GB'))
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
self.destroy_settings()
|
||||
|
||||
def validate_matching_tags_test(self):
|
||||
# Given a set of tags
|
||||
tags = ['{r}', '{/r}', '{bl}', '{/bl}', '{su}', '{/su}']
|
||||
|
||||
# WHEN we validate them
|
||||
valid = self.edit_song_form._validate_tags(tags)
|
||||
|
||||
# THEN they should be valid
|
||||
self.assertTrue(valid, "The tags list should be valid")
|
||||
|
||||
def validate_nonmatching_tags_test(self):
|
||||
# Given a set of tags
|
||||
tags = ['{r}', '{/r}', '{bl}', '{/bl}', '{br}', '{su}', '{/su}']
|
||||
|
||||
# WHEN we validate them
|
||||
valid = self.edit_song_form._validate_tags(tags)
|
||||
|
||||
# THEN they should be valid
|
||||
self.assertTrue(valid, "The tags list should be valid")
|
|
@ -85,6 +85,19 @@ class TestBibleHTTP(TestCase):
|
|||
# THEN: We should get back a valid service item
|
||||
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed'
|
||||
|
||||
def bible_gateway_extract_verse_nkjv_test(self):
|
||||
"""
|
||||
Test the Bible Gateway retrieval of verse list for NKJV bible John 3
|
||||
"""
|
||||
# GIVEN: A new Bible Gateway extraction class
|
||||
handler = BGExtract()
|
||||
|
||||
# WHEN: The Books list is called
|
||||
results = handler.get_bible_chapter('NKJV', 'John', 3)
|
||||
|
||||
# THEN: We should get back a valid service item
|
||||
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed'
|
||||
|
||||
def crosswalk_extract_books_test(self):
|
||||
"""
|
||||
Test Crosswalk retrieval of book list for NIV bible
|
||||
|
|
|
@ -34,6 +34,7 @@ from unittest import TestCase
|
|||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.common.uistrings import UiStrings
|
||||
from openlp.plugins.songs.forms.editsongform import EditSongForm
|
||||
from tests.interfaces import MagicMock
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
@ -136,3 +137,15 @@ class TestEditSongForm(TestCase, TestMixin):
|
|||
# THEN: The no-verse-order message should be shown.
|
||||
assert self.form.warning_label.text() == self.form.no_verse_order_entered_warning, \
|
||||
'The no-verse-order message should be shown.'
|
||||
|
||||
def bug_1404967_test(self):
|
||||
"""
|
||||
Test for CCLI label showing correct text
|
||||
"""
|
||||
# GIVEN; Mocked methods
|
||||
form = self.form
|
||||
# THEN: CCLI label should be CCLI song label
|
||||
self.assertNotEquals(form.ccli_label.text(), UiStrings().CCLINumberLabel,
|
||||
'CCLI label should not be "{}"'.format(UiStrings().CCLINumberLabel))
|
||||
self.assertEquals(form.ccli_label.text(), UiStrings().CCLISongNumberLabel,
|
||||
'CCLI label text should be "{}"'.format(UiStrings().CCLISongNumberLabel))
|
||||
|
|
Loading…
Reference in New Issue