This commit is contained in:
Jonathan Corwin 2010-10-14 21:39:09 +01:00
commit 3c464a4a0f
87 changed files with 3271 additions and 1025 deletions

View File

@ -1,19 +0,0 @@
# -*- mode: python -*-
a = Analysis([os.path.join(HOMEPATH,'support\\_mountzlib.py'), os.path.join(HOMEPATH,'support\\useUnicode.py'), 'openlp.pyw'],
pathex=[os.path.abspath('.')])
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
exclude_binaries=1,
name=os.path.join('build\\pyi.win32\\OpenLP', 'OpenLP.exe'),
debug=False,
strip=False,
upx=True,
console=False , icon='resources\\images\\OpenLP.ico')
coll = COLLECT( exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name=os.path.join('dist', 'OpenLP'))

View File

@ -17,7 +17,7 @@ import sys
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append(os.path.abspath(os.path.join('..', '..'))) sys.path.insert(0, os.path.abspath(os.path.join('..', '..')))
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------

View File

@ -60,12 +60,6 @@
.. autoclass:: openlp.core.lib.settingstab.SettingsTab .. autoclass:: openlp.core.lib.settingstab.SettingsTab
:members: :members:
:mod:`ThemeXML`
---------------
.. autoclass:: openlp.core.lib.themexmlhandler.ThemeXML
:members:
:mod:`OpenLPToolbar` :mod:`OpenLPToolbar`
-------------------- --------------------

View File

@ -0,0 +1,22 @@
.. OpenLP documentation master file, created by
sphinx-quickstart on Thu Sep 30 21:24:54 2010.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to OpenLP's documentation
==================================
Contents:
.. toctree::
:maxdepth: 2
introduction
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -0,0 +1,46 @@
=============
Introduction
=============
About
-----
OpenLP is an open source lyrics projection application developed specifically
for churches. It is licensed under the GNU Generic Public License, which means
that it is free to use and distribute, and it stays free.
Lyrics Projection
-----------------
OpenLP's purpose is to project the lyrics of songs and Bible verses using a
computer and a data projector. OpenLP also has the ability to project videos,
images, and also play audio. OpenLP also is highly customizable providing users
with the ability to set up a wide variety of themes, including themes with
video backgrounds.
Open Source
-----------
OpenLP is open source software. This means that the source code (the
programming instructions the developers write) is open to anyone who wants to
look at it. This gives you, the end user, a few freedoms.
From a developer's perspective, it gives you the freedom to inspect the code
and make sure that it is not malicious. Also, it gives you the freedom to
change the code and the freedom to "fork" the project and make it your own.
For end users open source software gives you the freedom to use software as
you wish. You are not required to pay for the software and you are free to
make copies and distribute it to anyone you want.
GNU General Public License
--------------------------
The GNU General Public License was specifically chosen because it ensures the
above mentioned freedoms. It specifically states that you are not allowed
to charge for the software, and that you have to distribute the source code as
well.
You can find a copy of the GNU General Public License from the Help menu
selecting about OpenLP or on-line
at: http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

View File

@ -1,7 +1,7 @@
.. _plugins-bibles: .. _plugins-bibles:
:mod:`bibles` Plugin Bibles Plugin
==================== =============
.. automodule:: openlp.plugins.bibles .. automodule:: openlp.plugins.bibles
:members: :members:
@ -60,8 +60,8 @@
.. autoclass:: openlp.plugins.bibles.lib.http.HTTPBible .. autoclass:: openlp.plugins.bibles.lib.http.HTTPBible
:members: :members:
:mod:`bibleOSISimpl` :mod:`osis`
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^
.. automodule:: openlp.plugins.bibles.lib.osis .. automodule:: openlp.plugins.bibles.lib.osis
:members: :members:
@ -69,18 +69,21 @@
.. autoclass:: openlp.plugins.bibles.lib.osis.OSISBible .. autoclass:: openlp.plugins.bibles.lib.osis.OSISBible
:members: :members:
:mod:`opensong`
^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.bibles.lib.opensong
:members:
.. autoclass:: openlp.plugins.bibles.lib.opensong.OpenSongBible
:members:
:mod:`biblestab` :mod:`biblestab`
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.bibles.lib.biblestab .. automodule:: openlp.plugins.bibles.lib.biblestab
:members: :members:
:mod:`common`
^^^^^^^^^^^^^
.. automodule:: openlp.plugins.bibles.lib.common
:members:
:mod:`manager` :mod:`manager`
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^

View File

@ -0,0 +1,8 @@
.. _plugins-images:
Images Plugin
=============
.. automodule:: openlp.plugins.images
:members:

View File

@ -11,24 +11,9 @@
songs songs
bibles bibles
presentations
:mod:`presentations` Plugin media
--------------------------- images
.. automodule:: openlp.plugins.presentations
:members:
:mod:`media` Plugin
-------------------
.. automodule:: openlp.plugins.media
:members:
:mod:`images` Plugin
--------------------
.. automodule:: openlp.plugins.images
:members:
:mod:`custom` Plugin :mod:`custom` Plugin
-------------------- --------------------

View File

@ -0,0 +1,8 @@
.. _plugins-media:
Media Plugin
============
.. automodule:: openlp.plugins.media
:members:

View File

@ -0,0 +1,40 @@
.. _plugins-presentations:
Presentations Plugin
====================
Plugin Class
------------
.. autoclass:: openlp.plugins.presentations.presentationplugin.PresentationPlugin
:members:
Helper Classes & Functions
--------------------------
.. automodule:: openlp.plugins.presentations.lib
:members:
.. automodule:: openlp.plugins.presentations.lib.mediaitem
:members:
.. automodule:: openlp.plugins.presentations.lib.presentationtab
:members:
.. automodule:: openlp.plugins.presentations.lib.messagelistener
:members:
.. automodule:: openlp.plugins.presentations.lib.presentationcontroller
:members:
Presentation Application Controllers
------------------------------------
.. automodule:: openlp.plugins.presentations.lib.impresscontroller
:members:
.. automodule:: openlp.plugins.presentations.lib.pptviewcontroller
:members:
.. automodule:: openlp.plugins.presentations.lib.powerpointcontroller
:members:

View File

@ -1,13 +1,13 @@
.. _plugins-songs: .. _plugins-songs:
:mod:`songs` Plugin Songs Plugin
=================== ============
.. automodule:: openlp.plugins.songs .. automodule:: openlp.plugins.songs
:members: :members:
:mod:`SongsPlugin` Class :class:`SongsPlugin` Class
------------------------ --------------------------
.. autoclass:: openlp.plugins.songs.songsplugin.SongsPlugin .. autoclass:: openlp.plugins.songs.songsplugin.SongsPlugin
:members: :members:
@ -18,8 +18,8 @@
.. automodule:: openlp.plugins.songs.forms .. automodule:: openlp.plugins.songs.forms
:members: :members:
:mod:`AuthorsForm` :class:`AuthorsForm`
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
.. autoclass:: openlp.plugins.songs.forms.authorsdialog.Ui_AuthorsDialog .. autoclass:: openlp.plugins.songs.forms.authorsdialog.Ui_AuthorsDialog
:members: :members:
@ -27,8 +27,8 @@
.. autoclass:: openlp.plugins.songs.forms.authorsform.AuthorsForm .. autoclass:: openlp.plugins.songs.forms.authorsform.AuthorsForm
:members: :members:
:mod:`EditSongForm` :class:`EditSongForm`
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: openlp.plugins.songs.forms.editsongdialog.Ui_EditSongDialog .. autoclass:: openlp.plugins.songs.forms.editsongdialog.Ui_EditSongDialog
:members: :members:
@ -36,11 +36,158 @@
.. autoclass:: openlp.plugins.songs.forms.editsongform.EditSongForm .. autoclass:: openlp.plugins.songs.forms.editsongform.EditSongForm
:members: :members:
:mod:`EditVerseForm` :class:`EditVerseForm`
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: openlp.plugins.songs.forms.editversedialog.Ui_EditVerseDialog .. autoclass:: openlp.plugins.songs.forms.editversedialog.Ui_EditVerseDialog
:members: :members:
.. autoclass:: openlp.plugins.songs.forms.editverseform.EditVerseForm .. autoclass:: openlp.plugins.songs.forms.editverseform.EditVerseForm
:members: :members:
:class:`SongBookForm`
^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: openlp.plugins.songs.forms.songbookdialog.Ui_SongBookDialog
:members:
.. autoclass:: openlp.plugins.songs.forms.songbookform.SongBookForm
:members:
:class:`SongImportForm`
^^^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: openlp.plugins.songs.forms.songimportwizard.Ui_SongImportWizard
:members:
.. autoclass:: openlp.plugins.songs.forms.songimportform.ImportWizardForm
:members:
:class:`SongMaintenanceForm`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: openlp.plugins.songs.forms.songmaintenancedialog.Ui_SongMaintenanceDialog
:members:
.. autoclass:: openlp.plugins.songs.forms.songmaintenanceform.SongMaintenanceForm
:members:
:class:`TopicsForm`
^^^^^^^^^^^^^^^^^^^
.. autoclass:: openlp.plugins.songs.forms.topicsdialog.Ui_TopicsDialog
:members:
.. autoclass:: openlp.plugins.songs.forms.topicsform.TopicsForm
:members:
:mod:`lib` Submodule
--------------------
.. automodule:: openlp.plugins.songs.lib
:members:
:mod:`cclifileimport` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.cclifileimport
:members:
.. autoclass:: openlp.plugins.songs.lib.cclifileimport.CCLIFileImportError
:members:
:mod:`db` Submodule
^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.db
:members:
:mod:`ewimport` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.ewimport
:members:
.. autoclass:: openlp.plugins.songs.lib.ewimport.FieldDescEntry
:members:
:mod:`importer` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.importer
:members:
:mod:`mediaitem` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.mediaitem
:members:
.. autoclass:: openlp.plugins.songs.lib.mediaitem.SongListView
:members:
:mod:`olp1import` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.olp1import
:members:
:mod:`olpimport` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.olpimport
:members:
:mod:`oooimport` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.oooimport
:members:
:mod:`opensongimport` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.opensongimport
:members:
:mod:`sofimport` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.sofimport
:members:
:mod:`songbeamerimport` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.songbeamerimport
:members:
:mod:`songimport` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.songimport
:members:
:mod:`songstab` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.songstab
:members:
:mod:`wowimport` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.wowimport
:members:
:mod:`songxml` Submodule
^^^^^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.songxml
:members:
:mod:`xml` Submodule
^^^^^^^^^^^^^^^^^^^^
.. automodule:: openlp.plugins.songs.lib.xml
:members:

View File

@ -30,6 +30,7 @@ import sys
import logging import logging
from optparse import OptionParser from optparse import OptionParser
from traceback import format_exception from traceback import format_exception
from subprocess import Popen, PIPE
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -71,6 +72,84 @@ class OpenLP(QtGui.QApplication):
""" """
log.info(u'OpenLP Application Loaded') log.info(u'OpenLP Application Loaded')
def _get_version(self):
"""
Load and store current Application Version
"""
if u'--dev-version' in sys.argv:
# If we're running the dev version, let's use bzr to get the version
try:
# If bzrlib is availble, use it
from bzrlib.branch import Branch
b = Branch.open_containing('.')[0]
b.lock_read()
try:
# Get the branch's latest revision number.
revno = b.revno()
# Convert said revision number into a bzr revision id.
revision_id = b.dotted_revno_to_revision_id((revno,))
# Get a dict of tags, with the revision id as the key.
tags = b.tags.get_reverse_tag_dict()
# Check if the latest
if revision_id in tags:
full_version = u'%s' % tags[revision_id][0]
else:
full_version = '%s-bzr%s' % \
(sorted(b.tags.get_tag_dict().keys())[-1], revno)
finally:
b.unlock()
except:
# Otherwise run the command line bzr client
bzr = Popen((u'bzr', u'tags', u'--sort', u'time'), stdout=PIPE)
output, error = bzr.communicate()
code = bzr.wait()
if code != 0:
raise Exception(u'Error running bzr tags')
lines = output.splitlines()
if len(lines) == 0:
tag = u'0.0.0'
revision = u'0'
else:
tag, revision = lines[-1].split()
bzr = Popen((u'bzr', u'log', u'--line', u'-r', u'-1'),
stdout=PIPE)
output, error = bzr.communicate()
code = bzr.wait()
if code != 0:
raise Exception(u'Error running bzr log')
latest = output.split(u':')[0]
full_version = latest == revision and tag or \
u'%s-bzr%s' % (tag, latest)
else:
# We're not running the development version, let's use the file
filepath = AppLocation.get_directory(AppLocation.VersionDir)
filepath = os.path.join(filepath, u'.version')
fversion = None
try:
fversion = open(filepath, u'r')
full_version = unicode(fversion.read()).rstrip()
except IOError:
log.exception('Error in version file.')
full_version = u'0.0.0-bzr000'
finally:
if fversion:
fversion.close()
bits = full_version.split(u'-')
app_version = {
u'full': full_version,
u'version': bits[0],
u'build': bits[1] if len(bits) > 1 else None
}
if app_version[u'build']:
log.info(
u'Openlp version %s build %s',
app_version[u'version'],
app_version[u'build']
)
else:
log.info(u'Openlp version %s' % app_version[u'version'])
return app_version
def notify(self, obj, evt): def notify(self, obj, evt):
#TODO needed for presentation exceptions #TODO needed for presentation exceptions
return QtGui.QApplication.notify(self, obj, evt) return QtGui.QApplication.notify(self, obj, evt)
@ -79,39 +158,7 @@ class OpenLP(QtGui.QApplication):
""" """
Run the OpenLP application. Run the OpenLP application.
""" """
#Load and store current Application Version app_version = self._get_version()
filepath = AppLocation.get_directory(AppLocation.VersionDir)
filepath = os.path.join(filepath, u'.version')
fversion = None
try:
fversion = open(filepath, u'r')
for line in fversion:
full_version = unicode(line).rstrip() #\
#.replace(u'\r', u'').replace(u'\n', u'')
bits = full_version.split(u'-')
app_version = {
u'full': full_version,
u'version': bits[0],
u'build': bits[1] if len(bits) > 1 else None
}
if app_version[u'build']:
log.info(
u'Openlp version %s build %s',
app_version[u'version'],
app_version[u'build']
)
else:
log.info(u'Openlp version %s' % app_version[u'version'])
except IOError:
log.exception('Error in version file.')
app_version = {
u'full': u'1.9.0-bzr000',
u'version': u'1.9.0',
u'build': u'bzr000'
}
finally:
if fversion:
fversion.close()
#provide a listener for widgets to reqest a screen update. #provide a listener for widgets to reqest a screen update.
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_process_events'), self.processEvents) QtCore.SIGNAL(u'openlp_process_events'), self.processEvents)
@ -172,6 +219,9 @@ def main():
parser.add_option('-p', '--portable', dest='portable', parser.add_option('-p', '--portable', dest='portable',
action='store_true', help='Specify if this should be run as a ' action='store_true', help='Specify if this should be run as a '
'portable app, off a USB flash drive (not implemented).') 'portable app, off a USB flash drive (not implemented).')
parser.add_option('-d', '--dev-version', dest='dev_version',
action='store_true', help='Ignore the version file and pull the '
'version directly from Bazaar')
parser.add_option('-s', '--style', dest='style', parser.add_option('-s', '--style', dest='style',
help='Set the Qt4 style (passed directly to Qt4).') help='Set the Qt4 style (passed directly to Qt4).')
# Set up logging # Set up logging

View File

@ -313,7 +313,7 @@ def expand_tags(text):
from spelltextedit import SpellTextEdit from spelltextedit import SpellTextEdit
from eventreceiver import Receiver from eventreceiver import Receiver
from settingsmanager import SettingsManager from settingsmanager import SettingsManager
from plugin import PluginStatus, Plugin from plugin import PluginStatus, StringContent, Plugin
from pluginmanager import PluginManager from pluginmanager import PluginManager
from settingstab import SettingsTab from settingstab import SettingsTab
from serviceitem import ServiceItem from serviceitem import ServiceItem

View File

@ -75,7 +75,7 @@ class EventReceiver(QtCore.QObject):
Broadcasts that an item has been made live/previewed Broadcasts that an item has been made live/previewed
``slidecontroller_{live|preview}_change`` ``slidecontroller_{live|preview}_change``
Informs the slidecontroller that a slide change has occurred and to Informs the slidecontroller that a slide change has occurred and to
update itself update itself
``slidecontroller_{live|preview}_changed`` ``slidecontroller_{live|preview}_changed``
@ -83,7 +83,7 @@ class EventReceiver(QtCore.QObject):
``slidecontroller_{live|preview}_text_request`` ``slidecontroller_{live|preview}_text_request``
Request the text for the current item in the controller Request the text for the current item in the controller
Returns a slidecontroller_{live|preview}_text_response with an Returns a slidecontroller_{live|preview}_text_response with an
array of dictionaries with the tag and verse text array of dictionaries with the tag and verse text
``slidecontroller_{live|preview}_blank`` ``slidecontroller_{live|preview}_blank``
@ -106,23 +106,23 @@ class EventReceiver(QtCore.QObject):
``servicemanager_set_item`` ``servicemanager_set_item``
Go live on a specific item, by index Go live on a specific item, by index
``servicemanager_list_request`` ``servicemanager_list_request``
Request the service list. Responds with servicemanager_list_response Request the service list. Responds with servicemanager_list_response
containing a array of dictionaries containing a array of dictionaries
``maindisplay_blank`` ``maindisplay_blank``
Blank the maindisplay window Blank the maindisplay window
``maindisplay_hide`` ``maindisplay_hide``
Hide the maindisplay window Hide the maindisplay window
``maindisplay_show`` ``maindisplay_show``
Return the maindisplay window Return the maindisplay window
``maindisplay_active`` ``maindisplay_active``
The maindisplay has been made active The maindisplay has been made active
``maindisplay_status_text`` ``maindisplay_status_text``
Changes the bottom status bar text on the maindisplay window Changes the bottom status bar text on the maindisplay window
@ -193,9 +193,17 @@ class EventReceiver(QtCore.QObject):
``{plugin}_add_service_item`` ``{plugin}_add_service_item``
Ask the plugin to push the selected items to the service item Ask the plugin to push the selected items to the service item
``{plugin}_service_load``
Ask the plugin to process an individual service item after it has been
loaded
``service_item_update``
Passes back to the service manager the service item after it has been
processed by the plugin
``alerts_text`` ``alerts_text``
Displays an alert message Displays an alert message
``bibles_nobook`` ``bibles_nobook``
Attempt to find book resulted in no match Attempt to find book resulted in no match

View File

@ -55,14 +55,17 @@ body {
background-color: black; background-color: black;
display: none; display: none;
} }
#image { #bgimage {
z-index:1; z-index:1;
} }
#video1 { #image {
z-index:2; z-index:2;
} }
#video1 {
z-index:3;
}
#video2 { #video2 {
z-index:2; z-index:3;
} }
#alert { #alert {
position: absolute; position: absolute;
@ -73,7 +76,7 @@ body {
} }
#footer { #footer {
position: absolute; position: absolute;
z-index:5; z-index:6;
%s %s
} }
/* lyric css */ /* lyric css */
@ -112,7 +115,7 @@ body {
vid2.volume = volume; vid2.volume = volume;
} }
switch(state){ switch(state){
case 'init': case 'init':
vid.src = path; vid.src = path;
vid2.src = path; vid2.src = path;
if(loop == null) loop = false; if(loop == null) loop = false;
@ -294,7 +297,8 @@ body {
</script> </script>
</head> </head>
<body> <body>
<img id="image" class="size" %s /> <img id="bgimage" class="size" %s />
<img id="image" class="size" style="display:none" />
<video id="video1" class="size" style="visibility:hidden" autobuffer preload> <video id="video1" class="size" style="visibility:hidden" autobuffer preload>
</video> </video>
<video id="video2" class="size" style="visibility:hidden" autobuffer preload> <video id="video2" class="size" style="visibility:hidden" autobuffer preload>
@ -397,7 +401,7 @@ def build_lyrics_css(item, webkitvers):
""" """
style = """ style = """
.lyricstable { .lyricstable {
z-index:4; z-index:5;
position: absolute; position: absolute;
display: table; display: table;
%s %s

View File

@ -32,7 +32,8 @@ import os
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import context_menu_action, context_menu_separator, \ from openlp.core.lib import context_menu_action, context_menu_separator, \
SettingsManager, OpenLPToolbar, ServiceItem, build_icon, translate SettingsManager, OpenLPToolbar, ServiceItem, StringContent, build_icon, \
translate, Receiver
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -51,26 +52,19 @@ class MediaManagerItem(QtGui.QWidget):
The parent widget. Usually this will be the *Media Manager* The parent widget. Usually this will be the *Media Manager*
itself. This needs to be a class descended from ``QWidget``. itself. This needs to be a class descended from ``QWidget``.
``plugin``
The plugin widget. Usually this will be the *Plugin*
itself. This needs to be a class descended from ``Plugin``.
``icon`` ``icon``
Either a ``QIcon``, a resource path, or a file name. This is Either a ``QIcon``, a resource path, or a file name. This is
the icon which is displayed in the *Media Manager*. the icon which is displayed in the *Media Manager*.
``title``
The title visible on the item in the *Media Manager*.
**Member Variables** **Member Variables**
When creating a descendant class from this class for your plugin, When creating a descendant class from this class for your plugin,
the following member variables should be set. the following member variables should be set.
``self.PluginNameShort``
The shortened (usually singular) name for the plugin e.g. *'Song'*
for the Songs plugin.
``self.pluginNameVisible``
The user visible name for a plugin which should use a suitable
translation function.
``self.OnNewPrompt`` ``self.OnNewPrompt``
Defaults to *'Select Image(s)'*. Defaults to *'Select Image(s)'*.
@ -93,13 +87,17 @@ class MediaManagerItem(QtGui.QWidget):
""" """
log.info(u'Media Item loaded') log.info(u'Media Item loaded')
def __init__(self, parent=None, icon=None, title=None): def __init__(self, parent=None, plugin=None, icon=None):
""" """
Constructor to create the media manager item. Constructor to create the media manager item.
""" """
QtGui.QWidget.__init__(self) QtGui.QWidget.__init__(self)
self.parent = parent self.parent = parent
self.settingsSection = title.lower() #TODO: plugin should not be the parent in future
self.plugin = parent # plugin
visible_title = self.plugin.getString(StringContent.VisibleName)
self.title = unicode(visible_title[u'title'])
self.settingsSection = self.plugin.name.lower()
if isinstance(icon, QtGui.QIcon): if isinstance(icon, QtGui.QIcon):
self.icon = icon self.icon = icon
elif isinstance(icon, basestring): elif isinstance(icon, basestring):
@ -107,8 +105,6 @@ class MediaManagerItem(QtGui.QWidget):
QtGui.QIcon.Normal, QtGui.QIcon.Off) QtGui.QIcon.Normal, QtGui.QIcon.Off)
else: else:
self.icon = None self.icon = None
if title:
self.title = title
self.toolbar = None self.toolbar = None
self.remoteTriggered = None self.remoteTriggered = None
self.serviceItemIconName = None self.serviceItemIconName = None
@ -119,6 +115,9 @@ class MediaManagerItem(QtGui.QWidget):
self.requiredIcons() self.requiredIcons()
self.setupUi() self.setupUi()
self.retranslateUi() self.retranslateUi()
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_service_load' % self.parent.name.lower()),
self.serviceLoad)
def requiredIcons(self): def requiredIcons(self):
""" """
@ -208,64 +207,58 @@ class MediaManagerItem(QtGui.QWidget):
""" """
## Import Button ## ## Import Button ##
if self.hasImportIcon: if self.hasImportIcon:
import_string = self.plugin.getString(StringContent.Import)
self.addToolbarButton( self.addToolbarButton(
unicode(translate('OpenLP.MediaManagerItem', 'Import %s')) % import_string[u'title'],
self.PluginNameShort, import_string[u'tooltip'],
unicode(translate('OpenLP.MediaManagerItem', 'Import a %s')) %
self.pluginNameVisible,
u':/general/general_import.png', self.onImportClick) u':/general/general_import.png', self.onImportClick)
## File Button ## ## Load Button ##
if self.hasFileIcon: if self.hasFileIcon:
load_string = self.plugin.getString(StringContent.Load)
self.addToolbarButton( self.addToolbarButton(
unicode(translate('OpenLP.MediaManagerItem', 'Load %s')) % load_string[u'title'],
self.PluginNameShort, load_string[u'tooltip'],
unicode(translate('OpenLP.MediaManagerItem', 'Load a new %s')) %
self.pluginNameVisible,
u':/general/general_open.png', self.onFileClick) u':/general/general_open.png', self.onFileClick)
## New Button ## ## New Button ##
if self.hasNewIcon: if self.hasNewIcon:
new_string = self.plugin.getString(StringContent.New)
self.addToolbarButton( self.addToolbarButton(
unicode(translate('OpenLP.MediaManagerItem', 'New %s')) % new_string[u'title'],
self.PluginNameShort, new_string[u'tooltip'],
unicode(translate('OpenLP.MediaManagerItem', 'Add a new %s')) %
self.pluginNameVisible,
u':/general/general_new.png', self.onNewClick) u':/general/general_new.png', self.onNewClick)
## Edit Button ## ## Edit Button ##
if self.hasEditIcon: if self.hasEditIcon:
edit_string = self.plugin.getString(StringContent.Edit)
self.addToolbarButton( self.addToolbarButton(
unicode(translate('OpenLP.MediaManagerItem', 'Edit %s')) % edit_string[u'title'],
self.PluginNameShort, edit_string[u'tooltip'],
unicode(translate(
'OpenLP.MediaManagerItem', 'Edit the selected %s')) %
self.pluginNameVisible,
u':/general/general_edit.png', self.onEditClick) u':/general/general_edit.png', self.onEditClick)
## Delete Button ## ## Delete Button ##
if self.hasDeleteIcon: if self.hasDeleteIcon:
delete_string = self.plugin.getString(StringContent.Delete)
self.addToolbarButton( self.addToolbarButton(
unicode(translate('OpenLP.MediaManagerItem', 'Delete %s')) % delete_string[u'title'],
self.PluginNameShort, delete_string[u'tooltip'],
translate('OpenLP.MediaManagerItem',
'Delete the selected item'),
u':/general/general_delete.png', self.onDeleteClick) u':/general/general_delete.png', self.onDeleteClick)
## Separator Line ## ## Separator Line ##
self.addToolbarSeparator() self.addToolbarSeparator()
## Preview ## ## Preview ##
preview_string = self.plugin.getString(StringContent.Preview)
self.addToolbarButton( self.addToolbarButton(
unicode(translate('OpenLP.MediaManagerItem', 'Preview %s')) % preview_string[u'title'],
self.PluginNameShort, preview_string[u'tooltip'],
translate('OpenLP.MediaManagerItem', 'Preview the selected item'),
u':/general/general_preview.png', self.onPreviewClick) u':/general/general_preview.png', self.onPreviewClick)
## Live Button ## ## Live Button ##
live_string = self.plugin.getString(StringContent.Live)
self.addToolbarButton( self.addToolbarButton(
u'Go Live', live_string[u'title'],
translate('OpenLP.MediaManagerItem', 'Send the selected item live'), live_string[u'tooltip'],
u':/general/general_live.png', self.onLiveClick) u':/general/general_live.png', self.onLiveClick)
## Add to service Button ## ## Add to service Button ##
service_string = self.plugin.getString(StringContent.Service)
self.addToolbarButton( self.addToolbarButton(
unicode(translate('OpenLP.MediaManagerItem', 'Add %s to Service')) % service_string[u'title'],
self.PluginNameShort, service_string[u'tooltip'],
translate('OpenLP.MediaManagerItem',
'Add the selected item(s) to the service'),
u':/general/general_add.png', self.onAddClick) u':/general/general_add.png', self.onAddClick)
def addListViewToToolBar(self): def addListViewToToolBar(self):
@ -281,17 +274,18 @@ class MediaManagerItem(QtGui.QWidget):
QtGui.QAbstractItemView.ExtendedSelection) QtGui.QAbstractItemView.ExtendedSelection)
self.listView.setAlternatingRowColors(True) self.listView.setAlternatingRowColors(True)
self.listView.setDragEnabled(True) self.listView.setDragEnabled(True)
self.listView.setObjectName(u'%sListView' % self.PluginNameShort) self.listView.setObjectName(u'%sListView' % self.plugin.name)
#Add to pageLayout #Add to pageLayout
self.pageLayout.addWidget(self.listView) self.pageLayout.addWidget(self.listView)
#define and add the context menu #define and add the context menu
self.listView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) self.listView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
name_string = self.plugin.getString(StringContent.Name)
if self.hasEditIcon: if self.hasEditIcon:
self.listView.addAction( self.listView.addAction(
context_menu_action( context_menu_action(
self.listView, u':/general/general_edit.png', self.listView, u':/general/general_edit.png',
unicode(translate('OpenLP.MediaManagerItem', '&Edit %s')) % unicode(translate('OpenLP.MediaManagerItem', '&Edit %s')) %
self.pluginNameVisible, name_string[u'singular'],
self.onEditClick)) self.onEditClick))
self.listView.addAction(context_menu_separator(self.listView)) self.listView.addAction(context_menu_separator(self.listView))
if self.hasDeleteIcon: if self.hasDeleteIcon:
@ -300,14 +294,14 @@ class MediaManagerItem(QtGui.QWidget):
self.listView, u':/general/general_delete.png', self.listView, u':/general/general_delete.png',
unicode(translate('OpenLP.MediaManagerItem', unicode(translate('OpenLP.MediaManagerItem',
'&Delete %s')) % '&Delete %s')) %
self.pluginNameVisible, name_string[u'singular'],
self.onDeleteClick)) self.onDeleteClick))
self.listView.addAction(context_menu_separator(self.listView)) self.listView.addAction(context_menu_separator(self.listView))
self.listView.addAction( self.listView.addAction(
context_menu_action( context_menu_action(
self.listView, u':/general/general_preview.png', self.listView, u':/general/general_preview.png',
unicode(translate('OpenLP.MediaManagerItem', '&Preview %s')) % unicode(translate('OpenLP.MediaManagerItem', '&Preview %s')) %
self.pluginNameVisible, name_string[u'singular'],
self.onPreviewClick)) self.onPreviewClick))
self.listView.addAction( self.listView.addAction(
context_menu_action( context_menu_action(
@ -447,7 +441,7 @@ class MediaManagerItem(QtGui.QWidget):
translate('OpenLP.MediaManagerItem', translate('OpenLP.MediaManagerItem',
'You must select one or more items to preview.')) 'You must select one or more items to preview.'))
else: else:
log.debug(self.PluginNameShort + u' Preview requested') log.debug(self.plugin.name + u' Preview requested')
service_item = self.buildServiceItem() service_item = self.buildServiceItem()
if service_item: if service_item:
service_item.from_plugin = True service_item.from_plugin = True
@ -464,7 +458,7 @@ class MediaManagerItem(QtGui.QWidget):
translate('OpenLP.MediaManagerItem', translate('OpenLP.MediaManagerItem',
'You must select one or more items to send live.')) 'You must select one or more items to send live.'))
else: else:
log.debug(self.PluginNameShort + u' Live requested') log.debug(self.plugin.name + u' Live requested')
service_item = self.buildServiceItem() service_item = self.buildServiceItem()
if service_item: if service_item:
service_item.from_plugin = True service_item.from_plugin = True
@ -480,10 +474,10 @@ class MediaManagerItem(QtGui.QWidget):
translate('OpenLP.MediaManagerItem', translate('OpenLP.MediaManagerItem',
'You must select one or more items.')) 'You must select one or more items.'))
else: else:
#Is it posssible to process multiple list items to generate multiple # Is it posssible to process multiple list items to generate multiple
#service items? # service items?
if self.singleServiceItem or self.remoteTriggered: if self.singleServiceItem or self.remoteTriggered:
log.debug(self.PluginNameShort + u' Add requested') log.debug(self.plugin.name + u' Add requested')
service_item = self.buildServiceItem() service_item = self.buildServiceItem()
if service_item: if service_item:
service_item.from_plugin = False service_item.from_plugin = False
@ -507,10 +501,10 @@ class MediaManagerItem(QtGui.QWidget):
translate('OpenLP.MediaManagerItem', translate('OpenLP.MediaManagerItem',
'You must select one or more items')) 'You must select one or more items'))
else: else:
log.debug(self.PluginNameShort + u' Add requested') log.debug(self.plugin.name + u' Add requested')
service_item = self.parent.serviceManager.getServiceItem() service_item = self.parent.serviceManager.getServiceItem()
if not service_item: if not service_item:
QtGui.QMessageBox.information(self, QtGui.QMessageBox.information(self,
translate('OpenLP.MediaManagerItem', translate('OpenLP.MediaManagerItem',
'No Service Item Selected'), 'No Service Item Selected'),
translate('OpenLP.MediaManagerItem', translate('OpenLP.MediaManagerItem',
@ -540,3 +534,11 @@ class MediaManagerItem(QtGui.QWidget):
return service_item return service_item
else: else:
return None return None
def serviceLoad(self, message):
"""
Method to add processing when a service has been loaded and
individual service items need to be processed by the plugins
"""
pass

View File

@ -42,6 +42,18 @@ class PluginStatus(object):
Inactive = 0 Inactive = 0
Disabled = -1 Disabled = -1
class StringContent(object):
Name = u'name'
Import = u'import'
Load = u'load'
New = u'new'
Edit = u'edit'
Delete = u'delete'
Preview = u'preview'
Live = u'live'
Service = u'service'
VisibleName = u'visible_name'
class Plugin(QtCore.QObject): class Plugin(QtCore.QObject):
""" """
Base class for openlp plugins to inherit from. Base class for openlp plugins to inherit from.
@ -117,6 +129,8 @@ class Plugin(QtCore.QObject):
""" """
QtCore.QObject.__init__(self) QtCore.QObject.__init__(self)
self.name = name self.name = name
self.textStrings = {}
self.setPluginTextStrings()
if version: if version:
self.version = version self.version = version
self.settingsSection = self.name.lower() self.settingsSection = self.name.lower()
@ -257,9 +271,9 @@ class Plugin(QtCore.QObject):
Called by the plugin to remove toolbar Called by the plugin to remove toolbar
""" """
if self.mediaItem: if self.mediaItem:
self.mediadock.remove_dock(self.name) self.mediadock.remove_dock(self.mediaItem)
if self.settings_tab: if self.settings_tab:
self.settingsForm.removeTab(self.name) self.settingsForm.removeTab(self.settings_tab)
def insertToolboxItem(self): def insertToolboxItem(self):
""" """
@ -289,3 +303,15 @@ class Plugin(QtCore.QObject):
The new name the plugin should now use. The new name the plugin should now use.
""" """
pass pass
def getString(self, name):
"""
encapsulate access of plugins translated text strings
"""
return self.textStrings[name]
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
pass

View File

@ -30,7 +30,7 @@ import os
import sys import sys
import logging import logging
from openlp.core.lib import Plugin, PluginStatus from openlp.core.lib import Plugin, StringContent, PluginStatus
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -152,12 +152,13 @@ class PluginManager(object):
for plugin in self.plugins: for plugin in self.plugins:
if plugin.status is not PluginStatus.Disabled: if plugin.status is not PluginStatus.Disabled:
plugin.settings_tab = plugin.getSettingsTab() plugin.settings_tab = plugin.getSettingsTab()
visible_title = plugin.getString(StringContent.VisibleName)
if plugin.settings_tab: if plugin.settings_tab:
log.debug(u'Inserting settings tab item from %s' % log.debug(u'Inserting settings tab item from %s' %
plugin.name) visible_title[u'title'])
settingsform.addTab(plugin.name, plugin.settings_tab) settingsform.addTab(visible_title[u'title'], plugin.settings_tab)
else: else:
log.debug(u'No tab settings in %s' % plugin.name) log.debug(u'No tab settings in %s' % visible_title[u'title'])
def hook_import_menu(self, import_menu): def hook_import_menu(self, import_menu):
""" """

View File

@ -58,6 +58,9 @@ class ItemCapabilities(object):
AllowsLoop = 5 AllowsLoop = 5
AllowsAdditions = 6 AllowsAdditions = 6
NoLineBreaks = 7 NoLineBreaks = 7
OnLoadUpdate = 8
AddIfNewItem = 9
class ServiceItem(object): class ServiceItem(object):
""" """
@ -98,6 +101,9 @@ class ServiceItem(object):
self.main = None self.main = None
self.footer = None self.footer = None
self.bg_image_bytes = None self.bg_image_bytes = None
self.search_string = u''
self.data_string = u''
self._new_item()
def _new_item(self): def _new_item(self):
""" """
@ -149,15 +155,15 @@ class ServiceItem(object):
line_break = True line_break = True
if self.is_capable(ItemCapabilities.NoLineBreaks): if self.is_capable(ItemCapabilities.NoLineBreaks):
line_break = False line_break = False
theme = None
if self.theme:
theme = self.theme
self.main, self.footer = \
self.render_manager.set_override_theme(theme, useOverride)
self.bg_image_bytes = self.render_manager.renderer.bg_image_bytes
self.themedata = self.render_manager.renderer._theme
if self.service_item_type == ServiceItemType.Text: if self.service_item_type == ServiceItemType.Text:
log.debug(u'Formatting slides') log.debug(u'Formatting slides')
theme = None
if self.theme:
theme = self.theme
self.main, self.footer = \
self.render_manager.set_override_theme(theme, useOverride)
self.bg_image_bytes = self.render_manager.renderer.bg_image_bytes
self.themedata = self.render_manager.renderer._theme
for slide in self._raw_frames: for slide in self._raw_frames:
before = time.time() before = time.time()
formatted = self.render_manager \ formatted = self.render_manager \
@ -170,7 +176,6 @@ class ServiceItem(object):
u'verseTag': slide[u'verseTag'] }) u'verseTag': slide[u'verseTag'] })
log.log(15, u'Formatting took %4s' % (time.time() - before)) log.log(15, u'Formatting took %4s' % (time.time() - before))
elif self.service_item_type == ServiceItemType.Image: elif self.service_item_type == ServiceItemType.Image:
self.themedata = self.render_manager.global_theme_data
for slide in self._raw_frames: for slide in self._raw_frames:
slide[u'image'] = resize_image(slide[u'image'], slide[u'image'] = resize_image(slide[u'image'],
self.render_manager.width, self.render_manager.height) self.render_manager.width, self.render_manager.height)
@ -255,7 +260,9 @@ class ServiceItem(object):
u'audit':self.audit, u'audit':self.audit,
u'notes':self.notes, u'notes':self.notes,
u'from_plugin':self.from_plugin, u'from_plugin':self.from_plugin,
u'capabilities':self.capabilities u'capabilities':self.capabilities,
u'search':self.search_string,
u'data':self.data_string
} }
service_data = [] service_data = []
if self.service_item_type == ServiceItemType.Text: if self.service_item_type == ServiceItemType.Text:
@ -293,6 +300,10 @@ class ServiceItem(object):
self.notes = header[u'notes'] self.notes = header[u'notes']
self.from_plugin = header[u'from_plugin'] self.from_plugin = header[u'from_plugin']
self.capabilities = header[u'capabilities'] self.capabilities = header[u'capabilities']
# Added later so may not be present in older services.
if u'search' in header:
self.search_string = header[u'search']
self.data_string = header[u'data']
if self.service_item_type == ServiceItemType.Text: if self.service_item_type == ServiceItemType.Text:
for slide in serviceitem[u'serviceitem'][u'data']: for slide in serviceitem[u'serviceitem'][u'data']:
self._raw_frames.append(slide) self._raw_frames.append(slide)
@ -306,6 +317,7 @@ class ServiceItem(object):
filename = os.path.join(path, text_image[u'title']) filename = os.path.join(path, text_image[u'title'])
self.add_from_command( self.add_from_command(
path, text_image[u'title'], text_image[u'image'] ) path, text_image[u'title'], text_image[u'image'] )
self._new_item()
def merge(self, other): def merge(self, other):
""" """

View File

@ -31,16 +31,19 @@ class SettingsTab(QtGui.QWidget):
SettingsTab is a helper widget for plugins to define Tabs for the settings SettingsTab is a helper widget for plugins to define Tabs for the settings
dialog. dialog.
""" """
def __init__(self, title): def __init__(self, title, visible_title=None):
""" """
Constructor to create the Settings tab item. Constructor to create the Settings tab item.
``title`` ``title``
The title of the tab, which is used internally for the tab handling.
``visible_title``
The title of the tab, which is usually displayed on the tab. The title of the tab, which is usually displayed on the tab.
""" """
QtGui.QWidget.__init__(self) QtGui.QWidget.__init__(self)
self.tabTitle = title self.tabTitle = title
self.tabTitleVisible = None self.tabTitleVisible = visible_title
self.settingsSection = self.tabTitle.lower() self.settingsSection = self.tabTitle.lower()
self.setupUi() self.setupUi()
self.retranslateUi() self.retranslateUi()

View File

@ -82,6 +82,9 @@ class AdvancedTab(SettingsTab):
self.doubleClickLiveCheckBox.setObjectName(u'doubleClickLiveCheckBox') self.doubleClickLiveCheckBox.setObjectName(u'doubleClickLiveCheckBox')
self.uiLayout.addWidget(self.doubleClickLiveCheckBox) self.uiLayout.addWidget(self.doubleClickLiveCheckBox)
self.leftLayout.addWidget(self.uiGroupBox) self.leftLayout.addWidget(self.uiGroupBox)
self.expandServiceItemCheckBox = QtGui.QCheckBox(self.uiGroupBox)
self.expandServiceItemCheckBox.setObjectName(u'expandServiceItemCheckBox')
self.uiLayout.addWidget(self.expandServiceItemCheckBox)
# self.sharedDirGroupBox = QtGui.QGroupBox(self.leftWidget) # self.sharedDirGroupBox = QtGui.QGroupBox(self.leftWidget)
# self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox') # self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox')
# self.sharedDirGroupBox.setGeometry(QtCore.QRect(0, 65, 500, 85)) # self.sharedDirGroupBox.setGeometry(QtCore.QRect(0, 65, 500, 85))
@ -140,6 +143,8 @@ class AdvancedTab(SettingsTab):
'Remember active media manager tab on startup')) 'Remember active media manager tab on startup'))
self.doubleClickLiveCheckBox.setText(translate('OpenLP.AdvancedTab', self.doubleClickLiveCheckBox.setText(translate('OpenLP.AdvancedTab',
'Double-click to send items straight to live (requires restart)')) 'Double-click to send items straight to live (requires restart)'))
self.expandServiceItemCheckBox.setText(translate('OpenLP.AdvancedTab',
'Expand new service items on creation'))
# self.sharedDirGroupBox.setTitle( # self.sharedDirGroupBox.setTitle(
# translate('AdvancedTab', 'Central Data Store')) # translate('AdvancedTab', 'Central Data Store'))
# self.sharedCheckBox.setText( # self.sharedCheckBox.setText(
@ -167,6 +172,9 @@ class AdvancedTab(SettingsTab):
self.doubleClickLiveCheckBox.setChecked( self.doubleClickLiveCheckBox.setChecked(
settings.value(u'double click live', settings.value(u'double click live',
QtCore.QVariant(False)).toBool()) QtCore.QVariant(False)).toBool())
self.expandServiceItemCheckBox.setChecked(
settings.value(u'expand service item',
QtCore.QVariant(False)).toBool())
settings.endGroup() settings.endGroup()
def save(self): def save(self):
@ -181,6 +189,8 @@ class AdvancedTab(SettingsTab):
QtCore.QVariant(self.mediaPluginCheckBox.isChecked())) QtCore.QVariant(self.mediaPluginCheckBox.isChecked()))
settings.setValue(u'double click live', settings.setValue(u'double click live',
QtCore.QVariant(self.doubleClickLiveCheckBox.isChecked())) QtCore.QVariant(self.doubleClickLiveCheckBox.isChecked()))
settings.setValue(u'expand service item',
QtCore.QVariant(self.expandServiceItemCheckBox.isChecked()))
settings.endGroup() settings.endGroup()
def onSharedCheckBoxChanged(self, checked): def onSharedCheckBoxChanged(self, checked):

View File

@ -114,21 +114,23 @@ class MainDisplay(DisplayWidget):
""" """
log.debug(u'Setup live = %s for %s ' % (self.isLive, log.debug(u'Setup live = %s for %s ' % (self.isLive,
self.screens.monitor_number)) self.screens.monitor_number))
self.usePhonon = QtCore.QSettings().value(
u'media/use phonon', QtCore.QVariant(True)).toBool()
self.phononActive = False
self.screen = self.screens.current self.screen = self.screens.current
self.setVisible(False) self.setVisible(False)
self.setGeometry(self.screen[u'size']) self.setGeometry(self.screen[u'size'])
try: self.videoWidget = Phonon.VideoWidget(self)
self.webView = QtWebKit.QGraphicsWebView() self.videoWidget.setVisible(False)
self.scene = QtGui.QGraphicsScene(self) self.videoWidget.setGeometry(QtCore.QRect(0, 0,
self.setScene(self.scene) self.screen[u'size'].width(), self.screen[u'size'].height()))
self.scene.addItem(self.webView) self.mediaObject = Phonon.MediaObject(self)
self.webView.setGeometry(QtCore.QRectF(0, 0, self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
self.screen[u'size'].width(), self.screen[u'size'].height())) Phonon.createPath(self.mediaObject, self.videoWidget)
except AttributeError: Phonon.createPath(self.mediaObject, self.audio)
# QGraphicsWebView a recent addition, so fall back to QWebView self.webView = QtWebKit.QWebView(self)
self.webView = QtWebKit.QWebView(self) self.webView.setGeometry(0, 0,
self.webView.setGeometry(0, 0, self.screen[u'size'].width(), self.screen[u'size'].height())
self.screen[u'size'].width(), self.screen[u'size'].height())
self.page = self.webView.page() self.page = self.webView.page()
self.frame = self.page.mainFrame() self.frame = self.page.mainFrame()
QtCore.QObject.connect(self.webView, QtCore.QObject.connect(self.webView,
@ -199,7 +201,7 @@ class MainDisplay(DisplayWidget):
""" """
log.debug(u'alert') log.debug(u'alert')
if self.height() != self.screen[u'size'].height() \ if self.height() != self.screen[u'size'].height() \
or not self.isVisible(): or not self.isVisible() or self.videoWidget.isVisible():
shrink = True shrink = True
else: else:
shrink = False shrink = False
@ -208,12 +210,17 @@ class MainDisplay(DisplayWidget):
u'top' if shrink else u'') u'top' if shrink else u'')
height = self.frame.evaluateJavaScript(js) height = self.frame.evaluateJavaScript(js)
if shrink: if shrink:
if text: if self.phononActive:
self.resize(self.width(), int(height.toString())) shrinkItem = self.webView
self.setVisible(True)
else: else:
self.setGeometry(self.screen[u'size']) shrinkItem = self
self.setVisible(False) if text:
shrinkItem.resize(self.width(), int(height.toString()))
shrinkItem.setVisible(True)
else:
shrinkItem.setVisible(False)
shrinkItem.resize(self.screen[u'size'].width(),
self.screen[u'size'].height())
def image(self, image): def image(self, image):
""" """
@ -259,14 +266,24 @@ class MainDisplay(DisplayWidget):
Used after Video plugin has changed the background Used after Video plugin has changed the background
""" """
log.debug(u'resetVideo') log.debug(u'resetVideo')
self.frame.evaluateJavaScript(u'show_video("close");') if self.phononActive:
self.mediaObject.stop()
self.mediaObject.clearQueue()
self.webView.setVisible(True)
self.videoWidget.setVisible(False)
self.phononActive = False
else:
self.frame.evaluateJavaScript(u'show_video("close");')
def videoPlay(self): def videoPlay(self):
""" """
Responds to the request to play a loaded video Responds to the request to play a loaded video
""" """
log.debug(u'videoPlay') log.debug(u'videoPlay')
self.frame.evaluateJavaScript(u'show_video("play");') if self.phononActive:
self.mediaObject.play()
else:
self.frame.evaluateJavaScript(u'show_video("play");')
# show screen # show screen
if self.isLive: if self.isLive:
self.setVisible(True) self.setVisible(True)
@ -276,32 +293,54 @@ class MainDisplay(DisplayWidget):
Responds to the request to pause a loaded video Responds to the request to pause a loaded video
""" """
log.debug(u'videoPause') log.debug(u'videoPause')
self.frame.evaluateJavaScript(u'show_video("pause");') if self.phononActive:
self.mediaObject.pause()
else:
self.frame.evaluateJavaScript(u'show_video("pause");')
def videoStop(self): def videoStop(self):
""" """
Responds to the request to stop a loaded video Responds to the request to stop a loaded video
""" """
log.debug(u'videoStop') log.debug(u'videoStop')
self.frame.evaluateJavaScript(u'show_video("stop");') if self.phononActive:
self.mediaObject.stop()
else:
self.frame.evaluateJavaScript(u'show_video("stop");')
def videoVolume(self, volume): def videoVolume(self, volume):
""" """
Changes the volume of a running video Changes the volume of a running video
""" """
log.debug(u'videoVolume %d' % volume) log.debug(u'videoVolume %d' % volume)
self.frame.evaluateJavaScript(u'show_video(null, null, %s);' % vol = float(volume)/float(10)
str(float(volume)/float(10))) if self.phononActive:
self.audio.setVolume(vol)
else:
self.frame.evaluateJavaScript(u'show_video(null, null, %s);' %
str(vol))
def video(self, videoPath, volume): def video(self, videoPath, volume, isBackground=False):
""" """
Loads and starts a video to run with the option of sound Loads and starts a video to run with the option of sound
""" """
log.debug(u'video') log.debug(u'video')
self.loaded = True self.loaded = True
js = u'show_video("init", "%s", %s, true); show_video("play");' % \ vol = float(volume)/float(10)
(videoPath.replace(u'\\', u'\\\\'), str(float(volume)/float(10))) if isBackground or not self.usePhonon:
self.frame.evaluateJavaScript(js) js = u'show_video("init", "%s", %s, true); show_video("play");' % \
(videoPath.replace(u'\\', u'\\\\'), \
str(vol))
self.frame.evaluateJavaScript(js)
else:
self.phononActive = True
self.mediaObject.stop()
self.mediaObject.clearQueue()
self.mediaObject.setCurrentSource(Phonon.MediaSource(videoPath))
self.mediaObject.play()
self.webView.setVisible(False)
self.videoWidget.setVisible(True)
self.audio.setVolume(vol)
return self.preview() return self.preview()
def isLoaded(self): def isLoaded(self):
@ -382,6 +421,8 @@ class MainDisplay(DisplayWidget):
Store the images so they can be replaced when required Store the images so they can be replaced when required
""" """
log.debug(u'hideDisplay mode = %d', mode) log.debug(u'hideDisplay mode = %d', mode)
if self.phononActive:
self.videoPause()
if mode == HideMode.Screen: if mode == HideMode.Screen:
self.frame.evaluateJavaScript(u'show_blank("desktop");') self.frame.evaluateJavaScript(u'show_blank("desktop");')
self.setVisible(False) self.setVisible(False)
@ -389,8 +430,11 @@ class MainDisplay(DisplayWidget):
self.frame.evaluateJavaScript(u'show_blank("black");') self.frame.evaluateJavaScript(u'show_blank("black");')
else: else:
self.frame.evaluateJavaScript(u'show_blank("theme");') self.frame.evaluateJavaScript(u'show_blank("theme");')
if mode != HideMode.Screen and self.isHidden(): if mode != HideMode.Screen:
self.setVisible(True) if self.isHidden():
self.setVisible(True)
if self.phononActive:
self.webView.setVisible(True)
self.hide_mode = mode self.hide_mode = mode
def showDisplay(self): def showDisplay(self):
@ -403,6 +447,9 @@ class MainDisplay(DisplayWidget):
self.frame.evaluateJavaScript('show_blank("show");') self.frame.evaluateJavaScript('show_blank("show");')
if self.isHidden(): if self.isHidden():
self.setVisible(True) self.setVisible(True)
if self.phononActive:
self.webView.setVisible(False)
self.videoPlay()
# Trigger actions when display is active again # Trigger actions when display is active again
Receiver.send_message(u'maindisplay_active') Receiver.send_message(u'maindisplay_active')
self.hide_mode = None self.hide_mode = None
@ -484,3 +531,4 @@ class AudioPlayer(QtCore.QObject):
""" """
log.debug(u'AudioPlayer Reached end of media playlist') log.debug(u'AudioPlayer Reached end of media playlist')
self.mediaObject.clearQueue() self.mediaObject.clearQueue()

View File

@ -175,19 +175,13 @@ class Ui_MainWindow(object):
QtCore.Qt.DockWidgetArea(2), self.ThemeManagerDock) QtCore.Qt.DockWidgetArea(2), self.ThemeManagerDock)
# Create the menu items # Create the menu items
self.FileNewItem = QtGui.QAction(MainWindow) self.FileNewItem = QtGui.QAction(MainWindow)
self.FileNewItem.setIcon( self.FileNewItem.setIcon(build_icon(u':/general/general_new.png'))
self.ServiceManagerContents.toolbar.getIconFromTitle(
u'New Service'))
self.FileNewItem.setObjectName(u'FileNewItem') self.FileNewItem.setObjectName(u'FileNewItem')
self.FileOpenItem = QtGui.QAction(MainWindow) self.FileOpenItem = QtGui.QAction(MainWindow)
self.FileOpenItem.setIcon( self.FileOpenItem.setIcon(build_icon(u':/general/general_open.png'))
self.ServiceManagerContents.toolbar.getIconFromTitle(
u'Open Service'))
self.FileOpenItem.setObjectName(u'FileOpenItem') self.FileOpenItem.setObjectName(u'FileOpenItem')
self.FileSaveItem = QtGui.QAction(MainWindow) self.FileSaveItem = QtGui.QAction(MainWindow)
self.FileSaveItem.setIcon( self.FileSaveItem.setIcon(build_icon(u':/general/general_save.png'))
self.ServiceManagerContents.toolbar.getIconFromTitle(
u'Save Service'))
self.FileSaveItem.setObjectName(u'FileSaveItem') self.FileSaveItem.setObjectName(u'FileSaveItem')
self.FileSaveAsItem = QtGui.QAction(MainWindow) self.FileSaveAsItem = QtGui.QAction(MainWindow)
self.FileSaveAsItem.setObjectName(u'FileSaveAsItem') self.FileSaveAsItem.setObjectName(u'FileSaveAsItem')
@ -343,7 +337,6 @@ class Ui_MainWindow(object):
Set up the translation system Set up the translation system
""" """
MainWindow.mainTitle = translate('OpenLP.MainWindow', 'OpenLP 2.0') MainWindow.mainTitle = translate('OpenLP.MainWindow', 'OpenLP 2.0')
# MainWindow.language = translate('OpenLP.MainWindow', 'English')
MainWindow.setWindowTitle(MainWindow.mainTitle) MainWindow.setWindowTitle(MainWindow.mainTitle)
self.FileMenu.setTitle(translate('OpenLP.MainWindow', '&File')) self.FileMenu.setTitle(translate('OpenLP.MainWindow', '&File'))
self.FileImportMenu.setTitle(translate('OpenLP.MainWindow', '&Import')) self.FileImportMenu.setTitle(translate('OpenLP.MainWindow', '&Import'))

View File

@ -26,6 +26,8 @@
import logging import logging
from openlp.core.lib import StringContent
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class MediaDockManager(object): class MediaDockManager(object):
@ -48,8 +50,9 @@ class MediaDockManager(object):
``icon`` ``icon``
An icon for this dock item An icon for this dock item
""" """
log.info(u'Adding %s dock' % media_item.title) visible_title = media_item.plugin.getString(StringContent.VisibleName)
self.media_dock.addItem(media_item, icon, media_item.title) log.info(u'Adding %s dock' % visible_title)
self.media_dock.addItem(media_item, icon, visible_title[u'title'])
def insert_dock(self, media_item, icon, weight): def insert_dock(self, media_item, icon, weight):
""" """
@ -57,27 +60,29 @@ class MediaDockManager(object):
This does not work as it gives a Segmentation error. This does not work as it gives a Segmentation error.
For now add at end of stack if not present For now add at end of stack if not present
""" """
log.debug(u'Inserting %s dock' % media_item.title) visible_title = media_item.plugin.getString(StringContent.VisibleName)
log.debug(u'Inserting %s dock' % visible_title[u'title'])
match = False match = False
for dock_index in range(0, self.media_dock.count()): for dock_index in range(0, self.media_dock.count()):
if self.media_dock.widget(dock_index).settingsSection == \ if self.media_dock.widget(dock_index).settingsSection == \
media_item.title.lower(): media_item.plugin.name.lower():
match = True match = True
break break
if not match: if not match:
self.media_dock.addItem(media_item, icon, media_item.title) self.media_dock.addItem(media_item, icon, visible_title[u'title'])
def remove_dock(self, name): def remove_dock(self, media_item):
""" """
Removes a MediaManagerItem from the dock Removes a MediaManagerItem from the dock
``name`` ``media_item``
The item to remove The item to add to the dock
""" """
log.debug(u'remove %s dock' % name) visible_title = media_item.plugin.getString(StringContent.VisibleName)
log.debug(u'remove %s dock' % visible_title[u'title'])
for dock_index in range(0, self.media_dock.count()): for dock_index in range(0, self.media_dock.count()):
if self.media_dock.widget(dock_index): if self.media_dock.widget(dock_index):
if self.media_dock.widget(dock_index).settingsSection == \ if self.media_dock.widget(dock_index).settingsSection == \
name.lower(): media_item.plugin.name.lower():
self.media_dock.widget(dock_index).hide() self.media_dock.widget(dock_index).hide()
self.media_dock.removeItem(dock_index) self.media_dock.removeItem(dock_index)

View File

@ -28,7 +28,7 @@ import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import PluginStatus, translate from openlp.core.lib import PluginStatus, StringContent, translate
from plugindialog import Ui_PluginViewDialog from plugindialog import Ui_PluginViewDialog
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -78,7 +78,8 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
elif plugin.status == PluginStatus.Disabled: elif plugin.status == PluginStatus.Disabled:
status_text = unicode( status_text = unicode(
translate('OpenLP.PluginForm', '%s (Disabled)')) translate('OpenLP.PluginForm', '%s (Disabled)'))
item.setText(status_text % plugin.name) name_string = plugin.getString(StringContent.Name)
item.setText(status_text % name_string[u'plural'])
# If the plugin has an icon, set it! # If the plugin has an icon, set it!
if plugin.icon: if plugin.icon:
item.setIcon(plugin.icon) item.setIcon(plugin.icon)
@ -106,10 +107,11 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
if self.pluginListWidget.currentItem() is None: if self.pluginListWidget.currentItem() is None:
self._clearDetails() self._clearDetails()
return return
plugin_name = self.pluginListWidget.currentItem().text().split(u' ')[0] plugin_name_plural = self.pluginListWidget.currentItem().text().split(u' ')[0]
self.activePlugin = None self.activePlugin = None
for plugin in self.parent.plugin_manager.plugins: for plugin in self.parent.plugin_manager.plugins:
if plugin.name == plugin_name: name_string = plugin.getString(StringContent.Name)
if name_string[u'plural'] == plugin_name_plural:
self.activePlugin = plugin self.activePlugin = plugin
break break
if self.activePlugin: if self.activePlugin:
@ -137,5 +139,6 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
elif self.activePlugin.status == PluginStatus.Disabled: elif self.activePlugin.status == PluginStatus.Disabled:
status_text = unicode( status_text = unicode(
translate('OpenLP.PluginForm', '%s (Disabled)')) translate('OpenLP.PluginForm', '%s (Disabled)'))
name_string = self.activePlugin.getString(StringContent.Name)
self.pluginListWidget.currentItem().setText( self.pluginListWidget.currentItem().setText(
status_text % self.activePlugin.name) status_text % name_string[u'plural'])

View File

@ -107,6 +107,7 @@ class ServiceManager(QtGui.QWidget):
self.serviceName = u'' self.serviceName = u''
self.suffixes = [] self.suffixes = []
self.droppos = 0 self.droppos = 0
self.expandTabs = False
#is a new service and has not been saved #is a new service and has not been saved
self.isNew = True self.isNew = True
self.serviceNoteForm = ServiceNoteForm(self.parent) self.serviceNoteForm = ServiceNoteForm(self.parent)
@ -199,6 +200,19 @@ class ServiceManager(QtGui.QWidget):
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'Delete the selected item from the service.'), 'Delete the selected item from the service.'),
self.onDeleteFromService) self.onDeleteFromService)
self.orderToolbar.addSeparator()
self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', '&Expand all'),
u':/services/service_top.png',
translate('OpenLP.ServiceManager',
'Expand all the service items.'),
self.onExpandAll)
self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', '&Collapse all'),
u':/services/service_bottom.png',
translate('OpenLP.ServiceManager',
'Collapse all the service items.'),
self.onCollapseAll)
self.layout.addWidget(self.orderToolbar) self.layout.addWidget(self.orderToolbar)
# Connect up our signals and slots # Connect up our signals and slots
QtCore.QObject.connect(self.themeComboBox, QtCore.QObject.connect(self.themeComboBox,
@ -220,9 +234,11 @@ class ServiceManager(QtGui.QWidget):
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'servicemanager_list_request'), self.listRequest) QtCore.SIGNAL(u'servicemanager_list_request'), self.listRequest)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.regenerateServiceItems) QtCore.SIGNAL(u'config_updated'), self.configUpdated)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'theme_update_global'), self.themeChange) QtCore.SIGNAL(u'theme_update_global'), self.themeChange)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'service_item_update'), self.serviceItemUpdate)
# Last little bits of setting up # Last little bits of setting up
self.service_theme = unicode(QtCore.QSettings().value( self.service_theme = unicode(QtCore.QSettings().value(
self.parent.serviceSettingsSection + u'/service theme', self.parent.serviceSettingsSection + u'/service theme',
@ -263,6 +279,17 @@ class ServiceManager(QtGui.QWidget):
self.themeMenu = QtGui.QMenu( self.themeMenu = QtGui.QMenu(
translate('OpenLP.ServiceManager', '&Change Item Theme')) translate('OpenLP.ServiceManager', '&Change Item Theme'))
self.menu.addMenu(self.themeMenu) self.menu.addMenu(self.themeMenu)
self.configUpdated(True)
def configUpdated(self, firstTime=False):
"""
Triggered when Config dialog is updated.
"""
self.expandTabs = QtCore.QSettings().value(
u'advanced/expand service item',
QtCore.QVariant(u'False')).toBool()
if not firstTime:
self.regenerateServiceItems()
def supportedSuffixes(self, suffix): def supportedSuffixes(self, suffix):
self.suffixes.append(suffix) self.suffixes.append(suffix)
@ -319,7 +346,7 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems[item][u'service_item']) self.serviceItems[item][u'service_item'])
if self.serviceItemEditForm.exec_(): if self.serviceItemEditForm.exec_():
self.addServiceItem(self.serviceItemEditForm.getServiceItem(), self.addServiceItem(self.serviceItemEditForm.getServiceItem(),
replace=True) replace=True, expand=self.serviceItems[item][u'expand'])
def nextItem(self): def nextItem(self):
""" """
@ -421,6 +448,14 @@ class ServiceManager(QtGui.QWidget):
if setSelected: if setSelected:
firstItem.setSelected(True) firstItem.setSelected(True)
def onCollapseAll(self):
"""
Collapse all the service items
"""
for item in self.serviceItems:
item[u'expanded'] = False
self.regenerateServiceItems()
def collapsed(self, item): def collapsed(self, item):
""" """
Record if an item is collapsed Record if an item is collapsed
@ -429,6 +464,14 @@ class ServiceManager(QtGui.QWidget):
pos = item.data(0, QtCore.Qt.UserRole).toInt()[0] pos = item.data(0, QtCore.Qt.UserRole).toInt()[0]
self.serviceItems[pos -1 ][u'expanded'] = False self.serviceItems[pos -1 ][u'expanded'] = False
def onExpandAll(self):
"""
Collapse all the service items
"""
for item in self.serviceItems:
item[u'expanded'] = True
self.regenerateServiceItems()
def expanded(self, item): def expanded(self, item):
""" """
Record if an item is collapsed Record if an item is collapsed
@ -526,12 +569,12 @@ class ServiceManager(QtGui.QWidget):
Used when moving items as the move takes place in supporting array, Used when moving items as the move takes place in supporting array,
and when regenerating all the items due to theme changes and when regenerating all the items due to theme changes
""" """
#Correct order of items in array # Correct order of items in array
count = 1 count = 1
for item in self.serviceItems: for item in self.serviceItems:
item[u'order'] = count item[u'order'] = count
count += 1 count += 1
#Repaint the screen # Repaint the screen
self.serviceManagerList.clear() self.serviceManagerList.clear()
for itemcount, item in enumerate(self.serviceItems): for itemcount, item in enumerate(self.serviceItems):
serviceitem = item[u'service_item'] serviceitem = item[u'service_item']
@ -600,6 +643,7 @@ class ServiceManager(QtGui.QWidget):
zip = None zip = None
file = None file = None
try: try:
write_list = []
zip = zipfile.ZipFile(unicode(filename), 'w') zip = zipfile.ZipFile(unicode(filename), 'w')
for item in self.serviceItems: for item in self.serviceItems:
service.append({u'serviceitem':item[u'service_item'] service.append({u'serviceitem':item[u'service_item']
@ -609,7 +653,10 @@ class ServiceManager(QtGui.QWidget):
path_from = unicode(os.path.join( path_from = unicode(os.path.join(
frame[u'path'], frame[u'path'],
frame[u'title'])) frame[u'title']))
zip.write(path_from.encode(u'utf-8')) # On write a file once
if not path_from in write_list:
write_list.append(path_from)
zip.write(path_from.encode(u'utf-8'))
file = open(servicefile, u'wb') file = open(servicefile, u'wb')
cPickle.dump(service, file) cPickle.dump(service, file)
file.close() file.close()
@ -711,6 +758,9 @@ class ServiceManager(QtGui.QWidget):
serviceitem.set_from_service(item, self.servicePath) serviceitem.set_from_service(item, self.servicePath)
self.validateItem(serviceitem) self.validateItem(serviceitem)
self.addServiceItem(serviceitem) self.addServiceItem(serviceitem)
if serviceitem.is_capable(ItemCapabilities.OnLoadUpdate):
Receiver.send_message(u'%s_service_load' %
serviceitem.name.lower(), serviceitem)
try: try:
if os.path.isfile(p_file): if os.path.isfile(p_file):
os.remove(p_file) os.remove(p_file)
@ -796,19 +846,48 @@ class ServiceManager(QtGui.QWidget):
self.isNew = True self.isNew = True
for item in tempServiceItems: for item in tempServiceItems:
self.addServiceItem( self.addServiceItem(
item[u'service_item'], False, item[u'expanded']) item[u'service_item'], False, expand=item[u'expanded'])
# Set to False as items may have changed rendering # Set to False as items may have changed rendering
# does not impact the saved song so True may also be valid # does not impact the saved song so True may also be valid
self.parent.serviceChanged(False, self.serviceName) self.parent.serviceChanged(False, self.serviceName)
def addServiceItem(self, item, rebuild=False, expand=True, replace=False): def serviceItemUpdate(self, message):
"""
Triggered from plugins to update service items.
"""
editId, uuid = message.split(u':')
for item in self.serviceItems:
if item[u'service_item']._uuid == uuid:
item[u'service_item'].editId = editId
def replaceServiceItem(self, newItem):
"""
Using the service item passed replace the one with the same edit id
if found.
"""
newItem.render()
for itemcount, item in enumerate(self.serviceItems):
if item[u'service_item'].editId == newItem.editId and \
item[u'service_item'].name == newItem.name:
newItem.merge(item[u'service_item'])
item[u'service_item'] = newItem
self.repaintServiceList(itemcount + 1, 0)
self.parent.LiveController.replaceServiceManagerItem(newItem)
self.parent.serviceChanged(False, self.serviceName)
def addServiceItem(self, item, rebuild=False, expand=None, replace=False):
""" """
Add a Service item to the list Add a Service item to the list
``item`` ``item``
Service Item to be added Service Item to be added
``expand``
Override the default expand settings. (Tristate)
""" """
log.debug(u'addServiceItem') log.debug(u'addServiceItem')
if expand == None:
expand = self.expandTabs
sitem = self.findServiceItem()[0] sitem = self.findServiceItem()[0]
item.render() item.render()
if replace: if replace:
@ -817,7 +896,7 @@ class ServiceManager(QtGui.QWidget):
self.repaintServiceList(sitem + 1, 0) self.repaintServiceList(sitem + 1, 0)
self.parent.LiveController.replaceServiceManagerItem(item) self.parent.LiveController.replaceServiceManagerItem(item)
else: else:
#nothing selected for dnd # nothing selected for dnd
if self.droppos == 0: if self.droppos == 0:
if isinstance(item, list): if isinstance(item, list):
for inditem in item: for inditem in item:
@ -834,7 +913,7 @@ class ServiceManager(QtGui.QWidget):
u'order': self.droppos, u'order': self.droppos,
u'expanded':expand}) u'expanded':expand})
self.repaintServiceList(self.droppos, 0) self.repaintServiceList(self.droppos, 0)
#if rebuilding list make sure live is fixed. # if rebuilding list make sure live is fixed.
if rebuild: if rebuild:
self.parent.LiveController.replaceServiceManagerItem(item) self.parent.LiveController.replaceServiceManagerItem(item)
self.droppos = 0 self.droppos = 0
@ -914,7 +993,7 @@ class ServiceManager(QtGui.QWidget):
else: else:
pos = parentitem.data(0, QtCore.Qt.UserRole).toInt()[0] pos = parentitem.data(0, QtCore.Qt.UserRole).toInt()[0]
count = item.data(0, QtCore.Qt.UserRole).toInt()[0] count = item.data(0, QtCore.Qt.UserRole).toInt()[0]
#adjust for zero based arrays # adjust for zero based arrays
pos = pos - 1 pos = pos - 1
return pos, count return pos, count
@ -940,7 +1019,7 @@ class ServiceManager(QtGui.QWidget):
if link.hasText(): if link.hasText():
plugin = event.mimeData().text() plugin = event.mimeData().text()
item = self.serviceManagerList.itemAt(event.pos()) item = self.serviceManagerList.itemAt(event.pos())
#ServiceManager started the drag and drop # ServiceManager started the drag and drop
if plugin == u'ServiceManager': if plugin == u'ServiceManager':
startpos, startCount = self.findServiceItem() startpos, startCount = self.findServiceItem()
if item is None: if item is None:
@ -952,22 +1031,22 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems.insert(endpos, serviceItem) self.serviceItems.insert(endpos, serviceItem)
self.repaintServiceList(endpos, startCount) self.repaintServiceList(endpos, startCount)
else: else:
#we are not over anything so drop # we are not over anything so drop
replace = False replace = False
if item is None: if item is None:
self.droppos = len(self.serviceItems) self.droppos = len(self.serviceItems)
else: else:
#we are over somthing so lets investigate # we are over somthing so lets investigate
pos = self._getParentItemData(item) - 1 pos = self._getParentItemData(item) - 1
serviceItem = self.serviceItems[pos] serviceItem = self.serviceItems[pos]
if (plugin == serviceItem[u'service_item'].name and if (plugin == serviceItem[u'service_item'].name and
serviceItem[u'service_item'].is_capable( serviceItem[u'service_item'].is_capable(
ItemCapabilities.AllowsAdditions)): ItemCapabilities.AllowsAdditions)):
action = self.dndMenu.exec_(QtGui.QCursor.pos()) action = self.dndMenu.exec_(QtGui.QCursor.pos())
#New action required # New action required
if action == self.newAction: if action == self.newAction:
self.droppos = self._getParentItemData(item) self.droppos = self._getParentItemData(item)
#Append to existing action # Append to existing action
if action == self.addToAction: if action == self.addToAction:
self.droppos = self._getParentItemData(item) self.droppos = self._getParentItemData(item)
item.setSelected(True) item.setSelected(True)

View File

@ -72,14 +72,15 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
self.settingsTabWidget.insertTab( self.settingsTabWidget.insertTab(
location + 14, tab, tab.tabTitleVisible) location + 14, tab, tab.tabTitleVisible)
def removeTab(self, name): def removeTab(self, tab):
""" """
Remove a tab from the form Remove a tab from the form
""" """
log.debug(u'remove %s tab' % name) log.debug(u'remove %s tab' % tab.tabTitleVisible)
for tabIndex in range(0, self.settingsTabWidget.count()): for tabIndex in range(0, self.settingsTabWidget.count()):
if self.settingsTabWidget.widget(tabIndex): if self.settingsTabWidget.widget(tabIndex):
if self.settingsTabWidget.widget(tabIndex).tabTitle == name: if self.settingsTabWidget.widget(tabIndex).tabTitleVisible == \
tab.tabTitleVisible:
self.settingsTabWidget.removeTab(tabIndex) self.settingsTabWidget.removeTab(tabIndex)
def accept(self): def accept(self):

View File

@ -179,19 +179,24 @@ class SlideController(QtGui.QWidget):
self.HideMenu.setMenu(QtGui.QMenu( self.HideMenu.setMenu(QtGui.QMenu(
translate('OpenLP.SlideController', 'Hide'), self.Toolbar)) translate('OpenLP.SlideController', 'Hide'), self.Toolbar))
self.BlankScreen = QtGui.QAction(QtGui.QIcon( self.BlankScreen = QtGui.QAction(QtGui.QIcon(
u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu) u':/slides/slide_blank.png'),
translate('OpenLP.SlideController',
'Blank Screen'), self.HideMenu)
self.BlankScreen.setCheckable(True) self.BlankScreen.setCheckable(True)
QtCore.QObject.connect(self.BlankScreen, QtCore.QObject.connect(self.BlankScreen,
QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay) QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay)
self.ThemeScreen = QtGui.QAction(QtGui.QIcon( self.ThemeScreen = QtGui.QAction(QtGui.QIcon(
u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu) u':/slides/slide_theme.png'),
translate('OpenLP.SlideController',
'Blank to Theme'), self.HideMenu)
self.ThemeScreen.setCheckable(True) self.ThemeScreen.setCheckable(True)
QtCore.QObject.connect(self.ThemeScreen, QtCore.QObject.connect(self.ThemeScreen,
QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay) QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay)
if self.screens.display_count > 1: if self.screens.display_count > 1:
self.DesktopScreen = QtGui.QAction(QtGui.QIcon( self.DesktopScreen = QtGui.QAction(QtGui.QIcon(
u':/slides/slide_desktop.png'), u'Show Desktop', u':/slides/slide_desktop.png'),
self.HideMenu) translate('OpenLP.SlideController',
'Show Desktop'), self.HideMenu)
self.DesktopScreen.setCheckable(True) self.DesktopScreen.setCheckable(True)
QtCore.QObject.connect(self.DesktopScreen, QtCore.QObject.connect(self.DesktopScreen,
QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay) QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay)
@ -538,7 +543,7 @@ class SlideController(QtGui.QWidget):
Receiver.send_message(u'%s_stop' % Receiver.send_message(u'%s_stop' %
self.serviceItem.name.lower(), [serviceItem, self.isLive]) self.serviceItem.name.lower(), [serviceItem, self.isLive])
if self.serviceItem.is_media(): if self.serviceItem.is_media():
self.onMediaStop() self.onMediaClose()
if self.isLive: if self.isLive:
blanked = self.BlankScreen.isChecked() blanked = self.BlankScreen.isChecked()
else: else:
@ -926,14 +931,13 @@ class SlideController(QtGui.QWidget):
Respond to the arrival of a media service item Respond to the arrival of a media service item
""" """
log.debug(u'SlideController onMediaStart') log.debug(u'SlideController onMediaStart')
file = os.path.join(item.get_frame_path(), item.get_frame_title())
if self.isLive: if self.isLive:
file = os.path.join(item.get_frame_path(), item.get_frame_title())
self.display.video(file, self.volume) self.display.video(file, self.volume)
self.volumeSlider.setValue(self.volume) self.volumeSlider.setValue(self.volume)
else: else:
self.mediaObject.stop() self.mediaObject.stop()
self.mediaObject.clearQueue() self.mediaObject.clearQueue()
file = os.path.join(item.get_frame_path(), item.get_frame_title())
self.mediaObject.setCurrentSource(Phonon.MediaSource(file)) self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
self.seekSlider.setMediaObject(self.mediaObject) self.seekSlider.setMediaObject(self.mediaObject)
self.seekSlider.show() self.seekSlider.show()
@ -981,3 +985,18 @@ class SlideController(QtGui.QWidget):
self.video.hide() self.video.hide()
self.SlidePreview.clear() self.SlidePreview.clear()
self.SlidePreview.show() self.SlidePreview.show()
def onMediaClose(self):
"""
Respond to a request to close the Video
"""
log.debug(u'SlideController onMediaStop')
if self.isLive:
self.display.resetVideo()
else:
self.mediaObject.stop()
self.mediaObject.clearQueue()
self.video.hide()
self.SlidePreview.clear()
self.SlidePreview.show()

View File

@ -28,7 +28,7 @@ import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, build_icon, translate from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.lib.db import Manager from openlp.core.lib.db import Manager
from openlp.plugins.alerts.lib import AlertsManager, AlertsTab from openlp.plugins.alerts.lib import AlertsManager, AlertsTab
from openlp.plugins.alerts.lib.db import init_schema from openlp.plugins.alerts.lib.db import init_schema
@ -101,3 +101,18 @@ class AlertsPlugin(Plugin):
'<br />The alert plugin controls the displaying of nursery alerts ' '<br />The alert plugin controls the displaying of nursery alerts '
'on the display screen') 'on the display screen')
return about_text return about_text
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
## Name PluginList ##
self.textStrings[StringContent.Name] = {
u'singular': translate('AlertsPlugin', 'Alert'),
u'plural': translate('AlertsPlugin', 'Alerts')
}
## Name for MediaDockManager, SettingsManager ##
self.textStrings[StringContent.VisibleName] = {
u'title': translate('AlertsPlugin', 'Alerts')
}

View File

@ -35,7 +35,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
""" """
Provide UI for the alert system Provide UI for the alert system
""" """
def __init__(self, plugin): def __init__(self, title, visible_title):
""" """
Initialise the alert form Initialise the alert form
""" """

View File

@ -32,14 +32,13 @@ class AlertsTab(SettingsTab):
""" """
AlertsTab is the alerts settings tab in the settings dialog. AlertsTab is the alerts settings tab in the settings dialog.
""" """
def __init__(self, parent): def __init__(self, parent, visible_title):
self.parent = parent self.parent = parent
self.manager = parent.manager self.manager = parent.manager
SettingsTab.__init__(self, parent.name) SettingsTab.__init__(self, parent.name, visible_title)
def setupUi(self): def setupUi(self):
self.setObjectName(u'AlertsTab') self.setObjectName(u'AlertsTab')
self.tabTitleVisible = translate('AlertsPlugin.AlertsTab', 'Alerts')
self.AlertsLayout = QtGui.QHBoxLayout(self) self.AlertsLayout = QtGui.QHBoxLayout(self)
self.AlertsLayout.setSpacing(8) self.AlertsLayout.setSpacing(8)
self.AlertsLayout.setMargin(8) self.AlertsLayout.setMargin(8)
@ -296,4 +295,3 @@ class AlertsTab(SettingsTab):
self.FontPreview.setFont(font) self.FontPreview.setFont(font)
self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' % self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' %
(self.bg_color, self.font_color)) (self.bg_color, self.font_color))

View File

@ -24,6 +24,6 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`bibles' module provides the Bible plugin to enable OpenLP to display The :mod:`bibles` module provides the Bible plugin to enable OpenLP to display
scripture. scripture.
""" """

View File

@ -28,7 +28,7 @@ import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, build_icon, translate from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -58,11 +58,12 @@ class BiblePlugin(Plugin):
self.exportBibleItem.setVisible(False) self.exportBibleItem.setVisible(False)
def getSettingsTab(self): def getSettingsTab(self):
return BiblesTab(self.name) visible_name = self.getString(StringContent.VisibleName)
return BiblesTab(self.name, visible_name[u'title'])
def getMediaManagerItem(self): def getMediaManagerItem(self):
# Create the BibleManagerItem object. # Create the BibleManagerItem object.
return BibleMediaItem(self, self.icon, self.name) return BibleMediaItem(self, self, self.icon)
def addImportMenuItem(self, import_menu): def addImportMenuItem(self, import_menu):
self.importBibleItem = QtGui.QAction(import_menu) self.importBibleItem = QtGui.QAction(import_menu)
@ -79,8 +80,7 @@ class BiblePlugin(Plugin):
self.exportBibleItem = QtGui.QAction(export_menu) self.exportBibleItem = QtGui.QAction(export_menu)
self.exportBibleItem.setObjectName(u'exportBibleItem') self.exportBibleItem.setObjectName(u'exportBibleItem')
export_menu.addAction(self.exportBibleItem) export_menu.addAction(self.exportBibleItem)
self.exportBibleItem.setText(translate( self.exportBibleItem.setText(translate('BiblesPlugin', '&Bible'))
'BiblesPlugin', '&Bible'))
self.exportBibleItem.setVisible(False) self.exportBibleItem.setVisible(False)
def onBibleImportClick(self): def onBibleImportClick(self):
@ -96,7 +96,6 @@ class BiblePlugin(Plugin):
def usesTheme(self, theme): def usesTheme(self, theme):
""" """
Called to find out if the bible plugin is currently using a theme. Called to find out if the bible plugin is currently using a theme.
Returns True if the theme is being used, otherwise returns False. Returns True if the theme is being used, otherwise returns False.
""" """
if self.settings_tab.bible_theme == theme: if self.settings_tab.bible_theme == theme:
@ -116,3 +115,60 @@ class BiblePlugin(Plugin):
The new name the plugin should now use. The new name the plugin should now use.
""" """
self.settings_tab.bible_theme = newTheme self.settings_tab.bible_theme = newTheme
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
## Name PluginList ##
self.textStrings[StringContent.Name] = {
u'singular': translate('BiblesPlugin', 'Bible'),
u'plural': translate('BiblesPlugin', 'Bibles')
}
## Name for MediaDockManager, SettingsManager ##
self.textStrings[StringContent.VisibleName] = {
u'title': translate('BiblesPlugin', 'Bibles')
}
# Middle Header Bar
## Import Button ##
self.textStrings[StringContent.Import] = {
u'title': translate('BiblesPlugin', 'Import'),
u'tooltip': translate('BiblesPlugin',
'Import a Bible')
}
## New Button ##
self.textStrings[StringContent.New] = {
u'title': translate('BiblesPlugin', 'Add'),
u'tooltip': translate('BiblesPlugin',
'Add a new Bible')
}
## Edit Button ##
self.textStrings[StringContent.Edit] = {
u'title': translate('BiblesPlugin', 'Edit'),
u'tooltip': translate('BiblesPlugin',
'Edit the selected Bible')
}
## Delete Button ##
self.textStrings[StringContent.Delete] = {
u'title': translate('BiblesPlugin', 'Delete'),
u'tooltip': translate('BiblesPlugin',
'Delete the selected Bible')
}
## Preview ##
self.textStrings[StringContent.Preview] = {
u'title': translate('BiblesPlugin', 'Preview'),
u'tooltip': translate('BiblesPlugin',
'Preview the selected Bible')
}
## Live Button ##
self.textStrings[StringContent.Live] = {
u'title': translate('BiblesPlugin', 'Live'),
u'tooltip': translate('BiblesPlugin',
'Send the selected Bible live')
}
## Add to service Button ##
self.textStrings[StringContent.Service] = {
u'title': translate('BiblesPlugin', 'Service'),
u'tooltip': translate('BiblesPlugin',
'Add the selected Bible to the service')
}

View File

@ -275,14 +275,14 @@ class Ui_BibleImportWizard(object):
self.CopyrightEdit.setObjectName(u'CopyrightEdit') self.CopyrightEdit.setObjectName(u'CopyrightEdit')
self.LicenseDetailsLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.LicenseDetailsLayout.setWidget(1, QtGui.QFormLayout.FieldRole,
self.CopyrightEdit) self.CopyrightEdit)
self.PermissionLabel = QtGui.QLabel(self.LicenseDetailsPage) self.PermissionsLabel = QtGui.QLabel(self.LicenseDetailsPage)
self.PermissionLabel.setObjectName(u'PermissionLabel') self.PermissionsLabel.setObjectName(u'PermissionsLabel')
self.LicenseDetailsLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.LicenseDetailsLayout.setWidget(2, QtGui.QFormLayout.LabelRole,
self.PermissionLabel) self.PermissionsLabel)
self.PermissionEdit = QtGui.QLineEdit(self.LicenseDetailsPage) self.PermissionsEdit = QtGui.QLineEdit(self.LicenseDetailsPage)
self.PermissionEdit.setObjectName(u'PermissionEdit') self.PermissionsEdit.setObjectName(u'PermissionsEdit')
self.LicenseDetailsLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.LicenseDetailsLayout.setWidget(2, QtGui.QFormLayout.FieldRole,
self.PermissionEdit) self.PermissionsEdit)
BibleImportWizard.addPage(self.LicenseDetailsPage) BibleImportWizard.addPage(self.LicenseDetailsPage)
self.ImportPage = QtGui.QWizardPage() self.ImportPage = QtGui.QWizardPage()
self.ImportPage.setObjectName(u'ImportPage') self.ImportPage.setObjectName(u'ImportPage')
@ -372,8 +372,8 @@ class Ui_BibleImportWizard(object):
translate('BiblesPlugin.ImportWizardForm', 'Version name:')) translate('BiblesPlugin.ImportWizardForm', 'Version name:'))
self.CopyrightLabel.setText( self.CopyrightLabel.setText(
translate('BiblesPlugin.ImportWizardForm', 'Copyright:')) translate('BiblesPlugin.ImportWizardForm', 'Copyright:'))
self.PermissionLabel.setText( self.PermissionsLabel.setText(
translate('BiblesPlugin.ImportWizardForm', 'Permission:')) translate('BiblesPlugin.ImportWizardForm', 'Permissions:'))
self.ImportPage.setTitle( self.ImportPage.setTitle(
translate('BiblesPlugin.ImportWizardForm', 'Importing')) translate('BiblesPlugin.ImportWizardForm', 'Importing'))
self.ImportPage.setSubTitle( self.ImportPage.setSubTitle(

View File

@ -282,7 +282,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
self.LicenseDetailsPage.registerField( self.LicenseDetailsPage.registerField(
u'license_copyright', self.CopyrightEdit) u'license_copyright', self.CopyrightEdit)
self.LicenseDetailsPage.registerField( self.LicenseDetailsPage.registerField(
u'license_permission', self.PermissionEdit) u'license_permissions', self.PermissionsEdit)
def setDefaults(self): def setDefaults(self):
settings = QtCore.QSettings() settings = QtCore.QSettings()
@ -308,8 +308,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
QtCore.QVariant(self.VersionNameEdit.text())) QtCore.QVariant(self.VersionNameEdit.text()))
self.setField(u'license_copyright', self.setField(u'license_copyright',
QtCore.QVariant(self.CopyrightEdit.text())) QtCore.QVariant(self.CopyrightEdit.text()))
self.setField(u'license_permission', self.setField(u'license_permissions',
QtCore.QVariant(self.PermissionEdit.text())) QtCore.QVariant(self.PermissionsEdit.text()))
self.onLocationComboBoxChanged(WebDownload.Crosswalk) self.onLocationComboBoxChanged(WebDownload.Crosswalk)
settings.endGroup() settings.endGroup()
@ -391,8 +391,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
bible_type = self.field(u'source_format').toInt()[0] bible_type = self.field(u'source_format').toInt()[0]
license_version = unicode(self.field(u'license_version').toString()) license_version = unicode(self.field(u'license_version').toString())
license_copyright = unicode(self.field(u'license_copyright').toString()) license_copyright = unicode(self.field(u'license_copyright').toString())
license_permission = \ license_permissions = \
unicode(self.field(u'license_permission').toString()) unicode(self.field(u'license_permissions').toString())
importer = None importer = None
if bible_type == BibleFormat.OSIS: if bible_type == BibleFormat.OSIS:
# Import an OSIS bible # Import an OSIS bible
@ -436,7 +436,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
) )
if importer.do_import(): if importer.do_import():
self.manager.save_meta_data(license_version, license_version, self.manager.save_meta_data(license_version, license_version,
license_copyright, license_permission) license_copyright, license_permissions)
self.manager.reload_bibles() self.manager.reload_bibles()
self.ImportProgressLabel.setText( self.ImportProgressLabel.setText(
translate('BiblesPlugin.ImportWizardForm', 'Finished import.')) translate('BiblesPlugin.ImportWizardForm', 'Finished import.'))

View File

@ -60,22 +60,32 @@ def parse_reference(reference):
and converts it to a reference list, a list of references to be queried and converts it to a reference list, a list of references to be queried
from the Bible database files. from the Bible database files.
BIBLE_REFERENCE regular expression produces the following match groups: The ``BIBLE_REFERENCE`` constant regular expression produces the following
0 This is a special group consisting of the whole string that matched. match groups:
1 [\w ]+ The book the reference is from.
2 [0-9]+ The first (or only) chapter in the reference. 0. (match string)
3 None|[0-9]+ None or the only verse or the first verse in a This is a special group consisting of the whole string that matched.
verse range or the start verse in a chapter range. 1. ``[\w ]+``
4 None|[0-9]+|end None or the end verse of the first verse range or The book the reference is from.
the end chapter of a chapter range. 2. ``[0-9]+``
5 None|[0-9]+ None or the second chapter in multiple The first (or only) chapter in the reference.
(non-ranged) chapters. 3. ``None`` or ``[0-9]+``
6 None|[0-9]+|end None, the start of the second verse range or the ``None``, or the only verse, or the first verse in a verse range or,
end of a chapter range. the start verse in a chapter range.
7 None|[0-9]+|end None or the end of the second verse range. 4. ``None`` or ``[0-9]+`` or ``end``
``None``, or the end verse of the first verse range, or the end chapter
of a chapter range.
5. ``None`` or ``[0-9]+``
``None``, or the second chapter in multiple (non-ranged) chapters.
6. ``None`` or ``[0-9]+`` or ``end``
``None``, the start of the second verse range. or the end of a chapter
range.
7. ``None`` or ``[0-9]+`` or ``end``
``None``, or the end of the second verse range.
The reference list is a list of tuples, with each tuple structured like The reference list is a list of tuples, with each tuple structured like
this:: this::
(book, chapter, start_verse, end_verse) (book, chapter, start_verse, end_verse)
``reference`` ``reference``
@ -154,7 +164,7 @@ def parse_reference(reference):
class SearchResults(object): class SearchResults(object):
""" """
Encapsulate a set of search results. This is Bible-type independant. Encapsulate a set of search results. This is Bible-type independent.
""" """
def __init__(self, book, chapter, verselist): def __init__(self, book, chapter, verselist):
""" """

View File

@ -38,15 +38,14 @@ class BiblesTab(SettingsTab):
""" """
log.info(u'Bible Tab loaded') log.info(u'Bible Tab loaded')
def __init__(self, title): def __init__(self, title, visible_title):
self.paragraph_style = True self.paragraph_style = True
self.show_new_chapters = False self.show_new_chapters = False
self.display_style = 0 self.display_style = 0
SettingsTab.__init__(self, title) SettingsTab.__init__(self, title, visible_title)
def setupUi(self): def setupUi(self):
self.setObjectName(u'BiblesTab') self.setObjectName(u'BiblesTab')
self.tabTitleVisible = translate('BiblesPlugin.BiblesTab', 'Bibles')
self.BibleLayout = QtGui.QHBoxLayout(self) self.BibleLayout = QtGui.QHBoxLayout(self)
self.BibleLayout.setSpacing(8) self.BibleLayout.setSpacing(8)
self.BibleLayout.setMargin(8) self.BibleLayout.setMargin(8)

View File

@ -64,10 +64,10 @@ class Verse(BaseModel):
def init_schema(url): def init_schema(url):
""" """
Setup a bible database connection and initialise the database schema Setup a bible database connection and initialise the database schema.
``url`` ``url``
The database to setup The database to setup.
""" """
session, metadata = init_db(url) session, metadata = init_db(url)
@ -240,7 +240,7 @@ class BibleDB(QtCore.QObject, Manager):
and the value is the verse text. and the value is the verse text.
""" """
log.debug(u'create_chapter %s,%s', book_id, chapter) log.debug(u'create_chapter %s,%s', book_id, chapter)
#text list has book and chapter as first two elements of the array # text list has book and chapter as first two elements of the array
for verse_number, verse_text in textlist.iteritems(): for verse_number, verse_text in textlist.iteritems():
verse = Verse.populate( verse = Verse.populate(
book_id = book_id, book_id = book_id,
@ -281,23 +281,23 @@ class BibleDB(QtCore.QObject, Manager):
def create_meta(self, key, value): def create_meta(self, key, value):
""" """
Utility method to save BibleMeta objects in a Bible database Utility method to save BibleMeta objects in a Bible database.
``key`` ``key``
The key for this instance The key for this instance.
``value`` ``value``
The value for this instance The value for this instance.
""" """
log.debug(u'save_meta %s/%s', key, value) log.debug(u'save_meta %s/%s', key, value)
self.save_object(BibleMeta.populate(key=key, value=value)) self.save_object(BibleMeta.populate(key=key, value=value))
def get_book(self, book): def get_book(self, book):
""" """
Return a book object from the database Return a book object from the database.
``book`` ``book``
The name of the book to return The name of the book to return.
""" """
log.debug(u'BibleDb.get_book("%s")', book) log.debug(u'BibleDb.get_book("%s")', book)
db_book = self.get_object_filtered(Book, Book.name.like(book + u'%')) db_book = self.get_object_filtered(Book, Book.name.like(book + u'%'))
@ -353,9 +353,9 @@ class BibleDB(QtCore.QObject, Manager):
QtGui.QMessageBox.information(self.bible_plugin.mediaItem, QtGui.QMessageBox.information(self.bible_plugin.mediaItem,
translate('BiblesPlugin.BibleDB', 'Book not found'), translate('BiblesPlugin.BibleDB', 'Book not found'),
translate('BiblesPlugin.BibleDB', 'The book you requested ' translate('BiblesPlugin.BibleDB', 'The book you requested '
'could not be found in this bible. Please check your ' 'could not be found in this Bible. Please check your '
'spelling and that this is a complete bible not just ' 'spelling and that this is a complete Bible not just '
'one testament.')) 'one testament.'))
return verse_list return verse_list
def verse_search(self, text): def verse_search(self, text):
@ -387,10 +387,10 @@ class BibleDB(QtCore.QObject, Manager):
def get_chapter_count(self, book): def get_chapter_count(self, book):
""" """
Return the number of chapters in a book Return the number of chapters in a book.
``book`` ``book``
The book to get the chapter count for The book to get the chapter count for.
""" """
log.debug(u'BibleDB.get_chapter_count("%s")', book) log.debug(u'BibleDB.get_chapter_count("%s")', book)
count = self.session.query(Verse.chapter).join(Book)\ count = self.session.query(Verse.chapter).join(Book)\
@ -403,13 +403,13 @@ class BibleDB(QtCore.QObject, Manager):
def get_verse_count(self, book, chapter): def get_verse_count(self, book, chapter):
""" """
Return the number of verses in a chapter Return the number of verses in a chapter.
``book`` ``book``
The book containing the chapter The book containing the chapter.
``chapter`` ``chapter``
The chapter to get the verse count for The chapter to get the verse count for.
""" """
log.debug(u'BibleDB.get_verse_count("%s", %s)', book, chapter) log.debug(u'BibleDB.get_verse_count("%s", %s)', book, chapter)
count = self.session.query(Verse).join(Book)\ count = self.session.query(Verse).join(Book)\
@ -423,7 +423,7 @@ class BibleDB(QtCore.QObject, Manager):
def dump_bible(self): def dump_bible(self):
""" """
Utility debugging method to dump the contents of a bible Utility debugging method to dump the contents of a bible.
""" """
log.debug(u'.........Dumping Bible Database') log.debug(u'.........Dumping Bible Database')
log.debug('...............................Books ') log.debug('...............................Books ')

View File

@ -187,16 +187,16 @@ class BGExtract(object):
def get_bible_chapter(self, version, bookname, chapter): def get_bible_chapter(self, version, bookname, chapter):
""" """
Access and decode bibles via the BibleGateway website Access and decode bibles via the BibleGateway website.
``version`` ``version``
The version of the bible like 31 for New International version The version of the bible like 31 for New International version.
``bookname`` ``bookname``
Name of the Book Name of the Book.
``chapter`` ``chapter``
Chapter number Chapter number.
""" """
log.debug(u'get_bible_chapter %s, %s, %s', version, bookname, chapter) log.debug(u'get_bible_chapter %s, %s, %s', version, bookname, chapter)
url_params = urllib.urlencode( url_params = urllib.urlencode(
@ -298,13 +298,13 @@ class CWExtract(object):
versetext = versetext + part versetext = versetext + part
elif part and part.attrMap and \ elif part and part.attrMap and \
(part.attrMap[u'class'] == u'WordsOfChrist' or \ (part.attrMap[u'class'] == u'WordsOfChrist' or \
part.attrMap[u'class'] == u'strongs'): part.attrMap[u'class'] == u'strongs'):
for subpart in part.contents: for subpart in part.contents:
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
if isinstance(subpart, NavigableString): if isinstance(subpart, NavigableString):
versetext = versetext + subpart versetext = versetext + subpart
elif subpart and subpart.attrMap and \ elif subpart and subpart.attrMap and \
subpart.attrMap[u'class'] == u'strongs': subpart.attrMap[u'class'] == u'strongs':
for subsub in subpart.contents: for subsub in subpart.contents:
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
if isinstance(subsub, NavigableString): if isinstance(subsub, NavigableString):
@ -428,7 +428,7 @@ class HTTPBible(BibleDB):
def get_chapter(self, book, chapter): def get_chapter(self, book, chapter):
""" """
Receive the request and call the relevant handler methods Receive the request and call the relevant handler methods.
""" """
log.debug(u'get_chapter %s, %s', book, chapter) log.debug(u'get_chapter %s, %s', book, chapter)
log.debug(u'source = %s', self.download_source) log.debug(u'source = %s', self.download_source)

View File

@ -137,7 +137,7 @@ class BibleManager(object):
name = bible.get_name() name = bible.get_name()
log.debug(u'Bible Name: "%s"', name) log.debug(u'Bible Name: "%s"', name)
self.db_cache[name] = bible self.db_cache[name] = bible
# look to see if lazy load bible exists and get create getter. # Look to see if lazy load bible exists and get create getter.
source = self.db_cache[name].get_object(BibleMeta, source = self.db_cache[name].get_object(BibleMeta,
u'download source') u'download source')
if source: if source:
@ -181,10 +181,10 @@ class BibleManager(object):
def get_bibles(self): def get_bibles(self):
""" """
Returns a list of the names of available Bibles. Returns a dict with all available Bibles.
""" """
log.debug(u'get_bibles') log.debug(u'get_bibles')
return self.db_cache.keys() return self.db_cache
def get_books(self, bible): def get_books(self, bible):
""" """
@ -204,7 +204,7 @@ class BibleManager(object):
def get_chapter_count(self, bible, book): def get_chapter_count(self, bible, book):
""" """
Returns the number of Chapters for a given book Returns the number of Chapters for a given book.
""" """
log.debug(u'get_book_chapter_count %s', book) log.debug(u'get_book_chapter_count %s', book)
return self.db_cache[bible].get_chapter_count(book) return self.db_cache[bible].get_chapter_count(book)
@ -212,7 +212,7 @@ class BibleManager(object):
def get_verse_count(self, bible, book, chapter): def get_verse_count(self, bible, book, chapter):
""" """
Returns all the number of verses for a given Returns all the number of verses for a given
book and chapterMaxBibleBookVerses book and chapterMaxBibleBookVerses.
""" """
log.debug(u'BibleManager.get_verse_count("%s", "%s", %s)', log.debug(u'BibleManager.get_verse_count("%s", "%s", %s)',
bible, book, chapter) bible, book, chapter)
@ -254,12 +254,35 @@ class BibleManager(object):
'Book Chapter:Verse-Verse\n' 'Book Chapter:Verse-Verse\n'
'Book Chapter:Verse-Verse,Verse-Verse\n' 'Book Chapter:Verse-Verse,Verse-Verse\n'
'Book Chapter:Verse-Verse,Chapter:Verse-Verse\n' 'Book Chapter:Verse-Verse,Chapter:Verse-Verse\n'
'Book Chapter:Verse-Chapter:Verse\n')) 'Book Chapter:Verse-Chapter:Verse'))
return None
def verse_search(self, bible, text):
"""
Does a verse search for the given bible and text.
``bible``
The bible to seach in (unicode).
``text``
The text to search for (unicode).
"""
log.debug(u'BibleManager.verse_search("%s", "%s")', bible, text)
if text:
return self.db_cache[bible].verse_search(text)
else:
QtGui.QMessageBox.information(self.parent.mediaItem,
translate('BiblesPlugin.BibleManager',
'Scripture Reference Error'),
translate('BiblesPlugin.BibleManager', 'You did not enter a '
'search keyword.\nYou can separate different keywords by a '
'space to search for all of your keywords and you can seperate '
'them by a comma to search for one of them.'))
return None return None
def save_meta_data(self, bible, version, copyright, permissions): def save_meta_data(self, bible, version, copyright, permissions):
""" """
Saves the bibles meta data Saves the bibles meta data.
""" """
log.debug(u'save_meta data %s,%s, %s,%s', log.debug(u'save_meta data %s,%s, %s,%s',
bible, version, copyright, permissions) bible, version, copyright, permissions)
@ -269,14 +292,14 @@ class BibleManager(object):
def get_meta_data(self, bible, key): def get_meta_data(self, bible, key):
""" """
Returns the meta data for a given key Returns the meta data for a given key.
""" """
log.debug(u'get_meta %s,%s', bible, key) log.debug(u'get_meta %s,%s', bible, key)
return self.db_cache[bible].get_object(BibleMeta, key) return self.db_cache[bible].get_object(BibleMeta, key)
def exists(self, name): def exists(self, name):
""" """
Check cache to see if new bible Check cache to see if new bible.
""" """
if not isinstance(name, unicode): if not isinstance(name, unicode):
name = unicode(name) name = unicode(name)

View File

@ -32,6 +32,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, Receiver, BaseListWithDnD, \ from openlp.core.lib import MediaManagerItem, Receiver, BaseListWithDnD, \
ItemCapabilities, translate ItemCapabilities, translate
from openlp.plugins.bibles.forms import ImportWizardForm from openlp.plugins.bibles.forms import ImportWizardForm
from openlp.plugins.bibles.lib.db import BibleDB
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -374,7 +375,7 @@ class BibleMediaItem(MediaManagerItem):
self.AdvancedSecondBibleComboBox.clear() self.AdvancedSecondBibleComboBox.clear()
self.QuickSecondBibleComboBox.addItem(u'') self.QuickSecondBibleComboBox.addItem(u'')
self.AdvancedSecondBibleComboBox.addItem(u'') self.AdvancedSecondBibleComboBox.addItem(u'')
bibles = self.parent.manager.get_bibles() bibles = self.parent.manager.get_bibles().keys()
# load bibles into the combo boxes # load bibles into the combo boxes
first = True first = True
for bible in bibles: for bible in bibles:
@ -407,21 +408,21 @@ class BibleMediaItem(MediaManagerItem):
self.initialiseChapterVerse(bible, book[u'name'], self.initialiseChapterVerse(bible, book[u'name'],
book[u'chapters']) book[u'chapters'])
def initialiseChapterVerse(self, bible, book, chapters): def initialiseChapterVerse(self, bible, book, chapter_count):
log.debug(u'initialiseChapterVerse %s, %s', bible, book) log.debug(u'initialiseChapterVerse %s, %s', bible, book)
self.chapters_from = chapters self.chapter_count = chapter_count
self.verses = self.parent.manager.get_verse_count(bible, book, 1) verse_count = self.parent.manager.get_verse_count(bible, book, 1)
if self.verses == 0: if verse_count == 0:
self.AdvancedSearchButton.setEnabled(False) self.AdvancedSearchButton.setEnabled(False)
self.AdvancedMessage.setText( self.AdvancedMessage.setText(
translate('BiblesPlugin.MediaItem', 'Bible not fully loaded.')) translate('BiblesPlugin.MediaItem', 'Bible not fully loaded.'))
else: else:
self.AdvancedSearchButton.setEnabled(True) self.AdvancedSearchButton.setEnabled(True)
self.AdvancedMessage.setText(u'') self.AdvancedMessage.setText(u'')
self.adjustComboBox(1, self.chapters_from, self.AdvancedFromChapter) self.adjustComboBox(1, self.chapter_count, self.AdvancedFromChapter)
self.adjustComboBox(1, self.chapters_from, self.AdvancedToChapter) self.adjustComboBox(1, self.chapter_count, self.AdvancedToChapter)
self.adjustComboBox(1, self.verses, self.AdvancedFromVerse) self.adjustComboBox(1, verse_count, self.AdvancedFromVerse)
self.adjustComboBox(1, self.verses, self.AdvancedToVerse) self.adjustComboBox(1, verse_count, self.AdvancedToVerse)
def onAdvancedVersionComboBox(self): def onAdvancedVersionComboBox(self):
self.initialiseBible( self.initialiseBible(
@ -435,47 +436,69 @@ class BibleMediaItem(MediaManagerItem):
self.AdvancedBookComboBox.itemData(item).toInt()[0]) self.AdvancedBookComboBox.itemData(item).toInt()[0])
def onAdvancedFromVerse(self): def onAdvancedFromVerse(self):
frm = int(self.AdvancedFromVerse.currentText()) chapter_from = int(self.AdvancedFromChapter.currentText())
chapter_frm = int(self.AdvancedFromChapter.currentText())
chapter_to = int(self.AdvancedToChapter.currentText()) chapter_to = int(self.AdvancedToChapter.currentText())
if chapter_frm == chapter_to: if chapter_from == chapter_to:
bible = unicode(self.AdvancedVersionComboBox.currentText()) bible = unicode(self.AdvancedVersionComboBox.currentText())
book = unicode(self.AdvancedBookComboBox.currentText()) book = unicode(self.AdvancedBookComboBox.currentText())
verses = self.parent.manager.get_verse_count(bible, book, chapter_to) verse_from = int(self.AdvancedFromVerse.currentText())
self.adjustComboBox(frm, verses, self.AdvancedToVerse) verse_count = self.parent.manager.get_verse_count(bible, book,
chapter_to)
self.adjustComboBox(verse_from, verse_count,
self.AdvancedToVerse, True)
def onAdvancedToChapter(self): def onAdvancedToChapter(self):
chapter_frm = int(self.AdvancedFromChapter.currentText())
chapter_to = int(self.AdvancedToChapter.currentText())
bible = unicode(self.AdvancedVersionComboBox.currentText()) bible = unicode(self.AdvancedVersionComboBox.currentText())
book = unicode(self.AdvancedBookComboBox.currentText()) book = unicode(self.AdvancedBookComboBox.currentText())
verses = self.parent.manager.get_verse_count(bible, book, chapter_to) chapter_from = int(self.AdvancedFromChapter.currentText())
if chapter_frm != chapter_to: chapter_to = int(self.AdvancedToChapter.currentText())
self.adjustComboBox(1, verses, self.AdvancedToVerse) verse_from = int(self.AdvancedFromVerse.currentText())
verse_to = int(self.AdvancedToVerse.currentText())
verse_count = self.parent.manager.get_verse_count(bible, book,
chapter_to)
if chapter_from == chapter_to and verse_from > verse_to:
self.adjustComboBox(verse_from, verse_count, self.AdvancedToVerse)
else: else:
frm = int(self.AdvancedFromVerse.currentText()) self.adjustComboBox(1, verse_count, self.AdvancedToVerse)
to = int(self.AdvancedToVerse.currentText())
if to < frm:
self.adjustComboBox(frm, verses, self.AdvancedToVerse)
def onAdvancedFromChapter(self): def onAdvancedFromChapter(self):
bible = unicode(self.AdvancedVersionComboBox.currentText()) bible = unicode(self.AdvancedVersionComboBox.currentText())
book = unicode(self.AdvancedBookComboBox.currentText()) book = unicode(self.AdvancedBookComboBox.currentText())
chapter_frm = int(self.AdvancedFromChapter.currentText()) chapter_from = int(self.AdvancedFromChapter.currentText())
self.adjustComboBox(chapter_frm, self.chapters_from, chapter_to = int(self.AdvancedToChapter.currentText())
self.AdvancedToChapter) verse_count = self.parent.manager.get_verse_count(bible, book,
verse = self.parent.manager.get_verse_count(bible, book, chapter_frm) chapter_from)
self.adjustComboBox(1, verse, self.AdvancedToVerse) self.adjustComboBox(1, verse_count, self.AdvancedFromVerse)
self.adjustComboBox(1, verse, self.AdvancedFromVerse) if chapter_from > chapter_to:
self.adjustComboBox(1, verse_count, self.AdvancedToVerse)
self.adjustComboBox(chapter_from, self.chapter_count,
self.AdvancedToChapter)
elif chapter_from == chapter_to:
self.adjustComboBox(chapter_from, self.chapter_count,
self.AdvancedToChapter)
self.adjustComboBox(1, verse_count, self.AdvancedToVerse, True)
else:
self.adjustComboBox(chapter_from, self.chapter_count,
self.AdvancedToChapter, True)
def adjustComboBox(self, range_from, range_to, combo): def adjustComboBox(self, range_from, range_to, combo, restore=False):
"""
``restore``
If True, then the combo's currentText will be restored after
adjusting (if possible).
"""
log.debug(u'adjustComboBox %s, %s, %s', combo, range_from, range_to) log.debug(u'adjustComboBox %s, %s, %s', combo, range_from, range_to)
if restore:
old_text = unicode(combo.currentText())
combo.clear() combo.clear()
for i in range(int(range_from), int(range_to) + 1): for i in range(int(range_from), int(range_to) + 1):
combo.addItem(unicode(i)) combo.addItem(unicode(i))
if restore and combo.findText(old_text) != -1:
combo.setCurrentIndex(combo.findText(old_text))
def onAdvancedSearchButton(self): def onAdvancedSearchButton(self):
log.debug(u'Advanced Search Button pressed') log.debug(u'Advanced Search Button pressed')
self.AdvancedSearchButton.setEnabled(False)
bible = unicode(self.AdvancedVersionComboBox.currentText()) bible = unicode(self.AdvancedVersionComboBox.currentText())
dual_bible = unicode(self.AdvancedSecondBibleComboBox.currentText()) dual_bible = unicode(self.AdvancedSecondBibleComboBox.currentText())
book = unicode(self.AdvancedBookComboBox.currentText()) book = unicode(self.AdvancedBookComboBox.currentText())
@ -508,16 +531,30 @@ class BibleMediaItem(MediaManagerItem):
self.displayResults(bible, dual_bible) self.displayResults(bible, dual_bible)
else: else:
self.displayResults(bible, dual_bible) self.displayResults(bible, dual_bible)
self.AdvancedSearchButton.setEnabled(True)
def onQuickSearchButton(self): def onQuickSearchButton(self):
log.debug(u'Quick Search Button pressed') log.debug(u'Quick Search Button pressed')
self.QuickSearchButton.setEnabled(False)
bible = unicode(self.QuickVersionComboBox.currentText()) bible = unicode(self.QuickVersionComboBox.currentText())
dual_bible = unicode(self.QuickSecondBibleComboBox.currentText()) dual_bible = unicode(self.QuickSecondBibleComboBox.currentText())
text = unicode(self.QuickSearchEdit.text()) text = unicode(self.QuickSearchEdit.text())
self.search_results = self.parent.manager.get_verses(bible, text) if self.QuickSearchComboBox.currentIndex() == 0:
if dual_bible: # We are doing a 'Verse Search'.
self.dual_search_results = self.parent.manager.get_verses( self.search_results = self.parent.manager.get_verses(bible, text)
dual_bible, text) if dual_bible and self.search_results:
self.dual_search_results = self.parent.manager.get_verses(
dual_bible, text)
else:
# We are doing a ' Text Search'.
bibles = self.parent.manager.get_bibles()
self.search_results = self.parent.manager.verse_search(bible, text)
if dual_bible and self.search_results:
text = []
for verse in self.search_results:
text.append((verse.book.name, verse.chapter, verse.verse,
verse.verse))
self.dual_search_results = bibles[dual_bible].get_verses(text)
if self.ClearQuickSearchComboBox.currentIndex() == 0: if self.ClearQuickSearchComboBox.currentIndex() == 0:
self.listView.clear() self.listView.clear()
if self.listView.count() != 0 and self.search_results: if self.listView.count() != 0 and self.search_results:
@ -537,6 +574,7 @@ class BibleMediaItem(MediaManagerItem):
self.displayResults(bible, dual_bible) self.displayResults(bible, dual_bible)
elif self.search_results: elif self.search_results:
self.displayResults(bible, dual_bible) self.displayResults(bible, dual_bible)
self.QuickSearchButton.setEnabled(True)
def displayResults(self, bible, dual_bible=u''): def displayResults(self, bible, dual_bible=u''):
""" """
@ -545,16 +583,16 @@ class BibleMediaItem(MediaManagerItem):
""" """
version = self.parent.manager.get_meta_data(bible, u'Version') version = self.parent.manager.get_meta_data(bible, u'Version')
copyright = self.parent.manager.get_meta_data(bible, u'Copyright') copyright = self.parent.manager.get_meta_data(bible, u'Copyright')
permission = self.parent.manager.get_meta_data(bible, u'Permissions') permissions = self.parent.manager.get_meta_data(bible, u'Permissions')
if dual_bible: if dual_bible:
dual_version = self.parent.manager.get_meta_data(dual_bible, dual_version = self.parent.manager.get_meta_data(dual_bible,
u'Version') u'Version')
dual_copyright = self.parent.manager.get_meta_data(dual_bible, dual_copyright = self.parent.manager.get_meta_data(dual_bible,
u'Copyright') u'Copyright')
dual_permission = self.parent.manager.get_meta_data(dual_bible, dual_permissions = self.parent.manager.get_meta_data(dual_bible,
u'Permissions') u'Permissions')
if not dual_permission: if not dual_permissions:
dual_permission = u'' dual_permissions = u''
# We count the number of rows which are maybe already present. # We count the number of rows which are maybe already present.
start_count = self.listView.count() start_count = self.listView.count()
for count, verse in enumerate(self.search_results): for count, verse in enumerate(self.search_results):
@ -566,12 +604,12 @@ class BibleMediaItem(MediaManagerItem):
'bible': QtCore.QVariant(bible), 'bible': QtCore.QVariant(bible),
'version': QtCore.QVariant(version.value), 'version': QtCore.QVariant(version.value),
'copyright': QtCore.QVariant(copyright.value), 'copyright': QtCore.QVariant(copyright.value),
'permission': QtCore.QVariant(permission.value), 'permissions': QtCore.QVariant(permissions.value),
'text': QtCore.QVariant(verse.text), 'text': QtCore.QVariant(verse.text),
'dual_bible': QtCore.QVariant(dual_bible), 'dual_bible': QtCore.QVariant(dual_bible),
'dual_version': QtCore.QVariant(dual_version.value), 'dual_version': QtCore.QVariant(dual_version.value),
'dual_copyright': QtCore.QVariant(dual_copyright.value), 'dual_copyright': QtCore.QVariant(dual_copyright.value),
'dual_permission': QtCore.QVariant(dual_permission.value), 'dual_permissions': QtCore.QVariant(dual_permissions.value),
'dual_text': QtCore.QVariant( 'dual_text': QtCore.QVariant(
self.dual_search_results[count].text) self.dual_search_results[count].text)
} }
@ -586,12 +624,12 @@ class BibleMediaItem(MediaManagerItem):
'bible': QtCore.QVariant(bible), 'bible': QtCore.QVariant(bible),
'version': QtCore.QVariant(version.value), 'version': QtCore.QVariant(version.value),
'copyright': QtCore.QVariant(copyright.value), 'copyright': QtCore.QVariant(copyright.value),
'permission': QtCore.QVariant(permission.value), 'permissions': QtCore.QVariant(permissions.value),
'text': QtCore.QVariant(verse.text), 'text': QtCore.QVariant(verse.text),
'dual_bible': QtCore.QVariant(u''), 'dual_bible': QtCore.QVariant(u''),
'dual_version': QtCore.QVariant(u''), 'dual_version': QtCore.QVariant(u''),
'dual_copyright': QtCore.QVariant(u''), 'dual_copyright': QtCore.QVariant(u''),
'dual_permission': QtCore.QVariant(u''), 'dual_permissions': QtCore.QVariant(u''),
'dual_text': QtCore.QVariant(u'') 'dual_text': QtCore.QVariant(u'')
} }
bible_text = u' %s %d:%d (%s)' % (verse.book.name, bible_text = u' %s %d:%d (%s)' % (verse.book.name,
@ -637,20 +675,20 @@ class BibleMediaItem(MediaManagerItem):
bible = self._decodeQtObject(bitem, 'bible') bible = self._decodeQtObject(bitem, 'bible')
version = self._decodeQtObject(bitem, 'version') version = self._decodeQtObject(bitem, 'version')
copyright = self._decodeQtObject(bitem, 'copyright') copyright = self._decodeQtObject(bitem, 'copyright')
permission = self._decodeQtObject(bitem, 'permission') permissions = self._decodeQtObject(bitem, 'permissions')
text = self._decodeQtObject(bitem, 'text') text = self._decodeQtObject(bitem, 'text')
dual_bible = self._decodeQtObject(bitem, 'dual_bible') dual_bible = self._decodeQtObject(bitem, 'dual_bible')
dual_version = self._decodeQtObject(bitem, 'dual_version') dual_version = self._decodeQtObject(bitem, 'dual_version')
dual_copyright = self._decodeQtObject(bitem, 'dual_copyright') dual_copyright = self._decodeQtObject(bitem, 'dual_copyright')
dual_permission = self._decodeQtObject(bitem, 'dual_permission') dual_permissions = self._decodeQtObject(bitem, 'dual_permissions')
dual_text = self._decodeQtObject(bitem, 'dual_text') dual_text = self._decodeQtObject(bitem, 'dual_text')
verse_text = self.formatVerse(old_chapter, chapter, verse) verse_text = self.formatVerse(old_chapter, chapter, verse)
footer = u'%s (%s %s %s)' % (book, version, copyright, permission) footer = u'%s (%s %s %s)' % (book, version, copyright, permissions)
if footer not in raw_footer: if footer not in raw_footer:
raw_footer.append(footer) raw_footer.append(footer)
if dual_bible: if dual_bible:
footer = u'%s (%s %s %s)' % (book, dual_version, dual_copyright, footer = u'%s (%s %s %s)' % (book, dual_version, dual_copyright,
dual_permission) dual_permissions)
if footer not in raw_footer: if footer not in raw_footer:
raw_footer.append(footer) raw_footer.append(footer)
bible_text = u'%s %s\n\n%s %s' % (verse_text, text, verse_text, bible_text = u'%s %s\n\n%s %s' % (verse_text, text, verse_text,

View File

@ -28,7 +28,7 @@ import logging
from forms import EditCustomForm from forms import EditCustomForm
from openlp.core.lib import Plugin, build_icon, translate from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.lib.db import Manager from openlp.core.lib.db import Manager
from openlp.plugins.custom.lib import CustomMediaItem, CustomTab from openlp.plugins.custom.lib import CustomMediaItem, CustomTab
from openlp.plugins.custom.lib.db import CustomSlide, init_schema from openlp.plugins.custom.lib.db import CustomSlide, init_schema
@ -55,11 +55,12 @@ class CustomPlugin(Plugin):
self.icon = build_icon(self.icon_path) self.icon = build_icon(self.icon_path)
def getSettingsTab(self): def getSettingsTab(self):
return CustomTab(self.name) visible_name = self.getString(StringContent.VisibleName)
return CustomTab(self.name, visible_name[u'title'])
def getMediaManagerItem(self): def getMediaManagerItem(self):
# Create the CustomManagerItem object # Create the CustomManagerItem object
return CustomMediaItem(self, self.icon, self.name) return CustomMediaItem(self, self, self.icon)
def about(self): def about(self):
about_text = translate('CustomPlugin', '<strong>Custom Plugin</strong>' about_text = translate('CustomPlugin', '<strong>Custom Plugin</strong>'
@ -96,3 +97,66 @@ class CustomPlugin(Plugin):
for custom in customsUsingTheme: for custom in customsUsingTheme:
custom.theme_name = newTheme custom.theme_name = newTheme
self.custommanager.save_object(custom) self.custommanager.save_object(custom)
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
## Name PluginList ##
self.textStrings[StringContent.Name] = {
u'singular': translate('CustomsPlugin', 'Custom'),
u'plural': translate('CustomsPlugin', 'Customs')
}
## Name for MediaDockManager, SettingsManager ##
self.textStrings[StringContent.VisibleName] = {
u'title': translate('CustomsPlugin', 'Customs')
}
# Middle Header Bar
## Import Button ##
self.textStrings[StringContent.Import] = {
u'title': translate('CustomsPlugin', 'Import'),
u'tooltip': translate('CustomsPlugin',
'Import a Custom')
}
## Load Button ##
self.textStrings[StringContent.Load] = {
u'title': translate('CustomsPlugin', 'Load'),
u'tooltip': translate('CustomsPlugin',
'Load a new Custom')
}
## New Button ##
self.textStrings[StringContent.New] = {
u'title': translate('CustomsPlugin', 'Add'),
u'tooltip': translate('CustomsPlugin',
'Add a new Custom')
}
## Edit Button ##
self.textStrings[StringContent.Edit] = {
u'title': translate('CustomsPlugin', 'Edit'),
u'tooltip': translate('CustomsPlugin',
'Edit the selected Custom')
}
## Delete Button ##
self.textStrings[StringContent.Delete] = {
u'title': translate('CustomsPlugin', 'Delete'),
u'tooltip': translate('CustomsPlugin',
'Delete the selected Custom')
}
## Preview ##
self.textStrings[StringContent.Preview] = {
u'title': translate('CustomsPlugin', 'Preview'),
u'tooltip': translate('CustomsPlugin',
'Preview the selected Custom')
}
## Live Button ##
self.textStrings[StringContent.Live] = {
u'title': translate('CustomsPlugin', 'Live'),
u'tooltip': translate('CustomsPlugin',
'Send the selected Custom live')
}
## Add to service Button ##
self.textStrings[StringContent.Service] = {
u'title': translate('CustomsPlugin', 'Service'),
u'tooltip': translate('CustomsPlugin',
'Add the selected Custom to the service')
}

View File

@ -25,3 +25,4 @@
############################################################################### ###############################################################################
from editcustomform import EditCustomForm from editcustomform import EditCustomForm
from editcustomslideform import EditCustomSlideForm

View File

@ -26,7 +26,7 @@
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, translate, SpellTextEdit from openlp.core.lib import build_icon, translate
class Ui_CustomEditDialog(object): class Ui_CustomEditDialog(object):
def setupUi(self, customEditDialog): def setupUi(self, customEditDialog):
@ -36,6 +36,70 @@ class Ui_CustomEditDialog(object):
build_icon(u':/icon/openlp.org-icon-32.bmp')) build_icon(u':/icon/openlp.org-icon-32.bmp'))
self.gridLayout = QtGui.QGridLayout(customEditDialog) self.gridLayout = QtGui.QGridLayout(customEditDialog)
self.gridLayout.setObjectName(u'gridLayout') self.gridLayout.setObjectName(u'gridLayout')
self.horizontalLayout3 = QtGui.QHBoxLayout()
self.horizontalLayout3.setObjectName(u'horizontalLayout3')
self.themeLabel = QtGui.QLabel(customEditDialog)
self.themeLabel.setObjectName(u'themeLabel')
self.horizontalLayout3.addWidget(self.themeLabel)
self.themeComboBox = QtGui.QComboBox(customEditDialog)
self.themeLabel.setBuddy(self.themeComboBox)
self.themeComboBox.setObjectName(u'themeComboBox')
self.horizontalLayout3.addWidget(self.themeComboBox)
self.gridLayout.addLayout(self.horizontalLayout3, 2, 0, 1, 1)
self.horizontalLayout2 = QtGui.QHBoxLayout()
self.horizontalLayout2.setObjectName(u'horizontalLayout2')
self.creditLabel = QtGui.QLabel(customEditDialog)
self.creditLabel.setObjectName(u'creditLabel')
self.horizontalLayout2.addWidget(self.creditLabel)
self.creditEdit = QtGui.QLineEdit(customEditDialog)
self.creditLabel.setBuddy(self.creditEdit)
self.creditEdit.setObjectName(u'creditEdit')
self.horizontalLayout2.addWidget(self.creditEdit)
self.gridLayout.addLayout(self.horizontalLayout2, 3, 0, 1, 1)
self.buttonBox = QtGui.QDialogButtonBox(customEditDialog)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel |
QtGui.QDialogButtonBox.Save)
self.buttonBox.setObjectName(u'buttonBox')
self.gridLayout.addWidget(self.buttonBox, 4, 0, 1, 1)
self.horizontalLayout4 = QtGui.QHBoxLayout()
self.horizontalLayout4.setObjectName(u'horizontalLayout4')
self.slideListView = QtGui.QListWidget(customEditDialog)
self.slideListView.setAlternatingRowColors(True)
self.slideListView.setObjectName(u'slideListView')
self.horizontalLayout4.addWidget(self.slideListView)
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName(u'verticalLayout')
self.addButton = QtGui.QPushButton(customEditDialog)
self.addButton.setObjectName(u'addButton')
self.verticalLayout.addWidget(self.addButton)
self.editButton = QtGui.QPushButton(customEditDialog)
self.editButton.setObjectName(u'editButton')
self.verticalLayout.addWidget(self.editButton)
self.editAllButton = QtGui.QPushButton(customEditDialog)
self.editAllButton.setObjectName(u'editAllButton')
self.verticalLayout.addWidget(self.editAllButton)
self.deleteButton = QtGui.QPushButton(customEditDialog)
self.deleteButton.setObjectName(u'deleteButton')
self.verticalLayout.addWidget(self.deleteButton)
spacerItem = QtGui.QSpacerItem(20, 128, QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.upButton = QtGui.QPushButton(customEditDialog)
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(u':/services/service_up.png'),
QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.upButton.setIcon(icon1)
self.upButton.setObjectName(u'upButton')
self.verticalLayout.addWidget(self.upButton)
self.downButton = QtGui.QPushButton(customEditDialog)
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(u':/services/service_down.png'),
QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.downButton.setIcon(icon2)
self.downButton.setObjectName(u'downButton')
self.verticalLayout.addWidget(self.downButton)
self.horizontalLayout4.addLayout(self.verticalLayout)
self.gridLayout.addLayout(self.horizontalLayout4, 1, 0, 1, 1)
self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(u'horizontalLayout') self.horizontalLayout.setObjectName(u'horizontalLayout')
self.titleLabel = QtGui.QLabel(customEditDialog) self.titleLabel = QtGui.QLabel(customEditDialog)
@ -46,91 +110,6 @@ class Ui_CustomEditDialog(object):
self.titleEdit.setObjectName(u'titleEdit') self.titleEdit.setObjectName(u'titleEdit')
self.horizontalLayout.addWidget(self.titleEdit) self.horizontalLayout.addWidget(self.titleEdit)
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
self.horizontalLayout4 = QtGui.QHBoxLayout()
self.horizontalLayout4.setObjectName(u'horizontalLayout4')
self.verseListView = QtGui.QListWidget(customEditDialog)
self.verseListView.setAlternatingRowColors(True)
self.verseListView.setObjectName(u'verseListView')
self.horizontalLayout4.addWidget(self.verseListView)
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName(u'verticalLayout')
self.upButton = QtGui.QPushButton(customEditDialog)
self.upButton.setIcon(build_icon(u':/services/service_up.png'))
self.upButton.setObjectName(u'upButton')
self.verticalLayout.addWidget(self.upButton)
spacerItem = QtGui.QSpacerItem(20, 128, QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.downButton = QtGui.QPushButton(customEditDialog)
self.downButton.setIcon(build_icon(u':/services/service_down.png'))
self.downButton.setObjectName(u'downButton')
self.verticalLayout.addWidget(self.downButton)
self.horizontalLayout4.addLayout(self.verticalLayout)
self.gridLayout.addLayout(self.horizontalLayout4, 1, 0, 1, 1)
self.editWidget = QtGui.QWidget(customEditDialog)
self.editWidget.setObjectName(u'editWidget')
self.editLayout3 = QtGui.QHBoxLayout(self.editWidget)
self.editLayout3.setSpacing(8)
self.editLayout3.setMargin(0)
self.editLayout3.setObjectName(u'editLayout3')
self.verseTextEdit = SpellTextEdit(self)
self.verseTextEdit.setObjectName(u'verseTextEdit')
self.editLayout3.addWidget(self.verseTextEdit)
self.buttonWidget = QtGui.QWidget(self.editWidget)
self.buttonWidget.setObjectName(u'buttonWidget')
self.verticalLayout2 = QtGui.QVBoxLayout(self.buttonWidget)
self.verticalLayout2.setObjectName(u'verticalLayout2')
self.addButton = QtGui.QPushButton(self.buttonWidget)
self.addButton.setObjectName(u'addButton')
self.verticalLayout2.addWidget(self.addButton)
self.editButton = QtGui.QPushButton(self.buttonWidget)
self.editButton.setObjectName(u'editButton')
self.verticalLayout2.addWidget(self.editButton)
self.editAllButton = QtGui.QPushButton(self.buttonWidget)
self.editAllButton.setObjectName(u'editAllButton')
self.verticalLayout2.addWidget(self.editAllButton)
self.saveButton = QtGui.QPushButton(self.buttonWidget)
self.saveButton.setObjectName(u'saveButton')
self.verticalLayout2.addWidget(self.saveButton)
self.deleteButton = QtGui.QPushButton(self.buttonWidget)
self.deleteButton.setObjectName(u'deleteButton')
self.verticalLayout2.addWidget(self.deleteButton)
self.clearButton = QtGui.QPushButton(self.buttonWidget)
self.clearButton.setObjectName(u'clearButton')
self.verticalLayout2.addWidget(self.clearButton)
self.splitButton = QtGui.QPushButton(self.buttonWidget)
self.splitButton.setObjectName(u'splitButton')
self.verticalLayout2.addWidget(self.splitButton)
spacerItem1 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding)
self.verticalLayout2.addItem(spacerItem1)
self.editLayout3.addWidget(self.buttonWidget)
self.gridLayout.addWidget(self.editWidget, 2, 0, 1, 1)
self.horizontalLayout3 = QtGui.QHBoxLayout()
self.horizontalLayout3.setObjectName(u'horizontalLayout3')
self.themeLabel = QtGui.QLabel(customEditDialog)
self.themeLabel.setObjectName(u'themeLabel')
self.horizontalLayout3.addWidget(self.themeLabel)
self.themeComboBox = QtGui.QComboBox(customEditDialog)
self.themeLabel.setBuddy(self.themeComboBox)
self.themeComboBox.setObjectName(u'themeComboBox')
self.horizontalLayout3.addWidget(self.themeComboBox)
self.gridLayout.addLayout(self.horizontalLayout3, 3, 0, 1, 1)
self.horizontalLayout2 = QtGui.QHBoxLayout()
self.horizontalLayout2.setObjectName(u'horizontalLayout2')
self.creditLabel = QtGui.QLabel(customEditDialog)
self.creditLabel.setObjectName(u'creditLabel')
self.horizontalLayout2.addWidget(self.creditLabel)
self.creditEdit = QtGui.QLineEdit(customEditDialog)
self.creditLabel.setBuddy(self.creditEdit)
self.creditEdit.setObjectName(u'creditEdit')
self.horizontalLayout2.addWidget(self.creditEdit)
self.gridLayout.addLayout(self.horizontalLayout2, 4, 0, 1, 1)
self.buttonBox = QtGui.QDialogButtonBox(customEditDialog)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel |
QtGui.QDialogButtonBox.Save)
self.buttonBox.setObjectName(u'buttonBox')
self.gridLayout.addWidget(self.buttonBox, 5, 0, 1, 1)
self.retranslateUi(customEditDialog) self.retranslateUi(customEditDialog)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'), QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'),
customEditDialog.accept) customEditDialog.accept)
@ -143,46 +122,32 @@ class Ui_CustomEditDialog(object):
translate('CustomPlugin.EditCustomForm', 'Edit Custom Slides')) translate('CustomPlugin.EditCustomForm', 'Edit Custom Slides'))
self.upButton.setToolTip( self.upButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Move slide up one ' translate('CustomPlugin.EditCustomForm', 'Move slide up one '
'position.')) 'position.'))
self.downButton.setToolTip( self.downButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Move slide down one ' translate('CustomPlugin.EditCustomForm', 'Move slide down one '
'position.')) 'position.'))
self.titleLabel.setText( self.titleLabel.setText(
translate('CustomPlugin.EditCustomForm', '&Title:')) translate('CustomPlugin.EditCustomForm', '&Title:'))
self.addButton.setText( self.addButton.setText(
translate('CustomPlugin.EditCustomForm', 'Add New')) translate('CustomPlugin.EditCustomForm', '&Add'))
self.addButton.setToolTip( self.addButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Add a new slide at ' translate('CustomPlugin.EditCustomForm', 'Add a new slide at '
'bottom.')) 'bottom.'))
self.editButton.setText( self.editButton.setText(
translate('CustomPlugin.EditCustomForm', 'Edit')) translate('CustomPlugin.EditCustomForm', '&Edit'))
self.editButton.setToolTip( self.editButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Edit the selected ' translate('CustomPlugin.EditCustomForm', 'Edit the selected '
'slide.')) 'slide.'))
self.editAllButton.setText( self.editAllButton.setText(
translate('CustomPlugin.EditCustomForm', 'Edit All')) translate('CustomPlugin.EditCustomForm', 'Ed&it All'))
self.editAllButton.setToolTip( self.editAllButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Edit all the slides at ' translate('CustomPlugin.EditCustomForm', 'Edit all the slides at '
'once.')) 'once.'))
self.saveButton.setText(
translate('CustomPlugin.EditCustomForm', 'Save'))
self.saveButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Save the slide currently '
'being edited.'))
self.deleteButton.setText( self.deleteButton.setText(
translate('CustomPlugin.EditCustomForm', 'Delete')) translate('CustomPlugin.EditCustomForm', '&Delete'))
self.deleteButton.setToolTip( self.deleteButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Delete the selected ' translate('CustomPlugin.EditCustomForm', 'Delete the selected '
'slide.')) 'slide.'))
self.clearButton.setText(
translate('CustomPlugin.EditCustomForm', 'Clear'))
self.clearButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Clear edit area'))
self.splitButton.setText(
translate('CustomPlugin.EditCustomForm', 'Split Slide'))
self.splitButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Split a slide into two '
'by inserting a slide splitter.'))
self.themeLabel.setText( self.themeLabel.setText(
translate('CustomPlugin.EditCustomForm', 'The&me:')) translate('CustomPlugin.EditCustomForm', 'The&me:'))
self.creditLabel.setText( self.creditLabel.setText(

View File

@ -32,6 +32,7 @@ from openlp.core.lib import Receiver, translate
from openlp.plugins.custom.lib import CustomXMLBuilder, CustomXMLParser from openlp.plugins.custom.lib import CustomXMLBuilder, CustomXMLParser
from openlp.plugins.custom.lib.db import CustomSlide from openlp.plugins.custom.lib.db import CustomSlide
from editcustomdialog import Ui_CustomEditDialog from editcustomdialog import Ui_CustomEditDialog
from editcustomslideform import EditCustomSlideForm
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -40,7 +41,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
Class documentation goes here. Class documentation goes here.
""" """
log.info(u'Custom Editor loaded') log.info(u'Custom Editor loaded')
def __init__(self, custommanager, parent = None): def __init__(self, custommanager, parent=None):
""" """
Constructor Constructor
""" """
@ -61,28 +62,20 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
QtCore.SIGNAL(u'pressed()'), self.onEditButtonPressed) QtCore.SIGNAL(u'pressed()'), self.onEditButtonPressed)
QtCore.QObject.connect(self.editAllButton, QtCore.QObject.connect(self.editAllButton,
QtCore.SIGNAL(u'pressed()'), self.onEditAllButtonPressed) QtCore.SIGNAL(u'pressed()'), self.onEditAllButtonPressed)
QtCore.QObject.connect(self.saveButton,
QtCore.SIGNAL(u'pressed()'), self.onSaveButtonPressed)
QtCore.QObject.connect(self.deleteButton, QtCore.QObject.connect(self.deleteButton,
QtCore.SIGNAL(u'pressed()'), self.onDeleteButtonPressed) QtCore.SIGNAL(u'pressed()'), self.onDeleteButtonPressed)
QtCore.QObject.connect(self.clearButton,
QtCore.SIGNAL(u'pressed()'), self.onClearButtonPressed)
QtCore.QObject.connect(self.upButton, QtCore.QObject.connect(self.upButton,
QtCore.SIGNAL(u'pressed()'), self.onUpButtonPressed) QtCore.SIGNAL(u'pressed()'), self.onUpButtonPressed)
QtCore.QObject.connect(self.downButton, QtCore.QObject.connect(self.downButton,
QtCore.SIGNAL(u'pressed()'), self.onDownButtonPressed) QtCore.SIGNAL(u'pressed()'), self.onDownButtonPressed)
QtCore.QObject.connect(self.splitButton, QtCore.QObject.connect(self.slideListView,
QtCore.SIGNAL(u'pressed()'), self.onSplitButtonPressed)
QtCore.QObject.connect(self.verseListView,
QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem*)'),
self.onVerseListViewSelected)
QtCore.QObject.connect(self.verseListView,
QtCore.SIGNAL(u'itemClicked(QListWidgetItem*)'), QtCore.SIGNAL(u'itemClicked(QListWidgetItem*)'),
self.onVerseListViewPressed) self.onSlideListViewPressed)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'theme_update_list'), self.loadThemes) QtCore.SIGNAL(u'theme_update_list'), self.loadThemes)
# Create other objects and forms # Create other objects and forms.
self.custommanager = custommanager self.custommanager = custommanager
self.editSlideForm = EditCustomSlideForm(self)
self.initialise() self.initialise()
def onPreview(self, button): def onPreview(self, button):
@ -92,21 +85,15 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
Receiver.send_message(u'custom_preview') Receiver.send_message(u'custom_preview')
def initialise(self): def initialise(self):
self.editAll = False
self.addButton.setEnabled(True) self.addButton.setEnabled(True)
self.deleteButton.setEnabled(False) self.deleteButton.setEnabled(False)
self.editButton.setEnabled(False) self.editButton.setEnabled(False)
self.editAllButton.setEnabled(True) self.editAllButton.setEnabled(True)
self.saveButton.setEnabled(False)
self.clearButton.setEnabled(False)
self.splitButton.setEnabled(False)
self.titleEdit.setText(u'') self.titleEdit.setText(u'')
self.creditEdit.setText(u'') self.creditEdit.setText(u'')
self.verseTextEdit.clear() self.slideListView.clear()
self.verseListView.clear() # Make sure we have a new item.
#make sure we have a new item
self.customSlide = CustomSlide() self.customSlide = CustomSlide()
self.themeComboBox.addItem(u'')
def loadThemes(self, themelist): def loadThemes(self, themelist):
self.themeComboBox.clear() self.themeComboBox.clear()
@ -115,6 +102,16 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
self.themeComboBox.addItem(themename) self.themeComboBox.addItem(themename)
def loadCustom(self, id, preview=False): def loadCustom(self, id, preview=False):
"""
Called when editing or creating a new custom.
``id``
The cutom's id. If zero, then a new custom is created.
``preview``
States whether the custom is edited while being previewed in the
preview panel.
"""
self.customSlide = CustomSlide() self.customSlide = CustomSlide()
self.initialise() self.initialise()
if id != 0: if id != 0:
@ -122,9 +119,9 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
self.titleEdit.setText(self.customSlide.title) self.titleEdit.setText(self.customSlide.title)
self.creditEdit.setText(self.customSlide.credits) self.creditEdit.setText(self.customSlide.credits)
customXML = CustomXMLParser(self.customSlide.text) customXML = CustomXMLParser(self.customSlide.text)
verseList = customXML.get_verses() slideList = customXML.get_verses()
for verse in verseList: for slide in slideList:
self.verseListView.addItem(verse[1]) self.slideListView.addItem(slide[1])
theme = self.customSlide.theme_name theme = self.customSlide.theme_name
id = self.themeComboBox.findText(theme, QtCore.Qt.MatchExactly) id = self.themeComboBox.findText(theme, QtCore.Qt.MatchExactly)
if id == -1: if id == -1:
@ -132,7 +129,8 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
self.themeComboBox.setCurrentIndex(id) self.themeComboBox.setCurrentIndex(id)
else: else:
self.themeComboBox.setCurrentIndex(0) self.themeComboBox.setCurrentIndex(0)
#if not preview hide the preview button self.editAllButton.setEnabled(False)
# If not preview hide the preview button.
self.previewButton.setVisible(False) self.previewButton.setVisible(False)
if preview: if preview:
self.previewButton.setVisible(True) self.previewButton.setVisible(True)
@ -148,6 +146,9 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
self.close() self.close()
def saveCustom(self): def saveCustom(self):
"""
Saves the custom.
"""
valid, message = self._validate() valid, message = self._validate()
if not valid: if not valid:
QtGui.QMessageBox.critical(self, QtGui.QMessageBox.critical(self,
@ -157,9 +158,9 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
sxml.new_document() sxml.new_document()
sxml.add_lyrics_to_song() sxml.add_lyrics_to_song()
count = 1 count = 1
for i in range(0, self.verseListView.count()): for i in range(0, self.slideListView.count()):
sxml.add_verse_to_lyrics(u'custom', unicode(count), sxml.add_verse_to_lyrics(u'custom', unicode(count),
unicode(self.verseListView.item(i).text())) unicode(self.slideListView.item(i).text()))
count += 1 count += 1
self.customSlide.title = unicode(self.titleEdit.displayText(), u'utf-8') self.customSlide.title = unicode(self.titleEdit.displayText(), u'utf-8')
self.customSlide.text = unicode(sxml.extract_xml(), u'utf-8') self.customSlide.text = unicode(sxml.extract_xml(), u'utf-8')
@ -170,114 +171,103 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
return self.custommanager.save_object(self.customSlide) return self.custommanager.save_object(self.customSlide)
def onUpButtonPressed(self): def onUpButtonPressed(self):
selectedRow = self.verseListView.currentRow() selectedRow = self.slideListView.currentRow()
if selectedRow != 0: if selectedRow != 0:
qw = self.verseListView.takeItem(selectedRow) qw = self.slideListView.takeItem(selectedRow)
self.verseListView.insertItem(selectedRow - 1, qw) self.slideListView.insertItem(selectedRow - 1, qw)
self.verseListView.setCurrentRow(selectedRow - 1) self.slideListView.setCurrentRow(selectedRow - 1)
def onDownButtonPressed(self): def onDownButtonPressed(self):
selectedRow = self.verseListView.currentRow() selectedRow = self.slideListView.currentRow()
# zero base arrays # zero base arrays
if selectedRow != self.verseListView.count() - 1: if selectedRow != self.slideListView.count() - 1:
qw = self.verseListView.takeItem(selectedRow) qw = self.slideListView.takeItem(selectedRow)
self.verseListView.insertItem(selectedRow + 1, qw) self.slideListView.insertItem(selectedRow + 1, qw)
self.verseListView.setCurrentRow(selectedRow + 1) self.slideListView.setCurrentRow(selectedRow + 1)
def onClearButtonPressed(self): def onSlideListViewPressed(self, item):
self.verseTextEdit.clear()
self.editAll = False
self.addButton.setEnabled(True)
self.editAllButton.setEnabled(True)
self.saveButton.setEnabled(False)
def onVerseListViewPressed(self, item):
self.deleteButton.setEnabled(True) self.deleteButton.setEnabled(True)
self.editButton.setEnabled(True) self.editButton.setEnabled(True)
def onVerseListViewSelected(self, item):
self.editText(item.text())
def onAddButtonPressed(self): def onAddButtonPressed(self):
self.verseListView.addItem(self.verseTextEdit.toPlainText()) self.editSlideForm.setText(u'')
self.deleteButton.setEnabled(False) if self.editSlideForm.exec_():
self.verseTextEdit.clear() for slide in self.editSlideForm.getText():
self.slideListView.addItem(slide)
self.editAllButton.setEnabled(True)
def onEditButtonPressed(self): def onEditButtonPressed(self):
self.editText(self.verseListView.currentItem().text()) self.editSlideForm.setText(self.slideListView.currentItem().text())
if self.editSlideForm.exec_():
self.updateSlideList(self.editSlideForm.getText())
def onEditAllButtonPressed(self): def onEditAllButtonPressed(self):
self.editAll = True """
self.addButton.setEnabled(False) Edits all slides.
self.splitButton.setEnabled(True) """
if self.verseListView.count() > 0: if self.slideListView.count() > 0:
verse_list = u'' slide_list = u''
for row in range(0, self.verseListView.count()): for row in range(0, self.slideListView.count()):
item = self.verseListView.item(row) item = self.slideListView.item(row)
verse_list += item.text() slide_list += item.text()
if row != self.verseListView.count() - 1: if row != self.slideListView.count() - 1:
verse_list += u'\n[---]\n' slide_list += u'\n[---]\n'
self.editText(verse_list) self.editSlideForm.setText(slide_list)
if self.editSlideForm.exec_():
self.updateSlideList(self.editSlideForm.getText(), True)
def editText(self, text): def updateSlideList(self, slides, edit_all=False):
self.beforeText = text """
self.verseTextEdit.setPlainText(text) Updates the slide list after editing slides.
self.deleteButton.setEnabled(False)
self.editButton.setEnabled(False)
self.editAllButton.setEnabled(False)
self.saveButton.setEnabled(True)
self.clearButton.setEnabled(True)
def onSaveButtonPressed(self): ``slides``
if self.editAll: A list of all slides which have been edited.
self.verseListView.clear()
for row in unicode(self.verseTextEdit.toPlainText()).split( ``edit_all``
u'\n[---]\n'): Indicates if all slides or only one slide has been edited.
self.verseListView.addItem(row) """
if len(slides) == 1:
self.slideListView.currentItem().setText(slides[0])
else: else:
self.verseListView.currentItem().setText( if edit_all:
self.verseTextEdit.toPlainText()) self.slideListView.clear()
#number of lines has change for slide in slides:
if len(self.beforeText.split(u'\n')) != \ self.slideListView.addItem(slide)
len(self.verseTextEdit.toPlainText().split(u'\n')): else:
tempList = {} old_slides = []
for row in range(0, self.verseListView.count()): old_row = self.slideListView.currentRow()
tempList[row] = self.verseListView.item(row).text() # Create a list with all (old/unedited) slides.
self.verseListView.clear() old_slides = [self.slideListView.item(row).text() for row in \
for row in range (0, len(tempList)): range(0, self.slideListView.count())]
self.verseListView.addItem(tempList[row]) self.slideListView.clear()
self.verseListView.repaint() old_slides.pop(old_row)
self.addButton.setEnabled(True) # Insert all slides to make the old_slides list complete.
self.saveButton.setEnabled(False) for slide in slides:
self.editButton.setEnabled(False) old_slides.insert(old_row, slide)
self.editAllButton.setEnabled(True) for slide in old_slides:
self.splitButton.setEnabled(False) self.slideListView.addItem(slide)
self.verseTextEdit.clear() self.slideListView.repaint()
def onSplitButtonPressed(self):
if self.verseTextEdit.textCursor().columnNumber() != 0:
self.verseTextEdit.insertPlainText(u'\n')
self.verseTextEdit.insertPlainText(u'[---]\n' )
self.verseTextEdit.setFocus()
def onDeleteButtonPressed(self): def onDeleteButtonPressed(self):
self.verseListView.takeItem(self.verseListView.currentRow()) self.slideListView.takeItem(self.slideListView.currentRow())
self.editButton.setEnabled(False) self.editButton.setEnabled(True)
self.editAllButton.setEnabled(True) self.editAllButton.setEnabled(True)
if self.slideListView.count() == 0:
self.deleteButton.setEnabled(False)
self.editButton.setEnabled(False)
self.editAllButton.setEnabled(False)
def _validate(self): def _validate(self):
"""
Checks whether a custom is valid or not.
"""
# We must have a title.
if len(self.titleEdit.displayText()) == 0: if len(self.titleEdit.displayText()) == 0:
self.titleEdit.setFocus() self.titleEdit.setFocus()
return False, translate('CustomPlugin.EditCustomForm', return False, translate('CustomPlugin.EditCustomForm',
'You need to type in a title.') 'You need to type in a title.')
# must have 1 slide # We must have one slide.
if self.verseListView.count() == 0: if self.slideListView.count() == 0:
self.verseTextEdit.setFocus()
return False, translate('CustomPlugin.EditCustomForm', return False, translate('CustomPlugin.EditCustomForm',
'You need to add at least one slide') 'You need to add at least one slide')
if self.verseTextEdit.toPlainText():
self.verseTextEdit.setFocus()
return False, translate('CustomPlugin.EditCustomForm',
'You have one or more unsaved slides, please either save your '
'slide(s) or clear your changes.')
return True, u'' return True, u''

View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, Frode Woldsund #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate, SpellTextEdit
class Ui_CustomSlideEditDialog(object):
def setupUi(self, customSlideEditDialog):
customSlideEditDialog.setObjectName(u'customSlideEditDialog')
customSlideEditDialog.resize(474, 442)
self.buttonBox = QtGui.QDialogButtonBox(customSlideEditDialog)
self.buttonBox.setGeometry(QtCore.QRect(8, 407, 458, 32))
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel |
QtGui.QDialogButtonBox.Save)
self.buttonBox.setObjectName(u'buttonBox')
self.slideTextEdit = SpellTextEdit(self)
self.slideTextEdit.setGeometry(QtCore.QRect(8, 8, 458, 349))
self.slideTextEdit.setObjectName(u'slideTextEdit')
self.splitButton = QtGui.QPushButton(customSlideEditDialog)
self.splitButton.setGeometry(QtCore.QRect(380, 370, 85, 27))
self.splitButton.setObjectName(u'splitButton')
self.retranslateUi(customSlideEditDialog)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'),
customSlideEditDialog.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'),
customSlideEditDialog.reject)
QtCore.QMetaObject.connectSlotsByName(customSlideEditDialog)
def retranslateUi(self, customSlideEditDialog):
self.splitButton.setText(
translate('CustomPlugin.EditCustomForm', 'Split Slide'))
self.splitButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Split a slide into two '
'by inserting a slide splitter.'))

View File

@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, Frode Woldsund #
# --------------------------------------------------------------------------- #
# 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 PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver, translate
from editcustomslidedialog import Ui_CustomSlideEditDialog
log = logging.getLogger(__name__)
class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
"""
Class documentation goes here.
"""
log.info(u'Custom Verse Editor loaded')
def __init__(self, parent=None):
"""
Constructor
"""
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
# Connecting signals and slots
QtCore.QObject.connect(self.splitButton,
QtCore.SIGNAL(u'pressed()'), self.onSplitButtonPressed)
def setText(self, text):
"""
Set the text for slideTextEdit.
``text``
The text (unicode).
"""
self.slideTextEdit.clear()
if text:
self.slideTextEdit.setPlainText(text)
self.slideTextEdit.setFocus()
def getText(self):
"""
Returns a list with all slides.
"""
return self.slideTextEdit.toPlainText().split(u'\n[---]\n')
def onSplitButtonPressed(self):
"""
Splits a slide in two slides.
"""
if self.slideTextEdit.textCursor().columnNumber() != 0:
self.slideTextEdit.insertPlainText(u'\n')
self.slideTextEdit.insertPlainText(u'[---]\n' )
self.slideTextEdit.setFocus()

View File

@ -32,12 +32,11 @@ class CustomTab(SettingsTab):
""" """
CustomTab is the Custom settings tab in the settings dialog. CustomTab is the Custom settings tab in the settings dialog.
""" """
def __init__(self, title): def __init__(self, title, visible_title):
SettingsTab.__init__(self, title) SettingsTab.__init__(self, title, visible_title)
def setupUi(self): def setupUi(self):
self.setObjectName(u'CustomTab') self.setObjectName(u'CustomTab')
self.tabTitleVisible = translate('CustomPlugin.CustomTab', 'Custom')
self.customLayout = QtGui.QFormLayout(self) self.customLayout = QtGui.QFormLayout(self)
self.customLayout.setSpacing(8) self.customLayout.setSpacing(8)
self.customLayout.setMargin(8) self.customLayout.setMargin(8)

View File

@ -46,14 +46,12 @@ class CustomMediaItem(MediaManagerItem):
""" """
log.info(u'Custom Media Item loaded') log.info(u'Custom Media Item loaded')
def __init__(self, parent, icon, title): def __init__(self, parent, plugin, icon):
self.PluginNameShort = u'Custom'
self.pluginNameVisible = translate('CustomPlugin.MediaItem', 'Custom')
self.IconPath = u'custom/custom' self.IconPath = u'custom/custom'
# this next is a class, not an instance of a class - it will # this next is a class, not an instance of a class - it will
# be instanced by the base MediaManagerItem # be instanced by the base MediaManagerItem
self.ListViewWithDnD_class = CustomListView self.ListViewWithDnD_class = CustomListView
MediaManagerItem.__init__(self, parent, icon, title) MediaManagerItem.__init__(self, parent, self, icon)
self.singleServiceItem = False self.singleServiceItem = False
# Holds information about whether the edit is remotly triggered and # Holds information about whether the edit is remotly triggered and
# which Custom is required. # which Custom is required.

View File

@ -26,7 +26,7 @@
import logging import logging
from openlp.core.lib import Plugin, build_icon, translate from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.plugins.images.lib import ImageMediaItem from openlp.plugins.images.lib import ImageMediaItem
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -42,7 +42,7 @@ class ImagePlugin(Plugin):
def getMediaManagerItem(self): def getMediaManagerItem(self):
# Create the MediaManagerItem object # Create the MediaManagerItem object
return ImageMediaItem(self, self.icon, self.name) return ImageMediaItem(self, self, self.icon)
def about(self): def about(self):
about_text = translate('ImagePlugin', '<strong>Image Plugin</strong>' about_text = translate('ImagePlugin', '<strong>Image Plugin</strong>'
@ -57,3 +57,60 @@ class ImagePlugin(Plugin):
'selected image as a background instead of the background ' 'selected image as a background instead of the background '
'provided by the theme.') 'provided by the theme.')
return about_text return about_text
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
## Name PluginList ##
self.textStrings[StringContent.Name] = {
u'singular': translate('ImagePlugin', 'Image'),
u'plural': translate('ImagePlugin', 'Images')
}
## Name for MediaDockManager, SettingsManager ##
self.textStrings[StringContent.VisibleName] = {
u'title': translate('ImagePlugin', 'Images')
}
# Middle Header Bar
## Load Button ##
self.textStrings[StringContent.Load] = {
u'title': translate('ImagePlugin', 'Load'),
u'tooltip': translate('ImagePlugin',
'Load a new Image')
}
## New Button ##
self.textStrings[StringContent.New] = {
u'title': translate('ImagePlugin', 'Add'),
u'tooltip': translate('ImagePlugin',
'Add a new Image')
}
## Edit Button ##
self.textStrings[StringContent.Edit] = {
u'title': translate('ImagePlugin', 'Edit'),
u'tooltip': translate('ImagePlugin',
'Edit the selected Image')
}
## Delete Button ##
self.textStrings[StringContent.Delete] = {
u'title': translate('ImagePlugin', 'Delete'),
u'tooltip': translate('ImagePlugin',
'Delete the selected Image')
}
## Preview ##
self.textStrings[StringContent.Preview] = {
u'title': translate('ImagePlugin', 'Preview'),
u'tooltip': translate('ImagePlugin',
'Preview the selected Image')
}
## Live Button ##
self.textStrings[StringContent.Live] = {
u'title': translate('ImagePlugin', 'Live'),
u'tooltip': translate('ImagePlugin',
'Send the selected Image live')
}
## Add to service Button ##
self.textStrings[StringContent.Service] = {
u'title': translate('ImagePlugin', 'Service'),
u'tooltip': translate('ImagePlugin',
'Add the selected Image to the service')
}

View File

@ -49,14 +49,12 @@ class ImageMediaItem(MediaManagerItem):
""" """
log.info(u'Image Media Item loaded') log.info(u'Image Media Item loaded')
def __init__(self, parent, icon, title): def __init__(self, parent, plugin, icon):
self.PluginNameShort = u'Image'
self.pluginNameVisible = translate('ImagePlugin.MediaItem', 'Image')
self.IconPath = u'images/image' self.IconPath = u'images/image'
# this next is a class, not an instance of a class - it will # this next is a class, not an instance of a class - it will
# be instanced by the base MediaManagerItem # be instanced by the base MediaManagerItem
self.ListViewWithDnD_class = ImageListView self.ListViewWithDnD_class = ImageListView
MediaManagerItem.__init__(self, parent, icon, title) MediaManagerItem.__init__(self, parent, self, icon)
def retranslateUi(self): def retranslateUi(self):
self.OnNewPrompt = translate('ImagePlugin.MediaItem', self.OnNewPrompt = translate('ImagePlugin.MediaItem',

View File

@ -25,5 +25,6 @@
############################################################################### ###############################################################################
from mediaitem import MediaMediaItem from mediaitem import MediaMediaItem
from mediatab import MediaTab
__all__ = ['MediaMediaItem'] __all__ = ['MediaMediaItem']

View File

@ -46,9 +46,7 @@ class MediaMediaItem(MediaManagerItem):
""" """
log.info(u'%s MediaMediaItem loaded', __name__) log.info(u'%s MediaMediaItem loaded', __name__)
def __init__(self, parent, icon, title): def __init__(self, parent, plugin, icon):
self.PluginNameShort = u'Media'
self.pluginNameVisible = translate('MediaPlugin.MediaItem', 'Media')
self.IconPath = u'images/image' self.IconPath = u'images/image'
self.background = False self.background = False
# this next is a class, not an instance of a class - it will # this next is a class, not an instance of a class - it will
@ -56,7 +54,7 @@ class MediaMediaItem(MediaManagerItem):
self.ListViewWithDnD_class = MediaListView self.ListViewWithDnD_class = MediaListView
self.PreviewFunction = QtGui.QPixmap( self.PreviewFunction = QtGui.QPixmap(
u':/media/media_video.png').toImage() u':/media/media_video.png').toImage()
MediaManagerItem.__init__(self, parent, icon, title) MediaManagerItem.__init__(self, parent, self, icon)
self.singleServiceItem = False self.singleServiceItem = False
self.serviceItemIconName = u':/media/media_video.png' self.serviceItemIconName = u':/media/media_video.png'
@ -115,7 +113,7 @@ class MediaMediaItem(MediaManagerItem):
'You must select a media file to replace the background with.')): 'You must select a media file to replace the background with.')):
item = self.listView.currentItem() item = self.listView.currentItem()
filename = unicode(item.data(QtCore.Qt.UserRole).toString()) filename = unicode(item.data(QtCore.Qt.UserRole).toString())
self.parent.liveController.display.video(filename, 0) self.parent.liveController.display.video(filename, 0, True)
self.resetButton.setVisible(True) self.resetButton.setVisible(True)
def generateSlideData(self, service_item, item=None): def generateSlideData(self, service_item, item=None):

View File

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, Frode Woldsund #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
from PyQt4 import QtCore, QtGui
from openlp.core.lib import SettingsTab, translate, Receiver
class MediaTab(SettingsTab):
"""
MediaTab is the Media settings tab in the settings dialog.
"""
def __init__(self, title):
SettingsTab.__init__(self, title)
def setupUi(self):
self.setObjectName(u'MediaTab')
self.tabTitleVisible = translate('MediaPlugin.MediaTab', 'Media')
self.mediaLayout = QtGui.QFormLayout(self)
self.mediaLayout.setSpacing(8)
self.mediaLayout.setMargin(8)
self.mediaLayout.setObjectName(u'mediaLayout')
self.mediaModeGroupBox = QtGui.QGroupBox(self)
self.mediaModeGroupBox.setObjectName(u'mediaModeGroupBox')
self.mediaModeLayout = QtGui.QVBoxLayout(self.mediaModeGroupBox)
self.mediaModeLayout.setSpacing(8)
self.mediaModeLayout.setMargin(8)
self.mediaModeLayout.setObjectName(u'mediaModeLayout')
self.usePhononCheckBox = QtGui.QCheckBox(self.mediaModeGroupBox)
self.usePhononCheckBox.setObjectName(u'usePhononCheckBox')
self.mediaModeLayout.addWidget(self.usePhononCheckBox)
self.mediaLayout.setWidget(
0, QtGui.QFormLayout.LabelRole, self.mediaModeGroupBox)
QtCore.QObject.connect(self.usePhononCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onUsePhononCheckBoxChanged)
def retranslateUi(self):
self.mediaModeGroupBox.setTitle(translate('MediaPlugin.MediaTab',
'Media Display'))
self.usePhononCheckBox.setText(
translate('MediaPlugin.MediaTab', 'Use Phonon for video playback'))
def onUsePhononCheckBoxChanged(self, check_state):
self.usePhonon = (check_state == QtCore.Qt.Checked)
self.usePhononChanged = True
def load(self):
self.usePhonon = QtCore.QSettings().value(
self.settingsSection + u'/use phonon',
QtCore.QVariant(True)).toBool()
self.usePhononCheckBox.setChecked(self.usePhonon)
def save(self):
oldUsePhonon = QtCore.QSettings().value(
u'media/use phonon', QtCore.QVariant(True)).toBool()
if oldUsePhonon != self.usePhonon:
QtCore.QSettings().setValue(self.settingsSection + u'/use phonon',
QtCore.QVariant(self.usePhonon))
Receiver.send_message(u'config_screen_changed')

View File

@ -28,8 +28,8 @@ import logging
from PyQt4.phonon import Phonon from PyQt4.phonon import Phonon
from openlp.core.lib import Plugin, build_icon, translate from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.plugins.media.lib import MediaMediaItem from openlp.plugins.media.lib import MediaMediaItem, MediaTab
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -68,11 +68,71 @@ class MediaPlugin(Plugin):
type = u'' type = u''
return list, type return list, type
def getSettingsTab(self):
return MediaTab(self.name)
def getMediaManagerItem(self): def getMediaManagerItem(self):
# Create the MediaManagerItem object # Create the MediaManagerItem object
return MediaMediaItem(self, self.icon, self.name) return MediaMediaItem(self, self, self.icon)
def about(self): def about(self):
about_text = translate('MediaPlugin', '<strong>Media Plugin</strong>' about_text = translate('MediaPlugin', '<strong>Media Plugin</strong>'
'<br />The media plugin provides playback of audio and video.') '<br />The media plugin provides playback of audio and video.')
return about_text return about_text
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
## Name PluginList ##
self.textStrings[StringContent.Name] = {
u'singular': translate('MediaPlugin', 'Media'),
u'plural': translate('MediaPlugin', 'Media')
}
## Name for MediaDockManager, SettingsManager ##
self.textStrings[StringContent.VisibleName] = {
u'title': translate('MediaPlugin', 'Media')
}
# Middle Header Bar
## Load Button ##
self.textStrings[StringContent.Load] = {
u'title': translate('MediaPlugin', 'Load'),
u'tooltip': translate('MediaPlugin',
'Load a new Media')
}
## New Button ##
self.textStrings[StringContent.New] = {
u'title': translate('MediaPlugin', 'Add'),
u'tooltip': translate('MediaPlugin',
'Add a new Media')
}
## Edit Button ##
self.textStrings[StringContent.Edit] = {
u'title': translate('MediaPlugin', 'Edit'),
u'tooltip': translate('MediaPlugin',
'Edit the selected Media')
}
## Delete Button ##
self.textStrings[StringContent.Delete] = {
u'title': translate('MediaPlugin', 'Delete'),
u'tooltip': translate('MediaPlugin',
'Delete the selected Media')
}
## Preview ##
self.textStrings[StringContent.Preview] = {
u'title': translate('MediaPlugin', 'Preview'),
u'tooltip': translate('MediaPlugin',
'Preview the selected Media')
}
## Live Button ##
self.textStrings[StringContent.Live] = {
u'title': translate('MediaPlugin', 'Live'),
u'tooltip': translate('MediaPlugin',
'Send the selected Media live')
}
## Add to service Button ##
self.textStrings[StringContent.Service] = {
u'title': translate('MediaPlugin', 'Service'),
u'tooltip': translate('MediaPlugin',
'Add the selected Media to the service')
}

View File

@ -58,15 +58,12 @@ class PresentationMediaItem(MediaManagerItem):
Constructor. Setup defaults Constructor. Setup defaults
""" """
self.controllers = controllers self.controllers = controllers
self.PluginNameShort = u'Presentation'
self.pluginNameVisible = translate('PresentationPlugin.MediaItem',
'Presentation')
self.IconPath = u'presentations/presentation' self.IconPath = u'presentations/presentation'
self.Automatic = u'' self.Automatic = u''
# this next is a class, not an instance of a class - it will # this next is a class, not an instance of a class - it will
# be instanced by the base MediaManagerItem # be instanced by the base MediaManagerItem
self.ListViewWithDnD_class = PresentationListView self.ListViewWithDnD_class = PresentationListView
MediaManagerItem.__init__(self, parent, icon, title) MediaManagerItem.__init__(self, parent, self, icon)
self.message_listener = MessageListener(self) self.message_listener = MessageListener(self)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild) QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)

View File

@ -110,10 +110,10 @@ class PowerpointDocument(PresentationDocument):
""" """
Class which holds information and controls a single presentation Class which holds information and controls a single presentation
""" """
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
""" """
Constructor, store information about the file and initialise Constructor, store information about the file and initialise
""" """
log.debug(u'Init Presentation Powerpoint') log.debug(u'Init Presentation Powerpoint')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
@ -125,13 +125,13 @@ class PowerpointDocument(PresentationDocument):
Opens the PowerPoint file using the process created earlier Opens the PowerPoint file using the process created earlier
``presentation`` ``presentation``
The file name of the presentations to run. The file name of the presentations to run.
""" """
log.debug(u'LoadPresentation') log.debug(u'LoadPresentation')
if not self.controller.process or not self.controller.process.Visible: if not self.controller.process or not self.controller.process.Visible:
self.controller.start_process() self.controller.start_process()
try: try:
self.controller.process.Presentations.Open(self.filepath, False, self.controller.process.Presentations.Open(self.filepath, False,
False, True) False, True)
except pywintypes.com_error: except pywintypes.com_error:
return False return False
@ -143,22 +143,24 @@ class PowerpointDocument(PresentationDocument):
def create_thumbnails(self): def create_thumbnails(self):
""" """
Create the thumbnail images for the current presentation. Create the thumbnail images for the current presentation.
Note an alternative and quicker method would be do
Note an alternative and quicker method would be do::
self.presentation.Slides[n].Copy() self.presentation.Slides[n].Copy()
thumbnail = QApplication.clipboard.image() thumbnail = QApplication.clipboard.image()
But for now we want a physical file since it makes
life easier elsewhere However, for the moment, we want a physical file since it makes life
easier elsewhere.
""" """
if self.check_thumbnails(): if self.check_thumbnails():
return return
self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''), self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''),
'png', 320, 240) 'png', 320, 240)
def close_presentation(self): def close_presentation(self):
""" """
Close presentation and clean up objects Close presentation and clean up objects. This is triggered by a new
Triggerent by new object being added to SlideController orOpenLP object being added to SlideController or OpenLP being shut down.
being shut down
""" """
log.debug(u'ClosePresentation') log.debug(u'ClosePresentation')
if self.presentation: if self.presentation:
@ -171,7 +173,7 @@ class PowerpointDocument(PresentationDocument):
def is_loaded(self): def is_loaded(self):
""" """
Returns true if a presentation is loaded Returns ``True`` if a presentation is loaded.
""" """
try: try:
if not self.controller.process.Visible: if not self.controller.process.Visible:
@ -187,7 +189,7 @@ class PowerpointDocument(PresentationDocument):
def is_active(self): def is_active(self):
""" """
Returns true if a presentation is currently active Returns ``True`` if a presentation is currently active.
""" """
if not self.is_loaded(): if not self.is_loaded():
return False return False
@ -202,7 +204,7 @@ class PowerpointDocument(PresentationDocument):
def unblank_screen(self): def unblank_screen(self):
""" """
Unblanks (restores) the presentationn Unblanks (restores) the presentation.
""" """
self.presentation.SlideShowSettings.Run() self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1 self.presentation.SlideShowWindow.View.State = 1
@ -210,13 +212,13 @@ class PowerpointDocument(PresentationDocument):
def blank_screen(self): def blank_screen(self):
""" """
Blanks the screen Blanks the screen.
""" """
self.presentation.SlideShowWindow.View.State = 3 self.presentation.SlideShowWindow.View.State = 3
def is_blank(self): def is_blank(self):
""" """
Returns true if screen is blank Returns ``True`` if screen is blank.
""" """
if self.is_active(): if self.is_active():
return self.presentation.SlideShowWindow.View.State == 3 return self.presentation.SlideShowWindow.View.State == 3
@ -225,14 +227,14 @@ class PowerpointDocument(PresentationDocument):
def stop_presentation(self): def stop_presentation(self):
""" """
Stops the current presentation and hides the output Stops the current presentation and hides the output.
""" """
self.presentation.SlideShowWindow.View.Exit() self.presentation.SlideShowWindow.View.Exit()
if os.name == u'nt': if os.name == u'nt':
def start_presentation(self): def start_presentation(self):
""" """
Starts a presentation from the beginning Starts a presentation from the beginning.
""" """
#SlideShowWindow measures its size/position by points, not pixels #SlideShowWindow measures its size/position by points, not pixels
try: try:
@ -254,40 +256,40 @@ class PowerpointDocument(PresentationDocument):
def get_slide_number(self): def get_slide_number(self):
""" """
Returns the current slide number Returns the current slide number.
""" """
return self.presentation.SlideShowWindow.View.CurrentShowPosition return self.presentation.SlideShowWindow.View.CurrentShowPosition
def get_slide_count(self): def get_slide_count(self):
""" """
Returns total number of slides Returns total number of slides.
""" """
return self.presentation.Slides.Count return self.presentation.Slides.Count
def goto_slide(self, slideno): def goto_slide(self, slideno):
""" """
Moves to a specific slide in the presentation Moves to a specific slide in the presentation.
""" """
self.presentation.SlideShowWindow.View.GotoSlide(slideno) self.presentation.SlideShowWindow.View.GotoSlide(slideno)
def next_step(self): def next_step(self):
""" """
Triggers the next effect of slide on the running presentation Triggers the next effect of slide on the running presentation.
""" """
self.presentation.SlideShowWindow.View.Next() self.presentation.SlideShowWindow.View.Next()
def previous_step(self): def previous_step(self):
""" """
Triggers the previous slide on the running presentation Triggers the previous slide on the running presentation.
""" """
self.presentation.SlideShowWindow.View.Previous() self.presentation.SlideShowWindow.View.Previous()
def get_slide_text(self, slide_no): def get_slide_text(self, slide_no):
""" """
Returns the text on the slide Returns the text on the slide.
``slide_no`` ``slide_no``
The slide the text is required for, starting at 1 The slide the text is required for, starting at 1.
""" """
text = '' text = ''
shapes = self.presentation.Slides(slide_no).Shapes shapes = self.presentation.Slides(slide_no).Shapes
@ -299,10 +301,10 @@ class PowerpointDocument(PresentationDocument):
def get_slide_notes(self, slide_no): def get_slide_notes(self, slide_no):
""" """
Returns the text on the slide Returns the text on the slide.
``slide_no`` ``slide_no``
The slide the notes are required for, starting at 1 The slide the notes are required for, starting at 1.
""" """
text = '' text = ''
shapes = self.presentation.Slides(slide_no).NotesPage.Shapes shapes = self.presentation.Slides(slide_no).NotesPage.Shapes

View File

@ -37,16 +37,21 @@ log = logging.getLogger(__name__)
class PresentationController(object): class PresentationController(object):
""" """
Base class for presentation controllers to inherit from This class is used to control interactions with presentation applications
Class to control interactions with presentations. by creating a runtime environment. This is a base class for presentation
It creates the runtime environment controllers to inherit from.
To create a new controller, take a copy of this file and name it
so it ends in controller.py, i.e. foobarcontroller.py To create a new controller, take a copy of this file and name it so it ends
Make sure it inherits PresentationController with ``controller.py``, i.e. ``foobarcontroller.py``. Make sure it inherits
Then fill in the blanks. If possible try and make sure it loads :class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`,
on all platforms, using for example os.name checks, although and then fill in the blanks. If possible try to make sure it loads on all
__init__, check_available and presentation_deleted should always work. platforms, usually by using :mod:``os.name`` checks, although
See impresscontroller, powerpointcontroller or pptviewcontroller ``__init__``, ``check_available`` and ``presentation_deleted`` should
always be implemented.
See :class:`~openlp.plugins.presentations.lib.impresscontroller.ImpressController`,
:class:`~openlp.plugins.presentations.lib.powerpointcontroller.PowerpointController` or
:class:`~openlp.plugins.presentations.lib.pptviewcontroller.PptviewController`
for examples. for examples.
**Basic Attributes** **Basic Attributes**
@ -70,7 +75,7 @@ class PresentationController(object):
``alsosupports`` ``alsosupports``
Other file types the application can import, although not necessarily Other file types the application can import, although not necessarily
the first choice due to potential incompatibilities the first choice due to potential incompatibilities
**Hook Functions** **Hook Functions**
``kill()`` ``kill()``
@ -246,7 +251,7 @@ class PresentationDocument(object):
``presentation`` ``presentation``
The file name of the presentations to the run. The file name of the presentations to the run.
Returns False if the file could not be opened Returns False if the file could not be opened
""" """
return False return False
@ -387,7 +392,7 @@ class PresentationDocument(object):
if os.path.isfile(file): if os.path.isfile(file):
img = resize_image(file, 320, 240) img = resize_image(file, 320, 240)
img.save(self.get_thumbnail_path(idx, False)) img.save(self.get_thumbnail_path(idx, False))
def get_thumbnail_path(self, slide_no, check_exists): def get_thumbnail_path(self, slide_no, check_exists):
""" """
Returns an image path containing a preview for the requested slide Returns an image path containing a preview for the requested slide

View File

@ -32,20 +32,18 @@ class PresentationTab(SettingsTab):
""" """
PresentationsTab is the Presentations settings tab in the settings dialog. PresentationsTab is the Presentations settings tab in the settings dialog.
""" """
def __init__(self, title, controllers): def __init__(self, title, visible_title, controllers):
""" """
Constructor Constructor
""" """
self.controllers = controllers self.controllers = controllers
SettingsTab.__init__(self, title) SettingsTab.__init__(self, title, visible_title)
def setupUi(self): def setupUi(self):
""" """
Create the controls for the settings tab Create the controls for the settings tab
""" """
self.setObjectName(u'PresentationTab') self.setObjectName(u'PresentationTab')
self.tabTitleVisible = translate('PresentationPlugin.PresentationTab',
'Presentations')
self.PresentationLayout = QtGui.QHBoxLayout(self) self.PresentationLayout = QtGui.QHBoxLayout(self)
self.PresentationLayout.setSpacing(8) self.PresentationLayout.setSpacing(8)
self.PresentationLayout.setMargin(8) self.PresentationLayout.setMargin(8)

View File

@ -30,7 +30,7 @@ presentations from a variety of document formats.
import os import os
import logging import logging
from openlp.core.lib import Plugin, build_icon, translate from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation
from openlp.plugins.presentations.lib import PresentationController, \ from openlp.plugins.presentations.lib import PresentationController, \
PresentationMediaItem, PresentationTab PresentationMediaItem, PresentationTab
@ -60,7 +60,8 @@ class PresentationPlugin(Plugin):
""" """
Create the settings Tab Create the settings Tab
""" """
return PresentationTab(self.name, self.controllers) visible_name = self.getString(StringContent.VisibleName)
return PresentationTab(self.name, visible_name[u'title'], self.controllers)
def initialise(self): def initialise(self):
""" """
@ -143,3 +144,48 @@ class PresentationPlugin(Plugin):
'programs. The choice of available presentation programs is ' 'programs. The choice of available presentation programs is '
'available to the user in a drop down box.') 'available to the user in a drop down box.')
return about_text return about_text
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
## Name PluginList ##
self.textStrings[StringContent.Name] = {
u'singular': translate('PresentationPlugin', 'Presentation'),
u'plural': translate('PresentationPlugin', 'Presentations')
}
## Name for MediaDockManager, SettingsManager ##
self.textStrings[StringContent.VisibleName] = {
u'title': translate('PresentationPlugin', 'Presentations')
}
# Middle Header Bar
## Load Button ##
self.textStrings[StringContent.Load] = {
u'title': translate('PresentationPlugin', 'Load'),
u'tooltip': translate('PresentationPlugin',
'Load a new Presentation')
}
## Delete Button ##
self.textStrings[StringContent.Delete] = {
u'title': translate('PresentationPlugin', 'Delete'),
u'tooltip': translate('PresentationPlugin',
'Delete the selected Presentation')
}
## Preview ##
self.textStrings[StringContent.Preview] = {
u'title': translate('PresentationPlugin', 'Preview'),
u'tooltip': translate('PresentationPlugin',
'Preview the selected Presentation')
}
## Live Button ##
self.textStrings[StringContent.Live] = {
u'title': translate('PresentationPlugin', 'Live'),
u'tooltip': translate('PresentationPlugin',
'Send the selected Presentation live')
}
## Add to service Button ##
self.textStrings[StringContent.Service] = {
u'title': translate('PresentationPlugin', 'Service'),
u'tooltip': translate('PresentationPlugin',
'Add the selected Presentation to the service')
}

View File

@ -32,12 +32,11 @@ class RemoteTab(SettingsTab):
""" """
RemoteTab is the Remotes settings tab in the settings dialog. RemoteTab is the Remotes settings tab in the settings dialog.
""" """
def __init__(self, title): def __init__(self, title, visible_title):
SettingsTab.__init__(self, title) SettingsTab.__init__(self, title, visible_title)
def setupUi(self): def setupUi(self):
self.setObjectName(u'RemoteTab') self.setObjectName(u'RemoteTab')
self.tabTitleVisible = translate('RemotePlugin.RemoteTab', 'Remotes')
self.remoteLayout = QtGui.QFormLayout(self) self.remoteLayout = QtGui.QFormLayout(self)
self.remoteLayout.setSpacing(8) self.remoteLayout.setSpacing(8)
self.remoteLayout.setMargin(8) self.remoteLayout.setMargin(8)

View File

@ -26,7 +26,7 @@
import logging import logging
from openlp.core.lib import Plugin, translate, build_icon from openlp.core.lib import Plugin, StringContent, translate, build_icon
from openlp.plugins.remotes.lib import RemoteTab, HttpServer from openlp.plugins.remotes.lib import RemoteTab, HttpServer
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -65,7 +65,8 @@ class RemotesPlugin(Plugin):
""" """
Create the settings Tab Create the settings Tab
""" """
return RemoteTab(self.name) visible_name = self.getString(StringContent.VisibleName)
return RemoteTab(self.name, visible_name[u'title'])
def about(self): def about(self):
""" """
@ -76,3 +77,17 @@ class RemotesPlugin(Plugin):
'a running version of OpenLP on a different computer via a web ' 'a running version of OpenLP on a different computer via a web '
'browser or through the remote API.') 'browser or through the remote API.')
return about_text return about_text
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
## Name PluginList ##
self.textStrings[StringContent.Name] = {
u'singular': translate('RemotePlugin', 'Remote'),
u'plural': translate('RemotePlugin', 'Remotes')
}
## Name for MediaDockManager, SettingsManager ##
self.textStrings[StringContent.VisibleName] = {
u'title': translate('RemotePlugin', 'Remotes')
}

View File

@ -483,6 +483,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.VerseListWidget.resizeRowsToContents() self.VerseListWidget.resizeRowsToContents()
self.VerseListWidget.repaint() self.VerseListWidget.repaint()
self.tagRows() self.tagRows()
self.VerseEditButton.setEnabled(False)
self.VerseDeleteButton.setEnabled(False)
def onVerseDeleteButtonClicked(self): def onVerseDeleteButtonClicked(self):
self.VerseListWidget.removeRow(self.VerseListWidget.currentRow()) self.VerseListWidget.removeRow(self.VerseListWidget.currentRow())

View File

@ -53,6 +53,9 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
QtCore.QObject.connect(self.verseTextEdit, QtCore.QObject.connect(self.verseTextEdit,
QtCore.SIGNAL(u'cursorPositionChanged()'), QtCore.SIGNAL(u'cursorPositionChanged()'),
self.onCursorPositionChanged) self.onCursorPositionChanged)
QtCore.QObject.connect(self.verseTypeComboBox,
QtCore.SIGNAL(u'currentIndexChanged(int)'),
self.onVerseTypeComboBoxChanged)
self.verse_regex = re.compile(r'---\[([-\w]+):([\d]+)\]---') self.verse_regex = re.compile(r'---\[([-\w]+):([\d]+)\]---')
def contextMenu(self, point): def contextMenu(self, point):
@ -66,27 +69,43 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
self.verseTextEdit.setFocus() self.verseTextEdit.setFocus()
def onInsertButtonClicked(self): def onInsertButtonClicked(self):
if self.verseTextEdit.textCursor().columnNumber() != 0:
self.verseTextEdit.insertPlainText(u'\n')
verse_type = self.verseTypeComboBox.currentIndex() verse_type = self.verseTypeComboBox.currentIndex()
if verse_type == VerseType.Verse: if VerseType.to_string(verse_type) is not None:
self.insertVerse(VerseType.to_string(VerseType.Verse), self.insertVerse(VerseType.to_string(verse_type),
self.verseNumberBox.value()) self.verseNumberBox.value())
elif verse_type == VerseType.Chorus:
self.insertVerse(VerseType.to_string(VerseType.Chorus), def onVerseTypeComboBoxChanged(self):
self.verseNumberBox.value()) """
elif verse_type == VerseType.Bridge: Adjusts the verse number SpinBox in regard to the selected verse type
self.insertVerse(VerseType.to_string(VerseType.Bridge)) and the cursor's position.
elif verse_type == VerseType.PreChorus: """
self.insertVerse(VerseType.to_string(VerseType.PreChorus)) position = self.verseTextEdit.textCursor().position()
elif verse_type == VerseType.Intro: text = unicode(self.verseTextEdit.toPlainText())
self.insertVerse(VerseType.to_string(VerseType.Intro)) verse_type = VerseType.to_string(self.verseTypeComboBox.currentIndex())
elif verse_type == VerseType.Ending: if not text:
self.insertVerse(VerseType.to_string(VerseType.Ending)) return
elif verse_type == VerseType.Other: position = text.rfind(u'---[%s' % verse_type, 0, position)
self.insertVerse(VerseType.to_string(VerseType.Other)) if position == -1:
self.verseNumberBox.setValue(1)
return
text = text[position:]
position = text.find(u']---')
if position == -1:
return
text = text[:position + 4]
match = self.verse_regex.match(text)
if match:
verse_type = match.group(1)
verse_number = int(match.group(2))
verse_type_index = VerseType.from_string(verse_type)
if verse_type_index is not None:
self.verseNumberBox.setValue(verse_number)
def onCursorPositionChanged(self): def onCursorPositionChanged(self):
"""
Determines the previous verse type and number in regard to the cursor's
position and adjusts the ComboBox and SpinBox to these values.
"""
position = self.verseTextEdit.textCursor().position() position = self.verseTextEdit.textCursor().position()
text = unicode(self.verseTextEdit.toPlainText()) text = unicode(self.verseTextEdit.toPlainText())
if not text: if not text:

View File

@ -112,6 +112,12 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
QtCore.QObject.connect(self.ewBrowseButton, QtCore.QObject.connect(self.ewBrowseButton,
QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
self.onEWBrowseButtonClicked) self.onEWBrowseButtonClicked)
QtCore.QObject.connect(self.songBeamerAddButton,
QtCore.SIGNAL(u'clicked()'),
self.onSongBeamerAddButtonClicked)
QtCore.QObject.connect(self.songBeamerRemoveButton,
QtCore.SIGNAL(u'clicked()'),
self.onSongBeamerRemoveButtonClicked)
QtCore.QObject.connect(self.cancelButton, QtCore.QObject.connect(self.cancelButton,
QtCore.SIGNAL(u'clicked(bool)'), QtCore.SIGNAL(u'clicked(bool)'),
self.onCancelButtonClicked) self.onCancelButtonClicked)
@ -227,23 +233,39 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
'file to import from.')) 'file to import from.'))
self.ewBrowseButton.setFocus() self.ewBrowseButton.setFocus()
return False return False
elif source_format == SongFormat.SongBeamer:
if self.songBeamerFileListWidget.count() == 0:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.ImportWizardForm',
'No SongBeamer File Selected'),
translate('SongsPlugin.ImportWizardForm',
'You need to add at least one SongBeamer '
'file to import from.'))
self.songBeamerAddButton.setFocus()
return False
return True return True
elif self.currentId() == 2: elif self.currentId() == 2:
# Progress page # Progress page
return True return True
def getFileName(self, title, editbox): def getFileName(self, title, editbox,
filters = '%s (*)' % translate('SongsPlugin.ImportWizardForm',
'All Files')):
filename = QtGui.QFileDialog.getOpenFileName(self, title, filename = QtGui.QFileDialog.getOpenFileName(self, title,
SettingsManager.get_last_dir(self.plugin.settingsSection, 1)) SettingsManager.get_last_dir(self.plugin.settingsSection, 1),
filters)
if filename: if filename:
editbox.setText(filename) editbox.setText(filename)
SettingsManager.set_last_dir( SettingsManager.set_last_dir(
self.plugin.settingsSection, self.plugin.settingsSection,
os.path.split(unicode(filename))[0], 1) os.path.split(unicode(filename))[0], 1)
def getFiles(self, title, listbox): def getFiles(self, title, listbox,
filters = u'%s (*)' % translate('SongsPlugin.ImportWizardForm',
'All Files')):
filenames = QtGui.QFileDialog.getOpenFileNames(self, title, filenames = QtGui.QFileDialog.getOpenFileNames(self, title,
SettingsManager.get_last_dir(self.plugin.settingsSection, 1)) SettingsManager.get_last_dir(self.plugin.settingsSection, 1),
filters)
if filenames: if filenames:
listbox.addItems(filenames) listbox.addItems(filenames)
SettingsManager.set_last_dir( SettingsManager.set_last_dir(
@ -265,14 +287,24 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.getFileName( self.getFileName(
translate('SongsPlugin.ImportWizardForm', translate('SongsPlugin.ImportWizardForm',
'Select OpenLP 2.0 Database File'), 'Select OpenLP 2.0 Database File'),
self.openLP2FilenameEdit self.openLP2FilenameEdit,
u'%s (*.sqlite);;%s (*)'
% (translate('SongsPlugin.ImportWizardForm',
'OpenLP 2.0 Databases'),
translate('SongsPlugin.ImportWizardForm',
'All Files'))
) )
def onOpenLP1BrowseButtonClicked(self): def onOpenLP1BrowseButtonClicked(self):
self.getFileName( self.getFileName(
translate('SongsPlugin.ImportWizardForm', translate('SongsPlugin.ImportWizardForm',
'Select openlp.org 1.x Database File'), 'Select openlp.org 1.x Database File'),
self.openLP1FilenameEdit self.openLP1FilenameEdit,
u'%s (*.olp);;%s (*)'
% (translate('SongsPlugin.ImportWizardForm',
'openlp.org v1.x Databases'),
translate('SongsPlugin.ImportWizardForm',
'All Files'))
) )
#def onOpenLyricsAddButtonClicked(self): #def onOpenLyricsAddButtonClicked(self):
@ -299,7 +331,12 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.getFiles( self.getFiles(
translate('SongsPlugin.ImportWizardForm', translate('SongsPlugin.ImportWizardForm',
'Select Words of Worship Files'), 'Select Words of Worship Files'),
self.wordsOfWorshipFileListWidget self.wordsOfWorshipFileListWidget,
u'%s (*.wsg *.wow-song);;%s (*)'
% (translate('SongsPlugin.ImportWizardForm',
'Words Of Worship Song Files'),
translate('SongsPlugin.ImportWizardForm',
'All Files'))
) )
def onWordsOfWorshipRemoveButtonClicked(self): def onWordsOfWorshipRemoveButtonClicked(self):
@ -319,7 +356,12 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.getFiles( self.getFiles(
translate('SongsPlugin.ImportWizardForm', translate('SongsPlugin.ImportWizardForm',
'Select Songs of Fellowship Files'), 'Select Songs of Fellowship Files'),
self.songsOfFellowshipFileListWidget self.songsOfFellowshipFileListWidget,
u'%s (*.rtf);;%s (*)'
% (translate('SongsPlugin.ImportWizardForm',
'Songs Of Felloship Song Files'),
translate('SongsPlugin.ImportWizardForm',
'All Files'))
) )
def onSongsOfFellowshipRemoveButtonClicked(self): def onSongsOfFellowshipRemoveButtonClicked(self):
@ -342,6 +384,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.ewFilenameEdit self.ewFilenameEdit
) )
def onSongBeamerAddButtonClicked(self):
self.getFiles(
translate('SongsPlugin.ImportWizardForm',
'Select SongBeamer Files'),
self.songBeamerFileListWidget
)
def onSongBeamerRemoveButtonClicked(self):
self.removeSelectedItems(self.songBeamerFileListWidget)
def onCancelButtonClicked(self, checked): def onCancelButtonClicked(self, checked):
""" """
Stop the import on pressing the cancel button. Stop the import on pressing the cancel button.
@ -373,6 +425,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.songsOfFellowshipFileListWidget.clear() self.songsOfFellowshipFileListWidget.clear()
self.genericFileListWidget.clear() self.genericFileListWidget.clear()
self.ewFilenameEdit.setText(u'') self.ewFilenameEdit.setText(u'')
self.songBeamerFileListWidget.clear()
#self.csvFilenameEdit.setText(u'') #self.csvFilenameEdit.setText(u'')
def incrementProgressBar(self, status_text, increment=1): def incrementProgressBar(self, status_text, increment=1):
@ -448,6 +501,12 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
importer = self.plugin.importSongs(SongFormat.EasyWorship, importer = self.plugin.importSongs(SongFormat.EasyWorship,
filename=unicode(self.ewFilenameEdit.text()) filename=unicode(self.ewFilenameEdit.text())
) )
elif source_format == SongFormat.SongBeamer:
# Import SongBeamer songs
importer = self.plugin.importSongs(SongFormat.SongBeamer,
filenames=self.getListOfFiles(
self.songBeamerFileListWidget)
)
success = importer.do_import() success = importer.do_import()
if success: if success:
# reload songs # reload songs

View File

@ -116,6 +116,8 @@ class Ui_SongImportWizard(object):
self.addMultiFileSelectItem(u'generic', None, True) self.addMultiFileSelectItem(u'generic', None, True)
# EasyWorship # EasyWorship
self.addSingleFileSelectItem(u'ew') self.addSingleFileSelectItem(u'ew')
# Words of Worship
self.addMultiFileSelectItem(u'songBeamer')
# Commented out for future use. # Commented out for future use.
# self.addSingleFileSelectItem(u'csv', u'CSV') # self.addSingleFileSelectItem(u'csv', u'CSV')
self.sourceLayout.addWidget(self.formatStackedWidget) self.sourceLayout.addWidget(self.formatStackedWidget)
@ -180,6 +182,8 @@ class Ui_SongImportWizard(object):
'Generic Document/Presentation')) 'Generic Document/Presentation'))
self.formatComboBox.setItemText(8, self.formatComboBox.setItemText(8,
translate('SongsPlugin.ImportWizardForm', 'EasyWorship')) translate('SongsPlugin.ImportWizardForm', 'EasyWorship'))
self.formatComboBox.setItemText(9,
translate('SongsPlugin.ImportWizardForm', 'SongBeamer'))
# self.formatComboBox.setItemText(9, # self.formatComboBox.setItemText(9,
# translate('SongsPlugin.ImportWizardForm', 'CSV')) # translate('SongsPlugin.ImportWizardForm', 'CSV'))
self.openLP2FilenameLabel.setText( self.openLP2FilenameLabel.setText(
@ -236,6 +240,10 @@ class Ui_SongImportWizard(object):
translate('SongsPlugin.ImportWizardForm', 'Filename:')) translate('SongsPlugin.ImportWizardForm', 'Filename:'))
self.ewBrowseButton.setText( self.ewBrowseButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Browse...')) translate('SongsPlugin.ImportWizardForm', 'Browse...'))
self.songBeamerAddButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
self.songBeamerRemoveButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
# self.csvFilenameLabel.setText( # self.csvFilenameLabel.setText(
# translate('SongsPlugin.ImportWizardForm', 'Filename:')) # translate('SongsPlugin.ImportWizardForm', 'Filename:'))
# self.csvBrowseButton.setText( # self.csvBrowseButton.setText(

View File

@ -38,9 +38,9 @@ class CCLIFileImportError(Exception):
class CCLIFileImport(SongImport): class CCLIFileImport(SongImport):
""" """
The :class:`CCLIFileImport` class provides OpenLP with the The :class:`CCLIFileImport` class provides OpenLP with the ability to
ability to import CCLI SongSelect song files in both .txt and import CCLI SongSelect song files in both .txt and .usr formats.
.usr formats. See http://www.ccli.com See http://www.ccli.com/ for more details.
""" """
def __init__(self, manager, **kwargs): def __init__(self, manager, **kwargs):
@ -49,6 +49,7 @@ class CCLIFileImport(SongImport):
``manager`` ``manager``
The song manager for the running OpenLP installation. The song manager for the running OpenLP installation.
``filenames`` ``filenames``
The files to be imported. The files to be imported.
""" """
@ -97,7 +98,7 @@ class CCLIFileImport(SongImport):
def do_import_usr_file(self, textList): def do_import_usr_file(self, textList):
""" """
The :method:`do_import_usr_file` method provides OpenLP The :func:`do_import_usr_file` method provides OpenLP
with the ability to import CCLI SongSelect songs in with the ability to import CCLI SongSelect songs in
*USR* file format *USR* file format
@ -105,6 +106,7 @@ class CCLIFileImport(SongImport):
An array of strings containing the usr file content. An array of strings containing the usr file content.
**SongSelect .usr file format** **SongSelect .usr file format**
``[File]`` ``[File]``
USR file format first line USR file format first line
``Type=`` ``Type=``
@ -140,6 +142,7 @@ class CCLIFileImport(SongImport):
Contains the songs various lyrics in order as shown by the Contains the songs various lyrics in order as shown by the
*Fields* description *Fields* description
e.g. *Words=Above all powers....* [/n = CR, /n/t = CRLF] e.g. *Words=Above all powers....* [/n = CR, /n/t = CRLF]
""" """
log.debug(u'USR file text: %s', textList) log.debug(u'USR file text: %s', textList)
lyrics = [] lyrics = []
@ -191,48 +194,32 @@ class CCLIFileImport(SongImport):
def do_import_txt_file(self, textList): def do_import_txt_file(self, textList):
""" """
The :method:`do_import_txt_file` method provides OpenLP The :func:`do_import_txt_file` method provides OpenLP
with the ability to import CCLI SongSelect songs in with the ability to import CCLI SongSelect songs in
*TXT* file format *TXT* file format
``textList`` ``textList``
An array of strings containing the txt file content. An array of strings containing the txt file content.
**SongSelect .txt file format** SongSelect .txt file format::
``Song Title`` Song Title # Contains the song title
Contains the song title <Empty line>
Verse type and number # e.g. Verse 1, Chorus 1
<Empty line> Verse lyrics
<Empty line>
``Title of following verse/chorus and number`` <Empty line>
e.g. Verse 1, Chorus 1 Verse type and number (repeats)
Verse lyrics
``Verse/Chorus lyrics`` <Empty line>
<Empty line>
<Empty line> Song CCLI number # e.g. CCLI Number (e.g.CCLI-Liednummer: 2672885)
Song copyright # e.g. © 1999 Integrity's Hosanna! Music | LenSongs Publishing
<Empty line> Song authors # e.g. Lenny LeBlanc | Paul Baloche
Licencing info # e.g. For use solely with the SongSelect Terms of Use.
``Title of next verse/chorus (repeats)``
``Verse/Chorus lyrics``
<Empty line>
<Empty line>
``Song CCLI Number``
e.g. CCLI Number (e.g.CCLI-Liednummer: 2672885)
``Song Copyright``
e.g. © 1999 Integrity's Hosanna! Music | LenSongs Publishing
``Song Authors``
e.g. Lenny LeBlanc | Paul Baloche
``Licencing info``
e.g. For use solely with the SongSelect Terms of Use.
All rights Reserved. www.ccli.com All rights Reserved. www.ccli.com
``CCLI Licence number of user`` CCLI Licence number of user # e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14
e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14
""" """
log.debug(u'TXT file text: %s', textList) log.debug(u'TXT file text: %s', textList)
self.set_defaults() self.set_defaults()

View File

@ -35,7 +35,7 @@ import struct
from openlp.core.lib import translate from openlp.core.lib import translate
from songimport import SongImport from songimport import SongImport
def strip_rtf(blob): def strip_rtf(blob, encoding):
depth = 0 depth = 0
control = False control = False
clear_text = [] clear_text = []
@ -69,12 +69,42 @@ def strip_rtf(blob):
if control_str == 'par' or control_str == 'line': if control_str == 'par' or control_str == 'line':
clear_text.append(u'\n') clear_text.append(u'\n')
elif control_str == 'tab': elif control_str == 'tab':
clear_text.append(u'\n') clear_text.append(u'\t')
# Prefer the encoding specified by the RTF data to that
# specified by the Paradox table header
# West European encoding
elif control_str == 'fcharset0':
encoding = u'cp1252'
# Greek encoding
elif control_str == 'fcharset161':
encoding = u'cp1253'
# Turkish encoding
elif control_str == 'fcharset162':
encoding = u'cp1254'
# Vietnamese encoding
elif control_str == 'fcharset163':
encoding = u'cp1258'
# Hebrew encoding
elif control_str == 'fcharset177':
encoding = u'cp1255'
# Arabic encoding
elif control_str == 'fcharset178':
encoding = u'cp1256'
# Baltic encoding
elif control_str == 'fcharset186':
encoding = u'cp1257'
# Cyrillic encoding
elif control_str == 'fcharset204':
encoding = u'cp1251'
# Thai encoding
elif control_str == 'fcharset222':
encoding = u'cp874'
# Central+East European encoding
elif control_str == 'fcharset238':
encoding = u'cp1250'
elif control_str[0] == '\'': elif control_str[0] == '\'':
# Really should take RTF character set into account but
# for now assume ANSI (Windows-1252) and call it good
s = chr(int(control_str[1:3], 16)) s = chr(int(control_str[1:3], 16))
clear_text.append(s.decode(u'windows-1252')) clear_text.append(s.decode(encoding))
del control_word[:] del control_word[:]
if c == '\\' and new_control: if c == '\\' and new_control:
control = True control = True
@ -126,6 +156,30 @@ class EasyWorshipSongImport(SongImport):
db_file.close() db_file.close()
self.memo_file.close() self.memo_file.close()
return False return False
# Take a stab at how text is encoded
self.encoding = u'cp1252'
db_file.seek(106)
code_page, = struct.unpack('<h', db_file.read(2))
if code_page == 852:
self.encoding = u'cp1250'
# The following codepage to actual encoding mappings have not been
# observed, but merely guessed. Actual example files are needed.
#if code_page == 737:
# self.encoding = u'cp1253'
#if code_page == 775:
# self.encoding = u'cp1257'
#if code_page == 855:
# self.encoding = u'cp1251'
#if code_page == 857:
# self.encoding = u'cp1254'
#if code_page == 866:
# self.encoding = u'cp1251'
#if code_page == 869:
# self.encoding = u'cp1253'
#if code_page == 862:
# self.encoding = u'cp1255'
#if code_page == 874:
# self.encoding = u'cp874'
# There does not appear to be a _reliable_ way of getting the number # There does not appear to be a _reliable_ way of getting the number
# of songs/records, so let's use file blocks for measuring progress. # of songs/records, so let's use file blocks for measuring progress.
total_blocks = (db_size - header_size) / (block_size * 1024) total_blocks = (db_size - header_size) / (block_size * 1024)
@ -204,7 +258,7 @@ class EasyWorshipSongImport(SongImport):
self.add_author(author_name.strip()) self.add_author(author_name.strip())
if words: if words:
# Format the lyrics # Format the lyrics
words = strip_rtf(words) words = strip_rtf(words, self.encoding)
for verse in words.split(u'\n\n'): for verse in words.split(u'\n\n'):
self.add_verse(verse.strip(), u'V') self.add_verse(verse.strip(), u'V')
if self.stop_import_flag: if self.stop_import_flag:
@ -263,7 +317,7 @@ class EasyWorshipSongImport(SongImport):
# Format the field depending on the field type # Format the field depending on the field type
if field_desc.type == 1: if field_desc.type == 1:
# string # string
return field.rstrip('\0').decode(u'windows-1252') return field.rstrip('\0').decode(self.encoding)
elif field_desc.type == 3: elif field_desc.type == 3:
# 16-bit int # 16-bit int
return field ^ 0x8000 return field ^ 0x8000

View File

@ -29,6 +29,7 @@ from olpimport import OpenLPSongImport
from wowimport import WowImport from wowimport import WowImport
from cclifileimport import CCLIFileImport from cclifileimport import CCLIFileImport
from ewimport import EasyWorshipSongImport from ewimport import EasyWorshipSongImport
from songbeamerimport import SongBeamerImport
# Imports that might fail # Imports that might fail
try: try:
from olp1import import OpenLP1SongImport from olp1import import OpenLP1SongImport
@ -64,6 +65,7 @@ class SongFormat(object):
Generic = 7 Generic = 7
#CSV = 8 #CSV = 8
EasyWorship = 8 EasyWorship = 8
SongBeamer = 9
@staticmethod @staticmethod
def get_class(format): def get_class(format):
@ -89,6 +91,8 @@ class SongFormat(object):
return CCLIFileImport return CCLIFileImport
elif format == SongFormat.EasyWorship: elif format == SongFormat.EasyWorship:
return EasyWorshipSongImport return EasyWorshipSongImport
elif format == SongFormat.SongBeamer:
return SongBeamerImport
# else: # else:
return None return None
@ -106,7 +110,8 @@ class SongFormat(object):
SongFormat.CCLI, SongFormat.CCLI,
SongFormat.SongsOfFellowship, SongFormat.SongsOfFellowship,
SongFormat.Generic, SongFormat.Generic,
SongFormat.EasyWorship SongFormat.EasyWorship,
SongFormat.SongBeamer
] ]
@staticmethod @staticmethod

View File

@ -48,12 +48,10 @@ class SongMediaItem(MediaManagerItem):
""" """
log.info(u'Song Media Item loaded') log.info(u'Song Media Item loaded')
def __init__(self, parent, icon, title): def __init__(self, parent, plugin, icon):
self.PluginNameShort = u'Song'
self.pluginNameVisible = translate('SongsPlugin.MediaItem', 'Song')
self.IconPath = u'songs/song' self.IconPath = u'songs/song'
self.ListViewWithDnD_class = SongListView self.ListViewWithDnD_class = SongListView
MediaManagerItem.__init__(self, parent, icon, title) MediaManagerItem.__init__(self, parent, self, icon)
self.edit_song_form = EditSongForm(self, self.parent.manager) self.edit_song_form = EditSongForm(self, self.parent.manager)
self.singleServiceItem = False self.singleServiceItem = False
#self.edit_song_form = EditSongForm(self.parent.manager, self) #self.edit_song_form = EditSongForm(self.parent.manager, self)
@ -62,6 +60,7 @@ class SongMediaItem(MediaManagerItem):
# Holds information about whether the edit is remotly triggered and # Holds information about whether the edit is remotly triggered and
# which Song is required. # which Song is required.
self.remoteSong = -1 self.remoteSong = -1
self.editItem = None
def requiredIcons(self): def requiredIcons(self):
MediaManagerItem.requiredIcons(self) MediaManagerItem.requiredIcons(self)
@ -125,7 +124,7 @@ class SongMediaItem(MediaManagerItem):
QtCore.SIGNAL(u'textChanged(const QString&)'), QtCore.SIGNAL(u'textChanged(const QString&)'),
self.onSearchTextEditChanged) self.onSearchTextEditChanged)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'songs_load_list'), self.onSearchTextButtonClick) QtCore.SIGNAL(u'songs_load_list'), self.onSongListLoad)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.configUpdated) QtCore.SIGNAL(u'config_updated'), self.configUpdated)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
@ -139,6 +138,12 @@ class SongMediaItem(MediaManagerItem):
self.searchAsYouType = QtCore.QSettings().value( self.searchAsYouType = QtCore.QSettings().value(
self.settingsSection + u'/search as type', self.settingsSection + u'/search as type',
QtCore.QVariant(u'False')).toBool() QtCore.QVariant(u'False')).toBool()
self.updateServiceOnEdit = QtCore.QSettings().value(
self.settingsSection + u'/update service on edit',
QtCore.QVariant(u'False')).toBool()
self.AddSongFromServide = QtCore.QSettings().value(
self.settingsSection + u'/add song from service',
QtCore.QVariant(u'True')).toBool()
def retranslateUi(self): def retranslateUi(self):
self.SearchTextLabel.setText( self.SearchTextLabel.setText(
@ -181,14 +186,26 @@ class SongMediaItem(MediaManagerItem):
Author.display_name.like(u'%' + search_keywords + u'%'), Author.display_name.like(u'%' + search_keywords + u'%'),
Author.display_name.asc()) Author.display_name.asc())
self.displayResultsAuthor(search_results) self.displayResultsAuthor(search_results)
#Called to redisplay the song list screen edith from a search
#or from the exit of the Song edit dialog. If remote editing is active def onSongListLoad(self):
#Trigger it and clean up so it will not update again. """
Handle the exit from the edit dialog and trigger remote updates
of songs
"""
# Called to redisplay the song list screen edit from a search
# or from the exit of the Song edit dialog. If remote editing is active
# Trigger it and clean up so it will not update again.
if self.remoteTriggered == u'L': if self.remoteTriggered == u'L':
self.onAddClick() self.onAddClick()
if self.remoteTriggered == u'P': if self.remoteTriggered == u'P':
self.onPreviewClick() self.onPreviewClick()
# Push edits to the service manager to update items
if self.editItem and self.updateServiceOnEdit and \
not self.remoteTriggered:
item = self.buildServiceItem(self.editItem)
self.parent.serviceManager.replaceServiceItem(item)
self.onRemoteEditClear() self.onRemoteEditClear()
self.onSearchTextButtonClick()
def displayResultsSong(self, searchresults): def displayResultsSong(self, searchresults):
log.debug(u'display results Song') log.debug(u'display results Song')
@ -273,8 +290,8 @@ class SongMediaItem(MediaManagerItem):
if check_item_selected(self.listView, if check_item_selected(self.listView,
translate('SongsPlugin.MediaItem', translate('SongsPlugin.MediaItem',
'You must select an item to edit.')): 'You must select an item to edit.')):
item = self.listView.currentItem() self.editItem = self.listView.currentItem()
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] item_id = (self.editItem.data(QtCore.Qt.UserRole)).toInt()[0]
self.edit_song_form.loadSong(item_id, False) self.edit_song_form.loadSong(item_id, False)
self.edit_song_form.exec_() self.edit_song_form.exec_()
@ -324,6 +341,8 @@ class SongMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.AllowsEdit) service_item.add_capability(ItemCapabilities.AllowsEdit)
service_item.add_capability(ItemCapabilities.AllowsPreview) service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop) service_item.add_capability(ItemCapabilities.AllowsLoop)
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
service_item.add_capability(ItemCapabilities.AddIfNewItem)
song = self.parent.manager.get_object(Song, item_id) song = self.parent.manager.get_object(Song, item_id)
service_item.theme = song.theme_name service_item.theme = song.theme_name
service_item.editId = item_id service_item.editId = item_id
@ -370,4 +389,31 @@ class SongMediaItem(MediaManagerItem):
service_item.audit = [ service_item.audit = [
song.title, author_audit, song.copyright, unicode(song.ccli_number) song.title, author_audit, song.copyright, unicode(song.ccli_number)
] ]
service_item.data_string = {u'title':song.search_title, u'authors':author_list}
return True return True
def serviceLoad(self, item):
"""
Triggered by a song being loaded by the service item
"""
if item.data_string:
search_results = self.parent.manager.get_all_objects(Song,
Song.search_title.like(u'%' +
item.data_string[u'title'].split(u'@')[0] + u'%'),
Song.search_title.asc())
author_list = item.data_string[u'authors'].split(u',')
editId = 0
uuid = 0
if search_results:
for song in search_results:
count = 0
for author in song.authors:
if author.display_name in author_list:
count += 1
if count == len(author_list):
editId = song.id
uuid = item._uuid
if editId != 0:
Receiver.send_message(u'service_item_update',
u'%s:%s' %(editId, uuid))

View File

@ -0,0 +1,236 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, Frode Woldsund #
# --------------------------------------------------------------------------- #
# 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:`songbeamerimport` module provides the functionality for importing
SongBeamer songs into the OpenLP database.
"""
import logging
import os
import re
import chardet
import codecs
from openlp.plugins.songs.lib.songimport import SongImport
log = logging.getLogger(__name__)
class SongBeamerTypes(object):
MarkTypes = {
u'Refrain': u'C',
u'Chorus': u'C',
u'Vers': u'V',
u'Verse': u'V',
u'Strophe': u'V',
u'Intro': u'I',
u'Coda': u'E',
u'Ending': u'E',
u'Bridge': u'B',
u'Interlude': u'B',
u'Zwischenspiel': u'B',
u'Pre-Chorus': u'P',
u'Pre-Refrain': u'P',
u'Pre-Bridge': u'O',
u'Pre-Coda': u'O',
u'Unbekannt': u'O',
u'Unknown': u'O'
}
class SongBeamerImport(SongImport):
"""
Import Song Beamer files(s)
Song Beamer file format is text based
in the beginning are one or more control tags written
"""
def __init__(self, master_manager, **kwargs):
"""
Initialise the import.
``master_manager``
The song manager for the running OpenLP installation.
"""
SongImport.__init__(self, master_manager)
self.master_manager = master_manager
if kwargs.has_key(u'filename'):
self.import_source = kwargs[u'filename']
if kwargs.has_key(u'filenames'):
self.import_source = kwargs[u'filenames']
log.debug(self.import_source)
def do_import(self):
"""
Recieve a single file, or a list of files to import.
"""
if isinstance(self.import_source, list):
self.import_wizard.importProgressBar.setMaximum(
len(self.import_source))
for file in self.import_source:
# TODO: check that it is a valid SongBeamer file
self.current_verse = u''
self.current_verse_type = u'V'
self.file_name = os.path.split(file)[1]
self.import_wizard.incrementProgressBar(
"Importing %s" % (self.file_name), 0)
if os.path.isfile(file):
detect_file = open(file, u'r')
details = chardet.detect(detect_file.read(2048))
detect_file.close()
infile = codecs.open(file, u'r', details['encoding'])
self.songData = infile.readlines()
else:
return False
for line in self.songData:
line = line.strip()
if line.startswith('#'):
log.debug(u'find tag: %s' % line)
if not self.parse_tags(line):
return False
elif line.startswith('---'):
log.debug(u'find ---')
if len(self.current_verse) > 0:
self.add_verse(self.current_verse,
self.current_verse_type)
self.current_verse = u''
self.current_verse_type = u'V'
self.read_verse = True
self.verse_start = True
elif self.read_verse:
if self.verse_start:
self.check_verse_marks(line)
self.verse_start = False
else:
self.current_verse += u'%s\n' % line
if len(self.current_verse) > 0:
self.add_verse(self.current_verse, self.current_verse_type)
self.finish()
self.import_wizard.incrementProgressBar(
"Importing %s" % (self.file_name))
return True
def parse_tags(self, line):
tag_val = line.split('=')
if len(tag_val[0]) == 0 or \
len(tag_val[1]) == 0:
return True
if tag_val[0] == '#(c)':
self.add_copyright(tag_val[1])
elif tag_val[0] == '#AddCopyrightInfo':
pass
elif tag_val[0] == '#Author':
#TODO split Authors
self.add_author(tag_val[1])
elif tag_val[0] == '#BackgroundImage':
pass
elif tag_val[0] == '#Bible':
pass
elif tag_val[0] == '#Categories':
self.topics = line.split(',')
elif tag_val[0] == '#CCLI':
self.ccli_number = tag_val[1]
elif tag_val[0] == '#Chords':
pass
elif tag_val[0] == '#ChurchSongID':
pass
elif tag_val[0] == '#ColorChords':
pass
elif tag_val[0] == '#Comments':
self.comments = tag_val[1]
elif tag_val[0] == '#Editor':
pass
elif tag_val[0] == '#Font':
pass
elif tag_val[0] == '#FontLang2':
pass
elif tag_val[0] == '#FontSize':
pass
elif tag_val[0] == '#Format':
pass
elif tag_val[0] == '#Format_PreLine':
pass
elif tag_val[0] == '#Format_PrePage':
pass
elif tag_val[0] == '#ID':
pass
elif tag_val[0] == '#Key':
pass
elif tag_val[0] == '#Keywords':
pass
elif tag_val[0] == '#LangCount':
pass
elif tag_val[0] == '#Melody':
#TODO split Authors
self.add_author(tag_val[1])
elif tag_val[0] == '#NatCopyright':
pass
elif tag_val[0] == '#OTitle':
pass
elif tag_val[0] == '#OutlineColor':
pass
elif tag_val[0] == '#OutlinedFont':
pass
elif tag_val[0] == '#QuickFind':
pass
elif tag_val[0] == '#Rights':
song_book_pub = tag_val[1]
elif tag_val[0] == '#Songbook':
book_num = tag_val[1].split(' / ')
self.song_book_name = book_num[0]
if len(book_num) == book_num[1]:
self.song_number = u''
elif tag_val[0] == '#Speed':
pass
elif tag_val[0] == '#TextAlign':
pass
elif tag_val[0] == '#Title':
self.title = u'%s' % tag_val[1]
elif tag_val[0] == '#TitleAlign':
pass
elif tag_val[0] == '#TitleFontSize':
pass
elif tag_val[0] == '#TitleLang2':
pass
elif tag_val[0] == '#TitleLang3':
pass
elif tag_val[0] == '#TitleLang4':
pass
elif tag_val[0] == '#Translation':
pass
elif tag_val[0] == '#Transpose':
pass
elif tag_val[0] == '#TransposeAccidental':
pass
elif tag_val[0] == '#Version':
pass
else:
pass
return True
def check_verse_marks(self, line):
marks = line.split(' ')
if len(marks) <= 2 and \
marks[0] in SongBeamerTypes.MarkTypes:
self.current_verse_type = SongBeamerTypes.MarkTypes[marks[0]]
if len(marks) == 2:
#TODO: may check, because of only digits are allowed
self.current_verse_type += marks[1]

View File

@ -32,12 +32,11 @@ class SongsTab(SettingsTab):
""" """
SongsTab is the Songs settings tab in the settings dialog. SongsTab is the Songs settings tab in the settings dialog.
""" """
def __init__(self, title): def __init__(self, title, visible_title):
SettingsTab.__init__(self, title) SettingsTab.__init__(self, title, visible_title)
def setupUi(self): def setupUi(self):
self.setObjectName(u'SongsTab') self.setObjectName(u'SongsTab')
self.tabTitleVisible = translate('SongsPlugin.SongsTab', 'Songs')
self.SongsLayout = QtGui.QFormLayout(self) self.SongsLayout = QtGui.QFormLayout(self)
self.SongsLayout.setSpacing(8) self.SongsLayout.setSpacing(8)
self.SongsLayout.setMargin(8) self.SongsLayout.setMargin(8)
@ -52,8 +51,14 @@ class SongsTab(SettingsTab):
self.SearchAsTypeCheckBox.setObjectName(u'SearchAsTypeCheckBox') self.SearchAsTypeCheckBox.setObjectName(u'SearchAsTypeCheckBox')
self.SongsModeLayout.addWidget(self.SearchAsTypeCheckBox) self.SongsModeLayout.addWidget(self.SearchAsTypeCheckBox)
self.SongBarActiveCheckBox = QtGui.QCheckBox(self.SongsModeGroupBox) self.SongBarActiveCheckBox = QtGui.QCheckBox(self.SongsModeGroupBox)
self.SongBarActiveCheckBox.setObjectName(u'SearchAsTypeCheckBox') self.SongBarActiveCheckBox.setObjectName(u'SongBarActiveCheckBox')
self.SongsModeLayout.addWidget(self.SongBarActiveCheckBox) self.SongsModeLayout.addWidget(self.SongBarActiveCheckBox)
self.SongUpdateOnEditCheckBox = QtGui.QCheckBox(self.SongsModeGroupBox)
self.SongUpdateOnEditCheckBox.setObjectName(u'SongUpdateOnEditCheckBox')
self.SongsModeLayout.addWidget(self.SongUpdateOnEditCheckBox)
self.SongAddFromServiceCheckBox = QtGui.QCheckBox(self.SongsModeGroupBox)
self.SongAddFromServiceCheckBox.setObjectName(u'SongAddFromServiceCheckBox')
self.SongsModeLayout.addWidget(self.SongAddFromServiceCheckBox)
self.SongsLayout.setWidget( self.SongsLayout.setWidget(
0, QtGui.QFormLayout.LabelRole, self.SongsModeGroupBox) 0, QtGui.QFormLayout.LabelRole, self.SongsModeGroupBox)
QtCore.QObject.connect(self.SearchAsTypeCheckBox, QtCore.QObject.connect(self.SearchAsTypeCheckBox,
@ -61,7 +66,13 @@ class SongsTab(SettingsTab):
self.onSearchAsTypeCheckBoxChanged) self.onSearchAsTypeCheckBoxChanged)
QtCore.QObject.connect(self.SongBarActiveCheckBox, QtCore.QObject.connect(self.SongBarActiveCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'), QtCore.SIGNAL(u'stateChanged(int)'),
self.SongBarActiveCheckBoxChanged) self.onSongBarActiveCheckBoxChanged)
QtCore.QObject.connect(self.SongUpdateOnEditCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onSongUpdateOnEditCheckBoxChanged)
QtCore.QObject.connect(self.SongBarActiveCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onSongAddFromServiceCheckBoxChanged)
def retranslateUi(self): def retranslateUi(self):
self.SongsModeGroupBox.setTitle( self.SongsModeGroupBox.setTitle(
@ -70,6 +81,10 @@ class SongsTab(SettingsTab):
translate('SongsPlugin.SongsTab', 'Enable search as you type')) translate('SongsPlugin.SongsTab', 'Enable search as you type'))
self.SongBarActiveCheckBox.setText(translate('SongsPlugin.SongsTab', self.SongBarActiveCheckBox.setText(translate('SongsPlugin.SongsTab',
'Display verses on live tool bar')) 'Display verses on live tool bar'))
self.SongUpdateOnEditCheckBox.setText(
translate('SongsPlugin.SongsTab', 'Update service from song edit'))
self.SongAddFromServiceCheckBox.setText(translate('SongsPlugin.SongsTab',
'Add missing songs when opening service'))
def onSearchAsTypeCheckBoxChanged(self, check_state): def onSearchAsTypeCheckBoxChanged(self, check_state):
self.song_search = False self.song_search = False
@ -77,12 +92,24 @@ class SongsTab(SettingsTab):
if check_state == QtCore.Qt.Checked: if check_state == QtCore.Qt.Checked:
self.song_search = True self.song_search = True
def SongBarActiveCheckBoxChanged(self, check_state): def onSongBarActiveCheckBoxChanged(self, check_state):
self.song_bar = False self.song_bar = False
# we have a set value convert to True/False # we have a set value convert to True/False
if check_state == QtCore.Qt.Checked: if check_state == QtCore.Qt.Checked:
self.song_bar = True self.song_bar = True
def onSongUpdateOnEditCheckBoxChanged(self, check_state):
self.update_edit = False
# we have a set value convert to True/False
if check_state == QtCore.Qt.Checked:
self.update_edit = True
def onSongAddFromServiceCheckBoxChanged(self, check_state):
self.update_load = False
# we have a set value convert to True/False
if check_state == QtCore.Qt.Checked:
self.update_load = True
def load(self): def load(self):
settings = QtCore.QSettings() settings = QtCore.QSettings()
settings.beginGroup(self.settingsSection) settings.beginGroup(self.settingsSection)
@ -90,8 +117,14 @@ class SongsTab(SettingsTab):
u'search as type', QtCore.QVariant(False)).toBool() u'search as type', QtCore.QVariant(False)).toBool()
self.song_bar = settings.value( self.song_bar = settings.value(
u'display songbar', QtCore.QVariant(True)).toBool() u'display songbar', QtCore.QVariant(True)).toBool()
self.update_edit = settings.value(
u'update service on edit', QtCore.QVariant(False)).toBool()
self.update_load = settings.value(
u'add song from service', QtCore.QVariant(True)).toBool()
self.SearchAsTypeCheckBox.setChecked(self.song_search) self.SearchAsTypeCheckBox.setChecked(self.song_search)
self.SongBarActiveCheckBox.setChecked(self.song_bar) self.SongBarActiveCheckBox.setChecked(self.song_bar)
self.SongUpdateOnEditCheckBox.setChecked(self.update_edit)
self.SongAddFromServiceCheckBox.setChecked(self.update_load)
settings.endGroup() settings.endGroup()
def save(self): def save(self):
@ -99,4 +132,6 @@ class SongsTab(SettingsTab):
settings.beginGroup(self.settingsSection) settings.beginGroup(self.settingsSection)
settings.setValue(u'search as type', QtCore.QVariant(self.song_search)) settings.setValue(u'search as type', QtCore.QVariant(self.song_search))
settings.setValue(u'display songbar', QtCore.QVariant(self.song_bar)) settings.setValue(u'display songbar', QtCore.QVariant(self.song_bar))
settings.setValue(u'update service on edit', QtCore.QVariant(self.update_edit))
settings.setValue(u'add song from service', QtCore.QVariant(self.update_load))
settings.endGroup() settings.endGroup()

View File

@ -282,11 +282,9 @@ class Song(object):
def get_author_list(self, asOneString = True): def get_author_list(self, asOneString = True):
"""Return the list of authors as a string """Return the list of authors as a string
asOneString ``asOneString``
True -- string: If ``True``, returns 'John Newton, A Parker'. If ``False``, returns
'John Newton, A Parker' [u'John Newton', u'A Parker']
False -- list of strings
['John Newton', u'A Parker']
""" """
if asOneString: if asOneString:
res = self._assure_string(self.author_list) res = self._assure_string(self.author_list)
@ -297,7 +295,8 @@ class Song(object):
def set_author_list(self, author_list): def set_author_list(self, author_list):
"""Set the author_list """Set the author_list
author_list -- a string or list of strings ``author_list``
a string or list of strings
""" """
if author_list is None: if author_list is None:
self.author_list = None self.author_list = None
@ -307,11 +306,9 @@ class Song(object):
def get_category_array(self, asOneString = True): def get_category_array(self, asOneString = True):
"""Return the list of categories as a string """Return the list of categories as a string
asOneString ``asOneString``
True -- string: If ``True``, returns 'Hymn, Gospel'. If ``False``, returns
'Hymn, Gospel' [u'Hymn', u'Gospel']
False -- list of strings
['Hymn', u'Gospel']
""" """
if asOneString: if asOneString:
res = self._assure_string(self.category_array) res = self._assure_string(self.category_array)
@ -381,6 +378,7 @@ class Song(object):
properties properties
slideNumber -- 1 .. numberOfSlides slideNumber -- 1 .. numberOfSlides
Returns a list as: Returns a list as:
[theme_name (string), [theme_name (string),
title (string), title (string),

View File

@ -24,7 +24,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
The :mod:`wowimport` module provides the functionality for importing Words of The :mod:`wowimport` module provides the functionality for importing Words of
Worship songs into the OpenLP database. Worship songs into the OpenLP database.
""" """
import os import os
@ -38,19 +38,18 @@ log = logging.getLogger(__name__)
class WowImport(SongImport): class WowImport(SongImport):
""" """
The :class:`WowImport` class provides the ability to import song files from The :class:`WowImport` class provides the ability to import song files from
Words of Worship. Words of Worship.
Words Of Worship Song File Format **Words Of Worship Song File Format:**
`````````````````````````````````
The Words Of Worship song file format is as follows: The Words Of Worship song file format is as follows:
* The song title is the file name minus the extension. * The song title is the file name minus the extension.
* The song has a header, a number of blocks, followed by footer containing * The song has a header, a number of blocks, followed by footer containing
the author and the copyright. the author and the copyright.
* A block can be a verse, chorus or bridge. * A block can be a verse, chorus or bridge.
File Header: File Header:
Bytes are counted from one, i.e. the first byte is byte 1. These bytes, Bytes are counted from one, i.e. the first byte is byte 1. These bytes,
up to the 56 byte, can change but no real meaning has been found. The up to the 56 byte, can change but no real meaning has been found. The
@ -65,29 +64,29 @@ class WowImport(SongImport):
Each block ends with 4 bytes, the first of which defines what type of Each block ends with 4 bytes, the first of which defines what type of
block it is, and the rest which are null bytes: block it is, and the rest which are null bytes:
* ``NUL`` (\x00) - Verse * ``NUL`` (0x00) - Verse
* ``SOH`` (\x01) - Chorus * ``SOH`` (0x01) - Chorus
* ``STX`` (\x02) - Bridge * ``STX`` (0x02) - Bridge
Blocks are seperated by two bytes. The first byte is ``SOH`` (\x01), Blocks are seperated by two bytes. The first byte is 0x01, and the
and the second byte is ```` (\x80). second byte is 0x80.
Lines: Lines:
Each line starts with a byte which specifies how long that line is, Each line starts with a byte which specifies how long that line is,
the line text, and ends with a null byte. the line text, and ends with a null byte.
Footer: Footer:
The footer follows on after the last block, the first byte specifies The footer follows on after the last block, the first byte specifies
the length of the author text, followed by the author text, if the length of the author text, followed by the author text, if
this byte is null, then there is no author text. The byte after the this byte is null, then there is no author text. The byte after the
author text specifies the length of the copyright text, followed author text specifies the length of the copyright text, followed
by the copyright text. by the copyright text.
The file is ended with four null bytes. The file is ended with four null bytes.
Valid extensions for a Words of Worship song file are: Valid extensions for a Words of Worship song file are:
* .wsg * .wsg
* .wow-song * .wow-song
""" """
@ -111,30 +110,30 @@ class WowImport(SongImport):
""" """
Recieve a single file, or a list of files to import. Recieve a single file, or a list of files to import.
""" """
if isinstance(self.import_source, list): if isinstance(self.import_source, list):
self.import_wizard.importProgressBar.setMaximum( self.import_wizard.importProgressBar.setMaximum(
len(self.import_source)) len(self.import_source))
for file in self.import_source: for file in self.import_source:
# TODO: check that it is a valid words of worship file (could
# check header for WoW File Song Word)
self.author = u'' self.author = u''
self.copyright = u'' self.copyright = u''
# Get the song title
self.file_name = os.path.split(file)[1] self.file_name = os.path.split(file)[1]
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
"Importing %s" % (self.file_name), 0) "Importing %s" % (self.file_name), 0)
# Get the song title
self.title = self.file_name.rpartition(u'.')[0] self.title = self.file_name.rpartition(u'.')[0]
self.songData = open(file, 'rb') self.songData = open(file, 'rb')
if self.songData.read(19) != u'WoW File\nSong Words':
continue
# Seek to byte which stores number of blocks in the song # Seek to byte which stores number of blocks in the song
self.songData.seek(56) self.songData.seek(56)
self.no_of_blocks = ord(self.songData.read(1)) self.no_of_blocks = ord(self.songData.read(1))
# Seek to the beging of the first block # Seek to the beging of the first block
self.songData.seek(82) self.songData.seek(82)
for block in range(self.no_of_blocks): for block in range(self.no_of_blocks):
self.lines_to_read = ord(self.songData.read(1)) self.lines_to_read = ord(self.songData.read(1))
# Skip 3 nulls to the beginnig of the 1st line # Skip 3 nulls to the beginnig of the 1st line
self.songData.seek(3, os.SEEK_CUR) self.songData.seek(3, os.SEEK_CUR)
self.block_text = u'' self.block_text = u''
while self.lines_to_read: while self.lines_to_read:
self.length_of_line = ord(self.songData.read(1)) self.length_of_line = ord(self.songData.read(1))
@ -148,7 +147,7 @@ class WowImport(SongImport):
self.block_type = BLOCK_TYPES[ord(self.songData.read(1))] self.block_type = BLOCK_TYPES[ord(self.songData.read(1))]
# Skip 3 nulls at the end of the block # Skip 3 nulls at the end of the block
self.songData.seek(3, os.SEEK_CUR) self.songData.seek(3, os.SEEK_CUR)
# Blocks are seperated by 2 bytes, skip them, but not if # Blocks are seperated by 2 bytes, skip them, but not if
# this is the last block! # this is the last block!
if (block + 1) < self.no_of_blocks: if (block + 1) < self.no_of_blocks:
self.songData.seek(2, os.SEEK_CUR) self.songData.seek(2, os.SEEK_CUR)
@ -170,4 +169,4 @@ class WowImport(SongImport):
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
"Importing %s" % (self.file_name)) "Importing %s" % (self.file_name))
return True return True

View File

@ -28,7 +28,7 @@ import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, build_icon, translate from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.lib.db import Manager from openlp.core.lib.db import Manager
from openlp.plugins.songs.lib import SongMediaItem, SongsTab from openlp.plugins.songs.lib import SongMediaItem, SongsTab
from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.db import init_schema, Song
@ -57,7 +57,8 @@ class SongsPlugin(Plugin):
self.icon = build_icon(self.icon_path) self.icon = build_icon(self.icon_path)
def getSettingsTab(self): def getSettingsTab(self):
return SongsTab(self.name) visible_name = self.getString(StringContent.VisibleName)
return SongsTab(self.name, visible_name[u'title'])
def initialise(self): def initialise(self):
log.info(u'Songs Initialising') log.info(u'Songs Initialising')
@ -70,7 +71,7 @@ class SongsPlugin(Plugin):
Create the MediaManagerItem object, which is displaed in the Create the MediaManagerItem object, which is displaed in the
Media Manager. Media Manager.
""" """
return SongMediaItem(self, self.icon, self.name) return SongMediaItem(self, self, self.icon)
def addImportMenuItem(self, import_menu): def addImportMenuItem(self, import_menu):
""" """
@ -147,3 +148,54 @@ class SongsPlugin(Plugin):
importer = class_(self.manager, **kwargs) importer = class_(self.manager, **kwargs)
importer.register(self.mediaItem.import_wizard) importer.register(self.mediaItem.import_wizard)
return importer return importer
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
## Name PluginList ##
self.textStrings[StringContent.Name] = {
u'singular': translate('SongsPlugin', 'Song'),
u'plural': translate('SongsPlugin', 'Songs')
}
## Name for MediaDockManager, SettingsManager ##
self.textStrings[StringContent.VisibleName] = {
u'title': translate('SongsPlugin', 'Songs')
}
# Middle Header Bar
## New Button ##
self.textStrings[StringContent.New] = {
u'title': translate('SongsPlugin', 'Add'),
u'tooltip': translate('SongsPlugin',
'Add a new Song')
}
## Edit Button ##
self.textStrings[StringContent.Edit] = {
u'title': translate('SongsPlugin', 'Edit'),
u'tooltip': translate('SongsPlugin',
'Edit the selected Song')
}
## Delete Button ##
self.textStrings[StringContent.Delete] = {
u'title': translate('SongsPlugin', 'Delete'),
u'tooltip': translate('SongsPlugin',
'Delete the selected Song')
}
## Preview ##
self.textStrings[StringContent.Preview] = {
u'title': translate('SongsPlugin', 'Preview'),
u'tooltip': translate('SongsPlugin',
'Preview the selected Song')
}
## Live Button ##
self.textStrings[StringContent.Live] = {
u'title': translate('SongsPlugin', 'Live'),
u'tooltip': translate('SongsPlugin',
'Send the selected Song live')
}
## Add to service Button ##
self.textStrings[StringContent.Service] = {
u'title': translate('SongsPlugin', 'Service'),
u'tooltip': translate('SongsPlugin',
'Add the selected Song to the service')
}

View File

@ -29,7 +29,7 @@ from datetime import datetime
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, Receiver, build_icon, translate from openlp.core.lib import Plugin, StringContent, Receiver, build_icon, translate
from openlp.core.lib.db import Manager from openlp.core.lib.db import Manager
from openlp.plugins.songusage.forms import SongUsageDetailForm, \ from openlp.plugins.songusage.forms import SongUsageDetailForm, \
SongUsageDeleteForm SongUsageDeleteForm
@ -162,3 +162,17 @@ class SongUsagePlugin(Plugin):
'</strong><br />This plugin tracks the usage of songs in ' '</strong><br />This plugin tracks the usage of songs in '
'services.') 'services.')
return about_text return about_text
def setPluginTextStrings(self):
"""
Called to define all translatable texts of the plugin
"""
## Name PluginList ##
self.textStrings[StringContent.Name] = {
u'singular': translate('SongUsagePlugin', 'SongUsage'),
u'plural': translate('SongUsagePlugin', 'SongUsage')
}
## Name for MediaDockManager, SettingsManager ##
self.textStrings[StringContent.VisibleName] = {
u'title': translate('SongUsagePlugin', 'SongUsage')
}

View File

@ -18,20 +18,48 @@
<normaloff>:/icon/openlp.org-icon-32.bmp</normaloff>:/icon/openlp.org-icon-32.bmp</iconset> <normaloff>:/icon/openlp.org-icon-32.bmp</normaloff>:/icon/openlp.org-icon-32.bmp</iconset>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
<widget class="QLabel" name="TitleLabel"> <widget class="QLabel" name="ThemeLabel">
<property name="text"> <property name="text">
<string>Title:</string> <string>Theme:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLineEdit" name="TitleEdit"/> <widget class="QComboBox" name="ThemeComboBox">
<property name="toolTip">
<string extracomment="Select custom theme for slide"/>
</property>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="CreditLabel">
<property name="text">
<string>Credits:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="CreditEdit"/>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="toolTip">
<string extracomment="Edit dialog"/>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<item> <item>
@ -44,16 +72,42 @@
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QPushButton" name="UpButton"> <widget class="QPushButton" name="AddButton">
<property name="toolTip"> <property name="toolTip">
<string extracomment="Move slide up 1"/> <string extracomment="Adds a new slide at bottom"/>
</property> </property>
<property name="text"> <property name="text">
<string/> <string>Add New</string>
</property> </property>
<property name="icon"> </widget>
<iconset resource="../images/openlp-2.qrc"> </item>
<normaloff>:/services/service_up.png</normaloff>:/services/service_up.png</iconset> <item>
<widget class="QPushButton" name="EditButton">
<property name="toolTip">
<string extracomment="Edit selected slide"/>
</property>
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="EditAllButton">
<property name="toolTip">
<string extracomment="Edit all slides"/>
</property>
<property name="text">
<string>Edit All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="DeleteButton">
<property name="toolTip">
<string extracomment="Delete selected slide"/>
</property>
<property name="text">
<string>Delete</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -70,6 +124,20 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="UpButton">
<property name="toolTip">
<string extracomment="Move slide up 1"/>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../images/openlp-2.qrc">
<normaloff>:/services/service_up.png</normaloff>:/services/service_up.png</iconset>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="DownButton"> <widget class="QPushButton" name="DownButton">
<property name="toolTip"> <property name="toolTip">
@ -88,166 +156,26 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="2" column="0"> <item row="0" column="0">
<widget class="QWidget" name="EditWidget" native="true"> <layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="EditLayout_3">
<property name="spacing">
<number>8</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="VerseTextEdit"/>
</item>
<item>
<widget class="QWidget" name="ButtonWidge" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="AddButton">
<property name="toolTip">
<string extracomment="Adds a new slide at bottom"/>
</property>
<property name="text">
<string>Add New</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="EditButton">
<property name="toolTip">
<string extracomment="Edit selected slide"/>
</property>
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="EditAllButton">
<property name="toolTip">
<string extracomment="Edit all slides"/>
</property>
<property name="text">
<string>Edit All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SaveButton">
<property name="toolTip">
<string extracomment="Replace edited slide"/>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="DeleteButton">
<property name="toolTip">
<string extracomment="Delete selected slide"/>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ClearButton">
<property name="toolTip">
<string extracomment="Clear selection"/>
</property>
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SplitButton">
<property name="toolTip">
<string extracomment="Add new slide split"/>
</property>
<property name="text">
<string>Split Slide</string>
</property>
</widget>
</item>
<item>
<spacer name="ButtonSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
<widget class="QLabel" name="ThemeLabel"> <widget class="QLabel" name="TitleLabel">
<property name="text"> <property name="text">
<string>Theme:</string> <string>Title:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="ThemeComboBox"> <widget class="QLineEdit" name="TitleEdit"/>
<property name="toolTip">
<string extracomment="Select custom theme for slide"/>
</property>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="CreditLabel">
<property name="text">
<string>Credits:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="CreditEdit"/>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="toolTip">
<string extracomment="Edit dialog"/>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>TitleEdit</tabstop> <tabstop>TitleEdit</tabstop>
<tabstop>VerseTextEdit</tabstop>
<tabstop>AddButton</tabstop>
<tabstop>VerseListView</tabstop> <tabstop>VerseListView</tabstop>
<tabstop>EditButton</tabstop>
<tabstop>EditAllButton</tabstop>
<tabstop>SaveButton</tabstop>
<tabstop>DeleteButton</tabstop>
<tabstop>CreditEdit</tabstop> <tabstop>CreditEdit</tabstop>
<tabstop>UpButton</tabstop>
<tabstop>DownButton</tabstop>
<tabstop>ThemeComboBox</tabstop> <tabstop>ThemeComboBox</tabstop>
</tabstops> </tabstops>
<resources> <resources>

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>customSlideEditDialog</class>
<widget class="QDialog" name="customSlideEditDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>474</width>
<height>442</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>8</x>
<y>407</y>
<width>458</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
<widget class="QTextEdit" name="VerseTextEdit">
<property name="geometry">
<rect>
<x>8</x>
<y>8</y>
<width>458</width>
<height>349</height>
</rect>
</property>
</widget>
<widget class="QPushButton" name="SplitButton">
<property name="geometry">
<rect>
<x>380</x>
<y>370</y>
<width>85</width>
<height>27</height>
</rect>
</property>
<property name="toolTip">
<string extracomment="Add new slide split"/>
</property>
<property name="text">
<string>Split Slide</string>
</property>
</widget>
</widget>
<resources>
<include location="../images/openlp-2.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>customSlideEditDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>customSlideEditDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -27,6 +27,7 @@ DefaultDirName={pf}\{#AppName}
DefaultGroupName={#AppVerName} DefaultGroupName={#AppVerName}
AllowNoIcons=true AllowNoIcons=true
LicenseFile=LICENSE.txt LicenseFile=LICENSE.txt
OutputDir=..\..\dist
OutputBaseFilename=OpenLP-{#RealVersion}-setup OutputBaseFilename=OpenLP-{#RealVersion}-setup
Compression=lzma Compression=lzma
SolidCompression=true SolidCompression=true

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,14 @@
# -*- mode: python -*-
a = Analysis([
os.path.join(HOMEPATH, 'support\\_mountzlib.py'),
os.path.join(HOMEPATH, 'support\\useUnicode.py'),
os.path.abspath('openlp.pyw')],
pathex=[os.path.abspath('.')])
pyz = PYZ(a.pure)
exe = EXE(pyz, a.scripts, exclude_binaries=1,
name=os.path.abspath(os.path.join('build', 'pyi.win32', 'OpenLP',
'OpenLP.exe')),
debug=False, strip=False, upx=True, console=False,
icon=os.path.abspath(os.path.join('resources', 'images', 'OpenLP.ico')))
coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True,
name=os.path.abspath(os.path.join('dist', 'OpenLP')))

View File

@ -0,0 +1,611 @@
W: no module named openlp.core.lib.build_html (top-level import by openlp.core.ui.maindisplay)
W: no module named mx (top-level import by sqlite.main)
W: no module named ctypes.create_string_buffer (delayed import by urllib)
W: no module named openlp.core.ui.ServiceNoteForm (top-level import by openlp.core.ui.servicemanager)
W: no module named openlp.core.lib.MediaManagerItem (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.core.ui.mainwindow)
W: no module named email.Iterators (delayed import by email.message)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.custom.lib.mediaitem)
W: no module named sqlalchemy.sql.join (top-level import by sqlalchemy)
W: no module named java (conditional import by xml.sax._exceptions)
W: no module named openlp.plugins.images.lib.ImageMediaItem (top-level import by openlp.plugins.images.imageplugin)
W: no module named sqlalchemy.SMALLINT (top-level import by sqlalchemy.databases.sybase)
W: no module named sqlalchemy.engine.create_engine (top-level import by sqlalchemy)
W: no module named sqlalchemy.sql.asc (top-level import by sqlalchemy)
W: no module named openlp.plugins.bibles.lib.BiblesTab (top-level import by openlp.plugins.bibles.bibleplugin)
W: no module named PyQt4._qt (top-level import by PyQt4.QtCore)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.alerts.lib.alertstab)
W: no module named sqlalchemy.Column (top-level import by openlp.plugins.custom.lib.db)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.songusage.songusageplugin)
W: no module named openlp.plugins.alerts.lib.AlertsManager (top-level import by openlp.plugins.alerts.alertsplugin)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.ui.generaltab)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.alerts.forms.alertdialog)
W: no module named openlp.core.lib.check_item_selected (top-level import by openlp.plugins.custom.lib.mediaitem)
W: no module named sqlalchemy.orm.MapperExtension (top-level import by sqlalchemy.orm.scoping)
W: no module named pyodbc (delayed import by sqlalchemy.databases.access)
W: no module named openlp.core.lib.expand_tags (top-level import by openlp.core.lib.renderer)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.ui.servicemanager)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.dynamic)
W: no module named ctypes._SimpleCData (top-level import by ctypes.wintypes)
W: no module named openlp.core.lib.SettingsTab (top-level import by openlp.core.ui.generaltab)
W: no module named simplejson (conditional import by openlp.plugins.remotes.lib.httpserver)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.alerts.forms.alertdialog)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.collections)
W: no module named openlp.core.lib.resize_image (top-level import by openlp.core.lib.serviceitem)
W: no module named openlp.plugins.songs.lib.SongXMLParser (top-level import by openlp.plugins.songs.forms.editsongform)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.bibles.bibleplugin)
W: no module named xml.dom.XMLNS_NAMESPACE (top-level import by xml.dom.minidom)
W: no module named openlp.plugins.songs.lib.VerseType (top-level import by openlp.plugins.songs.lib.songimport)
W: no module named openlp.plugins.songs.lib.SongXMLBuilder (top-level import by openlp.plugins.songs.forms.editsongform)
W: no module named openlp.core.ui.GeneralTab (top-level import by openlp.core.ui.settingsform)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named openlp.core.lib.BaseListWithDnD (top-level import by openlp.plugins.presentations.lib.mediaitem)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.presentations.presentationplugin)
W: no module named informixdb (delayed import by sqlalchemy.databases.informix)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.plugins.custom.customplugin)
W: no module named cjkcodecs (top-level import by BeautifulSoup)
W: no module named readline (delayed, conditional import by cmd)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.songs.lib.songimport)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.plugins.media.mediaplugin)
W: no module named openlp.core.lib.ThemeXML (top-level import by openlp.core.ui.amendthemeform)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.bibles.lib.manager)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.custom.lib.mediaitem)
W: no module named openlp.core.lib.ServiceItem (top-level import by openlp.core.ui.maindisplay)
W: no module named openlp.plugins.songs.forms.EditVerseForm (top-level import by openlp.plugins.songs.forms.editsongform)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.lib.ewimport)
W: no module named openlp.core.lib.SettingsTab (top-level import by openlp.plugins.songs.lib.songstab)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.ui.HideMode (top-level import by openlp.core.ui.maindisplay)
W: no module named sqlalchemy.Column (top-level import by sqlalchemy.databases.sybase)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.core.ui.servicemanager)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.slidecontroller)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.songs.forms.songimportform)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.presentations.lib.mediaitem)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.aboutdialog)
W: no module named sqlalchemy.sql.insert (top-level import by sqlalchemy)
W: no module named openlp.core.lib.str_to_bool (top-level import by openlp.core.ui.thememanager)
W: no module named sqlalchemy.MetaData (top-level import by sqlalchemy.databases.sybase)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.bibles.lib.biblestab)
W: no module named openlp.plugins.bibles.lib.BibleManager (top-level import by openlp.plugins.bibles.bibleplugin)
W: no module named openlp.core.ui.SlideController (top-level import by openlp.core.ui.mainwindow)
W: no module named MacOS (delayed import by platform)
W: no module named openlp.core.lib.ThemeXML (top-level import by openlp.core.ui.thememanager)
W: no module named cx_Oracle (delayed import by sqlalchemy.databases.oracle)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.ui.slidecontroller)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.servicenotedialog)
W: no module named sqlalchemy.sql.except_ (top-level import by sqlalchemy)
W: no module named openlp.core.ui.AboutForm (top-level import by openlp.core.ui.mainwindow)
W: no module named EasyDialogs (conditional import by getpass)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.core.lib.mediamanageritem)
W: no module named sqlalchemy.orm.SessionExtension (top-level import by sqlalchemy.orm.session)
W: no module named sqlalchemy.orm.relation (top-level import by openlp.plugins.bibles.lib.db)
W: no module named openlp.plugins.songs.lib.VerseType (top-level import by openlp.plugins.songs.forms.editsongform)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.plugins.bibles.lib.http)
W: no module named sqlalchemy.sql.func (top-level import by sqlalchemy)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.utils.languagemanager)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.ui.mainwindow)
W: no module named uno (conditional import by openlp.plugins.songs.lib.oooimport)
W: no module named kinterbasdb (delayed import by sqlalchemy.databases.firebird)
W: no module named multiprocessing.RLock (top-level import by multiprocessing.sharedctypes)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.filerenameform)
W: no module named sqlalchemy.ForeignKey (top-level import by sqlalchemy.databases.mssql)
W: no module named openlp.core.lib.Renderer (top-level import by openlp.core.lib.rendermanager)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.songmaintenancedialog)
W: no module named openlp.core.lib.check_item_selected (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.core.lib.mediamanageritem)
W: no module named openlp.core.ui.FileRenameForm (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.songsplugin)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.bibles.bibleplugin)
W: no module named vms_lib (delayed, conditional import by platform)
W: no module named openlp.core.lib.resize_image (top-level import by openlp.core.ui.slidecontroller)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.alerts.lib.alertsmanager)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songusage.forms.songusagedetaildialog)
W: no module named openlp.plugins.remotes.lib.RemoteTab (top-level import by openlp.plugins.remotes.remoteplugin)
W: no module named openlp.core.ui.ServiceManager (top-level import by openlp.core.ui.mainwindow)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.plugins.songusage.forms.songusagedetailform)
W: no module named openlp.core.ui.SettingsForm (top-level import by openlp.core.ui.mainwindow)
W: no module named openlp.plugins.alerts.lib.AlertsTab (top-level import by openlp.plugins.alerts.alertsplugin)
W: no module named xdg (delayed, conditional import by openlp.core.utils)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.custom.customplugin)
W: no module named openlp.core.ui.AmendThemeForm (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.lib.ItemCapabilities (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named openlp.core.lib.ItemCapabilities (top-level import by openlp.plugins.custom.lib.mediaitem)
W: no module named posix (delayed, conditional import by iu)
W: no module named openlp.plugins.songs.lib.VerseType (top-level import by openlp.plugins.songs.forms.editversedialog)
W: no module named multiprocessing.dummy.Process (delayed import by __main__)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.presentations.presentationplugin)
W: no module named sqlalchemy.String (top-level import by sqlalchemy.databases.mssql)
W: no module named xml.dom.EMPTY_PREFIX (top-level import by xml.dom.expatbuilder)
W: no module named openlp.core.lib.MediaManagerItem (top-level import by openlp.plugins.custom.lib.mediaitem)
W: no module named openlp.core.lib.ServiceItem (top-level import by openlp.core.lib.mediamanageritem)
W: no module named openlp.core.lib.PluginManager (top-level import by openlp.core.ui.mainwindow)
W: no module named multiprocessing.current_process (top-level import by multiprocessing.reduction)
W: no module named openlp.core.lib.MediaManagerItem (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.songbookform)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.plugins.presentations.presentationplugin)
W: no module named xmltok (top-level import by pyexpat)
W: no module named openlp.plugins.bibles.lib.SearchResults (top-level import by openlp.plugins.bibles.lib.http)
W: no module named sqlalchemy.sql.delete (top-level import by sqlalchemy)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.songs.lib.oooimport)
W: no module named _emx_link (conditional import by os)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.core.ui.aboutdialog)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.bibles.forms.bibleimportwizard)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.core.lib.pluginmanager)
W: no module named openlp.plugins.songs.lib.SongsTab (top-level import by openlp.plugins.songs.songsplugin)
W: no module named sqlalchemy.CHAR (top-level import by sqlalchemy.databases.sybase)
W: no module named sqlalchemy.sql.collate (top-level import by sqlalchemy)
W: no module named sqlalchemy.sql.outparam (top-level import by sqlalchemy)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.core.lib.db)
W: no module named openlp.core.ui.PluginForm (top-level import by openlp.core.ui.mainwindow)
W: no module named gobject (top-level import by enchant.checker.GtkSpellCheckerDialog)
W: no module named openlp.core.utils.VersionThread (top-level import by __main__)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.topicsform)
W: no module named sqlalchemy.Integer (top-level import by sqlalchemy.databases.mssql)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.exceptiondialog)
W: no module named pwd (delayed, conditional import by distutils.util)
W: no module named uno (conditional import by openlp.plugins.presentations.lib.impresscontroller)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.utils)
W: no module named sqlalchemy.orm.class_mapper (top-level import by openlp.plugins.songs.lib.olpimport)
W: no module named sqlalchemy.orm.relation (top-level import by openlp.plugins.songs.lib.olpimport)
W: no module named readline (delayed import by pdb)
W: no module named openlp.core.ui.MainDisplay (top-level import by openlp.core.lib.rendermanager)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.media.lib.mediaitem)
W: no module named sqlalchemy.Table (top-level import by openlp.plugins.songusage.lib.db)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.alerts.alertsplugin)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.plugins.media.lib.mediaitem)
W: no module named openlp.core.lib.BaseListWithDnD (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.bibles.forms.importwizardform)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.remotes.remoteplugin)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.images.imageplugin)
W: no module named openlp.core.lib.SettingsTab (top-level import by openlp.plugins.alerts.lib.alertstab)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.editversedialog)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.songusage.songusageplugin)
W: no module named openlp.core.ui.HideMode (top-level import by openlp.core.ui.slidecontroller)
W: no module named sqlalchemy.sql.between (top-level import by sqlalchemy)
W: no module named xml.dom.EMPTY_PREFIX (top-level import by xml.dom.minidom)
W: no module named pysqlite2 (delayed import by sqlalchemy.databases.sqlite)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.custom.lib.customtab)
W: no module named gtk (top-level import by enchant.checker.GtkSpellCheckerDialog)
W: no module named xml.dom.EMPTY_NAMESPACE (top-level import by xml.dom.expatbuilder)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.remotes.lib.httpserver)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songusage.forms.songusagedeleteform)
W: no module named sqlalchemy.orm.scoped_session (top-level import by openlp.core.lib.db)
W: no module named openlp.core.lib.clean_tags (top-level import by openlp.core.lib.serviceitem)
W: no module named openlp.core.utils.get_filesystem_encoding (top-level import by openlp.core.ui.thememanager)
W: no module named xml.dom.EMPTY_NAMESPACE (top-level import by xml.dom.minidom)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.core.lib.toolbar)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.remotes.remoteplugin)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.attributes)
W: no module named sqlalchemy.ForeignKey (top-level import by sqlalchemy.databases.sybase)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.servicemanager)
W: no module named sqlalchemy.Column (top-level import by openlp.plugins.alerts.lib.db)
W: no module named openlp.core.lib.SettingsTab (top-level import by openlp.core.ui.advancedtab)
W: no module named AES (delayed, conditional import by archive)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.songs.forms.songmaintenanceform)
W: no module named openlp.core.lib.context_menu_separator (top-level import by openlp.core.lib.mediamanageritem)
W: no module named fcntl (top-level import by tempfile)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.thememanager)
W: no module named mx (top-level import by sqlalchemy.databases.mxODBC)
W: no module named sqlalchemy.sql.union (top-level import by sqlalchemy)
W: no module named openlp.plugins.songs.forms.TopicsForm (top-level import by openlp.plugins.songs.forms.songmaintenanceform)
W: no module named openlp.core.lib.ItemCapabilities (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named MacOS (delayed import by distutils.sysconfig)
W: no module named openlp.core.lib.SpellTextEdit (top-level import by openlp.plugins.custom.forms.editcustomdialog)
W: no module named ic (top-level import by webbrowser)
W: no module named com (conditional import by openlp.plugins.presentations.lib.impresscontroller)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.media.mediaplugin)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.custom.forms.editcustomform)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.lib.spelltextedit)
W: no module named openlp.core.lib.html_expands (top-level import by openlp.core.lib.spelltextedit)
W: no module named openlp.core.lib.context_menu_action (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named sqlalchemy.Column (top-level import by openlp.plugins.songs.lib.db)
W: no module named py2exe (delayed import by enchant.tests)
W: no module named openlp.plugins.songs.forms.AuthorsForm (top-level import by openlp.plugins.songs.forms.songmaintenanceform)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.authorsform)
W: no module named openlp.plugins.remotes.lib.HttpServer (top-level import by openlp.plugins.remotes.remoteplugin)
W: no module named sqlalchemy.Table (top-level import by openlp.plugins.bibles.lib.db)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.plugins.remotes.remoteplugin)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.filerenamedialog)
W: no module named openlp.core.lib.BaseListWithDnD (top-level import by openlp.plugins.media.lib.mediaitem)
W: no module named sapdb (delayed import by sqlalchemy.databases.maxdb)
W: no module named ctypes.cdll (delayed import by ctypes.util)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.presentations.lib.presentationtab)
W: no module named openlp.core.lib.MediaManagerItem (top-level import by openlp.plugins.presentations.lib.mediaitem)
W: no module named sqlalchemy.sql.text (top-level import by sqlalchemy)
W: no module named openlp.core.lib.image_to_byte (top-level import by openlp.core.lib.renderer)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.plugins.bibles.forms.importwizardform)
W: no module named openlp.core.lib.image_to_byte (top-level import by openlp.core.ui.maindisplay)
W: no module named iconv_codec (top-level import by BeautifulSoup)
W: no module named openlp.plugins.songs.forms.SongBookForm (top-level import by openlp.plugins.songs.forms.songmaintenanceform)
W: no module named sqlalchemy.sql.not_ (top-level import by sqlalchemy)
W: no module named multiprocessing.Pipe (top-level import by multiprocessing.queues)
W: no module named openlp.core.lib.check_item_selected (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songusage.forms.songusagedetailform)
W: no module named sqlalchemy.sql.subquery (top-level import by sqlalchemy)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.songs.songsplugin)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.editsongdialog)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.plugins.bibles.bibleplugin)
W: no module named openlp.plugins.songs.forms.SongMaintenanceForm (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named xmlparse (top-level import by pyexpat)
W: no module named sqlalchemy.sql.exists (top-level import by sqlalchemy)
W: no module named sqlalchemy.sql.and_ (top-level import by openlp.plugins.songs.forms.songmaintenanceform)
W: no module named multiprocessing.Process (top-level import by multiprocessing.pool)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.settingsdialog)
W: no module named sqlalchemy.exceptions (top-level import by openlp.core.lib.db)
W: no module named sqlalchemy.sql.outerjoin (top-level import by sqlalchemy)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.songs.forms.editsongdialog)
W: no module named openlp.plugins.custom.lib.CustomXMLParser (top-level import by openlp.plugins.custom.lib.mediaitem)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.custom.forms.editcustomdialog)
W: no module named sqlalchemy.Integer (top-level import by sqlalchemy.databases.sybase)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.properties)
W: no module named multiprocessing.current_process (top-level import by multiprocessing.managers)
W: no module named openlp.core.ui.SplashScreen (top-level import by __main__)
W: no module named multiprocessing.TimeoutError (top-level import by multiprocessing.dummy)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.plugins.songs.forms.songimportform)
W: no module named xml.dom.XMLNS_NAMESPACE (top-level import by xml.dom.expatbuilder)
W: no module named ctypes.c_int32 (delayed import by urllib)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.amendthemedialog)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.core.lib.serviceitem)
W: no module named openlp.core.utils.LanguageManager (top-level import by __main__)
W: no module named sqlalchemy.orm.object_mapper (top-level import by sqlalchemy.orm.properties)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.core.utils.languagemanager)
W: no module named sqlalchemy.sql.literal_column (top-level import by sqlalchemy)
W: no module named openlp.plugins.songs.lib.SongXMLParser (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named ctypes.c_char_p (delayed import by urllib)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.plugins.images.imageplugin)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.remotes.lib.remotetab)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.core.ui.mainwindow)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.bibles.lib.opensong)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.songimportform)
W: no module named sqlalchemy.sql.select (top-level import by sqlalchemy)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.songbookdialog)
W: no module named openlp.plugins.bibles.lib.BibleMediaItem (top-level import by openlp.plugins.bibles.bibleplugin)
W: no module named PyQt4._qt (top-level import by PyQt4.QtNetwork)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.plugins.songs.songsplugin)
W: no module named sqlalchemy.sql.case (top-level import by sqlalchemy)
W: no module named wx (top-level import by enchant.checker.wxSpellCheckerDialog)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.alerts.alertsplugin)
W: no module named com (conditional import by openlp.plugins.songs.lib.sofimport)
W: no module named PyQt4._qt (top-level import by PyQt4)
W: no module named SOCKS (top-level import by ftplib)
W: no module named openlp.plugins.songusage.forms.SongUsageDetailForm (top-level import by openlp.plugins.songusage.songusageplugin)
W: no module named sqlalchemy.sql.null (top-level import by sqlalchemy)
W: no module named sqlalchemy.MetaData (top-level import by openlp.core.lib.db)
W: no module named openlp.core.lib.ItemCapabilities (top-level import by openlp.core.ui.servicemanager)
W: no module named openlp.plugins.custom.forms.EditCustomForm (top-level import by openlp.plugins.custom.customplugin)
W: no module named org (delayed import by xml.sax)
W: no module named openlp.core.lib.SpellTextEdit (top-level import by openlp.plugins.songs.forms.editversedialog)
W: no module named sqlalchemy.orm.EXT_CONTINUE (top-level import by sqlalchemy.orm.scoping)
W: no module named openlp.core.lib.build_lyrics_outline_css (top-level import by openlp.core.lib.renderer)
W: no module named openlp.plugins.songs.lib.VerseType (top-level import by openlp.plugins.songs.forms.editverseform)
W: no module named com (conditional import by openlp.plugins.songs.lib.oooimport)
W: no module named openlp.core.lib.str_to_bool (top-level import by openlp.core.lib.theme)
W: no module named sqlalchemy.sql.literal (top-level import by sqlalchemy)
W: no module named termios (top-level import by getpass)
W: no module named openlp.core.lib.build_lyrics_format_css (top-level import by openlp.core.lib.renderer)
W: no module named ctypes.byref (delayed import by urllib)
W: no module named openlp.plugins.custom.lib.CustomTab (top-level import by openlp.plugins.custom.customplugin)
W: no module named openlp.core.lib.BaseListWithDnD (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named openlp.plugins.bibles.lib.parse_reference (top-level import by openlp.plugins.bibles.lib.manager)
W: no module named java (delayed import by platform)
W: no module named openlp.core.ui.ServiceItemEditForm (top-level import by openlp.core.ui.servicemanager)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.custom.customplugin)
W: no module named openlp.core.lib.ThemeLevel (top-level import by openlp.core.lib.rendermanager)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.bibles.lib.db)
W: no module named _xmlrpclib (top-level import by xmlrpclib)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.plugins.presentations.lib.mediaitem)
W: no module named openlp.plugins.media.lib.MediaMediaItem (top-level import by openlp.plugins.media.mediaplugin)
W: no module named openlp.plugins.custom.lib.CustomXMLBuilder (top-level import by openlp.plugins.custom.forms.editcustomform)
W: no module named rourl2path (conditional import by urllib)
W: no module named pwd (delayed import by webbrowser)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.ui.themestab)
W: no module named PyQt4._qt (top-level import by PyQt4.QtWebKit)
W: no module named sqlalchemy.orm.class_mapper (delayed, conditional import by sqlalchemy.orm.interfaces)
W: no module named PyQt4._qt (top-level import by PyQt4.phonon)
W: no module named openlp.core.ui.HideMode (top-level import by openlp.plugins.presentations.lib.messagelistener)
W: no module named openlp.plugins.songusage.forms.SongUsageDeleteForm (top-level import by openlp.plugins.songusage.songusageplugin)
W: no module named openlp.core.lib.context_menu_separator (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.media.lib.mediaitem)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.plugindialog)
W: no module named fcntl (conditional import by subprocess)
W: no module named openlp.core.lib.BaseListWithDnD (top-level import by openlp.plugins.bibles.lib.mediaitem)
W: no module named openlp.core.lib.PluginStatus (top-level import by openlp.core.lib.pluginmanager)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.songs.forms.editsongform)
W: no module named openlp.core.ui.ScreenList (top-level import by __main__)
W: no module named sqlalchemy.or_ (top-level import by openlp.plugins.bibles.lib.db)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.editsongform)
W: no module named openlp.plugins.songs.forms.ImportWizardForm (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named openlp.core.lib.check_item_selected (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.ui.ThemeManager (top-level import by openlp.core.ui.mainwindow)
W: no module named pyodbc (delayed, conditional import by sqlalchemy.databases.mssql)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.core.ui.servicemanager)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.lib.mediamanageritem)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.bibles.lib.biblestab)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songusage.songusageplugin)
W: no module named multiprocessing.active_children (top-level import by multiprocessing.managers)
W: no module named openlp.plugins.songs.forms.EditSongForm (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named email.Generator (delayed import by email.message)
W: no module named mx (delayed import by sqlalchemy.databases.mxODBC)
W: no module named sqlalchemy.sql.or_ (top-level import by sqlalchemy)
W: no module named sqlalchemy.Table (top-level import by openlp.plugins.custom.lib.db)
W: no module named sqlalchemy.Table (top-level import by sqlalchemy.databases.sybase)
W: no module named openlp.core.lib.get_text_file_string (top-level import by openlp.core.ui.thememanager)
W: no module named sqlalchemy.orm.object_session (top-level import by sqlalchemy.orm.scoping)
W: no module named openlp.core.lib.MediaManagerItem (top-level import by openlp.plugins.bibles.lib.mediaitem)
W: no module named sqlalchemy.Table (top-level import by sqlalchemy.databases.mssql)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.core.ui.mainwindow)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.bibles.forms.bibleimportwizard)
W: no module named openlp.core.lib.resize_image (top-level import by openlp.core.lib.renderer)
W: no module named pymssql (delayed import by sqlalchemy.databases.mssql)
W: no module named sqlalchemy.orm.sessionmaker (top-level import by openlp.plugins.songs.lib.olpimport)
W: no module named openlp.core.lib.context_menu_action (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.lib.expand_tags (top-level import by openlp.core.lib.serviceitem)
W: no module named gestalt (delayed import by platform)
W: no module named enchant.checker.SpellChecker (top-level import by enchant.checker.CmdLineChecker)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.themestab)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.lib.olp1import)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.presentations.lib.messagelistener)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.interfaces)
W: no module named sqlalchemy.orm.object_mapper (top-level import by sqlalchemy.orm.query)
W: no module named sqlalchemy.sql.distinct (top-level import by sqlalchemy)
W: no module named openlp.core.lib.context_menu_action (top-level import by openlp.plugins.media.lib.mediaitem)
W: no module named sqlalchemy.sql.extract (top-level import by sqlalchemy)
W: no module named sqlalchemy.Column (top-level import by openlp.plugins.bibles.lib.db)
W: no module named psycopg2 (delayed import by sqlalchemy.databases.postgres)
W: no module named enchant.checker.SpellChecker (delayed import by enchant.checker.GtkSpellCheckerDialog)
W: no module named clr (conditional import by adodbapi.adodbapi)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named openlp.plugins.custom.lib.CustomXMLParser (top-level import by openlp.plugins.custom.forms.editcustomform)
W: no module named openlp.core.theme.Theme (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.custom.forms.editcustomform)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.core.lib.settingsmanager)
W: no module named openlp.core.lib.Receiver (top-level import by __main__)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.plugins.bibles.lib.manager)
W: no module named org (top-level import by pickle)
W: no module named enchant.DictNotFoundError (top-level import by openlp.core.lib.spelltextedit)
W: no module named sqlalchemy.sql.except_all (top-level import by sqlalchemy)
W: no module named openlp.plugins.presentations.lib.PresentationTab (top-level import by openlp.plugins.presentations.presentationplugin)
W: no module named sqlalchemy.sql.cast (top-level import by sqlalchemy)
W: no module named sqlalchemy.orm.relation (top-level import by openlp.plugins.songs.lib.db)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.core.ui.settingsdialog)
W: no module named openlp.core.utils.LanguageManager (top-level import by openlp.core.ui.mainwindow)
W: no module named openlp.core.lib.ItemCapabilities (top-level import by openlp.plugins.media.lib.mediaitem)
W: no module named sqlalchemy.sql.intersect (top-level import by sqlalchemy)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.lib.songimport)
W: no module named sqlalchemy.orm.class_mapper (top-level import by sqlalchemy.orm.scoping)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.util)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.lib.opensongimport)
W: no module named openlp.core.lib.SettingsTab (top-level import by openlp.plugins.bibles.lib.biblestab)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.aboutform)
W: no module named openlp.plugins.custom.lib.CustomMediaItem (top-level import by openlp.plugins.custom.customplugin)
W: no module named sqlalchemy.orm.scoped_session (top-level import by openlp.plugins.songs.lib.olpimport)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.amendthemeform)
W: no module named sqlalchemy.engine.engine_from_config (top-level import by sqlalchemy)
W: no module named openlp.core.lib.SettingsTab (top-level import by openlp.core.ui.themestab)
W: no module named openlp.core.lib.OpenLPToolbar (top-level import by openlp.core.ui.servicemanager)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.ui.maindisplay)
W: no module named openlp.core.lib.PluginStatus (top-level import by openlp.core.ui.pluginform)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.bibles.lib.http)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.plugins.remotes.lib.httpserver)
W: no module named sqlalchemy.Table (top-level import by openlp.plugins.songs.lib.db)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.utils)
W: no module named openlp.core.lib.RenderManager (top-level import by openlp.core.ui.mainwindow)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.plugins.alerts.alertsplugin)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.lib.plugin)
W: no module named sqlalchemy.ForeignKey (top-level import by openlp.plugins.bibles.lib.db)
W: no module named openlp.plugins.songs.lib.SongMediaItem (top-level import by openlp.plugins.songs.songsplugin)
W: no module named sqlalchemy.Index (top-level import by openlp.plugins.songs.lib.db)
W: no module named multiprocessing.TimeoutError (top-level import by multiprocessing.pool)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.songusage.forms.songusagedetaildialog)
W: no module named enchant.DictWithPWL (delayed import by enchant.checker.tests)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.lib.songstab)
W: no module named openlp.core.ui.AdvancedTab (top-level import by openlp.core.ui.settingsform)
W: no module named MySQLdb (delayed import by sqlalchemy.databases.mysql)
W: no module named openlp.plugins.presentations.lib.PresentationMediaItem (top-level import by openlp.plugins.presentations.presentationplugin)
W: no module named openlp.plugins.alerts.forms.AlertForm (top-level import by openlp.plugins.alerts.alertsplugin)
W: no module named ctypes.c_int (delayed import by urllib)
W: no module named xml.dom.XML_NAMESPACE (delayed import by xml.dom.pulldom)
W: no module named ctypes.c_void_p (delayed import by urllib)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.plugins.bibles.lib.osis)
W: no module named sqlalchemy.create_engine (top-level import by openlp.core.lib.db)
W: no module named win32com.client._get_good_object_ (top-level import by win32com.client.util)
W: no module named openlp.core.ui.MainDisplay (top-level import by openlp.core.ui.slidecontroller)
W: no module named openlp.core.lib.resize_image (top-level import by openlp.plugins.presentations.lib.presentationcontroller)
W: no module named openlp.core.lib.SettingsTab (top-level import by openlp.plugins.remotes.lib.remotetab)
W: no module named openlp.core.ui.MediaDockManager (top-level import by openlp.core.ui.mainwindow)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.session)
W: no module named sqlalchemy.Column (top-level import by sqlalchemy.databases.mssql)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.songs.forms.editversedialog)
W: no module named multiprocessing.Process (top-level import by multiprocessing.managers)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.presentations.lib.presentationcontroller)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.lib.mediaitem)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.custom.forms.editcustomdialog)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.alerts.lib.alertsmanager)
W: no module named sgmlop (top-level import by xmlrpclib)
W: no module named MacOS (conditional import by py_compile)
W: no module named multiprocessing.cpu_count (top-level import by multiprocessing.dummy)
W: no module named _dummy_threading (top-level import by dummy_threading)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.mainwindow)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.songmaintenanceform)
W: no module named openlp.plugins.presentations.lib.PresentationController (top-level import by openlp.plugins.presentations.presentationplugin)
W: no module named openlp.core.lib.OpenLPToolbar (top-level import by openlp.core.lib.mediamanageritem)
W: no module named sqlalchemy.sql.union_all (top-level import by sqlalchemy)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.bibles.lib.opensong)
W: no module named openlp.core.lib.ItemCapabilities (top-level import by openlp.plugins.bibles.lib.mediaitem)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.bibles.lib.osis)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.ui.settingsform)
W: no module named enchant.tokenize.get_tokenizer (top-level import by enchant.checker)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.plugins.bibles.lib.manager)
W: no module named org (top-level import by copy)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.core.ui.servicemanager)
W: no module named sqlalchemy.MetaData (top-level import by openlp.plugins.songs.lib.olpimport)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.songs.forms.songmaintenancedialog)
W: no module named sqlalchemy.sql.select (top-level import by sqlalchemy.databases.mssql)
W: no module named openlp.core.lib.SettingsManager (top-level import by openlp.plugins.bibles.forms.importwizardform)
W: no module named multiprocessing.current_process (top-level import by multiprocessing.connection)
W: no module named sqlalchemy.orm.sessionmaker (top-level import by openlp.core.lib.db)
W: no module named sqlalchemy.sql.desc (top-level import by sqlalchemy)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songusage.forms.songusagedeletedialog)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.bibles.lib.mediaitem)
W: no module named ctypes.cdll (delayed import by urllib)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.lib)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.media.mediaplugin)
W: no module named openlp.core.lib.SettingsTab (top-level import by openlp.plugins.presentations.lib.presentationtab)
W: no module named MySQLdb (delayed, conditional import by sqlalchemy.databases.mysql)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.presentations.lib.mediaitem)
W: no module named openlp.core.lib.MediaManagerItem (top-level import by openlp.plugins.media.lib.mediaitem)
W: no module named sqlalchemy.orm.object_session (top-level import by sqlalchemy.orm.dynamic)
W: no module named sqlalchemy.sql.modifier (top-level import by sqlalchemy)
W: no module named _xmlplus (top-level import by xml)
W: no module named sqlalchemy.Column (top-level import by openlp.plugins.songusage.lib.db)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.advancedtab)
W: no module named sqlalchemy.sql.and_ (top-level import by sqlalchemy)
W: no module named sqlalchemy.MetaData (top-level import by sqlalchemy.databases.mssql)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.dependency)
W: no module named openlp.core.lib.ThemeLevel (top-level import by openlp.core.ui.themestab)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.presentations.lib.presentationtab)
W: no module named openlp.core.utils.get_images_filter (top-level import by openlp.core.ui.amendthemeform)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.plugins.presentations.presentationplugin)
W: no module named openlp.plugins.presentations.lib.MessageListener (top-level import by openlp.plugins.presentations.lib.mediaitem)
W: no module named openlp.plugins.bibles.forms.ImportWizardForm (top-level import by openlp.plugins.bibles.lib.mediaitem)
W: no module named openlp.core.utils.AppLocation (top-level import by __main__)
W: no module named sqlalchemy.ForeignKey (top-level import by openlp.plugins.songs.lib.db)
W: no module named openlp.core.lib.OpenLPToolbar (top-level import by openlp.core.ui.thememanager)
W: no module named ctypes.cdll (conditional import by openlp.plugins.presentations.lib.pptviewcontroller)
W: no module named pwd (delayed import by getpass)
W: no module named sqlalchemy.sql.and_ (top-level import by openlp.plugins.songusage.forms.songusagedetailform)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.core.ui.amendthemedialog)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.plugins.songs.forms.songimportwizard)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.serviceitemeditdialog)
W: no module named openlp.core.lib.resize_image (top-level import by openlp.core.ui.maindisplay)
W: no module named openlp.core.lib.BaseListWithDnD (top-level import by openlp.plugins.custom.lib.mediaitem)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.topicsdialog)
W: no module named PyQt4._qt (top-level import by PyQt4.QtGui)
W: no module named sqlalchemy.sql.update (top-level import by sqlalchemy)
W: no module named multiprocessing.current_process (delayed, conditional import by logging)
W: no module named multiprocessing.Pool (top-level import by multiprocessing.managers)
W: no module named sqlalchemy.create_engine (delayed, conditional import by sqlalchemy.schema)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.bibles.forms.importwizardform)
W: no module named openlp.core.lib.context_menu_action (top-level import by openlp.core.lib.mediamanageritem)
W: no module named posix (conditional import by os)
W: no module named sqlalchemy.sql.bindparam (top-level import by sqlalchemy)
W: no module named xml.dom.DOMImplementation (top-level import by xml.dom.domreg)
W: no module named openlp.core.utils.add_actions (top-level import by openlp.core.ui.mainwindow)
W: no module named sqlalchemy.create_engine (top-level import by openlp.plugins.songs.lib.olpimport)
W: no module named multiprocessing.cpu_count (top-level import by multiprocessing.pool)
W: no module named multiprocessing.AuthenticationError (top-level import by multiprocessing.connection)
W: no module named openlp.core.ui.ThemesTab (top-level import by openlp.core.ui.settingsform)
W: no module named openlp.core.lib.check_item_selected (top-level import by openlp.plugins.media.lib.mediaitem)
W: no module named sqlalchemy.orm.class_mapper (top-level import by openlp.plugins.bibles.lib.db)
W: no module named sqlalchemy.String (top-level import by sqlalchemy.databases.sybase)
W: no module named openlp.core.lib.context_menu_action (top-level import by openlp.core.ui.servicemanager)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named pwd (delayed, conditional import by posixpath)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.strategies)
W: no module named mx (top-level import by adodbapi.adodbapi)
W: no module named sqlalchemy.sql.alias (top-level import by sqlalchemy)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.authorsdialog)
W: no module named sqlalchemy.Table (top-level import by openlp.plugins.alerts.lib.db)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.bibles.lib.mediaitem)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.images.imageplugin)
W: no module named sqlalchemy.orm.mapperlib (delayed import by sqlalchemy.orm.util)
W: no module named openlp.core.lib.ServiceItem (top-level import by openlp.core.lib.rendermanager)
W: no module named openlp.core.lib.OpenLPDockWidget (top-level import by openlp.core.ui.mainwindow)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.songs.forms.songimportwizard)
W: no module named openlp.core.lib.Plugin (top-level import by openlp.plugins.songusage.songusageplugin)
W: no module named openlp.core.utils.AppLocation (top-level import by openlp.plugins.presentations.lib.presentationcontroller)
W: no module named openlp.core.lib.context_menu_action (top-level import by openlp.core.lib.spelltextedit)
W: no module named openlp.core.lib.build_icon (top-level import by openlp.core.ui.thememanager)
W: no module named openlp.core.lib.check_item_selected (top-level import by openlp.plugins.presentations.lib.mediaitem)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.pluginform)
W: no module named enchant.checker.SpellChecker (delayed import by enchant.checker.wxSpellCheckerDialog)
W: no module named sqlalchemy.DefaultClause (top-level import by sqlalchemy.databases.sqlite)
W: no module named openlp.core.lib.ServiceItem (top-level import by openlp.core.ui.servicemanager)
W: no module named openlp.core.lib.ItemCapabilities (top-level import by openlp.core.ui.slidecontroller)
W: no module named openlp.core.utils.get_images_filter (top-level import by openlp.plugins.images.lib.mediaitem)
W: no module named pyodbc (delayed import by sqlalchemy.databases.mssql)
W: no module named openlp.core.lib.OpenLPToolbar (top-level import by openlp.core.ui.slidecontroller)
W: no module named System (conditional import by adodbapi.adodbapi)
W: no module named openlp.core.lib.SettingsTab (top-level import by openlp.plugins.custom.lib.customtab)
W: no module named openlp.core.lib.Receiver (top-level import by openlp.plugins.bibles.lib.csvbible)
W: no module named openlp.core.lib.translate (top-level import by openlp.core.ui.generaltab)
W: no module named openlp.core.lib.ThemeLevel (top-level import by openlp.core.ui.servicemanager)
W: no module named sqlalchemy.exceptions (top-level import by sqlalchemy.orm.scoping)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.alerts.forms.alertform)
W: no module named mypyodbc (delayed import by sqlalchemy.databases.sybase)
W: no module named sqlalchemy.sql.intersect_all (top-level import by sqlalchemy)
W: no module named openlp.core.lib.translate (top-level import by openlp.plugins.presentations.lib.mediaitem)
W: __all__ is built strangely at line 0 - dummy_threading (C:\Python26\lib\dummy_threading.pyc)
W: delayed exec statement detected at line 0 - bdb (C:\Python26\lib\bdb.pyc)
W: delayed eval hack detected at line 0 - bdb (C:\Python26\lib\bdb.pyc)
W: delayed eval hack detected at line 0 - bdb (C:\Python26\lib\bdb.pyc)
W: delayed __import__ hack detected at line 0 - optparse (C:\Python26\lib\optparse.pyc)
W: delayed conditional __import__ hack detected at line 0 - pkg_resources (build/bdist.linux-i686/egg/pkg_resources.pyc)
W: delayed conditional exec statement detected at line 0 - pkg_resources (build/bdist.linux-i686/egg/pkg_resources.pyc)
W: delayed conditional __import__ hack detected at line 0 - pkg_resources (build/bdist.linux-i686/egg/pkg_resources.pyc)
W: delayed __import__ hack detected at line 0 - pkg_resources (build/bdist.linux-i686/egg/pkg_resources.pyc)
W: delayed conditional __import__ hack detected at line 0 - doctest (C:\Python26\lib\doctest.pyc)
W: delayed exec statement detected at line 0 - doctest (C:\Python26\lib\doctest.pyc)
W: delayed conditional __import__ hack detected at line 0 - doctest (C:\Python26\lib\doctest.pyc)
W: __all__ is built strangely at line 0 - sqlalchemy.orm.interfaces (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\orm\interfaces.pyc)
W: __all__ is built strangely at line 0 - tokenize (C:\Python26\lib\tokenize.pyc)
W: __all__ is built strangely at line 0 - sqlalchemy.engine (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\engine\__init__.pyc)
W: delayed __import__ hack detected at line 0 - pickle (C:\Python26\lib\pickle.pyc)
W: delayed __import__ hack detected at line 0 - pickle (C:\Python26\lib\pickle.pyc)
W: top-level conditional exec statement detected at line 0 - sqlalchemy.sql.util (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\sql\util.pyc)
W: top-level conditional exec statement detected at line 0 - sqlalchemy.sql.util (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\sql\util.pyc)
W: delayed conditional __import__ hack detected at line 0 - openlp.core.lib.pluginmanager (c:\Documents and Settings\raoul\My Documents\My Projects\openlp\movements\openlp\core\lib\pluginmanager.pyc)
W: delayed conditional exec statement detected at line 0 - multiprocessing.sharedctypes (C:\Python26\lib\multiprocessing\sharedctypes.pyc)
W: delayed __import__ hack detected at line 0 - encodings (C:\Python26\lib\encodings\__init__.pyc)
W: __all__ is built strangely at line 0 - sqlalchemy.databases.mysql (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\databases\mysql.pyc)
W: delayed exec statement detected at line 0 - sqlalchemy.orm.attributes (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\orm\attributes.pyc)
W: delayed conditional __import__ hack detected at line 0 - openlp.plugins.presentations.presentationplugin (c:\Documents and Settings\raoul\My Documents\My Projects\openlp\movements\openlp\plugins\presentations\presentationplugin.pyc)
W: delayed __import__ hack detected at line 0 - enchant.tokenize (C:\Python26\lib\site-packages\enchant\tokenize\__init__.pyc)
W: __all__ is built strangely at line 0 - multiprocessing (C:\Python26\lib\multiprocessing\__init__.pyc)
W: __all__ is built strangely at line 0 - dis (C:\Python26\lib\dis.pyc)
W: __all__ is built strangely at line 0 - sqlalchemy.databases (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\databases\__init__.pyc)
W: delayed __import__ hack detected at line 0 - win32com.server.policy (C:\Python26\lib\site-packages\win32com\server\policy.pyc)
W: __all__ is built strangely at line 0 - sqlalchemy.orm.mapper (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\orm\mapper.pyc)
W: top-level exec statement detected at line 0 - hashlib (C:\Python26\lib\hashlib.pyc)
W: top-level conditional exec statement detected at line 0 - hashlib (C:\Python26\lib\hashlib.pyc)
W: delayed conditional eval hack detected at line 0 - warnings (C:\Python26\lib\warnings.pyc)
W: delayed conditional __import__ hack detected at line 0 - warnings (C:\Python26\lib\warnings.pyc)
W: delayed exec statement detected at line 0 - cgi (C:\Python26\lib\cgi.pyc)
W: delayed __import__ hack detected at line 0 - email (C:\Python26\lib\email\__init__.pyc)
W: __all__ is built strangely at line 0 - sqlalchemy.orm (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\orm\__init__.pyc)
W: delayed __import__ hack detected at line 0 - ctypes (C:\Python26\lib\ctypes\__init__.pyc)
W: delayed __import__ hack detected at line 0 - ctypes (C:\Python26\lib\ctypes\__init__.pyc)
W: delayed conditional __import__ hack detected at line 0 - xml.dom.domreg (C:\Python26\lib\xml\dom\domreg.pyc)
W: delayed exec statement detected at line 0 - pdb (C:\Python26\lib\pdb.pyc)
W: delayed conditional eval hack detected at line 0 - pdb (C:\Python26\lib\pdb.pyc)
W: delayed eval hack detected at line 0 - pdb (C:\Python26\lib\pdb.pyc)
W: delayed conditional eval hack detected at line 0 - pdb (C:\Python26\lib\pdb.pyc)
W: delayed eval hack detected at line 0 - pdb (C:\Python26\lib\pdb.pyc)
W: delayed conditional __import__ hack detected at line 0 - unittest (C:\Python26\lib\unittest.pyc)
W: delayed conditional __import__ hack detected at line 0 - unittest (C:\Python26\lib\unittest.pyc)
W: delayed conditional __import__ hack detected at line 0 - pkgutil (C:\Python26\lib\pkgutil.pyc)
W: delayed conditional __import__ hack detected at line 0 - pkgutil (C:\Python26\lib\pkgutil.pyc)
W: __all__ is built strangely at line 0 - sqlalchemy.orm.properties (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\orm\properties.pyc)
W: delayed conditional exec statement detected at line 0 - iu (c:\Documents and Settings\raoul\My Documents\My Projects\pyinstaller\iu.pyc)
W: delayed conditional exec statement detected at line 0 - iu (c:\Documents and Settings\raoul\My Documents\My Projects\pyinstaller\iu.pyc)
W: __all__ is built strangely at line 0 - sqlalchemy.sql (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\sql\__init__.pyc)
W: __all__ is built strangely at line 0 - collections (C:\Python26\lib\collections.pyc)
W: delayed exec statement detected at line 0 - collections (C:\Python26\lib\collections.pyc)
W: delayed __import__ hack detected at line 0 - sqlalchemy.engine.url (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\engine\url.pyc)
W: delayed exec statement detected at line 0 - multiprocessing.managers (C:\Python26\lib\multiprocessing\managers.pyc)
W: delayed exec statement detected at line 0 - socket (C:\Python26\lib\socket.pyc)
W: delayed conditional __import__ hack detected at line 0 - win32com.client.gencache (C:\Python26\lib\site-packages\win32com\client\gencache.pyc)
W: delayed __import__ hack detected at line 0 - win32com.client.gencache (C:\Python26\lib\site-packages\win32com\client\gencache.pyc)
W: delayed eval hack detected at line 0 - os (C:\Python26\lib\os.pyc)
W: __all__ is built strangely at line 0 - __future__ (C:\Python26\lib\__future__.pyc)
W: delayed __import__ hack detected at line 0 - win32com.client.makepy (C:\Python26\lib\site-packages\win32com\client\makepy.pyc)
W: delayed exec statement detected at line 0 - win32com.client.dynamic (C:\Python26\lib\site-packages\win32com\client\dynamic.pyc)
W: __all__ is built strangely at line 0 - sqlalchemy (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\__init__.pyc)
W: delayed __import__ hack detected at line 0 - xml.sax (C:\Python26\lib\xml\sax\__init__.pyc)
W: delayed eval hack detected at line 0 - gettext (C:\Python26\lib\gettext.pyc)
W: delayed eval hack detected at line 0 - sqlalchemy.util (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\util.pyc)
W: delayed exec statement detected at line 0 - sqlalchemy.util (C:\Python26\lib\site-packages\sqlalchemy-0.5.8-py2.6.egg\sqlalchemy\util.pyc)

View File

@ -63,6 +63,7 @@ IGNORED_PATHS = [u'scripts']
IGNORED_FILES = [u'setup.py'] IGNORED_FILES = [u'setup.py']
verbose_mode = False verbose_mode = False
quiet_mode = False
class Command(object): class Command(object):
""" """
@ -128,6 +129,20 @@ class CommandStack(object):
results.append(str((item[u'command'], ))) results.append(str((item[u'command'], )))
return u'[%s]' % u', '.join(results) return u'[%s]' % u', '.join(results)
def print_quiet(text, linefeed=True):
"""
This method checks to see if we are in quiet mode, and if not prints
``text`` out.
``text``
The text to print.
"""
global quiet_mode
if not quiet_mode:
if linefeed:
print text
else:
print text,
def print_verbose(text): def print_verbose(text):
""" """
@ -137,8 +152,8 @@ def print_verbose(text):
``text`` ``text``
The text to print. The text to print.
""" """
global verbose_mode global verbose_mode, quiet_mode
if verbose_mode: if not quiet_mode and verbose_mode:
print u' %s' % text print u' %s' % text
def run(command): def run(command):
@ -155,7 +170,6 @@ def run(command):
print_verbose(u'ReadyRead: %s' % QtCore.QString(process.readAll())) print_verbose(u'ReadyRead: %s' % QtCore.QString(process.readAll()))
print_verbose(u'Error(s):\n%s' % process.readAllStandardError()) print_verbose(u'Error(s):\n%s' % process.readAllStandardError())
print_verbose(u'Output:\n%s' % process.readAllStandardOutput()) print_verbose(u'Output:\n%s' % process.readAllStandardOutput())
print u' Done.'
def update_export_at_pootle(source_filename): def update_export_at_pootle(source_filename):
""" """
@ -170,7 +184,7 @@ def update_export_at_pootle(source_filename):
print_verbose(u'Accessing: %s' % (REVIEW_URL)) print_verbose(u'Accessing: %s' % (REVIEW_URL))
page = urllib.urlopen(REVIEW_URL) page = urllib.urlopen(REVIEW_URL)
page.close() page.close()
def download_file(source_filename, dest_filename): def download_file(source_filename, dest_filename):
""" """
@ -194,7 +208,7 @@ def download_translations():
""" """
This method downloads the translation files from the Pootle server. This method downloads the translation files from the Pootle server.
""" """
print 'Download translation files from Pootle' print_quiet(u'Download translation files from Pootle')
page = urllib.urlopen(SERVER_URL) page = urllib.urlopen(SERVER_URL)
soup = BeautifulSoup(page) soup = BeautifulSoup(page)
languages = soup.findAll(text=re.compile(r'.*\.ts')) languages = soup.findAll(text=re.compile(r'.*\.ts'))
@ -205,14 +219,14 @@ def download_translations():
language_file) language_file)
print_verbose(u'Get Translation File: %s' % filename) print_verbose(u'Get Translation File: %s' % filename)
download_file(language_file, filename) download_file(language_file, filename)
print u' Done.' print_quiet(u' Done.')
def prepare_project(): def prepare_project():
""" """
This method creates the project file needed to update the translation files This method creates the project file needed to update the translation files
and compile them into .qm files. and compile them into .qm files.
""" """
print u'Generating the openlp.pro file' print_quiet(u'Generating the openlp.pro file')
lines = [] lines = []
start_dir = os.path.abspath(u'..') start_dir = os.path.abspath(u'..')
start_dir = start_dir + os.sep start_dir = start_dir + os.sep
@ -251,10 +265,10 @@ def prepare_project():
file = open(os.path.join(start_dir, u'openlp.pro'), u'w') file = open(os.path.join(start_dir, u'openlp.pro'), u'w')
file.write(u'\n'.join(lines).encode('utf8')) file.write(u'\n'.join(lines).encode('utf8'))
file.close() file.close()
print u' Done.' print_quiet(u' Done.')
def update_translations(): def update_translations():
print u'Update the translation files' print_quiet(u'Update the translation files')
if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')): if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')):
print u'You have no generated a project file yet, please run this ' + \ print u'You have no generated a project file yet, please run this ' + \
u'script with the -p option.' u'script with the -p option.'
@ -265,7 +279,7 @@ def update_translations():
os.chdir(os.path.abspath(u'scripts')) os.chdir(os.path.abspath(u'scripts'))
def generate_binaries(): def generate_binaries():
print u'Generate the related *.qm files' print_quiet(u'Generate the related *.qm files')
if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')): if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')):
print u'You have not generated a project file yet, please run this ' + \ print u'You have not generated a project file yet, please run this ' + \
u'script with the -p option. It is also recommended that you ' + \ u'script with the -p option. It is also recommended that you ' + \
@ -283,8 +297,9 @@ def generate_binaries():
src_list = os.listdir(src_path) src_list = os.listdir(src_path)
for file in src_list: for file in src_list:
if re.search('.qm$', file): if re.search('.qm$', file):
copy(os.path.join(src_path, u'%s' % file), copy(os.path.join(src_path, u'%s' % file),
os.path.join(dest_path, u'%s' % file)) os.path.join(dest_path, u'%s' % file))
print_quiet(u' Done.')
def create_translation(language): def create_translation(language):
@ -294,17 +309,17 @@ def create_translation(language):
``language`` ``language``
The language file to create. The language file to create.
""" """
print "Create new Translation File" print_quiet(u'Create new Translation File')
if not language.endswith(u'.ts'): if not language.endswith(u'.ts'):
language += u'.ts' language += u'.ts'
filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n', language) filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n', language)
download_file(u'en.ts', filename) download_file(u'en.ts', filename)
print u' ** Please Note **' print_quiet(u' ** Please Note **')
print u' In order to get this file into OpenLP and onto the Pootle ' + \ print_quiet(u' In order to get this file into OpenLP and onto the '
u'translation server you will need to subscribe to the OpenLP' + \ u'Pootle translation server you will need to subscribe to the '
u'Translators mailing list, and request that your language file ' + \ u'OpenLP Translators mailing list, and request that your language '
u'be added to the project.' u'file be added to the project.')
print u' Done' print_quiet(u' Done.')
def process_stack(command_stack): def process_stack(command_stack):
""" """
@ -315,9 +330,9 @@ def process_stack(command_stack):
The command stack to process. The command stack to process.
""" """
if command_stack: if command_stack:
print u'Processing %d commands...' % len(command_stack) print_quiet(u'Processing %d commands...' % len(command_stack))
for command in command_stack: for command in command_stack:
print u'%d.' % (command_stack.current_index), print_quiet(u'%d.' % (command_stack.current_index), False)
if command == Command.Download: if command == Command.Download:
download_translations() download_translations()
elif command == Command.Prepare: elif command == Command.Prepare:
@ -329,12 +344,12 @@ def process_stack(command_stack):
elif command == Command.Create: elif command == Command.Create:
arguments = command_stack.arguments() arguments = command_stack.arguments()
create_translation(*arguments) create_translation(*arguments)
print u'Finished processing commands.' print_quiet(u'Finished processing commands.')
else: else:
print u'No commands to process.' print_quiet(u'No commands to process.')
def main(): def main():
global verbose_mode global verbose_mode, quiet_mode
# Set up command line options. # Set up command line options.
usage = u'%prog [options]\nOptions are parsed in the order they are ' + \ usage = u'%prog [options]\nOptions are parsed in the order they are ' + \
u'listed below. If no options are given, "-dpug" will be used.\n\n' + \ u'listed below. If no options are given, "-dpug" will be used.\n\n' + \
@ -352,6 +367,8 @@ def main():
help='compile .ts files into .qm files') help='compile .ts files into .qm files')
parser.add_option('-v', '--verbose', dest='verbose', action='store_true', parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
help='show extra information while processing translations') help='show extra information while processing translations')
parser.add_option('-q', '--quiet', dest='quiet', action='store_true',
help='suppress all output other than errors')
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
# Create and populate the command stack # Create and populate the command stack
command_stack = CommandStack() command_stack = CommandStack()
@ -366,6 +383,7 @@ def main():
if options.generate: if options.generate:
command_stack.append(Command.Generate) command_stack.append(Command.Generate)
verbose_mode = options.verbose verbose_mode = options.verbose
quiet_mode = options.quiet
if not command_stack: if not command_stack:
command_stack.append(Command.Download) command_stack.append(Command.Download)
command_stack.append(Command.Prepare) command_stack.append(Command.Prepare)

View File

@ -87,20 +87,33 @@ windows-builder.py
""" """
import os import os
import sys
from shutil import copy from shutil import copy
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
python_exe = sys.executable
innosetup_exe = os.path.join(os.getenv(u'PROGRAMFILES'), 'Inno Setup 5',
u'ISCC.exe')
# Base paths
script_path = os.path.split(os.path.abspath(__file__))[0] script_path = os.path.split(os.path.abspath(__file__))[0]
branch_path = os.path.abspath(os.path.join(script_path, u'..')) branch_path = os.path.abspath(os.path.join(script_path, u'..'))
site_packages = os.path.join(os.path.split(python_exe)[0], u'Lib',
u'site-packages')
# Files and executables
pyi_build = os.path.abspath(os.path.join(branch_path, u'..', u'..',
u'pyinstaller', u'Build.py'))
lrelease_exe = os.path.join(site_packages, u'PyQt4', u'bin', u'lrelease.exe')
i18n_utils = os.path.join(script_path, u'translation_utils.py')
# Paths
source_path = os.path.join(branch_path, u'openlp') source_path = os.path.join(branch_path, u'openlp')
i18n_path = os.path.join(branch_path, u'resources', u'i18n') i18n_path = os.path.join(branch_path, u'resources', u'i18n')
winres_path = os.path.join(branch_path, u'resources', u'windows')
build_path = os.path.join(branch_path, u'build', u'pyi.win32', u'OpenLP') build_path = os.path.join(branch_path, u'build', u'pyi.win32', u'OpenLP')
dist_path = os.path.join(branch_path, u'dist', u'OpenLP') dist_path = os.path.join(branch_path, u'dist', u'OpenLP')
pyinstaller_path = os.path.abspath(os.path.join(branch_path, u'..', u'..', u'pyinstaller')) enchant_path = os.path.join(site_packages, u'enchant')
innosetup_path = os.path.join(os.getenv(u'PROGRAMFILES'), 'Inno Setup 5')
iss_path = os.path.join(branch_path, u'resources', u'innosetup')
lrelease_path = u'C:\\Python26\\Lib\\site-packages\\PyQt4\\bin\\lrelease.exe'
enchant_path = u'C:\\Python26\\Lib\\site-packages\\enchant'
def clean_build_directories(): def clean_build_directories():
#if not os.path.exists(build_path) #if not os.path.exists(build_path)
@ -117,11 +130,13 @@ def clean_build_directories():
def run_pyinstaller(): def run_pyinstaller():
print u'Running PyInstaller...' print u'Running PyInstaller...'
os.chdir(branch_path) os.chdir(branch_path)
pyinstaller = Popen((u'python', os.path.join(pyinstaller_path, u'Build.py'), pyinstaller = Popen((python_exe, pyi_build, u'-y', u'-o', build_path,
u'-y', u'OpenLP.spec')) os.path.join(winres_path, u'OpenLP.spec')), stdout=PIPE)
output, error = pyinstaller.communicate()
code = pyinstaller.wait() code = pyinstaller.wait()
if code != 0: if code != 0:
raise Exception(u'Error running PyInstaller Build.py') print output
raise Exception(u'Error running PyInstaller')
def write_version_file(): def write_version_file():
print u'Writing version file...' print u'Writing version file...'
@ -155,7 +170,7 @@ def copy_enchant():
for root, dirs, files in os.walk(source): for root, dirs, files in os.walk(source):
for filename in files: for filename in files:
if not filename.endswith(u'.pyc') and not filename.endswith(u'.pyo'): if not filename.endswith(u'.pyc') and not filename.endswith(u'.pyo'):
dest_path = os.path.join(dest, root[len(source)+1:]) dest_path = os.path.join(dest, root[len(source) + 1:])
if not os.path.exists(dest_path): if not os.path.exists(dest_path):
os.makedirs(dest_path) os.makedirs(dest_path)
copy(os.path.join(root, filename), copy(os.path.join(root, filename),
@ -176,16 +191,18 @@ def copy_plugins():
def copy_windows_files(): def copy_windows_files():
print u'Copying extra files for Windows...' print u'Copying extra files for Windows...'
copy(os.path.join(iss_path, u'OpenLP.ico'), os.path.join(dist_path, u'OpenLP.ico')) copy(os.path.join(winres_path, u'OpenLP.ico'),
copy(os.path.join(iss_path, u'LICENSE.txt'), os.path.join(dist_path, u'LICENSE.txt')) os.path.join(dist_path, u'OpenLP.ico'))
copy(os.path.join(winres_path, u'LICENSE.txt'),
os.path.join(dist_path, u'LICENSE.txt'))
def update_translations(): def update_translations():
print u'Updating translations...' print u'Updating translations...'
os.chdir(script_path) os.chdir(script_path)
translation_utils = Popen(u'python translation_utils.py -dpu') translation_utils = Popen((python_exe, i18n_utils, u'-qdpu'))
code = translation_utils.wait() code = translation_utils.wait()
if code != 0: if code != 0:
print u'Error running translation_utils.py' raise Exception(u'Error running translation_utils.py')
def compile_translations(): def compile_translations():
print u'Compiling translations...' print u'Compiling translations...'
@ -197,19 +214,17 @@ def compile_translations():
source_path = os.path.join(i18n_path, file) source_path = os.path.join(i18n_path, file)
dest_path = os.path.join(dist_path, u'i18n', dest_path = os.path.join(dist_path, u'i18n',
file.replace(u'.ts', u'.qm')) file.replace(u'.ts', u'.qm'))
lconvert = Popen(u'"%s" "%s" -qm "%s"' % (lrelease_path, \ lconvert = Popen((lrelease_exe, u'-compress', u'-silent',
source_path, dest_path)) source_path, u'-qm', dest_path))
code = lconvert.wait() code = lconvert.wait()
if code != 0: if code != 0:
print 'Error running lconvert on %s' % source_path raise Exception('Error running lconvert on %s' % source_path)
def run_innosetup(): def run_innosetup():
print u'Running Inno Setup...' print u'Running Inno Setup...'
os.chdir(iss_path) os.chdir(winres_path)
run_command = u'"%s" "%s"' % (os.path.join(innosetup_path, u'ISCC.exe'), innosetup = Popen((innosetup_exe,
os.path.join(iss_path, u'OpenLP-2.0.iss')) os.path.join(winres_path, u'OpenLP-2.0.iss'), u'/q'))
print run_command
innosetup = Popen(run_command)
code = innosetup.wait() code = innosetup.wait()
if code != 0: if code != 0:
raise Exception(u'Error running Inno Setup') raise Exception(u'Error running Inno Setup')
@ -221,9 +236,9 @@ def main():
print "Branch path:", branch_path print "Branch path:", branch_path
print "Source path:", source_path print "Source path:", source_path
print "\"dist\" path:", dist_path print "\"dist\" path:", dist_path
print "PyInstaller path:", pyinstaller_path print "PyInstaller:", pyi_build
print "Inno Setup path:", innosetup_path print "Inno Setup path:", innosetup_path
print "ISS file path:", iss_path print "Windows resources:", winres_path
#clean_build_directories() #clean_build_directories()
run_pyinstaller() run_pyinstaller()
write_version_file() write_version_file()