- added a OpenLyrics exporter

- save OpenLyrics meta data to songs (service items and xml files)
- fixed a bug in OpenSong import
- fixed verse type when importing xml songs (instead of "V" we add "Verse" to the database)
- verse order in exported songs is now lower case (service items and xml files)
- updated copyright information (not the scripts directory)

Notes to the OpenLyrics export dialog:
- sorting does not work very nicely
- the search does only consider the title of the qlistwidgetitem:
    "title (author, author, author)"
- the search does not remove , and . and the like
- the buttons check/uncheck all songs (also hidden ones)

I thought about two scenarios:
1) Somebody wants to export all songs. He'll use the "check all" button and will not need the search box.

2) The user wants to export single songs. He will use the search box and double click the songs (or check the checkbox).

bzr-revno: 1286
This commit is contained in:
andreas 2011-02-10 17:38:42 +00:00 committed by Tim Bentley
commit 12677a4d36
16 changed files with 507 additions and 267 deletions

View File

@ -4,8 +4,8 @@
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 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 #

View File

@ -28,7 +28,7 @@ The :mod:`ui` module provides the core user interface for OpenLP
"""
from PyQt4 import QtGui
from openlp.core.lib import translate, Receiver
from openlp.core.lib import translate
class HideMode(object):
"""

View File

@ -164,9 +164,10 @@ class Ui_AboutDialog(object):
self.licenseTextEdit.setPlainText(translate('OpenLP.AboutForm',
'Copyright \xa9 2004-2011 Raoul Snyman\n'
'Portions copyright \xa9 2004-2011 '
'Tim Bentley, Jonathan Corwin, Michael Gorven, Scott Guerrieri, '
'Christian Richter, Maikel Stuivenberg, Martin Thompson, Jon '
'Tibble, Carsten Tinggaard\n'
'Tim Bentley, Jonathan Corwin, Michael Gorven, Scott Guerrieri,\n'
'Meinert Jordan, Andreas Preikschat, Christian Richter, Philip\n'
'Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Carstenn'
'Tinggaard, Frode Woldsund\n'
'\n'
'This program is free software; you can redistribute it and/or '
'modify it under the terms of the GNU General Public License as '

View File

@ -57,4 +57,6 @@ from songbookform import SongBookForm
from editverseform import EditVerseForm
from editsongform import EditSongForm
from songmaintenanceform import SongMaintenanceForm
from songimportform import SongImportForm
from songimportform import SongImportForm
from songexportform import SongExportForm

View File

@ -0,0 +1,372 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 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:`songexportform` module provides the wizard for exporting songs to the
OpenLyrics format.
"""
import logging
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, Receiver, SettingsManager, translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard
from openlp.plugins.songs.lib.db import Song
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
log = logging.getLogger(__name__)
class SongExportForm(OpenLPWizard):
"""
This is the Song Export Wizard, which allows easy exporting of Songs to the
OpenLyrics format.
"""
log.info(u'SongExportForm loaded')
def __init__(self, parent, plugin):
"""
Instantiate the wizard, and run any extra setup we need to.
``parent``
The QWidget-derived parent of the wizard.
``plugin``
The songs plugin.
"""
self.plugin = plugin
OpenLPWizard.__init__(self, parent, plugin, u'songExportWizard',
u':/wizards/wizard_exportsong.bmp')
self.stop_export_flag = False
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_stop_wizard'), self.stop_export)
def stop_export(self):
"""
Sets the flag for the exporter to stop the export.
"""
log.debug(u'Stopping songs export')
self.stop_export_flag = True
def setupUi(self, image):
"""
Set up the song wizard UI.
"""
OpenLPWizard.setupUi(self, image)
def customInit(self):
"""
Song wizard specific initialisation.
"""
pass
def customSignals(self):
"""
Song wizard specific signals.
"""
QtCore.QObject.connect(self.availableListWidget,
QtCore.SIGNAL(u'itemActivated(QListWidgetItem*)'), self.onItemPressed)
QtCore.QObject.connect(self.searchLineEdit,
QtCore.SIGNAL(u'textEdited(const QString&)'),
self.onSearchLineEditChanged)
QtCore.QObject.connect(self.uncheckButton,
QtCore.SIGNAL(u'clicked()'), self.onUncheckButtonClicked)
QtCore.QObject.connect(self.checkButton,
QtCore.SIGNAL(u'clicked()'), self.onCheckButtonClicked)
QtCore.QObject.connect(self.directoryButton,
QtCore.SIGNAL(u'clicked()'), self.onDirectoryButtonClicked)
def addCustomPages(self):
"""
Add song wizard specific pages.
"""
# The page with all available songs.
self.availableSongsPage = QtGui.QWizardPage()
self.availableSongsPage.setObjectName(u'availableSongsPage')
self.availableSongsLayout = QtGui.QHBoxLayout(self.availableSongsPage)
self.availableSongsLayout.setObjectName(u'availableSongsLayout')
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName(u'verticalLayout')
self.availableListWidget = QtGui.QListWidget(self.availableSongsPage)
self.availableListWidget.setObjectName(u'availableListWidget')
self.verticalLayout.addWidget(self.availableListWidget)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(u'horizontalLayout')
self.searchLabel = QtGui.QLabel(self.availableSongsPage)
self.searchLabel.setObjectName(u'searchLabel')
self.horizontalLayout.addWidget(self.searchLabel)
self.searchLineEdit = QtGui.QLineEdit(self.availableSongsPage)
self.searchLineEdit.setObjectName(u'searchLineEdit')
self.horizontalLayout.addWidget(self.searchLineEdit)
spacerItem = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.uncheckButton = QtGui.QPushButton(self.availableSongsPage)
self.uncheckButton.setObjectName(u'uncheckButton')
self.horizontalLayout.addWidget(self.uncheckButton)
self.checkButton = QtGui.QPushButton(self.availableSongsPage)
self.checkButton.setObjectName(u'selectButton')
self.horizontalLayout.addWidget(self.checkButton)
self.verticalLayout.addLayout(self.horizontalLayout)
self.availableSongsLayout.addLayout(self.verticalLayout)
self.addPage(self.availableSongsPage)
# The page with the selected songs.
self.exportSongPage = QtGui.QWizardPage()
self.exportSongPage.setObjectName(u'availableSongsPage')
self.exportSongLayout = QtGui.QHBoxLayout(self.exportSongPage)
self.exportSongLayout.setObjectName(u'exportSongLayout')
self.gridLayout = QtGui.QGridLayout()
self.gridLayout.setObjectName(u'gridLayout')
self.selectedListWidget = QtGui.QListWidget(self.exportSongPage)
self.selectedListWidget.setObjectName(u'selectedListWidget')
self.gridLayout.addWidget(self.selectedListWidget, 1, 0, 1, 1)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(u'horizontalLayout')
self.directoryLabel = QtGui.QLabel(self.exportSongPage)
self.directoryLabel.setObjectName(u'directoryLabel')
self.horizontalLayout.addWidget(self.directoryLabel)
self.directoryLineEdit = QtGui.QLineEdit(self.exportSongPage)
self.directoryLineEdit.setObjectName(u'directoryLineEdit')
self.horizontalLayout.addWidget(self.directoryLineEdit)
self.directoryButton = QtGui.QToolButton(self.exportSongPage)
self.directoryButton.setIcon(build_icon(u':/exports/export_load.png'))
self.directoryButton.setObjectName(u'directoryButton')
self.horizontalLayout.addWidget(self.directoryButton)
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
self.exportSongLayout.addLayout(self.gridLayout)
self.addPage(self.exportSongPage)
def retranslateUi(self):
"""
Song wizard localisation.
"""
self.setWindowTitle(
translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard'))
self.titleLabel.setText(
u'<span style="font-size:14pt; font-weight:600;">%s</span>' %
translate('SongsPlugin.ExportWizardForm',
'Welcome to the Song Export Wizard'))
self.informationLabel.setText(
translate('SongsPlugin.ExportWizardForm', 'This wizard will help to'
' export your songs to the open and free OpenLyrics worship song '
'format.'))
self.availableSongsPage.setTitle(
translate('SongsPlugin.ExportWizardForm', 'Select Songs'))
self.availableSongsPage.setSubTitle(
translate('SongsPlugin.ExportWizardForm',
'Check the songs, you want to export.'))
self.searchLabel.setText(
translate('SongsPlugin.ExportWizardForm', 'Search:'))
self.uncheckButton.setText(
translate('SongsPlugin.ExportWizardForm', 'Uncheck All'))
self.checkButton.setText(
translate('SongsPlugin.ExportWizardForm', 'Check All'))
self.exportSongPage.setTitle(
translate('SongsPlugin.ExportWizardForm', 'Select Directory'))
self.exportSongPage.setSubTitle(
translate('SongsPlugin.ExportWizardForm',
'Select the directory you want the songs to be saved.'))
self.directoryLabel.setText(
translate('SongsPlugin.ExportWizardForm', 'Directory:'))
self.progressPage.setTitle(
translate('SongsPlugin.ExportWizardForm', 'Exporting'))
self.progressPage.setSubTitle(
translate('SongsPlugin.ExportWizardForm',
'Please wait while your songs are exported.'))
self.progressLabel.setText(
translate('SongsPlugin.ExportWizardForm', 'Ready.'))
self.progressBar.setFormat(
translate('SongsPlugin.ExportWizardForm', '%p%'))
def validateCurrentPage(self):
"""
Validate the current page before moving on to the next page.
"""
if self.currentPage() == self.welcomePage:
return True
elif self.currentPage() == self.availableSongsPage:
items = [
item for item in self._findListWidgetItems(
self.availableListWidget) if item.checkState()
]
if not items:
critical_error_message_box(
translate('SongsPlugin.ExportWizardForm',
'No Song Selected'),
translate('SongsPlugin.ExportWizardForm',
'You need to add at least one Song to export.'))
return False
self.selectedListWidget.clear()
# Add the songs to the list of selected songs.
for item in items:
song = QtGui.QListWidgetItem(item.text())
song.setData(QtCore.Qt.UserRole,
QtCore.QVariant(item.data(QtCore.Qt.UserRole).toPyObject()))
song.setFlags(QtCore.Qt.ItemIsEnabled)
self.selectedListWidget.addItem(song)
return True
elif self.currentPage() == self.exportSongPage:
if not self.directoryLineEdit.text():
critical_error_message_box(
translate('SongsPlugin.ExportWizardForm',
'No Save Location specified'),
translate('SongsPlugin.ExportWizardForm',
'You need to specified a directory to save the songs in.'))
return False
return True
elif self.currentPage() == self.progressPage:
self.availableListWidget.clear()
self.selectedListWidget.clear()
return True
def registerFields(self):
"""
Register song export wizard fields.
"""
pass
def setDefaults(self):
"""
Set default form values for the song export wizard.
"""
self.restart()
self.finishButton.setVisible(False)
self.cancelButton.setVisible(True)
self.availableListWidget.clear()
self.selectedListWidget.clear()
self.directoryLineEdit.clear()
# Load the list of songs.
Receiver.send_message(u'cursor_busy')
songs = self.plugin.manager.get_all_objects(Song)
for song in songs:
authors = u', '.join([author.display_name
for author in song.authors])
title = u'%s (%s)' % (unicode(song.title), authors)
item = QtGui.QListWidgetItem(title)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(song))
item.setFlags(QtCore.Qt.ItemIsSelectable|
QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
item.setCheckState(QtCore.Qt.Unchecked)
self.availableListWidget.addItem(item)
Receiver.send_message(u'cursor_normal')
def preWizard(self):
"""
Perform pre export tasks.
"""
OpenLPWizard.preWizard(self)
self.progressLabel.setText(
translate('SongsPlugin.ExportWizardForm', 'Starting export...'))
Receiver.send_message(u'openlp_process_events')
def performWizard(self):
"""
Perform the actual export. This creates an *openlyricsexport* instance
and calls the *do_export* method.
"""
songs = [
song.data(QtCore.Qt.UserRole).toPyObject()
for song in self._findListWidgetItems(self.selectedListWidget)
]
exporter = OpenLyricsExport(
self, songs, unicode(self.directoryLineEdit.text()))
if exporter.do_export():
self.progressLabel.setText(
translate('SongsPlugin.SongExportForm', 'Finished export.'))
else:
self.progressLabel.setText(
translate('SongsPlugin.SongExportForm',
'Your song export failed.'))
def _findListWidgetItems(self, listWidget, text=u''):
"""
Returns a list of *QListWidgetItem*s of the ``listWidget``. Note, that
hidden items are included.
``listWidget``
The widget to get all items from. (QListWidget)
``text``
The text to search for. (unicode string)
"""
return [item for item in listWidget.findItems(
QtCore.QString(unicode(text)), QtCore.Qt.MatchContains)
]
def onItemPressed(self, item):
"""
Called, when an item in the *availableListWidget* has been pressed. Thes
item is check if it was not checked, whereas it is unchecked when it was
checked.
``item``
The *QListWidgetItem* which was pressed.
"""
item.setCheckState(
QtCore.Qt.Unchecked if item.checkState() else QtCore.Qt.Checked)
def onSearchLineEditChanged(self, text):
"""
The *searchLineEdit*'s text has been changed. Update the list of
available songs. Note that any song, which does not match the ``text``
will be hidden, but not unchecked!
``text``
The text of the *searchLineEdit*. (QString)
"""
search_result = [
song for song in self._findListWidgetItems(
self.availableListWidget, unicode(text))
]
for item in self._findListWidgetItems(self.availableListWidget):
item.setHidden(False if item in search_result else True)
def onUncheckButtonClicked(self):
"""
The *uncheckButton* has been clicked. Set all songs unchecked.
"""
for row in range(self.availableListWidget.count()):
item = self.availableListWidget.item(row)
item.setCheckState(QtCore.Qt.Unchecked)
def onCheckButtonClicked(self):
"""
The *checkButton* has been clicked. Set all songs checked.
"""
for row in range(self.availableListWidget.count()):
item = self.availableListWidget.item(row)
item.setCheckState(QtCore.Qt.Checked)
def onDirectoryButtonClicked(self):
"""
Called when the *directoryButton* was clicked. Opens a dialog and writes
the path to *directoryLineEdit*.
"""
path = unicode(QtGui.QFileDialog.getExistingDirectory(self,
translate('SongsPlugin.ExportWizardForm', 'Selecte to Folder'),
SettingsManager.get_last_dir(self.plugin.settingsSection, 1),
options=QtGui.QFileDialog.ShowDirsOnly))
SettingsManager.set_last_dir(self.plugin.settingsSection, path, 1)
self.directoryLineEdit.setText(path)

View File

@ -35,7 +35,7 @@ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \
ItemCapabilities, translate, check_item_selected, PluginStatus
from openlp.core.lib.ui import UiStrings
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm
SongImportForm, SongExportForm
from openlp.plugins.songs.lib import OpenLyrics, SongXML
from openlp.plugins.songs.lib.db import Author, Song
from openlp.core.lib.searchedit import SearchEdit
@ -265,6 +265,11 @@ class SongMediaItem(MediaManagerItem):
if self.import_wizard.exec_() == QtGui.QDialog.Accepted:
Receiver.send_message(u'songs_load_list')
def onExportClick(self):
if not hasattr(self, u'export_wizard'):
self.export_wizard = SongExportForm(self, self.parent)
self.export_wizard.exec_()
def onNewClick(self):
log.debug(u'onNewClick')
self.edit_song_form.newSong()

View File

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 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:`openlyricsexport` module provides the functionality for exporting
songs from the database to the OpenLyrics format.
"""
import logging
import os
from lxml import etree
from openlp.core.lib import Receiver, translate
from openlp.plugins.songs.lib import OpenLyrics
log = logging.getLogger(__name__)
class OpenLyricsExport(object):
"""
This provides the Openlyrics export.
"""
def __init__(self, parent, songs, save_path):
"""
Initialise the export.
"""
log.debug(u'initialise OpenLyricsExport')
self.parent = parent
self.manager = parent.plugin.manager
self.songs = songs
self.save_path = save_path
if not os.path.exists(self.save_path):
os.mkdir(self.save_path)
def do_export(self):
"""
Export the songs.
"""
log.debug(u'started OpenLyricsExport')
openLyrics = OpenLyrics(self.manager)
self.parent.progressBar.setMaximum(len(self.songs))
for song in self.songs:
Receiver.send_message(u'openlp_process_events')
if self.parent.stop_export_flag:
return False
self.parent.incrementProgressBar(unicode(translate(
'SongsPlugin.OpenLyricsExport', 'Exporting "%s"...')) %
song.title)
xml = openLyrics.song_to_xml(song)
tree = etree.ElementTree(etree.fromstring(xml))
tree.write(os.path.join(self.save_path, song.title + u'.xml'),
encoding=u'utf-8', xml_declaration=True, pretty_print=True)
return True

View File

@ -36,6 +36,7 @@ from openlp.plugins.songs.lib.songimport import SongImport
log = logging.getLogger(__name__)
#TODO: Use lxml for parsing and make sure we use methods of "SongImport" .
class OpenSongImport(SongImport):
"""
Import songs exported from OpenSong
@ -146,7 +147,7 @@ class OpenSongImport(SongImport):
log.info(u'Zip importing %s', parts[-1])
self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.ImportWizardForm',
'Importing %s...')) % parts[-1])
'Importing %s...')) % parts[-1])
songfile = z.open(song)
self.do_import_file(songfile)
if self.commit:
@ -276,7 +277,7 @@ class OpenSongImport(SongImport):
for num in versenums:
versetag = u'%s%s' % (our_verse_type, num)
lines = u'\n'.join(verses[versetype][num])
self.verses.append([versetag, lines])
self.add_verse(lines, versetag)
# Keep track of what we have for error checking later
versetags[versetag] = 1
# now figure out the presentation order
@ -292,6 +293,8 @@ class OpenSongImport(SongImport):
else:
log.warn(u'No verse order available for %s, skipping.',
self.title)
# TODO: make sure that the default order list will be overwritten, if
# the songs provides its own order list.
for tag in order:
if tag[0].isdigit():
# Assume it's a verse if it has no prefix

View File

@ -60,6 +60,7 @@ The XML of an `OpenLyrics <http://openlyrics.info/>`_ song looks like this::
</song>
"""
import datetime
import logging
import re
@ -89,8 +90,10 @@ class SongXML(object):
Add a verse to the ``<lyrics>`` tag.
``type``
A string denoting the type of verse. Possible values are "V",
"C", "B", "P", "I", "E" and "O".
A string denoting the type of verse. Possible values are *Verse*,
*Chorus*, *Bridge*, *Pre-Chorus*, *Intro*, *Ending* and *Other*.
Any other type is **not** allowed, this also includes translated
types.
``number``
An integer denoting the number of the item, for example: verse 1.
@ -126,8 +129,8 @@ class SongXML(object):
The returned list has the following format::
[[{'lang': 'en', 'type': 'V', 'label': '1'}, u"The English verse."],
[{'lang': 'en', 'type': 'C', 'label': '1'}, u"The English chorus."]]
[[{'lang': 'en', 'type': 'Verse', 'label': '1'}, u"English verse"],
[{'lang': 'en', 'type': 'Chorus', 'label': '1'}, u"English chorus"]]
"""
self.song_xml = None
if xml[:5] == u'<?xml':
@ -207,12 +210,14 @@ class OpenLyrics(object):
This property is not supported.
``<verse name="v1a" lang="he" translit="en">``
The attribute *translit* is not supported.
The attribute *translit* is not supported. Note, the attribute *lang* is
considered, but there is not further functionality implemented yet.
``<verseOrder>``
OpenLP supports this property.
"""
IMPLEMENTED_VERSION = u'0.7'
def __init__(self, manager):
self.manager = manager
@ -222,8 +227,14 @@ class OpenLyrics(object):
"""
sxml = SongXML()
verse_list = sxml.get_verses(song.lyrics)
song_xml = objectify.fromstring(
u'<song version="0.7" createdIn="OpenLP 2.0"/>')
song_xml = objectify.fromstring(u'<song/>')
# Append the necessary meta data to the song.
song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song')
song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION)
song_xml.set(u'createdIn', u'OpenLP 1.9.4') # Use variable
song_xml.set(u'modifiedIn', u'OpenLP 1.9.4') # Use variable
song_xml.set(u'modifiedDate',
datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S'))
properties = etree.SubElement(song_xml, u'properties')
titles = etree.SubElement(properties, u'titles')
self._add_text_to_element(u'title', titles, song.title.strip())
@ -237,7 +248,7 @@ class OpenLyrics(object):
self._add_text_to_element(u'copyright', properties, song.copyright)
if song.verse_order:
self._add_text_to_element(
u'verseOrder', properties, song.verse_order)
u'verseOrder', properties, song.verse_order.lower())
if song.ccli_number:
self._add_text_to_element(u'ccliNo', properties, song.ccli_number)
if song.authors:
@ -263,6 +274,8 @@ class OpenLyrics(object):
verse[0][u'type'][0].lower(), verse[0][u'label'])
element = \
self._add_text_to_element(u'verse', lyrics, None, verse_tag)
if verse[0].has_key(u'lang'):
element.set(u'lang', verse[0][u'lang'])
element = self._add_text_to_element(u'lines', element)
for line in unicode(verse[1]).split(u'\n'):
self._add_text_to_element(u'line', element, line)
@ -450,7 +463,7 @@ class OpenLyrics(object):
text += u'\n'
text += u'\n'.join([unicode(line) for line in lines.line])
verse_name = self._get(verse, u'name')
verse_type = unicode(VerseType.to_string(verse_name[0]))[0]
verse_type = unicode(VerseType.to_string(verse_name[0]))
verse_number = re.compile(u'[a-zA-Z]*').sub(u'', verse_name)
verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:])
# OpenLyrics allows e. g. "c", but we need "c1".
@ -478,9 +491,9 @@ class OpenLyrics(object):
for name in temp_verse_order:
if name[0] == previous_type:
if name[1] != previous_number:
verse_order.append(u''.join((name[0], name[1])))
verse_order.append(u''.join((name[0][0], name[1])))
else:
verse_order.append(u''.join((name[0], name[1])))
verse_order.append(u''.join((name[0][0], name[1])))
previous_type = name[0]
previous_number = name[1]
previous_part = name[2]

View File

@ -107,8 +107,17 @@ class SongsPlugin(Plugin):
The actual **Export** menu item, so that your actions can
use it as their parent.
"""
# No menu items for now.
pass
# Main song import menu item - will eventually be the only one
self.SongExportItem = QtGui.QAction(export_menu)
self.SongExportItem.setObjectName(u'SongExportItem')
self.SongExportItem.setText(translate(
'SongsPlugin', '&Song'))
self.SongExportItem.setToolTip(translate('SongsPlugin',
'Exports songs using the export wizard.'))
export_menu.addAction(self.SongExportItem)
# Signals and slots
QtCore.QObject.connect(self.SongExportItem,
QtCore.SIGNAL(u'triggered()'), self.onSongExportItemClicked)
def addToolsMenuItem(self, tools_menu):
"""
@ -173,6 +182,10 @@ class SongsPlugin(Plugin):
if self.mediaItem:
self.mediaItem.onImportClick()
def onSongExportItemClicked(self):
if self.mediaItem:
self.mediaItem.onExportClick()
def about(self):
about_text = translate('SongsPlugin', '<strong>Songs Plugin</strong>'
'<br />The songs plugin provides the ability to display and '

View File

@ -1,241 +0,0 @@
<ui version="4.0" >
<class>SongExportDialog</class>
<widget class="QDialog" name="SongExportDialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>641</width>
<height>607</height>
</rect>
</property>
<property name="windowTitle" >
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="SongExportLayout" >
<property name="spacing" >
<number>8</number>
</property>
<property name="margin" >
<number>8</number>
</property>
<item>
<widget class="QWidget" native="1" name="SongListsWidget" >
<layout class="QHBoxLayout" name="SongListsLayout" >
<property name="spacing" >
<number>8</number>
</property>
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="AvailableGroupBox" >
<property name="title" >
<string>Available Songs</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<widget class="QListWidget" name="AvailableListWidget" />
</item>
<item>
<widget class="QToolButton" name="AvailableAllToolButton" >
<property name="text" >
<string>Select All</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" native="1" name="SelectionWidget" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Minimum" hsizetype="Minimum" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize" >
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<layout class="QVBoxLayout" name="SelectionLayout" >
<property name="spacing" >
<number>8</number>
</property>
<property name="sizeConstraint" >
<enum>QLayout::SetMinimumSize</enum>
</property>
<property name="margin" >
<number>0</number>
</property>
<item>
<spacer name="TopVerticalSpacer" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>20</width>
<height>132</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="SelectToolButton" >
<property name="text" >
<string>Select Songs</string>
</property>
<property name="icon" >
<iconset resource="../images/openlp-2.qrc" >
<normaloff>:/exports/export_move_to_list.png</normaloff>:/exports/export_move_to_list.png</iconset>
</property>
<property name="iconSize" >
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="DeselectToolButton" >
<property name="text" >
<string>Deselect Songs</string>
</property>
<property name="icon" >
<iconset resource="../images/openlp-2.qrc" >
<normaloff>:/exports/export_remove.png</normaloff>:/exports/export_remove.png</iconset>
</property>
<property name="iconSize" >
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="BottomVerticalSpacer" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>20</width>
<height>131</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="SelectedGroupBox" >
<property name="title" >
<string>Selected Songs</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" >
<item>
<widget class="QListWidget" name="SelectedListWidget" />
</item>
<item>
<widget class="QToolButton" name="SelectedAllToolButton" >
<property name="text" >
<string>Select All</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTabWidget" name="ExportTabWidget" >
<property name="currentIndex" >
<number>0</number>
</property>
<widget class="QWidget" name="OpenLyricTab" >
<attribute name="title" >
<string>OpenLyric Format</string>
</attribute>
</widget>
<widget class="QWidget" name="TextFileTab" >
<attribute name="title" >
<string>Text File</string>
</attribute>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="SongExportButtonBox" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>SongExportButtonBox</tabstop>
<tabstop>AvailableListWidget</tabstop>
<tabstop>AvailableAllToolButton</tabstop>
<tabstop>SelectToolButton</tabstop>
<tabstop>DeselectToolButton</tabstop>
<tabstop>SelectedListWidget</tabstop>
<tabstop>SelectedAllToolButton</tabstop>
<tabstop>ExportTabWidget</tabstop>
</tabstops>
<resources>
<include location="../images/openlp-2.qrc" />
</resources>
<connections>
<connection>
<sender>SongExportButtonBox</sender>
<signal>accepted()</signal>
<receiver>SongExportDialog</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>SongExportButtonBox</sender>
<signal>rejected()</signal>
<receiver>SongExportDialog</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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

View File

@ -80,12 +80,10 @@
<file>import_load.png</file>
</qresource>
<qresource prefix="exports">
<file>export_selectall.png</file>
<file>export_remove.png</file>
<file>export_load.png</file>
<file>export_move_to_list.png</file>
</qresource>
<qresource prefix="wizards">
<file>wizard_exportsong.bmp</file>
<file>wizard_importsong.bmp</file>
<file>wizard_importbible.bmp</file>
<file>wizard_createtheme.bmp</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB