forked from openlp/openlp
head
This commit is contained in:
commit
49384aa672
@ -101,7 +101,7 @@ class OpenLP(QtWidgets.QApplication):
|
||||
ftw.initialize(screens)
|
||||
if ftw.exec() == QtWidgets.QDialog.Accepted:
|
||||
Settings().setValue('core/has run wizard', True)
|
||||
elif ftw.was_cancelled:
|
||||
else:
|
||||
QtCore.QCoreApplication.exit()
|
||||
sys.exit()
|
||||
# Correct stylesheet bugs
|
||||
|
@ -158,16 +158,20 @@ def get_web_page(url, headers=None, update_openlp=False, proxy=None):
|
||||
return response.text
|
||||
|
||||
|
||||
def get_url_file_size(url):
|
||||
def get_url_file_size(url, proxy=None):
|
||||
"""
|
||||
Get the size of a file.
|
||||
|
||||
:param url: The URL of the file we want to download.
|
||||
:param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types
|
||||
as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'}
|
||||
"""
|
||||
retries = 0
|
||||
if not isinstance(proxy, dict):
|
||||
proxy = get_proxy_settings(mode=proxy)
|
||||
while True:
|
||||
try:
|
||||
response = requests.head(url, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True)
|
||||
response = requests.head(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True)
|
||||
return int(response.headers['Content-Length'])
|
||||
except OSError:
|
||||
if retries > CONNECTION_RETRIES:
|
||||
@ -178,7 +182,7 @@ def get_url_file_size(url):
|
||||
continue
|
||||
|
||||
|
||||
def download_file(update_object, url, file_path, sha256=None):
|
||||
def download_file(update_object, url, file_path, sha256=None, proxy=None):
|
||||
""""
|
||||
Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any
|
||||
point. Returns False on download error.
|
||||
@ -187,15 +191,19 @@ def download_file(update_object, url, file_path, sha256=None):
|
||||
:param url: URL to download
|
||||
:param file_path: Destination file
|
||||
:param sha256: The check sum value to be checked against the download value
|
||||
:param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types
|
||||
as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'}
|
||||
"""
|
||||
block_count = 0
|
||||
block_size = 4096
|
||||
retries = 0
|
||||
if not isinstance(proxy, dict):
|
||||
proxy = get_proxy_settings(mode=proxy)
|
||||
log.debug('url_get_file: %s', url)
|
||||
while retries < CONNECTION_RETRIES:
|
||||
try:
|
||||
with file_path.open('wb') as saved_file:
|
||||
response = requests.get(url, timeout=float(CONNECTION_TIMEOUT), stream=True)
|
||||
response = requests.get(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), stream=True)
|
||||
if sha256:
|
||||
hasher = hashlib.sha256()
|
||||
# Download until finished or canceled.
|
||||
@ -244,21 +252,21 @@ class DownloadWorker(ThreadWorker):
|
||||
"""
|
||||
self._base_url = base_url
|
||||
self._file_name = file_name
|
||||
self._download_cancelled = False
|
||||
self.was_cancelled = False
|
||||
super().__init__()
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Download the url to the temporary directory
|
||||
"""
|
||||
if self._download_cancelled:
|
||||
if self.was_cancelled:
|
||||
self.quit.emit()
|
||||
return
|
||||
try:
|
||||
dest_path = Path(gettempdir()) / 'openlp' / self._file_name
|
||||
url = '{url}{name}'.format(url=self._base_url, name=self._file_name)
|
||||
is_success = download_file(self, url, dest_path)
|
||||
if is_success and not self._download_cancelled:
|
||||
if is_success and not self.was_cancelled:
|
||||
self.download_succeeded.emit(dest_path)
|
||||
else:
|
||||
self.download_failed.emit()
|
||||
@ -273,4 +281,4 @@ class DownloadWorker(ThreadWorker):
|
||||
"""
|
||||
A slot to allow the download to be cancelled from outside of the thread
|
||||
"""
|
||||
self._download_cancelled = True
|
||||
self.was_cancelled = True
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -43,8 +43,8 @@ from openlp.core.lib.formattingtags import FormattingTags
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
SLIM_CHARS = 'fiíIÍjlĺľrtť.,;/ ()|"\'!:\\'
|
||||
CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)' # noqa
|
||||
'([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?')
|
||||
CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)'
|
||||
r'([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?')
|
||||
CHORD_TEMPLATE = '<span class="chordline">{chord}</span>'
|
||||
FIRST_CHORD_TEMPLATE = '<span class="chordline firstchordline">{chord}</span>'
|
||||
CHORD_LINE_TEMPLATE = '<span class="chord"><span><strong>{chord}</strong></span></span>{tail}{whitespace}{remainder}'
|
||||
|
@ -81,7 +81,8 @@ class ServiceItem(RegistryProperties):
|
||||
self.items = []
|
||||
self.icon = UiIcons().default
|
||||
self.raw_footer = []
|
||||
self.foot_text = ''
|
||||
# Plugins can set footer_html themselves. If they don't, it will be generated from raw_footer.
|
||||
self.footer_html = ''
|
||||
self.theme = None
|
||||
self.service_item_type = None
|
||||
self.unique_identifier = 0
|
||||
@ -165,7 +166,8 @@ class ServiceItem(RegistryProperties):
|
||||
# the dict instead of rendering them again.
|
||||
previous_pages = {}
|
||||
index = 0
|
||||
self.foot_text = '<br>'.join([_f for _f in self.raw_footer if _f])
|
||||
if not self.footer_html:
|
||||
self.footer_html = '<br>'.join([_f for _f in self.raw_footer if _f])
|
||||
for raw_slide in self.slides:
|
||||
verse_tag = raw_slide['verse']
|
||||
if verse_tag in previous_pages and previous_pages[verse_tag][0] == raw_slide:
|
||||
@ -178,7 +180,7 @@ class ServiceItem(RegistryProperties):
|
||||
'title': raw_slide['title'],
|
||||
'text': render_tags(page),
|
||||
'verse': index,
|
||||
'footer': self.foot_text,
|
||||
'footer': self.footer_html,
|
||||
}
|
||||
self._rendered_slides.append(rendered_slide)
|
||||
display_slide = {
|
||||
|
@ -76,11 +76,11 @@ def get_thread_worker(thread_name):
|
||||
Get the worker by the thread name
|
||||
|
||||
:param str thread_name: The name of the thread
|
||||
:returns: The worker for this thread name
|
||||
:returns: The worker for this thread name, or None
|
||||
"""
|
||||
thread_info = Registry().get('application').worker_threads.get(thread_name)
|
||||
if not thread_info:
|
||||
raise KeyError('No thread named "{}" exists'.format(thread_name))
|
||||
return
|
||||
return thread_info.get('worker')
|
||||
|
||||
|
||||
|
@ -32,7 +32,7 @@ from tempfile import gettempdir
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common import clean_button_text, trace_error_handler
|
||||
from openlp.core.common import trace_error_handler
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.httputils import DownloadWorker, download_file, get_url_file_size, get_web_page
|
||||
from openlp.core.common.i18n import translate
|
||||
@ -46,6 +46,7 @@ from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.threading import get_thread_worker, is_thread_finished, run_thread
|
||||
from openlp.core.ui.firsttimewizard import FirstTimePage, UiFirstTimeWizard
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.widgets.widgets import ProxyDialog
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -91,7 +92,7 @@ class ThemeListWidgetItem(QtWidgets.QListWidgetItem):
|
||||
|
||||
class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
"""
|
||||
This is the Theme Import Wizard, which allows easy creation and editing of OpenLP themes.
|
||||
This is the FirstTimeWizard, designed to help new users to get up and running quickly.
|
||||
"""
|
||||
log.info('ThemeWizardForm loaded')
|
||||
|
||||
@ -103,6 +104,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
self.web_access = True
|
||||
self.web = ''
|
||||
self.setup_ui(self)
|
||||
self.customButtonClicked.connect(self._on_custom_button_clicked)
|
||||
self.themes_list_widget.itemSelectionChanged.connect(self.on_themes_list_widget_selection_changed)
|
||||
self.themes_deselect_all_button.clicked.connect(self.themes_list_widget.clearSelection)
|
||||
self.themes_select_all_button.clicked.connect(self.themes_list_widget.selectAll)
|
||||
@ -111,13 +113,13 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
"""
|
||||
Returns the id of the next FirstTimePage to go to based on enabled plugins
|
||||
"""
|
||||
if FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked():
|
||||
if FirstTimePage.Download < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked():
|
||||
# If the songs plugin is enabled then go to the songs page
|
||||
return FirstTimePage.Songs
|
||||
elif FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked():
|
||||
elif FirstTimePage.Download < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked():
|
||||
# Otherwise, if the Bibles plugin is enabled then go to the Bibles page
|
||||
return FirstTimePage.Bibles
|
||||
elif FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Themes:
|
||||
elif FirstTimePage.Download < self.currentId() < FirstTimePage.Themes:
|
||||
# Otherwise, if the current page is somewhere between the Welcome and the Themes pages, go to the themes
|
||||
return FirstTimePage.Themes
|
||||
else:
|
||||
@ -133,9 +135,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
if not self.web_access:
|
||||
return FirstTimePage.NoInternet
|
||||
else:
|
||||
return FirstTimePage.Plugins
|
||||
elif self.currentId() == FirstTimePage.Plugins:
|
||||
return self.get_next_page_id()
|
||||
return FirstTimePage.Songs
|
||||
elif self.currentId() == FirstTimePage.Progress:
|
||||
return -1
|
||||
elif self.currentId() == FirstTimePage.NoInternet:
|
||||
@ -147,7 +147,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
Run the wizard.
|
||||
"""
|
||||
self.set_defaults()
|
||||
return QtWidgets.QWizard.exec(self)
|
||||
return super().exec()
|
||||
|
||||
def initialize(self, screens):
|
||||
"""
|
||||
@ -227,17 +227,13 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
"""
|
||||
self.restart()
|
||||
self.web = 'https://get.openlp.org/ftw/'
|
||||
self.cancel_button.clicked.connect(self.on_cancel_button_clicked)
|
||||
self.no_internet_finish_button.clicked.connect(self.on_no_internet_finish_button_clicked)
|
||||
self.no_internet_cancel_button.clicked.connect(self.on_no_internet_cancel_button_clicked)
|
||||
self.currentIdChanged.connect(self.on_current_id_changed)
|
||||
Registry().register_function('config_screen_changed', self.screen_selection_widget.load)
|
||||
self.no_internet_finish_button.setVisible(False)
|
||||
self.no_internet_cancel_button.setVisible(False)
|
||||
# Check if this is a re-run of the wizard.
|
||||
self.has_run_wizard = Settings().value('core/has run wizard')
|
||||
create_paths(Path(gettempdir(), 'openlp'))
|
||||
self.theme_combo_box.clear()
|
||||
self.button(QtWidgets.QWizard.CustomButton1).setVisible(False)
|
||||
if self.has_run_wizard:
|
||||
self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active())
|
||||
self.bible_check_box.setChecked(self.plugin_manager.get_plugin_by_name('bibles').is_active())
|
||||
@ -260,48 +256,62 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
"""
|
||||
Detects Page changes and updates as appropriate.
|
||||
"""
|
||||
# Keep track of the page we are at. Triggering "Cancel" causes page_id to be a -1.
|
||||
back_button = self.button(QtWidgets.QWizard.BackButton)
|
||||
cancel_button = self.button(QtWidgets.QWizard.CancelButton)
|
||||
internet_settings_button = self.button(QtWidgets.QWizard.CustomButton1)
|
||||
next_button = self.button(QtWidgets.QWizard.NextButton)
|
||||
back_button.setVisible(True)
|
||||
next_button.setVisible(True)
|
||||
internet_settings_button.setVisible(False)
|
||||
self.application.process_events()
|
||||
if page_id != -1:
|
||||
self.last_id = page_id
|
||||
if page_id == FirstTimePage.Download:
|
||||
self.back_button.setVisible(False)
|
||||
self.next_button.setVisible(False)
|
||||
# Set the no internet page text.
|
||||
if self.has_run_wizard:
|
||||
self.no_internet_label.setText(self.no_internet_text)
|
||||
else:
|
||||
self.no_internet_label.setText(self.no_internet_text + self.cancel_wizard_text)
|
||||
if page_id == FirstTimePage.SampleOption:
|
||||
internet_settings_button.setVisible(True)
|
||||
elif page_id == FirstTimePage.Download:
|
||||
back_button.setVisible(False)
|
||||
next_button.setVisible(False)
|
||||
self.application.set_busy_cursor()
|
||||
self._download_index()
|
||||
self.application.set_normal_cursor()
|
||||
self.back_button.setVisible(False)
|
||||
self.next_button.setVisible(True)
|
||||
self.next()
|
||||
elif page_id == FirstTimePage.NoInternet:
|
||||
self.back_button.setVisible(False)
|
||||
self.next_button.setVisible(False)
|
||||
self.cancel_button.setVisible(False)
|
||||
self.no_internet_finish_button.setVisible(True)
|
||||
if self.has_run_wizard:
|
||||
self.no_internet_cancel_button.setVisible(False)
|
||||
else:
|
||||
self.no_internet_cancel_button.setVisible(True)
|
||||
elif page_id == FirstTimePage.Plugins:
|
||||
self.back_button.setVisible(False)
|
||||
next_button.setVisible(False)
|
||||
cancel_button.setVisible(False)
|
||||
internet_settings_button.setVisible(True)
|
||||
elif page_id == FirstTimePage.Progress:
|
||||
back_button.setVisible(False)
|
||||
next_button.setVisible(False)
|
||||
self.application.set_busy_cursor()
|
||||
self._pre_wizard()
|
||||
self._perform_wizard()
|
||||
self._post_wizard()
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def on_cancel_button_clicked(self):
|
||||
def accept(self):
|
||||
"""
|
||||
Process the triggering of the cancel button.
|
||||
Called when the user clicks 'Finish'. Reimplement it to to save the plugin status
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
self._set_plugin_status(self.songs_check_box, 'songs/status')
|
||||
self._set_plugin_status(self.bible_check_box, 'bibles/status')
|
||||
self._set_plugin_status(self.presentation_check_box, 'presentations/status')
|
||||
self._set_plugin_status(self.image_check_box, 'images/status')
|
||||
self._set_plugin_status(self.media_check_box, 'media/status')
|
||||
self._set_plugin_status(self.custom_check_box, 'custom/status')
|
||||
self._set_plugin_status(self.song_usage_check_box, 'songusage/status')
|
||||
self._set_plugin_status(self.alert_check_box, 'alerts/status')
|
||||
self.screen_selection_widget.save()
|
||||
if self.theme_combo_box.currentIndex() != -1:
|
||||
Settings().setValue('themes/global theme', self.theme_combo_box.currentText())
|
||||
super().accept()
|
||||
|
||||
def reject(self):
|
||||
"""
|
||||
Called when the user clicks the cancel button. Reimplement it to clean up the threads.
|
||||
|
||||
:rtype: None
|
||||
"""
|
||||
self.was_cancelled = True
|
||||
if self.thumbnail_download_threads: # TODO: Use main thread list
|
||||
for thread_name in self.thumbnail_download_threads:
|
||||
worker = get_thread_worker(thread_name)
|
||||
if worker:
|
||||
@ -311,6 +321,27 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
while any([not is_thread_finished(thread_name) for thread_name in self.thumbnail_download_threads]):
|
||||
time.sleep(0.1)
|
||||
self.application.set_normal_cursor()
|
||||
super().reject()
|
||||
|
||||
def _on_custom_button_clicked(self, which):
|
||||
"""
|
||||
Slot to handle the a click on one of the wizards custom buttons.
|
||||
|
||||
:param int QtWidgets.QWizard which: The button pressed
|
||||
:rtype: None
|
||||
"""
|
||||
# Internet settings button
|
||||
if which == QtWidgets.QWizard.CustomButton1:
|
||||
proxy_dialog = ProxyDialog(self)
|
||||
proxy_dialog.retranslate_ui()
|
||||
proxy_dialog.exec()
|
||||
|
||||
def on_projectors_check_box_clicked(self):
|
||||
# When clicking projectors_check box, change the visibility setting for Projectors panel.
|
||||
if Settings().value('projector/show after wizard'):
|
||||
Settings().setValue('projector/show after wizard', False)
|
||||
else:
|
||||
Settings().setValue('projector/show after wizard', True)
|
||||
|
||||
def on_themes_list_widget_selection_changed(self):
|
||||
"""
|
||||
@ -330,23 +361,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
elif not item.isSelected() and cbox_index != -1:
|
||||
self.theme_combo_box.removeItem(cbox_index)
|
||||
|
||||
def on_no_internet_finish_button_clicked(self):
|
||||
"""
|
||||
Process the triggering of the "Finish" button on the No Internet page.
|
||||
"""
|
||||
self.application.set_busy_cursor()
|
||||
self._perform_wizard()
|
||||
self.application.set_normal_cursor()
|
||||
Settings().setValue('core/has run wizard', True)
|
||||
self.close()
|
||||
|
||||
def on_no_internet_cancel_button_clicked(self):
|
||||
"""
|
||||
Process the triggering of the "Cancel" button on the No Internet page.
|
||||
"""
|
||||
self.was_cancelled = True
|
||||
self.close()
|
||||
|
||||
def update_progress(self, count, block_size):
|
||||
"""
|
||||
Calculate and display the download progress. This method is called by download_file().
|
||||
@ -373,7 +387,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
Prepare the UI for the process.
|
||||
"""
|
||||
self.max_progress = 0
|
||||
self.finish_button.setVisible(False)
|
||||
self.button(QtWidgets.QWizard.FinishButton).setEnabled(False)
|
||||
self.application.process_events()
|
||||
try:
|
||||
# Loop through the songs list and increase for each selected item
|
||||
@ -432,54 +446,31 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
||||
self.progress_bar.setValue(self.progress_bar.maximum())
|
||||
if self.has_run_wizard:
|
||||
text = translate('OpenLP.FirstTimeWizard',
|
||||
'Download complete. Click the {button} button to return to OpenLP.'
|
||||
).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
|
||||
self.progress_label.setText(text)
|
||||
'Download complete. Click the \'{finish_button}\' button to return to OpenLP.')
|
||||
else:
|
||||
text = translate('OpenLP.FirstTimeWizard',
|
||||
'Download complete. Click the {button} button to start OpenLP.'
|
||||
).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
|
||||
self.progress_label.setText(text)
|
||||
'Download complete. Click the \'{finish_button}\' button to start OpenLP.')
|
||||
else:
|
||||
if self.has_run_wizard:
|
||||
text = translate('OpenLP.FirstTimeWizard',
|
||||
'Click the {button} button to return to OpenLP.'
|
||||
).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
|
||||
self.progress_label.setText(text)
|
||||
text = translate('OpenLP.FirstTimeWizard', 'Click the \'{finish_button}\' button to return to OpenLP.')
|
||||
else:
|
||||
text = translate('OpenLP.FirstTimeWizard',
|
||||
'Click the {button} button to start OpenLP.'
|
||||
).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)))
|
||||
self.progress_label.setText(text)
|
||||
self.finish_button.setVisible(True)
|
||||
self.finish_button.setEnabled(True)
|
||||
self.cancel_button.setVisible(False)
|
||||
self.next_button.setVisible(False)
|
||||
text = translate('OpenLP.FirstTimeWizard', 'Click the \'{finish_button}\' button to start OpenLP.')
|
||||
self.progress_label.setText(text.format(finish_button=self.finish_button_text))
|
||||
self.button(QtWidgets.QWizard.FinishButton).setEnabled(True)
|
||||
self.button(QtWidgets.QWizard.CancelButton).setVisible(False)
|
||||
self.application.process_events()
|
||||
|
||||
def _perform_wizard(self):
|
||||
"""
|
||||
Run the tasks in the wizard.
|
||||
"""
|
||||
# Set plugin states
|
||||
self._increment_progress_bar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...'))
|
||||
self._set_plugin_status(self.songs_check_box, 'songs/status')
|
||||
self._set_plugin_status(self.bible_check_box, 'bibles/status')
|
||||
self._set_plugin_status(self.presentation_check_box, 'presentations/status')
|
||||
self._set_plugin_status(self.image_check_box, 'images/status')
|
||||
self._set_plugin_status(self.media_check_box, 'media/status')
|
||||
self._set_plugin_status(self.custom_check_box, 'custom/status')
|
||||
self._set_plugin_status(self.song_usage_check_box, 'songusage/status')
|
||||
self._set_plugin_status(self.alert_check_box, 'alerts/status')
|
||||
|
||||
if self.web_access:
|
||||
if not self._download_selected():
|
||||
critical_error_message_box(translate('OpenLP.FirstTimeWizard', 'Download Error'),
|
||||
translate('OpenLP.FirstTimeWizard', 'There was a connection problem while '
|
||||
'downloading, so further downloads will be skipped. Try to re-run '
|
||||
'the First Time Wizard later.'))
|
||||
self.screen_selection_widget.save()
|
||||
if self.theme_combo_box.currentIndex() != -1:
|
||||
Settings().setValue('themes/global theme', self.theme_combo_box.currentText())
|
||||
|
||||
def _download_selected(self):
|
||||
"""
|
||||
|
@ -26,7 +26,6 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common import clean_button_text, is_macosx
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib.ui import add_welcome_page
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
|
||||
@ -39,14 +38,15 @@ class FirstTimePage(object):
|
||||
An enumeration class with each of the pages of the wizard.
|
||||
"""
|
||||
Welcome = 0
|
||||
ScreenConfig = 1
|
||||
Download = 2
|
||||
NoInternet = 3
|
||||
Plugins = 4
|
||||
Songs = 5
|
||||
Bibles = 6
|
||||
Themes = 7
|
||||
Progress = 8
|
||||
Plugins = 1
|
||||
ScreenConfig = 2
|
||||
SampleOption = 3
|
||||
Download = 4
|
||||
NoInternet = 5
|
||||
Songs = 6
|
||||
Bibles = 7
|
||||
Themes = 8
|
||||
Progress = 9
|
||||
|
||||
|
||||
class ThemeListWidget(QtWidgets.QListWidget):
|
||||
@ -97,20 +97,13 @@ class UiFirstTimeWizard(object):
|
||||
first_time_wizard.resize(550, 386)
|
||||
first_time_wizard.setModal(True)
|
||||
first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage |
|
||||
QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1 |
|
||||
QtWidgets.QWizard.HaveCustomButton2)
|
||||
QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1)
|
||||
if is_macosx(): # pragma: nocover
|
||||
first_time_wizard.setPixmap(QtWidgets.QWizard.BackgroundPixmap,
|
||||
QtGui.QPixmap(':/wizards/openlp-osx-wizard.png'))
|
||||
first_time_wizard.resize(634, 386)
|
||||
else:
|
||||
first_time_wizard.setWizardStyle(QtWidgets.QWizard.ModernStyle)
|
||||
self.finish_button = self.button(QtWidgets.QWizard.FinishButton)
|
||||
self.no_internet_finish_button = self.button(QtWidgets.QWizard.CustomButton1)
|
||||
self.cancel_button = self.button(QtWidgets.QWizard.CancelButton)
|
||||
self.no_internet_cancel_button = self.button(QtWidgets.QWizard.CustomButton2)
|
||||
self.next_button = self.button(QtWidgets.QWizard.NextButton)
|
||||
self.back_button = self.button(QtWidgets.QWizard.BackButton)
|
||||
add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp')
|
||||
# The screen config page
|
||||
self.screen_page = QtWidgets.QWizardPage()
|
||||
@ -121,6 +114,18 @@ class UiFirstTimeWizard(object):
|
||||
self.screen_selection_widget.load()
|
||||
self.screen_page_layout.addRow(self.screen_selection_widget)
|
||||
first_time_wizard.setPage(FirstTimePage.ScreenConfig, self.screen_page)
|
||||
# Download Samples page
|
||||
self.resource_page = QtWidgets.QWizardPage()
|
||||
self.resource_page.setObjectName('resource_page')
|
||||
self.resource_page.setFinalPage(True)
|
||||
self.resource_layout = QtWidgets.QVBoxLayout(self.resource_page)
|
||||
self.resource_layout.setContentsMargins(50, 20, 50, 20)
|
||||
self.resource_layout.setObjectName('resource_layout')
|
||||
self.resource_label = QtWidgets.QLabel(self.resource_page)
|
||||
self.resource_label.setObjectName('resource_label')
|
||||
self.resource_label.setWordWrap(True)
|
||||
self.resource_layout.addWidget(self.resource_label)
|
||||
first_time_wizard.setPage(FirstTimePage.SampleOption, self.resource_page)
|
||||
# The download page
|
||||
self.download_page = QtWidgets.QWizardPage()
|
||||
self.download_page.setObjectName('download_page')
|
||||
@ -134,6 +139,7 @@ class UiFirstTimeWizard(object):
|
||||
# The "you don't have an internet connection" page.
|
||||
self.no_internet_page = QtWidgets.QWizardPage()
|
||||
self.no_internet_page.setObjectName('no_internet_page')
|
||||
self.no_internet_page.setFinalPage(True)
|
||||
self.no_internet_layout = QtWidgets.QVBoxLayout(self.no_internet_page)
|
||||
self.no_internet_layout.setContentsMargins(50, 30, 50, 40)
|
||||
self.no_internet_layout.setObjectName('no_internet_layout')
|
||||
@ -242,27 +248,32 @@ class UiFirstTimeWizard(object):
|
||||
self.progress_bar.setObjectName('progress_bar')
|
||||
self.progress_layout.addWidget(self.progress_bar)
|
||||
first_time_wizard.setPage(FirstTimePage.Progress, self.progress_page)
|
||||
self.retranslate_ui(first_time_wizard)
|
||||
self.retranslate_ui()
|
||||
|
||||
def retranslate_ui(self, first_time_wizard):
|
||||
def retranslate_ui(self):
|
||||
"""
|
||||
Translate the UI on the fly
|
||||
|
||||
:param first_time_wizard: The wizard form
|
||||
"""
|
||||
first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
|
||||
self.finish_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))
|
||||
back_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.BackButton))
|
||||
next_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.NextButton))
|
||||
|
||||
self.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
|
||||
text = translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard')
|
||||
first_time_wizard.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}'
|
||||
'</span>'.format(text=text))
|
||||
button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.NextButton))
|
||||
first_time_wizard.information_label.setText(
|
||||
self.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}</span>'.format(text=text))
|
||||
self.information_label.setText(
|
||||
translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. '
|
||||
'Click the {button} button below to start.').format(button=button))
|
||||
'Click the \'{next_button}\' button below to start.'
|
||||
).format(next_button=next_button_text))
|
||||
self.setButtonText(
|
||||
QtWidgets.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Internet Settings'))
|
||||
self.download_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading Resource Index'))
|
||||
self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while the resource index is '
|
||||
'downloaded.'))
|
||||
self.download_label.setText(translate('OpenLP.FirstTimeWizard', 'Please wait while OpenLP downloads the '
|
||||
'resource index file...'))
|
||||
self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
|
||||
'Please wait while the resource index is downloaded.'))
|
||||
self.download_label.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Please wait while OpenLP downloads the resource index file...'))
|
||||
self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Select parts of the program you wish to use'))
|
||||
self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
|
||||
'You can also change these settings after the Wizard.'))
|
||||
@ -270,11 +281,10 @@ class UiFirstTimeWizard(object):
|
||||
self.screen_page.setSubTitle(translate('OpenLP.FirstTimeWizard',
|
||||
'Choose the main display screen for OpenLP.'))
|
||||
self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))
|
||||
self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Custom Slides – Easier to manage than songs and they have their own'
|
||||
' list of slides'))
|
||||
self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Bibles – Import and show Bibles'))
|
||||
self.custom_check_box.setText(
|
||||
translate('OpenLP.FirstTimeWizard',
|
||||
'Custom Slides – Easier to manage than songs and they have their own list of slides'))
|
||||
self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bibles – Import and show Bibles'))
|
||||
self.image_check_box.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Images – Show images or replace background with them'))
|
||||
self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard',
|
||||
@ -283,22 +293,25 @@ class UiFirstTimeWizard(object):
|
||||
self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Song Usage Monitor'))
|
||||
self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Alerts – Display informative messages while showing other slides'))
|
||||
self.resource_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Resource Data'))
|
||||
self.resource_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Can OpenLP download some resource data?'))
|
||||
self.resource_label.setText(
|
||||
translate('OpenLP.FirstTimeWizard',
|
||||
'OpenLP has collected some resources that we have permission to distribute.\n\n'
|
||||
'If you would like to download some of these resources click the \'{next_button}\' button, '
|
||||
'otherwise click the \'{finish_button}\' button.'
|
||||
).format(next_button=next_button_text, finish_button=self.finish_button_text))
|
||||
self.no_internet_page.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection'))
|
||||
self.no_internet_page.setSubTitle(
|
||||
translate('OpenLP.FirstTimeWizard', 'Unable to detect an Internet connection.'))
|
||||
button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton))
|
||||
self.no_internet_text = translate('OpenLP.FirstTimeWizard',
|
||||
'No Internet connection was found. The First Time Wizard needs an Internet '
|
||||
'connection in order to be able to download sample songs, Bibles and themes.'
|
||||
' Click the {button} button now to start OpenLP with initial settings and '
|
||||
'no sample data.\n\nTo re-run the First Time Wizard and import this sample '
|
||||
'data at a later time, check your Internet connection and re-run this '
|
||||
'wizard by selecting "Tools/Re-run First Time Wizard" from OpenLP.'
|
||||
).format(button=button)
|
||||
button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton))
|
||||
self.cancel_wizard_text = translate('OpenLP.FirstTimeWizard',
|
||||
'\n\nTo cancel the First Time Wizard completely (and not start OpenLP), '
|
||||
'click the {button} button now.').format(button=button)
|
||||
self.no_internet_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Cannot connect to the internet.'))
|
||||
self.no_internet_label.setText(
|
||||
translate('OpenLP.FirstTimeWizard',
|
||||
'OpenLP could not connect to the internet to get information about the sample data available.\n\n'
|
||||
'Please check your internet connection. If your church uses a proxy server click the '
|
||||
'\'Internet Settings\' button below and enter the server details there.\n\nClick the '
|
||||
'\'{back_button}\' button to try again.\n\nIf you click the \'{finish_button}\' '
|
||||
'button you can download the data at a later time by selecting \'Re-run First Time Wizard\' '
|
||||
'from the \'Tools\' menu in OpenLP.'
|
||||
).format(back_button=back_button_text, finish_button=self.finish_button_text))
|
||||
self.songs_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Songs'))
|
||||
self.songs_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download public domain songs.'))
|
||||
self.bibles_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Bibles'))
|
||||
@ -310,17 +323,5 @@ class UiFirstTimeWizard(object):
|
||||
self.themes_select_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Select all'))
|
||||
self.themes_deselect_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Deselect all'))
|
||||
self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading and Configuring'))
|
||||
self.progress_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded '
|
||||
'and OpenLP is configured.'))
|
||||
self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...'))
|
||||
first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton1,
|
||||
clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton)))
|
||||
first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton2,
|
||||
clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton)))
|
||||
|
||||
def on_projectors_check_box_clicked(self):
|
||||
# When clicking projectors_check box, change the visibility setting for Projectors panel.
|
||||
if Settings().value('projector/show after wizard'):
|
||||
Settings().setValue('projector/show after wizard', False)
|
||||
else:
|
||||
Settings().setValue('projector/show after wizard', True)
|
||||
self.progress_page.setSubTitle(
|
||||
translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded and OpenLP is configured.'))
|
||||
|
@ -681,8 +681,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
|
||||
return
|
||||
first_run_wizard = FirstTimeForm(self)
|
||||
first_run_wizard.initialize(ScreenList())
|
||||
first_run_wizard.exec()
|
||||
if first_run_wizard.was_cancelled:
|
||||
if first_run_wizard.exec() == QtWidgets.QDialog.Rejected:
|
||||
return
|
||||
self.application.set_busy_cursor()
|
||||
self.first_time()
|
||||
|
@ -235,11 +235,11 @@ class PrintServiceForm(QtWidgets.QDialog, Ui_PrintServiceDialog, RegistryPropert
|
||||
for slide in range(len(item.get_frames())):
|
||||
self._add_element('li', item.get_frame_title(slide), ol)
|
||||
# add footer
|
||||
foot_text = item.foot_text
|
||||
foot_text = foot_text.partition('<br>')[2]
|
||||
if foot_text:
|
||||
foot_text = html.escape(foot_text.replace('<br>', '\n'))
|
||||
self._add_element('div', foot_text.replace('\n', '<br>'), parent=div, class_id='itemFooter')
|
||||
footer_html = item.footer_html
|
||||
footer_html = footer_html.partition('<br>')[2]
|
||||
if footer_html:
|
||||
footer_html = html.escape(footer_html.replace('<br>', '\n'))
|
||||
self._add_element('div', footer_html.replace('\n', '<br>'), parent=div, classId='itemFooter')
|
||||
# Add service items' notes.
|
||||
if self.notes_check_box.isChecked():
|
||||
if item.notes:
|
||||
|
@ -150,6 +150,34 @@ class ProxyWidget(QtWidgets.QGroupBox):
|
||||
settings.setValue('advanced/proxy password', self.password_edit.text())
|
||||
|
||||
|
||||
class ProxyDialog(QtWidgets.QDialog):
|
||||
"""
|
||||
A basic dialog to show proxy settingd
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.layout = QtWidgets.QVBoxLayout(self)
|
||||
self.proxy_widget = ProxyWidget(self)
|
||||
self.layout.addWidget(self.proxy_widget)
|
||||
self.button_box = \
|
||||
QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, self)
|
||||
self.layout.addWidget(self.button_box)
|
||||
self.button_box.accepted.connect(self.accept)
|
||||
self.button_box.rejected.connect(self.reject)
|
||||
|
||||
def accept(self):
|
||||
"""
|
||||
Reimplement the the accept slot so that the ProxyWidget settings can be saved.
|
||||
:rtype: None
|
||||
"""
|
||||
self.proxy_widget.save()
|
||||
super().accept()
|
||||
|
||||
def retranslate_ui(self):
|
||||
self.proxy_widget.retranslate_ui()
|
||||
self.setWindowTitle(translate('OpenLP.ProxyDialog', 'Proxy Server Settings'))
|
||||
|
||||
|
||||
class ScreenButton(QtWidgets.QPushButton):
|
||||
"""
|
||||
A special button class that holds the screen information about it
|
||||
|
@ -106,7 +106,7 @@ class OpenLPSongImport(SongImport):
|
||||
pass
|
||||
|
||||
# Check the file type
|
||||
if not isinstance(self.import_source, str) or not self.import_source.endswith('.sqlite'):
|
||||
if self.import_source.suffix != '.sqlite':
|
||||
self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
|
||||
'Not a valid OpenLP 2 song database.'))
|
||||
return
|
||||
|
@ -21,6 +21,7 @@
|
||||
###############################################################################
|
||||
import logging
|
||||
import os
|
||||
import mako
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
from sqlalchemy.sql import and_, or_
|
||||
@ -35,7 +36,7 @@ from openlp.core.lib import ServiceItemContext, check_item_selected, create_sepa
|
||||
from openlp.core.lib.mediamanageritem import MediaManagerItem
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||
from openlp.core.lib.ui import create_widget_action
|
||||
from openlp.core.lib.ui import create_widget_action, critical_error_message_box
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.songs.forms.editsongform import EditSongForm
|
||||
from openlp.plugins.songs.forms.songexportform import SongExportForm
|
||||
@ -131,9 +132,6 @@ class SongMediaItem(MediaManagerItem):
|
||||
self.is_search_as_you_type_enabled = Settings().value('advanced/search as type')
|
||||
self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit')
|
||||
self.add_song_from_service = Settings().value(self.settings_section + '/add song from service')
|
||||
self.display_songbook = Settings().value(self.settings_section + '/display songbook')
|
||||
self.display_written_by_text = Settings().value(self.settings_section + '/display written by')
|
||||
self.display_copyright_symbol = Settings().value(self.settings_section + '/display copyright symbol')
|
||||
|
||||
def retranslate_ui(self):
|
||||
self.search_text_label.setText('{text}:'.format(text=UiStrings().Search))
|
||||
@ -583,9 +581,11 @@ class SongMediaItem(MediaManagerItem):
|
||||
if Settings().value('songs/add songbook slide') and song.songbook_entries:
|
||||
first_slide = '\n'
|
||||
for songbook_entry in song.songbook_entries:
|
||||
first_slide = first_slide + '{book}/{num}/{pub}\n\n'.format(book=songbook_entry.songbook.name,
|
||||
num=songbook_entry.entry,
|
||||
pub=songbook_entry.songbook.publisher)
|
||||
first_slide += '{book} #{num}'.format(book=songbook_entry.songbook.name,
|
||||
num=songbook_entry.entry)
|
||||
if songbook_entry.songbook.publisher:
|
||||
first_slide += ' ({pub})'.format(pub=songbook_entry.songbook.publisher)
|
||||
first_slide += '\n\n'
|
||||
|
||||
service_item.add_from_text(first_slide, 'O1')
|
||||
# no verse list or only 1 space (in error)
|
||||
@ -675,12 +675,8 @@ class SongMediaItem(MediaManagerItem):
|
||||
item.raw_footer = []
|
||||
item.raw_footer.append(song.title)
|
||||
if authors_none:
|
||||
# If the setting for showing "Written by:" is enabled, show it before unspecified authors.
|
||||
if Settings().value('songs/display written by'):
|
||||
item.raw_footer.append("{text}: {authors}".format(text=translate('OpenLP.Ui', 'Written by'),
|
||||
authors=create_separated_list(authors_none)))
|
||||
else:
|
||||
item.raw_footer.append("{authors}".format(authors=create_separated_list(authors_none)))
|
||||
if authors_words_music:
|
||||
item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.WordsAndMusic],
|
||||
authors=create_separated_list(authors_words_music)))
|
||||
@ -694,34 +690,44 @@ class SongMediaItem(MediaManagerItem):
|
||||
item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.Translation],
|
||||
authors=create_separated_list(authors_translation)))
|
||||
if song.copyright:
|
||||
if self.display_copyright_symbol:
|
||||
item.raw_footer.append("{symbol} {song}".format(symbol=SongStrings.CopyrightSymbol,
|
||||
song=song.copyright))
|
||||
else:
|
||||
item.raw_footer.append(song.copyright)
|
||||
if self.display_songbook and song.songbook_entries:
|
||||
songbooks = [str(songbook_entry) for songbook_entry in song.songbook_entries]
|
||||
if song.songbook_entries:
|
||||
item.raw_footer.append(", ".join(songbooks))
|
||||
if Settings().value('core/ccli number'):
|
||||
item.raw_footer.append(translate('SongsPlugin.MediaItem',
|
||||
'CCLI License: ') + Settings().value('core/ccli number'))
|
||||
item.metadata.append('<em>{label}:</em> {title}'.format(label=translate('SongsPlugin.MediaItem', 'Title'),
|
||||
title=song.title))
|
||||
if song.alternate_title:
|
||||
item.metadata.append('<em>{label}:</em> {title}'.
|
||||
format(label=translate('SongsPlugin.MediaItem', 'Alt Title'),
|
||||
title=song.alternate_title))
|
||||
if song.songbook_entries:
|
||||
for songbook_entry in song.songbook_entries:
|
||||
item.metadata.append('<em>{label}:</em> {book}/{num}/{pub}'.
|
||||
format(label=translate('SongsPlugin.MediaItem', 'Songbook'),
|
||||
book=songbook_entry.songbook.name,
|
||||
num=songbook_entry.entry,
|
||||
pub=songbook_entry.songbook.publisher))
|
||||
if song.topics:
|
||||
for topics in song.topics:
|
||||
item.metadata.append('<em>{label}:</em> {topic}'.
|
||||
format(label=translate('SongsPlugin.MediaItem', 'Topic'), topic=topics.name))
|
||||
item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
|
||||
Settings().value('core/ccli number'))
|
||||
footer_template = Settings().value('songs/footer template')
|
||||
# Keep this in sync with the list in songstab.py
|
||||
vars = {
|
||||
'title': song.title,
|
||||
'alternate_title': song.alternate_title,
|
||||
'authors_none_label': translate('OpenLP.Ui', 'Written by'),
|
||||
'authors_none': authors_none,
|
||||
'authors_words_label': AuthorType.Types[AuthorType.Words],
|
||||
'authors_words': authors_words,
|
||||
'authors_music_label': AuthorType.Types[AuthorType.Music],
|
||||
'authors_music': authors_music,
|
||||
'authors_words_music_label': AuthorType.Types[AuthorType.WordsAndMusic],
|
||||
'authors_words_music': authors_words_music,
|
||||
'authors_translation_label': AuthorType.Types[AuthorType.Translation],
|
||||
'authors_translation': authors_translation,
|
||||
'authors_words_all': authors_words + authors_words_music,
|
||||
'authors_music_all': authors_music + authors_words_music,
|
||||
'copyright': song.copyright,
|
||||
'songbook_entries': songbooks,
|
||||
'ccli_license': Settings().value('core/ccli number'),
|
||||
'ccli_license_label': translate('SongsPlugin.MediaItem', 'CCLI License'),
|
||||
'ccli_number': song.ccli_number,
|
||||
'topics': [topic.name for topic in song.topics]
|
||||
}
|
||||
try:
|
||||
item.footer_html = mako.template.Template(footer_template).render_unicode(**vars).replace('\n', '')
|
||||
except mako.exceptions.SyntaxException:
|
||||
log.error('Failed to render Song footer html:\n' + mako.exceptions.text_error_template().render())
|
||||
critical_error_message_box(message=translate('SongsPlugin.MediaItem',
|
||||
'Failed to render Song footer html.\nSee log for details'))
|
||||
return authors_all
|
||||
|
||||
def service_load(self, item):
|
||||
|
@ -25,7 +25,7 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib.settingstab import SettingsTab
|
||||
from openlp.plugins.songs.lib.ui import SongStrings
|
||||
from openlp.plugins.songs.lib.db import AuthorType
|
||||
|
||||
|
||||
class SongsTab(SettingsTab):
|
||||
@ -54,15 +54,6 @@ class SongsTab(SettingsTab):
|
||||
self.songbook_slide_check_box = QtWidgets.QCheckBox(self.mode_group_box)
|
||||
self.songbook_slide_check_box.setObjectName('songbook_slide_check_box')
|
||||
self.mode_layout.addWidget(self.songbook_slide_check_box)
|
||||
self.display_songbook_check_box = QtWidgets.QCheckBox(self.mode_group_box)
|
||||
self.display_songbook_check_box.setObjectName('songbook_check_box')
|
||||
self.mode_layout.addWidget(self.display_songbook_check_box)
|
||||
self.display_written_by_check_box = QtWidgets.QCheckBox(self.mode_group_box)
|
||||
self.display_written_by_check_box.setObjectName('written_by_check_box')
|
||||
self.mode_layout.addWidget(self.display_written_by_check_box)
|
||||
self.display_copyright_check_box = QtWidgets.QCheckBox(self.mode_group_box)
|
||||
self.display_copyright_check_box.setObjectName('copyright_check_box')
|
||||
self.mode_layout.addWidget(self.display_copyright_check_box)
|
||||
self.left_layout.addWidget(self.mode_group_box)
|
||||
# Chords group box
|
||||
self.chords_group_box = QtWidgets.QGroupBox(self.left_column)
|
||||
@ -93,20 +84,34 @@ class SongsTab(SettingsTab):
|
||||
self.neolatin_notation_radio_button.setObjectName('neolatin_notation_radio_button')
|
||||
self.chords_layout.addWidget(self.neolatin_notation_radio_button)
|
||||
self.left_layout.addWidget(self.chords_group_box)
|
||||
# Footer group box
|
||||
self.footer_group_box = QtWidgets.QGroupBox(self.left_column)
|
||||
self.footer_group_box.setObjectName('footer_group_box')
|
||||
self.footer_layout = QtWidgets.QVBoxLayout(self.footer_group_box)
|
||||
self.footer_layout.setObjectName('chords_layout')
|
||||
self.footer_info_label = QtWidgets.QLabel(self.footer_group_box)
|
||||
self.footer_layout.addWidget(self.footer_info_label)
|
||||
self.footer_placeholder_info = QtWidgets.QTextEdit(self.footer_group_box)
|
||||
self.footer_layout.addWidget(self.footer_placeholder_info)
|
||||
self.footer_desc_label = QtWidgets.QLabel(self.footer_group_box)
|
||||
self.footer_layout.addWidget(self.footer_desc_label)
|
||||
self.footer_edit_box = QtWidgets.QTextEdit(self.footer_group_box)
|
||||
self.footer_layout.addWidget(self.footer_edit_box)
|
||||
self.footer_reset_button = QtWidgets.QPushButton(self.footer_group_box)
|
||||
self.footer_layout.addWidget(self.footer_reset_button, alignment=QtCore.Qt.AlignRight)
|
||||
self.right_layout.addWidget(self.footer_group_box)
|
||||
self.left_layout.addStretch()
|
||||
self.right_layout.addStretch()
|
||||
self.tool_bar_active_check_box.stateChanged.connect(self.on_tool_bar_active_check_box_changed)
|
||||
self.update_on_edit_check_box.stateChanged.connect(self.on_update_on_edit_check_box_changed)
|
||||
self.add_from_service_check_box.stateChanged.connect(self.on_add_from_service_check_box_changed)
|
||||
self.songbook_slide_check_box.stateChanged.connect(self.on_songbook_slide_check_box_changed)
|
||||
self.display_songbook_check_box.stateChanged.connect(self.on_songbook_check_box_changed)
|
||||
self.display_written_by_check_box.stateChanged.connect(self.on_written_by_check_box_changed)
|
||||
self.display_copyright_check_box.stateChanged.connect(self.on_copyright_check_box_changed)
|
||||
self.mainview_chords_check_box.stateChanged.connect(self.on_mainview_chords_check_box_changed)
|
||||
self.disable_chords_import_check_box.stateChanged.connect(self.on_disable_chords_import_check_box_changed)
|
||||
self.english_notation_radio_button.clicked.connect(self.on_english_notation_button_clicked)
|
||||
self.german_notation_radio_button.clicked.connect(self.on_german_notation_button_clicked)
|
||||
self.neolatin_notation_radio_button.clicked.connect(self.on_neolatin_notation_button_clicked)
|
||||
self.footer_reset_button.clicked.connect(self.on_footer_reset_button_clicked)
|
||||
|
||||
def retranslate_ui(self):
|
||||
self.mode_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Song related settings'))
|
||||
@ -116,13 +121,7 @@ class SongsTab(SettingsTab):
|
||||
self.add_from_service_check_box.setText(translate('SongsPlugin.SongsTab',
|
||||
'Import missing songs from Service files'))
|
||||
self.songbook_slide_check_box.setText(translate('SongsPlugin.SongsTab',
|
||||
'Add Songbooks as first side'))
|
||||
self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer'))
|
||||
self.display_written_by_check_box.setText(translate(
|
||||
'SongsPlugin.SongsTab', 'Show "Written by:" in footer for unspecified authors'))
|
||||
self.display_copyright_check_box.setText(translate('SongsPlugin.SongsTab',
|
||||
'Display "{symbol}" symbol before copyright '
|
||||
'info').format(symbol=SongStrings.CopyrightSymbol))
|
||||
'Add Songbooks as first slide'))
|
||||
self.chords_info_label.setText(translate('SongsPlugin.SongsTab', 'If enabled all text between "[" and "]" will '
|
||||
'be regarded as chords.'))
|
||||
self.chords_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Chords'))
|
||||
@ -134,6 +133,53 @@ class SongsTab(SettingsTab):
|
||||
self.german_notation_radio_button.setText(translate('SongsPlugin.SongsTab', 'German') + ' (C-D-E-F-G-A-H)')
|
||||
self.neolatin_notation_radio_button.setText(
|
||||
translate('SongsPlugin.SongsTab', 'Neo-Latin') + ' (Do-Re-Mi-Fa-Sol-La-Si)')
|
||||
self.footer_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Footer'))
|
||||
# Keep this in sync with the list in mediaitem.py
|
||||
const = '<code>"{}"</code>'
|
||||
placeholders = [
|
||||
# placeholder, description, can be empty, is a list
|
||||
['title', translate('SongsPlugin.SongsTab', 'Song Title'), False, False],
|
||||
['alternate_title', translate('SongsPlugin.SongsTab', 'Alternate Title'), True, False],
|
||||
['written_by', const.format(translate('SongsPlugin.SongsTab', 'Written By')), True, False],
|
||||
['authors_none', translate('SongsPlugin.SongsTab', 'Authors when type is not set'), False, True],
|
||||
['authors_words_label', const.format(AuthorType.Types[AuthorType.Words]), False, False],
|
||||
['authors_words', translate('SongsPlugin.SongsTab', 'Authors (Type "Words")'), False, True],
|
||||
['authors_music_label', const.format(AuthorType.Types[AuthorType.Music]), False, False],
|
||||
['authors_music', translate('SongsPlugin.SongsTab', 'Authors (Type "Music")'), False, True],
|
||||
['authors_words_music_label', const.format(AuthorType.Types[AuthorType.WordsAndMusic]), False, False],
|
||||
['authors_words_music', translate('SongsPlugin.SongsTab', 'Authors (Type "Words and Music")'), False, True],
|
||||
['authors_translation_label', const.format(AuthorType.Types[AuthorType.Translation]), False, False],
|
||||
['authors_translation', translate('SongsPlugin.SongsTab', 'Authors (Type "Translation")'), False, True],
|
||||
['authors_words_all', translate('SongsPlugin.SongsTab', 'Authors (Type "Words" & "Words and Music")'),
|
||||
False, True],
|
||||
['authors_music_all', translate('SongsPlugin.SongsTab', 'Authors (Type "Music" & "Words and Music")'),
|
||||
False, True],
|
||||
['copyright', translate('SongsPlugin.SongsTab', 'Copyright information'), True, False],
|
||||
['songbook_entries', translate('SongsPlugin.SongsTab', 'Songbook Entries'), False, True],
|
||||
['ccli_license', translate('SongsPlugin.SongsTab', 'CCLI License'), True, False],
|
||||
['ccli_license_label', const.format(translate('SongsPlugin.SongsTab', 'CCLI License')), False, False],
|
||||
['ccli_number', translate('SongsPlugin.SongsTab', 'Song CCLI Number'), True, False],
|
||||
['topics', translate('SongsPlugin.SongsTab', 'Topics'), False, True],
|
||||
]
|
||||
placeholder_info = '<table style="background: #eee">\n<tr><th><b>{ph}</b></th><th><b>{desc}</b></th></tr>\n'\
|
||||
.format(ph=translate('SongsPlugin.SongsTab', 'Placeholder'),
|
||||
desc=translate('SongsPlugin.SongsTab', 'Description'))
|
||||
for placeholder in placeholders:
|
||||
placeholder_info += '<tr><td>${{{pl}}}</td><td>{des}{opt}</td></tr>\n'\
|
||||
.format(pl=placeholder[0], des=placeholder[1],
|
||||
opt=(' ¹' if placeholder[2] else '') +
|
||||
(' ²' if placeholder[3] else ''))
|
||||
placeholder_info += '</table>'
|
||||
placeholder_info += '\n<br/>¹ {}'.format(translate('SongsPlugin.SongsTab', 'can be empty'))
|
||||
placeholder_info += '\n<br/>² {}'.format(translate('SongsPlugin.SongsTab', 'list of entries, can be empty'))
|
||||
self.footer_placeholder_info.setHtml(placeholder_info)
|
||||
self.footer_placeholder_info.setReadOnly(True)
|
||||
|
||||
self.footer_info_label.setText(translate('SongsPlugin.SongsTab', 'How to use Footers:'))
|
||||
self.footer_desc_label.setText('{} (<a href="http://docs.makotemplates.org">{}</a>):'
|
||||
.format(translate('SongsPlugin.SongsTab', 'Footer Template'),
|
||||
translate('SongsPlugin.SongsTab', 'Mako Syntax')))
|
||||
self.footer_reset_button.setText(translate('SongsPlugin.SongsTab', 'Reset Template'))
|
||||
|
||||
def on_search_as_type_check_box_changed(self, check_state):
|
||||
self.song_search = (check_state == QtCore.Qt.Checked)
|
||||
@ -150,15 +196,6 @@ class SongsTab(SettingsTab):
|
||||
def on_songbook_slide_check_box_changed(self, check_state):
|
||||
self.songbook_slide = (check_state == QtCore.Qt.Checked)
|
||||
|
||||
def on_songbook_check_box_changed(self, check_state):
|
||||
self.display_songbook = (check_state == QtCore.Qt.Checked)
|
||||
|
||||
def on_written_by_check_box_changed(self, check_state):
|
||||
self.display_written_by = (check_state == QtCore.Qt.Checked)
|
||||
|
||||
def on_copyright_check_box_changed(self, check_state):
|
||||
self.display_copyright_symbol = (check_state == QtCore.Qt.Checked)
|
||||
|
||||
def on_mainview_chords_check_box_changed(self, check_state):
|
||||
self.mainview_chords = (check_state == QtCore.Qt.Checked)
|
||||
|
||||
@ -174,6 +211,9 @@ class SongsTab(SettingsTab):
|
||||
def on_neolatin_notation_button_clicked(self):
|
||||
self.chord_notation = 'neo-latin'
|
||||
|
||||
def on_footer_reset_button_clicked(self):
|
||||
self.footer_edit_box.setPlainText(Settings().get_default_value('songs/footer template'))
|
||||
|
||||
def load(self):
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
@ -181,9 +221,6 @@ class SongsTab(SettingsTab):
|
||||
self.update_edit = settings.value('update service on edit')
|
||||
self.update_load = settings.value('add song from service')
|
||||
self.songbook_slide = settings.value('add songbook slide')
|
||||
self.display_songbook = settings.value('display songbook')
|
||||
self.display_written_by = settings.value('display written by')
|
||||
self.display_copyright_symbol = settings.value('display copyright symbol')
|
||||
self.enable_chords = settings.value('enable chords')
|
||||
self.chord_notation = settings.value('chord notation')
|
||||
self.mainview_chords = settings.value('mainview chords')
|
||||
@ -191,9 +228,6 @@ class SongsTab(SettingsTab):
|
||||
self.tool_bar_active_check_box.setChecked(self.tool_bar)
|
||||
self.update_on_edit_check_box.setChecked(self.update_edit)
|
||||
self.add_from_service_check_box.setChecked(self.update_load)
|
||||
self.display_songbook_check_box.setChecked(self.display_songbook)
|
||||
self.display_written_by_check_box.setChecked(self.display_written_by)
|
||||
self.display_copyright_check_box.setChecked(self.display_copyright_symbol)
|
||||
self.chords_group_box.setChecked(self.enable_chords)
|
||||
self.mainview_chords_check_box.setChecked(self.mainview_chords)
|
||||
self.disable_chords_import_check_box.setChecked(self.disable_chords_import)
|
||||
@ -203,6 +237,7 @@ class SongsTab(SettingsTab):
|
||||
self.neolatin_notation_radio_button.setChecked(True)
|
||||
else:
|
||||
self.english_notation_radio_button.setChecked(True)
|
||||
self.footer_edit_box.setPlainText(settings.value('footer template'))
|
||||
settings.endGroup()
|
||||
|
||||
def save(self):
|
||||
@ -211,13 +246,13 @@ class SongsTab(SettingsTab):
|
||||
settings.setValue('display songbar', self.tool_bar)
|
||||
settings.setValue('update service on edit', self.update_edit)
|
||||
settings.setValue('add song from service', self.update_load)
|
||||
settings.setValue('display songbook', self.display_songbook)
|
||||
settings.setValue('display written by', self.display_written_by)
|
||||
settings.setValue('display copyright symbol', self.display_copyright_symbol)
|
||||
settings.setValue('enable chords', self.chords_group_box.isChecked())
|
||||
settings.setValue('mainview chords', self.mainview_chords)
|
||||
settings.setValue('disable chords import', self.disable_chords_import)
|
||||
settings.setValue('chord notation', self.chord_notation)
|
||||
# Only save footer template if it has been changed. This allows future updates
|
||||
if self.footer_edit_box.toPlainText() != Settings().get_default_value('songs/footer template'):
|
||||
settings.setValue('footer template', self.footer_edit_box.toPlainText())
|
||||
settings.setValue('add songbook slide', self.songbook_slide)
|
||||
settings.endGroup()
|
||||
if self.tab_visited:
|
||||
|
@ -49,13 +49,6 @@ def report_song_list():
|
||||
Path(translate('SongPlugin.ReportSongList', 'song_extract.csv')),
|
||||
translate('SongPlugin.ReportSongList', 'CSV format (*.csv)'))
|
||||
|
||||
if report_file_path is None:
|
||||
main_window.error_message(
|
||||
translate('SongPlugin.ReportSongList', 'Output Path Not Selected'),
|
||||
translate('SongPlugin.ReportSongList', 'You have not set a valid output location for your report. \n'
|
||||
'Please select an existing path on your computer.')
|
||||
)
|
||||
return
|
||||
report_file_path.with_suffix('.csv')
|
||||
Registry().get('application').set_busy_cursor()
|
||||
try:
|
||||
|
@ -66,11 +66,8 @@ __default_settings__ = {
|
||||
'songs/add song from service': True,
|
||||
'songs/add songbook slide': False,
|
||||
'songs/display songbar': True,
|
||||
'songs/display songbook': False,
|
||||
'songs/display written by': True,
|
||||
'songs/display copyright symbol': False,
|
||||
'songs/last directory import': None,
|
||||
'songs/last directory export': None,
|
||||
'songs/last directory import': '',
|
||||
'songs/last directory export': '',
|
||||
'songs/songselect username': '',
|
||||
'songs/songselect password': '',
|
||||
'songs/songselect searches': '',
|
||||
@ -78,6 +75,59 @@ __default_settings__ = {
|
||||
'songs/chord notation': 'english', # Can be english, german or neo-latin
|
||||
'songs/mainview chords': False,
|
||||
'songs/disable chords import': False,
|
||||
'songs/footer template': """\
|
||||
${title}<br/>
|
||||
|
||||
%if authors_none:
|
||||
<%
|
||||
authors = ", ".join(authors_none)
|
||||
%>
|
||||
${authors_none_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if authors_words_music:
|
||||
<%
|
||||
authors = ", ".join(authors_words_music)
|
||||
%>
|
||||
${authors_words_music_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if authors_words:
|
||||
<%
|
||||
authors = ", ".join(authors_words)
|
||||
%>
|
||||
${authors_words_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if authors_music:
|
||||
<%
|
||||
authors = ", ".join(authors_music)
|
||||
%>
|
||||
${authors_music_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if authors_translation:
|
||||
<%
|
||||
authors = ", ".join(authors_translation)
|
||||
%>
|
||||
${authors_translation_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if copyright:
|
||||
© ${copyright}<br/>
|
||||
%endif
|
||||
|
||||
%if songbook_entries:
|
||||
<%
|
||||
entries = ", ".join(songbook_entries)
|
||||
%>
|
||||
${entries}<br/>
|
||||
%endif
|
||||
|
||||
%if ccli_license:
|
||||
${ccli_license_label} ${ccli_license}<br/>
|
||||
%endif
|
||||
""",
|
||||
}
|
||||
|
||||
|
||||
|
0
run_openlp.py
Executable file → Normal file
0
run_openlp.py
Executable file → Normal file
13
setup.py
Executable file → Normal file
13
setup.py
Executable file → Normal file
@ -120,7 +120,8 @@ requires = [
|
||||
'lxml',
|
||||
'Mako',
|
||||
'pymediainfo >= 2.2',
|
||||
'PyQt5 >= 5.5',
|
||||
'PyQt5 >= 5.12',
|
||||
'PyQtWebEngine',
|
||||
'QtAwesome',
|
||||
'requests',
|
||||
'SQLAlchemy >= 0.5',
|
||||
@ -128,6 +129,12 @@ requires = [
|
||||
'WebOb',
|
||||
'websockets'
|
||||
]
|
||||
test_requires = [
|
||||
'nose2',
|
||||
'pylint',
|
||||
'pyodbc',
|
||||
'pysword'
|
||||
]
|
||||
if sys.platform.startswith('win'):
|
||||
requires.append('pywin32')
|
||||
elif sys.platform.startswith('darwin'):
|
||||
@ -137,6 +144,8 @@ elif sys.platform.startswith('darwin'):
|
||||
])
|
||||
elif sys.platform.startswith('linux'):
|
||||
requires.append('dbus-python')
|
||||
test_requires.append('xlib')
|
||||
|
||||
|
||||
setup(
|
||||
name='OpenLP',
|
||||
@ -202,7 +211,7 @@ using a computer and a data projector.""",
|
||||
'jenkins': ['python-jenkins'],
|
||||
'launchpad': ['launchpadlib']
|
||||
},
|
||||
tests_require=['nose2', 'pylint', 'pyodbc', 'pysword'],
|
||||
tests_require=test_requires,
|
||||
test_suite='nose2.collector.collector',
|
||||
entry_points={'gui_scripts': ['openlp = run_openlp:start']}
|
||||
)
|
||||
|
@ -224,7 +224,7 @@ class TestHttpUtils(TestCase, TestMixin):
|
||||
file_size = get_url_file_size(fake_url)
|
||||
|
||||
# THEN: The correct methods are called with the correct arguments and a web page is returned
|
||||
mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, timeout=30.0)
|
||||
mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, proxies=None, timeout=30.0)
|
||||
assert file_size == 100
|
||||
|
||||
@patch('openlp.core.common.httputils.requests')
|
||||
@ -249,34 +249,30 @@ class TestGetProxySettings(TestCase, TestMixin):
|
||||
self.addCleanup(self.destroy_settings)
|
||||
|
||||
@patch('openlp.core.common.httputils.Settings')
|
||||
def test_mode_arg_specified(self, MockSettings):
|
||||
def test_mode_arg_specified(self, mocked_settings):
|
||||
"""
|
||||
Test that the argument is used rather than reading the 'advanced/proxy mode' setting
|
||||
"""
|
||||
# GIVEN: Mocked settings
|
||||
mocked_settings = MagicMock()
|
||||
MockSettings.return_value = mocked_settings
|
||||
|
||||
# WHEN: Calling `get_proxy_settings` with the mode arg specified
|
||||
get_proxy_settings(mode=ProxyMode.NO_PROXY)
|
||||
|
||||
# THEN: The mode arg should have been used rather than looking it up in the settings
|
||||
mocked_settings.value.assert_not_called()
|
||||
mocked_settings().value.assert_not_called()
|
||||
|
||||
@patch('openlp.core.common.httputils.Settings')
|
||||
def test_mode_incorrect_arg_specified(self, MockSettings):
|
||||
def test_mode_incorrect_arg_specified(self, mocked_settings):
|
||||
"""
|
||||
Test that the system settings are used when the mode arg specieied is invalid
|
||||
"""
|
||||
# GIVEN: Mocked settings
|
||||
mocked_settings = MagicMock()
|
||||
MockSettings.return_value = mocked_settings
|
||||
|
||||
# WHEN: Calling `get_proxy_settings` with an invalid mode arg specified
|
||||
result = get_proxy_settings(mode='qwerty')
|
||||
|
||||
# THEN: An None should be returned
|
||||
mocked_settings.value.assert_not_called()
|
||||
mocked_settings().value.assert_not_called()
|
||||
assert result is None
|
||||
|
||||
def test_no_proxy_mode(self):
|
||||
|
@ -133,15 +133,11 @@ def test_get_thread_worker_mising(MockRegistry):
|
||||
# GIVEN: A mocked thread worker
|
||||
MockRegistry.return_value.get.return_value.worker_threads = {}
|
||||
|
||||
try:
|
||||
# WHEN: get_thread_worker() is called
|
||||
get_thread_worker('test_thread')
|
||||
assert False, 'A KeyError should have been raised'
|
||||
except KeyError:
|
||||
# THEN: The mocked worker is returned
|
||||
pass
|
||||
except Exception:
|
||||
assert False, 'A KeyError should have been raised'
|
||||
result = get_thread_worker('test_thread')
|
||||
|
||||
# THEN: None should have been returned
|
||||
assert result is None
|
||||
|
||||
|
||||
@patch('openlp.core.threading.Registry')
|
||||
|
@ -27,6 +27,8 @@ import tempfile
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, call, patch, DEFAULT
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.common.path import Path
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem
|
||||
@ -45,6 +47,7 @@ class TestThemeListWidgetItem(TestCase):
|
||||
"""
|
||||
Test the :class:`ThemeListWidgetItem` class
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.sample_theme_data = {'file_name': 'BlueBurst.otz', 'sha256': 'sha_256_hash',
|
||||
'thumbnail': 'BlueBurst.png', 'title': 'Blue Burst'}
|
||||
@ -59,7 +62,7 @@ class TestThemeListWidgetItem(TestCase):
|
||||
"""
|
||||
Test that the theme data is loaded correctly in to a ThemeListWidgetItem object when instantiated
|
||||
"""
|
||||
# GIVEN: A sample theme dictanary object
|
||||
# GIVEN: A sample theme dictionary object
|
||||
# WHEN: Creating an instance of `ThemeListWidgetItem`
|
||||
instance = ThemeListWidgetItem('url', self.sample_theme_data, MagicMock())
|
||||
|
||||
@ -74,7 +77,7 @@ class TestThemeListWidgetItem(TestCase):
|
||||
"""
|
||||
Test that the `DownloadWorker` worker is set up correctly and that the thread is started.
|
||||
"""
|
||||
# GIVEN: A sample theme dictanary object
|
||||
# GIVEN: A sample theme dictionary object
|
||||
mocked_ftw = MagicMock(spec=FirstTimeForm)
|
||||
mocked_ftw.thumbnail_download_threads = []
|
||||
|
||||
@ -120,10 +123,23 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
# THEN: The screens should be set up, and the default values initialised
|
||||
assert expected_screens == frw.screens, 'The screens should be correct'
|
||||
assert frw.web_access is True, 'The default value of self.web_access should be True'
|
||||
assert frw.was_cancelled is False, 'The default value of self.was_cancelled should be False'
|
||||
assert [] == frw.thumbnail_download_threads, 'The list of threads should be empty'
|
||||
assert frw.has_run_wizard is False, 'has_run_wizard should be False'
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.exec')
|
||||
def test_exec(self, mocked_qwizard_exec):
|
||||
|
||||
# GIVEN: An instance of FirstTimeForm
|
||||
frw = FirstTimeForm(None)
|
||||
with patch.object(frw, 'set_defaults') as mocked_set_defaults:
|
||||
|
||||
# WHEN: exec is called
|
||||
frw.exec()
|
||||
|
||||
# THEN: The wizard should be reset and the exec methon on the super class should have been called
|
||||
mocked_set_defaults.assert_called_once()
|
||||
mocked_qwizard_exec.assert_called_once()
|
||||
|
||||
def test_set_defaults(self):
|
||||
"""
|
||||
Test that the default values are set when set_defaults() is run
|
||||
@ -134,8 +150,6 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
mocked_settings = MagicMock()
|
||||
mocked_settings.value.side_effect = lambda key: {'core/has run wizard': False}[key]
|
||||
with patch.object(frw, 'restart') as mocked_restart, \
|
||||
patch.object(frw, 'cancel_button') as mocked_cancel_button, \
|
||||
patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \
|
||||
patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \
|
||||
patch.object(frw, 'theme_combo_box') as mocked_theme_combo_box, \
|
||||
patch.object(frw, 'songs_check_box') as mocked_songs_check_box, \
|
||||
@ -153,12 +167,8 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
# THEN: The default values should have been set
|
||||
mocked_restart.assert_called_once()
|
||||
assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set'
|
||||
mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked)
|
||||
mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with(
|
||||
frw.on_no_internet_finish_button_clicked)
|
||||
mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed)
|
||||
mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load)
|
||||
mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False)
|
||||
mocked_settings.value.assert_has_calls([call('core/has run wizard')])
|
||||
mocked_gettempdir.assert_called_once()
|
||||
mocked_create_paths.assert_called_once_with(Path('temp', 'openlp'))
|
||||
@ -177,8 +187,6 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
mocked_settings.value.side_effect = \
|
||||
lambda key: {'core/has run wizard': True, 'themes/global theme': 'Default Theme'}[key]
|
||||
with patch.object(frw, 'restart') as mocked_restart, \
|
||||
patch.object(frw, 'cancel_button') as mocked_cancel_button, \
|
||||
patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \
|
||||
patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \
|
||||
patch.object(frw, 'theme_combo_box', **{'findText.return_value': 3}) as mocked_theme_combo_box, \
|
||||
patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT,
|
||||
@ -200,12 +208,8 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
# THEN: The default values should have been set
|
||||
mocked_restart.assert_called_once()
|
||||
assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set'
|
||||
mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked)
|
||||
mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with(
|
||||
frw.on_no_internet_finish_button_clicked)
|
||||
mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed)
|
||||
mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load)
|
||||
mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False)
|
||||
mocked_settings.value.assert_has_calls([call('core/has run wizard'), call('themes/global theme')])
|
||||
mocked_gettempdir.assert_called_once()
|
||||
mocked_create_paths.assert_called_once_with(Path('temp', 'openlp'))
|
||||
@ -219,12 +223,78 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
mocked_theme_combo_box.findText.assert_called_once_with('Default Theme')
|
||||
mocked_theme_combo_box.setCurrentIndex(3)
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.Settings')
|
||||
@patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.accept')
|
||||
def test_accept_method(self, mocked_qwizard_accept, *args):
|
||||
"""
|
||||
Test the FirstTimeForm.accept method
|
||||
"""
|
||||
# GIVEN: An instance of FirstTimeForm
|
||||
frw = FirstTimeForm(None)
|
||||
with patch.object(frw, '_set_plugin_status') as mocked_set_plugin_status, \
|
||||
patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT,
|
||||
image_check_box=DEFAULT, media_check_box=DEFAULT, custom_check_box=DEFAULT,
|
||||
song_usage_check_box=DEFAULT, alert_check_box=DEFAULT) as mocked_check_boxes, \
|
||||
patch.object(frw, 'screen_selection_widget') as mocked_screen_selection_widget:
|
||||
|
||||
# WHEN: Calling accept
|
||||
frw.accept()
|
||||
|
||||
# THEN: The selected plugins should be enabled, the screen selection saved and the super method called
|
||||
mocked_set_plugin_status.assert_has_calls([
|
||||
call(mocked_check_boxes['songs_check_box'], 'songs/status'),
|
||||
call(mocked_check_boxes['bible_check_box'], 'bibles/status'),
|
||||
call(mocked_check_boxes['presentation_check_box'], 'presentations/status'),
|
||||
call(mocked_check_boxes['image_check_box'], 'images/status'),
|
||||
call(mocked_check_boxes['media_check_box'], 'media/status'),
|
||||
call(mocked_check_boxes['custom_check_box'], 'custom/status'),
|
||||
call(mocked_check_boxes['song_usage_check_box'], 'songusage/status'),
|
||||
call(mocked_check_boxes['alert_check_box'], 'alerts/status')])
|
||||
mocked_screen_selection_widget.save.assert_called_once()
|
||||
mocked_qwizard_accept.assert_called_once()
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.Settings')
|
||||
def test_accept_method_theme_not_selected(self, mocked_settings):
|
||||
"""
|
||||
Test the FirstTimeForm.accept method when there is no default theme selected
|
||||
"""
|
||||
# GIVEN: An instance of FirstTimeForm
|
||||
frw = FirstTimeForm(None)
|
||||
with patch.object(frw, '_set_plugin_status'), patch.object(frw, 'screen_selection_widget'), \
|
||||
patch.object(frw, 'theme_combo_box', **{'currentIndex.return_value': -1}):
|
||||
|
||||
# WHEN: Calling accept and the currentIndex method of the theme_combo_box returns -1
|
||||
frw.accept()
|
||||
|
||||
# THEN: OpenLP should not try to save a theme name
|
||||
mocked_settings().setValue.assert_not_called()
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.Settings')
|
||||
def test_accept_method_theme_selected(self, mocked_settings):
|
||||
"""
|
||||
Test the FirstTimeForm.accept method when a default theme is selected
|
||||
"""
|
||||
# GIVEN: An instance of FirstTimeForm
|
||||
frw = FirstTimeForm(None)
|
||||
with patch.object(frw, '_set_plugin_status'), \
|
||||
patch.object(frw, 'screen_selection_widget'), \
|
||||
patch.object(
|
||||
frw, 'theme_combo_box', **{'currentIndex.return_value': 0, 'currentText.return_value': 'Test Item'}):
|
||||
|
||||
# WHEN: Calling accept and the currentIndex method of the theme_combo_box returns 0
|
||||
frw.accept()
|
||||
|
||||
# THEN: The 'currentItem' in the combobox should have been set as the default theme.
|
||||
mocked_settings().setValue.assert_called_once_with('themes/global theme', 'Test Item')
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.reject')
|
||||
@patch('openlp.core.ui.firsttimeform.time')
|
||||
@patch('openlp.core.ui.firsttimeform.get_thread_worker')
|
||||
@patch('openlp.core.ui.firsttimeform.is_thread_finished')
|
||||
def test_on_cancel_button_clicked(self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time):
|
||||
def test_reject_method(
|
||||
self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time, mocked_qwizard_reject):
|
||||
"""
|
||||
Test that the cancel button click slot shuts down the threads correctly
|
||||
Test that the reject method shuts down the threads correctly
|
||||
"""
|
||||
# GIVEN: A FRW, some mocked threads and workers (that isn't quite done) and other mocked stuff
|
||||
mocked_worker = MagicMock()
|
||||
@ -235,17 +305,47 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
frw.thumbnail_download_threads = ['test_thread']
|
||||
with patch.object(frw.application, 'set_normal_cursor') as mocked_set_normal_cursor:
|
||||
|
||||
# WHEN: on_cancel_button_clicked() is called
|
||||
frw.on_cancel_button_clicked()
|
||||
# WHEN: the reject method is called
|
||||
frw.reject()
|
||||
|
||||
# THEN: The right things should be called in the right order
|
||||
assert frw.was_cancelled is True, 'The was_cancelled property should have been set to True'
|
||||
mocked_get_thread_worker.assert_called_once_with('test_thread')
|
||||
mocked_worker.cancel_download.assert_called_once()
|
||||
mocked_is_thread_finished.assert_called_with('test_thread')
|
||||
assert mocked_is_thread_finished.call_count == 2, 'isRunning() should have been called twice'
|
||||
mocked_time.sleep.assert_called_once_with(0.1)
|
||||
mocked_set_normal_cursor.assert_called_once_with()
|
||||
mocked_set_normal_cursor.assert_called_once()
|
||||
mocked_qwizard_reject.assert_called_once()
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.ProxyDialog')
|
||||
def test_on_custom_button_clicked(self, mocked_proxy_dialog):
|
||||
"""
|
||||
Test _on_custom_button when it is called whe the 'internet settings' (CustomButton1) button is not clicked.
|
||||
"""
|
||||
# GIVEN: An instance of the FirstTimeForm
|
||||
frw = FirstTimeForm(None)
|
||||
|
||||
# WHEN: Calling _on_custom_button_clicked with a different button to the 'internet settings button.
|
||||
frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton2)
|
||||
|
||||
# THEN: The ProxyDialog should not be shown.
|
||||
mocked_proxy_dialog.assert_not_called()
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.ProxyDialog')
|
||||
def test_on_custom_button_clicked_internet_settings(self, mocked_proxy_dialog):
|
||||
"""
|
||||
Test _on_custom_button when it is called when the 'internet settings' (CustomButton1) button is clicked.
|
||||
"""
|
||||
# GIVEN: An instance of the FirstTimeForm
|
||||
frw = FirstTimeForm(None)
|
||||
|
||||
# WHEN: Calling _on_custom_button_clicked with the constant for the 'internet settings' button (CustomButton1)
|
||||
frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton1)
|
||||
|
||||
# THEN: The ProxyDialog should be shown.
|
||||
mocked_proxy_dialog.assert_called_with(frw)
|
||||
mocked_proxy_dialog().retranslate_ui.assert_called_once()
|
||||
mocked_proxy_dialog().exec.assert_called_once()
|
||||
|
||||
@patch('openlp.core.ui.firsttimeform.critical_error_message_box')
|
||||
def test__parse_config_invalid_config(self, mocked_critical_error_message_box):
|
||||
@ -279,10 +379,10 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
|
||||
# THEN: the critical_error_message_box should have been called
|
||||
mocked_message_box.critical.assert_called_once_with(
|
||||
first_time_form, 'Network Error', 'There was a network error attempting to connect to retrieve '
|
||||
'initial configuration information', 'OK')
|
||||
first_time_form, 'Network Error',
|
||||
'There was a network error attempting to connect to retrieve initial configuration information', 'OK')
|
||||
|
||||
@patch('openlp.core.ui.firsttimewizard.Settings')
|
||||
@patch('openlp.core.ui.firsttimeform.Settings')
|
||||
def test_on_projectors_check_box_checked(self, MockSettings):
|
||||
"""
|
||||
Test that the projector panel is shown when the checkbox in the first time wizard is checked
|
||||
@ -300,7 +400,7 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
||||
mocked_settings.value.assert_called_once_with('projector/show after wizard')
|
||||
mocked_settings.setValue.assert_called_once_with('projector/show after wizard', False)
|
||||
|
||||
@patch('openlp.core.ui.firsttimewizard.Settings')
|
||||
@patch('openlp.core.ui.firsttimeform.Settings')
|
||||
def test_on_projectors_check_box_unchecked(self, MockSettings):
|
||||
"""
|
||||
Test that the projector panel is shown when the checkbox in the first time wizard is checked
|
||||
|
@ -29,6 +29,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
from PyQt5 import QtCore, QtGui
|
||||
|
||||
from openlp.core.common import is_macosx, is_linux, is_win
|
||||
from openlp.core.common.path import Path
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.display.screens import ScreenList
|
||||
@ -49,6 +50,25 @@ SCREEN = {
|
||||
}
|
||||
|
||||
|
||||
def get_screen_resolution():
|
||||
"""
|
||||
Get the screen resolution
|
||||
"""
|
||||
if is_macosx():
|
||||
from AppKit import NSScreen
|
||||
screen_size = NSScreen.mainScreen().frame().size
|
||||
return screen_size.width, screen_size.height
|
||||
elif is_win():
|
||||
from win32api import GetSystemMetrics
|
||||
return GetSystemMetrics(0), GetSystemMetrics(1)
|
||||
elif is_linux():
|
||||
from Xlib.display import Display
|
||||
resolution = Display().screen().root.get_geometry()
|
||||
return resolution.width, resolution.height
|
||||
else:
|
||||
return 1024, 768
|
||||
|
||||
|
||||
class TestPdfController(TestCase, TestMixin):
|
||||
"""
|
||||
Test the PdfController.
|
||||
@ -137,8 +157,11 @@ class TestPdfController(TestCase, TestMixin):
|
||||
assert 1076 == image.height(), 'The height should be 1076'
|
||||
assert 760 == image.width(), 'The width should be 760'
|
||||
else:
|
||||
assert 768 == image.height(), 'The height should be 768'
|
||||
assert 543 == image.width(), 'The width should be 543'
|
||||
width, height = get_screen_resolution()
|
||||
# Calculate the width of the PDF based on the aspect ratio of the PDF
|
||||
width = int(round(height * 0.70703125, 0))
|
||||
assert image.height() == height, 'The height should be {height}'.format(height=height)
|
||||
assert image.width() == width, 'The width should be {width}'.format(width=width)
|
||||
|
||||
@patch('openlp.plugins.presentations.lib.pdfcontroller.check_binary_exists')
|
||||
def test_process_check_binary_mudraw(self, mocked_check_binary_exists):
|
||||
|
@ -34,6 +34,62 @@ from openlp.plugins.songs.lib.db import AuthorType, Song
|
||||
from openlp.plugins.songs.lib.mediaitem import SongMediaItem
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
__default_settings__ = {
|
||||
'songs/footer template': """
|
||||
${title}<br/>
|
||||
|
||||
%if authors_none:
|
||||
<%
|
||||
authors = ", ".join(authors_none)
|
||||
%>
|
||||
${authors_none_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if authors_words_music:
|
||||
<%
|
||||
authors = ", ".join(authors_words_music)
|
||||
%>
|
||||
${authors_words_music_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if authors_words:
|
||||
<%
|
||||
authors = ", ".join(authors_words)
|
||||
%>
|
||||
${authors_words_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if authors_music:
|
||||
<%
|
||||
authors = ", ".join(authors_music)
|
||||
%>
|
||||
${authors_music_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if authors_translation:
|
||||
<%
|
||||
authors = ", ".join(authors_translation)
|
||||
%>
|
||||
${authors_translation_label}: ${authors}<br/>
|
||||
%endif
|
||||
|
||||
%if copyright:
|
||||
© ${copyright}<br/>
|
||||
%endif
|
||||
|
||||
%if songbook_entries:
|
||||
<%
|
||||
entries = ", ".join(songbook_entries)
|
||||
%>
|
||||
${entries}<br/>
|
||||
%endif
|
||||
|
||||
%if ccli_license:
|
||||
${ccli_license_label} ${ccli_license}<br/>
|
||||
%endif
|
||||
"""
|
||||
}
|
||||
|
||||
|
||||
class TestMediaItem(TestCase, TestMixin):
|
||||
"""
|
||||
@ -61,6 +117,7 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
self.media_item.display_copyright_symbol = False
|
||||
self.setup_application()
|
||||
self.build_settings()
|
||||
Settings().extend_default_settings(__default_settings__)
|
||||
QtCore.QLocale.setDefault(QtCore.QLocale('en_GB'))
|
||||
|
||||
def tearDown(self):
|
||||
@ -297,15 +354,17 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
"""
|
||||
Test build songs footer with basic song and one author
|
||||
"""
|
||||
# GIVEN: A Song and a Service Item, mocked settings: True for 'songs/display written by'
|
||||
# and False for 'core/ccli number' (ccli will cause traceback if true)
|
||||
# GIVEN: A Song and a Service Item, mocked settings
|
||||
|
||||
mocked_settings = MagicMock()
|
||||
mocked_settings.value.side_effect = [True, False]
|
||||
mocked_settings.value.side_effect = [False, "", "0"]
|
||||
MockedSettings.return_value = mocked_settings
|
||||
|
||||
with patch('mako.template.Template.render_unicode') as MockedRenderer:
|
||||
mock_song = MagicMock()
|
||||
mock_song.title = 'My Song'
|
||||
mock_song.alternate_title = ''
|
||||
mock_song.ccli_number = ''
|
||||
mock_song.authors_songs = []
|
||||
mock_author = MagicMock()
|
||||
mock_author.display_name = 'my author'
|
||||
@ -313,47 +372,27 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
mock_author_song.author = mock_author
|
||||
mock_song.authors_songs.append(mock_author_song)
|
||||
mock_song.copyright = 'My copyright'
|
||||
mock_song.songbook_entries = []
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# WHEN: I generate the Footer with default settings
|
||||
author_list = self.media_item.generate_footer(service_item, mock_song)
|
||||
|
||||
# THEN: I get the following Array returned
|
||||
assert service_item.raw_footer == ['My Song', 'Written by: my author', 'My copyright'], \
|
||||
'The array should be returned correctly with a song, one author and copyright'
|
||||
assert author_list == ['my author'], 'The author list should be returned correctly with one author'
|
||||
|
||||
@patch(u'openlp.plugins.songs.lib.mediaitem.Settings')
|
||||
def test_build_song_footer_one_author_hide_written_by(self, MockedSettings):
|
||||
"""
|
||||
Test build songs footer with basic song and one author
|
||||
"""
|
||||
# GIVEN: A Song and a Service Item, mocked settings: False for 'songs/display written by'
|
||||
# and False for 'core/ccli number' (ccli will cause traceback if true)
|
||||
|
||||
mocked_settings = MagicMock()
|
||||
mocked_settings.value.side_effect = [False, False]
|
||||
MockedSettings.return_value = mocked_settings
|
||||
|
||||
mock_song = MagicMock()
|
||||
mock_song.title = 'My Song'
|
||||
mock_song.authors_songs = []
|
||||
mock_author = MagicMock()
|
||||
mock_author.display_name = 'my author'
|
||||
mock_author_song = MagicMock()
|
||||
mock_author_song.author = mock_author
|
||||
mock_song.authors_songs.append(mock_author_song)
|
||||
mock_song.copyright = 'My copyright'
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# WHEN: I generate the Footer with default settings
|
||||
author_list = self.media_item.generate_footer(service_item, mock_song)
|
||||
|
||||
# THEN: I get the following Array returned
|
||||
assert service_item.raw_footer == ['My Song', 'my author', 'My copyright'], \
|
||||
'The array should be returned correctly with a song, one author and copyright, ' \
|
||||
'text Written by should not be part of the text.'
|
||||
assert author_list == ['my author'], 'The author list should be returned correctly with one author'
|
||||
# THEN: The mako function was called with the following arguments
|
||||
args = {'authors_translation': [], 'authors_music_label': 'Music',
|
||||
'copyright': 'My copyright', 'songbook_entries': [],
|
||||
'alternate_title': '', 'topics': [], 'authors_music_all': [],
|
||||
'authors_words_label': 'Words', 'authors_music': [],
|
||||
'authors_words_music': [], 'ccli_number': '',
|
||||
'authors_none_label': 'Written by', 'title': 'My Song',
|
||||
'authors_words_music_label': 'Words and Music',
|
||||
'authors_none': ['my author'],
|
||||
'ccli_license_label': 'CCLI License', 'authors_words': [],
|
||||
'ccli_license': '0', 'authors_translation_label': 'Translation',
|
||||
'authors_words_all': []}
|
||||
MockedRenderer.assert_called_once_with(**args)
|
||||
self.assertEqual(author_list, ['my author'],
|
||||
'The author list should be returned correctly with one author')
|
||||
|
||||
def test_build_song_footer_two_authors(self):
|
||||
"""
|
||||
@ -382,6 +421,7 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
mock_author_song.author_type = AuthorType.Translation
|
||||
mock_song.authors_songs.append(mock_author_song)
|
||||
mock_song.copyright = 'My copyright'
|
||||
mock_song.songbook_entries = []
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# WHEN: I generate the Footer with default settings
|
||||
@ -389,7 +429,7 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
|
||||
# THEN: I get the following Array returned
|
||||
assert service_item.raw_footer == ['My Song', 'Words: another author', 'Music: my author',
|
||||
'Translation: translator', 'My copyright'], \
|
||||
'Translation: translator', '© My copyright'], \
|
||||
'The array should be returned correctly with a song, two authors and copyright'
|
||||
assert author_list == ['another author', 'my author', 'translator'], \
|
||||
'The author list should be returned correctly with two authors'
|
||||
@ -402,6 +442,7 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
mock_song = MagicMock()
|
||||
mock_song.title = 'My Song'
|
||||
mock_song.copyright = 'My copyright'
|
||||
mock_song.songbook_entries = []
|
||||
service_item = ServiceItem(None)
|
||||
Settings().setValue('core/ccli number', '1234')
|
||||
|
||||
@ -409,7 +450,7 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
self.media_item.generate_footer(service_item, mock_song)
|
||||
|
||||
# THEN: I get the following Array returned
|
||||
assert service_item.raw_footer == ['My Song', 'My copyright', 'CCLI License: 1234'], \
|
||||
assert service_item.raw_footer == ['My Song', '© My copyright', 'CCLI License: 1234'], \
|
||||
'The array should be returned correctly with a song, an author, copyright and ccli'
|
||||
|
||||
# WHEN: I amend the CCLI value
|
||||
@ -417,7 +458,7 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
self.media_item.generate_footer(service_item, mock_song)
|
||||
|
||||
# THEN: I would get an amended footer string
|
||||
assert service_item.raw_footer == ['My Song', 'My copyright', 'CCLI License: 4321'], \
|
||||
assert service_item.raw_footer == ['My Song', '© My copyright', 'CCLI License: 4321'], \
|
||||
'The array should be returned correctly with a song, an author, copyright and amended ccli'
|
||||
|
||||
def test_build_song_footer_base_songbook(self):
|
||||
@ -431,6 +472,8 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
song.copyright = 'My copyright'
|
||||
song.authors_songs = []
|
||||
song.songbook_entries = []
|
||||
song.alternate_title = ''
|
||||
song.topics = []
|
||||
song.ccli_number = ''
|
||||
book1 = MagicMock()
|
||||
book1.name = 'My songbook'
|
||||
@ -444,15 +487,8 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
# WHEN: I generate the Footer with default settings
|
||||
self.media_item.generate_footer(service_item, song)
|
||||
|
||||
# THEN: The songbook should not be in the footer
|
||||
assert service_item.raw_footer == ['My Song', 'My copyright']
|
||||
|
||||
# WHEN: I activate the "display songbook" option
|
||||
self.media_item.display_songbook = True
|
||||
self.media_item.generate_footer(service_item, song)
|
||||
|
||||
# THEN: The songbook should be in the footer
|
||||
assert service_item.raw_footer == ['My Song', 'My copyright', 'My songbook #12, Thy songbook #502A']
|
||||
assert service_item.raw_footer == ['My Song', '© My copyright', 'My songbook #12, Thy songbook #502A']
|
||||
|
||||
def test_build_song_footer_copyright_enabled(self):
|
||||
"""
|
||||
@ -463,6 +499,7 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
mock_song = MagicMock()
|
||||
mock_song.title = 'My Song'
|
||||
mock_song.copyright = 'My copyright'
|
||||
mock_song.songbook_entries = []
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# WHEN: I generate the Footer with default settings
|
||||
@ -479,13 +516,14 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
mock_song = MagicMock()
|
||||
mock_song.title = 'My Song'
|
||||
mock_song.copyright = 'My copyright'
|
||||
mock_song.songbook_entries = []
|
||||
service_item = ServiceItem(None)
|
||||
|
||||
# WHEN: I generate the Footer with default settings
|
||||
self.media_item.generate_footer(service_item, mock_song)
|
||||
|
||||
# THEN: The copyright symbol should not be in the footer
|
||||
assert service_item.raw_footer == ['My Song', 'My copyright']
|
||||
assert service_item.raw_footer == ['My Song', '© My copyright']
|
||||
|
||||
def test_authors_match(self):
|
||||
"""
|
||||
|
@ -22,6 +22,7 @@
|
||||
"""
|
||||
This module contains tests for the OpenLP song importer.
|
||||
"""
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@ -66,8 +67,7 @@ class TestOpenLPImport(TestCase):
|
||||
importer.stop_import_flag = True
|
||||
|
||||
# WHEN: Import source is not a list
|
||||
for source in ['not a list', 0]:
|
||||
importer.import_source = source
|
||||
importer.import_source = Path()
|
||||
|
||||
# THEN: do_import should return none and the progress bar maximum should not be set.
|
||||
assert importer.do_import() is None, 'do_import should return None when import_source is not a list'
|
||||
|
@ -22,6 +22,9 @@
|
||||
"""
|
||||
This module contains tests for the PresentationManager song importer.
|
||||
"""
|
||||
from unittest import skipIf
|
||||
|
||||
from openlp.core.common import is_macosx
|
||||
from tests.helpers.songfileimport import SongImportTestHelper
|
||||
from tests.utils.constants import RESOURCE_PATH
|
||||
|
||||
@ -36,6 +39,7 @@ class TestPresentationManagerFileImport(SongImportTestHelper):
|
||||
self.importer_module_name = 'presentationmanager'
|
||||
super(TestPresentationManagerFileImport, self).__init__(*args, **kwargs)
|
||||
|
||||
@skipIf(is_macosx(), 'This test fails for an undetermined reason on macOS')
|
||||
def test_song_import(self):
|
||||
"""
|
||||
Test that loading a PresentationManager file works correctly
|
||||
|
Loading…
Reference in New Issue
Block a user