This commit is contained in:
Tim Bentley 2014-12-31 10:53:01 +00:00
commit 82ca72471e
17 changed files with 144 additions and 29 deletions

View File

@ -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')

View File

@ -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):

View File

@ -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'),

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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):
"""

View File

@ -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'))

View File

@ -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'),

View File

@ -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'))

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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))