forked from openlp/openlp
- 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:
commit
12677a4d36
@ -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 #
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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 '
|
||||
|
@ -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
|
||||
|
||||
|
372
openlp/plugins/songs/forms/songexportform.py
Normal file
372
openlp/plugins/songs/forms/songexportform.py
Normal 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)
|
@ -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()
|
||||
|
74
openlp/plugins/songs/lib/openlyricsexport.py
Executable file
74
openlp/plugins/songs/lib/openlyricsexport.py
Executable 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
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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 '
|
||||
|
@ -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 |
@ -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>
|
||||
|
BIN
resources/images/wizard_exportsong.bmp
Normal file
BIN
resources/images/wizard_exportsong.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 168 KiB |
Loading…
Reference in New Issue
Block a user