Merge from trunk

This commit is contained in:
Ian Knight 2016-05-04 21:50:37 +09:30
commit a2bc59b3d9
74 changed files with 1332 additions and 358 deletions

View File

@ -45,3 +45,4 @@ cover
*.kdev4
coverage
tags
output

View File

@ -24,6 +24,7 @@ The :mod:`common` module contains most of the components and libraries that make
OpenLP work.
"""
import hashlib
import logging
import os
import re
@ -31,6 +32,7 @@ import sys
import traceback
from ipaddress import IPv4Address, IPv6Address, AddressValueError
from shutil import which
from subprocess import check_output, CalledProcessError, STDOUT
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QCryptographicHash as QHash
@ -247,6 +249,9 @@ from .applocation import AppLocation
from .actions import ActionList
from .languagemanager import LanguageManager
if is_win():
from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW
def add_actions(target, actions):
"""
@ -371,3 +376,28 @@ def clean_filename(filename):
if not isinstance(filename, str):
filename = str(filename, 'utf-8')
return INVALID_FILE_CHARS.sub('_', CONTROL_CHARS.sub('', filename))
def check_binary_exists(program_path):
"""
Function that checks whether a binary exists.
:param program_path:The full path to the binary to check.
:return: program output to be parsed
"""
log.debug('testing program_path: %s', program_path)
try:
# Setup startupinfo options for check_output to avoid console popping up on windows
if is_win():
startupinfo = STARTUPINFO()
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
else:
startupinfo = None
runlog = check_output([program_path, '--help'], stderr=STDOUT, startupinfo=startupinfo)
except CalledProcessError as e:
runlog = e.output
except Exception:
trace_error_handler(log)
runlog = ''
log.debug('check_output returned: %s' % runlog)
return runlog

View File

@ -182,13 +182,15 @@ class Settings(QtCore.QSettings):
'themes/wrap footer': False,
'user interface/live panel': True,
'user interface/live splitter geometry': QtCore.QByteArray(),
'user interface/lock panel': False,
'user interface/lock panel': True,
'user interface/main window geometry': QtCore.QByteArray(),
'user interface/main window position': QtCore.QPoint(0, 0),
'user interface/main window splitter geometry': QtCore.QByteArray(),
'user interface/main window state': QtCore.QByteArray(),
'user interface/preview panel': True,
'user interface/preview splitter geometry': QtCore.QByteArray(),
'user interface/is preset layout': False,
'projector/show after wizard': False,
'projector/db type': 'sqlite',
'projector/db username': '',
'projector/db password': '',

View File

@ -330,12 +330,9 @@ def create_separated_list(string_list):
return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (string_list[0], merged)
from .colorbutton import ColorButton
from .exceptions import ValidationError
from .filedialog import FileDialog
from .screen import ScreenList
from .listwidgetwithdnd import ListWidgetWithDnD
from .treewidgetwithdnd import TreeWidgetWithDnD
from .formattingtags import FormattingTags
from .spelltextedit import SpellTextEdit
from .plugin import PluginStatus, StringContent, Plugin
@ -343,8 +340,6 @@ from .pluginmanager import PluginManager
from .settingstab import SettingsTab
from .serviceitem import ServiceItem, ServiceItemType, ItemCapabilities
from .htmlbuilder import build_html, build_lyrics_format_css, build_lyrics_outline_css
from .toolbar import OpenLPToolbar
from .dockwidget import OpenLPDockWidget
from .imagemanager import ImageManager
from .renderer import Renderer
from .mediamanageritem import MediaManagerItem

View File

@ -29,10 +29,11 @@ import re
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate
from openlp.core.lib import FileDialog, OpenLPToolbar, ServiceItem, StringContent, ListWidgetWithDnD, \
ServiceItemContext
from openlp.core.lib import FileDialog, ServiceItem, StringContent, ServiceItemContext
from openlp.core.lib.searchedit import SearchEdit
from openlp.core.lib.ui import create_widget_action, critical_error_message_box
from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD
from openlp.core.ui.lib.toolbar import OpenLPToolbar
log = logging.getLogger(__name__)

View File

@ -113,7 +113,6 @@ from .settingsform import SettingsForm
from .formattingtagform import FormattingTagForm
from .formattingtagcontroller import FormattingTagController
from .shortcutlistform import ShortcutListForm
from .mediadockmanager import MediaDockManager
from .servicemanager import ServiceManager
from .thememanager import ThemeManager
from .projector.manager import ProjectorManager
@ -121,7 +120,7 @@ from .projector.tab import ProjectorTab
from .projector.editform import ProjectorEditForm
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeForm',
'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm',
'ThemeManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm',
'Display', 'ServiceNoteForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay',
'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'SingleColumnTableWidget',

View File

@ -24,7 +24,7 @@ The UI widgets for the first time wizard.
"""
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import translate, is_macosx, clean_button_text
from openlp.core.common import translate, is_macosx, clean_button_text, Settings
from openlp.core.lib import build_icon
from openlp.core.lib.ui import add_welcome_page
@ -136,6 +136,13 @@ class UiFirstTimeWizard(object):
self.alert_check_box.setChecked(True)
self.alert_check_box.setObjectName('alert_check_box')
self.plugin_layout.addWidget(self.alert_check_box)
self.projectors_check_box = QtWidgets.QCheckBox(self.plugin_page)
# If visibility setting for projector panel is True, check the box.
if Settings().value('projector/show after wizard'):
self.projectors_check_box.setChecked(True)
self.projectors_check_box.setObjectName('projectors_check_box')
self.projectors_check_box.clicked.connect(self.on_projectors_check_box_clicked)
self.plugin_layout.addWidget(self.projectors_check_box)
first_time_wizard.setPage(FirstTimePage.Plugins, self.plugin_page)
# The song samples page
self.songs_page = QtWidgets.QWizardPage()
@ -232,17 +239,28 @@ class UiFirstTimeWizard(object):
'downloaded.'))
self.download_label.setText(translate('OpenLP.FirstTimeWizard', 'Please wait while OpenLP downloads the '
'resource index file...'))
self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Activate required Plugins'))
self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select the Plugins you wish to use. '))
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.'))
self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))
self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Custom Slides'))
self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
self.image_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Images'))
self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations'))
self.media_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Media (Audio and Video)'))
self.remote_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Allow remote access'))
self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Monitor Song Usage'))
self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Allow Alerts'))
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',
'Presentations Show .ppt, .odp and .pdf files'))
self.media_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Media Playback of Audio and Video files'))
self.remote_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Remote Control OpenLP via browser or smart'
'phone app'))
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.projectors_check_box.setText(translate('OpenLP.FirstTimeWizard',
'Projectors Control PJLink compatible projects on your network'
' from OpenLP'))
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.'))
@ -277,3 +295,10 @@ class UiFirstTimeWizard(object):
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)

View File

@ -27,7 +27,8 @@ import logging
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, Settings, UiStrings, translate, get_images_filter
from openlp.core.lib import SettingsTab, ScreenList, ColorButton, build_icon
from openlp.core.lib import SettingsTab, ScreenList, build_icon
from openlp.core.ui.lib.colorbutton import ColorButton
log = logging.getLogger(__name__)

View File

@ -19,3 +19,15 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from .colorbutton import ColorButton
from .listwidgetwithdnd import ListWidgetWithDnD
from .treewidgetwithdnd import TreeWidgetWithDnD
from .toolbar import OpenLPToolbar
from .dockwidget import OpenLPDockWidget
from .wizard import OpenLPWizard, WizardStrings
from .mediadockmanager import MediaDockManager
from .listpreviewwidget import ListPreviewWidget
__all__ = ['ColorButton', 'ListPreviewWidget', 'ListWidgetWithDnD', 'OpenLPToolbar', 'OpenLPDockWidget',
'OpenLPWizard', 'WizardStrings', 'MediaDockManager', 'ListPreviewWidget']

View File

@ -152,9 +152,6 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
else:
label.setScaledContents(True)
if self.service_item.is_command():
#pixmap = QtGui.QPixmap(frame['image'])
#pixmap.setDevicePixelRatio(label.devicePixelRatio())
#label.setPixmap(pixmap)
image = self.image_manager.get_image(frame['image'], ImageSource.PresentationPlugin)
else:
image = self.image_manager.get_image(frame['path'], ImageSource.ImagePlugin)

View File

@ -45,6 +45,7 @@ class WizardStrings(object):
OS = 'OpenSong'
OSIS = 'OSIS'
ZEF = 'Zefania'
SWORD = 'Sword'
# These strings should need a good reason to be retranslated elsewhere.
FinishedImport = translate('OpenLP.Ui', 'Finished import.')
FormatLabel = translate('OpenLP.Ui', 'Format:')
@ -113,7 +114,7 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
Set up the wizard UI.
:param image: path to start up image
"""
self.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
self.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
self.setModal(True)
self.setOptions(QtWidgets.QWizard.IndependentPages |
QtWidgets.QWizard.NoBackButtonOnStartPage | QtWidgets.QWizard.NoBackButtonOnLastPage)

View File

@ -247,7 +247,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
"""
Set up and build the output screen
"""
self.log_debug('Start MainDisplay setup (live = %s)' % self.is_live)
self.log_debug('Start MainDisplay setup (live = {islive})'.format(islive=self.is_live))
self.screen = self.screens.current
self.setVisible(False)
Display.setup(self)
@ -288,7 +288,9 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
self.application.process_events()
self.setGeometry(self.screen['size'])
if animate:
self.frame.evaluateJavaScript('show_text("%s")' % slide.replace('\\', '\\\\').replace('\"', '\\\"'))
# NOTE: Verify this works with ''.format()
_text = slide.replace('\\', '\\\\').replace('\"', '\\\"')
self.frame.evaluateJavaScript('show_text("{text}")'.format(text=_text))
else:
# This exists for https://bugs.launchpad.net/openlp/+bug/1016843
# For unknown reasons if evaluateJavaScript is called
@ -309,10 +311,10 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
text_prepared = expand_tags(html.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"')
if self.height() != self.screen['size'].height() or not self.isVisible():
shrink = True
js = 'show_alert("%s", "%s")' % (text_prepared, 'top')
js = 'show_alert("{text}", "{top}")'.format(text=text_prepared, top='top')
else:
shrink = False
js = 'show_alert("%s", "")' % text_prepared
js = 'show_alert("{text}", "")'.format(text=text_prepared)
height = self.frame.evaluateJavaScript(js)
if shrink:
if text:
@ -368,7 +370,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
"""
self.setGeometry(self.screen['size'])
if image:
js = 'show_image("data:image/png;base64,%s");' % image
js = 'show_image("data:image/png;base64,{image}");'.format(image=image)
else:
js = 'show_image("");'
self.frame.evaluateJavaScript(js)
@ -492,7 +494,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
:param mode: How the screen is to be hidden
"""
self.log_debug('hide_display mode = %d' % mode)
self.log_debug('hide_display mode = {mode:d}'.format(mode=mode))
if self.screens.display_count == 1:
# Only make visible if setting enabled.
if not Settings().value('core/display on monitor'):

View File

@ -38,15 +38,17 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, Langua
check_directory_exists, translate, is_win, is_macosx, add_actions
from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.common.versionchecker import get_application_version
from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageManager, PluginStatus, ScreenList, \
build_icon
from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, ScreenList, build_icon
from openlp.core.lib.ui import UiStrings, create_action
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, LiveController, PluginForm, \
MediaDockManager, ShortcutListForm, FormattingTagForm, PreviewController
ShortcutListForm, FormattingTagForm, PreviewController
from openlp.core.ui.firsttimeform import FirstTimeForm
from openlp.core.ui.media import MediaController
from openlp.core.ui.printserviceform import PrintServiceForm
from openlp.core.ui.projector.manager import ProjectorManager
from openlp.core.ui.lib.toolbar import OpenLPToolbar
from openlp.core.ui.lib.dockwidget import OpenLPDockWidget
from openlp.core.ui.lib.mediadockmanager import MediaDockManager
log = logging.getLogger(__name__)
@ -620,11 +622,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
:param version: The Version to be displayed.
"""
log.debug('version_notice')
version_text = translate('OpenLP.MainWindow', 'Version %s of OpenLP is now available for download (you are '
'currently running version %s). \n\nYou can download the latest version from '
'http://openlp.org/.')
QtWidgets.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Version Updated'),
version_text % (version, get_application_version()[u'full']))
version_text = translate('OpenLP.MainWindow', 'Version {new} of OpenLP is now available for download (you are '
'currently running version {current}). \n\nYou can download the latest version from '
'http://openlp.org/.').format(new=version, current=get_application_version()[u'full'])
QtWidgets.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Version Updated'), version_text)
def show(self):
"""
@ -638,13 +639,15 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
self.open_cmd_line_files(self.arguments)
elif Settings().value(self.general_settings_section + '/auto open'):
self.service_manager_contents.load_last_file()
view_mode = Settings().value('%s/view mode' % self.general_settings_section)
if view_mode == 'default':
# This will store currently used layout preset so it remains enabled on next startup.
# If any panel is enabled/disabled after preset is set, this setting is not saved.
view_mode = Settings().value('{section}/view mode'.format(section=self.general_settings_section))
if view_mode == 'default' and Settings().value('user interface/is preset layout'):
self.mode_default_item.setChecked(True)
elif view_mode == 'setup':
elif view_mode == 'setup' and Settings().value('user interface/is preset layout'):
self.set_view_mode(True, True, False, True, False, True)
self.mode_setup_item.setChecked(True)
elif view_mode == 'live':
elif view_mode == 'live' and Settings().value('user interface/is preset layout'):
self.set_view_mode(False, True, False, False, True, True)
self.mode_live_item.setChecked(True)
@ -696,6 +699,11 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
return
self.application.set_busy_cursor()
self.first_time()
# Check if Projectors panel should be visible or not after wizard.
if Settings().value('projector/show after wizard'):
self.projector_manager_dock.setVisible(True)
else:
self.projector_manager_dock.setVisible(False)
for plugin in self.plugin_manager.plugins:
self.active_plugin = plugin
old_status = self.active_plugin.status
@ -722,8 +730,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
"""
settings = Settings()
self.live_controller.main_display_set_background()
if settings.value('%s/screen blank' % self.general_settings_section):
if settings.value('%s/blank warning' % self.general_settings_section):
if settings.value('{section}/screen blank'.format(section=self.general_settings_section)):
if settings.value('{section}/blank warning'.format(section=self.general_settings_section)):
QtWidgets.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Main Display Blanked'),
translate('OpenLP.MainWindow', 'The Main Display has been blanked out'))
@ -915,9 +923,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
try:
value = import_settings.value(section_key)
except KeyError:
log.warning('The key "%s" does not exist (anymore), so it will be skipped.' % section_key)
log.warning('The key "{key}" does not exist (anymore), so it will be skipped.'.format(key=section_key))
if value is not None:
settings.setValue('%s' % (section_key), value)
settings.setValue('{key}'.format(key=section_key), value)
now = datetime.now()
settings.beginGroup(self.header_section)
settings.setValue('file_imported', import_file_name)
@ -994,9 +1002,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
key_value = settings.value(section_key)
except KeyError:
QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'),
translate('OpenLP.MainWindow', 'The key "%s" does not have a default '
translate('OpenLP.MainWindow', 'The key "{key}" does not have a default '
'value so it will be skipped in this '
'export.') % section_key,
'export.').format(key=section_key),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
key_value = None
if key_value is not None:
@ -1018,8 +1026,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
os.remove(temp_file)
except OSError as ose:
QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'),
translate('OpenLP.MainWindow', 'An error occurred while exporting the '
'settings: %s') % ose.strerror,
translate('OpenLP.MainWindow',
'An error occurred while exporting the '
'settings: {err}').format(err=ose.strerror),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
def on_mode_default_item_clicked(self):
@ -1027,18 +1036,24 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
Put OpenLP into "Default" view mode.
"""
self.set_view_mode(True, True, True, True, True, True, 'default')
Settings().setValue('user interface/is preset layout', True)
Settings().setValue('projector/show after wizard', True)
def on_mode_setup_item_clicked(self):
"""
Put OpenLP into "Setup" view mode.
"""
self.set_view_mode(True, True, False, True, False, True, 'setup')
Settings().setValue('user interface/is preset layout', True)
Settings().setValue('projector/show after wizard', True)
def on_mode_live_item_clicked(self):
"""
Put OpenLP into "Live" view mode.
"""
self.set_view_mode(False, True, False, False, True, True, 'live')
Settings().setValue('user interface/is preset layout', True)
Settings().setValue('projector/show after wizard', True)
def set_view_mode(self, media=True, service=True, theme=True, preview=True, live=True, projector=True, mode=''):
"""
@ -1046,7 +1061,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
"""
if mode:
settings = Settings()
settings.setValue('%s/view mode' % self.general_settings_section, mode)
settings.setValue('{section}/view mode'.format(section=self.general_settings_section), mode)
self.media_manager_dock.setVisible(media)
self.service_manager_dock.setVisible(service)
self.theme_manager_dock.setVisible(theme)
@ -1153,9 +1168,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
:param file_name: The file name of the service file.
"""
if modified:
title = '%s - %s*' % (UiStrings().OLPV2x, file_name)
title = '{title} - {name}*'.format(title=UiStrings().OLPV2x, name=file_name)
else:
title = '%s - %s' % (UiStrings().OLPV2x, file_name)
title = '{title} - {name}'.format(title=UiStrings().OLPV2x, name=file_name)
self.setWindowTitle(title)
def show_status_message(self, message):
@ -1168,32 +1183,42 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
"""
Update the default theme indicator in the status bar
"""
self.default_theme_label.setText(translate('OpenLP.MainWindow', 'Default Theme: %s') %
Settings().value('themes/global theme'))
theme_name = Settings().value('themes/global theme')
self.default_theme_label.setText(translate('OpenLP.MainWindow',
'Default Theme: {theme}').format(theme=theme_name))
def toggle_media_manager(self):
"""
Toggle the visibility of the media manager
"""
self.media_manager_dock.setVisible(not self.media_manager_dock.isVisible())
Settings().setValue('user interface/is preset layout', False)
def toggle_projector_manager(self):
"""
Toggle visibility of the projector manager
"""
self.projector_manager_dock.setVisible(not self.projector_manager_dock.isVisible())
Settings().setValue('user interface/is preset layout', False)
# Check/uncheck checkbox on First time wizard based on visibility of this panel.
if not Settings().value('projector/show after wizard'):
Settings().setValue('projector/show after wizard', True)
else:
Settings().setValue('projector/show after wizard', False)
def toggle_service_manager(self):
"""
Toggle the visibility of the service manager
"""
self.service_manager_dock.setVisible(not self.service_manager_dock.isVisible())
Settings().setValue('user interface/is preset layout', False)
def toggle_theme_manager(self):
"""
Toggle the visibility of the theme manager
"""
self.theme_manager_dock.setVisible(not self.theme_manager_dock.isVisible())
Settings().setValue('user interface/is preset layout', False)
def set_preview_panel_visibility(self, visible):
"""
@ -1207,6 +1232,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
self.preview_controller.panel.setVisible(visible)
Settings().setValue('user interface/preview panel', visible)
self.view_preview_panel.setChecked(visible)
Settings().setValue('user interface/is preset layout', False)
def set_lock_panel(self, lock):
"""
@ -1217,6 +1243,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
self.service_manager_dock.setFeatures(QtWidgets.QDockWidget.NoDockWidgetFeatures)
self.media_manager_dock.setFeatures(QtWidgets.QDockWidget.NoDockWidgetFeatures)
self.projector_manager_dock.setFeatures(QtWidgets.QDockWidget.NoDockWidgetFeatures)
self.view_mode_menu.setEnabled(False)
self.view_media_manager_item.setEnabled(False)
self.view_service_manager_item.setEnabled(False)
self.view_theme_manager_item.setEnabled(False)
@ -1228,6 +1255,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
self.service_manager_dock.setFeatures(QtWidgets.QDockWidget.AllDockWidgetFeatures)
self.media_manager_dock.setFeatures(QtWidgets.QDockWidget.AllDockWidgetFeatures)
self.projector_manager_dock.setFeatures(QtWidgets.QDockWidget.AllDockWidgetFeatures)
self.view_mode_menu.setEnabled(True)
self.view_media_manager_item.setEnabled(True)
self.view_service_manager_item.setEnabled(True)
self.view_theme_manager_item.setEnabled(True)
@ -1248,6 +1276,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
self.live_controller.panel.setVisible(visible)
Settings().setValue('user interface/live panel', visible)
self.view_live_panel.setChecked(visible)
Settings().setValue('user interface/is preset layout', False)
def load_settings(self):
"""
@ -1303,7 +1332,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
recent_files_to_display = existing_recent_files[0:recent_file_count]
self.recent_files_menu.clear()
for file_id, filename in enumerate(recent_files_to_display):
log.debug('Recent file name: %s', filename)
log.debug('Recent file name: {name}'.format(name=filename))
# TODO: Verify ''.format() before committing
action = create_action(self, '', text='&%d %s' % (file_id + 1,
os.path.splitext(os.path.basename(str(filename)))[0]), data=filename,
triggers=self.service_manager_contents.on_recent_service_clicked)
@ -1396,7 +1426,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
"""
Change the data directory.
"""
log.info('Changing data path to %s' % self.new_data_path)
log.info('Changing data path to {newpath}'.format(newpath=self.new_data_path))
old_data_path = str(AppLocation.get_data_path())
# Copy OpenLP data to new location if requested.
self.application.set_busy_cursor()
@ -1404,17 +1434,17 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
log.info('Copying data to new path')
try:
self.show_status_message(
translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - %s '
'- Please wait for copy to finish').replace('%s', self.new_data_path))
translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - {path} '
'- Please wait for copy to finish').format(path=self.new_data_path))
dir_util.copy_tree(old_data_path, self.new_data_path)
log.info('Copy successful')
except (IOError, os.error, DistutilsFileError) as why:
self.application.set_normal_cursor()
log.exception('Data copy failed %s' % str(why))
log.exception('Data copy failed {err}'.format(err=str(why)))
err_text = translate('OpenLP.MainWindow',
'OpenLP Data directory copy failed\n\n{err}').format(err=str(why)),
QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'New Data Directory Error'),
translate('OpenLP.MainWindow',
'OpenLP Data directory copy failed\n\n%s').
replace('%s', str(why)),
err_text,
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
return False
else:

View File

@ -29,14 +29,16 @@ import datetime
from PyQt5 import QtCore, QtWidgets
from openlp.core.common import OpenLPMixin, Registry, RegistryMixin, RegistryProperties, Settings, UiStrings, translate
from openlp.core.lib import OpenLPToolbar, ItemCapabilities
from openlp.core.lib import ItemCapabilities
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players,\
parse_optical_path
from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
from openlp.core.ui.media.mediaplayer import MediaPlayer
from openlp.core.common import AppLocation
from openlp.core.ui import DisplayControllerType
from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
from openlp.core.ui.media.mediaplayer import MediaPlayer
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players,\
parse_optical_path
from openlp.core.ui.lib.toolbar import OpenLPToolbar
from openlp.core.ui.lib.dockwidget import OpenLPDockWidget
log = logging.getLogger(__name__)
@ -296,7 +298,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
triggers=controller.send_to_plugins)
controller.mediabar.add_toolbar_action('playbackLoop', text='media_playback_loop',
icon=':/slides/media_playback_stop.png', checked=False,
icon=':/media/media_repeat.png', checked=False,
tooltip=translate('OpenLP.SlideController', 'Loop playing media.'),
triggers=controller.send_to_plugins)
controller.position_label = QtWidgets.QLabel()

View File

@ -26,9 +26,10 @@ import platform
from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, Settings, UiStrings, translate
from openlp.core.lib import ColorButton, SettingsTab
from openlp.core.lib import SettingsTab
from openlp.core.lib.ui import create_button
from openlp.core.ui.media import get_media_players, set_media_players
from openlp.core.ui.lib.colorbutton import ColorButton
class MediaQCheckBox(QtWidgets.QCheckBox):

View File

@ -182,9 +182,10 @@ class ProjectorEditForm(QDialog, Ui_ProjectorEditForm):
QtWidgets.QMessageBox.warning(self,
translate('OpenLP.ProjectorEdit', 'Duplicate Name'),
translate('OpenLP.ProjectorEdit',
'There is already an entry with name "%s" in '
'the database as ID "%s". <br />'
'Please enter a different name.' % (name, record.id)))
'There is already an entry with name "{name}" in '
'the database as ID "{record}". <br />'
'Please enter a different name.'.format(name=name,
record=record.id)))
valid = False
return
adx = self.ip_text.text()
@ -198,17 +199,17 @@ class ProjectorEditForm(QDialog, Ui_ProjectorEditForm):
QtWidgets.QMessageBox.warning(self,
translate('OpenLP.ProjectorWizard', 'Duplicate IP Address'),
translate('OpenLP.ProjectorWizard',
'IP address "%s"<br />is already in the database as ID %s.'
'<br /><br />Please Enter a different IP address.' %
(adx, ip.id)))
'IP address "{ip}"<br />is already in the database '
'as ID {data}.<br /><br />Please Enter a different '
'IP address.'.format(ip=adx, data=ip.id)))
valid = False
return
else:
QtWidgets.QMessageBox.warning(self,
translate('OpenLP.ProjectorWizard', 'Invalid IP Address'),
translate('OpenLP.ProjectorWizard',
'IP address "%s"<br>is not a valid IP address.'
'<br /><br />Please enter a valid IP address.' % adx))
'IP address "{ip}"<br>is not a valid IP address.'
'<br /><br />Please enter a valid IP address.'.format(ip=adx)))
valid = False
return
port = int(self.port_text.text())
@ -219,8 +220,8 @@ class ProjectorEditForm(QDialog, Ui_ProjectorEditForm):
'Port numbers below 1000 are reserved for admin use only, '
'<br />and port numbers above 32767 are not currently usable.'
'<br /><br />Please enter a valid port number between '
' 1000 and 32767.'
'<br /><br />Default PJLink port is %s' % PJLINK_PORT))
'1000 and 32767.<br /><br />'
'Default PJLink port is {port}'.format(port=PJLINK_PORT)))
valid = False
if valid:
self.projector.ip = self.ip_text.text()

View File

@ -35,7 +35,7 @@ from PyQt5.QtWidgets import QWidget
from openlp.core.common import RegistryProperties, Settings, OpenLPMixin, \
RegistryMixin, translate
from openlp.core.lib import OpenLPToolbar
from openlp.core.ui.lib import OpenLPToolbar
from openlp.core.lib.ui import create_widget_action
from openlp.core.lib.projector import DialogSourceStyle
from openlp.core.lib.projector.constants import *
@ -344,7 +344,7 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
real_projector = item.data(QtCore.Qt.UserRole)
projector_name = str(item.text())
visible = real_projector.link.status_connect >= S_CONNECTED
log.debug('(%s) Building menu - visible = %s' % (projector_name, visible))
log.debug('({name}) Building menu - visible = {visible}'.format(name=projector_name, visible=visible))
self.delete_action.setVisible(True)
self.edit_action.setVisible(True)
self.connect_action.setVisible(not visible)
@ -394,7 +394,7 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
projectordb=self.projectordb,
edit=edit)
source = source_select_form.exec(projector.link)
log.debug('(%s) source_select_form() returned %s' % (projector.link.ip, source))
log.debug('({ip}) source_select_form() returned {data}'.format(ip=projector.link.ip, data=source))
if source is not None and source > 0:
projector.link.set_input_source(str(source))
return
@ -473,8 +473,9 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
return
projector = list_item.data(QtCore.Qt.UserRole)
msg = QtWidgets.QMessageBox()
msg.setText(translate('OpenLP.ProjectorManager', 'Delete projector (%s) %s?') % (projector.link.ip,
projector.link.name))
msg.setText(translate('OpenLP.ProjectorManager',
'Delete projector ({ip}) {name}?'.format(ip=projector.link.ip,
name=projector.link.name)))
msg.setInformativeText(translate('OpenLP.ProjectorManager', 'Are you sure you want to delete this projector?'))
msg.setStandardButtons(msg.Cancel | msg.Ok)
msg.setDefaultButton(msg.Cancel)
@ -522,7 +523,7 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
list_item = None
deleted = self.projectordb.delete_projector(projector.db_item)
for item in self.projector_list:
log.debug('New projector list - item: %s %s' % (item.link.ip, item.link.name))
log.debug('New projector list - item: {ip} {name}'.format(ip=item.link.ip, name=item.link.name))
def on_disconnect_projector(self, opt=None):
"""
@ -627,53 +628,58 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
"""
lwi = self.projector_list_widget.item(self.projector_list_widget.currentRow())
projector = lwi.data(QtCore.Qt.UserRole)
message = '<b>%s</b>: %s<BR />' % (translate('OpenLP.ProjectorManager', 'Name'),
projector.link.name)
message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'IP'),
projector.link.ip)
message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Port'),
projector.link.port)
message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Notes'),
projector.link.notes)
message = '%s<hr /><br >' % message
message = '<b>{title}</b>: {data}<BR />'.format(title=translate('OpenLP.ProjectorManager', 'Name'),
data=projector.link.name)
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'IP'),
data=projector.link.ip)
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Port'),
data=projector.link.port)
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Notes'),
data=projector.link.notes)
message += '<hr /><br >'
if projector.link.manufacturer is None:
message = '%s%s' % (message, translate('OpenLP.ProjectorManager',
'Projector information not available at this time.'))
message += translate('OpenLP.ProjectorManager', 'Projector information not available at this time.')
else:
message = '%s<b>%s</b>: %s<BR />' % (message, translate('OpenLP.ProjectorManager', 'Projector Name'),
projector.link.pjlink_name)
message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Manufacturer'),
projector.link.manufacturer)
message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Model'),
projector.link.model)
message = '%s<b>%s</b>: %s<br /><br />' % (message, translate('OpenLP.ProjectorManager', 'Other info'),
projector.link.other_info)
message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Power status'),
ERROR_MSG[projector.link.power])
message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Shutter is'),
translate('OpenLP.ProjectorManager', 'Closed')
if projector.link.shutter else translate('OpenLP', 'Open'))
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager',
'Projector Name'),
data=projector.link.pjlink_name)
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Manufacturer'),
data=projector.link.manufacturer)
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Model'),
data=projector.link.model)
message += '<b>{title}</b>: {data}<br /><br />'.format(title=translate('OpenLP.ProjectorManager',
'Other info'),
data=projector.link.other_info)
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Power status'),
data=ERROR_MSG[projector.link.power])
message += '<b>{title}</b>: {data}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Shutter is'),
data=translate('OpenLP.ProjectorManager', 'Closed')
if projector.link.shutter
else translate('OpenLP', 'Open'))
message = '%s<b>%s</b>: %s<br />' % (message,
translate('OpenLP.ProjectorManager', 'Current source input is'),
projector.link.source)
count = 1
for item in projector.link.lamp:
message = '%s <b>%s %s</b> (%s) %s: %s<br />' % (message,
translate('OpenLP.ProjectorManager', 'Lamp'),
count,
translate('OpenLP.ProjectorManager', 'On')
if item['On']
else translate('OpenLP.ProjectorManager', 'Off'),
translate('OpenLP.ProjectorManager', 'Hours'),
item['Hours'])
count = count + 1
message = '%s<hr /><br />' % message
message += '<b>{title} {count}</b> {status} '.format(title=translate('OpenLP.ProjectorManager',
'Lamp'),
count=count,
status=translate('OpenLP.ProjectorManager',
' is on')
if item['On']
else translate('OpenLP.ProjectorManager',
'is off'))
message += '<b>{title}</b>: {hours}<br />'.format(title=translate('OpenLP.ProjectorManager', 'Hours'),
hours=item['Hours'])
count += 1
message += '<hr /><br />'
if projector.link.projector_errors is None:
message = '%s%s' % (message, translate('OpenLP.ProjectorManager', 'No current errors or warnings'))
message += translate('OpenLP.ProjectorManager', 'No current errors or warnings')
else:
message = '%s<b>%s</b>' % (message, translate('OpenLP.ProjectorManager', 'Current errors/warnings'))
message += '<b>{data}</b>'.format(data=translate('OpenLP.ProjectorManager', 'Current errors/warnings'))
for (key, val) in projector.link.projector_errors.items():
message = '%s<b>%s</b>: %s<br />' % (message, key, ERROR_MSG[val])
message += '<b>{key}</b>: {data}<br />'.format(key=key, data=ERROR_MSG[val])
QtWidgets.QMessageBox.information(self, translate('OpenLP.ProjectorManager', 'Projector Information'), message)
def _add_projector(self, projector):
@ -743,7 +749,7 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
if start:
item.link.connect_to_host()
for item in self.projector_list:
log.debug('New projector list - item: (%s) %s' % (item.link.ip, item.link.name))
log.debug('New projector list - item: ({ip}) {name}'.format(ip=item.link.ip, name=item.link.name))
@pyqtSlot(str)
def add_projector_from_wizard(self, ip, opts=None):
@ -753,7 +759,7 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
:param ip: IP address of new record item to find
:param opts: Needed by PyQt5
"""
log.debug('add_projector_from_wizard(ip=%s)' % ip)
log.debug('add_projector_from_wizard(ip={ip})'.format(ip=ip))
item = self.projectordb.get_projector_by_ip(ip)
self.add_projector(item)
@ -764,7 +770,7 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
:param projector: Projector() instance of projector with updated information
"""
log.debug('edit_projector_from_wizard(ip=%s)' % projector.ip)
log.debug('edit_projector_from_wizard(ip={ip})'.format(ip=projector.ip))
self.old_projector.link.name = projector.name
self.old_projector.link.ip = projector.ip
self.old_projector.link.pin = None if projector.pin == '' else projector.pin
@ -816,7 +822,9 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
else:
status_code = status
message = ERROR_MSG[status] if msg is None else msg
log.debug('(%s) updateStatus(status=%s) message: "%s"' % (item.link.name, status_code, message))
log.debug('({name}) updateStatus(status={status}) message: "{message}"'.format(name=item.link.name,
status=status_code,
message=message))
if status in STATUS_ICONS:
if item.status == status:
return
@ -826,14 +834,14 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
status_code = ERROR_STRING[status]
elif status in STATUS_STRING:
status_code = STATUS_STRING[status]
log.debug('(%s) Updating icon with %s' % (item.link.name, status_code))
log.debug('({name}) Updating icon with {code}'.format(name=item.link.name, code=status_code))
item.widget.setIcon(item.icon)
self.update_icons()
def get_toolbar_item(self, name, enabled=False, hidden=False):
item = self.one_toolbar.findChild(QtWidgets.QAction, name)
if item == 0:
log.debug('No item found with name "%s"' % name)
log.debug('No item found with name "{name}"'.format(name=name))
return
item.setVisible(False if hidden else True)
item.setEnabled(True if enabled else False)
@ -918,11 +926,12 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
:param name: Name from QListWidgetItem
"""
QtWidgets.QMessageBox.warning(self, translate('OpenLP.ProjectorManager',
'"%s" Authentication Error' % name),
title = '"{name} {message}" '.format(name=name,
message=translate('OpenLP.ProjectorManager', 'Authentication Error'))
QtWidgets.QMessageBox.warning(self, title,
'<br />There was an authentication error while trying to connect.'
'<br /><br />Please verify your PIN setting '
'for projector item "%s"' % name)
'for projector item "{name}"'.format(name=name))
@pyqtSlot(str)
def no_authentication_error(self, name):
@ -932,11 +941,12 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager,
:param name: Name from QListWidgetItem
"""
QtWidgets.QMessageBox.warning(self, translate('OpenLP.ProjectorManager',
'"%s" No Authentication Error' % name),
title = '"{name} {message}" '.format(name=name,
message=translate('OpenLP.ProjectorManager', 'No Authentication Error'))
QtWidgets.QMessageBox.warning(self, title,
'<br />PIN is set and projector does not require authentication.'
'<br /><br />Please verify your PIN setting '
'for projector item "%s"' % name)
'for projector item "{name}"'.format(name=name))
class ProjectorItem(QObject):
@ -972,5 +982,5 @@ def not_implemented(function):
QtWidgets.QMessageBox.information(None,
translate('OpenLP.ProjectorManager', 'Not Implemented Yet'),
translate('OpenLP.ProjectorManager',
'Function "%s"<br />has not been implemented yet.'
'<br />Please check back again later.' % function))
'Function "{function}"<br />has not been implemented yet.'
'<br />Please check back again later.'.format(function=function)))

View File

@ -115,7 +115,7 @@ def Build_Tab(group, source_key, default, projector, projectordb, edit=False):
if edit:
for key in sourcelist:
item = QLineEdit()
item.setObjectName('source_key_%s' % key)
item.setObjectName('source_key_{key}'.format(key=key))
source_item = projectordb.get_source_by_code(code=key, projector_id=projector.db_item.id)
if source_item is None:
item.setText(PJLINK_DEFAULT_CODES[key])
@ -161,7 +161,7 @@ def set_button_tooltip(bar):
button.setToolTip(translate('OpenLP.SourceSelectForm',
'Save changes and return to OpenLP'))
else:
log.debug('No tooltip for button {}'.format(button.text()))
log.debug('No tooltip for button {text}'.format(text=button.text()))
class FingerTabBarWidget(QTabBar):
@ -359,16 +359,20 @@ class SourceSelectTabs(QDialog):
continue
item = self.projectordb.get_source_by_code(code=code, projector_id=projector.id)
if item is None:
log.debug("(%s) Adding new source text %s: %s" % (projector.ip, code, text))
log.debug("({ip}) Adding new source text {code}: {text}".format(ip=projector.ip,
code=code,
text=text))
item = ProjectorSource(projector_id=projector.id, code=code, text=text)
else:
item.text = text
log.debug('(%s) Updating source code %s with text="%s"' % (projector.ip, item.code, item.text))
log.debug('({ip}) Updating source code {code} with text="{text}"'.format(ip=projector.ip,
code=item.code,
text=item.text))
self.projectordb.add_source(item)
selected = 0
else:
selected = self.button_group.checkedId()
log.debug('SourceSelectTabs().accepted() Setting source to %s' % selected)
log.debug('SourceSelectTabs().accepted() Setting source to {selected}'.format(selected=selected))
self.done(selected)
@ -417,7 +421,7 @@ class SourceSelectSingle(QDialog):
if self.edit:
for key in keys:
item = QLineEdit()
item.setObjectName('source_key_%s' % key)
item.setObjectName('source_key_{key}'.format(key=key))
source_item = self.projectordb.get_source_by_code(code=key, projector_id=self.projector.db_item.id)
if source_item is None:
item.setText(PJLINK_DEFAULT_CODES[key])
@ -498,14 +502,18 @@ class SourceSelectSingle(QDialog):
continue
item = self.projectordb.get_source_by_code(code=code, projector_id=projector.id)
if item is None:
log.debug("(%s) Adding new source text %s: %s" % (projector.ip, code, text))
log.debug("({ip}) Adding new source text {code}: {text}".format(ip=projector.ip,
code=code,
text=text))
item = ProjectorSource(projector_id=projector.id, code=code, text=text)
else:
item.text = text
log.debug('(%s) Updating source code %s with text="%s"' % (projector.ip, item.code, item.text))
log.debug('({ip}) Updating source code {code} with text="{text}"'.format(ip=projector.ip,
code=item.code,
text=item.text))
self.projectordb.add_source(item)
selected = 0
else:
selected = self.button_group.checkedId()
log.debug('SourceSelectDialog().accepted() Setting source to %s' % selected)
log.debug('SourceSelectDialog().accepted() Setting source to {selected}'.format(selected=selected))
self.done(selected)

View File

@ -35,9 +35,10 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, ThemeLevel, OpenLPMixin, \
RegistryMixin, check_directory_exists, UiStrings, translate, split_filename, delete_file
from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, build_icon
from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon
from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
from openlp.core.ui.lib import OpenLPToolbar
from openlp.core.common.languagemanager import format_time

View File

@ -33,11 +33,14 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, Settings, SlideLimits, UiStrings, translate, \
RegistryMixin, OpenLPMixin
from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, \
ScreenList, build_icon, build_html
from openlp.core.lib import ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, ScreenList, build_icon, \
build_html
from openlp.core.lib.ui import create_action
from openlp.core.ui.lib.toolbar import OpenLPToolbar
from openlp.core.ui.lib.dockwidget import OpenLPDockWidget
from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget
from openlp.core.ui import HideMode, MainDisplay, Display, DisplayControllerType
from openlp.core.ui.listpreviewwidget import ListPreviewWidget
# Threshold which has to be trespassed to toggle.
HIDE_MENU_THRESHOLD = 27

View File

@ -31,6 +31,7 @@ from openlp.core.common import Registry, RegistryProperties, UiStrings, translat
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui import ThemeLayoutForm
from openlp.core.ui.lib.colorbutton import ColorButton
from .themewizard import Ui_ThemeWizard
log = logging.getLogger(__name__)

View File

@ -31,11 +31,12 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \
check_directory_exists, UiStrings, translate, is_win, get_filesystem_encoding, delete_file
from openlp.core.lib import FileDialog, ImageSource, OpenLPToolbar, ValidationError, get_text_file_string, build_icon, \
from openlp.core.lib import FileDialog, ImageSource, ValidationError, get_text_file_string, build_icon, \
check_item_selected, create_thumb, validate_thumb
from openlp.core.lib.theme import ThemeXML, BackgroundType
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
from openlp.core.ui import FileRenameForm, ThemeForm
from openlp.core.ui.lib import OpenLPToolbar
from openlp.core.common.languagemanager import get_locale_key

View File

@ -25,9 +25,10 @@ The Create/Edit theme wizard
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import UiStrings, translate, is_macosx
from openlp.core.lib import build_icon, ColorButton
from openlp.core.lib import build_icon
from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType
from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets
from openlp.core.ui.lib.colorbutton import ColorButton
class Ui_ThemeWizard(object):

View File

@ -23,8 +23,9 @@
from PyQt5 import QtGui, QtWidgets
from openlp.core.common import Settings, UiStrings, translate
from openlp.core.lib import ColorButton, SettingsTab
from openlp.core.lib import SettingsTab
from openlp.core.lib.ui import create_valign_selection_widgets
from openlp.core.ui.lib.colorbutton import ColorButton
class AlertsTab(SettingsTab):

View File

@ -27,14 +27,19 @@ import os
import urllib.error
from PyQt5 import QtWidgets
try:
from pysword import modules
PYSWORD_AVAILABLE = True
except:
PYSWORD_AVAILABLE = False
from openlp.core.common import AppLocation, Settings, UiStrings, translate, clean_filename
from openlp.core.lib.db import delete_database
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
from openlp.core.common.languagemanager import get_locale_key
from openlp.plugins.bibles.lib.manager import BibleFormat
from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename
from openlp.plugins.bibles.lib.db import clean_filename
from openlp.plugins.bibles.lib.http import CWExtract, BGExtract, BSExtract
log = logging.getLogger(__name__)
@ -94,6 +99,19 @@ class BibleImportForm(OpenLPWizard):
self.manager.set_process_dialog(self)
self.restart()
self.select_stack.setCurrentIndex(0)
if PYSWORD_AVAILABLE:
self.pysword_folder_modules = modules.SwordModules()
try:
self.pysword_folder_modules_json = self.pysword_folder_modules.parse_modules()
except FileNotFoundError:
log.debug('No installed SWORD modules found in the default location')
self.sword_bible_combo_box.clear()
return
bible_keys = self.pysword_folder_modules_json.keys()
for key in bible_keys:
self.sword_bible_combo_box.addItem(self.pysword_folder_modules_json[key]['description'], key)
else:
self.sword_tab_widget.setDisabled(True)
def custom_signals(self):
"""
@ -106,6 +124,8 @@ class BibleImportForm(OpenLPWizard):
self.open_song_browse_button.clicked.connect(self.on_open_song_browse_button_clicked)
self.zefania_browse_button.clicked.connect(self.on_zefania_browse_button_clicked)
self.web_update_button.clicked.connect(self.on_web_update_button_clicked)
self.sword_browse_button.clicked.connect(self.on_sword_browse_button_clicked)
self.sword_zipbrowse_button.clicked.connect(self.on_sword_zipbrowse_button_clicked)
def add_custom_pages(self):
"""
@ -121,7 +141,7 @@ class BibleImportForm(OpenLPWizard):
self.format_label = QtWidgets.QLabel(self.select_page)
self.format_label.setObjectName('FormatLabel')
self.format_combo_box = QtWidgets.QComboBox(self.select_page)
self.format_combo_box.addItems(['', '', '', '', ''])
self.format_combo_box.addItems(['', '', '', '', '', ''])
self.format_combo_box.setObjectName('FormatComboBox')
self.format_layout.addRow(self.format_label, self.format_combo_box)
self.spacer = QtWidgets.QSpacerItem(10, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
@ -275,6 +295,64 @@ class BibleImportForm(OpenLPWizard):
self.zefania_layout.addRow(self.zefania_file_label, self.zefania_file_layout)
self.zefania_layout.setItem(5, QtWidgets.QFormLayout.LabelRole, self.spacer)
self.select_stack.addWidget(self.zefania_widget)
self.sword_widget = QtWidgets.QWidget(self.select_page)
self.sword_widget.setObjectName('SwordWidget')
self.sword_layout = QtWidgets.QVBoxLayout(self.sword_widget)
self.sword_layout.setObjectName('SwordLayout')
self.sword_tab_widget = QtWidgets.QTabWidget(self.sword_widget)
self.sword_tab_widget.setObjectName('SwordTabWidget')
self.sword_folder_tab = QtWidgets.QWidget(self.sword_tab_widget)
self.sword_folder_tab.setObjectName('SwordFolderTab')
self.sword_folder_tab_layout = QtWidgets.QGridLayout(self.sword_folder_tab)
self.sword_folder_tab_layout.setObjectName('SwordTabFolderLayout')
self.sword_folder_label = QtWidgets.QLabel(self.sword_folder_tab)
self.sword_folder_label.setObjectName('SwordSourceLabel')
self.sword_folder_tab_layout.addWidget(self.sword_folder_label, 0, 0)
self.sword_folder_label.setObjectName('SwordFolderLabel')
self.sword_folder_edit = QtWidgets.QLineEdit(self.sword_folder_tab)
self.sword_folder_edit.setObjectName('SwordFolderEdit')
self.sword_browse_button = QtWidgets.QToolButton(self.sword_folder_tab)
self.sword_browse_button.setIcon(self.open_icon)
self.sword_browse_button.setObjectName('SwordBrowseButton')
self.sword_folder_tab_layout.addWidget(self.sword_folder_edit, 0, 1)
self.sword_folder_tab_layout.addWidget(self.sword_browse_button, 0, 2)
self.sword_bible_label = QtWidgets.QLabel(self.sword_folder_tab)
self.sword_bible_label.setObjectName('SwordBibleLabel')
self.sword_folder_tab_layout.addWidget(self.sword_bible_label, 1, 0)
self.sword_bible_combo_box = QtWidgets.QComboBox(self.sword_folder_tab)
self.sword_bible_combo_box.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.sword_bible_combo_box.setInsertPolicy(QtWidgets.QComboBox.InsertAlphabetically)
self.sword_bible_combo_box.setObjectName('SwordBibleComboBox')
self.sword_folder_tab_layout.addWidget(self.sword_bible_combo_box, 1, 1)
self.sword_tab_widget.addTab(self.sword_folder_tab, '')
self.sword_zip_tab = QtWidgets.QWidget(self.sword_tab_widget)
self.sword_zip_tab.setObjectName('SwordZipTab')
self.sword_zip_layout = QtWidgets.QGridLayout(self.sword_zip_tab)
self.sword_zip_layout.setObjectName('SwordZipLayout')
self.sword_zipfile_label = QtWidgets.QLabel(self.sword_zip_tab)
self.sword_zipfile_label.setObjectName('SwordZipFileLabel')
self.sword_zipfile_edit = QtWidgets.QLineEdit(self.sword_zip_tab)
self.sword_zipfile_edit.setObjectName('SwordZipFileEdit')
self.sword_zipbrowse_button = QtWidgets.QToolButton(self.sword_zip_tab)
self.sword_zipbrowse_button.setIcon(self.open_icon)
self.sword_zipbrowse_button.setObjectName('SwordZipBrowseButton')
self.sword_zipbible_label = QtWidgets.QLabel(self.sword_folder_tab)
self.sword_zipbible_label.setObjectName('SwordZipBibleLabel')
self.sword_zipbible_combo_box = QtWidgets.QComboBox(self.sword_zip_tab)
self.sword_zipbible_combo_box.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.sword_zipbible_combo_box.setInsertPolicy(QtWidgets.QComboBox.InsertAlphabetically)
self.sword_zipbible_combo_box.setObjectName('SwordZipBibleComboBox')
self.sword_zip_layout.addWidget(self.sword_zipfile_label, 0, 0)
self.sword_zip_layout.addWidget(self.sword_zipfile_edit, 0, 1)
self.sword_zip_layout.addWidget(self.sword_zipbrowse_button, 0, 2)
self.sword_zip_layout.addWidget(self.sword_zipbible_label, 1, 0)
self.sword_zip_layout.addWidget(self.sword_zipbible_combo_box, 1, 1)
self.sword_tab_widget.addTab(self.sword_zip_tab, '')
self.sword_layout.addWidget(self.sword_tab_widget)
self.sword_disabled_label = QtWidgets.QLabel(self.sword_widget)
self.sword_disabled_label.setObjectName('SwordDisabledLabel')
self.sword_layout.addWidget(self.sword_disabled_label)
self.select_stack.addWidget(self.sword_widget)
self.select_page_layout.addLayout(self.select_stack)
self.addPage(self.select_page)
# License Page
@ -323,6 +401,7 @@ class BibleImportForm(OpenLPWizard):
self.format_combo_box.setItemText(BibleFormat.WebDownload, translate('BiblesPlugin.ImportWizardForm',
'Web Download'))
self.format_combo_box.setItemText(BibleFormat.Zefania, WizardStrings.ZEF)
self.format_combo_box.setItemText(BibleFormat.SWORD, WizardStrings.SWORD)
self.osis_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
self.csv_books_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Books file:'))
self.csv_verses_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Verses file:'))
@ -346,6 +425,22 @@ class BibleImportForm(OpenLPWizard):
self.web_tab_widget.setTabText(
self.web_tab_widget.indexOf(self.web_proxy_tab), translate('BiblesPlugin.ImportWizardForm',
'Proxy Server (Optional)'))
self.sword_bible_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bibles:'))
self.sword_folder_label.setText(translate('BiblesPlugin.ImportWizardForm', 'SWORD data folder:'))
self.sword_zipfile_label.setText(translate('BiblesPlugin.ImportWizardForm', 'SWORD zip-file:'))
self.sword_folder_edit.setPlaceholderText(translate('BiblesPlugin.ImportWizardForm',
'Defaults to the standard SWORD data folder'))
self.sword_zipbible_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bibles:'))
self.sword_tab_widget.setTabText(self.sword_tab_widget.indexOf(self.sword_folder_tab),
translate('BiblesPlugin.ImportWizardForm', 'Import from folder'))
self.sword_tab_widget.setTabText(self.sword_tab_widget.indexOf(self.sword_zip_tab),
translate('BiblesPlugin.ImportWizardForm', 'Import from Zip-file'))
if PYSWORD_AVAILABLE:
self.sword_disabled_label.setText('')
else:
self.sword_disabled_label.setText(translate('BiblesPlugin.ImportWizardForm',
'To import SWORD bibles the pysword python module must be '
'installed. Please read the manual for instructions.'))
self.license_details_page.setTitle(
translate('BiblesPlugin.ImportWizardForm', 'License Details'))
self.license_details_page.setSubTitle(translate('BiblesPlugin.ImportWizardForm',
@ -374,6 +469,9 @@ class BibleImportForm(OpenLPWizard):
if self.currentPage() == self.welcome_page:
return True
elif self.currentPage() == self.select_page:
self.version_name_edit.clear()
self.permissions_edit.clear()
self.copyright_edit.clear()
if self.field('source_format') == BibleFormat.OSIS:
if not self.field('osis_location'):
critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.OSIS)
@ -410,6 +508,31 @@ class BibleImportForm(OpenLPWizard):
return False
else:
self.version_name_edit.setText(self.web_translation_combo_box.currentText())
elif self.field('source_format') == BibleFormat.SWORD:
# Test the SWORD tab that is currently active
if self.sword_tab_widget.currentIndex() == self.sword_tab_widget.indexOf(self.sword_folder_tab):
if not self.field('sword_folder_path') and self.sword_bible_combo_box.count() == 0:
critical_error_message_box(UiStrings().NFSs,
WizardStrings.YouSpecifyFolder % WizardStrings.SWORD)
self.sword_folder_edit.setFocus()
return False
key = self.sword_bible_combo_box.itemData(self.sword_bible_combo_box.currentIndex())
if 'description' in self.pysword_folder_modules_json[key]:
self.version_name_edit.setText(self.pysword_folder_modules_json[key]['description'])
if 'distributionlicense' in self.pysword_folder_modules_json[key]:
self.permissions_edit.setText(self.pysword_folder_modules_json[key]['distributionlicense'])
if 'copyright' in self.pysword_folder_modules_json[key]:
self.copyright_edit.setText(self.pysword_folder_modules_json[key]['copyright'])
elif self.sword_tab_widget.currentIndex() == self.sword_tab_widget.indexOf(self.sword_zip_tab):
if not self.field('sword_zip_path'):
critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.SWORD)
self.sword_zipfile_edit.setFocus()
return False
key = self.sword_zipbible_combo_box.itemData(self.sword_zipbible_combo_box.currentIndex())
if 'description' in self.pysword_zip_modules_json[key]:
self.version_name_edit.setText(self.pysword_zip_modules_json[key]['description'])
if 'distributionlicense' in self.pysword_zip_modules_json[key]:
self.permissions_edit.setText(self.pysword_zip_modules_json[key]['distributionlicense'])
return True
elif self.currentPage() == self.license_details_page:
license_version = self.field('license_version')
@ -531,6 +654,40 @@ class BibleImportForm(OpenLPWizard):
self.web_update_button.setEnabled(True)
self.web_progress_bar.setVisible(False)
def on_sword_browse_button_clicked(self):
"""
Show the file open dialog for the SWORD folder.
"""
self.get_folder(WizardStrings.OpenTypeFolder % WizardStrings.SWORD, self.sword_folder_edit,
'last directory import')
if self.sword_folder_edit.text():
try:
self.pysword_folder_modules = modules.SwordModules(self.sword_folder_edit.text())
self.pysword_folder_modules_json = self.pysword_folder_modules.parse_modules()
bible_keys = self.pysword_folder_modules_json.keys()
self.sword_bible_combo_box.clear()
for key in bible_keys:
self.sword_bible_combo_box.addItem(self.pysword_folder_modules_json[key]['description'], key)
except:
self.sword_bible_combo_box.clear()
def on_sword_zipbrowse_button_clicked(self):
"""
Show the file open dialog for a SWORD zip-file.
"""
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.SWORD, self.sword_zipfile_edit,
'last directory import')
if self.sword_zipfile_edit.text():
try:
self.pysword_zip_modules = modules.SwordModules(self.sword_zipfile_edit.text())
self.pysword_zip_modules_json = self.pysword_zip_modules.parse_modules()
bible_keys = self.pysword_zip_modules_json.keys()
self.sword_zipbible_combo_box.clear()
for key in bible_keys:
self.sword_zipbible_combo_box.addItem(self.pysword_zip_modules_json[key]['description'], key)
except:
self.sword_zipbible_combo_box.clear()
def register_fields(self):
"""
Register the bible import wizard fields.
@ -543,6 +700,8 @@ class BibleImportForm(OpenLPWizard):
self.select_page.registerField('zefania_file', self.zefania_file_edit)
self.select_page.registerField('web_location', self.web_source_combo_box)
self.select_page.registerField('web_biblename', self.web_translation_combo_box)
self.select_page.registerField('sword_folder_path', self.sword_folder_edit)
self.select_page.registerField('sword_zip_path', self.sword_zipfile_edit)
self.select_page.registerField('proxy_server', self.web_server_edit)
self.select_page.registerField('proxy_username', self.web_user_edit)
self.select_page.registerField('proxy_password', self.web_password_edit)
@ -565,6 +724,8 @@ class BibleImportForm(OpenLPWizard):
self.setField('csv_versefile', '')
self.setField('opensong_file', '')
self.setField('zefania_file', '')
self.setField('sword_folder_path', '')
self.setField('sword_zip_path', '')
self.setField('web_location', WebDownload.Crosswalk)
self.setField('web_biblename', self.web_translation_combo_box.currentIndex())
self.setField('proxy_server', settings.value('proxy address'))
@ -626,9 +787,21 @@ class BibleImportForm(OpenLPWizard):
language_id=language_id
)
elif bible_type == BibleFormat.Zefania:
# Import an Zefania bible.
# Import a Zefania bible.
importer = self.manager.import_bible(BibleFormat.Zefania, name=license_version,
filename=self.field('zefania_file'))
elif bible_type == BibleFormat.SWORD:
# Import a SWORD bible.
if self.sword_tab_widget.currentIndex() == self.sword_tab_widget.indexOf(self.sword_folder_tab):
importer = self.manager.import_bible(BibleFormat.SWORD, name=license_version,
sword_path=self.field('sword_folder_path'),
sword_key=self.sword_bible_combo_box.itemData(
self.sword_bible_combo_box.currentIndex()))
else:
importer = self.manager.import_bible(BibleFormat.SWORD, name=license_version,
sword_path=self.field('sword_zip_path'),
sword_key=self.sword_zipbible_combo_box.itemData(
self.sword_zipbible_combo_box.currentIndex()))
if importer.do_import(license_version):
self.manager.save_meta_data(license_version, license_version, license_copyright, license_permissions)
self.manager.reload_bibles()

View File

@ -32,7 +32,7 @@ from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, AppLocation, UiStrings, Settings, check_directory_exists, translate, \
delete_file
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB, BiblesResourcesDB
from openlp.plugins.bibles.lib.http import BSExtract, BGExtract, CWExtract

View File

@ -31,7 +31,10 @@ from .http import HTTPBible
from .opensong import OpenSongBible
from .osis import OSISBible
from .zefania import ZefaniaBible
try:
from .sword import SwordBible
except:
pass
log = logging.getLogger(__name__)
@ -46,6 +49,7 @@ class BibleFormat(object):
OpenSong = 2
WebDownload = 3
Zefania = 4
SWORD = 5
@staticmethod
def get_class(bible_format):
@ -64,6 +68,8 @@ class BibleFormat(object):
return HTTPBible
elif bible_format == BibleFormat.Zefania:
return ZefaniaBible
elif bible_format == BibleFormat.SWORD:
return SwordBible
else:
return None
@ -78,6 +84,7 @@ class BibleFormat(object):
BibleFormat.OpenSong,
BibleFormat.WebDownload,
BibleFormat.Zefania,
BibleFormat.SWORD
]

View File

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
from pysword import modules
from openlp.core.common import translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
log = logging.getLogger(__name__)
class SwordBible(BibleDB):
"""
SWORD Bible format importer class.
"""
def __init__(self, parent, **kwargs):
"""
Constructor to create and set up an instance of the SwordBible class. This class is used to import Bibles
from SWORD bible modules.
"""
log.debug(self.__class__.__name__)
BibleDB.__init__(self, parent, **kwargs)
self.sword_key = kwargs['sword_key']
self.sword_path = kwargs['sword_path']
if self.sword_path == '':
self.sword_path = None
def do_import(self, bible_name=None):
"""
Loads a Bible from SWORD module.
"""
log.debug('Starting SWORD import from "%s"' % self.sword_key)
success = True
try:
pysword_modules = modules.SwordModules(self.sword_path)
pysword_module_json = pysword_modules.parse_modules()[self.sword_key]
bible = pysword_modules.get_bible_from_module(self.sword_key)
language = pysword_module_json['lang']
language = language[language.find('.') + 1:]
language_id = BiblesResourcesDB.get_language(language)['id']
self.save_meta('language_id', language_id)
books = bible.get_structure().get_books()
# Count number of books
num_books = 0
if 'ot' in books:
num_books += len(books['ot'])
if 'nt' in books:
num_books += len(books['nt'])
self.wizard.progress_bar.setMaximum(num_books)
# Import the bible
for testament in books.keys():
for book in books[testament]:
book_ref_id = self.get_book_ref_id_by_name(book.name, num_books, language_id)
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.create_book(book_details['name'], book_ref_id, book_details['testament_id'])
for chapter_number in range(1, book.num_chapters + 1):
if self.stop_import_flag:
break
verses = bible.get_iter(book.name, chapter_number)
verse_number = 0
for verse in verses:
verse_number += 1
self.create_verse(db_book.id, chapter_number, verse_number, verse)
self.wizard.increment_progress_bar(
translate('BiblesPlugin.Sword', 'Importing %s...') % db_book.name)
self.session.commit()
self.application.process_events()
except Exception as e:
critical_error_message_box(
message=translate('BiblesPlugin.SwordImport', 'An unexpected error happened while importing the SWORD '
'bible, please report this to the OpenLP developers.\n'
'%s' % e))
log.exception(str(e))
success = False
if self.stop_import_flag:
return False
else:
return success

View File

@ -23,7 +23,8 @@
from PyQt5 import QtWidgets
from openlp.core.common import Settings, UiStrings, translate
from openlp.core.lib import ColorButton, SettingsTab
from openlp.core.lib import SettingsTab
from openlp.core.ui.lib.colorbutton import ColorButton
class ImageTab(SettingsTab):

View File

@ -27,9 +27,10 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, AppLocation, Settings, UiStrings, check_directory_exists, translate, \
delete_file, get_images_filter
from openlp.core.lib import ItemCapabilities, MediaManagerItem, ServiceItemContext, StringContent, TreeWidgetWithDnD,\
build_icon, check_item_selected, create_thumb, validate_thumb
from openlp.core.lib import ItemCapabilities, MediaManagerItem, ServiceItemContext, StringContent, build_icon, \
check_item_selected, create_thumb, validate_thumb
from openlp.core.lib.ui import create_widget_action, critical_error_message_box
from openlp.core.ui.lib.treewidgetwithdnd import TreeWidgetWithDnD
from openlp.core.common.languagemanager import get_locale_key
from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm
from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups

View File

@ -24,10 +24,13 @@ The Media plugin
"""
import logging
import os
import re
from shutil import which
from PyQt5 import QtCore
from openlp.core.common import Settings, translate
from openlp.core.common import AppLocation, Settings, translate, check_binary_exists, is_win
from openlp.core.lib import Plugin, StringContent, build_icon
from openlp.plugins.media.lib import MediaMediaItem, MediaTab
@ -62,6 +65,15 @@ class MediaPlugin(Plugin):
"""
super().initialise()
def check_pre_conditions(self):
"""
Check it we have a valid environment.
:return: true or false
"""
log.debug('check_installed Mediainfo')
# Use the user defined program if given
return process_check_binary('mediainfo')
def app_startup(self):
"""
Override app_startup() in order to do nothing
@ -137,3 +149,21 @@ class MediaPlugin(Plugin):
Add html code to htmlbuilder.
"""
return self.media_controller.get_media_display_html()
def process_check_binary(program_path):
"""
Function that checks whether a binary MediaInfo is present
:param program_path:The full path to the binary to check.
:return: If exists or not
"""
program_type = None
runlog = check_binary_exists(program_path)
print(runlog, type(runlog))
# Analyse the output to see it the program is mediainfo
for line in runlog.splitlines():
decoded_line = line.decode()
if re.search('MediaInfo Command line', decoded_line, re.IGNORECASE):
return True
return False

View File

@ -22,13 +22,12 @@
import os
import logging
from tempfile import NamedTemporaryFile
import re
from shutil import which
from subprocess import check_output, CalledProcessError, STDOUT
from subprocess import check_output, CalledProcessError
from openlp.core.common import AppLocation
from openlp.core.common import Settings, is_win, trace_error_handler
from openlp.core.common import AppLocation, check_binary_exists
from openlp.core.common import Settings, is_win
from openlp.core.lib import ScreenList
from .presentationcontroller import PresentationController, PresentationDocument
@ -61,7 +60,7 @@ class PdfController(PresentationController):
self.check_installed()
@staticmethod
def check_binary(program_path):
def process_check_binary(program_path):
"""
Function that checks whether a binary is either ghostscript or mudraw or neither.
Is also used from presentationtab.py
@ -70,22 +69,7 @@ class PdfController(PresentationController):
:return: Type of the binary, 'gs' if ghostscript, 'mudraw' if mudraw, None if invalid.
"""
program_type = None
runlog = ''
log.debug('testing program_path: %s', program_path)
try:
# Setup startupinfo options for check_output to avoid console popping up on windows
if is_win():
startupinfo = STARTUPINFO()
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
else:
startupinfo = None
runlog = check_output([program_path, '--help'], stderr=STDOUT, startupinfo=startupinfo)
except CalledProcessError as e:
runlog = e.output
except Exception:
trace_error_handler(log)
runlog = ''
log.debug('check_output returned: %s' % runlog)
runlog = check_binary_exists(program_path)
# Analyse the output to see it the program is mudraw, ghostscript or neither
for line in runlog.splitlines():
decoded_line = line.decode()
@ -122,7 +106,7 @@ class PdfController(PresentationController):
# Use the user defined program if given
if Settings().value('presentations/enable_pdf_program'):
pdf_program = Settings().value('presentations/pdf_program')
program_type = self.check_binary(pdf_program)
program_type = self.process_check_binary(pdf_program)
if program_type == 'gs':
self.gsbin = pdf_program
elif program_type == 'mudraw':

View File

@ -144,18 +144,33 @@ class RemoteTab(SettingsTab):
self.android_app_group_box = QtWidgets.QGroupBox(self.right_column)
self.android_app_group_box.setObjectName('android_app_group_box')
self.right_layout.addWidget(self.android_app_group_box)
self.qr_layout = QtWidgets.QVBoxLayout(self.android_app_group_box)
self.qr_layout.setObjectName('qr_layout')
self.qr_code_label = QtWidgets.QLabel(self.android_app_group_box)
self.qr_code_label.setPixmap(QtGui.QPixmap(':/remotes/android_app_qr.png'))
self.qr_code_label.setAlignment(QtCore.Qt.AlignCenter)
self.qr_code_label.setObjectName('qr_code_label')
self.qr_layout.addWidget(self.qr_code_label)
self.qr_description_label = QtWidgets.QLabel(self.android_app_group_box)
self.qr_description_label.setObjectName('qr_description_label')
self.qr_description_label.setOpenExternalLinks(True)
self.qr_description_label.setWordWrap(True)
self.qr_layout.addWidget(self.qr_description_label)
self.android_qr_layout = QtWidgets.QVBoxLayout(self.android_app_group_box)
self.android_qr_layout.setObjectName('android_qr_layout')
self.android_qr_code_label = QtWidgets.QLabel(self.android_app_group_box)
self.android_qr_code_label.setPixmap(QtGui.QPixmap(':/remotes/android_app_qr.png'))
self.android_qr_code_label.setAlignment(QtCore.Qt.AlignCenter)
self.android_qr_code_label.setObjectName('android_qr_code_label')
self.android_qr_layout.addWidget(self.android_qr_code_label)
self.android_qr_description_label = QtWidgets.QLabel(self.android_app_group_box)
self.android_qr_description_label.setObjectName('android_qr_description_label')
self.android_qr_description_label.setOpenExternalLinks(True)
self.android_qr_description_label.setWordWrap(True)
self.android_qr_layout.addWidget(self.android_qr_description_label)
self.ios_app_group_box = QtWidgets.QGroupBox(self.right_column)
self.ios_app_group_box.setObjectName('ios_app_group_box')
self.right_layout.addWidget(self.ios_app_group_box)
self.ios_qr_layout = QtWidgets.QVBoxLayout(self.ios_app_group_box)
self.ios_qr_layout.setObjectName('ios_qr_layout')
self.ios_qr_code_label = QtWidgets.QLabel(self.ios_app_group_box)
self.ios_qr_code_label.setPixmap(QtGui.QPixmap(':/remotes/ios_app_qr.png'))
self.ios_qr_code_label.setAlignment(QtCore.Qt.AlignCenter)
self.ios_qr_code_label.setObjectName('ios_qr_code_label')
self.ios_qr_layout.addWidget(self.ios_qr_code_label)
self.ios_qr_description_label = QtWidgets.QLabel(self.ios_app_group_box)
self.ios_qr_description_label.setObjectName('ios_qr_description_label')
self.ios_qr_description_label.setOpenExternalLinks(True)
self.ios_qr_description_label.setWordWrap(True)
self.ios_qr_layout.addWidget(self.ios_qr_description_label)
self.left_layout.addStretch()
self.right_layout.addStretch()
self.twelve_hour_check_box.stateChanged.connect(self.on_twelve_hour_check_box_changed)
@ -176,10 +191,15 @@ class RemoteTab(SettingsTab):
self.thumbnails_check_box.setText(translate('RemotePlugin.RemoteTab',
'Show thumbnails of non-text slides in remote and stage view.'))
self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App'))
self.qr_description_label.setText(
self.android_qr_description_label.setText(
translate('RemotePlugin.RemoteTab', 'Scan the QR code or click <a href="%s">download</a> to install the '
'Android app from Google Play.') %
'https://play.google.com/store/apps/details?id=org.openlp.android2')
self.ios_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'iOS App'))
self.ios_qr_description_label.setText(
translate('RemotePlugin.RemoteTab', 'Scan the QR code or click <a href="%s">download</a> to install the '
'iOS app from the App Store.') %
'https://itunes.apple.com/app/id1096218725')
self.https_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'HTTPS Server'))
self.https_error_label.setText(
translate('RemotePlugin.RemoteTab', 'Could not find an SSL certificate. The HTTPS server will not be '

View File

@ -30,7 +30,7 @@ import os
from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, RegistryProperties, translate
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
from openlp.plugins.songs.lib import delete_song
from openlp.plugins.songs.lib.db import Song, MediaFile
from openlp.plugins.songs.forms.songreviewwidget import SongReviewWidget

View File

@ -34,6 +34,7 @@ from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStrings, check_directory_exists, translate
from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
from openlp.core.common.languagemanager import get_natural_key
from openlp.plugins.songs.lib import VerseType, clean_song
from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile, SongBookEntry
from openlp.plugins.songs.lib.ui import SongStrings
@ -110,7 +111,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
"""
Generically load a set of objects into a cache and a combobox.
"""
objects = self.manager.get_all_objects(cls, order_by_ref=cls.name)
def get_key(obj):
"""Get the key to sort by"""
return get_natural_key(obj.name)
objects = self.manager.get_all_objects(cls)
objects.sort(key=get_key)
combo.clear()
combo.addItem('')
for obj in objects:
@ -343,7 +349,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
"""
Load the authors from the database into the combobox.
"""
authors = self.manager.get_all_objects(Author, order_by_ref=Author.display_name)
def get_author_key(author):
"""Get the key to sort by"""
return get_natural_key(author.display_name)
authors = self.manager.get_all_objects(Author)
authors.sort(key=get_author_key)
self.authors_combo_box.clear()
self.authors_combo_box.addItem('')
self.authors = []
@ -378,9 +389,14 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
"""
Load the themes into a combobox.
"""
def get_theme_key(theme):
"""Get the key to sort by"""
return get_natural_key(theme)
self.theme_combo_box.clear()
self.theme_combo_box.addItem('')
self.themes = theme_list
self.themes.sort(key=get_theme_key)
self.theme_combo_box.addItems(theme_list)
set_case_insensitive_completer(self.themes, self.theme_combo_box)

View File

@ -30,7 +30,7 @@ from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, UiStrings, translate
from openlp.core.lib import create_separated_list, build_icon
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
from openlp.plugins.songs.lib.db import Song
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
@ -203,6 +203,10 @@ class SongExportForm(OpenLPWizard):
"""
Set default form values for the song export wizard.
"""
def get_song_key(song):
"""Get the key to sort by"""
return song.sort_key
self.restart()
self.finish_button.setVisible(False)
self.cancel_button.setVisible(True)
@ -213,7 +217,7 @@ class SongExportForm(OpenLPWizard):
# Load the list of songs.
self.application.set_busy_cursor()
songs = self.plugin.manager.get_all_objects(Song)
songs.sort(key=lambda song: song.sort_key)
songs.sort(key=get_song_key)
for song in songs:
# No need to export temporary songs.
if song.temporary:

View File

@ -31,7 +31,7 @@ from PyQt5 import QtCore, QtWidgets
from openlp.core.common import RegistryProperties, Settings, UiStrings, translate
from openlp.core.lib import FileDialog
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect
log = logging.getLogger(__name__)

View File

@ -27,6 +27,7 @@ from sqlalchemy.sql import and_
from openlp.core.common import Registry, RegistryProperties, UiStrings, translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.common.languagemanager import get_natural_key
from openlp.plugins.songs.forms.authorsform import AuthorsForm
from openlp.plugins.songs.forms.topicsform import TopicsForm
from openlp.plugins.songs.forms.songbookform import SongBookForm
@ -120,8 +121,13 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP
"""
Reloads the Authors list.
"""
def get_author_key(author):
"""Get the key to sort by"""
return get_natural_key(author.display_name)
self.authors_list_widget.clear()
authors = self.manager.get_all_objects(Author, order_by_ref=Author.display_name)
authors = self.manager.get_all_objects(Author)
authors.sort(key=get_author_key)
for author in authors:
if author.display_name:
author_name = QtWidgets.QListWidgetItem(author.display_name)
@ -134,8 +140,13 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP
"""
Reloads the Topics list.
"""
def get_topic_key(topic):
"""Get the key to sort by"""
return get_natural_key(topic.name)
self.topics_list_widget.clear()
topics = self.manager.get_all_objects(Topic, order_by_ref=Topic.name)
topics = self.manager.get_all_objects(Topic)
topics.sort(key=get_topic_key)
for topic in topics:
topic_name = QtWidgets.QListWidgetItem(topic.name)
topic_name.setData(QtCore.Qt.UserRole, topic.id)
@ -145,8 +156,13 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP
"""
Reloads the Books list.
"""
def get_book_key(book):
"""Get the key to sort by"""
return get_natural_key(book.name)
self.song_books_list_widget.clear()
books = self.manager.get_all_objects(Book, order_by_ref=Book.name)
books = self.manager.get_all_objects(Book)
books.sort(key=get_book_key)
for book in books:
book_name = QtWidgets.QListWidgetItem('%s (%s)' % (book.name, book.publisher))
book_name.setData(QtCore.Qt.UserRole, book.id)

View File

@ -299,6 +299,7 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog):
# Set up UI components
self.view_button.setEnabled(False)
self.search_button.setEnabled(False)
self.search_combobox.setEnabled(False)
self.search_progress_bar.setMinimum(0)
self.search_progress_bar.setMaximum(0)
self.search_progress_bar.setValue(0)
@ -354,6 +355,7 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog):
self.application.process_events()
self.set_progress_visible(False)
self.search_button.setEnabled(True)
self.search_combobox.setEnabled(True)
self.application.process_events()
def on_search_results_widget_selection_changed(self):

View File

@ -383,7 +383,7 @@ def init_schema(url):
# Use lazy='joined' to always load authors when the song is fetched from the database (bug 1366198)
'authors': relation(Author, secondary=authors_songs_table, viewonly=True, lazy='joined'),
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
'songbook_entries': relation(SongBookEntry, backref='song', cascade="all, delete-orphan"),
'songbook_entries': relation(SongBookEntry, backref='song', cascade='all, delete-orphan'),
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)
})
mapper(Topic, topics_table)

View File

@ -26,7 +26,7 @@ import os
import logging
from openlp.core.common import translate, UiStrings, is_win
from openlp.core.ui.wizard import WizardStrings
from openlp.core.ui.lib.wizard import WizardStrings
from .importers.opensong import OpenSongImport
from .importers.easyslides import EasySlidesImport
from .importers.openlp import OpenLPSongImport

View File

@ -90,7 +90,7 @@ import os
from lxml import etree, objectify
from openlp.core.lib import translate
from openlp.core.ui.wizard import WizardStrings
from openlp.core.ui.lib.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic

View File

@ -31,7 +31,7 @@ from sqlalchemy.orm.exc import UnmappedClassError
from openlp.core.common import translate
from openlp.core.lib.db import BaseModel
from openlp.core.ui.wizard import WizardStrings
from openlp.core.ui.lib.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic, MediaFile
from .songimport import SongImport
@ -51,7 +51,7 @@ class OpenLPSongImport(SongImport):
:param manager: The song manager for the running OpenLP installation.
:param kwargs: The database providing the data to import.
"""
SongImport.__init__(self, manager, **kwargs)
super(OpenLPSongImport, self).__init__(manager, **kwargs)
self.source_session = None
def do_import(self, progress_dialog=None):
@ -63,49 +63,61 @@ class OpenLPSongImport(SongImport):
class OldAuthor(BaseModel):
"""
Author model
Maps to the authors table
"""
pass
class OldBook(BaseModel):
"""
Book model
Maps to the songbooks table
"""
pass
class OldMediaFile(BaseModel):
"""
MediaFile model
Maps to the media_files table
"""
pass
class OldSong(BaseModel):
"""
Song model
Maps to the songs table
"""
pass
class OldTopic(BaseModel):
"""
Topic model
Maps to the topics table
"""
pass
class OldSongBookEntry(BaseModel):
"""
Maps to the songs_songbooks table
"""
pass
# Check the file type
if not self.import_source.endswith('.sqlite'):
if not isinstance(self.import_source, str) or not self.import_source.endswith('.sqlite'):
self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
'Not a valid OpenLP 2 song database.'))
return
self.import_source = 'sqlite:///%s' % self.import_source
# Load the db file
# Load the db file and reflect it
engine = create_engine(self.import_source)
source_meta = MetaData()
source_meta.reflect(engine)
self.source_session = scoped_session(sessionmaker(bind=engine))
# Run some checks to see which version of the database we have
if 'media_files' in list(source_meta.tables.keys()):
has_media_files = True
else:
has_media_files = False
if 'songs_songbooks' in list(source_meta.tables.keys()):
has_songs_books = True
else:
has_songs_books = False
# Load up the tabls and map them out
source_authors_table = source_meta.tables['authors']
source_song_books_table = source_meta.tables['song_books']
source_songs_table = source_meta.tables['songs']
@ -113,6 +125,7 @@ class OpenLPSongImport(SongImport):
source_authors_songs_table = source_meta.tables['authors_songs']
source_songs_topics_table = source_meta.tables['songs_topics']
source_media_files_songs_table = None
# Set up media_files relations
if has_media_files:
source_media_files_table = source_meta.tables['media_files']
source_media_files_songs_table = source_meta.tables.get('media_files_songs')
@ -120,9 +133,15 @@ class OpenLPSongImport(SongImport):
class_mapper(OldMediaFile)
except UnmappedClassError:
mapper(OldMediaFile, source_media_files_table)
if has_songs_books:
source_songs_songbooks_table = source_meta.tables['songs_songbooks']
try:
class_mapper(OldSongBookEntry)
except UnmappedClassError:
mapper(OldSongBookEntry, source_songs_songbooks_table, properties={'songbook': relation(OldBook)})
# Set up the songs relationships
song_props = {
'authors': relation(OldAuthor, backref='songs', secondary=source_authors_songs_table),
'book': relation(OldBook, backref='songs'),
'topics': relation(OldTopic, backref='songs', secondary=source_songs_topics_table)
}
if has_media_files:
@ -134,6 +153,11 @@ class OpenLPSongImport(SongImport):
relation(OldMediaFile, backref='songs',
foreign_keys=[source_media_files_table.c.song_id],
primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id)
if has_songs_books:
song_props['songbook_entries'] = relation(OldSongBookEntry, backref='song', cascade='all, delete-orphan')
else:
song_props['book'] = relation(OldBook, backref='songs')
# Map the rest of the tables
try:
class_mapper(OldAuthor)
except UnmappedClassError:
@ -163,44 +187,54 @@ class OpenLPSongImport(SongImport):
old_titles = song.search_title.split('@')
if len(old_titles) > 1:
new_song.alternate_title = old_titles[1]
# Values will be set when cleaning the song.
# Transfer the values to the new song object
new_song.search_title = ''
new_song.search_lyrics = ''
new_song.song_number = song.song_number
new_song.lyrics = song.lyrics
new_song.verse_order = song.verse_order
new_song.copyright = song.copyright
new_song.comments = song.comments
new_song.theme_name = song.theme_name
new_song.ccli_number = song.ccli_number
if hasattr(song, 'song_number') and song.song_number:
new_song.song_number = song.song_number
# Find or create all the authors and add them to the new song object
for author in song.authors:
existing_author = self.manager.get_object_filtered(Author, Author.display_name == author.display_name)
if existing_author is None:
if not existing_author:
existing_author = Author.populate(
first_name=author.first_name,
last_name=author.last_name,
display_name=author.display_name)
new_song.add_author(existing_author)
if song.book:
existing_song_book = self.manager.get_object_filtered(Book, Book.name == song.book.name)
if existing_song_book is None:
existing_song_book = Book.populate(name=song.book.name, publisher=song.book.publisher)
new_song.book = existing_song_book
# Find or create all the topics and add them to the new song object
if song.topics:
for topic in song.topics:
existing_topic = self.manager.get_object_filtered(Topic, Topic.name == topic.name)
if existing_topic is None:
if not existing_topic:
existing_topic = Topic.populate(name=topic.name)
new_song.topics.append(existing_topic)
if has_media_files:
if song.media_files:
for media_file in song.media_files:
existing_media_file = self.manager.get_object_filtered(
MediaFile, MediaFile.file_name == media_file.file_name)
if existing_media_file:
new_song.media_files.append(existing_media_file)
else:
new_song.media_files.append(MediaFile.populate(file_name=media_file.file_name))
# Find or create all the songbooks and add them to the new song object
if has_songs_books and song.songbook_entries:
for entry in song.songbook_entries:
existing_book = self.manager.get_object_filtered(Book, Book.name == entry.songbook.name)
if not existing_book:
existing_book = Book.populate(name=entry.songbook.name, publisher=entry.songbook.publisher)
new_song.add_songbook_entry(existing_book, entry.entry)
elif song.book:
existing_book = self.manager.get_object_filtered(Book, Book.name == song.book.name)
if not existing_book:
existing_book = Book.populate(name=song.book.name, publisher=song.book.publisher)
new_song.add_songbook_entry(existing_book, '')
# Find or create all the media files and add them to the new song object
if has_media_files and song.media_files:
for media_file in song.media_files:
existing_media_file = self.manager.get_object_filtered(
MediaFile, MediaFile.file_name == media_file.file_name)
if existing_media_file:
new_song.media_files.append(existing_media_file)
else:
new_song.media_files.append(MediaFile.populate(file_name=media_file.file_name))
clean_song(self.manager, new_song)
self.manager.save_object(new_song)
if progress_dialog:

View File

@ -29,7 +29,7 @@ import os
from lxml import etree
from openlp.core.ui.wizard import WizardStrings
from openlp.core.ui.lib.wizard import WizardStrings
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, OpenLyricsError

View File

@ -27,7 +27,7 @@ Powerpraise song files into the current database.
import os
from lxml import objectify
from openlp.core.ui.wizard import WizardStrings
from openlp.core.ui.lib.wizard import WizardStrings
from .songimport import SongImport

View File

@ -26,10 +26,11 @@ Presentationmanager song files into the current database.
import os
import re
import chardet
from lxml import objectify, etree
from openlp.core.ui.wizard import WizardStrings
from openlp.core.ui.lib.wizard import WizardStrings
from .songimport import SongImport

View File

@ -29,7 +29,7 @@ import base64
import logging
from lxml import objectify
from openlp.core.ui.wizard import WizardStrings
from openlp.core.ui.lib.wizard import WizardStrings
from openlp.plugins.songs.lib import strip_rtf
from .songimport import SongImport

View File

@ -28,7 +28,7 @@ import os
from PyQt5 import QtCore
from openlp.core.common import Registry, AppLocation, check_directory_exists, translate
from openlp.core.ui.wizard import WizardStrings
from openlp.core.ui.lib.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings

View File

@ -29,7 +29,7 @@ import logging
import re
import struct
from openlp.core.ui.wizard import WizardStrings
from openlp.core.ui.lib.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
from openlp.plugins.songs.lib.importers.songimport import SongImport

View File

@ -21,7 +21,6 @@
###############################################################################
import logging
import re
import os
import shutil
@ -194,28 +193,30 @@ class SongMediaItem(MediaManagerItem):
log.debug('Authors Search')
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(
Author, Author.display_name.like(search_string), Author.display_name.asc())
Author, Author.display_name.like(search_string))
self.display_results_author(search_results)
elif search_type == SongSearch.Topics:
log.debug('Topics Search')
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(
Topic, Topic.name.like(search_string), Topic.name.asc())
Topic, Topic.name.like(search_string))
self.display_results_topic(search_results)
elif search_type == SongSearch.Books:
log.debug('Songbook Search')
search_keywords = search_keywords.rpartition(' ')
search_book = search_keywords[0] + '%'
search_entry = search_keywords[2] + '%'
search_results = (self.plugin.manager.session.query(SongBookEntry)
search_results = (self.plugin.manager.session.query(SongBookEntry.entry, Book.name, Song.title, Song.id)
.join(Song)
.join(Book)
.filter(Book.name.like(search_book), SongBookEntry.entry.like(search_entry)).all())
.filter(Book.name.like(search_book), SongBookEntry.entry.like(search_entry),
Song.temporary.is_(False)).all())
self.display_results_book(search_results)
elif search_type == SongSearch.Themes:
log.debug('Theme Search')
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(
Song, Song.theme_name.like(search_string), Song.theme_name.asc())
Song, Song.theme_name.like(search_string))
self.display_results_themes(search_results)
elif search_type == SongSearch.Copyright:
log.debug('Copyright Search')
@ -258,10 +259,14 @@ class SongMediaItem(MediaManagerItem):
:param search_results: A list of db Song objects
:return: None
"""
def get_song_key(song):
"""Get the key to sort by"""
return song.sort_key
log.debug('display results Song')
self.save_auto_select_id()
self.list_view.clear()
search_results.sort(key=lambda song: song.sort_key)
search_results.sort(key=get_song_key)
for song in search_results:
# Do not display temporary songs
if song.temporary:
@ -283,12 +288,20 @@ class SongMediaItem(MediaManagerItem):
:param search_results: A list of db Author objects
:return: None
"""
def get_author_key(author):
"""Get the key to sort by"""
return get_natural_key(author.display_name)
def get_song_key(song):
"""Get the key to sort by"""
return song.sort_key
log.debug('display results Author')
self.list_view.clear()
search_results = sorted(search_results, key=lambda author: get_natural_key(author.display_name))
search_results.sort(key=get_author_key)
for author in search_results:
songs = sorted(author.songs, key=lambda song: song.sort_key)
for song in songs:
author.songs.sort(key=get_song_key)
for song in author.songs:
# Do not display temporary songs
if song.temporary:
continue
@ -301,19 +314,20 @@ class SongMediaItem(MediaManagerItem):
"""
Display the song search results in the media manager list, grouped by book and entry
:param search_results: A list of db SongBookEntry objects
:param search_results: A tuple containing (songbook entry, book name, song title, song id)
:return: None
"""
def get_songbook_key(result):
"""Get the key to sort by"""
return (get_natural_key(result[1]), get_natural_key(result[0]), get_natural_key(result[2]))
log.debug('display results Book')
self.list_view.clear()
search_results = sorted(search_results, key=lambda songbook_entry:
(get_natural_key(songbook_entry.songbook.name), get_natural_key(songbook_entry.entry)))
for songbook_entry in search_results:
if songbook_entry.song.temporary:
continue
song_detail = '%s #%s: %s' % (songbook_entry.songbook.name, songbook_entry.entry, songbook_entry.song.title)
search_results.sort(key=get_songbook_key)
for result in search_results:
song_detail = '%s #%s: %s' % (result[1], result[0], result[2])
song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, songbook_entry.song.id)
song_name.setData(QtCore.Qt.UserRole, result[3])
self.list_view.addItem(song_name)
def display_results_topic(self, search_results):
@ -323,12 +337,20 @@ class SongMediaItem(MediaManagerItem):
:param search_results: A list of db Topic objects
:return: None
"""
def get_topic_key(topic):
"""Get the key to sort by"""
return get_natural_key(topic.name)
def get_song_key(song):
"""Get the key to sort by"""
return song.sort_key
log.debug('display results Topic')
self.list_view.clear()
search_results = sorted(search_results, key=lambda topic: get_natural_key(topic.name))
search_results.sort(key=get_topic_key)
for topic in search_results:
songs = sorted(topic.songs, key=lambda song: song.sort_key)
for song in songs:
topic.songs.sort(key=get_song_key)
for song in topic.songs:
# Do not display temporary songs
if song.temporary:
continue
@ -344,10 +366,13 @@ class SongMediaItem(MediaManagerItem):
:param search_results: A list of db Song objects
:return: None
"""
def get_theme_key(song):
"""Get the key to sort by"""
return (get_natural_key(song.theme_name), song.sort_key)
log.debug('display results Themes')
self.list_view.clear()
search_results = sorted(search_results, key=lambda song: (get_natural_key(song.theme_name),
song.sort_key))
search_results.sort(key=get_theme_key)
for song in search_results:
# Do not display temporary songs
if song.temporary:
@ -364,11 +389,14 @@ class SongMediaItem(MediaManagerItem):
:param search_results: A list of db Song objects
:return: None
"""
def get_cclinumber_key(song):
"""Get the key to sort by"""
return (get_natural_key(song.ccli_number), song.sort_key)
log.debug('display results CCLI number')
self.list_view.clear()
songs = sorted(search_results, key=lambda song: (get_natural_key(song.ccli_number),
song.sort_key))
for song in songs:
search_results.sort(key=get_cclinumber_key)
for song in search_results:
# Do not display temporary songs
if song.temporary:
continue

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

View File

@ -206,5 +206,6 @@
</qresource>
<qresource prefix="remotes">
<file>android_app_qr.png</file>
<file>ios_app_qr.png</file>
</qresource>
</RCC>

View File

@ -102,6 +102,7 @@ OPTIONAL_MODULES = [
('nose', '(testing framework)', True),
('mock', '(testing module)', sys.version_info[1] < 3),
('jenkins', '(access jenkins api - package name: jenkins-webapi)', True),
('pysword', '(import SWORD bibles)', True),
]
w = sys.stdout.write

View File

@ -23,13 +23,12 @@
Package to test the openlp.core.ui.projector.networkutils package.
"""
import os
from unittest import TestCase
from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_HASH
salt = TEST_SALT
pin = TEST_PIN
test_hash = TEST_HASH

View File

@ -197,6 +197,7 @@ FOOTER_CSS_BASE = """
"""
FOOTER_CSS = FOOTER_CSS_BASE % ('nowrap')
FOOTER_CSS_WRAP = FOOTER_CSS_BASE % ('normal')
FOOTER_CSS_INVALID = ''
class Htmbuilder(TestCase, TestMixin):
@ -359,6 +360,27 @@ class Htmbuilder(TestCase, TestMixin):
# THEN: Footer should wrap
self.assertEqual(FOOTER_CSS_WRAP, css, 'The footer strings should be equal.')
def build_footer_invalid_test(self):
"""
Test the build_footer_css() function
"""
# GIVEN: Create a theme.
css = []
item = MagicMock()
item.theme_data = None
item.footer = 'FAIL'
height = 1024
# WHEN: Settings say that footer should wrap
css.append(build_footer_css(item, height))
item.theme_data = 'TEST'
item.footer = None
css.append(build_footer_css(item, height))
# THEN: Footer should wrap
self.assertEqual(FOOTER_CSS_INVALID, css[0], 'The footer strings should be blank.')
self.assertEqual(FOOTER_CSS_INVALID, css[1], 'The footer strings should be blank.')
def webkit_version_test(self):
"""
Test the webkit_version() function

View File

@ -124,3 +124,30 @@ class TestPJLink(TestCase):
'Lamp power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[0]['Hours'], 22222,
'Lamp hours should have been set to 22222')
@patch.object(pjlink_test, 'projectorReceivedData')
def projector_process_multiple_lamp_test(self, mock_projectorReceivedData):
"""
Test setting multiple lamp on/off and hours
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: Call process_command with lamp data
pjlink.process_command('LAMP', '11111 1 22222 0 33333 1')
# THEN: Lamp should have been set with proper lamp status
self.assertEquals(len(pjlink.lamp), 3,
'Projector should have 3 lamps specified')
self.assertEquals(pjlink.lamp[0]['On'], True,
'Lamp 1 power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[0]['Hours'], 11111,
'Lamp 1 hours should have been set to 11111')
self.assertEquals(pjlink.lamp[1]['On'], False,
'Lamp 2 power status should have been set to FALSE')
self.assertEquals(pjlink.lamp[1]['Hours'], 22222,
'Lamp 2 hours should have been set to 22222')
self.assertEquals(pjlink.lamp[2]['On'], True,
'Lamp 3 power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[2]['Hours'], 33333,
'Lamp 3 hours should have been set to 33333')

View File

@ -28,7 +28,7 @@ PREREQUISITE: add_record() and get_all() functions validated.
import os
from unittest import TestCase
from openlp.core.lib.projector.db import Projector, ProjectorDB, ProjectorSource
from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource
from tests.functional import MagicMock, patch
from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
@ -82,13 +82,13 @@ class TestProjectorDB(TestCase):
"""
Test case for ProjectorDB
"""
def setUp(self):
@patch('openlp.core.lib.projector.db.init_url')
def setUp(self, mocked_init_url):
"""
Set up anything necessary for all tests
"""
with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url:
mocked_init_url.return_value = 'sqlite:///%s' % TEST_DB
self.projector = ProjectorDB()
mocked_init_url.return_value = 'sqlite:///{db}'.format(db=TEST_DB)
self.projector = ProjectorDB()
def tearDown(self):
"""
@ -192,3 +192,17 @@ class TestProjectorDB(TestCase):
# THEN: Projector should have the same source entry
item = self.projector.get_projector_by_id(item_id)
self.assertTrue(compare_source(item.source_list[0], source))
def manufacturer_repr_test(self):
"""
Test manufacturer class __repr__ text
"""
# GIVEN: Test object
manufacturer = Manufacturer()
# WHEN: Name is set
manufacturer.name = 'OpenLP Test'
# THEN: __repr__ should return a proper string
self.assertEqual(str(manufacturer), '<Manufacturer(name="OpenLP Test")>',
'Manufacturer.__repr__() should have returned a proper representation string')

View File

@ -26,6 +26,8 @@ import os
from unittest import TestCase
from PyQt5 import QtWidgets
from openlp.core.ui.mainwindow import MainWindow
from openlp.core.lib.ui import UiStrings
from openlp.core.common.registry import Registry
@ -189,3 +191,57 @@ class TestMainWindow(TestCase, TestMixin):
# THEN: The media manager dock is made visible
self.assertEqual(0, mocked_media_manager_dock.setVisible.call_count)
mocked_widget.on_focus.assert_called_with()
@patch('openlp.core.ui.mainwindow.MainWindow.plugin_manager')
@patch('openlp.core.ui.mainwindow.MainWindow.first_time')
@patch('openlp.core.ui.mainwindow.MainWindow.application')
@patch('openlp.core.ui.mainwindow.FirstTimeForm')
@patch('openlp.core.ui.mainwindow.QtWidgets.QMessageBox.warning')
@patch('openlp.core.ui.mainwindow.Settings')
def on_first_time_wizard_clicked_show_projectors_after_test(self, mocked_Settings, mocked_warning,
mocked_FirstTimeForm, mocked_application,
mocked_first_time,
mocked_plugin_manager):
# GIVEN: Main_window, patched things, patched "Yes" as confirmation to re-run wizard, settings to True.
mocked_Settings_obj = MagicMock()
mocked_Settings_obj.value.return_value = True
mocked_Settings.return_value = mocked_Settings_obj
mocked_warning.return_value = QtWidgets.QMessageBox.Yes
mocked_FirstTimeForm_obj = MagicMock()
mocked_FirstTimeForm_obj.was_cancelled = False
mocked_FirstTimeForm.return_value = mocked_FirstTimeForm_obj
mocked_plugin_manager.plugins = []
self.main_window.projector_manager_dock = MagicMock()
# WHEN: on_first_time_wizard_clicked is called
self.main_window.on_first_time_wizard_clicked()
# THEN: projector_manager_dock.setVisible should had been called once
self.main_window.projector_manager_dock.setVisible.assert_called_once_with(True)
@patch('openlp.core.ui.mainwindow.MainWindow.plugin_manager')
@patch('openlp.core.ui.mainwindow.MainWindow.first_time')
@patch('openlp.core.ui.mainwindow.MainWindow.application')
@patch('openlp.core.ui.mainwindow.FirstTimeForm')
@patch('openlp.core.ui.mainwindow.QtWidgets.QMessageBox.warning')
@patch('openlp.core.ui.mainwindow.Settings')
def on_first_time_wizard_clicked_hide_projectors_after_test(self, mocked_Settings, mocked_warning,
mocked_FirstTimeForm, mocked_application,
mocked_first_time,
mocked_plugin_manager):
# GIVEN: Main_window, patched things, patched "Yes" as confirmation to re-run wizard, settings to False.
mocked_Settings_obj = MagicMock()
mocked_Settings_obj.value.return_value = False
mocked_Settings.return_value = mocked_Settings_obj
mocked_warning.return_value = QtWidgets.QMessageBox.Yes
mocked_FirstTimeForm_obj = MagicMock()
mocked_FirstTimeForm_obj.was_cancelled = False
mocked_FirstTimeForm.return_value = mocked_FirstTimeForm_obj
mocked_plugin_manager.plugins = []
self.main_window.projector_manager_dock = MagicMock()
# WHEN: on_first_time_wizard_clicked is called
self.main_window.on_first_time_wizard_clicked()
# THEN: projector_manager_dock.setVisible should had been called once
self.main_window.projector_manager_dock.setVisible.assert_called_once_with(False)

View File

@ -24,7 +24,7 @@ This module contains tests for the openlp.core.lib.filedialog module
"""
from unittest import TestCase
from openlp.core.lib.colorbutton import ColorButton
from openlp.core.ui.lib.colorbutton import ColorButton
from tests.functional import MagicMock, call, patch
@ -33,11 +33,11 @@ class TestColorDialog(TestCase):
Test the :class:`~openlp.core.lib.colorbutton.ColorButton` class
"""
def setUp(self):
self.change_color_patcher = patch('openlp.core.lib.colorbutton.ColorButton.change_color')
self.clicked_patcher = patch('openlp.core.lib.colorbutton.ColorButton.clicked')
self.color_changed_patcher = patch('openlp.core.lib.colorbutton.ColorButton.colorChanged')
self.qt_gui_patcher = patch('openlp.core.lib.colorbutton.QtWidgets')
self.translate_patcher = patch('openlp.core.lib.colorbutton.translate', **{'return_value': 'Tool Tip Text'})
self.change_color_patcher = patch('openlp.core.ui.lib.colorbutton.ColorButton.change_color')
self.clicked_patcher = patch('openlp.core.ui.lib.colorbutton.ColorButton.clicked')
self.color_changed_patcher = patch('openlp.core.ui.lib.colorbutton.ColorButton.colorChanged')
self.qt_gui_patcher = patch('openlp.core.ui.lib.colorbutton.QtWidgets')
self.translate_patcher = patch('openlp.core.ui.lib.colorbutton.translate', **{'return_value': 'Tool Tip Text'})
self.addCleanup(self.change_color_patcher.stop)
self.addCleanup(self.clicked_patcher.stop)
self.addCleanup(self.color_changed_patcher.stop)
@ -55,7 +55,7 @@ class TestColorDialog(TestCase):
"""
# GIVEN: The ColorButton class, a mocked change_color, setToolTip methods and clicked signal
with patch('openlp.core.lib.colorbutton.ColorButton.setToolTip') as mocked_set_tool_tip:
with patch('openlp.core.ui.lib.colorbutton.ColorButton.setToolTip') as mocked_set_tool_tip:
# WHEN: The ColorButton object is instantiated
widget = ColorButton()
@ -74,7 +74,7 @@ class TestColorDialog(TestCase):
self.change_color_patcher.stop()
# GIVEN: An instance of the ColorButton object, and a mocked out setStyleSheet
with patch('openlp.core.lib.colorbutton.ColorButton.setStyleSheet') as mocked_set_style_sheet:
with patch('openlp.core.ui.lib.colorbutton.ColorButton.setStyleSheet') as mocked_set_style_sheet:
widget = ColorButton()
# WHEN: Changing the color
@ -123,7 +123,7 @@ class TestColorDialog(TestCase):
"""
# GIVEN: An instance of ColorButton, with a mocked __init__
with patch('openlp.core.lib.colorbutton.ColorButton.__init__', **{'return_value': None}):
with patch('openlp.core.ui.lib.colorbutton.ColorButton.__init__', **{'return_value': None}):
widget = ColorButton()
# WHEN: Setting the color property

View File

@ -20,12 +20,12 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Package to test the openlp.core.ui.listpreviewwidget package.
Package to test the openlp.core.ui.lib.listpreviewwidget package.
"""
from unittest import TestCase
from openlp.core.common import Settings
from openlp.core.ui.listpreviewwidget import ListPreviewWidget
from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget
from openlp.core.lib import ServiceItem
from tests.functional import MagicMock, patch, call
@ -38,13 +38,13 @@ class TestListPreviewWidget(TestCase):
Mock out stuff for all the tests
"""
# Mock self.parent().width()
self.parent_patcher = patch('openlp.core.ui.listpreviewwidget.ListPreviewWidget.parent')
self.parent_patcher = patch('openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.parent')
self.mocked_parent = self.parent_patcher.start()
self.mocked_parent.width.return_value = 100
self.addCleanup(self.parent_patcher.stop)
# Mock Settings().value()
self.Settings_patcher = patch('openlp.core.ui.listpreviewwidget.Settings')
self.Settings_patcher = patch('openlp.core.ui.lib.listpreviewwidget.Settings')
self.mocked_Settings = self.Settings_patcher.start()
self.mocked_Settings_obj = MagicMock()
self.mocked_Settings_obj.value.return_value = None
@ -52,7 +52,7 @@ class TestListPreviewWidget(TestCase):
self.addCleanup(self.Settings_patcher.stop)
# Mock self.viewport().width()
self.viewport_patcher = patch('openlp.core.ui.listpreviewwidget.ListPreviewWidget.viewport')
self.viewport_patcher = patch('openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.viewport')
self.mocked_viewport = self.viewport_patcher.start()
self.mocked_viewport_obj = MagicMock()
self.mocked_viewport_obj.width.return_value = 200
@ -72,8 +72,8 @@ class TestListPreviewWidget(TestCase):
self.assertIsNotNone(list_preview_widget, 'The ListPreviewWidget object should not be None')
self.assertEquals(list_preview_widget.screen_ratio, 1, 'Should not be called')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.setRowHeight')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
def replace_recalculate_layout_test_text(self, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." enabled, txt slides unchanged in replace_service_item & __recalc...
@ -104,8 +104,8 @@ class TestListPreviewWidget(TestCase):
self.assertEquals(mocked_resizeRowsToContents.call_count, 2, 'Should be called')
self.assertEquals(mocked_setRowHeight.call_count, 0, 'Should not be called')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.setRowHeight')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
def replace_recalculate_layout_test_img(self, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." disabled, img slides unchanged in replace_service_item & __recalc...
@ -140,8 +140,8 @@ class TestListPreviewWidget(TestCase):
calls = [call(0, 200), call(1, 200), call(0, 400), call(1, 400), call(0, 400), call(1, 400)]
mocked_setRowHeight.assert_has_calls(calls)
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.setRowHeight')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
def replace_recalculate_layout_test_img_max(self, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." enabled, img slides resized in replace_service_item & __recalc...
@ -174,9 +174,9 @@ class TestListPreviewWidget(TestCase):
calls = [call(0, 100), call(1, 100), call(0, 100), call(1, 100)]
mocked_setRowHeight.assert_has_calls(calls)
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.setRowHeight')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.cellWidget')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.cellWidget')
def row_resized_test_text(self, mocked_cellWidget, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." enabled, text-based slides not affected in row_resized.
@ -208,9 +208,9 @@ class TestListPreviewWidget(TestCase):
# THEN: self.cellWidget(row, 0).children()[1].setMaximumWidth() should not be called
self.assertEquals(mocked_cellWidget_child.setMaximumWidth.call_count, 0, 'Should not be called')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.setRowHeight')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.cellWidget')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.cellWidget')
def row_resized_test_img(self, mocked_cellWidget, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." disabled, image-based slides not affected in row_resized.
@ -244,9 +244,9 @@ class TestListPreviewWidget(TestCase):
# THEN: self.cellWidget(row, 0).children()[1].setMaximumWidth() should not be called
self.assertEquals(mocked_cellWidget_child.setMaximumWidth.call_count, 0, 'Should not be called')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.setRowHeight')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.cellWidget')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.resizeRowsToContents')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.setRowHeight')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.cellWidget')
def row_resized_test_img_max(self, mocked_cellWidget, mocked_setRowHeight, mocked_resizeRowsToContents):
"""
Test if "Max height for non-text slides..." enabled, image-based slides are scaled in row_resized.
@ -278,10 +278,10 @@ class TestListPreviewWidget(TestCase):
# THEN: self.cellWidget(row, 0).children()[1].setMaximumWidth() should be called
mocked_cellWidget_child.setMaximumWidth.assert_called_once_with(150)
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.selectRow')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.scrollToItem')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.item')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.slide_count')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.selectRow')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.scrollToItem')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.item')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.slide_count')
def autoscroll_test_setting_invalid(self, mocked_slide_count, mocked_item, mocked_scrollToItem, mocked_selectRow):
"""
Test if 'advanced/autoscrolling' setting None or invalid, that no autoscrolling occurs on change_slide().
@ -314,10 +314,10 @@ class TestListPreviewWidget(TestCase):
self.assertEquals(mocked_selectRow.call_count, 0, 'Should not be called')
self.assertEquals(mocked_item.call_count, 0, 'Should not be called')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.selectRow')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.scrollToItem')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.item')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.slide_count')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.selectRow')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.scrollToItem')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.item')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.slide_count')
def autoscroll_test_dist_bounds(self, mocked_slide_count, mocked_item, mocked_scrollToItem, mocked_selectRow):
"""
Test if 'advanced/autoscrolling' setting asks to scroll beyond list bounds, that it does not beyond.
@ -344,10 +344,10 @@ class TestListPreviewWidget(TestCase):
calls = [call(0, 0), call(0, 0)]
mocked_item.assert_has_calls(calls)
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.selectRow')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.scrollToItem')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.item')
@patch(u'openlp.core.ui.listpreviewwidget.ListPreviewWidget.slide_count')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.selectRow')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.scrollToItem')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.item')
@patch(u'openlp.core.ui.lib.listpreviewwidget.ListPreviewWidget.slide_count')
def autoscroll_test_normal(self, mocked_slide_count, mocked_item, mocked_scrollToItem, mocked_selectRow):
"""
Test if 'advanced/autoscrolling' setting valid, autoscrolling called as expected.

View File

@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This module contains tests for the SWORD Bible importer.
"""
import os
import json
from unittest import TestCase, SkipTest
from tests.functional import MagicMock, patch
try:
from openlp.plugins.bibles.lib.sword import SwordBible
except ImportError:
raise SkipTest('PySword is not installed, skipping SWORD test.')
from openlp.plugins.bibles.lib.db import BibleDB
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', '..', 'resources', 'bibles'))
class TestSwordImport(TestCase):
"""
Test the functions in the :mod:`swordimport` module.
"""
def setUp(self):
self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry')
self.registry_patcher.start()
self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
self.manager_patcher.start()
def tearDown(self):
self.registry_patcher.stop()
self.manager_patcher.stop()
def create_importer_test(self):
"""
Test creating an instance of the Sword file importer
"""
# GIVEN: A mocked out "manager"
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = SwordBible(mocked_manager, path='.', name='.', filename='', sword_key='', sword_path='')
# THEN: The importer should be an instance of BibleDB
self.assertIsInstance(importer, BibleDB)
@patch('openlp.plugins.bibles.lib.sword.SwordBible.application')
@patch('openlp.plugins.bibles.lib.sword.modules')
@patch('openlp.plugins.bibles.lib.db.BiblesResourcesDB')
def simple_import_test(self, mocked_bible_res_db, mocked_pysword_modules, mocked_application):
"""
Test that a simple SWORD import works
"""
# GIVEN: Test files with a mocked out "manager", "import_wizard", and mocked functions
# get_book_ref_id_by_name, create_verse, create_book, session and get_language.
# Also mocked pysword structures
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = SwordBible(mocked_manager, path='.', name='.', filename='', sword_key='', sword_path='')
result_file = open(os.path.join(TEST_PATH, 'dk1933.json'), 'rb')
test_data = json.loads(result_file.read().decode())
importer.wizard = mocked_import_wizard
importer.get_book_ref_id_by_name = MagicMock()
importer.create_verse = MagicMock()
importer.create_book = MagicMock()
importer.session = MagicMock()
mocked_bible_res_db.get_language.return_value = 'Danish'
mocked_bible = MagicMock()
mocked_genesis = MagicMock()
mocked_genesis.name = 'Genesis'
mocked_genesis.num_chapters = 1
books = {'ot': [mocked_genesis]}
mocked_structure = MagicMock()
mocked_structure.get_books.return_value = books
mocked_bible.get_structure.return_value = mocked_structure
mocked_bible.get_iter.return_value = [verse[1] for verse in test_data['verses']]
mocked_module = MagicMock()
mocked_module.get_bible_from_module.return_value = mocked_bible
mocked_pysword_modules.SwordModules.return_value = mocked_module
# WHEN: Importing bible file
importer.do_import()
# THEN: The create_verse() method should have been called with each verse in the file.
self.assertTrue(importer.create_verse.called)
for verse_tag, verse_text in test_data['verses']:
importer.create_verse.assert_any_call(importer.create_book().id, 1, int(verse_tag), verse_text)

View File

@ -25,7 +25,7 @@ Test the media plugin
from unittest import TestCase
from openlp.core import Registry
from openlp.plugins.media.mediaplugin import MediaPlugin
from openlp.plugins.media.mediaplugin import MediaPlugin, process_check_binary
from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin
@ -63,3 +63,29 @@ class MediaPluginTest(TestCase, TestMixin):
self.assertIsInstance(MediaPlugin.about(), str)
# THEN: about() should return a non-empty string
self.assertNotEquals(len(MediaPlugin.about()), 0)
@patch('openlp.plugins.media.mediaplugin.check_binary_exists')
def process_check_binary_pass_test(self, mocked_checked_binary_exists):
"""
Test that the Process check returns true if found
"""
# GIVEN: A media plugin instance
# WHEN: function is called with the correct name
mocked_checked_binary_exists.return_value = str.encode('MediaInfo Command line')
result = process_check_binary('MediaInfo')
# THEN: The the result should be True
self.assertTrue(result, 'Mediainfo should have been found')
@patch('openlp.plugins.media.mediaplugin.check_binary_exists')
def process_check_binary_fail_test(self, mocked_checked_binary_exists):
"""
Test that the Process check returns false if not found
"""
# GIVEN: A media plugin instance
# WHEN: function is called with the wrong name
mocked_checked_binary_exists.return_value = str.encode('MediaInfo1 Command line')
result = process_check_binary("MediaInfo1")
# THEN: The the result should be True
self.assertFalse(result, "Mediainfo should not have been found")

View File

@ -23,6 +23,7 @@
This module contains tests for the lib submodule of the Songs plugin.
"""
from unittest import TestCase
from unittest.mock import call
from PyQt5 import QtCore
@ -53,6 +54,7 @@ class TestMediaItem(TestCase, TestMixin):
self.media_item.list_view.save_auto_select_id = MagicMock()
self.media_item.list_view.clear = MagicMock()
self.media_item.list_view.addItem = MagicMock()
self.media_item.list_view.setCurrentItem = MagicMock()
self.media_item.auto_select_id = -1
self.media_item.display_songbook = False
self.media_item.display_copyright_symbol = False
@ -79,13 +81,22 @@ class TestMediaItem(TestCase, TestMixin):
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.authors = []
mock_song_temp = MagicMock()
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.authors = []
mock_author = MagicMock()
mock_author.display_name = 'My Author'
mock_song.authors.append(mock_author)
mock_song_temp.authors.append(mock_author)
mock_song.temporary = False
mock_song_temp.temporary = True
mock_search_results.append(mock_song)
mock_search_results.append(mock_song_temp)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
self.media_item.auto_select_id = 1
# WHEN: I display song search results
self.media_item.display_results_song(mock_search_results)
@ -93,9 +104,10 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
self.media_item.save_auto_select_id.assert_called_with()
MockedQListWidgetItem.assert_called_with('My Song (My Author)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
MockedQListWidgetItem.assert_called_once_with('My Song (My Author)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
self.media_item.list_view.setCurrentItem.assert_called_with(mock_qlist_widget)
def display_results_author_test(self):
"""
@ -107,13 +119,19 @@ class TestMediaItem(TestCase, TestMixin):
mock_search_results = []
mock_author = MagicMock()
mock_song = MagicMock()
mock_song_temp = MagicMock()
mock_author.display_name = 'My Author'
mock_author.songs = []
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.temporary = False
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.temporary = True
mock_author.songs.append(mock_song)
mock_author.songs.append(mock_song_temp)
mock_search_results.append(mock_author)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
@ -123,9 +141,9 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_with('My Author (My Song)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
MockedQListWidgetItem.assert_called_once_with('My Author (My Song)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
def display_results_book_test(self):
"""
@ -134,19 +152,7 @@ class TestMediaItem(TestCase, TestMixin):
# GIVEN: Search results grouped by book and entry, plus a mocked QtListWidgetItem
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
mock_search_results = []
mock_songbook_entry = MagicMock()
mock_songbook = MagicMock()
mock_song = MagicMock()
mock_songbook_entry.entry = '1'
mock_songbook.name = 'My Book'
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.temporary = False
mock_songbook_entry.song = mock_song
mock_songbook_entry.songbook = mock_songbook
mock_search_results.append(mock_songbook_entry)
mock_search_results = [('1', 'My Book', 'My Song', 1)]
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
@ -155,9 +161,35 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_with('My Book #1: My Song')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_songbook_entry.song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
MockedQListWidgetItem.assert_called_once_with('My Book #1: My Song')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, 1)
self.media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
def songbook_natural_sorting_test(self):
"""
Test that songbooks are sorted naturally
"""
# GIVEN: Search results grouped by book and entry, plus a mocked QtListWidgetItem
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem:
mock_search_results = [('2', 'Thy Book', 'Thy Song', 50),
('2', 'My Book', 'Your Song', 7),
('10', 'My Book', 'Our Song', 12),
('1', 'My Book', 'My Song', 1),
('2', 'Thy Book', 'A Song', 8)]
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
# WHEN: I display song search results grouped by book
self.media_item.display_results_book(mock_search_results)
# THEN: The songbooks are inserted in the right (natural) order,
# grouped first by book, then by number, then by song title
calls = [call('My Book #1: My Song'), call().setData(QtCore.Qt.UserRole, 1),
call('My Book #2: Your Song'), call().setData(QtCore.Qt.UserRole, 7),
call('My Book #10: Our Song'), call().setData(QtCore.Qt.UserRole, 12),
call('Thy Book #2: A Song'), call().setData(QtCore.Qt.UserRole, 8),
call('Thy Book #2: Thy Song'), call().setData(QtCore.Qt.UserRole, 50)]
MockedQListWidgetItem.assert_has_calls(calls)
def display_results_topic_test(self):
"""
@ -169,13 +201,19 @@ class TestMediaItem(TestCase, TestMixin):
mock_search_results = []
mock_topic = MagicMock()
mock_song = MagicMock()
mock_song_temp = MagicMock()
mock_topic.name = 'My Topic'
mock_topic.songs = []
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.temporary = False
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.temporary = True
mock_topic.songs.append(mock_song)
mock_topic.songs.append(mock_song_temp)
mock_search_results.append(mock_topic)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
@ -185,9 +223,9 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_with('My Topic (My Song)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
MockedQListWidgetItem.assert_called_once_with('My Topic (My Song)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
def display_results_themes_test(self):
"""
@ -198,12 +236,19 @@ class TestMediaItem(TestCase, TestMixin):
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
mock_search_results = []
mock_song = MagicMock()
mock_song_temp = MagicMock()
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.theme_name = 'My Theme'
mock_song.temporary = False
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.theme_name = 'My Theme'
mock_song_temp.temporary = True
mock_search_results.append(mock_song)
mock_search_results.append(mock_song_temp)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
@ -212,9 +257,9 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_with('My Theme (My Song)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
MockedQListWidgetItem.assert_called_once_with('My Theme (My Song)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
def display_results_cclinumber_test(self):
"""
@ -225,12 +270,19 @@ class TestMediaItem(TestCase, TestMixin):
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
mock_search_results = []
mock_song = MagicMock()
mock_song_temp = MagicMock()
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.ccli_number = '12345'
mock_song.temporary = False
mock_song_temp.id = 2
mock_song_temp.title = 'My Temporary'
mock_song_temp.sort_key = 'My Temporary'
mock_song_temp.ccli_number = '12346'
mock_song_temp.temporary = True
mock_search_results.append(mock_song)
mock_search_results.append(mock_song_temp)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
@ -239,9 +291,9 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_with('12345 (My Song)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
MockedQListWidgetItem.assert_called_once_with('12345 (My Song)')
mock_qlist_widget.setData.assert_called_once_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_once_with(mock_qlist_widget)
def build_song_footer_one_author_test(self):
"""

View File

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This module contains tests for the OpenLP song importer.
"""
from unittest import TestCase
from openlp.plugins.songs.lib.importers.openlp import OpenLPSongImport
from openlp.core.common import Registry
from tests.functional import patch, MagicMock
class TestOpenLPImport(TestCase):
"""
Test the functions in the :mod:`openlp` importer module.
"""
def setUp(self):
"""
Create the registry
"""
Registry.create()
def create_importer_test(self):
"""
Test creating an instance of the OpenLP database importer
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.importers.openlp.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = OpenLPSongImport(mocked_manager, filenames=[])
# THEN: The importer object should not be None
self.assertIsNotNone(importer, 'Import should not be none')
def invalid_import_source_test(self):
"""
Test OpenLPSongImport.do_import handles different invalid import_source values
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.importers.openlp.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = OpenLPSongImport(mocked_manager, filenames=[])
importer.import_wizard = mocked_import_wizard
importer.stop_import_flag = True
# WHEN: Import source is not a list
for source in ['not a list', 0]:
importer.import_source = source
# THEN: do_import should return none and the progress bar maximum should not be set.
self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list')
self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False,
'setMaximum on import_wizard.progress_bar should not have been called')

View File

@ -716,8 +716,43 @@ class TestSongSelectForm(TestCase, TestMixin):
# WHEN: The stop button is clicked
ssform.on_stop_button_clicked()
# THEN: The view button should be enabled
# THEN: The view button, search box and search button should be enabled
mocked_song_select_importer.stop.assert_called_with()
self.assertTrue(ssform.search_button.isEnabled())
self.assertTrue(ssform.search_combobox.isEnabled())
@patch('openlp.plugins.songs.forms.songselectform.Settings')
@patch('openlp.plugins.songs.forms.songselectform.QtCore.QThread')
@patch('openlp.plugins.songs.forms.songselectform.SearchWorker')
def on_search_button_clicked_test(self, MockedSearchWorker, MockedQtThread, MockedSettings):
"""
Test that search fields are disabled when search button is clicked.
"""
# GIVEN: A mocked SongSelect form
ssform = SongSelectForm(None, MagicMock(), MagicMock())
ssform.initialise()
# WHEN: The search button is clicked
ssform.on_search_button_clicked()
# THEN: The search box and search button should be disabled
self.assertFalse(ssform.search_button.isEnabled())
self.assertFalse(ssform.search_combobox.isEnabled())
def on_search_finished_test(self):
"""
Test that search fields are enabled when search is finished.
"""
# GIVEN: A mocked SongSelect form
ssform = SongSelectForm(None, MagicMock(), MagicMock())
ssform.initialise()
# WHEN: The search is finished
ssform.on_search_finished()
# THEN: The search box and search button should be enabled
self.assertTrue(ssform.search_button.isEnabled())
self.assertTrue(ssform.search_combobox.isEnabled())
class TestSongSelectFileImport(SongImportTestHelper):

View File

@ -20,7 +20,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Package to test the openlp.core.ui.listpreviewwidget.
Package to test the openlp.core.ui.lib.listpreviewwidget.
"""
from unittest import TestCase
@ -29,7 +29,7 @@ from PyQt5 import QtGui, QtWidgets
from openlp.core.common import Registry
from openlp.core.lib import ServiceItem
from openlp.core.ui import listpreviewwidget
from openlp.core.ui.lib import ListWidgetWithDnD, ListPreviewWidget
from tests.interfaces import MagicMock, patch
from tests.utils.osdinteraction import read_service_from_file
from tests.helpers.testmixin import TestMixin
@ -48,7 +48,7 @@ class TestListPreviewWidget(TestCase, TestMixin):
self.image_manager = MagicMock()
self.image_manager.get_image.return_value = self.image
Registry().register('image_manager', self.image_manager)
self.preview_widget = listpreviewwidget.ListPreviewWidget(self.main_window, 2)
self.preview_widget = ListPreviewWidget(self.main_window, 2)
def tearDown(self):
"""

View File

@ -27,7 +27,7 @@ from unittest import TestCase
from PyQt5 import QtWidgets
from openlp.core.common import Registry
from openlp.plugins.bibles.forms.bibleimportform import BibleImportForm, WebDownload
import openlp.plugins.bibles.forms.bibleimportform as bibleimportform
from tests.helpers.testmixin import TestMixin
from tests.functional import MagicMock, patch
@ -46,7 +46,8 @@ class TestBibleImportForm(TestCase, TestMixin):
self.setup_application()
self.main_window = QtWidgets.QMainWindow()
Registry().register('main_window', self.main_window)
self.form = BibleImportForm(self.main_window, MagicMock(), MagicMock())
bibleimportform.PYSWORD_AVAILABLE = False
self.form = bibleimportform.BibleImportForm(self.main_window, MagicMock(), MagicMock())
def tearDown(self):
"""
@ -76,3 +77,16 @@ class TestBibleImportForm(TestCase, TestMixin):
# THEN: The webbible list should still be empty
self.assertEqual(self.form.web_bible_list, {}, 'The webbible list should be empty')
def custom_init_test(self):
"""
Test that custom_init works as expected if pysword is unavailable
"""
# GIVEN: A mocked sword_tab_widget
self.form.sword_tab_widget = MagicMock()
# WHEN: Running custom_init
self.form.custom_init()
# THEN: sword_tab_widget.setDisabled(True) should have been called
self.form.sword_tab_widget.setDisabled.assert_called_with(True)