forked from openlp/openlp
Merged with trunk and fixed tests
This commit is contained in:
commit
406cc506c1
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
|
11
README.txt
11
README.txt
|
@ -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!
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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 ''
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 #
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(''))
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
The actual plugin view form
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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 += ';;'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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_".
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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('')
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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()
|
||||
|
|
|
@ -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())]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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.')
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
"""
|
|
@ -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
Loading…
Reference in New Issue