This commit is contained in:
Tomas Groth 2014-04-20 15:57:12 +02:00
commit f6231e024a
87 changed files with 1223 additions and 390 deletions

View File

@ -2,6 +2,7 @@
*.*~ *.*~
\#*\# \#*\#
*.eric4project *.eric4project
*.eric5project
*.ropeproject *.ropeproject
*.e4* *.e4*
.eric4project .eric4project

View File

@ -30,7 +30,6 @@
The :mod:`openlp` module contains all the project produced OpenLP functionality The :mod:`openlp` module contains all the project produced OpenLP functionality
""" """
import openlp.core from openlp import core, plugins
import openlp.plugins
__all__ = ['core', 'plugins'] __all__ = ['core', 'plugins']

View File

@ -38,7 +38,7 @@ import traceback
from PyQt4 import QtCore from PyQt4 import QtCore
log = logging.getLogger(__name__+'.__init__') log = logging.getLogger(__name__ + '.__init__')
FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)') FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')
@ -76,6 +76,9 @@ def check_directory_exists(directory, do_not_log=False):
def get_frozen_path(frozen_option, non_frozen_option): def get_frozen_path(frozen_option, non_frozen_option):
""" """
Return a path based on the system status. Return a path based on the system status.
:param frozen_option:
:param non_frozen_option:
""" """
if hasattr(sys, 'frozen') and sys.frozen == 1: if hasattr(sys, 'frozen') and sys.frozen == 1:
return frozen_option return frozen_option

View File

@ -39,7 +39,7 @@ from PyQt4 import QtCore, QtGui, Qt
from openlp.core.common import translate from openlp.core.common import translate
log = logging.getLogger(__name__+'.__init__') log = logging.getLogger(__name__ + '.__init__')
class ServiceItemContext(object): class ServiceItemContext(object):

View File

@ -168,29 +168,29 @@ class MediaManagerItem(QtGui.QWidget, RegistryProperties):
Create buttons for the media item toolbar Create buttons for the media item toolbar
""" """
toolbar_actions = [] toolbar_actions = []
## Import Button ## # Import Button
if self.has_import_icon: if self.has_import_icon:
toolbar_actions.append(['Import', StringContent.Import, toolbar_actions.append(['Import', StringContent.Import,
':/general/general_import.png', self.on_import_click]) ':/general/general_import.png', self.on_import_click])
## Load Button ## # Load Button
if self.has_file_icon: if self.has_file_icon:
toolbar_actions.append(['Load', StringContent.Load, ':/general/general_open.png', self.on_file_click]) toolbar_actions.append(['Load', StringContent.Load, ':/general/general_open.png', self.on_file_click])
## New Button ## # New Button
if self.has_new_icon: if self.has_new_icon:
toolbar_actions.append(['New', StringContent.New, ':/general/general_new.png', self.on_new_click]) toolbar_actions.append(['New', StringContent.New, ':/general/general_new.png', self.on_new_click])
## Edit Button ## # Edit Button
if self.has_edit_icon: if self.has_edit_icon:
toolbar_actions.append(['Edit', StringContent.Edit, ':/general/general_edit.png', self.on_edit_click]) toolbar_actions.append(['Edit', StringContent.Edit, ':/general/general_edit.png', self.on_edit_click])
## Delete Button ## # Delete Button
if self.has_delete_icon: if self.has_delete_icon:
toolbar_actions.append(['Delete', StringContent.Delete, toolbar_actions.append(['Delete', StringContent.Delete,
':/general/general_delete.png', self.on_delete_click]) ':/general/general_delete.png', self.on_delete_click])
## Preview ## # Preview
toolbar_actions.append(['Preview', StringContent.Preview, toolbar_actions.append(['Preview', StringContent.Preview,
':/general/general_preview.png', self.on_preview_click]) ':/general/general_preview.png', self.on_preview_click])
## Live Button ## # Live Button
toolbar_actions.append(['Live', StringContent.Live, ':/general/general_live.png', self.on_live_click]) toolbar_actions.append(['Live', StringContent.Live, ':/general/general_live.png', self.on_live_click])
## Add to service Button ## # Add to service Button
toolbar_actions.append(['Service', StringContent.Service, ':/general/general_add.png', self.on_add_click]) toolbar_actions.append(['Service', StringContent.Service, ':/general/general_add.png', self.on_add_click])
for action in toolbar_actions: for action in toolbar_actions:
if action[0] == StringContent.Preview: if action[0] == StringContent.Preview:

View File

@ -101,7 +101,7 @@ class Plugin(QtCore.QObject, RegistryProperties):
``add_import_menu_item(import_menu)`` ``add_import_menu_item(import_menu)``
Add an item to the Import menu. Add an item to the Import menu.
``add_export_menu_Item(export_menu)`` ``add_export_menu_item(export_menu)``
Add an item to the Export menu. Add an item to the Export menu.
``create_settings_tab()`` ``create_settings_tab()``
@ -226,7 +226,7 @@ class Plugin(QtCore.QObject, RegistryProperties):
""" """
pass pass
def add_export_menu_Item(self, export_menu): def add_export_menu_item(self, export_menu):
""" """
Create a menu item and add it to the "Export" menu. Create a menu item and add it to the "Export" menu.
@ -329,22 +329,24 @@ class Plugin(QtCore.QObject, RegistryProperties):
def set_plugin_ui_text_strings(self, tooltips): def set_plugin_ui_text_strings(self, tooltips):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin
:param tooltips:
""" """
## Load Action ## # Load Action
self.__set_name_text_string(StringContent.Load, UiStrings().Load, tooltips['load']) self.__set_name_text_string(StringContent.Load, UiStrings().Load, tooltips['load'])
## Import Action ## # Import Action
self.__set_name_text_string(StringContent.Import, UiStrings().Import, tooltips['import']) self.__set_name_text_string(StringContent.Import, UiStrings().Import, tooltips['import'])
## New Action ## # New Action
self.__set_name_text_string(StringContent.New, UiStrings().Add, tooltips['new']) self.__set_name_text_string(StringContent.New, UiStrings().Add, tooltips['new'])
## Edit Action ## # Edit Action
self.__set_name_text_string(StringContent.Edit, UiStrings().Edit, tooltips['edit']) self.__set_name_text_string(StringContent.Edit, UiStrings().Edit, tooltips['edit'])
## Delete Action ## # Delete Action
self.__set_name_text_string(StringContent.Delete, UiStrings().Delete, tooltips['delete']) self.__set_name_text_string(StringContent.Delete, UiStrings().Delete, tooltips['delete'])
## Preview Action ## # Preview Action
self.__set_name_text_string(StringContent.Preview, UiStrings().Preview, tooltips['preview']) self.__set_name_text_string(StringContent.Preview, UiStrings().Preview, tooltips['preview'])
## Send Live Action ## # Send Live Action
self.__set_name_text_string(StringContent.Live, UiStrings().Live, tooltips['live']) self.__set_name_text_string(StringContent.Live, UiStrings().Live, tooltips['live'])
## Add to Service Action ## # Add to Service Action
self.__set_name_text_string(StringContent.Service, UiStrings().Service, tooltips['service']) self.__set_name_text_string(StringContent.Service, UiStrings().Service, tooltips['service'])
def __set_name_text_string(self, name, title, tooltip): def __set_name_text_string(self, name, title, tooltip):

View File

@ -161,7 +161,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
""" """
for plugin in self.plugins: for plugin in self.plugins:
if plugin.status is not PluginStatus.Disabled: if plugin.status is not PluginStatus.Disabled:
plugin.add_export_menu_Item(self.main_window.file_export_menu) plugin.add_export_menu_item(self.main_window.file_export_menu)
def hook_tools_menu(self): def hook_tools_menu(self):
""" """

View File

@ -108,8 +108,12 @@ class ItemCapabilities(object):
``CanAutoStartForLive`` ``CanAutoStartForLive``
The capability to ignore the do not play if display blank flag. The capability to ignore the do not play if display blank flag.
``CanEditTitle``
The capability to edit the title of the item
``IsOptical`` ``IsOptical``
.Determines is the service_item is based on an optical device .Determines is the service_item is based on an optical device
""" """
CanPreview = 1 CanPreview = 1
CanEdit = 2 CanEdit = 2
@ -127,7 +131,8 @@ class ItemCapabilities(object):
CanWordSplit = 14 CanWordSplit = 14
HasBackgroundAudio = 15 HasBackgroundAudio = 15
CanAutoStartForLive = 16 CanAutoStartForLive = 16
IsOptical = 17 CanEditTitle = 17
IsOptical = 18
class ServiceItem(RegistryProperties): class ServiceItem(RegistryProperties):
@ -386,7 +391,7 @@ class ServiceItem(RegistryProperties):
self.will_auto_start = header.get('will_auto_start', False) self.will_auto_start = header.get('will_auto_start', False)
self.processor = header.get('processor', None) self.processor = header.get('processor', None)
self.has_original_files = True self.has_original_files = True
#TODO Remove me in 2,3 build phase # TODO: Remove me in 2,3 build phase
if self.is_capable(ItemCapabilities.HasDetailedTitleDisplay): if self.is_capable(ItemCapabilities.HasDetailedTitleDisplay):
self.capabilities.remove(ItemCapabilities.HasDetailedTitleDisplay) self.capabilities.remove(ItemCapabilities.HasDetailedTitleDisplay)
self.processor = self.title self.processor = self.title
@ -429,7 +434,8 @@ class ServiceItem(RegistryProperties):
""" """
Returns the title of the service item. Returns the title of the service item.
""" """
if self.is_text() or self.is_capable(ItemCapabilities.IsOptical): if self.is_text() or self.is_capable(ItemCapabilities.IsOptical) \
or self.is_capable(temCapabilities.CanEditTitle):
return self.title return self.title
else: else:
if len(self._raw_frames) > 1: if len(self._raw_frames) > 1:

View File

@ -173,7 +173,7 @@ def create_button(parent, name, **kwargs):
kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.')) kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.'))
else: else:
log.warn('The role "%s" is not defined in create_push_button().', role) log.warn('The role "%s" is not defined in create_push_button().', role)
if kwargs.pop('class', '') == 'toolbutton': if kwargs.pop('btn_class', '') == 'toolbutton':
button = QtGui.QToolButton(parent) button = QtGui.QToolButton(parent)
else: else:
button = QtGui.QPushButton(parent) button = QtGui.QPushButton(parent)

View File

@ -95,15 +95,15 @@ class Ui_ExceptionDialog(object):
Translate the widgets on the fly. Translate the widgets on the fly.
""" """
exception_dialog.setWindowTitle(translate('OpenLP.ExceptionDialog', 'Error Occurred')) exception_dialog.setWindowTitle(translate('OpenLP.ExceptionDialog', 'Error Occurred'))
self.description_explanation.setText(translate('OpenLP.ExceptionDialog', self.description_explanation.setText(
'Please enter a description of what you were doing to cause this error ' translate('OpenLP.ExceptionDialog', 'Please enter a description of what you were doing to cause this error '
'\n(Minimum 20 characters)')) '\n(Minimum 20 characters)'))
self.message_label.setText(translate('OpenLP.ExceptionDialog', 'Oops! ' self.message_label.setText(
'OpenLP hit a problem, and couldn\'t recover. The text in the box ' translate('OpenLP.ExceptionDialog', 'Oops! OpenLP hit a problem, and couldn\'t recover. The text in the '
'below contains information that might be helpful to the OpenLP ' 'box below contains information that might be helpful to the OpenLP '
'developers, so please e-mail it to bugs@openlp.org, along with a ' 'developers, so please e-mail it to bugs@openlp.org, along with a '
'detailed description of what you were doing when the problem ' 'detailed description of what you were doing when the problem '
'occurred.')) 'occurred.'))
self.send_report_button.setText(translate('OpenLP.ExceptionDialog', 'Send E-Mail')) self.send_report_button.setText(translate('OpenLP.ExceptionDialog', 'Send E-Mail'))
self.save_report_button.setText(translate('OpenLP.ExceptionDialog', 'Save to File')) self.save_report_button.setText(translate('OpenLP.ExceptionDialog', 'Save to File'))
self.attach_tile_button.setText(translate('OpenLP.ExceptionDialog', 'Attach File')) self.attach_tile_button.setText(translate('OpenLP.ExceptionDialog', 'Attach File'))

View File

@ -67,7 +67,7 @@ class ThemeScreenshotThread(QtCore.QThread):
title = config.get('theme_%s' % theme, 'title') title = config.get('theme_%s' % theme, 'title')
filename = config.get('theme_%s' % theme, 'filename') filename = config.get('theme_%s' % theme, 'filename')
screenshot = config.get('theme_%s' % theme, 'screenshot') screenshot = config.get('theme_%s' % theme, 'screenshot')
urllib.request.urlretrieve('%s%s' % (self.parent().web, screenshot), urllib.request.urlretrieve('%s%s' % (self.parent().themes_url, screenshot),
os.path.join(gettempdir(), 'openlp', screenshot)) os.path.join(gettempdir(), 'openlp', screenshot))
item = QtGui.QListWidgetItem(title, self.parent().themes_list_widget) item = QtGui.QListWidgetItem(title, self.parent().themes_list_widget)
item.setData(QtCore.Qt.UserRole, filename) item.setData(QtCore.Qt.UserRole, filename)
@ -96,6 +96,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
if self.web_access: if self.web_access:
files = self.web_access.read() files = self.web_access.read()
self.config.read_string(files.decode()) self.config.read_string(files.decode())
self.web = self.config.get('general', 'base url')
self.songs_url = self.web + self.config.get('songs', 'directory') + '/'
self.bibles_url = self.web + self.config.get('bibles', 'directory') + '/'
self.themes_url = self.web + self.config.get('themes', 'directory') + '/'
self.update_screen_list_combo() self.update_screen_list_combo()
self.was_download_cancelled = False self.was_download_cancelled = False
self.theme_screenshot_thread = None self.theme_screenshot_thread = None
@ -341,7 +345,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
item = self.songs_list_widget.item(i) item = self.songs_list_widget.item(i)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.Checked:
filename = item.data(QtCore.Qt.UserRole) filename = item.data(QtCore.Qt.UserRole)
size = self._get_file_size('%s%s' % (self.web, filename)) size = self._get_file_size('%s%s' % (self.songs_url, filename))
self.max_progress += size self.max_progress += size
# Loop through the Bibles list and increase for each selected item # Loop through the Bibles list and increase for each selected item
iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget) iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget)
@ -350,7 +354,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
item = iterator.value() item = iterator.value()
if item.parent() and item.checkState(0) == QtCore.Qt.Checked: if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
filename = item.data(0, QtCore.Qt.UserRole) filename = item.data(0, QtCore.Qt.UserRole)
size = self._get_file_size('%s%s' % (self.web, filename)) size = self._get_file_size('%s%s' % (self.bibles_url, filename))
self.max_progress += size self.max_progress += size
iterator += 1 iterator += 1
# Loop through the themes list and increase for each selected item # Loop through the themes list and increase for each selected item
@ -359,7 +363,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
item = self.themes_list_widget.item(i) item = self.themes_list_widget.item(i)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.Checked:
filename = item.data(QtCore.Qt.UserRole) filename = item.data(QtCore.Qt.UserRole)
size = self._get_file_size('%s%s' % (self.web, filename)) size = self._get_file_size('%s%s' % (self.themes_url, filename))
self.max_progress += size self.max_progress += size
if self.max_progress: if self.max_progress:
# Add on 2 for plugins status setting plus a "finished" point. # Add on 2 for plugins status setting plus a "finished" point.
@ -435,7 +439,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
self._increment_progress_bar(self.downloading % filename, 0) self._increment_progress_bar(self.downloading % filename, 0)
self.previous_size = 0 self.previous_size = 0
destination = os.path.join(songs_destination, str(filename)) destination = os.path.join(songs_destination, str(filename))
self.url_get_file('%s%s' % (self.web, filename), destination) self.url_get_file('%s%s' % (self.songs_url, filename), destination)
# Download Bibles # Download Bibles
bibles_iterator = QtGui.QTreeWidgetItemIterator( bibles_iterator = QtGui.QTreeWidgetItemIterator(
self.bibles_tree_widget) self.bibles_tree_widget)
@ -445,7 +449,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
bible = item.data(0, QtCore.Qt.UserRole) bible = item.data(0, QtCore.Qt.UserRole)
self._increment_progress_bar(self.downloading % bible, 0) self._increment_progress_bar(self.downloading % bible, 0)
self.previous_size = 0 self.previous_size = 0
self.url_get_file('%s%s' % (self.web, bible), os.path.join(bibles_destination, bible)) self.url_get_file('%s%s' % (self.bibles_url, bible), os.path.join(bibles_destination, bible))
bibles_iterator += 1 bibles_iterator += 1
# Download themes # Download themes
for i in range(self.themes_list_widget.count()): for i in range(self.themes_list_widget.count()):
@ -454,7 +458,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
theme = item.data(QtCore.Qt.UserRole) theme = item.data(QtCore.Qt.UserRole)
self._increment_progress_bar(self.downloading % theme, 0) self._increment_progress_bar(self.downloading % theme, 0)
self.previous_size = 0 self.previous_size = 0
self.url_get_file('%s%s' % (self.web, theme), os.path.join(themes_destination, theme)) self.url_get_file('%s%s' % (self.themes_url, theme), os.path.join(themes_destination, theme))
# Set Default Display # Set Default Display
if self.display_combo_box.currentIndex() != -1: if self.display_combo_box.currentIndex() != -1:
Settings().setValue('core/monitor', self.display_combo_box.currentIndex()) Settings().setValue('core/monitor', self.display_combo_box.currentIndex())

View File

@ -211,9 +211,9 @@ class Ui_FirstTimeWizard(object):
first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard')) first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard'))
self.title_label.setText('<span style="font-size:14pt; font-weight:600;">%s</span>' % self.title_label.setText('<span style="font-size:14pt; font-weight:600;">%s</span>' %
translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard')) translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard'))
self.information_label.setText(translate('OpenLP.FirstTimeWizard', self.information_label.setText(
'This wizard will help you to configure OpenLP for initial use. ' translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. '
'Click the next button below to start.')) 'Click the next button below to start.'))
self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Activate required Plugins')) 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.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select the Plugins you wish to use. '))
self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs')) self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))

View File

@ -63,7 +63,6 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont
self.services = FormattingTagController() self.services = FormattingTagController()
self.tag_table_widget.itemSelectionChanged.connect(self.on_row_selected) self.tag_table_widget.itemSelectionChanged.connect(self.on_row_selected)
self.new_button.clicked.connect(self.on_new_clicked) self.new_button.clicked.connect(self.on_new_clicked)
#self.save_button.clicked.connect(self.on_saved_clicked)
self.delete_button.clicked.connect(self.on_delete_clicked) self.delete_button.clicked.connect(self.on_delete_clicked)
self.tag_table_widget.currentCellChanged.connect(self.on_current_cell_changed) self.tag_table_widget.currentCellChanged.connect(self.on_current_cell_changed)
self.button_box.rejected.connect(self.close) self.button_box.rejected.connect(self.close)
@ -202,5 +201,4 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont
if errors: if errors:
QtGui.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), errors, QtGui.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), errors,
QtGui.QMessageBox.Ok) QtGui.QMessageBox.Ok)
#self.tag_table_widget.selectRow(pre_row - 1)
self.tag_table_widget.resizeRowsToContents() self.tag_table_widget.resizeRowsToContents()

View File

@ -168,8 +168,10 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
""" """
if enabled: if enabled:
self.setAutoFillBackground(False) self.setAutoFillBackground(False)
self.setStyleSheet("QGraphicsView {background: transparent; border: 0px;}")
else: else:
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False) self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False)
self.setStyleSheet("QGraphicsView {}")
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled) self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled)
self.repaint() self.repaint()
@ -350,7 +352,6 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
self.hide_display(self.hide_mode) self.hide_display(self.hide_mode)
# Only continue if the visibility wasn't changed during method call. # Only continue if the visibility wasn't changed during method call.
elif was_visible == self.isVisible(): elif was_visible == self.isVisible():
# Single screen active # Single screen active
if self.screens.display_count == 1: if self.screens.display_count == 1:
# Only make visible if setting enabled. # Only make visible if setting enabled.

View File

@ -56,29 +56,27 @@ from openlp.core.ui.firsttimeform import FirstTimeForm
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
MEDIA_MANAGER_STYLE = """ MEDIA_MANAGER_STYLE = """
QToolBox { QToolBox {
padding-bottom: 2px; padding-bottom: 2px;
} }
QToolBox::tab { QToolBox::tab {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 palette(button), stop: 0.5 palette(button), stop: 0 palette(button), stop: 1.0 palette(mid));
stop: 1.0 palette(mid)); border: 1px solid palette(mid);
border: 1px groove palette(mid); border-radius: 3px;
border-radius: 5px; }
} QToolBox::tab:selected {
QToolBox::tab:selected {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 palette(light), stop: 0.5 palette(midlight), stop: 0 palette(light), stop: 1.0 palette(button));
stop: 1.0 palette(dark)); border: 1px solid palette(mid);
border: 1px groove palette(dark);
font-weight: bold; font-weight: bold;
} }
""" """
PROGRESSBAR_STYLE = """ PROGRESSBAR_STYLE = """
QProgressBar{ QProgressBar{
height: 10px; height: 10px;
} }
""" """
@ -369,7 +367,7 @@ class Ui_MainWindow(object):
self.settings_menu.setTitle(translate('OpenLP.MainWindow', '&Settings')) self.settings_menu.setTitle(translate('OpenLP.MainWindow', '&Settings'))
self.settings_language_menu.setTitle(translate('OpenLP.MainWindow', '&Language')) self.settings_language_menu.setTitle(translate('OpenLP.MainWindow', '&Language'))
self.help_menu.setTitle(translate('OpenLP.MainWindow', '&Help')) self.help_menu.setTitle(translate('OpenLP.MainWindow', '&Help'))
self.media_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Media Manager')) self.media_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Library'))
self.service_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Service Manager')) self.service_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Service Manager'))
self.theme_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Theme Manager')) self.theme_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Theme Manager'))
self.file_new_item.setText(translate('OpenLP.MainWindow', '&New')) self.file_new_item.setText(translate('OpenLP.MainWindow', '&New'))
@ -396,12 +394,12 @@ class Ui_MainWindow(object):
self.settings_shortcuts_item.setText(translate('OpenLP.MainWindow', 'Configure &Shortcuts...')) self.settings_shortcuts_item.setText(translate('OpenLP.MainWindow', 'Configure &Shortcuts...'))
self.formatting_tag_item.setText(translate('OpenLP.MainWindow', 'Configure &Formatting Tags...')) self.formatting_tag_item.setText(translate('OpenLP.MainWindow', 'Configure &Formatting Tags...'))
self.settings_configure_item.setText(translate('OpenLP.MainWindow', '&Configure OpenLP...')) self.settings_configure_item.setText(translate('OpenLP.MainWindow', '&Configure OpenLP...'))
self.settings_export_item.setStatusTip(translate('OpenLP.MainWindow', self.settings_export_item.setStatusTip(
'Export OpenLP settings to a specified *.config file')) translate('OpenLP.MainWindow', 'Export OpenLP settings to a specified *.config file'))
self.settings_export_item.setText(translate('OpenLP.MainWindow', 'Settings')) self.settings_export_item.setText(translate('OpenLP.MainWindow', 'Settings'))
self.settings_import_item.setStatusTip(translate('OpenLP.MainWindow', self.settings_import_item.setStatusTip(
'Import OpenLP settings from a specified *.config file previously ' translate('OpenLP.MainWindow', 'Import OpenLP settings from a specified *.config file previously '
'exported on this or another machine')) 'exported on this or another machine'))
self.settings_import_item.setText(translate('OpenLP.MainWindow', 'Settings')) self.settings_import_item.setText(translate('OpenLP.MainWindow', 'Settings'))
self.view_media_manager_item.setText(translate('OpenLP.MainWindow', '&Media Manager')) self.view_media_manager_item.setText(translate('OpenLP.MainWindow', '&Media Manager'))
self.view_media_manager_item.setToolTip(translate('OpenLP.MainWindow', 'Toggle Media Manager')) self.view_media_manager_item.setToolTip(translate('OpenLP.MainWindow', 'Toggle Media Manager'))
@ -598,13 +596,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
self.live_controller.display.setFocus() self.live_controller.display.setFocus()
self.activateWindow() self.activateWindow()
if self.arguments: if self.arguments:
args = [] self.open_cmd_line_files()
for a in self.arguments:
args.extend([a])
filename = args[0]
if not isinstance(filename, str):
filename = str(filename, sys.getfilesystemencoding())
self.service_manager_contents.load_file(filename)
elif Settings().value(self.general_settings_section + '/auto open'): elif Settings().value(self.general_settings_section + '/auto open'):
self.service_manager_contents.load_Last_file() self.service_manager_contents.load_Last_file()
self.timer_version_id = self.startTimer(1000) self.timer_version_id = self.startTimer(1000)
@ -868,7 +860,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
section = 'general' section = 'general'
section_key = section + "/" + key section_key = section + "/" + key
# Make sure it's a valid section for us. # Make sure it's a valid section for us.
if not section in setting_sections: if section not in setting_sections:
continue continue
# We have a good file, import it. # We have a good file, import it.
for section_key in import_keys: for section_key in import_keys:
@ -1364,3 +1356,17 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
if self.new_data_path == AppLocation.get_directory(AppLocation.DataDir): if self.new_data_path == AppLocation.get_directory(AppLocation.DataDir):
settings.remove('advanced/data path') settings.remove('advanced/data path')
self.application.set_normal_cursor() self.application.set_normal_cursor()
def open_cmd_line_files(self):
"""
Open files passed in through command line arguments
"""
args = []
for a in self.arguments:
args.extend([a])
for arg in args:
filename = arg
if not isinstance(filename, str):
filename = str(filename, sys.getfilesystemencoding())
if filename.endswith(('.osz', '.oszl')):
self.service_manager_contents.load_file(filename)

View File

@ -35,7 +35,7 @@ from openlp.core.common import Settings
from PyQt4 import QtCore from PyQt4 import QtCore
log = logging.getLogger(__name__+'.__init__') log = logging.getLogger(__name__ + '.__init__')
class MediaState(object): class MediaState(object):

View File

@ -137,7 +137,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
for player in list(self.media_players.values()): for player in list(self.media_players.values()):
if player.is_active: if player.is_active:
for item in player.audio_extensions_list: for item in player.audio_extensions_list:
if not item in self.audio_extensions_list: if item not in self.audio_extensions_list:
self.audio_extensions_list.append(item) self.audio_extensions_list.append(item)
suffix_list.append(item[2:]) suffix_list.append(item[2:])
self.video_extensions_list = [] self.video_extensions_list = []
@ -184,8 +184,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
return False return False
saved_players, overridden_player = get_media_players() saved_players, overridden_player = get_media_players()
invalid_media_players = \ invalid_media_players = \
[mediaPlayer for mediaPlayer in saved_players if not mediaPlayer in self.media_players or [media_player for media_player in saved_players if media_player not in self.media_players or
not self.media_players[mediaPlayer].check_available()] not self.media_players[media_player].check_available()]
if invalid_media_players: if invalid_media_players:
for invalidPlayer in invalid_media_players: for invalidPlayer in invalid_media_players:
saved_players.remove(invalidPlayer) saved_players.remove(invalidPlayer)
@ -560,7 +560,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
else: else:
self.media_volume(controller, controller.media_info.volume) self.media_volume(controller, controller.media_info.volume)
if status: if status:
display.frame.evaluateJavaScript('show_blank("desktop");') if not controller.media_info.is_background:
display.frame.evaluateJavaScript('show_blank("desktop");')
self.current_media_players[controller.controller_type].set_visible(display, True) self.current_media_players[controller.controller_type].set_visible(display, True)
# Flash needs to be played and will not AutoPlay # Flash needs to be played and will not AutoPlay
if controller.media_info.is_flash: if controller.media_info.is_flash:
@ -571,7 +572,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.mediabar.actions['playbackPause'].setVisible(True) controller.mediabar.actions['playbackPause'].setVisible(True)
controller.mediabar.actions['playbackStop'].setVisible(True) controller.mediabar.actions['playbackStop'].setVisible(True)
if controller.is_live: if controller.is_live:
if controller.hide_menu.defaultAction().isChecked(): if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
controller.hide_menu.defaultAction().trigger() controller.hide_menu.defaultAction().trigger()
# Start Timer for ui updates # Start Timer for ui updates
if not self.timer.isActive(): if not self.timer.isActive():

View File

@ -48,13 +48,13 @@ import sys
from inspect import getargspec from inspect import getargspec
__version__ = "N/A" __version__ = "N/A"
build_date = "Mon Apr 1 23:47:38 2013" build_date = "Tue Jul 2 10:35:53 2013"
if sys.version_info[0] > 2: if sys.version_info[0] > 2:
str = str str = str
str = str unicode = str
bytes = bytes bytes = bytes
str = (str, bytes) basestring = (str, bytes)
PYTHON3 = True PYTHON3 = True
def str_to_bytes(s): def str_to_bytes(s):
"""Translate string or bytes to bytes. """Translate string or bytes to bytes.
@ -73,14 +73,14 @@ if sys.version_info[0] > 2:
return b return b
else: else:
str = str str = str
str = str unicode = unicode
bytes = str bytes = str
str = str basestring = basestring
PYTHON3 = False PYTHON3 = False
def str_to_bytes(s): def str_to_bytes(s):
"""Translate string or bytes to bytes. """Translate string or bytes to bytes.
""" """
if isinstance(s, str): if isinstance(s, unicode):
return s.encode(sys.getfilesystemencoding()) return s.encode(sys.getfilesystemencoding())
else: else:
return s return s
@ -89,7 +89,7 @@ else:
"""Translate bytes to unicode string. """Translate bytes to unicode string.
""" """
if isinstance(b, str): if isinstance(b, str):
return str(b, sys.getfilesystemencoding()) return unicode(b, sys.getfilesystemencoding())
else: else:
return b return b
@ -110,7 +110,7 @@ def find_lib():
p = find_library('libvlc.dll') p = find_library('libvlc.dll')
if p is None: if p is None:
try: # some registry settings try: # some registry settings
import winreg as w # leaner than win32api, win32con import _winreg as w # leaner than win32api, win32con
for r in w.HKEY_LOCAL_MACHINE, w.HKEY_CURRENT_USER: for r in w.HKEY_LOCAL_MACHINE, w.HKEY_CURRENT_USER:
try: try:
r = w.OpenKey(r, 'Software\\VideoLAN\\VLC') r = w.OpenKey(r, 'Software\\VideoLAN\\VLC')
@ -168,7 +168,7 @@ class VLCException(Exception):
pass pass
try: try:
_Ints = (int, int) _Ints = (int, long)
except NameError: # no long in Python 3+ except NameError: # no long in Python 3+
_Ints = int _Ints = int
_Seqs = (list, tuple) _Seqs = (list, tuple)
@ -327,6 +327,9 @@ class _Enum(ctypes.c_uint):
n = self._enum_names_.get(self.value, '') or ('FIXME_(%r)' % (self.value,)) n = self._enum_names_.get(self.value, '') or ('FIXME_(%r)' % (self.value,))
return '.'.join((self.__class__.__name__, n)) return '.'.join((self.__class__.__name__, n))
def __hash__(self):
return self.value
def __repr__(self): def __repr__(self):
return '.'.join((self.__class__.__module__, self.__str__())) return '.'.join((self.__class__.__module__, self.__str__()))
@ -1294,7 +1297,7 @@ class Instance(_Ctype):
i = args[0] i = args[0]
if isinstance(i, _Ints): if isinstance(i, _Ints):
return _Constructor(cls, i) return _Constructor(cls, i)
elif isinstance(i, str): elif isinstance(i, basestring):
args = i.strip().split() args = i.strip().split()
elif isinstance(i, _Seqs): elif isinstance(i, _Seqs):
args = i args = i
@ -2078,7 +2081,7 @@ class MediaList(_Ctype):
@param mrl: a media instance or a MRL. @param mrl: a media instance or a MRL.
@return: 0 on success, -1 if the media list is read-only. @return: 0 on success, -1 if the media list is read-only.
""" """
if isinstance(mrl, str): if isinstance(mrl, basestring):
mrl = (self.get_instance() or get_default_instance()).media_new(mrl) mrl = (self.get_instance() or get_default_instance()).media_new(mrl)
return libvlc_media_list_add_media(self, mrl) return libvlc_media_list_add_media(self, mrl)
@ -3351,6 +3354,39 @@ def libvlc_event_type_name(event_type):
ctypes.c_char_p, ctypes.c_uint) ctypes.c_char_p, ctypes.c_uint)
return f(event_type) return f(event_type)
def libvlc_log_get_context(ctx):
'''Gets debugging informations about a log message: the name of the VLC module
emitting the message and the message location within the source code.
The returned module name and file name will be NULL if unknown.
The returned line number will similarly be zero if unknown.
@param ctx: message context (as passed to the @ref libvlc_log_cb callback).
@return: module module name storage (or NULL), file source code file name storage (or NULL), line source code file line number storage (or NULL).
@version: LibVLC 2.1.0 or later.
'''
f = _Cfunctions.get('libvlc_log_get_context', None) or \
_Cfunction('libvlc_log_get_context', ((1,), (2,), (2,), (2,),), None,
None, Log_ptr, ListPOINTER(ctypes.c_char_p), ListPOINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_uint))
return f(ctx)
def libvlc_log_get_object(ctx, id):
'''Gets VLC object informations about a log message: the type name of the VLC
object emitting the message, the object header if any and a temporaly-unique
object identifier. These informations are mainly meant for B{manual}
troubleshooting.
The returned type name may be "generic" if unknown, but it cannot be NULL.
The returned header will be NULL if unset; in current versions, the header
is used to distinguish for VLM inputs.
The returned object ID will be zero if the message is not associated with
any VLC object.
@param ctx: message context (as passed to the @ref libvlc_log_cb callback).
@return: name object name storage (or NULL), header object header (or NULL), line source code file line number storage (or NULL).
@version: LibVLC 2.1.0 or later.
'''
f = _Cfunctions.get('libvlc_log_get_object', None) or \
_Cfunction('libvlc_log_get_object', ((1,), (2,), (2,), (1,),), None,
None, Log_ptr, ListPOINTER(ctypes.c_char_p), ListPOINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_uint))
return f(ctx, id)
def libvlc_log_unset(p_instance): def libvlc_log_unset(p_instance):
'''Unsets the logging callback for a LibVLC instance. This is rarely needed: '''Unsets the logging callback for a LibVLC instance. This is rarely needed:
the callback is implicitly unset when the instance is destroyed. the callback is implicitly unset when the instance is destroyed.
@ -5827,7 +5863,7 @@ def libvlc_vlm_get_event_manager(p_instance):
# libvlc_printerr # libvlc_printerr
# libvlc_set_exit_handler # libvlc_set_exit_handler
# 15 function(s) not wrapped as methods: # 17 function(s) not wrapped as methods:
# libvlc_audio_output_device_list_release # libvlc_audio_output_device_list_release
# libvlc_audio_output_list_release # libvlc_audio_output_list_release
# libvlc_clearerr # libvlc_clearerr
@ -5838,6 +5874,8 @@ def libvlc_vlm_get_event_manager(p_instance):
# libvlc_get_changeset # libvlc_get_changeset
# libvlc_get_compiler # libvlc_get_compiler
# libvlc_get_version # libvlc_get_version
# libvlc_log_get_context
# libvlc_log_get_object
# libvlc_media_tracks_release # libvlc_media_tracks_release
# libvlc_module_description_list_release # libvlc_module_description_list_release
# libvlc_new # libvlc_new
@ -5910,9 +5948,9 @@ def debug_callback(event, *args, **kwds):
''' '''
l = ['event %s' % (event.type,)] l = ['event %s' % (event.type,)]
if args: if args:
l.extend(list(map(str, args))) l.extend(map(str, args))
if kwds: if kwds:
l.extend(sorted('%s=%s' % t for t in list(kwds.items()))) l.extend(sorted('%s=%s' % t for t in kwds.items()))
print('Debug callback (%s)' % ', '.join(l)) print('Debug callback (%s)' % ', '.join(l))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -174,34 +174,11 @@ FLASH_HTML = """
<div id="flash" class="size" style="visibility:hidden"></div> <div id="flash" class="size" style="visibility:hidden"></div>
""" """
VIDEO_EXT = [ VIDEO_EXT = ['*.3gp', '*.3gpp', '*.3g2', '*.3gpp2', '*.aac', '*.flv', '*.f4a', '*.f4b', '*.f4p', '*.f4v', '*.mov',
'*.3gp', '*.m4a', '*.m4b', '*.m4p', '*.m4v', '*.mkv', '*.mp4', '*.ogv', '*.webm', '*.mpg', '*.wmv', '*.mpeg',
'*.3gpp', '*.avi', '*.swf']
'*.3g2',
'*.3gpp2',
'*.aac',
'*.flv',
'*.f4a',
'*.f4b',
'*.f4p',
'*.f4v',
'*.mov',
'*.m4a',
'*.m4b',
'*.m4p',
'*.m4v',
'*.mkv',
'*.mp4',
'*.ogv',
'*.webm',
'*.mpg', '*.wmv', '*.mpeg', '*.avi',
'*.swf'
]
AUDIO_EXT = [ AUDIO_EXT = ['*.mp3', '*.ogg']
'*.mp3',
'*.ogg'
]
class WebkitPlayer(MediaPlayer): class WebkitPlayer(MediaPlayer):
@ -411,10 +388,9 @@ class WebkitPlayer(MediaPlayer):
""" """
Return some information about this player Return some information about this player
""" """
return(translate('Media.player', 'Webkit is a media player which runs ' part1 = translate('Media.player', 'Webkit is a media player which runs inside a web browser. This player '
'inside a web browser. This player allows text over video to be ' 'allows text over video to be rendered.')
'rendered.') + part2 = translate('Media.player', 'Audio')
'<br/> <strong>' + translate('Media.player', 'Audio') + part3 = translate('Media.player', 'Video')
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' + return part1 + '<br/> <strong>' + part2 + '</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' + part3 + \
translate('Media.player', 'Video') + '</strong><br/>' + '</strong><br/>' + str(VIDEO_EXT) + '<br/>'
str(VIDEO_EXT) + '<br/>')

View File

@ -234,6 +234,9 @@ class Ui_ServiceManager(object):
self.menu = QtGui.QMenu() self.menu = QtGui.QMenu()
self.edit_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'), self.edit_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'),
icon=':/general/general_edit.png', triggers=self.remote_edit) icon=':/general/general_edit.png', triggers=self.remote_edit)
self.rename_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Rename...'),
icon=':/general/general_edit.png',
triggers=self.on_service_item_rename)
self.maintain_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'), self.maintain_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'),
icon=':/general/general_edit.png', icon=':/general/general_edit.png',
triggers=self.on_service_item_edit_form) triggers=self.on_service_item_edit_form)
@ -399,7 +402,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
:param suffix_list: New Suffix's to be supported :param suffix_list: New Suffix's to be supported
""" """
for suffix in suffix_list: for suffix in suffix_list:
if not suffix in self.suffixes: if suffix not in self.suffixes:
self.suffixes.append(suffix) self.suffixes.append(suffix)
def on_new_service_clicked(self, field=None): def on_new_service_clicked(self, field=None):
@ -629,7 +632,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
for item in self.service_items: for item in self.service_items:
self.main_window.increment_progress_bar() self.main_window.increment_progress_bar()
service_item = item['service_item'].get_service_repr(self._save_lite) service_item = item['service_item'].get_service_repr(self._save_lite)
#TODO: check for file item on save. # TODO: check for file item on save.
service.append({'serviceitem': service_item}) service.append({'serviceitem': service_item})
self.main_window.increment_progress_bar() self.main_window.increment_progress_bar()
service_content = json.dumps(service) service_content = json.dumps(service)
@ -754,8 +757,9 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
items = json.load(file_to) items = json.load(file_to)
else: else:
critical_error_message_box(message=translate('OpenLP.ServiceManager', critical_error_message_box(message=translate('OpenLP.ServiceManager',
'The service file you are trying to open is in an old format.\n ' 'The service file you are trying to open is in an old '
'Please save it using OpenLP 2.0.2 or greater.')) 'format.\n Please save it using OpenLP 2.0.2 or '
'greater.'))
return return
file_to.close() file_to.close()
self.new_file() self.new_file()
@ -848,6 +852,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
pos = item.data(0, QtCore.Qt.UserRole) pos = item.data(0, QtCore.Qt.UserRole)
service_item = self.service_items[pos - 1] service_item = self.service_items[pos - 1]
self.edit_action.setVisible(False) self.edit_action.setVisible(False)
self.rename_action.setVisible(False)
self.create_custom_action.setVisible(False) self.create_custom_action.setVisible(False)
self.maintain_action.setVisible(False) self.maintain_action.setVisible(False)
self.notes_action.setVisible(False) self.notes_action.setVisible(False)
@ -855,6 +860,8 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
self.auto_start_action.setVisible(False) self.auto_start_action.setVisible(False)
if service_item['service_item'].is_capable(ItemCapabilities.CanEdit) and service_item['service_item'].edit_id: if service_item['service_item'].is_capable(ItemCapabilities.CanEdit) and service_item['service_item'].edit_id:
self.edit_action.setVisible(True) self.edit_action.setVisible(True)
if service_item['service_item'].is_capable(ItemCapabilities.CanEditTitle):
self.rename_action.setVisible(True)
if service_item['service_item'].is_capable(ItemCapabilities.CanMaintain): if service_item['service_item'].is_capable(ItemCapabilities.CanMaintain):
self.maintain_action.setVisible(True) self.maintain_action.setVisible(True)
if item.parent() is None: if item.parent() is None:
@ -1482,6 +1489,24 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
if new_item: if new_item:
self.add_service_item(new_item, replace=True) self.add_service_item(new_item, replace=True)
def on_service_item_rename(self, field=None):
"""
Opens a dialog to rename the service item.
:param field: Not used, but PyQt needs this.
"""
item = self.find_service_item()[0]
if not self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEditTitle):
return
title = self.service_items[item]['service_item'].title
title, ok = QtGui.QInputDialog.getText(self, translate('OpenLP.ServiceManager', 'Rename item title'),
translate('OpenLP.ServiceManager', 'Title:'),
QtGui.QLineEdit.Normal, self.trUtf8(title))
if ok:
self.service_items[item]['service_item'].title = title
self.repaint_service_list(item, -1)
self.set_modified()
def create_custom(self, field=None): def create_custom(self, field=None):
""" """
Saves the current text item as a custom slide Saves the current text item as a custom slide

View File

@ -150,5 +150,5 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog, RegistryProperties):
:param function: The function to be called :param function: The function to be called
""" """
if not function in self.processes: if function not in self.processes:
self.processes.append(function) self.processes.append(function)

View File

@ -495,14 +495,14 @@ class SlideController(DisplayController, RegistryProperties):
self.on_theme_display(False) self.on_theme_display(False)
self.on_hide_display(False) self.on_hide_display(False)
def service_previous(self): def service_previous(self, field=None):
""" """
Live event to select the previous service item from the service manager. Live event to select the previous service item from the service manager.
""" """
self.keypress_queue.append(ServiceItemAction.Previous) self.keypress_queue.append(ServiceItemAction.Previous)
self._process_queue() self._process_queue()
def service_next(self): def service_next(self, field=None):
""" """
Live event to select the next service item from the service manager. Live event to select the next service item from the service manager.
""" """
@ -1039,7 +1039,6 @@ class SlideController(DisplayController, RegistryProperties):
""" """
self.preview_widget.change_slide(row) self.preview_widget.change_slide(row)
self.update_preview() self.update_preview()
Registry().execute('slidecontroller_%s_changed' % self.type_prefix, row)
def update_preview(self): def update_preview(self):
""" """

View File

@ -44,7 +44,6 @@ class Ui_ThemeLayoutDialog(object):
Set up the UI Set up the UI
""" """
themeLayoutDialog.setObjectName('themeLayoutDialogDialog') themeLayoutDialog.setObjectName('themeLayoutDialogDialog')
#themeLayoutDialog.resize(300, 200)
self.preview_layout = QtGui.QVBoxLayout(themeLayoutDialog) self.preview_layout = QtGui.QVBoxLayout(themeLayoutDialog)
self.preview_layout.setObjectName('preview_layout') self.preview_layout.setObjectName('preview_layout')
self.preview_area = QtGui.QWidget(themeLayoutDialog) self.preview_area = QtGui.QWidget(themeLayoutDialog)

View File

@ -114,17 +114,19 @@ class ThemesTab(SettingsTab):
self.global_group_box.setTitle(translate('OpenLP.ThemesTab', 'Global Theme')) self.global_group_box.setTitle(translate('OpenLP.ThemesTab', 'Global Theme'))
self.level_group_box.setTitle(translate('OpenLP.ThemesTab', 'Theme Level')) self.level_group_box.setTitle(translate('OpenLP.ThemesTab', 'Theme Level'))
self.song_level_radio_button.setText(translate('OpenLP.ThemesTab', 'S&ong Level')) self.song_level_radio_button.setText(translate('OpenLP.ThemesTab', 'S&ong Level'))
self.song_level_label.setText(translate('OpenLP.ThemesTab', 'Use the theme from each song ' self.song_level_label.setText(
'in the database. If a song doesn\'t have a theme associated with ' translate('OpenLP.ThemesTab', 'Use the theme from each song in the database. If a song doesn\'t have a '
'it, then use the service\'s theme. If the service doesn\'t have ' 'theme associated with it, then use the service\'s theme. If the service '
'a theme, then use the global theme.')) 'doesn\'t have a theme, then use the global theme.'))
self.service_level_radio_button.setText(translate('OpenLP.ThemesTab', '&Service Level')) self.service_level_radio_button.setText(translate('OpenLP.ThemesTab', '&Service Level'))
self.service_level_label.setText(translate('OpenLP.ThemesTab', 'Use the theme from the service, ' self.service_level_label.setText(
'overriding any of the individual songs\' themes. If the ' translate('OpenLP.ThemesTab', 'Use the theme from the service, overriding any of the individual '
'service doesn\'t have a theme, then use the global theme.')) 'songs\' themes. If the service doesn\'t have a theme, then use the global '
'theme.'))
self.global_level_radio_button.setText(translate('OpenLP.ThemesTab', '&Global Level')) self.global_level_radio_button.setText(translate('OpenLP.ThemesTab', '&Global Level'))
self.global_level_label.setText(translate('OpenLP.ThemesTab', 'Use the global theme, overriding ' self.global_level_label.setText(translate('OpenLP.ThemesTab', 'Use the global theme, overriding any themes '
'any themes associated with either the service or the songs.')) 'associated with either the service or the '
'songs.'))
def load(self): def load(self):
""" """

View File

@ -56,7 +56,7 @@ if sys.platform != 'win32' and sys.platform != 'darwin':
from openlp.core.common import translate from openlp.core.common import translate
log = logging.getLogger(__name__+'.__init__') log = logging.getLogger(__name__ + '.__init__')
APPLICATION_VERSION = {} APPLICATION_VERSION = {}
IMAGES_FILTER = None IMAGES_FILTER = None

View File

@ -74,7 +74,7 @@ class LanguageManager(object):
log.debug('Translation files: %s', AppLocation.get_directory( log.debug('Translation files: %s', AppLocation.get_directory(
AppLocation.LanguageDir)) AppLocation.LanguageDir))
trans_dir = QtCore.QDir(AppLocation.get_directory(AppLocation.LanguageDir)) trans_dir = QtCore.QDir(AppLocation.get_directory(AppLocation.LanguageDir))
file_names = trans_dir.entryList('*.qm', QtCore.QDir.Files, QtCore.QDir.Name) file_names = trans_dir.entryList(['*.qm'], QtCore.QDir.Files, QtCore.QDir.Name)
# Remove qm files from the list which start with "qt_". # Remove qm files from the list which start with "qt_".
file_names = [file_ for file_ in file_names if not file_.startswith('qt_')] file_names = [file_ for file_ in file_names if not file_.startswith('qt_')]
return list(map(trans_dir.filePath, file_names)) return list(map(trans_dir.filePath, file_names))

View File

@ -207,12 +207,12 @@ class AlertsPlugin(Plugin):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin
""" """
## Name PluginList ## # Name PluginList
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
'singular': translate('AlertsPlugin', 'Alert', 'name singular'), 'singular': translate('AlertsPlugin', 'Alert', 'name singular'),
'plural': translate('AlertsPlugin', 'Alerts', 'name plural') 'plural': translate('AlertsPlugin', 'Alerts', 'name plural')
} }
## Name for MediaDockManager, SettingsManager ## # Name for MediaDockManager, SettingsManager
self.text_strings[StringContent.VisibleName] = { self.text_strings[StringContent.VisibleName] = {
'title': translate('AlertsPlugin', 'Alerts', 'container title') 'title': translate('AlertsPlugin', 'Alerts', 'container title')
} }

View File

@ -88,8 +88,6 @@ class BiblePlugin(Plugin):
self.import_bible_item.setVisible(True) self.import_bible_item.setVisible(True)
action_list = ActionList.get_instance() action_list = ActionList.get_instance()
action_list.add_action(self.import_bible_item, UiStrings().Import) action_list.add_action(self.import_bible_item, UiStrings().Import)
# Do not add the action to the list yet.
#action_list.add_action(self.export_bible_item, UiStrings().Export)
# Set to invisible until we can export bibles # Set to invisible until we can export bibles
self.export_bible_item.setVisible(False) self.export_bible_item.setVisible(False)
self.tools_upgrade_item.setVisible(bool(self.manager.old_bible_databases)) self.tools_upgrade_item.setVisible(bool(self.manager.old_bible_databases))
@ -104,7 +102,6 @@ class BiblePlugin(Plugin):
action_list = ActionList.get_instance() action_list = ActionList.get_instance()
action_list.remove_action(self.import_bible_item, UiStrings().Import) action_list.remove_action(self.import_bible_item, UiStrings().Import)
self.import_bible_item.setVisible(False) self.import_bible_item.setVisible(False)
#action_list.remove_action(self.export_bible_item, UiStrings().Export)
self.export_bible_item.setVisible(False) self.export_bible_item.setVisible(False)
def app_startup(self): def app_startup(self):
@ -115,19 +112,27 @@ class BiblePlugin(Plugin):
if self.manager.old_bible_databases: if self.manager.old_bible_databases:
if QtGui.QMessageBox.information( if QtGui.QMessageBox.information(
self.main_window, translate('OpenLP', 'Information'), self.main_window, translate('OpenLP', 'Information'),
translate('OpenLP', 'Bible format has changed.\nYou have to upgrade your existing Bibles.\n' translate('OpenLP', 'Bible format has changed.\nYou have to upgrade your '
'Should OpenLP upgrade now?'), 'existing Bibles.\nShould OpenLP upgrade now?'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) == \ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) == \
QtGui.QMessageBox.Yes: QtGui.QMessageBox.Yes:
self.on_tools_upgrade_Item_triggered() self.on_tools_upgrade_Item_triggered()
def add_import_menu_item(self, import_menu): def add_import_menu_item(self, import_menu):
"""
:param import_menu:
"""
self.import_bible_item = create_action(import_menu, 'importBibleItem', self.import_bible_item = create_action(import_menu, 'importBibleItem',
text=translate('BiblesPlugin', '&Bible'), visible=False, text=translate('BiblesPlugin', '&Bible'), visible=False,
triggers=self.on_bible_import_click) triggers=self.on_bible_import_click)
import_menu.addAction(self.import_bible_item) import_menu.addAction(self.import_bible_item)
def add_export_menu_Item(self, export_menu): def add_export_menu_item(self, export_menu):
"""
:param export_menu:
"""
self.export_bible_item = create_action(export_menu, 'exportBibleItem', self.export_bible_item = create_action(export_menu, 'exportBibleItem',
text=translate('BiblesPlugin', '&Bible'), visible=False) text=translate('BiblesPlugin', '&Bible'), visible=False)
export_menu.addAction(self.export_bible_item) export_menu.addAction(self.export_bible_item)
@ -190,12 +195,12 @@ class BiblePlugin(Plugin):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin
""" """
## Name PluginList ## # Name PluginList
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
'singular': translate('BiblesPlugin', 'Bible', 'name singular'), 'singular': translate('BiblesPlugin', 'Bible', 'name singular'),
'plural': translate('BiblesPlugin', 'Bibles', 'name plural') 'plural': translate('BiblesPlugin', 'Bibles', 'name plural')
} }
## Name for MediaDockManager, SettingsManager ## # Name for MediaDockManager, SettingsManager
self.text_strings[StringContent.VisibleName] = { self.text_strings[StringContent.VisibleName] = {
'title': translate('BiblesPlugin', 'Bibles', 'container title') 'title': translate('BiblesPlugin', 'Bibles', 'container title')
} }

View File

@ -78,7 +78,7 @@ class BibleUpgradeForm(OpenLPWizard):
Set up the UI for the bible wizard. Set up the UI for the bible wizard.
""" """
super(BibleUpgradeForm, self).setupUi(image) super(BibleUpgradeForm, self).setupUi(image)
Registry().execute('openlp_stop_wizard', self.stop_import) Registry().register_function('openlp_stop_wizard', self.stop_import)
def stop_import(self): def stop_import(self):
""" """

View File

@ -32,9 +32,11 @@ import logging
import os import os
import re import re
import sqlite3 import sqlite3
import time
from PyQt4 import QtCore from PyQt4 import QtCore
from sqlalchemy import Column, ForeignKey, Table, or_, types, func from sqlalchemy import Column, ForeignKey, Table, or_, types, func
from sqlalchemy.exc import OperationalError
from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm import class_mapper, mapper, relation
from sqlalchemy.orm.exc import UnmappedClassError from sqlalchemy.orm.exc import UnmappedClassError
@ -154,7 +156,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
if 'path' in kwargs: if 'path' in kwargs:
self.path = kwargs['path'] self.path = kwargs['path']
self.wizard = None self.wizard = None
Registry().execute('openlp_stop_wizard', self.stop_import) Registry().register_function('openlp_stop_wizard', self.stop_import)
def stop_import(self): def stop_import(self):
""" """
@ -191,7 +193,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
:param testament: *Defaults to 1.* The testament_reference_id from :param testament: *Defaults to 1.* The testament_reference_id from
bibles_resources.sqlite of the testament this book belongs to. bibles_resources.sqlite of the testament this book belongs to.
""" """
log.debug('BibleDB.create_book("%s", "%s")', name, bk_ref_id) log.debug('BibleDB.create_book("%s", "%s")' % (name, bk_ref_id))
book = Book.populate(name=name, book_reference_id=bk_ref_id, testament_reference_id=testament) book = Book.populate(name=name, book_reference_id=bk_ref_id, testament_reference_id=testament)
self.save_object(book) self.save_object(book)
return book return book
@ -202,7 +204,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
:param book: The book object :param book: The book object
""" """
log.debug('BibleDB.update_book("%s")', book.name) log.debug('BibleDB.update_book("%s")' % book.name)
return self.save_object(book) return self.save_object(book)
def delete_book(self, db_book): def delete_book(self, db_book):
@ -211,7 +213,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
:param db_book: The book object. :param db_book: The book object.
""" """
log.debug('BibleDB.delete_book("%s")', db_book.name) log.debug('BibleDB.delete_book("%s")' % db_book.name)
if self.delete_object(Book, db_book.id): if self.delete_object(Book, db_book.id):
return True return True
return False return False
@ -225,7 +227,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
:param text_list: A dict of the verses to be inserted. The key is the verse number, and the value is the :param text_list: A dict of the verses to be inserted. The key is the verse number, and the value is the
verse text. verse text.
""" """
log.debug('BibleDBcreate_chapter("%s", "%s")', book_id, chapter) log.debug('BibleDBcreate_chapter("%s", "%s")' % (book_id, chapter))
# Text list has book and chapter as first two elements of the array. # Text list has book and chapter as first two elements of the array.
for verse_number, verse_text in text_list.items(): for verse_number, verse_text in text_list.items():
verse = Verse.populate( verse = Verse.populate(
@ -235,7 +237,12 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
text=verse_text text=verse_text
) )
self.session.add(verse) self.session.add(verse)
self.session.commit() try:
self.session.commit()
except OperationalError:
# Wait 10ms and try again (lp#1154467)
time.sleep(0.01)
self.session.commit()
def create_verse(self, book_id, chapter, verse, text): def create_verse(self, book_id, chapter, verse, text):
""" """
@ -267,7 +274,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
""" """
if not isinstance(value, str): if not isinstance(value, str):
value = str(value) value = str(value)
log.debug('BibleDB.save_meta("%s/%s")', key, value) log.debug('BibleDB.save_meta("%s/%s")' % (key, value))
meta = self.get_object(BibleMeta, key) meta = self.get_object(BibleMeta, key)
if meta: if meta:
meta.value = value meta.value = value
@ -281,7 +288,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
:param book: The name of the book to return. :param book: The name of the book to return.
""" """
log.debug('BibleDB.get_book("%s")', book) log.debug('BibleDB.get_book("%s")' % book)
return self.get_object_filtered(Book, Book.name.like(book + '%')) return self.get_object_filtered(Book, Book.name.like(book + '%'))
def get_books(self): def get_books(self):
@ -292,17 +299,17 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
log.debug('BibleDB.get_books()') log.debug('BibleDB.get_books()')
return self.get_all_objects(Book, order_by_ref=Book.id) return self.get_all_objects(Book, order_by_ref=Book.id)
def get_book_by_book_ref_id(self, id): def get_book_by_book_ref_id(self, ref_id):
""" """
Return a book object from the database. Return a book object from the database.
:param id: The reference id of the book to return. :param ref_id: The reference id of the book to return.
""" """
log.debug('BibleDB.get_book_by_book_ref_id("%s")', id) log.debug('BibleDB.get_book_by_book_ref_id("%s")' % ref_id)
return self.get_object_filtered(Book, Book.book_reference_id.like(id)) return self.get_object_filtered(Book, Book.book_reference_id.like(ref_id))
def get_book_ref_id_by_name(self, book, maxbooks, language_id=None): def get_book_ref_id_by_name(self, book, maxbooks, language_id=None):
log.debug('BibleDB.get_book_ref_id_by_name:("%s", "%s")', book, language_id) log.debug('BibleDB.get_book_ref_id_by_name:("%s", "%s")' % (book, language_id))
book_id = None book_id = None
if BiblesResourcesDB.get_book(book, True): if BiblesResourcesDB.get_book(book, True):
book_temp = BiblesResourcesDB.get_book(book, True) book_temp = BiblesResourcesDB.get_book(book, True)
@ -328,7 +335,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
:param book: The name of the book, according to the selected language. :param book: The name of the book, according to the selected language.
:param language_selection: The language selection the user has chosen in the settings section of the Bible. :param language_selection: The language selection the user has chosen in the settings section of the Bible.
""" """
log.debug('get_book_ref_id_by_localised_name("%s", "%s")', book, language_selection) log.debug('get_book_ref_id_by_localised_name("%s", "%s")' % (book, language_selection))
from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings
book_names = BibleStrings().BookNames book_names = BibleStrings().BookNames
# escape reserved characters # escape reserved characters
@ -376,14 +383,14 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
[(u'35', 1, 1, 1), (u'35', 2, 2, 3)] [(u'35', 1, 1, 1), (u'35', 2, 2, 3)]
:param show_error: :param show_error:
""" """
log.debug('BibleDB.get_verses("%s")', reference_list) log.debug('BibleDB.get_verses("%s")' % reference_list)
verse_list = [] verse_list = []
book_error = False book_error = False
for book_id, chapter, start_verse, end_verse in reference_list: for book_id, chapter, start_verse, end_verse in reference_list:
db_book = self.get_book_by_book_ref_id(book_id) db_book = self.get_book_by_book_ref_id(book_id)
if db_book: if db_book:
book_id = db_book.book_reference_id book_id = db_book.book_reference_id
log.debug('Book name corrected to "%s"', db_book.name) log.debug('Book name corrected to "%s"' % db_book.name)
if end_verse == -1: if end_verse == -1:
end_verse = self.get_verse_count(book_id, chapter) end_verse = self.get_verse_count(book_id, chapter)
verses = self.session.query(Verse) \ verses = self.session.query(Verse) \
@ -395,7 +402,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
.all() .all()
verse_list.extend(verses) verse_list.extend(verses)
else: else:
log.debug('OpenLP failed to find book with id "%s"', book_id) log.debug('OpenLP failed to find book with id "%s"' % book_id)
book_error = True book_error = True
if book_error and show_error: if book_error and show_error:
critical_error_message_box( critical_error_message_box(
@ -414,7 +421,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
contains spaces, it will split apart and AND'd on the list of contains spaces, it will split apart and AND'd on the list of
values. values.
""" """
log.debug('BibleDB.verse_search("%s")', text) log.debug('BibleDB.verse_search("%s")' % text)
verses = self.session.query(Verse) verses = self.session.query(Verse)
if text.find(',') > -1: if text.find(',') > -1:
keywords = ['%%%s%%' % keyword.strip() for keyword in text.split(',')] keywords = ['%%%s%%' % keyword.strip() for keyword in text.split(',')]
@ -433,7 +440,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
:param book: The book object to get the chapter count for. :param book: The book object to get the chapter count for.
""" """
log.debug('BibleDB.get_chapter_count("%s")', book.name) log.debug('BibleDB.get_chapter_count("%s")' % book.name)
count = self.session.query(func.max(Verse.chapter)).join(Book).filter( count = self.session.query(func.max(Verse.chapter)).join(Book).filter(
Book.book_reference_id == book.book_reference_id).scalar() Book.book_reference_id == book.book_reference_id).scalar()
if not count: if not count:
@ -447,7 +454,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
:param book_ref_id: The book reference id. :param book_ref_id: The book reference id.
:param chapter: The chapter to get the verse count for. :param chapter: The chapter to get the verse count for.
""" """
log.debug('BibleDB.get_verse_count("%s", "%s")', book_ref_id, chapter) log.debug('BibleDB.get_verse_count("%s", "%s")' % (book_ref_id, chapter))
count = self.session.query(func.max(Verse.verse)).join(Book) \ count = self.session.query(func.max(Verse.verse)).join(Book) \
.filter(Book.book_reference_id == book_ref_id) \ .filter(Book.book_reference_id == book_ref_id) \
.filter(Verse.chapter == chapter) \ .filter(Verse.chapter == chapter) \
@ -563,7 +570,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param name: The name or abbreviation of the book. :param name: The name or abbreviation of the book.
:param lower: True if the comparison should be only lowercase :param lower: True if the comparison should be only lowercase
""" """
log.debug('BiblesResourcesDB.get_book("%s")', name) log.debug('BiblesResourcesDB.get_book("%s")' % name)
if not isinstance(name, str): if not isinstance(name, str):
name = str(name) name = str(name)
if lower: if lower:
@ -592,7 +599,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param string: The string to search for in the book names or abbreviations. :param string: The string to search for in the book names or abbreviations.
""" """
log.debug('BiblesResourcesDB.get_book_like("%s")', string) log.debug('BiblesResourcesDB.get_book_like("%s")' % string)
if not isinstance(string, str): if not isinstance(string, str):
name = str(string) name = str(string)
books = BiblesResourcesDB.run_sql( books = BiblesResourcesDB.run_sql(
@ -611,17 +618,17 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
return None return None
@staticmethod @staticmethod
def get_book_by_id(id): def get_book_by_id(book_id):
""" """
Return a book by id. Return a book by id.
:param id: The id of the book. :param book_id: The id of the book.
""" """
log.debug('BiblesResourcesDB.get_book_by_id("%s")', id) log.debug('BiblesResourcesDB.get_book_by_id("%s")' % book_id)
if not isinstance(id, int): if not isinstance(book_id, int):
id = int(id) book_id = int(book_id)
books = BiblesResourcesDB.run_sql( books = BiblesResourcesDB.run_sql(
'SELECT id, testament_id, name, abbreviation, chapters FROM book_reference WHERE id = ?', (id, )) 'SELECT id, testament_id, name, abbreviation, chapters FROM book_reference WHERE id = ?', (book_id, ))
if books: if books:
return { return {
'id': books[0][0], 'id': books[0][0],
@ -641,7 +648,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param book_ref_id: The id of a book. :param book_ref_id: The id of a book.
:param chapter: The chapter number. :param chapter: The chapter number.
""" """
log.debug('BiblesResourcesDB.get_chapter("%s", "%s")', book_ref_id, chapter) log.debug('BiblesResourcesDB.get_chapter("%s", "%s")' % (book_ref_id, chapter))
if not isinstance(chapter, int): if not isinstance(chapter, int):
chapter = int(chapter) chapter = int(chapter)
chapters = BiblesResourcesDB.run_sql( chapters = BiblesResourcesDB.run_sql(
@ -649,10 +656,10 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
'chapter, verse_count FROM chapters WHERE book_reference_id = ?', (book_ref_id,)) 'chapter, verse_count FROM chapters WHERE book_reference_id = ?', (book_ref_id,))
try: try:
return { return {
'id': chapters[chapter-1][0], 'id': chapters[chapter - 1][0],
'book_reference_id': chapters[chapter-1][1], 'book_reference_id': chapters[chapter - 1][1],
'chapter': chapters[chapter-1][2], 'chapter': chapters[chapter - 1][2],
'verse_count': chapters[chapter-1][3] 'verse_count': chapters[chapter - 1][3]
} }
except (IndexError, TypeError): except (IndexError, TypeError):
return None return None
@ -664,7 +671,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param book_ref_id: The id of the book. :param book_ref_id: The id of the book.
""" """
log.debug('BiblesResourcesDB.get_chapter_count("%s")', book_ref_id) log.debug('BiblesResourcesDB.get_chapter_count("%s")' % book_ref_id)
details = BiblesResourcesDB.get_book_by_id(book_ref_id) details = BiblesResourcesDB.get_book_by_id(book_ref_id)
if details: if details:
return details['chapters'] return details['chapters']
@ -678,7 +685,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param book_ref_id: The id of the book. :param book_ref_id: The id of the book.
:param chapter: The number of the chapter. :param chapter: The number of the chapter.
""" """
log.debug('BiblesResourcesDB.get_verse_count("%s", "%s")', book_ref_id, chapter) log.debug('BiblesResourcesDB.get_verse_count("%s", "%s")' % (book_ref_id, chapter))
details = BiblesResourcesDB.get_chapter(book_ref_id, chapter) details = BiblesResourcesDB.get_chapter(book_ref_id, chapter)
if details: if details:
return details['verse_count'] return details['verse_count']
@ -691,7 +698,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param source: The name or abbreviation of the book. :param source: The name or abbreviation of the book.
""" """
log.debug('BiblesResourcesDB.get_download_source("%s")', source) log.debug('BiblesResourcesDB.get_download_source("%s")' % source)
if not isinstance(source, str): if not isinstance(source, str):
source = str(source) source = str(source)
source = source.title() source = source.title()
@ -712,7 +719,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param source: The source of the web_bible. :param source: The source of the web_bible.
""" """
log.debug('BiblesResourcesDB.get_webbibles("%s")', source) log.debug('BiblesResourcesDB.get_webbibles("%s")' % source)
if not isinstance(source, str): if not isinstance(source, str):
source = str(source) source = str(source)
source = BiblesResourcesDB.get_download_source(source) source = BiblesResourcesDB.get_download_source(source)
@ -737,7 +744,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param abbreviation: The abbreviation of the web_bible. :param abbreviation: The abbreviation of the web_bible.
:param source: The source of the web_bible. :param source: The source of the web_bible.
""" """
log.debug('BiblesResourcesDB.get_webbibles("%s", "%s")', abbreviation, source) log.debug('BiblesResourcesDB.get_webbibles("%s", "%s")' % (abbreviation, source))
if not isinstance(abbreviation, str): if not isinstance(abbreviation, str):
abbreviation = str(abbreviation) abbreviation = str(abbreviation)
if not isinstance(source, str): if not isinstance(source, str):
@ -765,7 +772,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param name: The name to search the id. :param name: The name to search the id.
:param language_id: The language_id for which language should be searched :param language_id: The language_id for which language should be searched
""" """
log.debug('BiblesResourcesDB.get_alternative_book_name("%s", "%s")', name, language_id) log.debug('BiblesResourcesDB.get_alternative_book_name("%s", "%s")' % (name, language_id))
if language_id: if language_id:
books = BiblesResourcesDB.run_sql( books = BiblesResourcesDB.run_sql(
'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ? ORDER BY id', 'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ? ORDER BY id',
@ -784,7 +791,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param name: The name or abbreviation of the language. :param name: The name or abbreviation of the language.
""" """
log.debug('BiblesResourcesDB.get_language("%s")', name) log.debug('BiblesResourcesDB.get_language("%s")' % name)
if not isinstance(name, str): if not isinstance(name, str):
name = str(name) name = str(name)
language = BiblesResourcesDB.run_sql( language = BiblesResourcesDB.run_sql(
@ -846,13 +853,13 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
file_path = os.path.join( file_path = os.path.join(
AppLocation.get_directory(AppLocation.DataDir), 'bibles', 'alternative_book_names.sqlite') AppLocation.get_directory(AppLocation.DataDir), 'bibles', 'alternative_book_names.sqlite')
if not os.path.exists(file_path): if not os.path.exists(file_path):
#create new DB, create table alternative_book_names # create new DB, create table alternative_book_names
AlternativeBookNamesDB.conn = sqlite3.connect(file_path) AlternativeBookNamesDB.conn = sqlite3.connect(file_path)
AlternativeBookNamesDB.conn.execute( AlternativeBookNamesDB.conn.execute(
'CREATE TABLE alternative_book_names(id INTEGER NOT NULL, ' 'CREATE TABLE alternative_book_names(id INTEGER NOT NULL, '
'book_reference_id INTEGER, language_id INTEGER, name VARCHAR(50), PRIMARY KEY (id))') 'book_reference_id INTEGER, language_id INTEGER, name VARCHAR(50), PRIMARY KEY (id))')
else: else:
#use existing DB # use existing DB
AlternativeBookNamesDB.conn = sqlite3.connect(file_path) AlternativeBookNamesDB.conn = sqlite3.connect(file_path)
AlternativeBookNamesDB.cursor = AlternativeBookNamesDB.conn.cursor() AlternativeBookNamesDB.cursor = AlternativeBookNamesDB.conn.cursor()
return AlternativeBookNamesDB.cursor return AlternativeBookNamesDB.cursor
@ -880,7 +887,7 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
:param name: The name to search the id. :param name: The name to search the id.
:param language_id: The language_id for which language should be searched :param language_id: The language_id for which language should be searched
""" """
log.debug('AlternativeBookNamesDB.get_book_reference_id("%s", "%s")', name, language_id) log.debug('AlternativeBookNamesDB.get_book_reference_id("%s", "%s")' % (name, language_id))
if language_id: if language_id:
books = AlternativeBookNamesDB.run_sql( books = AlternativeBookNamesDB.run_sql(
'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ?', (language_id, )) 'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ?', (language_id, ))
@ -901,8 +908,8 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
:param book_reference_id: The book_reference_id of the book. :param book_reference_id: The book_reference_id of the book.
:param language_id: The language to which the alternative book name belong. :param language_id: The language to which the alternative book name belong.
""" """
log.debug('AlternativeBookNamesDB.create_alternative_book_name("%s", "%s", "%s")', log.debug('AlternativeBookNamesDB.create_alternative_book_name("%s", "%s", "%s")' %
name, book_reference_id, language_id) (name, book_reference_id, language_id))
return AlternativeBookNamesDB.run_sql( return AlternativeBookNamesDB.run_sql(
'INSERT INTO alternative_book_names(book_reference_id, language_id, name) ' 'INSERT INTO alternative_book_names(book_reference_id, language_id, name) '
'VALUES (?, ?, ?)', (book_reference_id, language_id, name), True) 'VALUES (?, ?, ?)', (book_reference_id, language_id, name), True)

View File

@ -552,10 +552,10 @@ class HTTPBible(BibleDB, RegistryProperties):
self.application.set_busy_cursor() self.application.set_busy_cursor()
search_results = self.get_chapter(book, reference[1]) search_results = self.get_chapter(book, reference[1])
if search_results and search_results.has_verse_list(): if search_results and search_results.has_verse_list():
## We have found a book of the bible lets check to see # We have found a book of the bible lets check to see
## if it was there. By reusing the returned book name # if it was there. By reusing the returned book name
## we get a correct book. For example it is possible # we get a correct book. For example it is possible
## to request ac and get Acts back. # to request ac and get Acts back.
book_name = search_results.book book_name = search_results.book
self.application.process_events() self.application.process_events()
# Check to see if book/chapter exists. # Check to see if book/chapter exists.

View File

@ -480,6 +480,10 @@ class BibleMediaItem(MediaManagerItem):
self.reload_bibles() self.reload_bibles()
def on_delete_click(self): def on_delete_click(self):
"""
When the delete button is pressed
"""
bible = None
if self.quickTab.isVisible(): if self.quickTab.isVisible():
bible = self.quickVersionComboBox.currentText() bible = self.quickVersionComboBox.currentText()
elif self.advancedTab.isVisible(): elif self.advancedTab.isVisible():
@ -488,8 +492,9 @@ class BibleMediaItem(MediaManagerItem):
if QtGui.QMessageBox.question( if QtGui.QMessageBox.question(
self, UiStrings().ConfirmDelete, self, UiStrings().ConfirmDelete,
translate('BiblesPlugin.MediaItem', 'Are you sure you want to completely delete "%s" Bible from ' translate('BiblesPlugin.MediaItem', 'Are you sure you want to completely delete "%s" Bible from '
'OpenLP?\n\nYou will need to re-import this Bible to use it again.') % bible, 'OpenLP?\n\nYou will need to re-import this Bible to use it '
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), 'again.') % bible,
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No: QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
return return
self.plugin.manager.delete_bible(bible) self.plugin.manager.delete_bible(bible)
@ -752,7 +757,7 @@ class BibleMediaItem(MediaManagerItem):
log.exception('The second_search_results does not have as many verses as the search_results.') log.exception('The second_search_results does not have as many verses as the search_results.')
break break
bible_text = '%s %d%s%d (%s, %s)' % (book, verse.chapter, verse_separator, verse.verse, version, bible_text = '%s %d%s%d (%s, %s)' % (book, verse.chapter, verse_separator, verse.verse, version,
second_version) second_version)
else: else:
bible_text = '%s %d%s%d (%s)' % (book, verse.chapter, verse_separator, verse.verse, version) bible_text = '%s %d%s%d (%s)' % (book, verse.chapter, verse_separator, verse.verse, version)
bible_verse = QtGui.QListWidgetItem(bible_text) bible_verse = QtGui.QListWidgetItem(bible_text)
@ -840,6 +845,7 @@ class BibleMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.CanLoop) service_item.add_capability(ItemCapabilities.CanLoop)
service_item.add_capability(ItemCapabilities.CanWordSplit) service_item.add_capability(ItemCapabilities.CanWordSplit)
service_item.add_capability(ItemCapabilities.CanEditTitle)
# Service Item: Title # Service Item: Title
service_item.title = create_separated_list(raw_title) service_item.title = create_separated_list(raw_title)
# Service Item: Theme # Service Item: Theme

View File

@ -94,5 +94,5 @@ class VerseReferenceList(object):
result = result + ', ' + version['permission'] result = result + ', ' + version['permission']
result = result.rstrip() result = result.rstrip()
if result.endswith(','): if result.endswith(','):
return result[:len(result)-1] return result[:len(result) - 1]
return result return result

View File

@ -43,7 +43,7 @@ log = logging.getLogger(__name__)
__default_settings__ = { __default_settings__ = {
'custom/db type': 'sqlite', 'custom/db type': 'sqlite',
'custom/last search type': CustomSearch.Titles, 'custom/last search type': CustomSearch.Titles,
'custom/display footer': True, 'custom/display footer': True,
'custom/add custom from service': True 'custom/add custom from service': True
} }
@ -97,12 +97,12 @@ class CustomPlugin(Plugin):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin
""" """
## Name PluginList ## # Name PluginList
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
'singular': translate('CustomPlugin', 'Custom Slide', 'name singular'), 'singular': translate('CustomPlugin', 'Custom Slide', 'name singular'),
'plural': translate('CustomPlugin', 'Custom Slides', 'name plural') 'plural': translate('CustomPlugin', 'Custom Slides', 'name plural')
} }
## Name for MediaDockManager, SettingsManager ## # Name for MediaDockManager, SettingsManager
self.text_strings[StringContent.VisibleName] = { self.text_strings[StringContent.VisibleName] = {
'title': translate('CustomPlugin', 'Custom Slides', 'container title') 'title': translate('CustomPlugin', 'Custom Slides', 'container title')
} }

View File

@ -1,4 +1,3 @@
#lint:disable
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4

View File

@ -51,7 +51,7 @@ from lxml import etree, objectify
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
#TODO: These classes need to be refactored into a single class. # TODO: These classes need to be refactored into a single class.
class CustomXMLBuilder(object): class CustomXMLBuilder(object):
""" """
This class builds the XML used to describe songs. This class builds the XML used to describe songs.

View File

@ -95,12 +95,12 @@ class ImagePlugin(Plugin):
""" """
Called to define all translatable texts of the plugin. Called to define all translatable texts of the plugin.
""" """
## Name PluginList ## # Name PluginList
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
'singular': translate('ImagePlugin', 'Image', 'name singular'), 'singular': translate('ImagePlugin', 'Image', 'name singular'),
'plural': translate('ImagePlugin', 'Images', 'name plural') 'plural': translate('ImagePlugin', 'Images', 'name plural')
} }
## Name for MediaDockManager, SettingsManager ## # Name for MediaDockManager, SettingsManager
self.text_strings[StringContent.VisibleName] = {'title': translate('ImagePlugin', 'Images', 'container title')} self.text_strings[StringContent.VisibleName] = {'title': translate('ImagePlugin', 'Images', 'container title')}
# Middle Header Bar # Middle Header Bar
tooltips = { tooltips = {

View File

@ -550,6 +550,7 @@ class ImageMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.CanLoop) service_item.add_capability(ItemCapabilities.CanLoop)
service_item.add_capability(ItemCapabilities.CanAppend) service_item.add_capability(ItemCapabilities.CanAppend)
service_item.add_capability(ItemCapabilities.CanEditTitle)
# force a nonexistent theme # force a nonexistent theme
service_item.theme = -1 service_item.theme = -1
missing_items_file_names = [] missing_items_file_names = []

View File

@ -103,7 +103,6 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
self.reset_action.setToolTip(UiStrings().ResetLiveBG) self.reset_action.setToolTip(UiStrings().ResetLiveBG)
self.automatic = UiStrings().Automatic self.automatic = UiStrings().Automatic
self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:')) self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
#self.rebuild_players()
def required_icons(self): def required_icons(self):
""" """
@ -164,7 +163,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
if index == 0: if index == 0:
set_media_players(player) set_media_players(player)
else: else:
set_media_players(player, player[index-1]) set_media_players(player, player[index - 1])
def on_reset_click(self): def on_reset_click(self):
""" """
@ -261,6 +260,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
if not self.media_controller.media_length(service_item): if not self.media_controller.media_length(service_item):
return False return False
service_item.add_capability(ItemCapabilities.CanAutoStartForLive) service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
service_item.add_capability(ItemCapabilities.CanEditTitle)
service_item.add_capability(ItemCapabilities.RequiresMedia) service_item.add_capability(ItemCapabilities.RequiresMedia)
if Settings().value(self.settings_section + '/media auto start') == QtCore.Qt.Checked: if Settings().value(self.settings_section + '/media auto start') == QtCore.Qt.Checked:
service_item.will_auto_start = True service_item.will_auto_start = True

View File

@ -73,12 +73,12 @@ class MediaPlugin(Plugin):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin
""" """
## Name PluginList ## # Name PluginList
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
'singular': translate('MediaPlugin', 'Media', 'name singular'), 'singular': translate('MediaPlugin', 'Media', 'name singular'),
'plural': translate('MediaPlugin', 'Media', 'name plural') 'plural': translate('MediaPlugin', 'Media', 'name plural')
} }
## Name for MediaDockManager, SettingsManager ## # Name for MediaDockManager, SettingsManager
self.text_strings[StringContent.VisibleName] = { self.text_strings[StringContent.VisibleName] = {
'title': translate('MediaPlugin', 'Media', 'container title') 'title': translate('MediaPlugin', 'Media', 'container title')
} }

View File

@ -377,8 +377,6 @@ class ImpressDocument(PresentationDocument):
Stop the presentation, remove from screen. Stop the presentation, remove from screen.
""" """
log.debug('stop presentation OpenOffice') log.debug('stop presentation OpenOffice')
# deactivate should hide the screen according to docs, but doesn't
#self.control.deactivate()
self.presentation.end() self.presentation.end()
self.control = None self.control = None

View File

@ -263,6 +263,7 @@ class PresentationMediaItem(MediaManagerItem):
file_type = os.path.splitext(filename)[1][1:] file_type = os.path.splitext(filename)[1][1:]
if not self.display_type_combo_box.currentText(): if not self.display_type_combo_box.currentText():
return False return False
service_item.add_capability(ItemCapabilities.CanEditTitle)
if (file_type == 'pdf' or file_type == 'xps') and context != ServiceItemContext.Service: if (file_type == 'pdf' or file_type == 'xps') and context != ServiceItemContext.Service:
service_item.add_capability(ItemCapabilities.CanMaintain) service_item.add_capability(ItemCapabilities.CanMaintain)
service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanPreview)

View File

@ -74,7 +74,7 @@ class PdfController(PresentationController):
runlog = '' runlog = ''
log.debug('testing program_path: %s', program_path) log.debug('testing program_path: %s', program_path)
try: try:
runlog = check_output([program_path, '--help'], stderr=STDOUT) runlog = check_output([program_path, '--help'], stderr=STDOUT)
except CalledProcessError as e: except CalledProcessError as e:
runlog = e.output runlog = e.output
except Exception: except Exception:
@ -183,7 +183,7 @@ class PdfDocument(PresentationDocument):
self.image_files = [] self.image_files = []
self.num_pages = -1 self.num_pages = -1
def gs_get_resolution(self, size): def gs_get_resolution(self, size):
""" """
Only used when using ghostscript Only used when using ghostscript
Ghostscript can't scale automatically while keeping aspect like mupdf, so we need Ghostscript can't scale automatically while keeping aspect like mupdf, so we need
@ -204,19 +204,19 @@ class PdfDocument(PresentationDocument):
log.debug(' '.join(e.cmd)) log.debug(' '.join(e.cmd))
log.debug(e.output) log.debug(e.output)
# Extract the pdf resolution from output, the format is " Size: x: <width>, y: <height>" # Extract the pdf resolution from output, the format is " Size: x: <width>, y: <height>"
width = 0 width = 0.0
height = 0 height = 0.0
for line in runlog.splitlines(): for line in runlog.splitlines():
try: try:
width = int(re.search('.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1)) width = float(re.search('.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1))
height = int(re.search('.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1)) height = float(re.search('.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1))
break break
except AttributeError: except AttributeError:
pass continue
# Calculate the ratio from pdf to screen # Calculate the ratio from pdf to screen
if width > 0 and height > 0: if width > 0 and height > 0:
width_ratio = size.right() / float(width) width_ratio = size.right() / width
height_ratio = size.bottom() / float(height) height_ratio = size.bottom() / height
# return the resolution that should be used. 72 is default. # return the resolution that should be used. 72 is default.
if width_ratio > height_ratio: if width_ratio > height_ratio:
return int(height_ratio * 72) return int(height_ratio * 72)
@ -236,7 +236,7 @@ class PdfDocument(PresentationDocument):
if os.path.isfile(os.path.join(self.get_temp_folder(), 'mainslide001.png')): if os.path.isfile(os.path.join(self.get_temp_folder(), 'mainslide001.png')):
created_files = sorted(os.listdir(self.get_temp_folder())) created_files = sorted(os.listdir(self.get_temp_folder()))
for fn in created_files: for fn in created_files:
if os.path.isfile(os.path.join(self.get_temp_folder(), fn)): if os.path.isfile(os.path.join(self.get_temp_folder(), fn)):
self.image_files.append(os.path.join(self.get_temp_folder(), fn)) self.image_files.append(os.path.join(self.get_temp_folder(), fn))
self.num_pages = len(self.image_files) self.num_pages = len(self.image_files)
return True return True

View File

@ -247,7 +247,7 @@ class PowerpointDocument(PresentationDocument):
Starts a presentation from the beginning. Starts a presentation from the beginning.
""" """
log.debug('start_presentation') log.debug('start_presentation')
#SlideShowWindow measures its size/position by points, not pixels # SlideShowWindow measures its size/position by points, not pixels
try: try:
dpi = win32ui.GetActiveWindow().GetDC().GetDeviceCaps(88) dpi = win32ui.GetActiveWindow().GetDC().GetDeviceCaps(88)
except win32ui.error: except win32ui.error:

View File

@ -122,8 +122,10 @@ class PresentationDocument(object):
a file, e.g. thumbnails a file, e.g. thumbnails
""" """
try: try:
shutil.rmtree(self.get_thumbnail_folder()) if os.path.exists(self.get_thumbnail_folder()):
shutil.rmtree(self.get_temp_folder()) shutil.rmtree(self.get_thumbnail_folder())
if os.path.exists(self.get_temp_folder()):
shutil.rmtree(self.get_temp_folder())
except OSError: except OSError:
log.exception('Failed to delete presentation controller files') log.exception('Failed to delete presentation controller files')

View File

@ -175,10 +175,10 @@ class PresentationTab(SettingsTab):
if pdf_program == '': if pdf_program == '':
enable_pdf_program = 0 enable_pdf_program = 0
if pdf_program != Settings().value(self.settings_section + '/pdf_program'): if pdf_program != Settings().value(self.settings_section + '/pdf_program'):
Settings().setValue(self.settings_section + '/pdf_program', pdf_program) Settings().setValue(self.settings_section + '/pdf_program', pdf_program)
changed = True changed = True
if enable_pdf_program != Settings().value(self.settings_section + '/enable_pdf_program'): if enable_pdf_program != Settings().value(self.settings_section + '/enable_pdf_program'):
Settings().setValue(self.settings_section + '/enable_pdf_program', enable_pdf_program) Settings().setValue(self.settings_section + '/enable_pdf_program', enable_pdf_program)
changed = True changed = True
if changed: if changed:
self.settings_form.register_post_process('mediaitem_suffix_reset') self.settings_form.register_post_process('mediaitem_suffix_reset')

View File

@ -156,12 +156,12 @@ class PresentationPlugin(Plugin):
""" """
Called to define all translatable texts of the plugin. Called to define all translatable texts of the plugin.
""" """
## Name PluginList ## # Name PluginList
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
'singular': translate('PresentationPlugin', 'Presentation', 'name singular'), 'singular': translate('PresentationPlugin', 'Presentation', 'name singular'),
'plural': translate('PresentationPlugin', 'Presentations', 'name plural') 'plural': translate('PresentationPlugin', 'Presentations', 'name plural')
} }
## Name for MediaDockManager, SettingsManager ## # Name for MediaDockManager, SettingsManager
self.text_strings[StringContent.VisibleName] = { self.text_strings[StringContent.VisibleName] = {
'title': translate('PresentationPlugin', 'Presentations', 'container title') 'title': translate('PresentationPlugin', 'Presentations', 'container title')
} }

View File

@ -92,12 +92,12 @@ class RemotesPlugin(Plugin):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin
""" """
## Name PluginList ## # Name PluginList
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
'singular': translate('RemotePlugin', 'Remote', 'name singular'), 'singular': translate('RemotePlugin', 'Remote', 'name singular'),
'plural': translate('RemotePlugin', 'Remotes', 'name plural') 'plural': translate('RemotePlugin', 'Remotes', 'name plural')
} }
## Name for MediaDockManager, SettingsManager ## # Name for MediaDockManager, SettingsManager
self.text_strings[StringContent.VisibleName] = { self.text_strings[StringContent.VisibleName] = {
'title': translate('RemotePlugin', 'Remote', 'container title') 'title': translate('RemotePlugin', 'Remote', 'container title')
} }

View File

@ -31,6 +31,7 @@ The duplicate song removal logic for OpenLP.
""" """
import logging import logging
import multiprocessing
import os import os
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -45,6 +46,18 @@ from openlp.plugins.songs.lib.songcompare import songs_probably_equal
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def song_generator(songs):
"""
This is a generator function to return tuples of two songs. When completed then all songs have once been returned
combined with any other songs.
:param songs: All songs in the database.
"""
for outer_song_counter in range(len(songs) - 1):
for inner_song_counter in range(outer_song_counter + 1, len(songs)):
yield (songs[outer_song_counter], songs[inner_song_counter])
class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
""" """
This is the Duplicate Song Removal Wizard. It provides functionality to search for and remove duplicate songs This is the Duplicate Song Removal Wizard. It provides functionality to search for and remove duplicate songs
@ -114,7 +127,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
self.review_layout.addWidget(self.review_scroll_area) self.review_layout.addWidget(self.review_scroll_area)
self.review_page_id = self.addPage(self.review_page) self.review_page_id = self.addPage(self.review_page)
# Add a dummy page to the end, to prevent the finish button to appear and the next button do disappear on the # Add a dummy page to the end, to prevent the finish button to appear and the next button do disappear on the
#review page. # review page.
self.dummy_page = QtGui.QWizardPage() self.dummy_page = QtGui.QWizardPage()
self.dummy_page_id = self.addPage(self.dummy_page) self.dummy_page_id = self.addPage(self.dummy_page)
@ -167,24 +180,31 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
max_progress_count = max_songs * (max_songs - 1) // 2 max_progress_count = max_songs * (max_songs - 1) // 2
self.duplicate_search_progress_bar.setMaximum(max_progress_count) self.duplicate_search_progress_bar.setMaximum(max_progress_count)
songs = self.plugin.manager.get_all_objects(Song) songs = self.plugin.manager.get_all_objects(Song)
for outer_song_counter in range(max_songs - 1): # Create a worker/process pool to check the songs.
for inner_song_counter in range(outer_song_counter + 1, max_songs): process_number = max(1, multiprocessing.cpu_count() - 1)
if songs_probably_equal(songs[outer_song_counter], songs[inner_song_counter]): pool = multiprocessing.Pool(process_number)
duplicate_added = self.add_duplicates_to_song_list( result = pool.imap_unordered(songs_probably_equal, song_generator(songs), 30)
songs[outer_song_counter], songs[inner_song_counter]) # Do not accept any further tasks. Also this closes the processes if all tasks are done.
if duplicate_added: pool.close()
self.found_duplicates_edit.appendPlainText( # While the processes are still working, start to look at the results.
songs[outer_song_counter].title + " = " + songs[inner_song_counter].title) for song_tuple in result:
self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1) self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)
# The call to process_events() will keep the GUI responsive. # The call to process_events() will keep the GUI responsive.
self.application.process_events() self.application.process_events()
if self.break_search: if self.break_search:
return pool.terminate()
return
if song_tuple is None:
continue
song1, song2 = song_tuple
duplicate_added = self.add_duplicates_to_song_list(song1, song2)
if duplicate_added:
self.found_duplicates_edit.appendPlainText(song1.title + " = " + song2.title)
self.review_total_count = len(self.duplicate_song_list) self.review_total_count = len(self.duplicate_song_list)
if self.review_total_count == 0: if self.duplicate_song_list:
self.notify_no_duplicates()
else:
self.button(QtGui.QWizard.NextButton).show() self.button(QtGui.QWizard.NextButton).show()
else:
self.notify_no_duplicates()
finally: finally:
self.application.set_normal_cursor() self.application.set_normal_cursor()
elif page_id == self.review_page_id: elif page_id == self.review_page_id:
@ -217,12 +237,12 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
duplicate_added = False duplicate_added = False
for duplicate_group in self.duplicate_song_list: for duplicate_group in self.duplicate_song_list:
# Skip the first song in the duplicate lists, since the first one has to be an earlier song. # Skip the first song in the duplicate lists, since the first one has to be an earlier song.
if search_song in duplicate_group and not duplicate_song in duplicate_group: if search_song in duplicate_group and duplicate_song not in duplicate_group:
duplicate_group.append(duplicate_song) duplicate_group.append(duplicate_song)
duplicate_group_found = True duplicate_group_found = True
duplicate_added = True duplicate_added = True
break break
elif not search_song in duplicate_group and duplicate_song in duplicate_group: elif search_song not in duplicate_group and duplicate_song in duplicate_group:
duplicate_group.append(search_song) duplicate_group.append(search_song)
duplicate_group_found = True duplicate_group_found = True
duplicate_added = True duplicate_added = True

View File

@ -675,14 +675,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
separator = parts.find(':') separator = parts.find(':')
if separator >= 0: if separator >= 0:
verse_name = parts[0:separator].strip() verse_name = parts[0:separator].strip()
verse_num = parts[separator+1:].strip() verse_num = parts[separator + 1:].strip()
else: else:
verse_name = parts verse_name = parts
verse_num = '1' verse_num = '1'
verse_index = VerseType.from_loose_input(verse_name) verse_index = VerseType.from_loose_input(verse_name)
verse_tag = VerseType.tags[verse_index] verse_tag = VerseType.tags[verse_index]
# Later we need to handle v1a as well. # Later we need to handle v1a as well.
#regex = re.compile(r'(\d+\w.)')
regex = re.compile(r'\D*(\d+)\D*') regex = re.compile(r'\D*(\d+)\D*')
match = regex.match(verse_num) match = regex.match(verse_num)
if match: if match:

View File

@ -75,7 +75,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
text = self.verse_text_edit.toPlainText() text = self.verse_text_edit.toPlainText()
position = self.verse_text_edit.textCursor().position() position = self.verse_text_edit.textCursor().position()
insert_string = '[---]' insert_string = '[---]'
if position and text[position-1] != '\n': if position and text[position - 1] != '\n':
insert_string = '\n' + insert_string insert_string = '\n' + insert_string
if position == len(text) or text[position] != '\n': if position == len(text) or text[position] != '\n':
insert_string += '\n' insert_string += '\n'

View File

@ -66,8 +66,10 @@ class Ui_MediaFilesDialog(object):
def retranslateUi(self, media_files_dialog): def retranslateUi(self, media_files_dialog):
""" """
Translate the UI on the fly. Translate the UI on the fly.
:param media_files_dialog:
""" """
media_files_dialog.setWindowTitle(translate('SongsPlugin.MediaFilesForm', 'Select Media File(s)')) media_files_dialog.setWindowTitle(translate('SongsPlugin.MediaFilesForm', 'Select Media File(s)'))
self.select_label.setText(translate('SongsPlugin.MediaFilesForm', self.select_label.setText(translate('SongsPlugin.MediaFilesForm', 'Select one or more audio files from the '
'Select one or more audio files from the list below, and click OK to import them ' 'list below, and click OK to import them '
'into this song.')) 'into this song.'))

View File

@ -152,9 +152,9 @@ class SongExportForm(OpenLPWizard):
self.setWindowTitle(translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard')) self.setWindowTitle(translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard'))
self.title_label.setText(WizardStrings.HeaderStyle % self.title_label.setText(WizardStrings.HeaderStyle %
translate('OpenLP.Ui', 'Welcome to the Song Export Wizard')) translate('OpenLP.Ui', 'Welcome to the Song Export Wizard'))
self.information_label.setText(translate('SongsPlugin.ExportWizardForm', 'This wizard will help to' self.information_label.setText(
' export your songs to the open and free <strong>OpenLyrics </strong> worship ' translate('SongsPlugin.ExportWizardForm', 'This wizard will help to export your songs to the open and free '
'song format.')) '<strong>OpenLyrics </strong> worship song format.'))
self.available_songs_page.setTitle(translate('SongsPlugin.ExportWizardForm', 'Select Songs')) self.available_songs_page.setTitle(translate('SongsPlugin.ExportWizardForm', 'Select Songs'))
self.available_songs_page.setSubTitle(translate('SongsPlugin.ExportWizardForm', self.available_songs_page.setSubTitle(translate('SongsPlugin.ExportWizardForm',
'Check the songs you want to export.')) 'Check the songs you want to export.'))

View File

@ -270,13 +270,13 @@ class CCLIFileImport(SongImport):
verse_text = '' verse_text = ''
verse_start = False verse_start = False
else: else:
#line_number=0, song title # line_number=0, song title
if line_number == 0: if line_number == 0:
self.title = clean_line self.title = clean_line
line_number += 1 line_number += 1
#line_number=1, verses # line_number=1, verses
elif line_number == 1: elif line_number == 1:
#line_number=1, ccli number, first line after verses # line_number=1, ccli number, first line after verses
if clean_line.startswith('CCLI'): if clean_line.startswith('CCLI'):
line_number += 1 line_number += 1
ccli_parts = clean_line.split(' ') ccli_parts = clean_line.split(' ')
@ -319,21 +319,21 @@ class CCLIFileImport(SongImport):
# last part. Add l so as to keep the CRLF # last part. Add l so as to keep the CRLF
verse_text = verse_text + line verse_text = verse_text + line
else: else:
#line_number=2, copyright # line_number=2, copyright
if line_number == 2: if line_number == 2:
line_number += 1 line_number += 1
if clean_line.startswith('©'): if clean_line.startswith('©'):
self.copyright = clean_line self.copyright = clean_line
else: else:
song_author = clean_line song_author = clean_line
#n=3, authors # n=3, authors
elif line_number == 3: elif line_number == 3:
line_number += 1 line_number += 1
if song_author: if song_author:
self.copyright = clean_line self.copyright = clean_line
else: else:
song_author = clean_line song_author = clean_line
#line_number=4, comments lines before last line # line_number=4, comments lines before last line
elif line_number == 4 and not clean_line.startswith('CCL'): elif line_number == 4 and not clean_line.startswith('CCL'):
self.comments += clean_line self.comments += clean_line
# split on known separators # split on known separators

View File

@ -305,7 +305,7 @@ class EasyWorshipSongImport(SongImport):
elif field_desc.field_type == FieldType.Logical: elif field_desc.field_type == FieldType.Logical:
return field ^ 0x80 == 1 return field ^ 0x80 == 1
elif field_desc.field_type == FieldType.Memo or field_desc.field_type == FieldType.Blob: elif field_desc.field_type == FieldType.Memo or field_desc.field_type == FieldType.Blob:
block_start, blob_size = struct.unpack_from('<II', field, len(field)-10) block_start, blob_size = struct.unpack_from('<II', field, len(field) - 10)
sub_block = block_start & 0xff sub_block = block_start & 0xff
block_start &= ~0xff block_start &= ~0xff
self.memo_file.seek(block_start) self.memo_file.seek(block_start)

View File

@ -96,7 +96,7 @@ class SongMediaItem(MediaManagerItem):
def add_end_header_bar(self): def add_end_header_bar(self):
self.toolbar.addSeparator() self.toolbar.addSeparator()
## Song Maintenance Button ## # Song Maintenance Button
self.maintenance_action = self.toolbar.add_toolbar_action('maintenance_action', self.maintenance_action = self.toolbar.add_toolbar_action('maintenance_action',
icon=':/songs/song_maintenance.png', icon=':/songs/song_maintenance.png',
triggers=self.on_song_maintenance_click) triggers=self.on_song_maintenance_click)
@ -266,7 +266,7 @@ class SongMediaItem(MediaManagerItem):
# Do not display temporary songs # Do not display temporary songs
if song.temporary: if song.temporary:
continue continue
if song_number and not song_number in song.song_number: if song_number and song_number not in song.song_number:
continue continue
song_detail = '%s - %s (%s)' % (book.name, song.song_number, song.title) song_detail = '%s - %s (%s)' % (book.name, song.song_number, song.title)
song_name = QtGui.QListWidgetItem(song_detail) song_name = QtGui.QListWidgetItem(song_detail)

View File

@ -56,11 +56,15 @@ class SongBeamerTypes(object):
'Zwischenspiel': VerseType.tags[VerseType.Bridge], 'Zwischenspiel': VerseType.tags[VerseType.Bridge],
'Pre-Chorus': VerseType.tags[VerseType.PreChorus], 'Pre-Chorus': VerseType.tags[VerseType.PreChorus],
'Pre-Refrain': VerseType.tags[VerseType.PreChorus], 'Pre-Refrain': VerseType.tags[VerseType.PreChorus],
'Misc': VerseType.tags[VerseType.Other],
'Pre-Bridge': VerseType.tags[VerseType.Other], 'Pre-Bridge': VerseType.tags[VerseType.Other],
'Pre-Coda': VerseType.tags[VerseType.Other], 'Pre-Coda': VerseType.tags[VerseType.Other],
'Part': VerseType.tags[VerseType.Other],
'Teil': VerseType.tags[VerseType.Other],
'Unbekannt': VerseType.tags[VerseType.Other], 'Unbekannt': VerseType.tags[VerseType.Other],
'Unknown': VerseType.tags[VerseType.Other], 'Unknown': VerseType.tags[VerseType.Other],
'Unbenannt': VerseType.tags[VerseType.Other] 'Unbenannt': VerseType.tags[VerseType.Other],
'$$M=': VerseType.tags[VerseType.Other]
} }
@ -132,7 +136,8 @@ class SongBeamerImport(SongImport):
line = str(line).strip() line = str(line).strip()
if line.startswith('#') and not read_verses: if line.startswith('#') and not read_verses:
self.parseTags(line) self.parseTags(line)
elif line.startswith('---'): elif line.startswith('--'):
# --- and -- allowed for page-breaks (difference in Songbeamer only in printout)
if self.current_verse: if self.current_verse:
self.replace_html_tags() self.replace_html_tags()
self.add_verse(self.current_verse, self.current_verse_type) self.add_verse(self.current_verse, self.current_verse_type)
@ -282,4 +287,7 @@ class SongBeamerImport(SongImport):
if marks[1].isdigit(): if marks[1].isdigit():
self.current_verse_type += marks[1] self.current_verse_type += marks[1]
return True return True
elif marks[0].startswith('$$M='): # this verse-mark cannot be numbered
self.current_verse_type = SongBeamerTypes.MarkTypes['$$M=']
return True
return False return False

View File

@ -52,13 +52,13 @@ MIN_BLOCK_SIZE = 70
MAX_TYPO_SIZE = 3 MAX_TYPO_SIZE = 3
def songs_probably_equal(song1, song2): def songs_probably_equal(song_tupel):
""" """
Calculate and return whether two songs are probably equal. Calculate and return whether two songs are probably equal.
:param song1: The first song to compare. :param song_tupel: A tuple of two songs to compare.
:param song2: The second song to compare.
""" """
song1, song2 = song_tupel
if len(song1.search_lyrics) < len(song2.search_lyrics): if len(song1.search_lyrics) < len(song2.search_lyrics):
small = song1.search_lyrics small = song1.search_lyrics
large = song2.search_lyrics large = song2.search_lyrics
@ -75,18 +75,19 @@ def songs_probably_equal(song1, song2):
for element in diff_no_typos: for element in diff_no_typos:
if element[0] == "equal" and _op_length(element) >= MIN_BLOCK_SIZE: if element[0] == "equal" and _op_length(element) >= MIN_BLOCK_SIZE:
length_of_equal_blocks += _op_length(element) length_of_equal_blocks += _op_length(element)
if length_of_equal_blocks >= MIN_BLOCK_SIZE: if length_of_equal_blocks >= MIN_BLOCK_SIZE:
return True return song1, song2
# Check 2: Similarity based on the relative length of the longest equal block. # Check 2: Similarity based on the relative length of the longest equal block.
# Calculate the length of the largest equal block of the diff set. # Calculate the length of the largest equal block of the diff set.
length_of_longest_equal_block = 0 length_of_longest_equal_block = 0
for element in diff_no_typos: for element in diff_no_typos:
if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block: if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block:
length_of_longest_equal_block = _op_length(element) length_of_longest_equal_block = _op_length(element)
if length_of_equal_blocks >= MIN_BLOCK_SIZE or length_of_longest_equal_block > len(small) * 2 // 3: if length_of_longest_equal_block > len(small) * 2 // 3:
return True return song1, song2
# Both checks failed. We assume the songs are not equal. # Both checks failed. We assume the songs are not equal.
return False return None
def _op_length(opcode): def _op_length(opcode):

View File

@ -675,6 +675,7 @@ class OpenLyrics(object):
sxml = SongXML() sxml = SongXML()
verses = {} verses = {}
verse_def_list = [] verse_def_list = []
verse_order = self._text(properties.verseOrder).split(' ') if hasattr(properties, 'verseOrder') else []
try: try:
lyrics = song_xml.lyrics lyrics = song_xml.lyrics
except AttributeError: except AttributeError:
@ -717,13 +718,17 @@ class OpenLyrics(object):
else: else:
verses[(verse_tag, verse_number, lang, translit, verse_part)] = text verses[(verse_tag, verse_number, lang, translit, verse_part)] = text
verse_def_list.append((verse_tag, verse_number, lang, translit, verse_part)) verse_def_list.append((verse_tag, verse_number, lang, translit, verse_part))
# Update verse order when the verse name has changed
if verse_def != verse_tag + verse_number + verse_part:
for i in range(len(verse_order)):
if verse_order[i] == verse_def:
verse_order[i] = verse_tag + verse_number + verse_part
# We have to use a list to keep the order, as dicts are not sorted. # We have to use a list to keep the order, as dicts are not sorted.
for verse in verse_def_list: for verse in verse_def_list:
sxml.add_verse_to_lyrics(verse[0], verse[1], verses[verse], verse[2]) sxml.add_verse_to_lyrics(verse[0], verse[1], verses[verse], verse[2])
song_obj.lyrics = str(sxml.extract_xml(), 'utf-8') song_obj.lyrics = str(sxml.extract_xml(), 'utf-8')
# Process verse order # Process verse order
if hasattr(properties, 'verseOrder'): song_obj.verse_order = ' '.join(verse_order)
song_obj.verse_order = self._text(properties.verseOrder)
def _process_songbooks(self, properties, song): def _process_songbooks(self, properties, song):
""" """

View File

@ -132,7 +132,7 @@ class SongsPlugin(Plugin):
) )
import_menu.addAction(self.import_songselect_item) import_menu.addAction(self.import_songselect_item)
def add_export_menu_Item(self, export_menu): def add_export_menu_item(self, export_menu):
""" """
Give the Songs plugin the opportunity to add items to the **Export** menu. Give the Songs plugin the opportunity to add items to the **Export** menu.
@ -261,12 +261,12 @@ class SongsPlugin(Plugin):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin
""" """
## Name PluginList ## # Name PluginList
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
'singular': translate('SongsPlugin', 'Song', 'name singular'), 'singular': translate('SongsPlugin', 'Song', 'name singular'),
'plural': translate('SongsPlugin', 'Songs', 'name plural') 'plural': translate('SongsPlugin', 'Songs', 'name plural')
} }
## Name for MediaDockManager, SettingsManager ## # Name for MediaDockManager, SettingsManager
self.text_strings[StringContent.VisibleName] = { self.text_strings[StringContent.VisibleName] = {
'title': translate('SongsPlugin', 'Songs', 'container title') 'title': translate('SongsPlugin', 'Songs', 'container title')
} }

View File

@ -246,12 +246,12 @@ class SongUsagePlugin(Plugin):
""" """
Called to define all translatable texts of the plugin Called to define all translatable texts of the plugin
""" """
## Name PluginList ## # Name PluginList
self.text_strings[StringContent.Name] = { self.text_strings[StringContent.Name] = {
'singular': translate('SongUsagePlugin', 'SongUsage', 'name singular'), 'singular': translate('SongUsagePlugin', 'SongUsage', 'name singular'),
'plural': translate('SongUsagePlugin', 'SongUsage', 'name plural') 'plural': translate('SongUsagePlugin', 'SongUsage', 'name plural')
} }
## Name for MediaDockManager, SettingsManager ## # Name for MediaDockManager, SettingsManager
self.text_strings[StringContent.VisibleName] = { self.text_strings[StringContent.VisibleName] = {
'title': translate('SongUsagePlugin', 'SongUsage', 'container title') 'title': translate('SongUsagePlugin', 'SongUsage', 'container title')
} }

View File

@ -40,6 +40,7 @@ You can look up the token in the Branch-01-Pull job configuration or ask in IRC.
""" """
from optparse import OptionParser from optparse import OptionParser
import re
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import sys import sys
@ -49,6 +50,9 @@ from jenkins import Jenkins
JENKINS_URL = 'http://ci.openlp.org/' JENKINS_URL = 'http://ci.openlp.org/'
REPO_REGEX = r'(.*/+)(~.*)'
# Allows us to black list token. So when we change the token, we can display a proper message to the user.
OLD_TOKENS = []
class OpenLPJobs(object): class OpenLPJobs(object):
@ -59,9 +63,20 @@ class OpenLPJobs(object):
Branch_Functional = 'Branch-02-Functional-Tests' Branch_Functional = 'Branch-02-Functional-Tests'
Branch_Interface = 'Branch-03-Interface-Tests' Branch_Interface = 'Branch-03-Interface-Tests'
Branch_Windows = 'Branch-04-Windows_Tests' Branch_Windows = 'Branch-04-Windows_Tests'
Branch_PEP = 'Branch-05-Code-Analysis' Branch_PEP = 'Branch-05a-Code_Analysis'
Branch_Coverage = 'Branch-05b-Test_Coverage'
Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP] Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP, Branch_Coverage]
class Colour(object):
"""
This class holds values which can be used to print coloured text.
"""
RED_START = '\033[1;31m'
RED_END = '\033[1;m'
GREEN_START = '\033[1;32m'
GREEN_END = '\033[1;m'
class JenkinsTrigger(object): class JenkinsTrigger(object):
@ -79,14 +94,25 @@ class JenkinsTrigger(object):
""" """
Ask our jenkins server to build the "Branch-01-Pull" job. Ask our jenkins server to build the "Branch-01-Pull" job.
""" """
self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build({'BRANCH_NAME': self.repo_name}, token=self.token) bzr = Popen(('bzr', 'whoami'), stdout=PIPE, stderr=PIPE)
raw_output, error = bzr.communicate()
# We just want the name (not the email).
name = ' '.join(raw_output.decode().split()[:-1])
cause = 'Build triggered by %s (%s)' % (name, self.repo_name)
self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build(
{'BRANCH_NAME': self.repo_name, 'cause': cause}, token=self.token)
def print_output(self): def print_output(self):
""" """
Print the status information of the build tirggered. Print the status information of the build tirggered.
""" """
print("Add this to your merge proposal:") print('Add this to your merge proposal:')
print("--------------------------------") print('--------------------------------')
bzr = Popen(('bzr', 'revno'), stdout=PIPE, stderr=PIPE)
raw_output, error = bzr.communicate()
revno = raw_output.decode().strip()
print('%s (revision %s)' % (get_repo_name(), revno))
for job in OpenLPJobs.Jobs: for job in OpenLPJobs.Jobs:
self.__print_build_info(job) self.__print_build_info(job)
@ -107,17 +133,17 @@ class JenkinsTrigger(object):
""" """
job = self.jenkins_instance.job(job_name) job = self.jenkins_instance.job(job_name)
while job.info['inQueue']: while job.info['inQueue']:
# Give other processes the possibility to take over. Like Thread.yield(). time.sleep(1)
time.sleep(0)
build = job.last_build build = job.last_build
build.wait() build.wait()
result_string = build.info['result'] if build.info['result'] == 'SUCCESS':
# Make 'SUCCESS' green.
result_string = '%s%s%s' % (Colour.GREEN_START, build.info['result'], Colour.GREEN_END)
else:
# Make 'FAILURE' red.
result_string = '%s%s%s' % (Colour.RED_START, build.info['result'], Colour.RED_END)
url = build.info['url'] url = build.info['url']
print('[%s] %s' % (result_string, url)) print('[%s] %s' % (result_string, url))
# On failure open the browser.
#if result_string == "FAILURE":
# url += 'console'
# Popen(('xdg-open', url), stderr=PIPE)
def get_repo_name(): def get_repo_name():
@ -139,46 +165,41 @@ def get_repo_name():
for line in output_list: for line in output_list:
# Check if it is remote branch. # Check if it is remote branch.
if 'push branch' in line: if 'push branch' in line:
repo_name = line.replace('push branch: bzr+ssh://bazaar.launchpad.net/', 'lp:') match = re.match(REPO_REGEX, line)
break if match:
repo_name = 'lp:%s' % match.group(2)
break
elif 'checkout of branch' in line: elif 'checkout of branch' in line:
repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/', 'lp:') match = re.match(REPO_REGEX, line)
break if match:
repo_name = repo_name.strip('/') repo_name = 'lp:%s' % match.group(2)
break
# Did we find the branch name? return repo_name.strip('/')
if not repo_name:
for line in output_list:
# Check if the branch was pushed.
if 'Shared repository with trees (format: 2a)' in line:
print('Not a branch. cd to a branch.')
return
print('Not a branch. Have you pushed it to launchpad?')
return
return repo_name
def main(): def main():
usage = 'Usage: python %prog TOKEN [options]' usage = 'Usage: python %prog TOKEN [options]'
parser = OptionParser(usage=usage) parser = OptionParser(usage=usage)
parser.add_option('-d', '--disable-output', dest='enable_output', action="store_false", default=True, parser.add_option('-d', '--disable-output', dest='enable_output', action='store_false', default=True,
help='Disable output.') help='Disable output.')
parser.add_option('-b', '--open-browser', dest='open_browser', action="store_true", default=False, parser.add_option('-b', '--open-browser', dest='open_browser', action='store_true', default=False,
help='Opens the jenkins page in your browser.') help='Opens the jenkins page in your browser.')
#parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true",
# default=False, help='Opens the jenkins page in your browser in case a test fails.')
options, args = parser.parse_args(sys.argv) options, args = parser.parse_args(sys.argv)
if len(args) == 2: if len(args) == 2:
if not get_repo_name(): if not get_repo_name():
print('Not a branch. Have you pushed it to launchpad? Did you cd to the branch?')
return return
token = args[-1] token = args[-1]
if token in OLD_TOKENS:
print('Your token is not valid anymore. Get the most recent one.')
return
jenkins_trigger = JenkinsTrigger(token) jenkins_trigger = JenkinsTrigger(token)
try: try:
jenkins_trigger.trigger_build() jenkins_trigger.trigger_build()
except HTTPError as e: except HTTPError as e:
print("Wrong token.") print('Wrong token.')
return return
# Open the browser before printing the output. # Open the browser before printing the output.
if options.open_browser: if options.open_browser:

View File

@ -32,12 +32,13 @@ Functional tests to test the AppLocation class and related methods.
from unittest import TestCase from unittest import TestCase
from openlp.core.common import de_hump from openlp.core.common import de_hump, trace_error_handler
from tests.functional import MagicMock, patch
class TestInitFunctions(TestCase): class TestCommonFunctions(TestCase):
""" """
A test suite to test out various functions in the __init__ class. A test suite to test out various functions in the openlp.core.common module.
""" """
def de_hump_conversion_test(self): def de_hump_conversion_test(self):
""" """
@ -64,3 +65,19 @@ class TestInitFunctions(TestCase):
# THEN: the new string should be converted to python format # THEN: the new string should be converted to python format
self.assertTrue(new_string == "my_class", 'The class name should have been preserved') self.assertTrue(new_string == "my_class", 'The class name should have been preserved')
def trace_error_handler_test(self):
"""
Test the trace_error_handler() method
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.traceback') as mocked_traceback:
mocked_traceback.extract_stack.return_value = [('openlp.fake', 56, None, 'trace_error_handler_test')]
mocked_logger = MagicMock()
# WHEN: trace_error_handler() is called
trace_error_handler(mocked_logger)
# THEN: The mocked_logger.error() method should have been called with the correct parameters
mocked_logger.error.assert_called_with('OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test')

View File

@ -52,7 +52,7 @@ class TestRegistryProperties(TestCase, RegistryProperties):
# GIVEN an Empty Registry # GIVEN an Empty Registry
# WHEN there is no Application # WHEN there is no Application
# THEN the application should be none # THEN the application should be none
self.assertEquals(self.application, None, 'The application value should be None') self.assertEqual(self.application, None, 'The application value should be None')
def application_test(self): def application_test(self):
""" """
@ -63,4 +63,4 @@ class TestRegistryProperties(TestCase, RegistryProperties):
# WHEN the application is registered # WHEN the application is registered
Registry().register('application', application) Registry().register('application', application)
# THEN the application should be none # THEN the application should be none
self.assertEquals(self.application, application, 'The application value should match') self.assertEqual(self.application, application, 'The application value should match')

View File

@ -50,8 +50,8 @@ class TestDB(TestCase):
""" """
# GIVEN: Mocked out SQLAlchemy calls and return objects, and an in-memory SQLite database URL # GIVEN: Mocked out SQLAlchemy calls and return objects, and an in-memory SQLite database URL
with patch('openlp.core.lib.db.create_engine') as mocked_create_engine, \ with patch('openlp.core.lib.db.create_engine') as mocked_create_engine, \
patch('openlp.core.lib.db.MetaData') as MockedMetaData, \ patch('openlp.core.lib.db.MetaData') as MockedMetaData, \
patch('openlp.core.lib.db.sessionmaker') as mocked_sessionmaker, \ patch('openlp.core.lib.db.sessionmaker') as mocked_sessionmaker, \
patch('openlp.core.lib.db.scoped_session') as mocked_scoped_session: patch('openlp.core.lib.db.scoped_session') as mocked_scoped_session:
mocked_engine = MagicMock() mocked_engine = MagicMock()
mocked_metadata = MagicMock() mocked_metadata = MagicMock()

View File

@ -30,12 +30,16 @@
Package to test the openlp.core.ui package. Package to test the openlp.core.ui package.
""" """
import os import os
import time
from threading import Lock
from unittest import TestCase from unittest import TestCase
from PyQt4 import QtGui from PyQt4 import QtGui
from openlp.core.common import Registry from openlp.core.common import Registry
from openlp.core.lib import ImageManager, ScreenList from openlp.core.lib import ImageManager, ScreenList
from openlp.core.lib.imagemanager import Priority
from tests.functional import patch
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources'))
@ -51,6 +55,8 @@ class TestImageManager(TestCase, TestMixin):
self.get_application() self.get_application()
ScreenList.create(self.app.desktop()) ScreenList.create(self.app.desktop())
self.image_manager = ImageManager() self.image_manager = ImageManager()
self.lock = Lock()
self.sleep_time = 0.1
def tearDown(self): def tearDown(self):
""" """
@ -82,3 +88,87 @@ class TestImageManager(TestCase, TestMixin):
with self.assertRaises(KeyError) as context: with self.assertRaises(KeyError) as context:
self.image_manager.get_image(TEST_PATH, 'church1.jpg') self.image_manager.get_image(TEST_PATH, 'church1.jpg')
self.assertNotEquals(context.exception, '', 'KeyError exception should have been thrown for missing image') self.assertNotEquals(context.exception, '', 'KeyError exception should have been thrown for missing image')
def process_cache_test(self):
"""
Test the process_cache method
"""
with patch('openlp.core.lib.imagemanager.resize_image') as mocked_resize_image, \
patch('openlp.core.lib.imagemanager.image_to_byte') as mocked_image_to_byte:
# GIVEN: Mocked functions
mocked_resize_image.side_effect = self.mocked_resize_image
mocked_image_to_byte.side_effect = self.mocked_image_to_byte
image1 = 'church.jpg'
image2 = 'church2.jpg'
image3 = 'church3.jpg'
image4 = 'church4.jpg'
# WHEN: Add the images. Then get the lock (=queue can not be processed).
self.lock.acquire()
self.image_manager.add_image(TEST_PATH, image1, None)
self.image_manager.add_image(TEST_PATH, image2, None)
# THEN: All images have been added to the queue, and only the first image is not be in the list anymore, but
# is being processed (see mocked methods/functions).
# Note: Priority.Normal means, that the resize_image() was not completed yet (because afterwards the #
# priority is adjusted to Priority.Lowest).
self.assertEqual(self.get_image_priority(image1), Priority.Normal,
"image1's priority should be 'Priority.Normal'")
self.assertEqual(self.get_image_priority(image2), Priority.Normal,
"image2's priority should be 'Priority.Normal'")
# WHEN: Add more images.
self.image_manager.add_image(TEST_PATH, image3, None)
self.image_manager.add_image(TEST_PATH, image4, None)
# Allow the queue to process.
self.lock.release()
# Request some "data".
image_bytes = self.image_manager.get_image_bytes(TEST_PATH, image4)
image_object = self.image_manager.get_image(TEST_PATH, image3)
# Now the mocked methods/functions do not have to sleep anymore.
self.sleep_time = 0
# Wait for the queue to finish.
while not self.image_manager._conversion_queue.empty():
time.sleep(0.1)
# Because empty() is not reliable, wait a litte; just to make sure.
time.sleep(0.1)
# THEN: The images' priority reflect how they were processed.
self.assertEqual(self.image_manager._conversion_queue.qsize(), 0, "The queue should be empty.")
self.assertEqual(self.get_image_priority(image1), Priority.Lowest,
"The image should have not been requested (=Lowest)")
self.assertEqual(self.get_image_priority(image2), Priority.Lowest,
"The image should have not been requested (=Lowest)")
self.assertEqual(self.get_image_priority(image3), Priority.Low,
"Only the QImage should have been requested (=Low).")
self.assertEqual(self.get_image_priority(image4), Priority.Urgent,
"The image bytes should have been requested (=Urgent).")
def get_image_priority(self, image):
"""
This is a help method to get the priority of the given image out of the image_manager's cache.
NOTE: This requires, that the image has been added to the image manager using the *TEST_PATH*.
:param image: The name of the image. E. g. ``image1``
"""
return self.image_manager._cache[(TEST_PATH, image)].priority
def mocked_resize_image(self, *args):
"""
This is a mocked method, so that we can control the work flow of the image manager.
"""
self.lock.acquire()
self.lock.release()
# The sleep time is adjusted in the test case.
time.sleep(self.sleep_time)
return QtGui.QImage()
def mocked_image_to_byte(self, *args):
"""
This is a mocked method, so that we can control the work flow of the image manager.
"""
self.lock.acquire()
self.lock.release()
# The sleep time is adjusted in the test case.
time.sleep(self.sleep_time)
return ''

View File

@ -212,9 +212,9 @@ class TestPluginManager(TestCase):
# WHEN: We run hook_export_menu() # WHEN: We run hook_export_menu()
plugin_manager.hook_export_menu() plugin_manager.hook_export_menu()
# THEN: The add_export_menu_Item() method should not have been called # THEN: The add_export_menu_item() method should not have been called
self.assertEqual(0, mocked_plugin.add_export_menu_Item.call_count, self.assertEqual(0, mocked_plugin.add_export_menu_item.call_count,
'The add_export_menu_Item() method should not have been called.') 'The add_export_menu_item() method should not have been called.')
def hook_export_menu_with_active_plugin_test(self): def hook_export_menu_with_active_plugin_test(self):
""" """
@ -229,8 +229,8 @@ class TestPluginManager(TestCase):
# WHEN: We run hook_export_menu() # WHEN: We run hook_export_menu()
plugin_manager.hook_export_menu() plugin_manager.hook_export_menu()
# THEN: The add_export_menu_Item() method should have been called # THEN: The add_export_menu_item() method should have been called
mocked_plugin.add_export_menu_Item.assert_called_with(self.mocked_main_window.file_export_menu) mocked_plugin.add_export_menu_item.assert_called_with(self.mocked_main_window.file_export_menu)
def hook_upgrade_plugin_settings_with_disabled_plugin_test(self): def hook_upgrade_plugin_settings_with_disabled_plugin_test(self):
""" """
@ -264,7 +264,7 @@ class TestPluginManager(TestCase):
# WHEN: We run hook_upgrade_plugin_settings() # WHEN: We run hook_upgrade_plugin_settings()
plugin_manager.hook_upgrade_plugin_settings(settings) plugin_manager.hook_upgrade_plugin_settings(settings)
# THEN: The add_export_menu_Item() method should have been called # THEN: The add_export_menu_item() method should have been called
mocked_plugin.upgrade_settings.assert_called_with(settings) mocked_plugin.upgrade_settings.assert_called_with(settings)
def hook_tools_menu_with_disabled_plugin_test(self): def hook_tools_menu_with_disabled_plugin_test(self):

View File

@ -49,7 +49,7 @@ class TestRenderer(TestCase):
def setUp(self): def setUp(self):
""" """
Set up the components need for all tests. Set up the components need for all tests
""" """
# Mocked out desktop object # Mocked out desktop object
self.desktop = MagicMock() self.desktop = MagicMock()
@ -67,7 +67,7 @@ class TestRenderer(TestCase):
def initial_renderer_test(self): def initial_renderer_test(self):
""" """
Test the initial renderer state . Test the initial renderer state
""" """
# GIVEN: A new renderer instance. # GIVEN: A new renderer instance.
renderer = Renderer() renderer = Renderer()
@ -77,7 +77,7 @@ class TestRenderer(TestCase):
def default_screen_layout_test(self): def default_screen_layout_test(self):
""" """
Test the default layout calculations. Test the default layout calculations
""" """
# GIVEN: A new renderer instance. # GIVEN: A new renderer instance.
renderer = Renderer() renderer = Renderer()
@ -87,3 +87,35 @@ class TestRenderer(TestCase):
self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller') self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller')
self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller') self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller')
self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller') self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller')
def _get_start_tags_test(self):
"""
Test the _get_start_tags() method
"""
# GIVEN: A new renderer instance. Broken raw_text (missing closing tags).
renderer = Renderer()
given_raw_text = '{st}{r}Text text text'
expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}',
'<strong><span style="-webkit-text-fill-color:red">')
# WHEN:
result = renderer._get_start_tags(given_raw_text)
# THEN: Check if the correct tuple is returned.
self.assertEqual(result, expected_tuple), 'A tuple should be returned containing the text with correct ' \
'tags, the opening tags, and the opening html tags.'
def _word_split_test(self):
"""
Test the _word_split() method
"""
# GIVEN: A line of text
renderer = Renderer()
given_line = 'beginning asdf \n end asdf'
expected_words = ['beginning', 'asdf', 'end', 'asdf']
# WHEN: Split the line
result_words = renderer._words_split(given_line)
# THEN: The word lists should be the same.
self.assertListEqual(result_words, expected_words)

View File

@ -81,3 +81,92 @@ class TestUi(TestCase):
self.assertIsInstance(btnbox, QtGui.QDialogButtonBox) self.assertIsInstance(btnbox, QtGui.QDialogButtonBox)
self.assertEqual(1, len(btnbox.buttons())) self.assertEqual(1, len(btnbox.buttons()))
self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])) self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0]))
def test_create_button(self):
"""
Test creating a button
"""
# GIVEN: A dialog
dialog = QtGui.QDialog()
# WHEN: We create the button
btn = create_button(dialog, 'my_btn')
# THEN: We should get a button with a name
self.assertIsInstance(btn, QtGui.QPushButton)
self.assertEqual('my_btn', btn.objectName())
self.assertTrue(btn.isEnabled())
# WHEN: We create a button with some attributes
btn = create_button(dialog, 'my_btn', text='Hello', tooltip='How are you?', enabled=False)
# THEN: We should get a button with those attributes
self.assertIsInstance(btn, QtGui.QPushButton)
self.assertEqual('Hello', btn.text())
self.assertEqual('How are you?', btn.toolTip())
self.assertFalse(btn.isEnabled())
# WHEN: We create a toolbutton
btn = create_button(dialog, 'my_btn', btn_class='toolbutton')
# THEN: We should get a toolbutton
self.assertIsInstance(btn, QtGui.QToolButton)
self.assertEqual('my_btn', btn.objectName())
self.assertTrue(btn.isEnabled())
def test_create_valign_selection_widgets(self):
"""
Test creating a combo box for valign selection
"""
# GIVEN: A dialog
dialog = QtGui.QDialog()
# WHEN: We create the widgets
label, combo = create_valign_selection_widgets(dialog)
# THEN: We should get a label and a combobox.
self.assertEqual(translate('OpenLP.Ui', '&Vertical Align:'), label.text())
self.assertIsInstance(combo, QtGui.QComboBox)
self.assertEqual(combo, label.buddy())
for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]:
self.assertTrue(combo.findText(text) >= 0)
def test_create_horizontal_adjusting_combo_box(self):
"""
Test creating a horizontal adjusting combo box
"""
# GIVEN: A dialog
dialog = QtGui.QDialog()
# WHEN: We create the combobox
combo = create_horizontal_adjusting_combo_box(dialog, 'combo1')
# THEN: We should get a ComboBox
self.assertIsInstance(combo, QtGui.QComboBox)
self.assertEqual('combo1', combo.objectName())
self.assertEqual(QtGui.QComboBox.AdjustToMinimumContentsLength, combo.sizeAdjustPolicy())
def test_create_action(self):
"""
Test creating an action
"""
# GIVEN: A dialog
dialog = QtGui.QDialog()
# WHEN: We create an action
action = create_action(dialog, 'my_action')
# THEN: We should get a QAction
self.assertIsInstance(action, QtGui.QAction)
self.assertEqual('my_action', action.objectName())
# WHEN: We create an action with some properties
action = create_action(dialog, 'my_action', text='my text', icon=':/wizards/wizard_firsttime.bmp',
tooltip='my tooltip', statustip='my statustip')
# THEN: These properties should be set
self.assertIsInstance(action, QtGui.QAction)
self.assertEqual('my text', action.text())
self.assertIsInstance(action.icon(), QtGui.QIcon)
self.assertEqual('my tooltip', action.toolTip())
self.assertEqual('my statustip', action.statusTip())

View File

@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
"""
Package to test the openlp.core.ui.firsttimeform package.
"""
from unittest import TestCase
from tests.functional import MagicMock
from tests.helpers.testmixin import TestMixin
from openlp.core.common import Registry
from openlp.core.ui.firsttimeform import FirstTimeForm
class TestFirstTimeForm(TestCase, TestMixin):
def setUp(self):
screens = MagicMock()
self.get_application()
Registry.create()
Registry().register('application', self.app)
self.first_time_form = FirstTimeForm(screens)
def test_access_to_config(self):
"""
Test if we can access the First Time Form's config file
"""
# GIVEN A new First Time Form instance.
# WHEN The default First Time Form is built.
# THEN The First Time Form web configuration file should be accessable.
self.assertTrue(self.first_time_form.web_access,
'First Time Wizard\'s web configuration file should be available')
def test_parsable_config(self):
"""
Test if the First Time Form's config file is parsable
"""
# GIVEN A new First Time Form instance.
# WHEN The default First Time Form is built.
# THEN The First Time Form web configuration file should be parsable
self.assertTrue(self.first_time_form.songs_url,
'First Time Wizard\'s web configuration file should be parsable')

View File

@ -79,3 +79,31 @@ class TestMainDisplay(TestCase):
# THEN: The controller should not be a live controller. # THEN: The controller should not be a live controller.
self.assertEqual(main_display.is_live, True, 'The main display should be a live controller') self.assertEqual(main_display.is_live, True, 'The main display should be a live controller')
def set_transparency_test(self):
"""
Test creating an instance of the MainDisplay class
"""
# GIVEN: get an instance of MainDisplay
display = MagicMock()
main_display = MainDisplay(display)
# WHEN: We enable transparency
main_display.set_transparency(True)
# THEN: There should be a Stylesheet
self.assertEqual('QGraphicsView {background: transparent; border: 0px;}', main_display.styleSheet(),
'MainDisplay instance should be transparent')
self.assertFalse(main_display.autoFillBackground(),
'MainDisplay instance should be without background auto fill')
self.assertTrue(main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground),
'MainDisplay hasnt translucent background')
# WHEN: We disable transparency
main_display.set_transparency(False)
# THEN: The Stylesheet should be empty
self.assertEqual('QGraphicsView {}', main_display.styleSheet(),
'MainDisplay instance should not be transparent')
self.assertFalse(main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground),
'MainDisplay hasnt translucent background')

View File

@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
"""
Package to test openlp.core.ui.mainwindow package.
"""
import os
from unittest import TestCase
from openlp.core.ui.mainwindow import MainWindow
from openlp.core.common.registry import Registry
from tests.utils.constants import TEST_RESOURCES_PATH
from tests.helpers.testmixin import TestMixin
from tests.functional import MagicMock, patch
class TestMainWindow(TestCase, TestMixin):
def setUp(self):
Registry.create()
self.registry = Registry()
self.get_application()
# Mock cursor busy/normal methods.
self.app.set_busy_cursor = MagicMock()
self.app.set_normal_cursor = MagicMock()
self.app.args = []
Registry().register('application', self.app)
# Mock classes and methods used by mainwindow.
with patch('openlp.core.ui.mainwindow.SettingsForm') as mocked_settings_form, \
patch('openlp.core.ui.mainwindow.ImageManager') as mocked_image_manager, \
patch('openlp.core.ui.mainwindow.LiveController') as mocked_live_controller, \
patch('openlp.core.ui.mainwindow.PreviewController') as mocked_preview_controller, \
patch('openlp.core.ui.mainwindow.OpenLPDockWidget') as mocked_dock_widget, \
patch('openlp.core.ui.mainwindow.QtGui.QToolBox') as mocked_q_tool_box_class, \
patch('openlp.core.ui.mainwindow.QtGui.QMainWindow.addDockWidget') as mocked_add_dock_method, \
patch('openlp.core.ui.mainwindow.ThemeManager') as mocked_theme_manager, \
patch('openlp.core.ui.mainwindow.Renderer') as mocked_renderer:
self.main_window = MainWindow()
def tearDown(self):
del self.main_window
def cmd_line_file_test(self):
"""
Test that passing a service file from the command line loads the service.
"""
# GIVEN a service as an argument to openlp
service = os.path.join(TEST_RESOURCES_PATH, 'service', 'test.osz')
self.main_window.arguments = [service]
with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path:
# WHEN the argument is processed
self.main_window.open_cmd_line_files()
# THEN the service from the arguments is loaded
mocked_load_path.assert_called_with(service), 'load_path should have been called with the service\'s path'
def cmd_line_arg_test(self):
"""
Test that passing a non service file does nothing.
"""
# GIVEN a non service file as an argument to openlp
service = os.path.join('openlp.py')
self.main_window.arguments = [service]
with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path:
# WHEN the argument is processed
self.main_window.open_cmd_line_files()
# THEN the file should not be opened
assert not mocked_load_path.called, 'load_path should not have been called'

View File

@ -180,4 +180,4 @@ class TestBSExtract(TestCase):
'http://m.bibleserver.com/overlay/selectBook?translation=NIV') 'http://m.bibleserver.com/overlay/selectBook?translation=NIV')
self.assertFalse(self.mock_log.error.called, 'log.error should not have been called') self.assertFalse(self.mock_log.error.called, 'log.error should not have been called')
self.assertFalse(self.mock_send_error_message.called, 'send_error_message should not have been called') self.assertFalse(self.mock_send_error_message.called, 'send_error_message should not have been called')
self.assertEquals(result, ['Genesis', 'Leviticus']) self.assertEqual(result, ['Genesis', 'Leviticus'])

View File

@ -139,10 +139,10 @@ class TestEasyWorshipSongImport(TestCase):
# THEN: # THEN:
self.assertIsNotNone(field_desc_entry, 'Import should not be none') self.assertIsNotNone(field_desc_entry, 'Import should not be none')
self.assertEquals(field_desc_entry.name, name, 'FieldDescEntry.name should be the same as the name argument') self.assertEqual(field_desc_entry.name, name, 'FieldDescEntry.name should be the same as the name argument')
self.assertEquals(field_desc_entry.field_type, field_type, self.assertEqual(field_desc_entry.field_type, field_type,
'FieldDescEntry.type should be the same as the type argument') 'FieldDescEntry.type should be the same as the type argument')
self.assertEquals(field_desc_entry.size, size, 'FieldDescEntry.size should be the same as the size argument') self.assertEqual(field_desc_entry.size, size, 'FieldDescEntry.size should be the same as the size argument')
def create_importer_test(self): def create_importer_test(self):
""" """
@ -174,7 +174,7 @@ class TestEasyWorshipSongImport(TestCase):
for field_name in existing_fields: for field_name in existing_fields:
# THEN: The item corresponding the index returned should have the same name attribute # THEN: The item corresponding the index returned should have the same name attribute
self.assertEquals(importer.field_descriptions[importer.find_field(field_name)].name, field_name) self.assertEqual(importer.field_descriptions[importer.find_field(field_name)].name, field_name)
def find_non_existing_field_test(self): def find_non_existing_field_test(self):
""" """
@ -230,7 +230,7 @@ class TestEasyWorshipSongImport(TestCase):
return_value = importer.get_field(field_index) return_value = importer.get_field(field_index)
# THEN: get_field should return the known results # THEN: get_field should return the known results
self.assertEquals(return_value, result, self.assertEqual(return_value, result,
'get_field should return "%s" when called with "%s"' % 'get_field should return "%s" when called with "%s"' %
(result, TEST_FIELDS[field_index])) (result, TEST_FIELDS[field_index]))
@ -257,7 +257,7 @@ class TestEasyWorshipSongImport(TestCase):
get_field_seek_calls = test_results[2]['seek'] get_field_seek_calls = test_results[2]['seek']
# THEN: get_field should return the appropriate value with the appropriate mocked objects being called # THEN: get_field should return the appropriate value with the appropriate mocked objects being called
self.assertEquals(importer.get_field(field_index), get_field_result) self.assertEqual(importer.get_field(field_index), get_field_result)
for call in get_field_read_calls: for call in get_field_read_calls:
mocked_memo_file.read.assert_any_call(call) mocked_memo_file.read.assert_any_call(call)
for call in get_field_seek_calls: for call in get_field_seek_calls:
@ -403,11 +403,11 @@ class TestEasyWorshipSongImport(TestCase):
if song_copyright: if song_copyright:
self.assertEqual(importer.copyright, song_copyright) self.assertEqual(importer.copyright, song_copyright)
if ccli_number: if ccli_number:
self.assertEquals(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s'
% (title, ccli_number)) % (title, ccli_number))
for verse_text, verse_tag in add_verse_calls: for verse_text, verse_tag in add_verse_calls:
mocked_add_verse.assert_any_call(verse_text, verse_tag) mocked_add_verse.assert_any_call(verse_text, verse_tag)
if verse_order_list: if verse_order_list:
self.assertEquals(importer.verse_order_list, verse_order_list, self.assertEqual(importer.verse_order_list, verse_order_list,
'verse_order_list for %s should be %s' % (title, verse_order_list)) 'verse_order_list for %s should be %s' % (title, verse_order_list))
mocked_finish.assert_called_with() mocked_finish.assert_called_with()

View File

@ -96,10 +96,10 @@ class TestLib(TestCase):
self.song2.search_lyrics = self.full_lyrics self.song2.search_lyrics = self.full_lyrics
# WHEN: We compare those songs for equality. # WHEN: We compare those songs for equality.
result = songs_probably_equal(self.song1, self.song2) result = songs_probably_equal((self.song1, self.song2))
# THEN: The result should be True. # THEN: The result should be a tuple..
assert result is True, 'The result should be True' assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
def songs_probably_equal_short_song_test(self): def songs_probably_equal_short_song_test(self):
""" """
@ -110,10 +110,10 @@ class TestLib(TestCase):
self.song2.search_lyrics = self.short_lyrics self.song2.search_lyrics = self.short_lyrics
# WHEN: We compare those songs for equality. # WHEN: We compare those songs for equality.
result = songs_probably_equal(self.song1, self.song2) result = songs_probably_equal((self.song1, self.song2))
# THEN: The result should be True. # THEN: The result should be a tuple..
assert result is True, 'The result should be True' assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
def songs_probably_equal_error_song_test(self): def songs_probably_equal_error_song_test(self):
""" """
@ -124,10 +124,11 @@ class TestLib(TestCase):
self.song2.search_lyrics = self.error_lyrics self.song2.search_lyrics = self.error_lyrics
# WHEN: We compare those songs for equality. # WHEN: We compare those songs for equality.
result = songs_probably_equal(self.song1, self.song2) result = songs_probably_equal((self.song1, self.song2))
# THEN: The result should be a tuple of songs..
assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
# THEN: The result should be True.
assert result is True, 'The result should be True'
def songs_probably_equal_different_song_test(self): def songs_probably_equal_different_song_test(self):
""" """
@ -138,10 +139,10 @@ class TestLib(TestCase):
self.song2.search_lyrics = self.different_lyrics self.song2.search_lyrics = self.different_lyrics
# WHEN: We compare those songs for equality. # WHEN: We compare those songs for equality.
result = songs_probably_equal(self.song1, self.song2) result = songs_probably_equal((self.song1, self.song2))
# THEN: The result should be False. # THEN: The result should be None.
assert result is False, 'The result should be False' assert result is None, 'The result should be None'
def remove_typos_beginning_test(self): def remove_typos_beginning_test(self):
""" """

View File

@ -35,6 +35,7 @@ from unittest import TestCase
from tests.functional import MagicMock, patch from tests.functional import MagicMock, patch
from openlp.plugins.songs.lib.songbeamerimport import SongBeamerImport from openlp.plugins.songs.lib.songbeamerimport import SongBeamerImport
from openlp.plugins.songs.lib import VerseType
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', '..', 'resources', 'songbeamersongs')) '..', '..', '..', 'resources', 'songbeamersongs'))
@ -89,7 +90,7 @@ class TestSongBeamerImport(TestCase):
# THEN: do_import should return none and the progress bar maximum should not be set. # 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.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list')
self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False, self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False,
'setMaxium on import_wizard.progress_bar should not have been called') 'setMaxium on import_wizard.progress_bar should not have been called')
def valid_import_source_test(self): def valid_import_source_test(self):
@ -143,13 +144,90 @@ class TestSongBeamerImport(TestCase):
# THEN: do_import should return none, the song data should be as expected, and finish should have been # THEN: do_import should return none, the song data should be as expected, and finish should have been
# called. # called.
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (song_file, title)) self.assertEqual(importer.title, title, 'title for %s should be "%s"' % (song_file, title))
for verse_text, verse_tag in add_verse_calls: for verse_text, verse_tag in add_verse_calls:
mocked_add_verse.assert_any_call(verse_text, verse_tag) mocked_add_verse.assert_any_call(verse_text, verse_tag)
if song_book_name: if song_book_name:
self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' %
(song_file, song_book_name)) (song_file, song_book_name))
if song_number: if song_number:
self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' % self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' %
(song_file, song_number)) (song_file, song_number))
mocked_finish.assert_called_with() mocked_finish.assert_called_with()
def check_verse_marks_test(self):
"""
Tests different lines to see if a verse mark is detected or not
"""
# GIVEN: line with unnumbered verse-type
line = 'Refrain'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back true and c as self.current_verse_type
self.assertTrue(result, 'Versemark for <Refrain> should be found, value true')
self.assertEqual(self.current_verse_type, 'c', '<Refrain> should be interpreted as <c>')
# GIVEN: line with unnumbered verse-type and trailing space
line = 'Refrain '
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back true and c as self.current_verse_type
self.assertTrue(result, 'Versemark for <Refrain > should be found, value true')
self.assertEqual(self.current_verse_type, 'c', '<Refrain > should be interpreted as <c>')
# GIVEN: line with numbered verse-type
line = 'Verse 1'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back true and v1 as self.current_verse_type
self.assertTrue(result, 'Versemark for <Verse 1> should be found, value true')
self.assertEqual(self.current_verse_type, 'v1', u'<Verse 1> should be interpreted as <v1>')
# GIVEN: line with special unnumbered verse-mark (used in Songbeamer to allow usage of non-supported tags)
line = '$$M=special'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back true and o as self.current_verse_type
self.assertTrue(result, 'Versemark for <$$M=special> should be found, value true')
self.assertEqual(self.current_verse_type, 'o', u'<$$M=special> should be interpreted as <o>')
# GIVEN: line with song-text with 3 words
line = 'Jesus my saviour'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back false and none as self.current_verse_type
self.assertFalse(result, 'No versemark for <Jesus my saviour> should be found, value false')
self.assertIsNone(self.current_verse_type, '<Jesus my saviour> should be interpreted as none versemark')
# GIVEN: line with song-text with 2 words
line = 'Praise him'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back false and none as self.current_verse_type
self.assertFalse(result, 'No versemark for <Praise him> should be found, value false')
self.assertIsNone(self.current_verse_type, '<Praise him> should be interpreted as none versemark')
# GIVEN: line with only a space (could occur, nothing regular)
line = ' '
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back false and none as self.current_verse_type
self.assertFalse(result, 'No versemark for < > should be found, value false')
self.assertIsNone(self.current_verse_type, '< > should be interpreted as none versemark')
# GIVEN: blank line (could occur, nothing regular)
line = ''
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back false and none as self.current_verse_type
self.assertFalse(result, 'No versemark for <> should be found, value false')
self.assertIsNone(self.current_verse_type, '<> should be interpreted as none versemark')

View File

@ -95,7 +95,7 @@ class TestSongShowPlusImport(TestCase):
# THEN: do_import should return none and the progress bar maximum should not be set. # 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.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list')
self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False, self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False,
'setMaximum on import_wizard.progress_bar should not have been called') 'setMaximum on import_wizard.progress_bar should not have been called')
def valid_import_source_test(self): def valid_import_source_test(self):
@ -143,7 +143,7 @@ class TestSongShowPlusImport(TestCase):
# THEN: The returned value should should correlate with the input arguments # THEN: The returned value should should correlate with the input arguments
for original_tag, openlp_tag in test_values: for original_tag, openlp_tag in test_values:
self.assertEquals(importer.to_openlp_verse_tag(original_tag), openlp_tag, self.assertEqual(importer.to_openlp_verse_tag(original_tag), openlp_tag,
'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' %
(openlp_tag, original_tag)) (openlp_tag, original_tag))
@ -172,6 +172,6 @@ class TestSongShowPlusImport(TestCase):
# THEN: The returned value should should correlate with the input arguments # THEN: The returned value should should correlate with the input arguments
for original_tag, openlp_tag in test_values: for original_tag, openlp_tag in test_values:
self.assertEquals(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag, self.assertEqual(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag,
'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' %
(openlp_tag, original_tag)) (openlp_tag, original_tag))

View File

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# 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 ZionWorx song importer.
"""
from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.songs.lib.zionworximport import ZionWorxImport
from openlp.plugins.songs.lib.songimport import SongImport
class TestZionWorxImport(TestCase):
"""
Test the functions in the :mod:`zionworximport` module.
"""
def create_importer_test(self):
"""
Test creating an instance of the ZionWorx file importer
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = ZionWorxImport(mocked_manager, filenames=[])
# THEN: The importer should be an instance of SongImport
self.assertIsInstance(importer, SongImport)

View File

@ -110,29 +110,29 @@ class SongImportTestHelper(TestCase):
# THEN: do_import should return none, the song data should be as expected, and finish should have been # THEN: do_import should return none, the song data should be as expected, and finish should have been
# called. # called.
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title)) self.assertEqual(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title))
for author in author_calls: for author in author_calls:
self.mocked_parse_author.assert_any_call(author) self.mocked_parse_author.assert_any_call(author)
if song_copyright: if song_copyright:
self.mocked_add_copyright.assert_called_with(song_copyright) self.mocked_add_copyright.assert_called_with(song_copyright)
if ccli_number: if ccli_number:
self.assertEquals(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' % self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' %
(source_file_name, ccli_number)) (source_file_name, ccli_number))
for verse_text, verse_tag in add_verse_calls: for verse_text, verse_tag in add_verse_calls:
self.mocked_add_verse.assert_any_call(verse_text, verse_tag) self.mocked_add_verse.assert_any_call(verse_text, verse_tag)
if topics: if topics:
self.assertEquals(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics)) self.assertEqual(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics))
if comments: if comments:
self.assertEquals(importer.comments, comments, 'comments for %s should be "%s"' % self.assertEqual(importer.comments, comments, 'comments for %s should be "%s"' %
(source_file_name, comments)) (source_file_name, comments))
if song_book_name: if song_book_name:
self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' %
(source_file_name, song_book_name)) (source_file_name, song_book_name))
if song_number: if song_number:
self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' % self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' %
(source_file_name, song_number)) (source_file_name, song_number))
if verse_order_list: if verse_order_list:
self.assertEquals(importer.verse_order_list, [], 'verse_order_list for %s should be %s' % self.assertEqual(importer.verse_order_list, [], 'verse_order_list for %s should be %s' %
(source_file_name, verse_order_list)) (source_file_name, verse_order_list))
self.mocked_finish.assert_called_with() self.mocked_finish.assert_called_with()

View File

@ -313,6 +313,7 @@ class TestServiceManager(TestCase, TestMixin):
self.service_manager.notes_action.setVisible = MagicMock() self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock() self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock() self.service_manager.auto_start_action.setVisible = MagicMock()
self.service_manager.rename_action.setVisible = MagicMock()
# WHEN: Show the context menu. # WHEN: Show the context menu.
self.service_manager.context_menu(q_point) self.service_manager.context_menu(q_point)
@ -329,6 +330,8 @@ class TestServiceManager(TestCase, TestMixin):
'The action should be set invisible.' 'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_with(True), \ self.service_manager.auto_start_action.setVisible.assert_called_with(True), \
'The action should be set visible.' 'The action should be set visible.'
self.service_manager.rename_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def click_on_new_service_test(self): def click_on_new_service_test(self):
""" """

View File

@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
"""
Test the openlp.core.ui.splashscreen class.
"""
from unittest import TestCase
from PyQt4 import QtGui
from openlp.core.ui import SplashScreen
from tests.helpers.testmixin import TestMixin
class TestSplashScreen(TestCase, TestMixin):
def setUp(self):
self.get_application()
self.main_window = QtGui.QMainWindow()
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
del self.app
del self.main_window
def setupUi_test(self):
"""
Test if the setupUi method....
"""
# GIVEN: A splash screen instance.
splash = SplashScreen()
# THEN: Check if the splash has a setupUi method.
assert hasattr(splash, 'setupUi'), 'The Splash Screen should have a setupUi() method.'

View File

@ -83,7 +83,7 @@ class TestBibleManager(TestCase, TestMixin):
# WHEN asking to parse the bible reference # WHEN asking to parse the bible reference
results = parse_reference('1 Timothy 1', self.manager.db_cache['tests'], MagicMock(), 54) results = parse_reference('1 Timothy 1', self.manager.db_cache['tests'], MagicMock(), 54)
# THEN a verse array should be returned # THEN a verse array should be returned
self.assertEquals([(54, 1, 1, -1)], results, "The bible verses should matches the expected results") self.assertEqual([(54, 1, 1, -1)], results, "The bible verses should matches the expected results")
def parse_reference_two_test(self): def parse_reference_two_test(self):
""" """
@ -93,7 +93,7 @@ class TestBibleManager(TestCase, TestMixin):
# WHEN asking to parse the bible reference # WHEN asking to parse the bible reference
results = parse_reference('1 Timothy 1:1-2', self.manager.db_cache['tests'], MagicMock(), 54) results = parse_reference('1 Timothy 1:1-2', self.manager.db_cache['tests'], MagicMock(), 54)
# THEN a verse array should be returned # THEN a verse array should be returned
self.assertEquals([(54, 1, 1, 2)], results, "The bible verses should matches the expected results") self.assertEqual([(54, 1, 1, 2)], results, "The bible verses should matches the expected results")
def parse_reference_three_test(self): def parse_reference_three_test(self):
""" """
@ -103,5 +103,5 @@ class TestBibleManager(TestCase, TestMixin):
# WHEN asking to parse the bible reference # WHEN asking to parse the bible reference
results = parse_reference('1 Timothy 1:1-2:1', self.manager.db_cache['tests'], MagicMock(), 54) results = parse_reference('1 Timothy 1:1-2:1', self.manager.db_cache['tests'], MagicMock(), 54)
# THEN a verse array should be returned # THEN a verse array should be returned
self.assertEquals([(54, 1, 1, -1), (54, 2, 1, 1)], results, "The bible verses should matches the expected " self.assertEqual([(54, 1, 1, -1), (54, 2, 1, 1)], results, "The bible verses should matches the expected "
"results") "results")

BIN
tests/resources/church2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

BIN
tests/resources/church3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

BIN
tests/resources/church4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

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-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
"""
Package to test for proper bzr tags.
"""
import os
from unittest import TestCase
from subprocess import Popen, PIPE
TAGS = [
['1.9.0', '1'],
['1.9.1', '775'],
['1.9.2', '890'],
['1.9.3', '1063'],
['1.9.4', '1196'],
['1.9.5', '1421'],
['1.9.6', '1657'],
['1.9.7', '1761'],
['1.9.8', '1856'],
['1.9.9', '1917'],
['1.9.10', '2003'],
['1.9.11', '2039'],
['1.9.12', '2063'],
['2.0', '2118'],
['2.0.1', '?'],
['2.0.2', '?'],
['2.0.3', '?'],
['2.1.0', '2119']
]
class TestBzrTags(TestCase):
def bzr_tags_test(self):
"""
Test for proper bzr tags
"""
# GIVEN: A bzr branch
path = os.path.dirname(__file__)
# WHEN getting the branches tags
bzr = Popen(('bzr', 'tags', '--directory=' + path), stdout=PIPE)
stdout = bzr.communicate()[0]
tags = [line.decode('utf-8').split() for line in stdout.splitlines()]
# THEN the tags should match the accepted tags
self.assertEqual(TAGS, tags, 'List of tags should match')