Merged with trunk and fixed tests

This commit is contained in:
Tomas Groth 2014-07-11 13:35:56 +02:00
commit 406cc506c1
199 changed files with 5348 additions and 1280 deletions

View File

@ -6,6 +6,8 @@
*.ropeproject
*.e4*
.eric4project
.komodotools
*.komodoproject
list
openlp.org 2.0.e4*
documentation/build/html
@ -30,3 +32,4 @@ tests.kdev4
*.orig
__pycache__
*.dll
.directory

View File

@ -5,6 +5,8 @@ recursive-include openlp *.html
recursive-include openlp *.js
recursive-include openlp *.css
recursive-include openlp *.png
recursive-include openlp *.ps
recursive-include openlp *.json
recursive-include documentation *
recursive-include resources *
recursive-include scripts *

View File

@ -1,16 +1,15 @@
OpenLP 2.0
==========
OpenLP
======
You're probably reading this because you've just downloaded the source code for
OpenLP 2.0. If you are looking for the installer file, please go to the download
OpenLP. If you are looking for the installer file, please go to the download
page on the web site::
http://openlp.org/en/download.html
http://openlp.org/download
If you're looking for how to contribute to OpenLP, then please look at the
OpenLP wiki::
http://wiki.openlp.org/
Thanks for downloading OpenLP 2.0!
Thanks for downloading OpenLP!

View File

@ -72,15 +72,15 @@ class AppLocation(object):
:param dir_type: The directory type you want, for instance the data directory. Default *AppLocation.AppDir*
"""
if dir_type == AppLocation.AppDir:
return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0])
return get_frozen_path(os.path.abspath(os.path.dirname(sys.argv[0])), os.path.dirname(openlp.__file__))
elif dir_type == AppLocation.PluginsDir:
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
app_path = os.path.abspath(os.path.dirname(sys.argv[0]))
return get_frozen_path(os.path.join(app_path, 'plugins'),
os.path.join(os.path.split(openlp.__file__)[0], 'plugins'))
os.path.join(os.path.dirname(openlp.__file__), 'plugins'))
elif dir_type == AppLocation.VersionDir:
return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0])
return get_frozen_path(os.path.abspath(os.path.dirname(sys.argv[0])), os.path.dirname(openlp.__file__))
elif dir_type == AppLocation.LanguageDir:
app_path = get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), _get_os_dir_path(dir_type))
app_path = get_frozen_path(os.path.abspath(os.path.dirname(sys.argv[0])), _get_os_dir_path(dir_type))
return os.path.join(app_path, 'i18n')
elif dir_type == AppLocation.DataDir and AppLocation.BaseDir:
return os.path.join(AppLocation.BaseDir, 'data')
@ -110,7 +110,7 @@ class AppLocation(object):
:param extension:
Defaults to *None*. The extension to search for. For example::
u'.png'
'.png'
"""
path = AppLocation.get_data_path()
if section:
@ -140,18 +140,22 @@ def _get_os_dir_path(dir_type):
"""
Return a path based on which OS and environment we are running in.
"""
# If running from source, return the language directory from the source directory
if dir_type == AppLocation.LanguageDir:
directory = os.path.abspath(os.path.join(os.path.dirname(openlp.__file__), '..', 'resources'))
if os.path.exists(directory):
return directory
if sys.platform == 'win32':
if dir_type == AppLocation.DataDir:
return os.path.join(str(os.getenv('APPDATA')), 'openlp', 'data')
elif dir_type == AppLocation.LanguageDir:
return os.path.split(openlp.__file__)[0]
return os.path.dirname(openlp.__file__)
return os.path.join(str(os.getenv('APPDATA')), 'openlp')
elif sys.platform == 'darwin':
if dir_type == AppLocation.DataDir:
return os.path.join(str(os.getenv('HOME')),
'Library', 'Application Support', 'openlp', 'Data')
return os.path.join(str(os.getenv('HOME')), 'Library', 'Application Support', 'openlp', 'Data')
elif dir_type == AppLocation.LanguageDir:
return os.path.split(openlp.__file__)[0]
return os.path.dirname(openlp.__file__)
return os.path.join(str(os.getenv('HOME')), 'Library', 'Application Support', 'openlp')
else:
if dir_type == AppLocation.LanguageDir:

View File

@ -68,8 +68,7 @@ class Settings(QtCore.QSettings):
``__obsolete_settings__``
Each entry is structured in the following way::
(u'general/enable slide loop', u'advanced/slide limits',
[(SlideLimits.Wrap, True), (SlideLimits.End, False)])
('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)])
The first entry is the *old key*; it will be removed.

View File

@ -300,8 +300,7 @@ def create_separated_list(string_list):
:param string_list: List of unicode strings
"""
if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and \
LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'):
if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'):
return QtCore.QLocale().createSeparatedList(string_list)
if not string_list:
return ''

View File

@ -194,6 +194,7 @@ class Manager(object):
db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
except (SQLAlchemyError, DBAPIError):
log.exception('Error loading database: %s', self.db_url)
return
if db_ver > up_ver:
critical_error_message_box(
translate('OpenLP.Manager', 'Database Error'),
@ -215,7 +216,7 @@ class Manager(object):
Save an object to the database
:param object_instance: The object to save
:param commit: Commit the session with this object
:param commit: Commit the session with this object
"""
for try_count in range(3):
try:

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #

View File

@ -495,8 +495,8 @@ s
if service_item:
service_item.from_plugin = True
self.preview_controller.add_service_item(service_item)
if keep_focus:
self.list_view.setFocus()
if not keep_focus:
self.preview_controller.preview_widget.setFocus()
def on_live_click(self):
"""
@ -535,6 +535,7 @@ s
if remote:
service_item.will_auto_start = True
self.live_controller.add_service_item(service_item)
self.live_controller.preview_widget.setFocus()
def create_item_from_id(self, item_id):
"""

View File

@ -129,7 +129,7 @@ class Plugin(QtCore.QObject, RegistryProperties):
class MyPlugin(Plugin):
def __init__(self):
super(MyPlugin, self).__init__('MyPlugin', version=u'0.1')
super(MyPlugin, self).__init__('MyPlugin', version='0.1')
:param name: Defaults to *None*. The name of the plugin.
:param default_settings: A dict containing the plugin's settings. The value to each key is the default value

View File

@ -82,11 +82,6 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
present_plugin_dir = os.path.join(self.base_path, 'presentations')
self.log_debug('finding plugins in %s at depth %d' % (self.base_path, start_depth))
for root, dirs, files in os.walk(self.base_path):
if sys.platform == 'darwin' and root.startswith(present_plugin_dir):
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it. The following code will ignore files from the presentation plugin directory
# and thereby never import the plugin.
continue
for name in files:
if name.endswith('.py') and not name.startswith('__'):
path = os.path.abspath(os.path.join(root, name))

View File

@ -59,7 +59,6 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
"""
super(Renderer, self).__init__(None)
# Need live behaviour if this is also working as a pseudo MainDisplay.
self.is_live = True
self.screens = ScreenList()
self.theme_level = ThemeLevel.Global
self.global_theme_name = ''
@ -248,6 +247,9 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
elif item.is_capable(ItemCapabilities.CanSoftBreak):
pages = []
if '[---]' in text:
# Remove two or more option slide breaks next to each other (causing infinite loop).
while '\n[---]\n[---]\n' in text:
text = text.replace('\n[---]\n[---]\n', '\n[---]\n')
while True:
slides = text.split('\n[---]\n', 2)
# If there are (at least) two occurrences of [---] we use the first two slides (and neglect the last
@ -392,7 +394,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
off when displayed.
:param lines: The text to be fitted on the slide split into lines.
:param line_end: The text added after each line. Either ``u' '`` or ``u'<br>``.
:param line_end: The text added after each line. Either ``' '`` or ``'<br>``.
"""
formatted = []
previous_html = ''
@ -416,7 +418,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
processed word by word. This is sometimes need for **bible** verses.
:param lines: The text to be fitted on the slide split into lines.
:param line_end: The text added after each line. Either ``u' '`` or ``u'<br>``. This is needed for **bibles**.
:param line_end: The text added after each line. Either ``' '`` or ``'<br>``. This is needed for **bibles**.
"""
formatted = []
previous_html = ''
@ -453,7 +455,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
"""
Tests the given text for not closed formatting tags and returns a tuple consisting of three unicode strings::
(u'{st}{r}Text text text{/r}{/st}', u'{st}{r}', u'<strong><span style="-webkit-text-fill-color:red">')
('{st}{r}Text text text{/r}{/st}', '{st}{r}', '<strong><span style="-webkit-text-fill-color:red">')
The first unicode string is the text, with correct closing tags. The second unicode string are OpenLP's opening
formatting tags and the third unicode string the html opening formatting tags.
@ -500,8 +502,8 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
The text contains html.
:param raw_list: The elements which do not fit on a slide and needs to be processed using the binary chop.
The elements can contain formatting tags.
:param separator: The separator for the elements. For lines this is ``u'<br>'`` and for words this is ``u' '``.
:param line_end: The text added after each "element line". Either ``u' '`` or ``u'<br>``. This is needed for
:param separator: The separator for the elements. For lines this is ``'<br>'`` and for words this is ``' '``.
:param line_end: The text added after each "element line". Either ``' '`` or ``'<br>``. This is needed for
bibles.
"""
smallest_index = 0

View File

@ -63,8 +63,7 @@ class ScreenList(object):
"""
Initialise the screen list.
``desktop``
A ``QDesktopWidget`` object.
:param desktop: A QDesktopWidget object.
"""
screen_list = cls()
screen_list.desktop = desktop
@ -136,7 +135,7 @@ class ScreenList(object):
Returns a list with the screens. This should only be used to display
available screens to the user::
[u'Screen 1 (primary)', u'Screen 2']
['Screen 1 (primary)', 'Screen 2']
"""
screen_list = []
for screen in self.screen_list:
@ -153,9 +152,9 @@ class ScreenList(object):
:param screen: A dict with the screen properties::
{
u'primary': True,
u'number': 0,
u'size': PyQt4.QtCore.QRect(0, 0, 1024, 768)
'primary': True,
'number': 0,
'size': PyQt4.QtCore.QRect(0, 0, 1024, 768)
}
"""
log.info('Screen %d found with resolution %s' % (screen['number'], screen['size']))

View File

@ -295,7 +295,7 @@ def set_case_insensitive_completer(cache, widget):
Sets a case insensitive text completer for a widget.
:param cache: The list of items to use as suggestions.
:param widget: A widget to set the completer (QComboBox or QTextEdit instance)
:param widget: A widget to set the completer (QComboBox or QLineEdit instance)
"""
completer = QtGui.QCompleter(cache)
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)

View File

@ -44,7 +44,7 @@ class Ui_AboutDialog(object):
Set up the UI for the dialog.
"""
about_dialog.setObjectName('about_dialog')
about_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
about_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
self.about_dialog_layout = QtGui.QVBoxLayout(about_dialog)
self.about_dialog_layout.setObjectName('about_dialog_layout')
self.logo_label = QtGui.QLabel(about_dialog)

View File

@ -30,7 +30,7 @@
The About dialog.
"""
from PyQt4 import QtCore, QtGui
from PyQt4 import QtGui
from .aboutdialog import Ui_AboutDialog
from openlp.core.lib import translate

View File

@ -30,9 +30,9 @@
The GUI widgets of the exception dialog.
"""
from PyQt4 import QtCore, QtGui
from PyQt4 import QtGui
from openlp.core.lib import translate
from openlp.core.lib import translate, build_icon
from openlp.core.lib.ui import create_button, create_button_box
@ -45,6 +45,7 @@ class Ui_ExceptionDialog(object):
Set up the UI.
"""
exception_dialog.setObjectName('exception_dialog')
exception_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
self.exception_layout = QtGui.QVBoxLayout(exception_dialog)
self.exception_layout.setObjectName('exception_layout')
self.message_layout = QtGui.QHBoxLayout()

View File

@ -31,7 +31,7 @@ The UI widgets for the rename dialog
"""
from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate
from openlp.core.lib import translate, build_icon
from openlp.core.lib.ui import create_button_box
@ -44,6 +44,7 @@ class Ui_FileRenameDialog(object):
Set up the UI
"""
file_rename_dialog.setObjectName('file_rename_dialog')
file_rename_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
file_rename_dialog.resize(300, 10)
self.dialog_layout = QtGui.QGridLayout(file_rename_dialog)
self.dialog_layout.setObjectName('dialog_layout')

View File

@ -114,10 +114,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
"""
Run the wizard.
"""
self.setDefaults()
self.set_defaults()
return QtGui.QWizard.exec_(self)
def setDefaults(self):
def set_defaults(self):
"""
Set up display at start of theme edit.
"""
@ -199,8 +199,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
self.no_internet_label.setText(self.no_internet_text + self.cancelWizardText)
elif page_id == FirstTimePage.Defaults:
self.theme_combo_box.clear()
for iter in range(self.themes_list_widget.count()):
item = self.themes_list_widget.item(iter)
for index in range(self.themes_list_widget.count()):
item = self.themes_list_widget.item(index)
if item.checkState() == QtCore.Qt.Checked:
self.theme_combo_box.addItem(item.text())
if self.has_run_wizard:
@ -292,13 +292,9 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
"""
themes = self.config.get('themes', 'files')
themes = themes.split(',')
for theme in themes:
filename = self.config.get('theme_%s' % theme, 'filename')
for index, theme in enumerate(themes):
screenshot = self.config.get('theme_%s' % theme, 'screenshot')
for index in range(self.themes_list_widget.count()):
item = self.themes_list_widget.item(index)
if item.data(QtCore.Qt.UserRole) == filename:
break
item = self.themes_list_widget.item(index)
item.setIcon(build_icon(os.path.join(gettempdir(), 'openlp', screenshot)))
def _get_file_size(self, url):
@ -416,10 +412,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
self._increment_progress_bar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...'))
self._set_plugin_status(self.songs_check_box, 'songs/status')
self._set_plugin_status(self.bible_check_box, 'bibles/status')
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it.
if sys.platform != 'darwin':
self._set_plugin_status(self.presentation_check_box, 'presentations/status')
self._set_plugin_status(self.presentation_check_box, 'presentations/status')
self._set_plugin_status(self.image_check_box, 'images/status')
self._set_plugin_status(self.media_check_box, 'media/status')
self._set_plugin_status(self.remote_check_box, 'remotes/status')

View File

@ -32,6 +32,7 @@ The UI widgets of the language selection dialog.
from PyQt4 import QtGui
from openlp.core.common import translate
from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_button_box
@ -44,6 +45,7 @@ class Ui_FirstTimeLanguageDialog(object):
Set up the UI.
"""
language_dialog.setObjectName('language_dialog')
language_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
language_dialog.resize(300, 50)
self.dialog_layout = QtGui.QVBoxLayout(language_dialog)
self.dialog_layout.setContentsMargins(8, 8, 8, 8)

View File

@ -34,6 +34,7 @@ from PyQt4 import QtCore, QtGui
import sys
from openlp.core.common import translate
from openlp.core.lib import build_icon
from openlp.core.lib.ui import add_welcome_page
@ -60,6 +61,7 @@ class Ui_FirstTimeWizard(object):
Set up the UI.
"""
first_time_wizard.setObjectName('first_time_wizard')
first_time_wizard.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
first_time_wizard.resize(550, 386)
first_time_wizard.setModal(True)
first_time_wizard.setWizardStyle(QtGui.QWizard.ModernStyle)
@ -93,13 +95,10 @@ class Ui_FirstTimeWizard(object):
self.image_check_box.setChecked(True)
self.image_check_box.setObjectName('image_check_box')
self.plugin_layout.addWidget(self.image_check_box)
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it.
if sys.platform != 'darwin':
self.presentation_check_box = QtGui.QCheckBox(self.plugin_page)
self.presentation_check_box.setChecked(True)
self.presentation_check_box.setObjectName('presentation_check_box')
self.plugin_layout.addWidget(self.presentation_check_box)
self.presentation_check_box = QtGui.QCheckBox(self.plugin_page)
self.presentation_check_box.setChecked(True)
self.presentation_check_box.setObjectName('presentation_check_box')
self.plugin_layout.addWidget(self.presentation_check_box)
self.media_check_box = QtGui.QCheckBox(self.plugin_page)
self.media_check_box.setChecked(True)
self.media_check_box.setObjectName('media_check_box')
@ -220,10 +219,7 @@ class Ui_FirstTimeWizard(object):
self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Custom Slides'))
self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
self.image_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Images'))
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it.
if sys.platform != 'darwin':
self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations'))
self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations'))
self.media_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Media (Audio and Video)'))
self.remote_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Allow remote access'))
self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Monitor Song Usage'))

View File

@ -45,6 +45,7 @@ class Ui_FormattingTagDialog(object):
Set up the UI
"""
formatting_tag_dialog.setObjectName('formatting_tag_dialog')
formatting_tag_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
formatting_tag_dialog.resize(725, 548)
self.list_data_grid_layout = QtGui.QVBoxLayout(formatting_tag_dialog)
self.list_data_grid_layout.setMargin(8)

View File

@ -91,10 +91,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont
"""
new_row = self.tag_table_widget.rowCount()
self.tag_table_widget.insertRow(new_row)
self.tag_table_widget.setItem(new_row, 0,
QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', 'New Tag%s')
% str(new_row)))
self.tag_table_widget.setItem(new_row, 1, QtGui.QTableWidgetItem('n%s' % str(new_row)))
self.tag_table_widget.setItem(new_row, 0, QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm',
'New Tag %d' % new_row)))
self.tag_table_widget.setItem(new_row, 1, QtGui.QTableWidgetItem('n%d' % new_row))
self.tag_table_widget.setItem(new_row, 2,
QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', '<HTML here>')))
self.tag_table_widget.setItem(new_row, 3, QtGui.QTableWidgetItem(''))

View File

@ -136,7 +136,6 @@ class ListPreviewWidget(QtGui.QTableWidget, RegistryProperties):
if self.service_item.is_text():
self.resizeRowsToContents()
self.setColumnWidth(0, self.viewport().width())
self.setFocus()
self.change_slide(slide_number)
def change_slide(self, slide):

View File

@ -66,11 +66,8 @@ class Display(QtGui.QGraphicsView):
if hasattr(parent, 'is_live') and parent.is_live:
self.is_live = True
if self.is_live:
super(Display, self).__init__()
# Overwrite the parent() method.
self.parent = lambda: parent
else:
super(Display, self).__init__(parent)
super(Display, self).__init__()
self.controller = parent
self.screen = {}
# FIXME: On Mac OS X (tested on 10.7) the display screen is corrupt with

View File

@ -89,7 +89,7 @@ class Ui_MainWindow(object):
Set up the user interface
"""
main_window.setObjectName('MainWindow')
main_window.setWindowIcon(build_icon(':/icon/openlp-logo-64x64.png'))
main_window.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
main_window.setDockNestingEnabled(True)
# Set up the main container, which contains all the other form widgets.
self.main_content = QtGui.QWidget(main_window)
@ -320,14 +320,14 @@ class Ui_MainWindow(object):
# i18n add Language Actions
add_actions(self.settings_language_menu, (self.auto_language_item, None))
add_actions(self.settings_language_menu, self.language_group.actions())
# Order things differently in OS X so that Preferences menu item in the
# app menu is correct (this gets picked up automatically by Qt).
# Qt on OS X looks for keywords in the menu items title to determine which menu items get added to the main
# menu. If we are running on Mac OS X the menu items whose title contains those keywords but don't belong in the
# main menu need to be marked as such with QAction.NoRole.
if sys.platform == 'darwin':
add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(),
None, self.settings_configure_item, self.settings_shortcuts_item, self.formatting_tag_item))
else:
add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(),
None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item))
self.settings_shortcuts_item.setMenuRole(QtGui.QAction.NoRole)
self.formatting_tag_item.setMenuRole(QtGui.QAction.NoRole)
add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(),
None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item))
add_actions(self.tools_menu, (self.tools_add_tool_item, None))
add_actions(self.tools_menu, (self.tools_open_data_folder, None))
add_actions(self.tools_menu, (self.tools_first_time_wizard, None))
@ -598,7 +598,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
if self.arguments:
self.open_cmd_line_files()
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)
view_mode = Settings().value('%s/view mode' % self.general_settings_section)
if view_mode == 'default':
@ -1334,7 +1334,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
if self.copy_data:
log.info('Copying data to new path')
try:
self.showStatusMessage(
self.show_status_message(
translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - %s '
'- Please wait for copy to finish').replace('%s', self.new_data_path))
dir_util.copy_tree(old_data_path, self.new_data_path)
@ -1364,8 +1364,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
args = []
for a in self.arguments:
args.extend([a])
for arg in args:
filename = arg
for filename in args:
if not isinstance(filename, str):
filename = str(filename, sys.getfilesystemencoding())
if filename.endswith(('.osz', '.oszl')):

View File

@ -29,8 +29,6 @@
"""
The :mod:`~openlp.core.ui.media.mediaplayer` module contains the MediaPlayer class.
"""
import os
from openlp.core.common import RegistryProperties
from openlp.core.ui.media import MediaState

View File

@ -33,10 +33,8 @@ import logging
import mimetypes
from datetime import datetime
from PyQt4 import QtGui
from PyQt4.phonon import Phonon
from openlp.core.common import Settings
from openlp.core.lib import translate
from openlp.core.ui.media import MediaState

View File

@ -48,7 +48,7 @@ import sys
from inspect import getargspec
__version__ = "N/A"
build_date = "Tue Jul 2 10:35:53 2013"
build_date = "Wed Jun 25 13:46:01 2014"
if sys.version_info[0] > 2:
str = str
@ -110,7 +110,11 @@ def find_lib():
p = find_library('libvlc.dll')
if p is None:
try: # some registry settings
import _winreg as w # leaner than win32api, win32con
# leaner than win32api, win32con
if PYTHON3:
import winreg as w
else:
import _winreg as w
for r in w.HKEY_LOCAL_MACHINE, w.HKEY_CURRENT_USER:
try:
r = w.OpenKey(r, 'Software\\VideoLAN\\VLC')
@ -365,6 +369,7 @@ class EventType(_Enum):
3: 'MediaParsedChanged',
4: 'MediaFreed',
5: 'MediaStateChanged',
6: 'MediaSubItemTreeAdded',
0x100: 'MediaPlayerMediaChanged',
257: 'MediaPlayerNothingSpecial',
258: 'MediaPlayerOpening',
@ -384,6 +389,7 @@ class EventType(_Enum):
272: 'MediaPlayerSnapshotTaken',
273: 'MediaPlayerLengthChanged',
274: 'MediaPlayerVout',
275: 'MediaPlayerScrambledChanged',
0x200: 'MediaListItemAdded',
513: 'MediaListWillAddItem',
514: 'MediaListItemDeleted',
@ -439,6 +445,7 @@ EventType.MediaPlayerPausableChanged = EventType(270)
EventType.MediaPlayerPaused = EventType(261)
EventType.MediaPlayerPlaying = EventType(260)
EventType.MediaPlayerPositionChanged = EventType(268)
EventType.MediaPlayerScrambledChanged = EventType(275)
EventType.MediaPlayerSeekableChanged = EventType(269)
EventType.MediaPlayerSnapshotTaken = EventType(272)
EventType.MediaPlayerStopped = EventType(262)
@ -447,6 +454,7 @@ EventType.MediaPlayerTitleChanged = EventType(271)
EventType.MediaPlayerVout = EventType(274)
EventType.MediaStateChanged = EventType(5)
EventType.MediaSubItemAdded = EventType(1)
EventType.MediaSubItemTreeAdded = EventType(6)
EventType.VlmMediaAdded = EventType(0x600)
EventType.VlmMediaChanged = EventType(1538)
EventType.VlmMediaInstanceStarted = EventType(1539)
@ -480,23 +488,35 @@ class Meta(_Enum):
14: 'EncodedBy',
15: 'ArtworkURL',
16: 'TrackID',
17: 'TrackTotal',
18: 'Director',
19: 'Season',
20: 'Episode',
21: 'ShowName',
22: 'Actors',
}
Meta.Actors = Meta(22)
Meta.Album = Meta(4)
Meta.Artist = Meta(1)
Meta.ArtworkURL = Meta(15)
Meta.Copyright = Meta(3)
Meta.Date = Meta(8)
Meta.Description = Meta(6)
Meta.Director = Meta(18)
Meta.EncodedBy = Meta(14)
Meta.Episode = Meta(20)
Meta.Genre = Meta(2)
Meta.Language = Meta(11)
Meta.NowPlaying = Meta(12)
Meta.Publisher = Meta(13)
Meta.Rating = Meta(7)
Meta.Season = Meta(19)
Meta.Setting = Meta(9)
Meta.ShowName = Meta(21)
Meta.Title = Meta(0)
Meta.TrackID = Meta(16)
Meta.TrackNumber = Meta(5)
Meta.TrackTotal = Meta(17)
Meta.URL = Meta(10)
class State(_Enum):
@ -594,6 +614,32 @@ NavigateMode.left = NavigateMode(3)
NavigateMode.right = NavigateMode(4)
NavigateMode.up = NavigateMode(1)
class Position(_Enum):
'''Enumeration of values used to set position (e.g. of video title).
'''
_enum_names_ = {
-1: 'disable',
0: 'center',
1: 'left',
2: 'right',
3: 'top',
4: 'left',
5: 'right',
6: 'bottom',
7: 'left',
8: 'right',
}
Position.bottom = Position(6)
Position.center = Position(0)
Position.disable = Position(-1)
Position.left = Position(1)
Position.left = Position(4)
Position.left = Position(7)
Position.right = Position(2)
Position.right = Position(5)
Position.right = Position(8)
Position.top = Position(3)
class VideoLogoOption(_Enum):
'''Option values for libvlc_video_{get,set}_logo_{int,string}.
'''
@ -685,7 +731,7 @@ class LogCb(ctypes.c_void_p):
"""Callback prototype for LibVLC log message handler.
\param data data pointer as given to L{libvlc_log_set}()
\param level message level (@ref enum libvlc_log_level)
\param ctx message context (meta-informations about the message)
\param ctx message context (meta-information about the message)
\param fmt printf() format string (as defined by ISO C11)
\param args variable argument list for the format
\note Log message handlers <b>must</b> be thread-safe.
@ -823,18 +869,18 @@ class CallbackDecorators(object):
Callback = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p)
Callback.__doc__ = '''Callback function notification
\param p_event the event triggering the callback
'''
'''
LogCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, Log_ptr, ctypes.c_char_p, ctypes.c_void_p)
LogCb.__doc__ = '''Callback prototype for LibVLC log message handler.
\param data data pointer as given to L{libvlc_log_set}()
\param level message level (@ref enum libvlc_log_level)
\param ctx message context (meta-informations about the message)
\param ctx message context (meta-information about the message)
\param fmt printf() format string (as defined by ISO C11)
\param args variable argument list for the format
\note Log message handlers <b>must</b> be thread-safe.
\warning The message context pointer, the format string parameters and the
variable arguments are only valid until the callback returns.
'''
'''
VideoLockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p))
VideoLockCb.__doc__ = '''Callback prototype to allocate and lock a picture buffer.
Whenever a new video frame needs to be decoded, the lock callback is
@ -846,7 +892,7 @@ planes must be aligned on 32-bytes boundaries.
of void pointers, this callback must initialize the array) [OUT]
\return a private pointer for the display and unlock callbacks to identify
the picture buffers
'''
'''
VideoUnlockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p))
VideoUnlockCb.__doc__ = '''Callback prototype to unlock a picture buffer.
When the video frame decoding is complete, the unlock callback is invoked.
@ -859,7 +905,7 @@ but before the picture is displayed.
callback [IN]
\param planes pixel planes as defined by the @ref libvlc_video_lock_cb
callback (this parameter is only for convenience) [IN]
'''
'''
VideoDisplayCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p)
VideoDisplayCb.__doc__ = '''Callback prototype to display a picture.
When the video frame needs to be shown, as determined by the media playback
@ -867,7 +913,7 @@ clock, the display callback is invoked.
\param opaque private pointer as passed to L{libvlc_video_set_callbacks}() [IN]
\param picture private pointer returned from the @ref libvlc_video_lock_cb
callback [IN]
'''
'''
VideoFormatCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_uint), ListPOINTER(ctypes.c_void_p), ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint))
VideoFormatCb.__doc__ = '''Callback prototype to configure picture buffers format.
This callback gets the format of the video as output by the video decoder
@ -891,47 +937,47 @@ the pixel height.
Furthermore, we recommend that pitches and lines be multiple of 32
to not break assumption that might be made by various optimizations
in the video decoders, video filters and/or video converters.
'''
'''
VideoCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
VideoCleanupCb.__doc__ = '''Callback prototype to configure picture buffers format.
\param opaque private pointer as passed to L{libvlc_video_set_callbacks}()
(and possibly modified by @ref libvlc_video_format_cb) [IN]
'''
'''
AudioPlayCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_int64)
AudioPlayCb.__doc__ = '''Callback prototype for audio playback.
\param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN]
\param samples pointer to the first audio sample to play back [IN]
\param count number of audio samples to play back
\param pts expected play time stamp (see libvlc_delay())
'''
'''
AudioPauseCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int64)
AudioPauseCb.__doc__ = '''Callback prototype for audio pause.
\note The pause callback is never called if the audio is already paused.
\param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN]
\param pts time stamp of the pause request (should be elapsed already)
'''
'''
AudioResumeCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int64)
AudioResumeCb.__doc__ = '''Callback prototype for audio resumption (i.e. restart from pause).
\note The resume callback is never called if the audio is not paused.
\param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN]
\param pts time stamp of the resumption request (should be elapsed already)
'''
'''
AudioFlushCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int64)
AudioFlushCb.__doc__ = '''Callback prototype for audio buffer flush
(i.e. discard all pending buffers and stop playback as soon as possible).
\param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN]
'''
'''
AudioDrainCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
AudioDrainCb.__doc__ = '''Callback prototype for audio buffer drain
(i.e. wait for pending buffers to be played).
\param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN]
'''
'''
AudioSetVolumeCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_float, ctypes.c_bool)
AudioSetVolumeCb.__doc__ = '''Callback prototype for audio volume change.
\param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN]
\param volume software volume (1. = nominal, 0. = mute)
\param mute muted flag
'''
'''
AudioSetupCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_int), ListPOINTER(ctypes.c_void_p), ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint))
AudioSetupCb.__doc__ = '''Callback prototype to setup the audio playback.
This is called when the media player needs to create a new audio output.
@ -941,12 +987,12 @@ This is called when the media player needs to create a new audio output.
\param rate sample rate [IN/OUT]
\param channels channels count [IN/OUT]
\return 0 on success, anything else to skip audio playback
'''
'''
AudioCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
AudioCleanupCb.__doc__ = '''Callback prototype for audio playback cleanup.
This is called when the media player no longer needs an audio output.
\param opaque data pointer as passed to L{libvlc_audio_set_callbacks}() [IN]
'''
'''
cb = CallbackDecorators
# End of generated enum types #
@ -1210,7 +1256,7 @@ class EventManager(_Ctype):
@note: Only a single notification can be registered
for each event type in an EventManager instance.
'''
_callback_handler = None
@ -1287,7 +1333,7 @@ class Instance(_Ctype):
- a string
- a list of strings as first parameters
- the parameters given as the constructor parameters (must be strings)
'''
def __new__(cls, *args):
@ -1432,6 +1478,16 @@ class Instance(_Ctype):
'''
return libvlc_set_user_agent(self, str_to_bytes(name), str_to_bytes(http))
def set_app_id(self, id, version, icon):
'''Sets some meta-information about the application.
See also L{set_user_agent}().
@param id: Java-style application identifier, e.g. "com.acme.foobar".
@param version: application version numbers, e.g. "1.2.3".
@param icon: application icon name, e.g. "foobar".
@version: LibVLC 2.1.0 or later.
'''
return libvlc_set_app_id(self, str_to_bytes(id), str_to_bytes(version), str_to_bytes(icon))
def log_unset(self):
'''Unsets the logging callback for a LibVLC instance. This is rarely needed:
the callback is implicitly unset when the instance is destroyed.
@ -1521,13 +1577,13 @@ class Instance(_Ctype):
return libvlc_media_library_new(self)
def audio_output_list_get(self):
'''Gets the list of available audio outputs.
'''Gets the list of available audio output modules.
@return: list of available audio outputs. It must be freed it with In case of error, NULL is returned.
'''
return libvlc_audio_output_list_get(self)
def audio_output_device_list_get(self, aout):
'''Gets a list of audio output devices for a given audio output.
'''Gets a list of audio output devices for a given audio output module,
See L{audio_output_device_set}().
@note: Not all audio outputs support this. In particular, an empty (NULL)
list of devices does B{not} imply that the specified audio output does
@ -1753,11 +1809,11 @@ class Instance(_Ctype):
class Media(_Ctype):
'''Create a new Media instance.
Usage: Media(MRL, *options)
See vlc.Instance.media_new documentation for details.
'''
def __new__(cls, *args):
@ -1790,6 +1846,19 @@ class Media(_Ctype):
for o in options:
self.add_option(o)
def tracks_get(self):
"""Get media descriptor's elementary streams description
Note, you need to call L{parse}() or play the media at least once
before calling this function.
Not doing this will result in an empty array.
The result must be freed with L{tracks_release}.
@version: LibVLC 2.1.0 and later.
"""
mediaTrack_pp = ctypes.POINTER(MediaTrack)()
n = libvlc_media_tracks_get(self, byref(mediaTrack_pp))
info = cast(ctypes.mediaTrack_pp, ctypes.POINTER(ctypes.POINTER(MediaTrack) * n))
return info
def add_option(self, psz_options):
'''Add an option to the media.
@ -1962,17 +2031,6 @@ class Media(_Ctype):
'''
return libvlc_media_get_user_data(self)
def tracks_get(self, tracks):
'''Get media descriptor's elementary streams description
Note, you need to call L{parse}() or play the media at least once
before calling this function.
Not doing this will result in an empty array.
@param tracks: address to store an allocated array of Elementary Streams descriptions (must be freed with L{tracks_release}.
@return: the number of Elementary Streams (zero on error).
@version: LibVLC 2.1.0 and later.
'''
return libvlc_media_tracks_get(self, tracks)
def player_new_from_media(self):
'''Create a Media Player object from a Media.
@return: a new media player object, or NULL on error.
@ -2053,11 +2111,11 @@ class MediaLibrary(_Ctype):
class MediaList(_Ctype):
'''Create a new MediaList instance.
Usage: MediaList(list_of_MRLs)
See vlc.Instance.media_list_new documentation for details.
'''
def __new__(cls, *args):
@ -2073,10 +2131,10 @@ class MediaList(_Ctype):
def get_instance(self):
return getattr(self, '_instance', None)
def add_media(self, mrl):
"""Add media instance to media list.
The L{lock} should be held upon entering this function.
@param mrl: a media instance or a MRL.
@return: 0 on success, -1 if the media list is read-only.
@ -2193,7 +2251,7 @@ class MediaListPlayer(_Ctype):
It may take as parameter either:
- a vlc.Instance
- nothing
'''
def __new__(cls, arg=None):
@ -2319,13 +2377,13 @@ class MediaPlayer(_Ctype):
It may take as parameter either:
- a string (media URI), options... In this case, a vlc.Instance will be created.
- a vlc.Instance, a string (media URI), options...
'''
def __new__(cls, *args):
if len(args) == 1 and isinstance(args[0], _Ints):
return _Constructor(cls, args[0])
if args and isinstance(args[0], Instance):
instance = args[0]
args = args[1:]
@ -2397,13 +2455,13 @@ class MediaPlayer(_Ctype):
Specify where the media player should render its video
output. If LibVLC was built without Win32/Win64 API output
support, then this has no effects.
@param drawable: windows handle of the drawable.
"""
if not isinstance(drawable, ctypes.c_void_p):
drawable = ctypes.c_void_p(int(drawable))
libvlc_media_player_set_hwnd(self, drawable)
def video_get_width(self, num=0):
"""Get the width of a video in pixels.
@ -2556,12 +2614,12 @@ class MediaPlayer(_Ctype):
If you want to use it along with Qt4 see the QMacCocoaViewContainer. Then
the following code should work:
@begincode
NSView *video = [[NSView alloc] init];
QMacCocoaViewContainer *container = new QMacCocoaViewContainer(video, parent);
L{set_nsobject}(mp, video);
[video release];
@endcode
You can find a live example in VLCVideoView in VLCKit.framework.
@param drawable: the drawable that is either an NSView or an object following the VLCOpenGLVideoViewEmbedding protocol.
@ -2796,6 +2854,13 @@ class MediaPlayer(_Ctype):
'''
return libvlc_media_player_can_pause(self)
def program_scrambled(self):
'''Check if the current program is scrambled.
@return: true if the current program is scrambled \libvlc_return_bool.
@version: LibVLC 2.2.0 or later.
'''
return libvlc_media_player_program_scrambled(self)
def next_frame(self):
'''Display the next frame (if supported).
'''
@ -2808,6 +2873,14 @@ class MediaPlayer(_Ctype):
'''
return libvlc_media_player_navigate(self, navigate)
def set_video_title_display(self, position, timeout):
'''Set if, and how, the video title will be shown when media is played.
@param position: position at which to display the title, or libvlc_position_disable to prevent the title from being displayed.
@param timeout: title display timeout in milliseconds (ignored if libvlc_position_disable).
@version: libVLC 2.1.0 or later.
'''
return libvlc_media_player_set_video_title_display(self, position, timeout)
def toggle_fullscreen(self):
'''Toggle fullscreen status on non-embedded video outputs.
@warning: The same limitations applies to this function
@ -3083,7 +3156,7 @@ class MediaPlayer(_Ctype):
return libvlc_video_set_adjust_float(self, option, value)
def audio_output_set(self, psz_name):
'''Sets the audio output.
'''Selects an audio output module.
@note: Any change will take be effect only after playback is stopped and
restarted. Audio output cannot be changed while playing.
@param psz_name: name of audio output, use psz_name of See L{AudioOutput}.
@ -3091,21 +3164,46 @@ class MediaPlayer(_Ctype):
'''
return libvlc_audio_output_set(self, str_to_bytes(psz_name))
def audio_output_device_set(self, psz_audio_output, psz_device_id):
'''Configures an explicit audio output device for a given audio output plugin.
A list of possible devices can be obtained with
def audio_output_device_enum(self):
'''Gets a list of potential audio output devices,
See L{audio_output_device_set}().
@note: Not all audio outputs support enumerating devices.
The audio output may be functional even if the list is empty (NULL).
@note: The list may not be exhaustive.
@warning: Some audio output devices in the list might not actually work in
some circumstances. By default, it is recommended to not specify any
explicit audio device.
@return: A NULL-terminated linked list of potential audio output devices. It must be freed it with L{audio_output_device_list_release}().
@version: LibVLC 2.2.0 or later.
'''
return libvlc_audio_output_device_enum(self)
def audio_output_device_set(self, module, device_id):
'''Configures an explicit audio output device.
If the module paramater is NULL, audio output will be moved to the device
specified by the device identifier string immediately. This is the
recommended usage.
A list of adequate potential device strings can be obtained with
L{audio_output_device_enum}().
However passing NULL is supported in LibVLC version 2.2.0 and later only;
in earlier versions, this function would have no effects when the module
parameter was NULL.
If the module parameter is not NULL, the device parameter of the
corresponding audio output, if it exists, will be set to the specified
string. Note that some audio output modules do not have such a parameter
(notably MMDevice and PulseAudio).
A list of adequate potential device strings can be obtained with
L{audio_output_device_list_get}().
@note: This function does not select the specified audio output plugin.
L{audio_output_set}() is used for that purpose.
@warning: The syntax for the device parameter depends on the audio output.
This is not portable. Only use this function if you know what you are doing.
Some audio outputs do not support this function (e.g. PulseAudio, WASAPI).
Some audio outputs require further parameters (e.g. ALSA: channels map).
@param psz_audio_output: - name of audio output, See L{AudioOutput}.
@param psz_device_id: device.
@return: Nothing. Errors are ignored.
Some audio output modules require further parameters (e.g. a channels map
in the case of ALSA).
@param module: If NULL, current audio output module. if non-NULL, name of audio output module.
@param device_id: device identifier string.
@return: Nothing. Errors are ignored (this is a design bug).
'''
return libvlc_audio_output_device_set(self, str_to_bytes(psz_audio_output), str_to_bytes(psz_device_id))
return libvlc_audio_output_device_set(self, str_to_bytes(module), str_to_bytes(device_id))
def audio_toggle_mute(self):
'''Toggle mute status.
@ -3184,6 +3282,28 @@ class MediaPlayer(_Ctype):
'''
return libvlc_audio_set_delay(self, i_delay)
def set_equalizer(self, p_equalizer):
'''Apply new equalizer settings to a media player.
The equalizer is first created by invoking L{audio_equalizer_new}() or
L{audio_equalizer_new_from_preset}().
It is possible to apply new equalizer settings to a media player whether the media
player is currently playing media or not.
Invoking this method will immediately apply the new equalizer settings to the audio
output of the currently playing media if there is any.
If there is no currently playing media, the new equalizer settings will be applied
later if and when new media is played.
Equalizer settings will automatically be applied to subsequently played media.
To disable the equalizer for a media player invoke this method passing NULL for the
p_equalizer parameter.
The media player does not keep a reference to the supplied equalizer so it is safe
for an application to release the equalizer reference any time after this method
returns.
@param p_equalizer: opaque equalizer handle, or NULL to disable the equalizer for this media player.
@return: zero on success, -1 on error.
@version: LibVLC 2.2.0 or later.
'''
return libvlc_media_player_set_equalizer(self, p_equalizer)
# LibVLC __version__ functions #
@ -3279,6 +3399,20 @@ def libvlc_set_user_agent(p_instance, name, http):
None, Instance, ctypes.c_char_p, ctypes.c_char_p)
return f(p_instance, name, http)
def libvlc_set_app_id(p_instance, id, version, icon):
'''Sets some meta-information about the application.
See also L{libvlc_set_user_agent}().
@param p_instance: LibVLC instance.
@param id: Java-style application identifier, e.g. "com.acme.foobar".
@param version: application version numbers, e.g. "1.2.3".
@param icon: application icon name, e.g. "foobar".
@version: LibVLC 2.1.0 or later.
'''
f = _Cfunctions.get('libvlc_set_app_id', None) or \
_Cfunction('libvlc_set_app_id', ((1,), (1,), (1,), (1,),), None,
None, Instance, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p)
return f(p_instance, id, version, icon)
def libvlc_get_version():
'''Retrieve libvlc version.
Example: "1.1.0-git The Luggage".
@ -3355,7 +3489,7 @@ def libvlc_event_type_name(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
'''Gets debugging information 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.
@ -3369,9 +3503,9 @@ def libvlc_log_get_context(ctx):
return f(ctx)
def libvlc_log_get_object(ctx, id):
'''Gets VLC object informations about a log message: the type name of the VLC
'''Gets VLC object information 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}
object identifier. This information is 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
@ -4430,12 +4564,12 @@ def libvlc_media_player_set_nsobject(p_mi, drawable):
If you want to use it along with Qt4 see the QMacCocoaViewContainer. Then
the following code should work:
@begincode
NSView *video = [[NSView alloc] init];
QMacCocoaViewContainer *container = new QMacCocoaViewContainer(video, parent);
L{libvlc_media_player_set_nsobject}(mp, video);
[video release];
@endcode
You can find a live example in VLCVideoView in VLCKit.framework.
@param p_mi: the Media Player.
@ -4814,6 +4948,17 @@ def libvlc_media_player_can_pause(p_mi):
ctypes.c_int, MediaPlayer)
return f(p_mi)
def libvlc_media_player_program_scrambled(p_mi):
'''Check if the current program is scrambled.
@param p_mi: the media player.
@return: true if the current program is scrambled \libvlc_return_bool.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_media_player_program_scrambled', None) or \
_Cfunction('libvlc_media_player_program_scrambled', ((1,),), None,
ctypes.c_int, MediaPlayer)
return f(p_mi)
def libvlc_media_player_next_frame(p_mi):
'''Display the next frame (if supported).
@param p_mi: the media player.
@ -4834,6 +4979,18 @@ def libvlc_media_player_navigate(p_mi, navigate):
None, MediaPlayer, ctypes.c_uint)
return f(p_mi, navigate)
def libvlc_media_player_set_video_title_display(p_mi, position, timeout):
'''Set if, and how, the video title will be shown when media is played.
@param p_mi: the media player.
@param position: position at which to display the title, or libvlc_position_disable to prevent the title from being displayed.
@param timeout: title display timeout in milliseconds (ignored if libvlc_position_disable).
@version: libVLC 2.1.0 or later.
'''
f = _Cfunctions.get('libvlc_media_player_set_video_title_display', None) or \
_Cfunction('libvlc_media_player_set_video_title_display', ((1,), (1,), (1,),), None,
None, MediaPlayer, Position, ctypes.c_int)
return f(p_mi, position, timeout)
def libvlc_track_description_list_release(p_track_description):
'''Release (free) L{TrackDescription}.
@param p_track_description: the structure to release.
@ -5335,7 +5492,7 @@ def libvlc_video_set_adjust_float(p_mi, option, value):
return f(p_mi, option, value)
def libvlc_audio_output_list_get(p_instance):
'''Gets the list of available audio outputs.
'''Gets the list of available audio output modules.
@param p_instance: libvlc instance.
@return: list of available audio outputs. It must be freed it with In case of error, NULL is returned.
'''
@ -5345,7 +5502,7 @@ def libvlc_audio_output_list_get(p_instance):
return f(p_instance)
def libvlc_audio_output_list_release(p_list):
'''Frees the list of available audio outputs.
'''Frees the list of available audio output modules.
@param p_list: list with audio outputs for release.
'''
f = _Cfunctions.get('libvlc_audio_output_list_release', None) or \
@ -5354,7 +5511,7 @@ def libvlc_audio_output_list_release(p_list):
return f(p_list)
def libvlc_audio_output_set(p_mi, psz_name):
'''Sets the audio output.
'''Selects an audio output module.
@note: Any change will take be effect only after playback is stopped and
restarted. Audio output cannot be changed while playing.
@param p_mi: media player.
@ -5366,8 +5523,26 @@ def libvlc_audio_output_set(p_mi, psz_name):
ctypes.c_int, MediaPlayer, ctypes.c_char_p)
return f(p_mi, psz_name)
def libvlc_audio_output_device_enum(mp):
'''Gets a list of potential audio output devices,
See L{libvlc_audio_output_device_set}().
@note: Not all audio outputs support enumerating devices.
The audio output may be functional even if the list is empty (NULL).
@note: The list may not be exhaustive.
@warning: Some audio output devices in the list might not actually work in
some circumstances. By default, it is recommended to not specify any
explicit audio device.
@param mp: media player.
@return: A NULL-terminated linked list of potential audio output devices. It must be freed it with L{libvlc_audio_output_device_list_release}().
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_output_device_enum', None) or \
_Cfunction('libvlc_audio_output_device_enum', ((1,),), None,
ctypes.POINTER(AudioOutputDevice), MediaPlayer)
return f(mp)
def libvlc_audio_output_device_list_get(p_instance, aout):
'''Gets a list of audio output devices for a given audio output.
'''Gets a list of audio output devices for a given audio output module,
See L{libvlc_audio_output_device_set}().
@note: Not all audio outputs support this. In particular, an empty (NULL)
list of devices does B{not} imply that the specified audio output does
@ -5396,25 +5571,36 @@ def libvlc_audio_output_device_list_release(p_list):
None, ctypes.POINTER(AudioOutputDevice))
return f(p_list)
def libvlc_audio_output_device_set(p_mi, psz_audio_output, psz_device_id):
'''Configures an explicit audio output device for a given audio output plugin.
A list of possible devices can be obtained with
def libvlc_audio_output_device_set(mp, module, device_id):
'''Configures an explicit audio output device.
If the module paramater is NULL, audio output will be moved to the device
specified by the device identifier string immediately. This is the
recommended usage.
A list of adequate potential device strings can be obtained with
L{libvlc_audio_output_device_enum}().
However passing NULL is supported in LibVLC version 2.2.0 and later only;
in earlier versions, this function would have no effects when the module
parameter was NULL.
If the module parameter is not NULL, the device parameter of the
corresponding audio output, if it exists, will be set to the specified
string. Note that some audio output modules do not have such a parameter
(notably MMDevice and PulseAudio).
A list of adequate potential device strings can be obtained with
L{libvlc_audio_output_device_list_get}().
@note: This function does not select the specified audio output plugin.
L{libvlc_audio_output_set}() is used for that purpose.
@warning: The syntax for the device parameter depends on the audio output.
This is not portable. Only use this function if you know what you are doing.
Some audio outputs do not support this function (e.g. PulseAudio, WASAPI).
Some audio outputs require further parameters (e.g. ALSA: channels map).
@param p_mi: media player.
@param psz_audio_output: - name of audio output, See L{AudioOutput}.
@param psz_device_id: device.
@return: Nothing. Errors are ignored.
Some audio output modules require further parameters (e.g. a channels map
in the case of ALSA).
@param mp: media player.
@param module: If NULL, current audio output module. if non-NULL, name of audio output module.
@param device_id: device identifier string.
@return: Nothing. Errors are ignored (this is a design bug).
'''
f = _Cfunctions.get('libvlc_audio_output_device_set', None) or \
_Cfunction('libvlc_audio_output_device_set', ((1,), (1,), (1,),), None,
None, MediaPlayer, ctypes.c_char_p, ctypes.c_char_p)
return f(p_mi, psz_audio_output, psz_device_id)
return f(mp, module, device_id)
def libvlc_audio_toggle_mute(p_mi):
'''Toggle mute status.
@ -5551,6 +5737,175 @@ def libvlc_audio_set_delay(p_mi, i_delay):
ctypes.c_int, MediaPlayer, ctypes.c_int64)
return f(p_mi, i_delay)
def libvlc_audio_equalizer_get_preset_count():
'''Get the number of equalizer presets.
@return: number of presets.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_get_preset_count', None) or \
_Cfunction('libvlc_audio_equalizer_get_preset_count', (), None,
ctypes.c_uint)
return f()
def libvlc_audio_equalizer_get_preset_name(u_index):
'''Get the name of a particular equalizer preset.
This name can be used, for example, to prepare a preset label or menu in a user
interface.
@param u_index: index of the preset, counting from zero.
@return: preset name, or NULL if there is no such preset.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_get_preset_name', None) or \
_Cfunction('libvlc_audio_equalizer_get_preset_name', ((1,),), None,
ctypes.c_char_p, ctypes.c_uint)
return f(u_index)
def libvlc_audio_equalizer_get_band_count():
'''Get the number of distinct frequency bands for an equalizer.
@return: number of frequency bands.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_get_band_count', None) or \
_Cfunction('libvlc_audio_equalizer_get_band_count', (), None,
ctypes.c_uint)
return f()
def libvlc_audio_equalizer_get_band_frequency(u_index):
'''Get a particular equalizer band frequency.
This value can be used, for example, to create a label for an equalizer band control
in a user interface.
@param u_index: index of the band, counting from zero.
@return: equalizer band frequency (Hz), or -1 if there is no such band.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_get_band_frequency', None) or \
_Cfunction('libvlc_audio_equalizer_get_band_frequency', ((1,),), None,
ctypes.c_float, ctypes.c_uint)
return f(u_index)
def libvlc_audio_equalizer_new():
'''Create a new default equalizer, with all frequency values zeroed.
The new equalizer can subsequently be applied to a media player by invoking
L{libvlc_media_player_set_equalizer}().
The returned handle should be freed via L{libvlc_audio_equalizer_release}() when
it is no longer needed.
@return: opaque equalizer handle, or NULL on error.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_new', None) or \
_Cfunction('libvlc_audio_equalizer_new', (), None,
ctypes.c_void_p)
return f()
def libvlc_audio_equalizer_new_from_preset(u_index):
'''Create a new equalizer, with initial frequency values copied from an existing
preset.
The new equalizer can subsequently be applied to a media player by invoking
L{libvlc_media_player_set_equalizer}().
The returned handle should be freed via L{libvlc_audio_equalizer_release}() when
it is no longer needed.
@param u_index: index of the preset, counting from zero.
@return: opaque equalizer handle, or NULL on error.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_new_from_preset', None) or \
_Cfunction('libvlc_audio_equalizer_new_from_preset', ((1,),), None,
ctypes.c_void_p, ctypes.c_uint)
return f(u_index)
def libvlc_audio_equalizer_release(p_equalizer):
'''Release a previously created equalizer instance.
The equalizer was previously created by using L{libvlc_audio_equalizer_new}() or
L{libvlc_audio_equalizer_new_from_preset}().
It is safe to invoke this method with a NULL p_equalizer parameter for no effect.
@param p_equalizer: opaque equalizer handle, or NULL.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_release', None) or \
_Cfunction('libvlc_audio_equalizer_release', ((1,),), None,
None, ctypes.c_void_p)
return f(p_equalizer)
def libvlc_audio_equalizer_set_preamp(p_equalizer, f_preamp):
'''Set a new pre-amplification value for an equalizer.
The new equalizer settings are subsequently applied to a media player by invoking
L{libvlc_media_player_set_equalizer}().
The supplied amplification value will be clamped to the -20.0 to +20.0 range.
@param p_equalizer: valid equalizer handle, must not be NULL.
@param f_preamp: preamp value (-20.0 to 20.0 Hz).
@return: zero on success, -1 on error.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_set_preamp', None) or \
_Cfunction('libvlc_audio_equalizer_set_preamp', ((1,), (1,),), None,
ctypes.c_int, ctypes.c_void_p, ctypes.c_float)
return f(p_equalizer, f_preamp)
def libvlc_audio_equalizer_get_preamp(p_equalizer):
'''Get the current pre-amplification value from an equalizer.
@param p_equalizer: valid equalizer handle, must not be NULL.
@return: preamp value (Hz).
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_get_preamp', None) or \
_Cfunction('libvlc_audio_equalizer_get_preamp', ((1,),), None,
ctypes.c_float, ctypes.c_void_p)
return f(p_equalizer)
def libvlc_audio_equalizer_set_amp_at_index(p_equalizer, f_amp, u_band):
'''Set a new amplification value for a particular equalizer frequency band.
The new equalizer settings are subsequently applied to a media player by invoking
L{libvlc_media_player_set_equalizer}().
The supplied amplification value will be clamped to the -20.0 to +20.0 range.
@param p_equalizer: valid equalizer handle, must not be NULL.
@param f_amp: amplification value (-20.0 to 20.0 Hz).
@param u_band: index, counting from zero, of the frequency band to set.
@return: zero on success, -1 on error.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_set_amp_at_index', None) or \
_Cfunction('libvlc_audio_equalizer_set_amp_at_index', ((1,), (1,), (1,),), None,
ctypes.c_int, ctypes.c_void_p, ctypes.c_float, ctypes.c_uint)
return f(p_equalizer, f_amp, u_band)
def libvlc_audio_equalizer_get_amp_at_index(p_equalizer, u_band):
'''Get the amplification value for a particular equalizer frequency band.
@param p_equalizer: valid equalizer handle, must not be NULL.
@param u_band: index, counting from zero, of the frequency band to get.
@return: amplification value (Hz); NaN if there is no such frequency band.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_equalizer_get_amp_at_index', None) or \
_Cfunction('libvlc_audio_equalizer_get_amp_at_index', ((1,), (1,),), None,
ctypes.c_float, ctypes.c_void_p, ctypes.c_uint)
return f(p_equalizer, u_band)
def libvlc_media_player_set_equalizer(p_mi, p_equalizer):
'''Apply new equalizer settings to a media player.
The equalizer is first created by invoking L{libvlc_audio_equalizer_new}() or
L{libvlc_audio_equalizer_new_from_preset}().
It is possible to apply new equalizer settings to a media player whether the media
player is currently playing media or not.
Invoking this method will immediately apply the new equalizer settings to the audio
output of the currently playing media if there is any.
If there is no currently playing media, the new equalizer settings will be applied
later if and when new media is played.
Equalizer settings will automatically be applied to subsequently played media.
To disable the equalizer for a media player invoke this method passing NULL for the
p_equalizer parameter.
The media player does not keep a reference to the supplied equalizer so it is safe
for an application to release the equalizer reference any time after this method
returns.
@param p_mi: opaque media player handle.
@param p_equalizer: opaque equalizer handle, or NULL to disable the equalizer for this media player.
@return: zero on success, -1 on error.
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_media_player_set_equalizer', None) or \
_Cfunction('libvlc_media_player_set_equalizer', ((1,), (1,),), None,
ctypes.c_int, MediaPlayer, ctypes.c_void_p)
return f(p_mi, p_equalizer)
def libvlc_vlm_release(p_instance):
'''Release the vlm instance related to the given L{Instance}.
@param p_instance: the instance.
@ -5863,7 +6218,18 @@ def libvlc_vlm_get_event_manager(p_instance):
# libvlc_printerr
# libvlc_set_exit_handler
# 17 function(s) not wrapped as methods:
# 28 function(s) not wrapped as methods:
# libvlc_audio_equalizer_get_amp_at_index
# libvlc_audio_equalizer_get_band_count
# libvlc_audio_equalizer_get_band_frequency
# libvlc_audio_equalizer_get_preamp
# libvlc_audio_equalizer_get_preset_count
# libvlc_audio_equalizer_get_preset_name
# libvlc_audio_equalizer_new
# libvlc_audio_equalizer_new_from_preset
# libvlc_audio_equalizer_release
# libvlc_audio_equalizer_set_amp_at_index
# libvlc_audio_equalizer_set_preamp
# libvlc_audio_output_device_list_release
# libvlc_audio_output_list_release
# libvlc_clearerr

View File

@ -34,6 +34,7 @@ from distutils.version import LooseVersion
import logging
import os
import sys
import threading
from PyQt4 import QtGui
@ -207,7 +208,7 @@ class VlcPlayer(MediaPlayer):
start_time = 0
if self.state != MediaState.Paused and controller.media_info.start_time > 0:
start_time = controller.media_info.start_time
display.vlc_media_player.play()
threading.Thread(target=display.vlc_media_player.play).start()
if not self.media_state_wait(display, vlc.State.Playing):
return False
self.volume(display, controller.media_info.volume)
@ -233,7 +234,7 @@ class VlcPlayer(MediaPlayer):
"""
Stop the current item
"""
display.vlc_media_player.stop()
threading.Thread(target=display.vlc_media_player.stop).start()
self.state = MediaState.Stopped
def volume(self, display, vol):

View File

@ -32,6 +32,7 @@ The UI widgets of the plugin view dialog
from PyQt4 import QtCore, QtGui
from openlp.core.common import UiStrings, translate
from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_button_box
@ -44,6 +45,7 @@ class Ui_PluginViewDialog(object):
Set up the UI
"""
pluginViewDialog.setObjectName('pluginViewDialog')
pluginViewDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
pluginViewDialog.setWindowModality(QtCore.Qt.ApplicationModal)
self.plugin_layout = QtGui.QVBoxLayout(pluginViewDialog)
self.plugin_layout.setObjectName('plugin_layout')

View File

@ -30,7 +30,6 @@
The actual plugin view form
"""
import logging
import os
from PyQt4 import QtGui

View File

@ -56,6 +56,7 @@ class Ui_PrintServiceDialog(object):
Set up the UI
"""
print_service_dialog.setObjectName('print_service_dialog')
print_service_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
print_service_dialog.resize(664, 594)
self.main_layout = QtGui.QVBoxLayout(print_service_dialog)
self.main_layout.setSpacing(0)

View File

@ -242,7 +242,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog, RegistryProperties)
Creates a html element. If ``text`` is given, the element's text will set and if a ``parent`` is given,
the element is appended.
:param tag: The html tag, e. g. ``u'span'``. Defaults to ``None``.
:param tag: The html tag, e. g. ``'span'``. Defaults to ``None``.
:param text: The text for the tag. Defaults to ``None``.
:param parent: The parent element. Defaults to ``None``.
:param classId: Value for the class attribute

View File

@ -32,6 +32,7 @@ The UI widgets for the service item edit dialog
from PyQt4 import QtGui
from openlp.core.common import translate
from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_button_box, create_button
@ -44,6 +45,7 @@ class Ui_ServiceItemEditDialog(object):
Set up the UI
"""
serviceItemEditDialog.setObjectName('serviceItemEditDialog')
serviceItemEditDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
self.dialog_layout = QtGui.QGridLayout(serviceItemEditDialog)
self.dialog_layout.setContentsMargins(8, 8, 8, 8)
self.dialog_layout.setSpacing(8)

View File

@ -401,9 +401,12 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
:param suffix_list: New Suffix's to be supported
"""
for suffix in suffix_list:
if suffix not in self.suffixes:
self.suffixes.append(suffix)
if isinstance(suffix_list, str):
self.suffixes.append(suffix_list)
else:
for suffix in suffix_list:
if suffix not in self.suffixes:
self.suffixes.append(suffix)
def on_new_service_clicked(self, field=None):
"""
@ -1081,6 +1084,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
:param field:
:param message: The data passed in from a remove message
"""
self.log_debug(message)
self.set_item(int(message))
def set_item(self, index):
@ -1089,7 +1093,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
:param index: The index of the service item list to be actioned.
"""
if 0 >= index < self.service_manager_list.topLevelItemCount():
if 0 <= index < self.service_manager_list.topLevelItemCount():
item = self.service_manager_list.topLevelItem(index)
self.service_manager_list.setCurrentItem(item)
self.make_live()

View File

@ -45,8 +45,8 @@ class Ui_SettingsDialog(object):
Set up the UI
"""
settings_dialog.setObjectName('settings_dialog')
settings_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
settings_dialog.resize(800, 500)
settings_dialog.setWindowIcon(build_icon(':/system/system_settings.png'))
self.dialog_layout = QtGui.QGridLayout(settings_dialog)
self.dialog_layout.setObjectName('dialog_layout')
self.dialog_layout.setMargin(8)

View File

@ -66,6 +66,7 @@ class Ui_ShortcutListDialog(object):
Set up the UI
"""
shortcutListDialog.setObjectName('shortcutListDialog')
shortcutListDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
shortcutListDialog.resize(500, 438)
self.shortcut_list_layout = QtGui.QVBoxLayout(shortcutListDialog)
self.shortcut_list_layout.setObjectName('shortcut_list_layout')

View File

@ -244,10 +244,10 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties)
self.primary_push_button.setChecked(False)
self.alternate_push_button.setChecked(False)
else:
if action.defaultShortcuts:
primary_label_text = action.defaultShortcuts[0].toString()
if len(action.defaultShortcuts) == 2:
alternate_label_text = action.defaultShortcuts[1].toString()
if action.default_shortcuts:
primary_label_text = action.default_shortcuts[0].toString()
if len(action.default_shortcuts) == 2:
alternate_label_text = action.default_shortcuts[1].toString()
shortcuts = self._action_shortcuts(action)
# We do not want to loose pending changes, that is why we have to keep the text when, this function has not
# been triggered by a signal.
@ -292,7 +292,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties)
self._adjust_button(self.alternate_push_button, False, text='')
for category in self.action_list.categories:
for action in category.actions:
self.changed_actions[action] = action.defaultShortcuts
self.changed_actions[action] = action.default_shortcuts
self.refresh_shortcut_list()
def on_default_radio_button_clicked(self, toggled):
@ -306,7 +306,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties)
if action is None:
return
temp_shortcuts = self._action_shortcuts(action)
self.changed_actions[action] = action.defaultShortcuts
self.changed_actions[action] = action.default_shortcuts
self.refresh_shortcut_list()
primary_button_text = ''
alternate_button_text = ''
@ -357,8 +357,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties)
return
shortcuts = self._action_shortcuts(action)
new_shortcuts = []
if action.defaultShortcuts:
new_shortcuts.append(action.defaultShortcuts[0])
if action.default_shortcuts:
new_shortcuts.append(action.default_shortcuts[0])
# We have to check if the primary default shortcut is available. But we only have to check, if the action
# has a default primary shortcut (an "empty" shortcut is always valid and if the action does not have a
# default primary shortcut, then the alternative shortcut (not the default one) will become primary
@ -383,8 +383,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties)
new_shortcuts = []
if shortcuts:
new_shortcuts.append(shortcuts[0])
if len(action.defaultShortcuts) == 2:
new_shortcuts.append(action.defaultShortcuts[1])
if len(action.default_shortcuts) == 2:
new_shortcuts.append(action.default_shortcuts[1])
if len(new_shortcuts) == 2:
if not self._validiate_shortcut(action, new_shortcuts[1]):
return

View File

@ -32,6 +32,7 @@ The UI widgets for the time dialog
from PyQt4 import QtCore, QtGui
from openlp.core.common import UiStrings, translate
from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_button_box
@ -44,6 +45,7 @@ class Ui_StartTimeDialog(object):
Set up the UI
"""
StartTimeDialog.setObjectName('StartTimeDialog')
StartTimeDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
StartTimeDialog.resize(350, 10)
self.dialog_layout = QtGui.QGridLayout(StartTimeDialog)
self.dialog_layout.setObjectName('dialog_layout')

View File

@ -18,11 +18,11 @@
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# AN_y WARRANT_y; without even the implied warranty of MERCHANTABILIT_y or #
# 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 #
# 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 #
###############################################################################
@ -90,7 +90,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties):
self.footer_font_combo_box.activated.connect(self.update_theme)
self.footer_size_spin_box.valueChanged.connect(self.update_theme)
def setDefaults(self):
def set_defaults(self):
"""
Set up display at start of theme edit.
"""
@ -179,7 +179,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties):
if self.page(self.currentId()) == self.background_page and \
self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename):
QtGui.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
translate('OpenLP.ThemeWizard', '_you have not selected a '
translate('OpenLP.ThemeWizard', 'You have not selected a '
'background image. Please select one before continuing.'))
return False
else:
@ -261,7 +261,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties):
log.debug('Editing theme %s' % self.theme.theme_name)
self.temp_background_filename = ''
self.update_theme_allowed = False
self.setDefaults()
self.set_defaults()
self.update_theme_allowed = True
self.theme_name_label.setVisible(not edit)
self.theme_name_edit.setVisible(not edit)

View File

@ -32,6 +32,7 @@ The layout of the theme
from PyQt4 import QtGui
from openlp.core.common import translate
from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_button_box
@ -44,6 +45,7 @@ class Ui_ThemeLayoutDialog(object):
Set up the UI
"""
themeLayoutDialog.setObjectName('themeLayoutDialogDialog')
themeLayoutDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
self.preview_layout = QtGui.QVBoxLayout(themeLayoutDialog)
self.preview_layout.setObjectName('preview_layout')
self.preview_area = QtGui.QWidget(themeLayoutDialog)

View File

@ -190,7 +190,7 @@ class ThemesTab(SettingsTab):
:param theme_list: The list of available themes::
[u'Bible Theme', u'Song Theme']
['Bible Theme', 'Song Theme']
"""
# Reload as may have been triggered by the ThemeManager.
self.global_theme = Settings().value(self.settings_section + '/global theme')

View File

@ -46,6 +46,7 @@ class Ui_ThemeWizard(object):
Set up the UI
"""
themeWizard.setObjectName('OpenLP.ThemeWizard')
themeWizard.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
themeWizard.setModal(True)
themeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
themeWizard.setOptions(QtGui.QWizard.IndependentPages |

View File

@ -51,6 +51,7 @@ class WizardStrings(object):
CSV = 'CSV'
OS = 'OpenSong'
OSIS = 'OSIS'
ZEF = 'Zefania'
# These strings should need a good reason to be retranslated elsewhere.
FinishedImport = translate('OpenLP.Ui', 'Finished import.')
FormatLabel = translate('OpenLP.Ui', 'Format:')
@ -118,6 +119,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties):
"""
Set up the wizard UI.
"""
self.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
self.setModal(True)
self.setWizardStyle(QtGui.QWizard.ModernStyle)
self.setOptions(QtGui.QWizard.IndependentPages |
@ -197,7 +199,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties):
"""
Run the wizard.
"""
self.setDefaults()
self.set_defaults()
return QtGui.QWizard.exec_(self)
def reject(self):
@ -279,7 +281,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties):
:param filters: The file extension filters. It should contain the file description
as well as the file extension. For example::
u'OpenLP 2.0 Databases (*.sqlite)'
'OpenLP 2.0 Databases (*.sqlite)'
"""
if filters:
filters += ';;'

View File

@ -113,7 +113,7 @@ def get_application_version():
"""
Returns the application version of the running instance of OpenLP::
{u'full': u'1.9.4-bzr1249', u'version': u'1.9.4', u'build': u'bzr1249'}
{'full': '1.9.4-bzr1249', 'version': '1.9.4', 'build': 'bzr1249'}
"""
global APPLICATION_VERSION
if APPLICATION_VERSION:
@ -149,9 +149,9 @@ def get_application_version():
# If they are equal, then this tree is tarball with the source for the release. We do not want the revision
# number in the full version.
if tree_revision == tag_revision:
full_version = tag_version
full_version = tag_version.decode('utf-8')
else:
full_version = '%s-bzr%s' % (tag_version, tree_revision)
full_version = '%s-bzr%s' % (tag_version.decode('utf-8'), tree_revision.decode('utf-8'))
else:
# We're not running the development version, let's use the file.
filepath = AppLocation.get_directory(AppLocation.VersionDir)

View File

@ -65,20 +65,14 @@ class CategoryActionList(object):
self.index = 0
self.actions = []
def __getitem__(self, key):
"""
Implement the __getitem__() method to make this class a dictionary type
"""
for weight, action in self.actions:
if action.text() == key:
return action
raise KeyError('Action "%s" does not exist.' % key)
def __contains__(self, item):
def __contains__(self, key):
"""
Implement the __contains__() method to make this class a dictionary type
"""
return item in self
for weight, action in self.actions:
if action == key:
return True
return False
def __len__(self):
"""
@ -103,23 +97,14 @@ class CategoryActionList(object):
self.index += 1
return self.actions[self.index - 1][1]
def has_key(self, key):
"""
Implement the has_key() method to make this class a dictionary type
"""
for weight, action in self.actions:
if action.text() == key:
return True
return False
def append(self, name):
def append(self, action):
"""
Append an action
"""
weight = 0
if self.actions:
weight = self.actions[-1][0] + 1
self.add(name, weight)
self.add(action, weight)
def add(self, action, weight=0):
"""
@ -128,14 +113,15 @@ class CategoryActionList(object):
self.actions.append((weight, action))
self.actions.sort(key=lambda act: act[0])
def remove(self, remove_action):
def remove(self, action):
"""
Remove an action
"""
for action in self.actions:
if action[1] == remove_action:
self.actions.remove(action)
for item in self.actions:
if item[1] == action:
self.actions.remove(item)
return
raise ValueError('Action "%s" does not exist.' % action)
class CategoryList(object):
@ -184,9 +170,9 @@ class CategoryList(object):
self.index += 1
return self.categories[self.index - 1]
def has_key(self, key):
def __contains__(self, key):
"""
Implement the has_key() method to make this class like a dictionary
Implement the __contains__() method to make this class like a dictionary
"""
for category in self.categories:
if category.name == key:
@ -200,10 +186,7 @@ class CategoryList(object):
weight = 0
if self.categories:
weight = self.categories[-1].weight + 1
if actions:
self.add(name, weight, actions)
else:
self.add(name, weight)
self.add(name, weight, actions)
def add(self, name, weight=0, actions=None):
"""
@ -226,6 +209,8 @@ class CategoryList(object):
for category in self.categories:
if category.name == name:
self.categories.remove(category)
return
raise ValueError('Category "%s" does not exist.' % name)
class ActionList(object):
@ -270,7 +255,7 @@ class ActionList(object):
settings = Settings()
settings.beginGroup('shortcuts')
# Get the default shortcut from the config.
action.defaultShortcuts = settings.get_default_value(action.objectName())
action.default_shortcuts = settings.get_default_value(action.objectName())
if weight is None:
self.categories[category].actions.append(action)
else:

View File

@ -71,8 +71,7 @@ class LanguageManager(object):
"""
Find all available language files in this OpenLP install
"""
log.debug('Translation files: %s', AppLocation.get_directory(
AppLocation.LanguageDir))
log.debug('Translation files: %s', 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)
# Remove qm files from the list which start with "qt_".

View File

@ -32,7 +32,7 @@ other class holds all the functional code, like slots and loading and saving.
The first class, commonly known as the **Dialog** class, is typically named ``Ui_<name>Dialog``. It is a slightly
modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings.
converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings.
The second class, commonly known as the **Form** class, is typically named ``<name>Form``. This class is the one which
is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned

View File

@ -46,7 +46,7 @@ class Ui_AlertDialog(object):
"""
alert_dialog.setObjectName('alert_dialog')
alert_dialog.resize(400, 300)
alert_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
alert_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
self.alert_dialog_layout = QtGui.QGridLayout(alert_dialog)
self.alert_dialog_layout.setObjectName('alert_dialog_layout')
self.alert_text_layout = QtGui.QFormLayout()

View File

@ -33,7 +33,7 @@ other class holds all the functional code, like slots and loading and saving.
The first class, commonly known as the **Dialog** class, is typically named ``Ui_<name>Dialog``. It is a slightly
modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings.
converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings.
The second class, commonly known as the **Form** class, is typically named ``<name>Form``. This class is the one which
is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned

View File

@ -110,6 +110,7 @@ class BibleImportForm(OpenLPWizard):
self.csv_books_button.clicked.connect(self.on_csv_books_browse_button_clicked)
self.csv_verses_button.clicked.connect(self.on_csv_verses_browse_button_clicked)
self.open_song_browse_button.clicked.connect(self.on_open_song_browse_button_clicked)
self.zefania_browse_button.clicked.connect(self.on_zefania_browse_button_clicked)
def add_custom_pages(self):
"""
@ -125,7 +126,7 @@ class BibleImportForm(OpenLPWizard):
self.format_label = QtGui.QLabel(self.select_page)
self.format_label.setObjectName('FormatLabel')
self.format_combo_box = QtGui.QComboBox(self.select_page)
self.format_combo_box.addItems(['', '', '', ''])
self.format_combo_box.addItems(['', '', '', '', ''])
self.format_combo_box.setObjectName('FormatComboBox')
self.format_layout.addRow(self.format_label, self.format_combo_box)
self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
@ -247,6 +248,25 @@ class BibleImportForm(OpenLPWizard):
self.web_proxy_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.web_password_edit)
self.web_tab_widget.addTab(self.web_proxy_tab, '')
self.select_stack.addWidget(self.web_tab_widget)
self.zefania_widget = QtGui.QWidget(self.select_page)
self.zefania_widget.setObjectName('ZefaniaWidget')
self.zefania_layout = QtGui.QFormLayout(self.zefania_widget)
self.zefania_layout.setMargin(0)
self.zefania_layout.setObjectName('ZefaniaLayout')
self.zefania_file_label = QtGui.QLabel(self.zefania_widget)
self.zefania_file_label.setObjectName('ZefaniaFileLabel')
self.zefania_file_layout = QtGui.QHBoxLayout()
self.zefania_file_layout.setObjectName('ZefaniaFileLayout')
self.zefania_file_edit = QtGui.QLineEdit(self.zefania_widget)
self.zefania_file_edit.setObjectName('ZefaniaFileEdit')
self.zefania_file_layout.addWidget(self.zefania_file_edit)
self.zefania_browse_button = QtGui.QToolButton(self.zefania_widget)
self.zefania_browse_button.setIcon(self.open_icon)
self.zefania_browse_button.setObjectName('ZefaniaBrowseButton')
self.zefania_file_layout.addWidget(self.zefania_browse_button)
self.zefania_layout.addRow(self.zefania_file_label, self.zefania_file_layout)
self.zefania_layout.setItem(5, QtGui.QFormLayout.LabelRole, self.spacer)
self.select_stack.addWidget(self.zefania_widget)
self.select_page_layout.addLayout(self.select_stack)
self.addPage(self.select_page)
# License Page
@ -294,11 +314,13 @@ class BibleImportForm(OpenLPWizard):
self.format_combo_box.setItemText(BibleFormat.OpenSong, WizardStrings.OS)
self.format_combo_box.setItemText(BibleFormat.WebDownload, translate('BiblesPlugin.ImportWizardForm',
'Web Download'))
self.format_combo_box.setItemText(BibleFormat.Zefania, WizardStrings.ZEF)
self.osis_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
self.csv_books_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Books file:'))
self.csv_verses_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Verses file:'))
self.open_song_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
self.web_source_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Location:'))
self.zefania_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
self.web_source_combo_box.setItemText(WebDownload.Crosswalk, translate('BiblesPlugin.ImportWizardForm',
'Crosswalk'))
self.web_source_combo_box.setItemText(WebDownload.BibleGateway, translate('BiblesPlugin.ImportWizardForm',
@ -331,7 +353,8 @@ class BibleImportForm(OpenLPWizard):
self.osis_file_label.minimumSizeHint().width(),
self.csv_books_label.minimumSizeHint().width(),
self.csv_verses_label.minimumSizeHint().width(),
self.open_song_file_label.minimumSizeHint().width())
self.open_song_file_label.minimumSizeHint().width(),
self.zefania_file_label.minimumSizeHint().width())
self.spacer.changeSize(label_width, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
def validateCurrentPage(self):
@ -366,6 +389,11 @@ class BibleImportForm(OpenLPWizard):
critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.OS)
self.open_song_file_edit.setFocus()
return False
elif self.field('source_format') == BibleFormat.Zefania:
if not self.field('zefania_file'):
critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.ZEF)
self.zefania_file_edit.setFocus()
return False
elif self.field('source_format') == BibleFormat.WebDownload:
self.version_name_edit.setText(self.web_translation_combo_box.currentText())
return True
@ -447,6 +475,13 @@ class BibleImportForm(OpenLPWizard):
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.open_song_file_edit,
'last directory import')
def on_zefania_browse_button_clicked(self):
"""
Show the file open dialog for the Zefania file.
"""
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.ZEF, self.zefania_file_edit,
'last directory import')
def register_fields(self):
"""
Register the bible import wizard fields.
@ -456,6 +491,7 @@ class BibleImportForm(OpenLPWizard):
self.select_page.registerField('csv_booksfile', self.csv_books_edit)
self.select_page.registerField('csv_versefile', self.csv_verses_edit)
self.select_page.registerField('opensong_file', self.open_song_file_edit)
self.select_page.registerField('zefania_file', self.zefania_file_edit)
self.select_page.registerField('web_location', self.web_source_combo_box)
self.select_page.registerField('web_biblename', self.web_translation_combo_box)
self.select_page.registerField('proxy_server', self.web_server_edit)
@ -465,7 +501,7 @@ class BibleImportForm(OpenLPWizard):
self.license_details_page.registerField('license_copyright', self.copyright_edit)
self.license_details_page.registerField('license_permissions', self.permissions_edit)
def setDefaults(self):
def set_defaults(self):
"""
Set default values for the wizard pages.
"""
@ -479,6 +515,7 @@ class BibleImportForm(OpenLPWizard):
self.setField('csv_booksfile', '')
self.setField('csv_versefile', '')
self.setField('opensong_file', '')
self.setField('zefania_file', '')
self.setField('web_location', WebDownload.Crosswalk)
self.setField('web_biblename', self.web_translation_combo_box.currentIndex())
self.setField('proxy_server', settings.value('proxy address'))
@ -562,6 +599,10 @@ class BibleImportForm(OpenLPWizard):
proxy_username=self.field('proxy_username'),
proxy_password=self.field('proxy_password')
)
elif bible_type == BibleFormat.Zefania:
# Import an Zefania bible.
importer = self.manager.import_bible(BibleFormat.Zefania, name=license_version,
filename=self.field('zefania_file'))
if importer.do_import(license_version):
self.manager.save_meta_data(license_version, license_version, license_copyright, license_permissions)
self.manager.reload_bibles()

View File

@ -307,7 +307,7 @@ class BibleUpgradeForm(OpenLPWizard):
if self.currentPage() == self.progress_page:
return True
def setDefaults(self):
def set_defaults(self):
"""
Set default values for the wizard pages.
"""

View File

@ -30,12 +30,14 @@
from PyQt4 import QtCore, QtGui
from openlp.core.common import translate
from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_button_box
class Ui_BookNameDialog(object):
def setupUi(self, book_name_dialog):
book_name_dialog.setObjectName('book_name_dialog')
book_name_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
book_name_dialog.resize(400, 271)
self.book_name_layout = QtGui.QVBoxLayout(book_name_dialog)
self.book_name_layout.setSpacing(8)

View File

@ -39,8 +39,8 @@ from openlp.plugins.bibles.lib.db import BiblesResourcesDB
class Ui_EditBibleDialog(object):
def setupUi(self, edit_bible_dialog):
edit_bible_dialog.setObjectName('edit_bible_dialog')
edit_bible_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
edit_bible_dialog.resize(520, 400)
edit_bible_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
edit_bible_dialog.setModal(True)
self.dialog_layout = QtGui.QVBoxLayout(edit_bible_dialog)
self.dialog_layout.setSpacing(8)

View File

@ -30,12 +30,14 @@
from PyQt4 import QtGui
from openlp.core.common import translate
from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_button_box
class Ui_LanguageDialog(object):
def setupUi(self, language_dialog):
language_dialog.setObjectName('language_dialog')
language_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
language_dialog.resize(400, 165)
self.language_layout = QtGui.QVBoxLayout(language_dialog)
self.language_layout.setSpacing(8)

View File

@ -262,7 +262,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
For example::
[(u'John', 3, 16, 18), (u'John', 4, 1, 1)]
[('John', 3, 16, 18), ('John', 4, 1, 1)]
**Reference string details:**
@ -311,7 +311,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
``(?P<to_verse>[0-9]+)``
The ``to_verse`` reference is equivalent to group 2.
The full reference is matched against get_reference_match(u'full'). This regular expression looks like this:
The full reference is matched against get_reference_match('full'). This regular expression looks like this:
``^\s*(?!\s)(?P<book>[\d]*[^\d]+)(?<!\s)\s*``
The ``book`` group starts with the first non-whitespace character. There are optional leading digits followed by

View File

@ -405,7 +405,7 @@ class BiblesTab(SettingsTab):
:param theme_list:
The list of available themes::
[u'Bible Theme', u'Song Theme']
['Bible Theme', 'Song Theme']
"""
self.bible_theme_combo_box.clear()
self.bible_theme_combo_box.addItem('')

View File

@ -149,7 +149,7 @@ class CSVBible(BibleDB):
book_ptr = book.name
self.wizard.increment_progress_bar(
translate('BiblesPlugin.CSVBible',
'Importing verses from %s... Importing verses from <book name>...') % book.name)
'Importing verses from %s...' % book.name, 'Importing verses from <book name>...'))
self.session.commit()
try:
verse_text = str(line[3], details['encoding'])

View File

@ -370,17 +370,16 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
This is probably the most used function. It retrieves the list of
verses based on the user's query.
:param reference_list: This is the list of references the media manager item wants. It is
a list of tuples, with the following format::
:param reference_list: This is the list of references the media manager item wants. It is a list of tuples, with
the following format::
(book_reference_id, chapter, start_verse, end_verse)
Therefore, when you are looking for multiple items, simply break
them up into references like this, bundle them into a list. This
function then runs through the list, and returns an amalgamated
list of ``Verse`` objects. For example::
Therefore, when you are looking for multiple items, simply break them up into references like this, bundle
them into a list. This function then runs through the list, and returns an amalgamated list of ``Verse``
objects. For example::
[(u'35', 1, 1, 1), (u'35', 2, 2, 3)]
[('35', 1, 1, 1), ('35', 2, 2, 3)]
:param show_error:
"""
log.debug('BibleDB.get_verses("%s")' % reference_list)

View File

@ -225,7 +225,7 @@ class BGExtract(RegistryProperties):
url_book_name = urllib.parse.quote(book_name.encode("utf-8"))
url_params = 'search=%s+%s&version=%s' % (url_book_name, chapter, version)
soup = get_soup_for_bible_ref(
'http://www.biblegateway.com/passage/?%s' % url_params,
'http://legacy.biblegateway.com/passage/?%s' % url_params,
pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='')
if not soup:
return None
@ -252,7 +252,7 @@ class BGExtract(RegistryProperties):
"""
log.debug('BGExtract.get_books_from_http("%s")', version)
url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '%s' % version})
reference_url = 'http://www.biblegateway.com/versions/?%s#books' % url_params
reference_url = 'http://legacy.biblegateway.com/versions/?%s#books' % url_params
page = get_web_page(reference_url)
if not page:
send_error_message('download')
@ -534,7 +534,7 @@ class HTTPBible(BibleDB, RegistryProperties):
them into a list. This function then runs through the list, and returns an amalgamated list of ``Verse``
objects. For example::
[(u'35', 1, 1, 1), (u'35', 2, 2, 3)]
[('35', 1, 1, 1), ('35', 2, 2, 3)]
"""
log.debug('HTTPBible.get_verses("%s")', reference_list)
for reference in reference_list:

View File

@ -38,6 +38,7 @@ from .csvbible import CSVBible
from .http import HTTPBible
from .opensong import OpenSongBible
from .osis import OSISBible
from .zefania import ZefaniaBible
log = logging.getLogger(__name__)
@ -52,22 +53,25 @@ class BibleFormat(object):
CSV = 1
OpenSong = 2
WebDownload = 3
Zefania = 4
@staticmethod
def get_class(format):
def get_class(bible_format):
"""
Return the appropriate implementation class.
:param format: The Bible format.
:param bible_format: The Bible format.
"""
if format == BibleFormat.OSIS:
if bible_format == BibleFormat.OSIS:
return OSISBible
elif format == BibleFormat.CSV:
elif bible_format == BibleFormat.CSV:
return CSVBible
elif format == BibleFormat.OpenSong:
elif bible_format == BibleFormat.OpenSong:
return OpenSongBible
elif format == BibleFormat.WebDownload:
elif bible_format == BibleFormat.WebDownload:
return HTTPBible
elif bible_format == BibleFormat.Zefania:
return ZefaniaBible
else:
return None
@ -81,6 +85,7 @@ class BibleFormat(object):
BibleFormat.CSV,
BibleFormat.OpenSong,
BibleFormat.WebDownload,
BibleFormar.Zefania,
]

View File

@ -60,7 +60,6 @@ class BibleMediaItem(MediaManagerItem):
log.info('Bible Media Item loaded')
def __init__(self, parent, plugin):
self.icon_path = 'songs/song'
self.lock_icon = build_icon(':/bibles/bibles_search_lock.png')
self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png')
MediaManagerItem.__init__(self, parent, plugin)
@ -172,6 +171,7 @@ class BibleMediaItem(MediaManagerItem):
self.page_layout.addWidget(tab)
tab.setVisible(False)
lock_button.toggled.connect(self.on_lock_button_toggled)
second_combo_box.currentIndexChanged.connect(self.on_second_bible_combobox_index_changed)
setattr(self, prefix + 'VersionLabel', version_label)
setattr(self, prefix + 'VersionComboBox', version_combo_box)
setattr(self, prefix + 'SecondLabel', second_label)
@ -263,11 +263,15 @@ class BibleMediaItem(MediaManagerItem):
def config_update(self):
log.debug('config_update')
if Settings().value(self.settings_section + '/second bibles'):
self.quickSecondLabel.setVisible(True)
self.quickSecondComboBox.setVisible(True)
self.advancedSecondLabel.setVisible(True)
self.advancedSecondComboBox.setVisible(True)
self.quickSecondLabel.setVisible(True)
self.quickSecondComboBox.setVisible(True)
else:
self.quickSecondLabel.setVisible(False)
self.quickSecondComboBox.setVisible(False)
self.advancedSecondLabel.setVisible(False)
self.advancedSecondComboBox.setVisible(False)
self.quickSecondLabel.setVisible(False)
@ -360,8 +364,7 @@ class BibleMediaItem(MediaManagerItem):
combo boxes on the 'Advanced Search' Tab. This is not of any importance of the 'Quick Search' Tab.
:param bible: The bible to initialise (unicode).
:param last_book_id: The "book reference id" of the book which is chosen at the moment.
(int)
:param last_book_id: The "book reference id" of the book which is chosen at the moment. (int)
"""
log.debug('initialise_advanced_bible %s, %s', bible, last_book_id)
book_data = self.plugin.manager.get_books(bible)
@ -421,9 +424,8 @@ class BibleMediaItem(MediaManagerItem):
def update_auto_completer(self):
"""
This updates the bible book completion list for the search field. The
completion depends on the bible. It is only updated when we are doing a
reference search, otherwise the auto completion list is removed.
This updates the bible book completion list for the search field. The completion depends on the bible. It is
only updated when we are doing a reference search, otherwise the auto completion list is removed.
"""
log.debug('update_auto_completer')
# Save the current search type to the configuration.
@ -461,6 +463,17 @@ class BibleMediaItem(MediaManagerItem):
books.sort(key=get_locale_key)
set_case_insensitive_completer(books, self.quick_search_edit)
def on_second_bible_combobox_index_changed(self, selection):
"""
Activate the style combobox only when no second bible is selected
"""
if selection == 0:
self.quickStyleComboBox.setEnabled(True)
self.advancedStyleComboBox.setEnabled(True)
else:
self.quickStyleComboBox.setEnabled(False)
self.advancedStyleComboBox.setEnabled(False)
def on_import_click(self):
if not hasattr(self, 'import_wizard'):
self.import_wizard = BibleImportForm(self, self.plugin.manager, self.plugin)
@ -593,8 +606,7 @@ class BibleMediaItem(MediaManagerItem):
:param range_from: The first number of the range (int).
:param range_to: The last number of the range (int).
:param combo: The combo box itself (QComboBox).
:param restore: If True, then the combo's currentText will be restored after
adjusting (if possible).
:param restore: If True, then the combo's currentText will be restored after adjusting (if possible).
"""
log.debug('adjust_combo_box %s, %s, %s', combo, range_from, range_to)
if restore:
@ -640,8 +652,8 @@ class BibleMediaItem(MediaManagerItem):
def on_quick_search_button(self):
"""
Does a quick search and saves the search results. Quick search can
either be "Reference Search" or "Text Search".
Does a quick search and saves the search results. Quick search can either be "Reference Search" or
"Text Search".
"""
log.debug('Quick Search Button clicked')
self.quickSearchButton.setEnabled(False)
@ -696,8 +708,7 @@ class BibleMediaItem(MediaManagerItem):
def display_results(self, bible, second_bible=''):
"""
Displays the search results in the media manager. All data needed for
further action is saved for/in each row.
Displays the search results in the media manager. All data needed for further action is saved for/in each row.
"""
items = self.build_display_results(bible, second_bible, self.search_results)
for bible_verse in items:
@ -708,8 +719,7 @@ class BibleMediaItem(MediaManagerItem):
def build_display_results(self, bible, second_bible, search_results):
"""
Displays the search results in the media manager. All data needed for
further action is saved for/in each row.
Displays the search results in the media manager. All data needed for further action is saved for/in each row.
"""
verse_separator = get_reference_separator('sep_v_display')
version = self.plugin.manager.get_meta_data(bible, 'name').value
@ -837,7 +847,6 @@ class BibleMediaItem(MediaManagerItem):
# If there are no more items we check whether we have to add bible_text.
if bible_text:
raw_slides.append(bible_text.lstrip())
bible_text = ''
# Service Item: Capabilities
if self.settings.layout_style == LayoutStyle.Continuous and not second_bible:
# Split the line but do not replace line breaks in renderer.
@ -859,9 +868,8 @@ class BibleMediaItem(MediaManagerItem):
def format_title(self, start_bitem, old_bitem):
"""
This method is called, when we have to change the title, because
we are at the end of a verse range. E. g. if we want to add
Genesis 1:1-6 as well as Daniel 2:14.
This method is called, when we have to change the title, because we are at the end of a verse range. E. g. if we
want to add Genesis 1:1-6 as well as Daniel 2:14.
:param start_bitem: The first item of a range.
:param old_bitem: The last item of a range.
@ -891,10 +899,8 @@ class BibleMediaItem(MediaManagerItem):
def check_title(self, bitem, old_bitem):
"""
This method checks if we are at the end of an verse range. If that is
the case, we return True, otherwise False. E. g. if we added
Genesis 1:1-6, but the next verse is Daniel 2:14, we return True.
This method checks if we are at the end of an verse range. If that is the case, we return True, otherwise False.
E. g. if we added Genesis 1:1-6, but the next verse is Daniel 2:14, we return True.
:param bitem: The item we are dealing with at the moment.
:param old_bitem: The item we were previously dealing with.
@ -918,20 +924,17 @@ class BibleMediaItem(MediaManagerItem):
return True
elif old_chapter + 1 == chapter and (verse != 1 or old_verse !=
self.plugin.manager.get_verse_count(old_bible, old_book, old_chapter)):
# We are in the following chapter, but the last verse was not the
# last verse of the chapter or the current verse is not the
# first one of the chapter.
# We are in the following chapter, but the last verse was not the last verse of the chapter or the current
# verse is not the first one of the chapter.
return True
return False
def format_verse(self, old_chapter, chapter, verse):
"""
Formats and returns the text, each verse starts with, for the given
chapter and verse. The text is either surrounded by round, square,
Formats and returns the text, each verse starts with, for the given chapter and verse. The text is either
surrounded by round, square, curly brackets or no brackets at all. For example::
curly brackets or no brackets at all. For example::
u'{su}1:1{/su}'
'{su}1:1{/su}'
:param old_chapter: The previous verse's chapter number (int).
:param chapter: The chapter number (int).

View File

@ -81,6 +81,13 @@ class OpenSongBible(BibleDB):
import_file = open(self.filename, 'rb')
opensong = objectify.parse(import_file)
bible = opensong.getroot()
# Check that we're not trying to import a Zefania XML bible, it is sometimes refered to as 'OpenSong'
if bible.tag.upper() == 'XMLBIBLE':
critical_error_message_box(
message=translate('BiblesPlugin.OpenSongImport',
'Incorrect Bible file type supplied. This looks like a Zefania XML bible, '
'please use the Zefania import option.'))
return False
language_id = self.get_language(bible_name)
if not language_id:
log.error('Importing books from "%s" failed' % self.filename)

View File

@ -0,0 +1,113 @@
# -*- 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 #
###############################################################################
import logging
from lxml import etree, objectify
from openlp.core.common import translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
log = logging.getLogger(__name__)
class ZefaniaBible(BibleDB):
"""
Zefania Bible format importer class.
"""
def __init__(self, parent, **kwargs):
"""
Constructor to create and set up an instance of the ZefaniaBible class. This class is used to import Bibles
from ZefaniaBible's XML format.
"""
log.debug(self.__class__.__name__)
BibleDB.__init__(self, parent, **kwargs)
self.filename = kwargs['filename']
def do_import(self, bible_name=None):
"""
Loads a Bible from file.
"""
log.debug('Starting Zefania import from "%s"' % self.filename)
if not isinstance(self.filename, str):
self.filename = str(self.filename, 'utf8')
import_file = None
success = True
try:
# NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding
# detection, and the two mechanisms together interfere with each other.
import_file = open(self.filename, 'rb')
language_id = self.get_language(bible_name)
if not language_id:
log.error('Importing books from "%s" failed' % self.filename)
return False
zefania_bible_tree = etree.parse(import_file)
num_books = int(zefania_bible_tree.xpath("count(//BIBLEBOOK)"))
# Strip tags we don't use - keep content
etree.strip_tags(zefania_bible_tree, ('STYLE', 'GRAM', 'NOTE', 'SUP', 'XREF'))
# Strip tags we don't use - remove content
etree.strip_elements(zefania_bible_tree, ('PROLOG', 'REMARK', 'CAPTION', 'MEDIA'), with_tail=False)
xmlbible = zefania_bible_tree.getroot()
for BIBLEBOOK in xmlbible:
book_ref_id = self.get_book_ref_id_by_name(str(BIBLEBOOK.get('bname')), num_books)
if not book_ref_id:
book_ref_id = self.get_book_ref_id_by_localised_name(str(BIBLEBOOK.get('bname')))
if not book_ref_id:
log.error('Importing books from "%s" failed' % self.filename)
return False
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.create_book(book_details['name'], book_ref_id, book_details['testament_id'])
for CHAPTER in BIBLEBOOK:
if self.stop_import_flag:
break
chapter_number = CHAPTER.get("cnumber")
for VERS in CHAPTER:
verse_number = VERS.get("vnumber")
self.create_verse(db_book.id, chapter_number, verse_number, VERS.text.replace('<BR/>', '\n'))
self.wizard.increment_progress_bar(
translate('BiblesPlugin.Zefnia', 'Importing %(bookname)s %(chapter)s...' %
{'bookname': db_book.name, 'chapter': chapter_number}))
self.session.commit()
self.application.process_events()
except Exception as e:
print(str(e))
critical_error_message_box(
message=translate('BiblesPlugin.ZefaniaImport',
'Incorrect Bible file type supplied. Zefania Bibles may be '
'compressed. You must decompress them before import.'))
log.exception(str(e))
success = False
finally:
if import_file:
import_file.close()
if self.stop_import_flag:
return False
else:
return success

View File

@ -41,8 +41,8 @@ class Ui_CustomEditDialog(object):
:param custom_edit_dialog: The Dialog
"""
custom_edit_dialog.setObjectName('custom_edit_dialog')
custom_edit_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
custom_edit_dialog.resize(450, 350)
custom_edit_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
self.dialog_layout = QtGui.QVBoxLayout(custom_edit_dialog)
self.dialog_layout.setObjectName('dialog_layout')
self.title_layout = QtGui.QHBoxLayout()

View File

@ -197,7 +197,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
self.slide_list_view.clear()
self.slide_list_view.addItems(slides)
else:
old_slides = []
old_row = self.slide_list_view.currentRow()
# Create a list with all (old/unedited) slides.
old_slides = [self.slide_list_view.item(row).text() for row in range(self.slide_list_view.count())]

View File

@ -30,13 +30,14 @@
from PyQt4 import QtGui
from openlp.core.common import UiStrings, translate
from openlp.core.lib import SpellTextEdit
from openlp.core.lib import SpellTextEdit, build_icon
from openlp.core.lib.ui import create_button, create_button_box
class Ui_CustomSlideEditDialog(object):
def setupUi(self, custom_slide_edit_dialog):
custom_slide_edit_dialog.setObjectName('custom_slide_edit_dialog')
custom_slide_edit_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
custom_slide_edit_dialog.resize(350, 300)
self.dialog_layout = QtGui.QVBoxLayout(custom_slide_edit_dialog)
self.slide_text_edit = SpellTextEdit(self)

View File

@ -32,7 +32,7 @@ other class holds all the functional code, like slots and loading and saving.
The first class, commonly known as the **Dialog** class, is typically named ``Ui_<name>Dialog``. It is a slightly
modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings.
converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings.
The second class, commonly known as the **Form** class, is typically named ``<name>Form``. This class is the one which
is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned

View File

@ -31,7 +31,7 @@ The :mod:`db` module provides the database and schema that is the backend for th
"""
from sqlalchemy import Column, ForeignKey, Table, types
from sqlalchemy.orm import mapper, relation, reconstructor
from sqlalchemy.orm import mapper
from openlp.core.lib.db import BaseModel, init_db

View File

@ -353,7 +353,7 @@ class ImageMediaItem(MediaManagerItem):
icon = build_icon(thumb)
else:
icon = create_thumb(imageFile.filename, thumb)
item_name = QtGui.QTreeWidgetItem(filename)
item_name = QtGui.QTreeWidgetItem([filename])
item_name.setText(0, filename)
item_name.setIcon(0, icon)
item_name.setToolTip(0, imageFile.filename)

View File

@ -229,7 +229,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
check_directory_exists(self.service_path)
self.load_list(Settings().value(self.settings_section + '/media files'))
self.populate_display_types()
self.rebuild_players()
def rebuild_players(self):
"""
@ -314,7 +314,6 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
def get_list(self, type=MediaType.Audio):
media = Settings().value(self.settings_section + '/media files')
media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
extension = []
if type == MediaType.Audio:
extension = self.media_controller.audio_extensions_list
else:

View File

@ -126,8 +126,8 @@ class PdfController(PresentationController):
if os.name == 'nt':
# for windows we only accept mudraw.exe in the base folder
application_path = AppLocation.get_directory(AppLocation.AppDir)
if os.path.isfile(application_path + '/../mudraw.exe'):
self.mudrawbin = application_path + '/../mudraw.exe'
if os.path.isfile(os.path.join(application_path, 'mudraw.exe')):
self.mudrawbin = os.path.join(application_path, 'mudraw.exe')
else:
DEVNULL = open(os.devnull, 'wb')
# First try to find mupdf
@ -145,8 +145,8 @@ class PdfController(PresentationController):
# Last option: check if mudraw is placed in OpenLP base folder
if not self.mudrawbin and not self.gsbin:
application_path = AppLocation.get_directory(AppLocation.AppDir)
if os.path.isfile(application_path + '/../mudraw'):
self.mudrawbin = application_path + '/../mudraw'
if os.path.isfile(os.path.join(application_path, 'mudraw')):
self.mudrawbin = os.path.join(application_path, 'mudraw')
if self.mudrawbin:
self.also_supports = ['xps']
return True

View File

@ -43,6 +43,8 @@ if os.name == 'nt':
from openlp.core.lib import ScreenList
from openlp.core.common import Registry
from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate
from openlp.core.common import trace_error_handler
from .presentationcontroller import PresentationController, PresentationDocument
log = logging.getLogger(__name__)
@ -103,7 +105,7 @@ class PowerpointController(PresentationController):
if self.process.Presentations.Count > 0:
return
self.process.Quit()
except pywintypes.com_error:
except (AttributeError, pywintypes.com_error):
pass
self.process = None
@ -130,12 +132,23 @@ class PowerpointDocument(PresentationDocument):
earlier.
"""
log.debug('load_presentation')
if not self.controller.process or not self.controller.process.Visible:
self.controller.start_process()
try:
if not self.controller.process or not self.controller.process.Visible:
self.controller.start_process()
self.controller.process.Presentations.Open(self.file_path, False, False, True)
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
self.create_thumbnails()
# Powerpoint 2013 pops up when loading a file, so we minimize it again
if self.presentation.Application.Version == u'15.0':
try:
self.presentation.Application.WindowState = 2
except:
log.error('Failed to minimize main powerpoint window')
trace_error_handler(log)
return True
except pywintypes.com_error:
log.debug('PPT open failed')
log.error('PPT open failed')
trace_error_handler(log)
return False
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
self.create_thumbnails()
@ -211,23 +224,33 @@ class PowerpointDocument(PresentationDocument):
Unblanks (restores) the presentation.
"""
log.debug('unblank_screen')
self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate()
if self.presentation.Application.Version == '14.0':
# Unblanking is broken in PowerPoint 2010, need to redisplay
slide = self.presentation.SlideShowWindow.View.CurrentShowPosition
click = self.presentation.SlideShowWindow.View.GetClickIndex()
self.presentation.SlideShowWindow.View.GotoSlide(slide)
if click:
self.presentation.SlideShowWindow.View.GotoClick(click)
try:
self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate()
if self.presentation.Application.Version == '14.0':
# Unblanking is broken in PowerPoint 2010, need to redisplay
slide = self.presentation.SlideShowWindow.View.CurrentShowPosition
click = self.presentation.SlideShowWindow.View.GetClickIndex()
self.presentation.SlideShowWindow.View.GotoSlide(slide)
if click:
self.presentation.SlideShowWindow.View.GotoClick(click)
except pywintypes.com_error:
log.error('COM error while in unblank_screen')
trace_error_handler(log)
self.show_error_msg()
def blank_screen(self):
"""
Blanks the screen.
"""
log.debug('blank_screen')
self.presentation.SlideShowWindow.View.State = 3
try:
self.presentation.SlideShowWindow.View.State = 3
except pywintypes.com_error:
log.error('COM error while in blank_screen')
trace_error_handler(log)
self.show_error_msg()
def is_blank(self):
"""
@ -235,7 +258,12 @@ class PowerpointDocument(PresentationDocument):
"""
log.debug('is_blank')
if self.is_active():
return self.presentation.SlideShowWindow.View.State == 3
try:
return self.presentation.SlideShowWindow.View.State == 3
except pywintypes.com_error:
log.error('COM error while in is_blank')
trace_error_handler(log)
self.show_error_msg()
else:
return False
@ -244,7 +272,12 @@ class PowerpointDocument(PresentationDocument):
Stops the current presentation and hides the output.
"""
log.debug('stop_presentation')
self.presentation.SlideShowWindow.View.Exit()
try:
self.presentation.SlideShowWindow.View.Exit()
except pywintypes.com_error:
log.error('COM error while in stop_presentation')
trace_error_handler(log)
self.show_error_msg()
if os.name == 'nt':
def start_presentation(self):
@ -264,24 +297,49 @@ class PowerpointDocument(PresentationDocument):
ppt_window = self.presentation.SlideShowSettings.Run()
if not ppt_window:
return
ppt_window.Top = size.y() * 72 / dpi
ppt_window.Height = size.height() * 72 / dpi
ppt_window.Left = size.x() * 72 / dpi
ppt_window.Width = size.width() * 72 / dpi
try:
ppt_window.Top = size.y() * 72 / dpi
ppt_window.Height = size.height() * 72 / dpi
ppt_window.Left = size.x() * 72 / dpi
ppt_window.Width = size.width() * 72 / dpi
except AttributeError as e:
log.error('AttributeError while in start_presentation')
log.error(e)
# Powerpoint 2013 pops up when starting a file, so we minimize it again
if self.presentation.Application.Version == u'15.0':
try:
self.presentation.Application.WindowState = 2
except:
log.error('Failed to minimize main powerpoint window')
trace_error_handler(log)
def get_slide_number(self):
"""
Returns the current slide number.
"""
log.debug('get_slide_number')
return self.presentation.SlideShowWindow.View.CurrentShowPosition
ret = 0
try:
ret = self.presentation.SlideShowWindow.View.CurrentShowPosition
except pywintypes.com_error:
log.error('COM error while in get_slide_number')
trace_error_handler(log)
self.show_error_msg()
return ret
def get_slide_count(self):
"""
Returns total number of slides.
"""
log.debug('get_slide_count')
return self.presentation.Slides.Count
ret = 0
try:
ret = self.presentation.Slides.Count
except pywintypes.com_error:
log.error('COM error while in get_slide_count')
trace_error_handler(log)
self.show_error_msg()
return ret
def goto_slide(self, slide_no):
"""
@ -290,14 +348,25 @@ class PowerpointDocument(PresentationDocument):
:param slide_no: The slide the text is required for, starting at 1
"""
log.debug('goto_slide')
self.presentation.SlideShowWindow.View.GotoSlide(slide_no)
try:
self.presentation.SlideShowWindow.View.GotoSlide(slide_no)
except pywintypes.com_error:
log.error('COM error while in goto_slide')
trace_error_handler(log)
self.show_error_msg()
def next_step(self):
"""
Triggers the next effect of slide on the running presentation.
"""
log.debug('next_step')
self.presentation.SlideShowWindow.View.Next()
try:
self.presentation.SlideShowWindow.View.Next()
except pywintypes.com_error:
log.error('COM error while in next_step')
trace_error_handler(log)
self.show_error_msg()
return
if self.get_slide_number() > self.get_slide_count():
self.previous_step()
@ -306,7 +375,12 @@ class PowerpointDocument(PresentationDocument):
Triggers the previous slide on the running presentation.
"""
log.debug('previous_step')
self.presentation.SlideShowWindow.View.Previous()
try:
self.presentation.SlideShowWindow.View.Previous()
except pywintypes.com_error:
log.error('COM error while in previous_step')
trace_error_handler(log)
self.show_error_msg()
def get_slide_text(self, slide_no):
"""
@ -347,6 +421,15 @@ class PowerpointDocument(PresentationDocument):
self.save_titles_and_notes(titles, notes)
return
def show_error_msg(self):
"""
Stop presentation and display an error message.
"""
self.stop_presentation()
critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument',
'An error occurred in the Powerpoint integration '
'and the presentation will be stopped. '
'Restart the presentation if you wish to present it.'))
def _get_text_from_shapes(shapes):
"""
@ -362,26 +445,29 @@ def _get_text_from_shapes(shapes):
return text
if os.name == "nt":
ppE = win32com.client.getevents("PowerPoint.Application")
try:
ppE = win32com.client.getevents("PowerPoint.Application")
class PowerpointEvents(ppE):
def OnSlideShowBegin(self, hwnd):
#print("SS Begin")
return
class PowerpointEvents(ppE):
def OnSlideShowBegin(self, hwnd):
#print("SS Begin")
return
def OnSlideShowEnd(self, pres):
#print("SS End")
return
def OnSlideShowEnd(self, pres):
#print("SS End")
return
def OnSlideShowNextSlide(self, hwnd):
Registry().execute('slidecontroller_live_change', hwnd.View.CurrentShowPosition - 1)
#print('Slide change:',hwnd.View.CurrentShowPosition)
return
def OnSlideShowNextSlide(self, hwnd):
Registry().execute('slidecontroller_live_change', hwnd.View.CurrentShowPosition - 1)
#print('Slide change:',hwnd.View.CurrentShowPosition)
return
def OnSlideShowOnNext(self, hwnd):
#print("SS Advance")
return
def OnSlideShowOnNext(self, hwnd):
#print("SS Advance")
return
def OnSlideShowOnPrevious(self, hwnd):
#print("SS GoBack")
return
def OnSlideShowOnPrevious(self, hwnd):
#print("SS GoBack")
return
except pywintypes.com_error:
log.debug('COM error trying to get powerpoint events - powerpoint is probably not installed.')

View File

@ -397,7 +397,7 @@ class PresentationController(object):
class MyPresentationController(PresentationController):
def __init__(self, plugin):
PresentationController.__init(
self, plugin, u'My Presenter App')
self, plugin, 'My Presenter App')
:param plugin: Defaults to *None*. The presentationplugin object
:param name: Name of the application, to appear in the application

View File

@ -149,11 +149,11 @@ class HttpRouter(RegistryProperties):
"""
Initialise the router stack and any other variables.
"""
authcode = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password'))
auth_code = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password'))
try:
self.auth = base64.b64encode(authcode)
self.auth = base64.b64encode(auth_code)
except TypeError:
self.auth = base64.b64encode(authcode.encode()).decode()
self.auth = base64.b64encode(auth_code.encode()).decode()
self.routes = [
('^/$', {'function': self.serve_file, 'secure': False}),
('^/(stage)$', {'function': self.serve_file, 'secure': False}),
@ -378,7 +378,6 @@ class HttpRouter(RegistryProperties):
Examines the extension of the file and determines what the content_type should be, defaults to text/plain
Returns the extension and the content_type
"""
content_type = 'text/plain'
ext = os.path.splitext(file_name)[1]
content_type = FILE_TYPES.get(ext, 'text/plain')
return ext, content_type
@ -470,7 +469,7 @@ class HttpRouter(RegistryProperties):
if plugin.status == PluginStatus.Active:
try:
text = json.loads(self.request_data)['request']['text']
except KeyError as ValueError:
except KeyError:
return self.do_http_error()
text = urllib.parse.unquote(text)
self.alerts_manager.emit(QtCore.SIGNAL('alerts_text'), [text])
@ -484,6 +483,7 @@ class HttpRouter(RegistryProperties):
"""
Perform an action on the slide controller.
"""
log.debug("controller_text var = %s" % var)
current_item = self.live_controller.service_item
data = []
if current_item:
@ -531,7 +531,7 @@ class HttpRouter(RegistryProperties):
if self.request_data:
try:
data = json.loads(self.request_data)['request']['id']
except KeyError as ValueError:
except KeyError:
return self.do_http_error()
log.info(data)
# This slot expects an int within a list.
@ -590,7 +590,7 @@ class HttpRouter(RegistryProperties):
"""
try:
text = json.loads(self.request_data)['request']['text']
except KeyError as ValueError:
except KeyError:
return self.do_http_error()
text = urllib.parse.unquote(text)
plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
@ -606,12 +606,12 @@ class HttpRouter(RegistryProperties):
Go live on an item of type ``plugin``.
"""
try:
id = json.loads(self.request_data)['request']['id']
except KeyError as ValueError:
request_id = json.loads(self.request_data)['request']['id']
except KeyError:
return self.do_http_error()
plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
if plugin.status == PluginStatus.Active and plugin.media_item:
plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [id, True])
plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [request_id, True])
return self.do_http_success()
def add_to_service(self, plugin_name):
@ -619,12 +619,12 @@ class HttpRouter(RegistryProperties):
Add item of type ``plugin_name`` to the end of the service.
"""
try:
id = json.loads(self.request_data)['request']['id']
except KeyError as ValueError:
request_id = json.loads(self.request_data)['request']['id']
except KeyError:
return self.do_http_error()
plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
if plugin.status == PluginStatus.Active and plugin.media_item:
item_id = plugin.media_item.create_item_from_id(id)
item_id = plugin.media_item.create_item_from_id(request_id)
plugin.media_item.emit(QtCore.SIGNAL('%s_add_to_service' % plugin_name), [item_id, True])
self.do_http_success()

View File

@ -40,7 +40,7 @@ import time
from PyQt4 import QtCore
from openlp.core.common import AppLocation, Settings
from openlp.core.common import AppLocation, Settings, RegistryProperties
from openlp.plugins.remotes.lib import HttpRouter
@ -94,13 +94,18 @@ class HttpThread(QtCore.QThread):
"""
self.http_server.start_server()
def stop(self):
log.debug("stop called")
self.http_server.stop = True
class OpenLPServer():
class OpenLPServer(RegistryProperties):
def __init__(self):
"""
Initialise the http server, and start the server of the correct type http / https
"""
log.debug('Initialise httpserver')
super(OpenLPServer, self).__init__()
log.debug('Initialise OpenLP')
self.settings_section = 'remotes'
self.http_thread = HttpThread(self)
self.http_thread.start()
@ -110,32 +115,49 @@ class OpenLPServer():
Start the correct server and save the handler
"""
address = Settings().value(self.settings_section + '/ip address')
if Settings().value(self.settings_section + '/https enabled'):
self.address = address
self.is_secure = Settings().value(self.settings_section + '/https enabled')
self.needs_authentication = Settings().value(self.settings_section + '/authentication enabled')
if self.is_secure:
port = Settings().value(self.settings_section + '/https port')
self.httpd = HTTPSServer((address, port), CustomHandler)
log.debug('Started ssl httpd...')
self.port = port
self.start_server_instance(address, port, HTTPSServer)
else:
port = Settings().value(self.settings_section + '/port')
loop = 1
while loop < 3:
try:
self.httpd = ThreadingHTTPServer((address, port), CustomHandler)
except OSError:
loop += 1
time.sleep(0.1)
except:
log.error('Failed to start server ')
log.debug('Started non ssl httpd...')
self.port = port
self.start_server_instance(address, port, ThreadingHTTPServer)
if hasattr(self, 'httpd') and self.httpd:
self.httpd.serve_forever()
else:
log.debug('Failed to start server')
def start_server_instance(self, address, port, server_class):
"""
Start the server
:param address: The server address
:param port: The run port
:param server_class: the class to start
"""
loop = 1
while loop < 4:
try:
self.httpd = server_class((address, port), CustomHandler)
log.debug("Server started for class %s %s %d" % (server_class, address, port))
except OSError:
log.debug("failed to start http server thread state %d %s" %
(loop, self.http_thread.isRunning()))
loop += 1
time.sleep(0.1)
except:
log.error('Failed to start server ')
def stop_server(self):
"""
Stop the server
"""
self.http_thread.exit(0)
if self.http_thread.isRunning():
self.http_thread.stop()
self.httpd = None
log.debug('Stopped the server.')

View File

@ -32,7 +32,7 @@ import os.path
from PyQt4 import QtCore, QtGui, QtNetwork
from openlp.core.common import AppLocation, Settings, translate
from openlp.core.lib import SettingsTab
from openlp.core.lib import SettingsTab, build_icon
ZERO_URL = '0.0.0.0'
@ -234,6 +234,7 @@ class RemoteTab(SettingsTab):
"""
Load the configuration and update the server configuration if necessary
"""
self.is_secure = Settings().value(self.settings_section + '/https enabled')
self.port_spin_box.setValue(Settings().value(self.settings_section + '/port'))
self.https_port_spin_box.setValue(Settings().value(self.settings_section + '/https port'))
self.address_edit.setText(Settings().value(self.settings_section + '/ip address'))
@ -263,9 +264,7 @@ class RemoteTab(SettingsTab):
Settings().value(self.settings_section + '/port') != self.port_spin_box.value() or \
Settings().value(self.settings_section + '/https port') != self.https_port_spin_box.value() or \
Settings().value(self.settings_section + '/https enabled') != \
self.https_settings_group_box.isChecked() or \
Settings().value(self.settings_section + '/authentication enabled') != \
self.user_login_group_box.isChecked():
self.https_settings_group_box.isChecked():
self.settings_form.register_post_process('remotes_config_updated')
Settings().setValue(self.settings_section + '/port', self.port_spin_box.value())
Settings().setValue(self.settings_section + '/https port', self.https_port_spin_box.value())
@ -275,6 +274,7 @@ class RemoteTab(SettingsTab):
Settings().setValue(self.settings_section + '/authentication enabled', self.user_login_group_box.isChecked())
Settings().setValue(self.settings_section + '/user id', self.user_id.text())
Settings().setValue(self.settings_section + '/password', self.password.text())
self.generate_icon()
def on_twelve_hour_check_box_changed(self, check_state):
"""
@ -290,3 +290,25 @@ class RemoteTab(SettingsTab):
Invert the HTTP group box based on Https group settings
"""
self.http_settings_group_box.setEnabled(not self.https_settings_group_box.isChecked())
def generate_icon(self):
"""
Generate icon for main window
"""
self.remote_server_icon.hide()
icon = QtGui.QImage(':/remote/network_server.png')
icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
if self.is_secure:
overlay = QtGui.QImage(':/remote/network_ssl.png')
overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
painter = QtGui.QPainter(icon)
painter.drawImage(0, 0, overlay)
painter.end()
if Settings().value(self.settings_section + '/authentication enabled'):
overlay = QtGui.QImage(':/remote/network_auth.png')
overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
painter = QtGui.QPainter(icon)
painter.drawImage(20, 0, overlay)
painter.end()
self.remote_server_icon.setPixmap(QtGui.QPixmap.fromImage(icon))
self.remote_server_icon.show()

View File

@ -28,7 +28,8 @@
###############################################################################
import logging
import time
from PyQt4 import QtGui
from openlp.core.lib import Plugin, StringContent, translate, build_icon
from openlp.core.common import Registry
@ -72,6 +73,21 @@ class RemotesPlugin(Plugin):
self.websocketserver = WebSocketManager()
self.websocketserver.start()
Registry().register_function('websock_send', self.websocketserver.send)
if not hasattr(self, 'remote_server_icon'):
self.remote_server_icon = QtGui.QLabel(self.main_window.status_bar)
size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
size_policy.setHorizontalStretch(0)
size_policy.setVerticalStretch(0)
size_policy.setHeightForWidth(self.remote_server_icon.sizePolicy().hasHeightForWidth())
self.remote_server_icon.setSizePolicy(size_policy)
self.remote_server_icon.setFrameShadow(QtGui.QFrame.Plain)
self.remote_server_icon.setLineWidth(1)
self.remote_server_icon.setScaledContents(True)
self.remote_server_icon.setFixedSize(20, 20)
self.remote_server_icon.setObjectName('remote_server_icon')
self.main_window.status_bar.insertPermanentWidget(2, self.remote_server_icon)
self.settings_tab.remote_server_icon = self.remote_server_icon
self.settings_tab.generate_icon()
def finalise(self):
"""
@ -112,9 +128,11 @@ class RemotesPlugin(Plugin):
def config_update(self):
"""
Called when Config is changed to restart the server on new address or port
Called when Config is changed to requests a restart with the server on new address or port
"""
log.debug('remote config changed')
self.finalise()
time.sleep(0.5)
self.initialise()
QtGui.QMessageBox.information(self.main_window,
translate('RemotePlugin', 'Server Config Change'),
translate('RemotePlugin', 'Server configuration changes will require a restart '
'to take effect.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))

View File

@ -34,7 +34,7 @@ code, like slots and loading and saving.
The first class, commonly known as the **Dialog** class, is typically named
``Ui_<name>Dialog``. It is a slightly modified version of the class that the
``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
converting most strings from "" to u'' and using OpenLP's ``translate()``
converting most strings from "" to '' and using OpenLP's ``translate()``
function for translating strings.
The second class, commonly known as the **Form** class, is typically named

View File

@ -43,8 +43,8 @@ class Ui_AuthorsDialog(object):
Set up the UI for the dialog.
"""
authors_dialog.setObjectName('authors_dialog')
authors_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
authors_dialog.resize(300, 10)
authors_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
authors_dialog.setModal(True)
self.dialog_layout = QtGui.QVBoxLayout(authors_dialog)
self.dialog_layout.setObjectName('dialog_layout')

View File

@ -264,7 +264,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
self.break_search = True
self.plugin.media_item.on_search_text_button_clicked()
def setDefaults(self):
def set_defaults(self):
"""
Set default form values for the song import wizard.
"""

View File

@ -43,8 +43,8 @@ class Ui_EditSongDialog(object):
"""
def setupUi(self, edit_song_dialog):
edit_song_dialog.setObjectName('edit_song_dialog')
edit_song_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
edit_song_dialog.resize(650, 400)
edit_song_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
edit_song_dialog.setModal(True)
self.dialog_layout = QtGui.QVBoxLayout(edit_song_dialog)
self.dialog_layout.setSpacing(8)
@ -118,13 +118,18 @@ class Ui_EditSongDialog(object):
self.authors_group_box.setObjectName('authors_group_box')
self.authors_layout = QtGui.QVBoxLayout(self.authors_group_box)
self.authors_layout.setObjectName('authors_layout')
self.author_add_layout = QtGui.QHBoxLayout()
self.author_add_layout = QtGui.QVBoxLayout()
self.author_add_layout.setObjectName('author_add_layout')
self.author_type_layout = QtGui.QHBoxLayout()
self.author_type_layout.setObjectName('author_type_layout')
self.authors_combo_box = create_combo_box(self.authors_group_box, 'authors_combo_box')
self.author_add_layout.addWidget(self.authors_combo_box)
self.author_types_combo_box = create_combo_box(self.authors_group_box, 'author_types_combo_box', editable=False)
self.author_type_layout.addWidget(self.author_types_combo_box)
self.author_add_button = QtGui.QPushButton(self.authors_group_box)
self.author_add_button.setObjectName('author_add_button')
self.author_add_layout.addWidget(self.author_add_button)
self.author_type_layout.addWidget(self.author_add_button)
self.author_add_layout.addLayout(self.author_type_layout)
self.authors_layout.addLayout(self.author_add_layout)
self.authors_list_view = QtGui.QListWidget(self.authors_group_box)
self.authors_list_view.setAlternatingRowColors(True)
@ -330,7 +335,7 @@ class Ui_EditSongDialog(object):
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> You have not entered a verse order.')
def create_combo_box(parent, name):
def create_combo_box(parent, name, editable=True):
"""
Utility method to generate a standard combo box for this dialog.
@ -340,7 +345,7 @@ def create_combo_box(parent, name):
combo_box = QtGui.QComboBox(parent)
combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength)
combo_box.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
combo_box.setEditable(True)
combo_box.setEditable(editable)
combo_box.setInsertPolicy(QtGui.QComboBox.NoInsert)
combo_box.setObjectName(name)
return combo_box

View File

@ -42,9 +42,9 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStri
from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
from openlp.plugins.songs.lib import VerseType, clean_song
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.xml import SongXML
from openlp.plugins.songs.lib.openlyricsxml import SongXML
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
from openlp.plugins.songs.forms.editverseform import EditVerseForm
from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm
@ -107,6 +107,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
self.audio_list_widget.setAlternatingRowColors(True)
self.find_verse_split = re.compile('---\[\]---\n', re.UNICODE)
self.whitespace = re.compile(r'\W+', re.UNICODE)
self.find_tags = re.compile(u'\{/?\w+\}', re.UNICODE)
def _load_objects(self, cls, combo, cache):
"""
@ -122,12 +123,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
combo.setItemData(row, obj.id)
set_case_insensitive_completer(cache, combo)
def _add_author_to_list(self, author):
def _add_author_to_list(self, author, author_type):
"""
Add an author to the author list.
"""
author_item = QtGui.QListWidgetItem(str(author.display_name))
author_item.setData(QtCore.Qt.UserRole, author.id)
author_item = QtGui.QListWidgetItem(author.get_display_name(author_type))
author_item.setData(QtCore.Qt.UserRole, (author.id, author_type))
self.authors_list_view.addItem(author_item)
def _extract_verse_order(self, verse_order):
@ -217,8 +218,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
if self.authors_list_view.count() == 0:
self.song_tab_widget.setCurrentIndex(1)
self.authors_list_view.setFocus()
critical_error_message_box(
message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.'))
critical_error_message_box(message=translate('SongsPlugin.EditSongForm',
'You need to have an author for this song.'))
return False
if self.verse_order_edit.text():
result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount())
@ -234,8 +235,57 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
self.manager.save_object(book)
else:
return False
# Validate tags (lp#1199639)
misplaced_tags = []
verse_tags = []
for i in range(self.verse_list_widget.rowCount()):
item = self.verse_list_widget.item(i, 0)
tags = self.find_tags.findall(item.text())
field = item.data(QtCore.Qt.UserRole)
verse_tags.append(field)
if not self._validate_tags(tags):
misplaced_tags.append('%s %s' % (VerseType.translated_name(field[0]), field[1:]))
if misplaced_tags:
critical_error_message_box(
message=translate('SongsPlugin.EditSongForm',
'There are misplaced formatting tags in the following verses:\n\n%s\n\n'
'Please correct these tags before continuing.' % ', '.join(misplaced_tags)))
return False
for tag in verse_tags:
if verse_tags.count(tag) > 26:
# lp#1310523: OpenLyrics allows only a-z variants of one verse:
# http://openlyrics.info/dataformat.html#verse-name
critical_error_message_box(message=translate(
'SongsPlugin.EditSongForm', 'You have %(count)s verses named %(name)s %(number)s. '
'You can have at most 26 verses with the same name' %
{'count': verse_tags.count(tag),
'name': VerseType.translated_name(tag[0]),
'number': tag[1:]}))
return False
return True
def _validate_tags(self, tags):
"""
Validates a list of tags
Deletes the first affiliated tag pair which is located side by side in the list
and call itself recursively with the shortened tag list.
If there is any misplaced tag in the list, either the length of the tag list is not even,
or the function won't find any tag pairs side by side.
If there is no misplaced tag, the length of the list will be zero on any recursive run.
:param tags: A list of tags
:return: True if the function can't find any mismatched tags. Else False.
"""
if len(tags) == 0:
return True
if len(tags) % 2 != 0:
return False
for i in range(len(tags)-1):
if tags[i+1] == "{/" + tags[i][1:]:
del tags[i:i+2]
return self._validate_tags(tags)
return False
def _process_lyrics(self):
"""
Process the lyric data entered by the user into the OpenLP XML format.
@ -302,6 +352,15 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
self.authors.append(author.display_name)
set_case_insensitive_completer(self.authors, self.authors_combo_box)
# Types
self.author_types_combo_box.clear()
self.author_types_combo_box.addItem('')
# Don't iterate over the dictionary to give them this specific order
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Words], AuthorType.Words)
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Music], AuthorType.Music)
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.WordsAndMusic], AuthorType.WordsAndMusic)
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Translation], AuthorType.Translation)
def load_topics(self):
"""
Load the topics into the combobox.
@ -454,10 +513,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
self.tag_rows()
# clear the results
self.authors_list_view.clear()
for author in self.song.authors:
author_name = QtGui.QListWidgetItem(str(author.display_name))
author_name.setData(QtCore.Qt.UserRole, author.id)
self.authors_list_view.addItem(author_name)
for author_song in self.song.authors_songs:
self._add_author_to_list(author_song.author, author_song.author_type)
# clear the results
self.topics_list_view.clear()
for topic in self.song.topics:
@ -496,6 +553,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
"""
item = int(self.authors_combo_box.currentIndex())
text = self.authors_combo_box.currentText().strip(' \r\n\t')
author_type = self.author_types_combo_box.itemData(self.author_types_combo_box.currentIndex())
# This if statement is for OS X, which doesn't seem to work well with
# the QCompleter auto-completion class. See bug #812628.
if text in self.authors:
@ -513,7 +571,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
author = Author.populate(first_name=text.rsplit(' ', 1)[0], last_name=text.rsplit(' ', 1)[1],
display_name=text)
self.manager.save_object(author)
self._add_author_to_list(author)
self._add_author_to_list(author, author_type)
self.load_authors()
self.authors_combo_box.setCurrentIndex(0)
else:
@ -521,11 +579,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
elif item > 0:
item_id = (self.authors_combo_box.itemData(item))
author = self.manager.get_object(Author, item_id)
if self.authors_list_view.findItems(str(author.display_name), QtCore.Qt.MatchExactly):
if self.authors_list_view.findItems(author.get_display_name(author_type), QtCore.Qt.MatchExactly):
critical_error_message_box(
message=translate('SongsPlugin.EditSongForm', 'This author is already in the list.'))
else:
self._add_author_to_list(author)
self._add_author_to_list(author, author_type)
self.authors_combo_box.setCurrentIndex(0)
else:
QtGui.QMessageBox.warning(
@ -905,13 +963,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
else:
self.song.theme_name = None
self._process_lyrics()
self.song.authors = []
self.song.authors_songs = []
for row in range(self.authors_list_view.count()):
item = self.authors_list_view.item(row)
author_id = (item.data(QtCore.Qt.UserRole))
author = self.manager.get_object(Author, author_id)
if author is not None:
self.song.authors.append(author)
self.song.add_author(self.manager.get_object(Author, item.data(QtCore.Qt.UserRole)[0]),
item.data(QtCore.Qt.UserRole)[1])
self.song.topics = []
for row in range(self.topics_list_view.count()):
item = self.topics_list_view.item(row)

View File

@ -37,6 +37,7 @@ from openlp.plugins.songs.lib import VerseType
class Ui_EditVerseDialog(object):
def setupUi(self, edit_verse_dialog):
edit_verse_dialog.setObjectName('edit_verse_dialog')
edit_verse_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
edit_verse_dialog.resize(400, 400)
edit_verse_dialog.setModal(True)
self.dialog_layout = QtGui.QVBoxLayout(edit_verse_dialog)

View File

@ -122,8 +122,6 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
text = text[:position + 4]
match = VERSE_REGEX.match(text)
if match:
# TODO: Not used, remove?
# verse_tag = match.group(1)
try:
verse_num = int(match.group(2)) + 1
except ValueError:

View File

@ -42,10 +42,10 @@ class Ui_MediaFilesDialog(object):
Set up the user interface.
"""
media_files_dialog.setObjectName('media_files_dialog')
media_files_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
media_files_dialog.setWindowModality(QtCore.Qt.ApplicationModal)
media_files_dialog.resize(400, 300)
media_files_dialog.setModal(True)
media_files_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
self.files_vertical_layout = QtGui.QVBoxLayout(media_files_dialog)
self.files_vertical_layout.setSpacing(8)
self.files_vertical_layout.setMargin(8)

View File

@ -29,7 +29,7 @@
from PyQt4 import QtGui
from openlp.core.lib import translate
from openlp.core.lib import translate, build_icon
from openlp.core.lib.ui import create_button_box
@ -42,6 +42,7 @@ class Ui_SongBookDialog(object):
Set up the user interface.
"""
song_book_dialog.setObjectName('song_book_dialog')
song_book_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
song_book_dialog.resize(300, 10)
self.dialog_layout = QtGui.QVBoxLayout(song_book_dialog)
self.dialog_layout.setObjectName('dialog_layout')

View File

@ -231,11 +231,11 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
"""
Opens a QFileDialog and writes the filenames to the given listbox.
:param title: The title of the dialog (unicode).
:param title: The title of the dialog (str).
:param listbox: A listbox (QListWidget).
:param filters: The file extension filters. It should contain the file descriptions
as well as the file extensions. For example::
u'SongBeamer Files (*.sng)'
:param filters: The file extension filters. It should contain the file descriptions as well as the file
extensions. For example::
'SongBeamer Files (*.sng)'
"""
if filters:
filters += ';;'
@ -304,7 +304,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
"""
self.source_page.emit(QtCore.SIGNAL('completeChanged()'))
def setDefaults(self):
def set_defaults(self):
"""
Set default form values for the song import wizard.
"""

View File

@ -44,6 +44,7 @@ class Ui_SongMaintenanceDialog(object):
Set up the user interface for the song maintenance dialog
"""
song_maintenance_dialog.setObjectName('song_maintenance_dialog')
song_maintenance_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
song_maintenance_dialog.setWindowModality(QtCore.Qt.ApplicationModal)
song_maintenance_dialog.resize(10, 350)
self.dialog_layout = QtGui.QGridLayout(song_maintenance_dialog)

View File

@ -400,7 +400,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog, RegistryPrope
"""
Merges two authors into one author.
:param old_author: The object, which was edited, that will be deleted
:param old_author: The object, which was edited, that will be deleted
"""
# Find the duplicate.
existing_author = self.manager.get_object_filtered(
@ -415,11 +415,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog, RegistryPrope
# Find the songs, which have the old_author as author.
songs = self.manager.get_all_objects(Song, Song.authors.contains(old_author))
for song in songs:
# We check if the song has already existing_author as author. If
# that is not the case we add it.
if existing_author not in song.authors:
song.authors.append(existing_author)
song.authors.remove(old_author)
for author_song in song.authors_songs:
song.add_author(existing_author, author_song.author_type)
song.remove_author(old_author, author_song.author_type)
self.manager.save_object(song)
self.manager.delete_object(Author, old_author.id)

View File

@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.xml import SongXML
from openlp.plugins.songs.lib.openlyricsxml import SongXML
class SongReviewWidget(QtGui.QWidget):

View File

@ -319,8 +319,6 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog):
def on_search_finished(self):
"""
Slot which is called when the search is completed.
:param songs:
"""
self.application.process_events()
self.search_progress_bar.setVisible(False)

View File

@ -29,7 +29,7 @@
from PyQt4 import QtGui
from openlp.core.lib import translate
from openlp.core.lib import translate, build_icon
from openlp.core.lib.ui import create_button_box
@ -42,6 +42,7 @@ class Ui_TopicsDialog(object):
Set up the user interface for the topics dialog.
"""
topics_dialog.setObjectName('topics_dialog')
topics_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
topics_dialog.resize(300, 10)
self.dialog_layout = QtGui.QVBoxLayout(topics_dialog)
self.dialog_layout.setObjectName('dialog_layout')

View File

@ -206,14 +206,14 @@ class VerseType(object):
Return the VerseType for a given tag
:param verse_tag: The string to return a VerseType for
:param default: Default return value if no matching tag is found
:param default: Default return value if no matching tag is found (a valid VerseType or None)
:return: A VerseType of the tag
"""
verse_tag = verse_tag[0].lower()
for num, tag in enumerate(VerseType.tags):
if verse_tag == tag:
return num
if len(VerseType.names) > default:
if default in range(0, len(VerseType.names)) or default is None:
return default
else:
return VerseType.Other
@ -231,7 +231,7 @@ class VerseType(object):
for num, tag in enumerate(VerseType.translated_tags):
if verse_tag == tag:
return num
if len(VerseType.names) > default:
if default in range(0, len(VerseType.names)) or default is None:
return default
else:
return VerseType.Other
@ -278,7 +278,7 @@ class VerseType(object):
if verse_index is None:
verse_index = VerseType.from_string(verse_name, default)
elif len(verse_name) == 1:
verse_index = VerseType.from_translated_tag(verse_name, None)
verse_index = VerseType.from_translated_tag(verse_name, default)
if verse_index is None:
verse_index = VerseType.from_tag(verse_name, default)
else:
@ -390,12 +390,12 @@ def clean_song(manager, song):
verses = SongXML().get_verses(song.lyrics)
song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses])
# The song does not have any author, add one.
if not song.authors:
if not song.authors_songs:
name = SongStrings.AuthorUnknown
author = manager.get_object_filtered(Author, Author.display_name == name)
if author is None:
author = Author.populate(display_name=name, last_name='', first_name='')
song.authors.append(author)
song.add_author(author)
if song.copyright:
song.copyright = CONTROL_CHARS.sub('', song.copyright).strip()
@ -434,7 +434,7 @@ def strip_rtf(text, default_encoding=None):
# Current font is the font tag we last met.
font = ''
# Character encoding is defined inside fonttable.
# font_table could contain eg u'0': u'cp1252'
# font_table could contain eg '0': u'cp1252'
font_table = {'': ''}
# Stack of things to keep track of when entering/leaving groups.
stack = []

View File

@ -35,19 +35,53 @@ import re
from sqlalchemy import Column, ForeignKey, Table, types
from sqlalchemy.orm import mapper, relation, reconstructor
from sqlalchemy.sql.expression import func
from sqlalchemy.sql.expression import func, text
from openlp.core.lib.db import BaseModel, init_db
from openlp.core.utils import get_natural_key
from openlp.core.lib import translate
class Author(BaseModel):
"""
Author model
"""
def get_display_name(self, author_type=None):
if author_type:
return "%s (%s)" % (self.display_name, AuthorType.Types[author_type])
return self.display_name
class AuthorSong(BaseModel):
"""
Relationship between Authors and Songs (many to many).
Need to define this relationship table explicit to get access to the
Association Object (author_type).
http://docs.sqlalchemy.org/en/latest/orm/relationships.html#association-object
"""
pass
class AuthorType(object):
"""
Enumeration for Author types.
They are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors
The 'words+music' type is not an official type, but is provided for convenience.
"""
Words = 'words'
Music = 'music'
WordsAndMusic = 'words+music'
Translation = 'translation'
Types = {
Words: translate('SongsPlugin.AuthorType', 'Words', 'Author who wrote the lyrics of a song'),
Music: translate('SongsPlugin.AuthorType', 'Music', 'Author who wrote the music of a song'),
WordsAndMusic: translate('SongsPlugin.AuthorType', 'Words and Music',
'Author who wrote both lyrics and music of a song'),
Translation: translate('SongsPlugin.AuthorType', 'Translation', 'Author who translated the song')
}
class Book(BaseModel):
"""
Book model
@ -67,6 +101,7 @@ class Song(BaseModel):
"""
Song model
"""
def __init__(self):
self.sort_key = []
@ -80,6 +115,33 @@ class Song(BaseModel):
"""
self.sort_key = get_natural_key(self.title)
def add_author(self, author, author_type=None):
"""
Add an author to the song if it not yet exists
:param author: Author object
:param author_type: AuthorType constant or None
"""
for author_song in self.authors_songs:
if author_song.author == author and author_song.author_type == author_type:
return
new_author_song = AuthorSong()
new_author_song.author = author
new_author_song.author_type = author_type
self.authors_songs.append(new_author_song)
def remove_author(self, author, author_type=None):
"""
Remove an existing author from the song
:param author: Author object
:param author_type: AuthorType constant or None
"""
for author_song in self.authors_songs:
if author_song.author == author and author_song.author_type == author_type:
self.authors_songs.remove(author_song)
return
class Topic(BaseModel):
"""
@ -120,6 +182,7 @@ def init_schema(url):
* author_id
* song_id
* author_type
**media_files Table**
* id
@ -230,7 +293,8 @@ def init_schema(url):
authors_songs_table = Table(
'authors_songs', metadata,
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True)
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
Column('author_type', types.String(), primary_key=True, nullable=False, server_default=text('""'))
)
# Definition of the "songs_topics" table
@ -241,10 +305,16 @@ def init_schema(url):
)
mapper(Author, authors_table)
mapper(AuthorSong, authors_songs_table, properties={
'author': relation(Author)
})
mapper(Book, song_books_table)
mapper(MediaFile, media_files_table)
mapper(Song, songs_table, properties={
'authors': relation(Author, backref='songs', secondary=authors_songs_table, lazy=False),
# Use the authors_songs relation when you need access to the 'author_type' attribute
# or when creating new relations
'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"),
'authors': relation(Author, secondary=authors_songs_table, viewonly=True),
'book': relation(Book, backref='songs'),
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)

View File

@ -1,325 +0,0 @@
# -*- 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 #
###############################################################################
"""
The :mod:`ewimport` module provides the functionality for importing
EasyWorship song databases into the current installation database.
"""
import os
import struct
import re
from openlp.core.lib import translate
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib import retrieve_windows_encoding, strip_rtf
from .songimport import SongImport
RTF_STRIPPING_REGEX = re.compile(r'\{\\tx[^}]*\}')
# regex: at least two newlines, can have spaces between them
SLIDE_BREAK_REGEX = re.compile(r'\n *?\n[\n ]*')
NUMBER_REGEX = re.compile(r'[0-9]+')
NOTE_REGEX = re.compile(r'\(.*?\)')
class FieldDescEntry:
def __init__(self, name, field_type, size):
self.name = name
self.field_type = field_type
self.size = size
class FieldType(object):
"""
An enumeration class for different field types that can be expected in an EasyWorship song file.
"""
String = 1
Int16 = 3
Int32 = 4
Logical = 9
Memo = 0x0c
Blob = 0x0d
Timestamp = 0x15
class EasyWorshipSongImport(SongImport):
"""
The :class:`EasyWorshipSongImport` class provides OpenLP with the
ability to import EasyWorship song files.
"""
def __init__(self, manager, **kwargs):
super(EasyWorshipSongImport, self).__init__(manager, **kwargs)
def do_import(self):
"""
Import the songs
:return:
"""
# Open the DB and MB files if they exist
import_source_mb = self.import_source.replace('.DB', '.MB')
if not os.path.isfile(self.import_source) or not os.path.isfile(import_source_mb):
return
db_size = os.path.getsize(self.import_source)
if db_size < 0x800:
return
db_file = open(self.import_source, 'rb')
self.memo_file = open(import_source_mb, 'rb')
# Don't accept files that are clearly not paradox files
record_size, header_size, block_size, first_block, num_fields = struct.unpack('<hhxb8xh17xh', db_file.read(35))
if header_size != 0x800 or block_size < 1 or block_size > 4:
db_file.close()
self.memo_file.close()
return
# Take a stab at how text is encoded
self.encoding = 'cp1252'
db_file.seek(106)
code_page, = struct.unpack('<h', db_file.read(2))
if code_page == 852:
self.encoding = 'cp1250'
# The following codepage to actual encoding mappings have not been
# observed, but merely guessed. Actual example files are needed.
elif code_page == 737:
self.encoding = 'cp1253'
elif code_page == 775:
self.encoding = 'cp1257'
elif code_page == 855:
self.encoding = 'cp1251'
elif code_page == 857:
self.encoding = 'cp1254'
elif code_page == 866:
self.encoding = 'cp1251'
elif code_page == 869:
self.encoding = 'cp1253'
elif code_page == 862:
self.encoding = 'cp1255'
elif code_page == 874:
self.encoding = 'cp874'
self.encoding = retrieve_windows_encoding(self.encoding)
if not self.encoding:
return
# Read the field description information
db_file.seek(120)
field_info = db_file.read(num_fields * 2)
db_file.seek(4 + (num_fields * 4) + 261, os.SEEK_CUR)
field_names = db_file.read(header_size - db_file.tell()).split(b'\0', num_fields)
field_names.pop()
field_descriptions = []
for i, field_name in enumerate(field_names):
field_type, field_size = struct.unpack_from('BB', field_info, i * 2)
field_descriptions.append(FieldDescEntry(field_name, field_type, field_size))
self.set_record_struct(field_descriptions)
# Pick out the field description indexes we will need
try:
success = True
fi_title = self.find_field(b'Title')
fi_author = self.find_field(b'Author')
fi_copy = self.find_field(b'Copyright')
fi_admin = self.find_field(b'Administrator')
fi_words = self.find_field(b'Words')
fi_ccli = self.find_field(b'Song Number')
except IndexError:
# This is the wrong table
success = False
# There does not appear to be a _reliable_ way of getting the number of songs/records, so loop through the file
# blocks and total the number of records. Store the information in a list so we dont have to do all this again.
cur_block = first_block
total_count = 0
block_list = []
while cur_block != 0 and success:
cur_block_pos = header_size + ((cur_block - 1) * 1024 * block_size)
db_file.seek(cur_block_pos)
cur_block, rec_count = struct.unpack('<h2xh', db_file.read(6))
rec_count = (rec_count + record_size) // record_size
block_list.append((cur_block_pos, rec_count))
total_count += rec_count
self.import_wizard.progress_bar.setMaximum(total_count)
for block in block_list:
cur_block_pos, rec_count = block
db_file.seek(cur_block_pos + 6)
# Loop through each record within the current block
for i in range(rec_count):
if self.stop_import_flag:
break
raw_record = db_file.read(record_size)
self.fields = self.record_structure.unpack(raw_record)
self.set_defaults()
self.title = self.get_field(fi_title).decode()
# Get remaining fields.
copy = self.get_field(fi_copy)
admin = self.get_field(fi_admin)
ccli = self.get_field(fi_ccli)
authors = self.get_field(fi_author)
words = self.get_field(fi_words)
# Set the SongImport object members.
if copy:
self.copyright = copy.decode()
if admin:
if copy:
self.copyright += ', '
self.copyright += translate('SongsPlugin.EasyWorshipSongImport',
'Administered by %s') % admin.decode()
if ccli:
self.ccli_number = ccli.decode()
if authors:
# Split up the authors
author_list = authors.split(b'/')
if len(author_list) < 2:
author_list = authors.split(b';')
if len(author_list) < 2:
author_list = authors.split(b',')
for author_name in author_list:
self.add_author(author_name.decode().strip())
if words:
# Format the lyrics
result = strip_rtf(words.decode(), self.encoding)
if result is None:
return
words, self.encoding = result
verse_type = VerseType.tags[VerseType.Verse]
for verse in SLIDE_BREAK_REGEX.split(words):
verse = verse.strip()
if not verse:
continue
verse_split = verse.split('\n', 1)
first_line_is_tag = False
# EW tags: verse, chorus, pre-chorus, bridge, tag,
# intro, ending, slide
for tag in VerseType.tags + ['tag', 'slide']:
tag = tag.lower()
ew_tag = verse_split[0].strip().lower()
if ew_tag.startswith(tag):
verse_type = tag[0]
if tag == 'tag' or tag == 'slide':
verse_type = VerseType.tags[VerseType.Other]
first_line_is_tag = True
number_found = False
# check if tag is followed by number and/or note
if len(ew_tag) > len(tag):
match = NUMBER_REGEX.search(ew_tag)
if match:
number = match.group()
verse_type += number
number_found = True
match = NOTE_REGEX.search(ew_tag)
if match:
self.comments += ew_tag + '\n'
if not number_found:
verse_type += '1'
break
self.add_verse(verse_split[-1].strip() if first_line_is_tag else verse, verse_type)
if len(self.comments) > 5:
self.comments += str(translate('SongsPlugin.EasyWorshipSongImport',
'\n[above are Song Tags with notes imported from EasyWorship]'))
if self.stop_import_flag:
break
if not self.finish():
self.log_error(self.import_source)
db_file.close()
self.memo_file.close()
def find_field(self, field_name):
"""
Find a field in the descriptions
:param field_name: field to find
:return:
"""
return [i for i, x in enumerate(self.field_descriptions) if x.name == field_name][0]
def set_record_struct(self, field_descriptions):
"""
Save the record structure
:param field_descriptions: An array of field descriptions
"""
# Begin with empty field struct list
fsl = ['>']
for field_desc in field_descriptions:
if field_desc.field_type == FieldType.String:
fsl.append('%ds' % field_desc.size)
elif field_desc.field_type == FieldType.Int16:
fsl.append('H')
elif field_desc.field_type == FieldType.Int32:
fsl.append('I')
elif field_desc.field_type == FieldType.Logical:
fsl.append('B')
elif field_desc.field_type == FieldType.Memo:
fsl.append('%ds' % field_desc.size)
elif field_desc.field_type == FieldType.Blob:
fsl.append('%ds' % field_desc.size)
elif field_desc.field_type == FieldType.Timestamp:
fsl.append('Q')
else:
fsl.append('%ds' % field_desc.size)
self.record_structure = struct.Struct(''.join(fsl))
self.field_descriptions = field_descriptions
def get_field(self, field_desc_index):
"""
Extract the field
:param field_desc_index: Field index value
:return:
"""
field = self.fields[field_desc_index]
field_desc = self.field_descriptions[field_desc_index]
# Return None in case of 'blank' entries
if isinstance(field, bytes):
if not field.rstrip(b'\0'):
return None
elif field == 0:
return None
# Format the field depending on the field type
if field_desc.field_type == FieldType.String:
return field.rstrip(b'\0')
elif field_desc.field_type == FieldType.Int16:
return field ^ 0x8000
elif field_desc.field_type == FieldType.Int32:
return field ^ 0x80000000
elif field_desc.field_type == FieldType.Logical:
return field ^ 0x80 == 1
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)
sub_block = block_start & 0xff
block_start &= ~0xff
self.memo_file.seek(block_start)
memo_block_type, = struct.unpack('b', self.memo_file.read(1))
if memo_block_type == 2:
self.memo_file.seek(8, os.SEEK_CUR)
elif memo_block_type == 3:
if sub_block > 63:
return b''
self.memo_file.seek(11 + (5 * sub_block), os.SEEK_CUR)
sub_block_start, = struct.unpack('B', self.memo_file.read(1))
self.memo_file.seek(block_start + (sub_block_start * 16))
else:
return b''
return self.memo_file.read(blob_size)
else:
return 0

View File

@ -34,21 +34,23 @@ import logging
from openlp.core.common import translate, UiStrings
from openlp.core.ui.wizard import WizardStrings
from .opensongimport import OpenSongImport
from .easyslidesimport import EasySlidesImport
from .olpimport import OpenLPSongImport
from .openlyricsimport import OpenLyricsImport
from .wowimport import WowImport
from .cclifileimport import CCLIFileImport
from .dreambeamimport import DreamBeamImport
from .powersongimport import PowerSongImport
from .ewimport import EasyWorshipSongImport
from .songbeamerimport import SongBeamerImport
from .songshowplusimport import SongShowPlusImport
from .songproimport import SongProImport
from .sundayplusimport import SundayPlusImport
from .foilpresenterimport import FoilPresenterImport
from .zionworximport import ZionWorxImport
from .importers.opensong import OpenSongImport
from .importers.easyslides import EasySlidesImport
from .importers.openlp import OpenLPSongImport
from .importers.openlyrics import OpenLyricsImport
from .importers.wordsofworship import WordsOfWorshipImport
from .importers.cclifile import CCLIFileImport
from .importers.dreambeam import DreamBeamImport
from .importers.powersong import PowerSongImport
from .importers.easyworship import EasyWorshipSongImport
from .importers.songbeamer import SongBeamerImport
from .importers.songshowplus import SongShowPlusImport
from .importers.songpro import SongProImport
from .importers.sundayplus import SundayPlusImport
from .importers.foilpresenter import FoilPresenterImport
from .importers.zionworx import ZionWorxImport
from .importers.propresenter import ProPresenterImport
from .importers.worshipassistant import WorshipAssistantImport
# Imports that might fail
@ -56,13 +58,13 @@ log = logging.getLogger(__name__)
try:
from .sofimport import SofImport
from .importers.songsoffellowship import SongsOfFellowshipImport
HAS_SOF = True
except ImportError:
log.exception('Error importing %s', 'SofImport')
log.exception('Error importing %s', 'SongsOfFellowshipImport')
HAS_SOF = False
try:
from .oooimport import OooImport
from .importers.openoffice import OpenOfficeImport
HAS_OOO = True
except ImportError:
log.exception('Error importing %s', 'OooImport')
@ -70,14 +72,14 @@ except ImportError:
HAS_MEDIASHOUT = False
if os.name == 'nt':
try:
from .mediashoutimport import MediaShoutImport
from .importers.mediashout import MediaShoutImport
HAS_MEDIASHOUT = True
except ImportError:
log.exception('Error importing %s', 'MediaShoutImport')
HAS_WORSHIPCENTERPRO = False
if os.name == 'nt':
try:
from .worshipcenterproimport import WorshipCenterProImport
from .importers.worshipcenterpro import WorshipCenterProImport
HAS_WORSHIPCENTERPRO = True
except ImportError:
log.exception('Error importing %s', 'WorshipCenterProImport')
@ -107,7 +109,7 @@ class SongFormat(object):
Name of the format, e.g. ``'OpenLyrics'``
``'prefix'``
Prefix for Qt objects. Use mixedCase, e.g. ``'open_lyrics'``
Prefix for Qt objects. Use mixedCase, e.g. ``'openLyrics'``
See ``SongImportForm.add_file_select_item()``
Optional attributes for each song format:
@ -153,19 +155,22 @@ class SongFormat(object):
CCLI = 3
DreamBeam = 4
EasySlides = 5
EasyWorship = 6
FoilPresenter = 7
MediaShout = 8
OpenSong = 9
PowerSong = 10
SongBeamer = 11
SongPro = 12
SongShowPlus = 13
SongsOfFellowship = 14
SundayPlus = 15
WordsOfWorship = 16
WorshipCenterPro = 17
ZionWorx = 18
EasyWorshipDB = 6
EasyWorshipService = 7
FoilPresenter = 8
MediaShout = 9
OpenSong = 10
PowerSong = 11
ProPresenter = 12
SongBeamer = 13
SongPro = 14
SongShowPlus = 15
SongsOfFellowship = 16
SundayPlus = 17
WordsOfWorship = 18
WorshipAssistant = 19
WorshipCenterPro = 20
ZionWorx = 21
# Set optional attribute defaults
__defaults__ = {
@ -185,7 +190,7 @@ class SongFormat(object):
OpenLyrics: {
'class': OpenLyricsImport,
'name': 'OpenLyrics',
'prefix': 'open_lyrics',
'prefix': 'openLyrics',
'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'OpenLyrics or OpenLP 2.0 Exported Song')
},
@ -224,13 +229,20 @@ class SongFormat(object):
'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'EasySlides XML File')
},
EasyWorship: {
EasyWorshipDB: {
'class': EasyWorshipSongImport,
'name': 'EasyWorship',
'name': 'EasyWorship Song Database',
'prefix': 'ew',
'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.db)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Song Database')
},
EasyWorshipService: {
'class': EasyWorshipSongImport,
'name': 'EasyWorship Service File',
'prefix': 'ew',
'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.ews)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Service File')
},
FoilPresenter: {
'class': FoilPresenterImport,
'name': 'Foilpresenter',
@ -262,6 +274,12 @@ class SongFormat(object):
'invalidSourceMsg': translate('SongsPlugin.ImportWizardForm', 'You need to specify a valid PowerSong 1.0 '
'database folder.')
},
ProPresenter: {
'class': ProPresenterImport,
'name': 'ProPresenter',
'prefix': 'proPresenter',
'filter': '%s (*.pro4)' % translate('SongsPlugin.ImportWizardForm', 'ProPresenter Song Files')
},
SongBeamer: {
'class': SongBeamerImport,
'name': 'SongBeamer',
@ -300,11 +318,21 @@ class SongFormat(object):
'filter': '%s (*.ptf)' % translate('SongsPlugin.ImportWizardForm', 'SundayPlus Song Files')
},
WordsOfWorship: {
'class': WowImport,
'class': WordsOfWorshipImport,
'name': 'Words of Worship',
'prefix': 'wordsOfWorship',
'filter': '%s (*.wsg *.wow-song)' % translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files')
},
WorshipAssistant: {
'class': WorshipAssistantImport,
'name': 'Worship Assistant 0',
'prefix': 'worshipAssistant',
'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.csv)' % translate('SongsPlugin.ImportWizardForm', 'Worship Assistant Files'),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'Worship Assistant (CSV)'),
'descriptionText': translate('SongsPlugin.ImportWizardForm',
'In Worship Assistant, export your Database to a CSV file.')
},
WorshipCenterPro: {
'name': 'WorshipCenter Pro',
'prefix': 'worshipCenterPro',
@ -341,27 +369,30 @@ class SongFormat(object):
SongFormat.CCLI,
SongFormat.DreamBeam,
SongFormat.EasySlides,
SongFormat.EasyWorship,
SongFormat.EasyWorshipDB,
SongFormat.EasyWorshipService,
SongFormat.FoilPresenter,
SongFormat.MediaShout,
SongFormat.OpenSong,
SongFormat.PowerSong,
SongFormat.ProPresenter,
SongFormat.SongBeamer,
SongFormat.SongPro,
SongFormat.SongShowPlus,
SongFormat.SongsOfFellowship,
SongFormat.SundayPlus,
SongFormat.WordsOfWorship,
SongFormat.WorshipAssistant,
SongFormat.WorshipCenterPro,
SongFormat.ZionWorx
]
@staticmethod
def get(format, *attributes):
def get(song_format, *attributes):
"""
Return requested song format attribute(s).
:param format: A song format from SongFormat.
:param song_format: A song format from SongFormat.
:param attributes: Zero or more song format attributes from SongFormat.
Return type depends on number of supplied attributes:
@ -371,31 +402,31 @@ class SongFormat(object):
:>1: Return tuple of requested attribute values.
"""
if not attributes:
return SongFormat.__attributes__.get(format)
return SongFormat.__attributes__.get(song_format)
elif len(attributes) == 1:
default = SongFormat.__defaults__.get(attributes[0])
return SongFormat.__attributes__[format].get(attributes[0], default)
return SongFormat.__attributes__[song_format].get(attributes[0], default)
else:
values = []
for attr in attributes:
default = SongFormat.__defaults__.get(attr)
values.append(SongFormat.__attributes__[format].get(attr, default))
values.append(SongFormat.__attributes__[song_format].get(attr, default))
return tuple(values)
@staticmethod
def set(format, attribute, value):
def set(song_format, attribute, value):
"""
Set specified song format attribute to the supplied value.
"""
SongFormat.__attributes__[format][attribute] = value
SongFormat.__attributes__[song_format][attribute] = value
SongFormat.set(SongFormat.SongsOfFellowship, 'availability', HAS_SOF)
if HAS_SOF:
SongFormat.set(SongFormat.SongsOfFellowship, 'class', SofImport)
SongFormat.set(SongFormat.SongsOfFellowship, 'class', SongsOfFellowshipImport)
SongFormat.set(SongFormat.Generic, 'availability', HAS_OOO)
if HAS_OOO:
SongFormat.set(SongFormat.Generic, 'class', OooImport)
SongFormat.set(SongFormat.Generic, 'class', OpenOfficeImport)
SongFormat.set(SongFormat.MediaShout, 'availability', HAS_MEDIASHOUT)
if HAS_MEDIASHOUT:
SongFormat.set(SongFormat.MediaShout, 'class', MediaShoutImport)

View File

@ -0,0 +1,31 @@
# -*- 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 #
###############################################################################
"""
The :mod:`~openlp.plugins.songs.lib.import` module contains importers for the Songs plugin.
"""

View File

@ -63,9 +63,8 @@ class CCLIFileImport(SongImport):
for filename in self.import_source:
filename = str(filename)
log.debug('Importing CCLI File: %s', filename)
lines = []
if os.path.isfile(filename):
detect_file = open(filename, 'r')
detect_file = open(filename, 'rb')
detect_content = detect_file.read(2048)
try:
str(detect_content, 'utf-8')
@ -250,7 +249,7 @@ class CCLIFileImport(SongImport):
# e.g. For use solely with the SongSelect Terms of Use.
All rights Reserved. www.ccli.com
CCLI Licence number of user
# e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14
# e.g. CCLI-Liedlizenznummer: 14 / CCLI License No. 14
"""
log.debug('TXT file text: %s', text_list)

Some files were not shown because too many files have changed in this diff Show More