Reworked SongSelect branch after a major refactor in trunk.

To recap:
- Added a standalone CCLI SongSelect importer
- Add icons to the import and export menu items
- Move Theme import and export up with Settings
This commit is contained in:
Raoul Snyman 2014-01-01 00:27:27 +02:00
parent 80c6c371df
commit 1cf3140073
11 changed files with 934 additions and 89 deletions

View File

@ -345,3 +345,4 @@ from .dockwidget import OpenLPDockWidget
from .imagemanager import ImageManager
from .renderer import Renderer
from .mediamanageritem import MediaManagerItem
from .historycombobox import HistoryComboBox

View File

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`~openlp.core.lib.historycombobox` module contains the HistoryComboBox widget
"""
from PyQt4 import QtCore, QtGui
class HistoryComboBox(QtGui.QComboBox):
"""
The :class:`~openlp.core.lib.historycombobox.HistoryComboBox` widget emulates the QLineEdit ``returnPressed`` signal
for when the :kbd:`Enter` or :kbd:`Return` keys are pressed, and saves anything that is typed into the edit box into
its list.
"""
returnPressed = QtCore.pyqtSignal()
def __init__(self, parent=None):
"""
Initialise the combo box, setting duplicates to False and the insert policy to insert items at the top.
:param parent: The parent widget
"""
super().__init__(parent)
self.setDuplicatesEnabled(False)
self.setEditable(True)
self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred)
self.setInsertPolicy(QtGui.QComboBox.InsertAtTop)
def keyPressEvent(self, event):
"""
Override the inherited keyPressEvent method to emit the ``returnPressed`` signal and to save the current text to
the dropdown list.
:param event: The keyboard event
"""
# Handle Enter and Return ourselves
if event.key() == QtCore.Qt.Key_Enter or event.key() == QtCore.Qt.Key_Return:
# Emit the returnPressed signal
self.returnPressed.emit()
# Save the current text to the dropdown list
if self.currentText() and self.findText(self.currentText()) == -1:
self.insertItem(0, self.currentText())
# Let the parent handle any keypress events
super().keyPressEvent(event)
def focusOutEvent(self, event):
"""
Override the inherited focusOutEvent to save the current text to the dropdown list.
:param event: The focus event
"""
# Save the current text to the dropdown list
if self.currentText() and self.findText(self.currentText()) == -1:
self.insertItem(0, self.currentText())
# Let the parent handle any keypress events
super().focusOutEvent(event)
def getItems(self):
"""
Get all the items from the history
:return: A list of strings
"""
return [self.itemText(i) for i in range(self.count())]

View File

@ -29,6 +29,7 @@
"""
The :mod:`ui` module provides the core user interface for OpenLP
"""
from PyQt4 import QtGui
class HideMode(object):
@ -77,6 +78,29 @@ class DisplayControllerType(object):
Plugin = 2
class SingleColumnTableWidget(QtGui.QTableWidget):
"""
Class to for a single column table widget to use for the verse table widget.
"""
def __init__(self, parent):
"""
Constructor
"""
super(SingleColumnTableWidget, self).__init__(parent)
self.horizontalHeader().setVisible(False)
self.setColumnCount(1)
def resizeEvent(self, event):
"""
Resize the first column together with the widget.
"""
QtGui.QTableWidget.resizeEvent(self, event)
if self.columnCount():
self.setColumnWidth(0, event.size().width())
self.resizeRowsToContents()
from .firsttimeform import FirstTimeForm
from .firsttimelanguageform import FirstTimeLanguageForm
from .themelayoutform import ThemeLayoutForm
@ -102,8 +126,9 @@ from .servicemanager import ServiceManager
from .thememanagerhelper import ThemeManagerHelper
from .thememanager import ThemeManager
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager',
'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', 'ThemeForm',
'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay', 'Display', 'ServiceNoteForm',
'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'ThemeManagerHelper']
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeForm',
'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm',
'Display', 'ServiceNoteForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay',
'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'ThemeManagerHelper',
'SingleColumnTableWidget']

View File

@ -119,8 +119,10 @@ class Ui_MainWindow(object):
self.recent_files_menu = QtGui.QMenu(self.file_menu)
self.recent_files_menu.setObjectName('recentFilesMenu')
self.file_import_menu = QtGui.QMenu(self.file_menu)
self.file_import_menu.setIcon(build_icon(u':/general/general_import.png'))
self.file_import_menu.setObjectName('file_import_menu')
self.file_export_menu = QtGui.QMenu(self.file_menu)
self.file_export_menu.setIcon(build_icon(u':/general/general_export.png'))
self.file_export_menu.setObjectName('file_export_menu')
# View Menu
self.view_menu = QtGui.QMenu(self.menu_bar)
@ -305,10 +307,10 @@ class Ui_MainWindow(object):
'searchShortcut', can_shortcuts=True,
category=translate('OpenLP.MainWindow', 'General'),
triggers=self.on_search_shortcut_triggered)
add_actions(self.file_import_menu, (self.settings_import_item, None, self.import_theme_item,
self.import_language_item))
add_actions(self.file_export_menu, (self.settings_export_item, None, self.export_theme_item,
self.export_language_item))
add_actions(self.file_import_menu, (self.settings_import_item, self.import_theme_item,
self.import_language_item, None))
add_actions(self.file_export_menu, (self.settings_export_item, self.export_theme_item,
self.export_language_item, None))
add_actions(self.file_menu, (self.file_new_item, self.file_open_item,
self.file_save_item, self.file_save_as_item, self.recent_files_menu.menuAction(), None,
self.file_import_menu.menuAction(), self.file_export_menu.menuAction(), None, self.print_service_order_item,

View File

@ -32,6 +32,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.common import UiStrings, translate
from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_button_box, create_button
from openlp.core.ui import SingleColumnTableWidget
from openlp.plugins.songs.lib.ui import SongStrings
@ -346,25 +347,3 @@ def create_combo_box(parent, name):
combo_box.setInsertPolicy(QtGui.QComboBox.NoInsert)
combo_box.setObjectName(name)
return combo_box
class SingleColumnTableWidget(QtGui.QTableWidget):
"""
Class to for a single column table widget to use for the verse table widget.
"""
def __init__(self, parent):
"""
Constructor
"""
super(SingleColumnTableWidget, self).__init__(parent)
self.horizontalHeader().setVisible(False)
self.setColumnCount(1)
def resizeEvent(self, event):
"""
Resize the first column together with the widget.
"""
QtGui.QTableWidget.resizeEvent(self, event)
if self.columnCount():
self.setColumnWidth(0, event.size().width())
self.resizeRowsToContents()

View File

@ -0,0 +1,251 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`~openlp.plugins.songs.forms.songselectdialog` module contains the user interface code for the dialog
"""
from PyQt4 import QtCore, QtGui
from openlp.core.lib import HistoryComboBox, translate, build_icon
from openlp.core.ui import SingleColumnTableWidget
class Ui_SongSelectDialog(object):
"""
The actual Qt components that make up the dialog.
"""
def setup_ui(self, songselect_dialog):
songselect_dialog.setObjectName('songselect_dialog')
songselect_dialog.resize(616, 378)
self.songselect_layout = QtGui.QVBoxLayout(songselect_dialog)
self.songselect_layout.setSpacing(0)
self.songselect_layout.setMargin(0)
self.songselect_layout.setObjectName('songselect_layout')
self.stacked_widget = QtGui.QStackedWidget(songselect_dialog)
self.stacked_widget.setObjectName('stacked_widget')
self.login_page = QtGui.QWidget()
self.login_page.setObjectName('login_page')
self.login_layout = QtGui.QFormLayout(self.login_page)
self.login_layout.setContentsMargins(120, 100, 120, 100)
self.login_layout.setSpacing(8)
self.login_layout.setObjectName('login_layout')
self.notice_layout = QtGui.QHBoxLayout()
self.notice_layout.setObjectName('notice_layout')
self.notice_label = QtGui.QLabel(self.login_page)
self.notice_label.setWordWrap(True)
self.notice_label.setObjectName('notice_label')
self.notice_layout.addWidget(self.notice_label)
self.login_layout.setLayout(0, QtGui.QFormLayout.SpanningRole, self.notice_layout)
self.username_label = QtGui.QLabel(self.login_page)
self.username_label.setObjectName('usernameLabel')
self.login_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.username_label)
self.username_edit = QtGui.QLineEdit(self.login_page)
self.username_edit.setObjectName('usernameEdit')
self.login_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.username_edit)
self.password_label = QtGui.QLabel(self.login_page)
self.password_label.setObjectName('passwordLabel')
self.login_layout.setWidget(2, QtGui.QFormLayout.LabelRole, self.password_label)
self.password_edit = QtGui.QLineEdit(self.login_page)
self.password_edit.setEchoMode(QtGui.QLineEdit.Password)
self.password_edit.setObjectName('passwordEdit')
self.login_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.password_edit)
self.save_password_checkbox = QtGui.QCheckBox(self.login_page)
self.save_password_checkbox.setTristate(False)
self.save_password_checkbox.setObjectName('save_password_checkbox')
self.login_layout.setWidget(3, QtGui.QFormLayout.FieldRole, self.save_password_checkbox)
self.login_button_layout = QtGui.QHBoxLayout()
self.login_button_layout.setSpacing(8)
self.login_button_layout.setContentsMargins(0, -1, -1, -1)
self.login_button_layout.setObjectName('login_button_layout')
self.login_spacer = QtGui.QWidget(self.login_page)
self.login_spacer.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.login_spacer.setObjectName('login_spacer')
self.login_button_layout.addWidget(self.login_spacer)
self.login_progress_bar = QtGui.QProgressBar(self.login_page)
self.login_progress_bar.setMinimum(0)
self.login_progress_bar.setMaximum(3)
self.login_progress_bar.setValue(0)
self.login_progress_bar.setMinimumWidth(200)
self.login_progress_bar.setVisible(False)
self.login_button_layout.addWidget(self.login_progress_bar)
self.login_button = QtGui.QPushButton(self.login_page)
self.login_button.setIcon(build_icon(':/songs/song_author_edit.png'))
self.login_button.setObjectName('login_button')
self.login_button_layout.addWidget(self.login_button)
self.login_layout.setLayout(4, QtGui.QFormLayout.SpanningRole, self.login_button_layout)
self.stacked_widget.addWidget(self.login_page)
self.search_page = QtGui.QWidget()
self.search_page.setObjectName('search_page')
self.search_layout = QtGui.QVBoxLayout(self.search_page)
self.search_layout.setSpacing(8)
self.search_layout.setMargin(8)
self.search_layout.setObjectName('search_layout')
self.search_input_layout = QtGui.QHBoxLayout()
self.search_input_layout.setSpacing(8)
self.search_input_layout.setObjectName('search_input_layout')
self.search_label = QtGui.QLabel(self.search_page)
self.search_label.setObjectName('search_label')
self.search_input_layout.addWidget(self.search_label)
self.search_combobox = HistoryComboBox(self.search_page)
self.search_combobox.setObjectName('search_combobox')
self.search_input_layout.addWidget(self.search_combobox)
self.search_button = QtGui.QPushButton(self.search_page)
self.search_button.setIcon(build_icon(':/general/general_find.png'))
self.search_button.setObjectName('search_button')
self.search_input_layout.addWidget(self.search_button)
self.search_layout.addLayout(self.search_input_layout)
self.search_progress_bar = QtGui.QProgressBar(self.search_page)
self.search_progress_bar.setMinimum(0)
self.search_progress_bar.setMaximum(3)
self.search_progress_bar.setValue(0)
self.search_progress_bar.setVisible(False)
self.search_layout.addWidget(self.search_progress_bar)
self.search_results_widget = QtGui.QListWidget(self.search_page)
self.search_results_widget.setProperty("showDropIndicator", False)
self.search_results_widget.setAlternatingRowColors(True)
self.search_results_widget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.search_results_widget.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.search_results_widget.setObjectName('search_results_widget')
self.search_layout.addWidget(self.search_results_widget)
self.result_count_label = QtGui.QLabel(self.search_page)
self.result_count_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignCenter)
self.result_count_label.setObjectName('result_count_label')
self.search_layout.addWidget(self.result_count_label)
self.view_layout = QtGui.QHBoxLayout()
self.view_layout.setSpacing(8)
self.view_layout.setObjectName('view_layout')
self.logout_button = QtGui.QPushButton(self.search_page)
self.logout_button.setIcon(build_icon(':/songs/song_author_edit.png'))
self.view_layout.addWidget(self.logout_button)
self.view_spacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.view_layout.addItem(self.view_spacer)
self.view_button = QtGui.QPushButton(self.search_page)
self.view_button.setIcon(build_icon(':/songs/song_search_all.png'))
self.view_button.setObjectName('view_button')
self.view_layout.addWidget(self.view_button)
self.search_layout.addLayout(self.view_layout)
self.stacked_widget.addWidget(self.search_page)
self.song_page = QtGui.QWidget()
self.song_page.setObjectName('song_page')
self.song_layout = QtGui.QGridLayout(self.song_page)
self.song_layout.setMargin(8)
self.song_layout.setSpacing(8)
self.song_layout.setObjectName('song_layout')
self.title_label = QtGui.QLabel(self.song_page)
self.title_label.setObjectName('title_label')
self.song_layout.addWidget(self.title_label, 0, 0, 1, 1)
self.title_edit = QtGui.QLineEdit(self.song_page)
self.title_edit.setReadOnly(True)
self.title_edit.setObjectName('title_edit')
self.song_layout.addWidget(self.title_edit, 0, 1, 1, 1)
self.authors_label = QtGui.QLabel(self.song_page)
self.authors_label.setObjectName('authors_label')
self.song_layout.addWidget(self.authors_label, 0, 2, 1, 1)
self.author_list_widget = QtGui.QListWidget(self.song_page)
self.author_list_widget.setObjectName('author_list_widget')
self.song_layout.addWidget(self.author_list_widget, 0, 3, 3, 1)
self.copyright_label = QtGui.QLabel(self.song_page)
self.copyright_label.setObjectName('copyright_label')
self.song_layout.addWidget(self.copyright_label, 1, 0, 1, 1)
self.copyright_edit = QtGui.QLineEdit(self.song_page)
self.copyright_edit.setReadOnly(True)
self.copyright_edit.setObjectName('copyright_edit')
self.song_layout.addWidget(self.copyright_edit, 1, 1, 1, 1)
self.ccli_label = QtGui.QLabel(self.song_page)
self.ccli_label.setObjectName('ccli_label')
self.song_layout.addWidget(self.ccli_label, 2, 0, 1, 1)
self.ccli_edit = QtGui.QLineEdit(self.song_page)
self.ccli_edit.setReadOnly(True)
self.ccli_edit.setObjectName('ccli_edit')
self.song_layout.addWidget(self.ccli_edit, 2, 1, 1, 1)
self.lyrics_label = QtGui.QLabel(self.song_page)
self.lyrics_label.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
self.lyrics_label.setObjectName('lyrics_label')
self.song_layout.addWidget(self.lyrics_label, 3, 0, 1, 1)
self.lyrics_table_widget = SingleColumnTableWidget(self.song_page)
self.lyrics_table_widget.setObjectName('lyrics_table_widget')
self.lyrics_table_widget.setRowCount(0)
self.song_layout.addWidget(self.lyrics_table_widget, 3, 1, 1, 3)
self.song_progress_bar = QtGui.QProgressBar(self.song_page)
self.song_progress_bar.setMinimum(0)
self.song_progress_bar.setMaximum(3)
self.song_progress_bar.setValue(0)
self.song_progress_bar.setVisible(False)
self.song_layout.addWidget(self.song_progress_bar, 4, 0, 1, 4)
self.import_layout = QtGui.QHBoxLayout()
self.import_layout.setObjectName('import_layout')
self.back_button = QtGui.QPushButton(self.song_page)
self.back_button.setIcon(build_icon(':/general/general_back.png'))
self.back_button.setObjectName('back_button')
self.import_layout.addWidget(self.back_button)
self.import_spacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.import_layout.addItem(self.import_spacer)
self.import_button = QtGui.QPushButton(self.song_page)
self.import_button.setIcon(build_icon(':/general/general_import.png'))
self.import_button.setObjectName('import_button')
self.import_layout.addWidget(self.import_button)
self.song_layout.addLayout(self.import_layout, 5, 0, 1, 5)
self.stacked_widget.addWidget(self.song_page)
self.songselect_layout.addWidget(self.stacked_widget)
self.username_label.setBuddy(self.username_edit)
self.password_label.setBuddy(self.password_edit)
self.title_label.setBuddy(self.title_edit)
self.authors_label.setBuddy(self.author_list_widget)
self.copyright_label.setBuddy(self.copyright_edit)
self.ccli_label.setBuddy(self.ccli_edit)
self.lyrics_label.setBuddy(self.lyrics_table_widget)
self.retranslate_ui(songselect_dialog)
self.stacked_widget.setCurrentIndex(0)
def retranslate_ui(self, songselect_dialog):
"""
Translate the GUI.
"""
songselect_dialog.setWindowTitle(translate('SongsPlugin.SongSelectForm', 'CCLI SongSelect Importer'))
self.notice_label.setText(
translate('SongsPlugin.SongSelectForm', '<strong>Note:</strong> '
'An Internet connection is required in order to import songs from CCLI SongSelect.')
)
self.username_label.setText(translate('SongsPlugin.SongSelectForm', 'Username:'))
self.password_label.setText(translate('SongsPlugin.SongSelectForm', 'Password:'))
self.save_password_checkbox.setText(translate('SongsPlugin.SongSelectForm', 'Save username and password'))
self.login_button.setText(translate('SongsPlugin.SongSelectForm', 'Login'))
self.search_label.setText(translate('SongsPlugin.SongSelectForm', 'Search Text:'))
self.search_button.setText(translate('SongsPlugin.SongSelectForm', 'Search'))
self.result_count_label.setText(translate('SongsPlugin.SongSelectForm', 'Found %s song(s)') % 0)
self.logout_button.setText(translate('SongsPlugin.SongSelectForm', 'Logout'))
self.view_button.setText(translate('SongsPlugin.SongSelectForm', 'View'))
self.title_label.setText(translate('SongsPlugin.SongSelectForm', 'Title:'))
self.authors_label.setText(translate('SongsPlugin.SongSelectForm', 'Author(s):'))
self.copyright_label.setText(translate('SongsPlugin.SongSelectForm', 'Copyright:'))
self.ccli_label.setText(translate('SongsPlugin.SongSelectForm', 'CCLI Number:'))
self.lyrics_label.setText(translate('SongsPlugin.SongSelectForm', 'Lyrics:'))
self.back_button.setText(translate('SongsPlugin.SongSelectForm', 'Back'))
self.import_button.setText(translate('SongsPlugin.SongSelectForm', 'Import'))

View File

@ -0,0 +1,473 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`~openlp.plugins.songs.forms.songselectform` module contains the GUI for the SongSelect importer
"""
import logging
from http.cookiejar import CookieJar
from urllib.parse import urlencode
from urllib.request import HTTPCookieProcessor, HTTPError, build_opener
from html.parser import HTMLParser
from time import sleep
from PyQt4 import QtCore, QtGui
from bs4 import BeautifulSoup, NavigableString
from openlp.core import Settings
from openlp.core.common import Registry
from openlp.core.lib import translate
from openlp.plugins.songs.lib import VerseType, clean_song
from openlp.plugins.songs.forms.songselectdialog import Ui_SongSelectDialog
from openlp.plugins.songs.lib.db import Author, Song
from openlp.plugins.songs.lib.xml import SongXML
USER_AGENT = 'Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; GT-I9000 ' \
'Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 ' \
'Mobile Safari/534.30'
BASE_URL = 'https://mobile.songselect.com'
LOGIN_URL = BASE_URL + '/account/login'
LOGOUT_URL = BASE_URL + '/account/logout'
SEARCH_URL = BASE_URL + '/search/results'
log = logging.getLogger(__name__)
class SearchWorker(QtCore.QObject):
"""
Run the actual SongSelect search, and notify the GUI when we find each song.
"""
show_info = QtCore.pyqtSignal(str, str)
found_song = QtCore.pyqtSignal(dict)
finished = QtCore.pyqtSignal(list)
quit = QtCore.pyqtSignal()
def __init__(self, opener, params):
super().__init__()
self.opener = opener
self.params = params
self.html_parser = HTMLParser()
def _search_and_parse_results(self, params):
params = urlencode(params)
results_page = BeautifulSoup(self.opener.open(SEARCH_URL + '?' + params).read(), 'lxml')
search_results = results_page.find_all('li', 'result pane')
songs = []
for result in search_results:
song = {
'title': self.html_parser.unescape(result.find('h3').string),
'authors': [self.html_parser.unescape(author.string) for author in result.find_all('li')],
'link': BASE_URL + result.find('a')['href']
}
self.found_song.emit(song)
songs.append(song)
return songs
def start(self):
"""
Run a search and then parse the results page of the search.
"""
songs = self._search_and_parse_results(self.params)
search_results = []
self.params['page'] = 1
total = 0
while songs:
search_results.extend(songs)
self.params['page'] += 1
total += len(songs)
if total >= 1000:
self.show_info.emit(
translate('SongsPlugin.SongSelectForm', 'More than 1000 results'),
translate('SongsPlugin.SongSelectForm', 'Your search has returned more than 1000 results, it has '
'been stopped. Please refine your search to fetch better '
'results.'))
break
songs = self._search_and_parse_results(self.params)
self.finished.emit(search_results)
self.quit.emit()
class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog):
"""
The :class:`SongSelectForm` class is the SongSelect dialog.
"""
def __init__(self, parent=None, plugin=None, db_manager=None):
QtGui.QDialog.__init__(self, parent)
self.setup_ui(self)
self.thread = None
self.worker = None
self.song_count = 0
self.song = None
self.plugin = plugin
self.db_manager = db_manager
self.html_parser = HTMLParser()
self.opener = build_opener(HTTPCookieProcessor(CookieJar()))
self.opener.addheaders = [('User-Agent', USER_AGENT)]
self.save_password_checkbox.toggled.connect(self.on_save_password_checkbox_toggled)
self.login_button.clicked.connect(self.on_login_button_clicked)
self.search_button.clicked.connect(self.on_search_button_clicked)
self.search_combobox.returnPressed.connect(self.on_search_button_clicked)
self.logout_button.clicked.connect(self.done)
self.search_results_widget.itemDoubleClicked.connect(self.on_search_results_widget_double_clicked)
self.search_results_widget.itemSelectionChanged.connect(self.on_search_results_widget_selection_changed)
self.view_button.clicked.connect(self.on_view_button_clicked)
self.back_button.clicked.connect(self.on_back_button_clicked)
self.import_button.clicked.connect(self.on_import_button_clicked)
def exec_(self):
"""
Execute the dialog. This method sets everything back to its initial
values.
"""
self.stacked_widget.setCurrentIndex(0)
self.username_edit.setEnabled(True)
self.password_edit.setEnabled(True)
self.save_password_checkbox.setEnabled(True)
self.search_combobox.clearEditText()
self.search_combobox.clear()
self.search_results_widget.clear()
self.view_button.setEnabled(False)
if Settings().contains(self.plugin.settings_section + '/songselect password'):
self.username_edit.setText(Settings().value(self.plugin.settings_section + '/songselect username'))
self.password_edit.setText(Settings().value(self.plugin.settings_section + '/songselect password'))
self.save_password_checkbox.setChecked(True)
if Settings().contains(self.plugin.settings_section + '/songselect searches'):
self.search_combobox.addItems(
Settings().value(self.plugin.settings_section + '/songselect searches').split('|'))
self.username_edit.setFocus()
return QtGui.QDialog.exec_(self)
def done(self, r):
"""
Log out of SongSelect.
:param r: The result of the dialog.
"""
log.debug('Closing SongSelectForm')
if self.stacked_widget.currentIndex() > 0:
progress_dialog = QtGui.QProgressDialog(
translate('SongsPlugin.SongSelectForm', 'Logging out...'), '', 0, 2, self)
progress_dialog.setWindowModality(QtCore.Qt.WindowModal)
progress_dialog.setCancelButton(None)
progress_dialog.setValue(1)
progress_dialog.show()
progress_dialog.setFocus()
self.main_window.application.process_events()
sleep(0.5)
self.main_window.application.process_events()
self.opener.open(LOGOUT_URL)
self.main_window.application.process_events()
progress_dialog.setValue(2)
return QtGui.QDialog.done(self, r)
def _get_main_window(self):
if not hasattr(self, '_main_window'):
self._main_window = Registry().get('main_window')
return self._main_window
main_window = property(_get_main_window)
def _view_song(self, current_item):
if not current_item:
return
else:
current_item = current_item.data(QtCore.Qt.UserRole)
self.song_progress_bar.setVisible(True)
self.import_button.setEnabled(False)
self.back_button.setEnabled(False)
self.title_edit.setText('')
self.title_edit.setEnabled(False)
self.copyright_edit.setText('')
self.copyright_edit.setEnabled(False)
self.ccli_edit.setText('')
self.ccli_edit.setEnabled(False)
self.author_list_widget.clear()
self.author_list_widget.setEnabled(False)
self.lyrics_table_widget.clear()
self.lyrics_table_widget.setRowCount(0)
self.lyrics_table_widget.setEnabled(False)
self.stacked_widget.setCurrentIndex(2)
song = {}
for key, value in current_item.items():
song[key] = value
self.song_progress_bar.setValue(1)
self.main_window.application.process_events()
song_page = BeautifulSoup(self.opener.open(song['link']).read(), 'lxml')
self.song_progress_bar.setValue(2)
self.main_window.application.process_events()
try:
lyrics_page = BeautifulSoup(self.opener.open(song['link'] + '/lyrics').read(), 'lxml')
except HTTPError:
lyrics_page = None
self.song_progress_bar.setValue(3)
self.main_window.application.process_events()
song['copyright'] = '/'.join([li.string for li in song_page.find('ul', 'copyright').find_all('li')])
song['copyright'] = self.html_parser.unescape(song['copyright'])
song['ccli_number'] = song_page.find('ul', 'info').find('li').string.split(':')[1].strip()
song['verses'] = []
if lyrics_page:
verses = lyrics_page.find('section', 'lyrics').find_all('p')
verse_labels = lyrics_page.find('section', 'lyrics').find_all('h3')
for counter in range(len(verses)):
verse = {'label': verse_labels[counter].string, 'lyrics': ''}
for v in verses[counter].contents:
if isinstance(v, NavigableString):
verse['lyrics'] = verse['lyrics'] + v.string
else:
verse['lyrics'] += '\n'
verse['lyrics'] = verse['lyrics'].strip(' \n\r\t')
song['verses'].append(self.html_parser.unescape(verse))
self.title_edit.setText(song['title'])
self.copyright_edit.setText(song['copyright'])
self.ccli_edit.setText(song['ccli_number'])
for author in song['authors']:
QtGui.QListWidgetItem(self.html_parser.unescape(author), self.author_list_widget)
for counter, verse in enumerate(song['verses']):
log.debug('Verse type: %s', verse['label'])
self.lyrics_table_widget.setRowCount(self.lyrics_table_widget.rowCount() + 1)
item = QtGui.QTableWidgetItem(verse['lyrics'])
item.setData(QtCore.Qt.UserRole, verse['label'])
item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
self.lyrics_table_widget.setItem(counter, 0, item)
self.lyrics_table_widget.setVerticalHeaderLabels([verse['label'] for verse in song['verses']])
self.lyrics_table_widget.resizeRowsToContents()
self.title_edit.setEnabled(True)
self.copyright_edit.setEnabled(True)
self.ccli_edit.setEnabled(True)
self.author_list_widget.setEnabled(True)
self.lyrics_table_widget.setEnabled(True)
self.lyrics_table_widget.repaint()
self.import_button.setEnabled(True)
self.back_button.setEnabled(True)
self.song_progress_bar.setVisible(False)
self.song_progress_bar.setValue(0)
self.song = song
self.main_window.application.process_events()
def on_save_password_checkbox_toggled(self, checked):
"""
Show a warning dialog when the user toggles the save checkbox on or off.
:param checked: If the combobox is checked or not
"""
if checked and self.login_page.isVisible():
answer = QtGui.QMessageBox.question(
self, translate('SongsPlugin.SongSelectForm', 'Save Username and Password'),
translate('SongsPlugin.SongSelectForm', 'WARNING: Saving your username and password is INSECURE, your '
'password is stored in PLAIN TEXT. Click Yes to save your '
'password or No to cancel this.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
self.save_password_checkbox.setChecked(False)
def on_login_button_clicked(self):
"""
Log the user in to SongSelect.
"""
self.username_edit.setEnabled(False)
self.password_edit.setEnabled(False)
self.save_password_checkbox.setEnabled(False)
self.login_button.setEnabled(False)
self.login_spacer.setVisible(False)
self.login_progress_bar.setVisible(True)
self.login_progress_bar.setValue(1)
self.main_window.application.process_events()
login_page = BeautifulSoup(self.opener.open(LOGIN_URL).read(), 'lxml')
self.login_progress_bar.setValue(2)
self.main_window.application.process_events()
token_input = login_page.find('input', attrs={'name': '__RequestVerificationToken'})
data = urlencode({
'__RequestVerificationToken': token_input['value'],
'UserName': self.username_edit.text(),
'Password': self.password_edit.text(),
'RememberMe': 'false'
})
posted_page = BeautifulSoup(self.opener.open(LOGIN_URL, data.encode('utf-8')).read(), 'lxml')
self.login_progress_bar.setValue(3)
self.main_window.application.process_events()
if posted_page.find('input', attrs={'name': '__RequestVerificationToken'}):
QtGui.QMessageBox.critical(
self,
translate('SongsPlugin.SongSelectForm', 'Error Logging In'),
translate('SongsPlugin.SongSelectForm',
'There was a problem logging in, perhaps your username or password is incorrect?')
)
else:
if self.save_password_checkbox.isChecked():
Settings().setValue(self.plugin.settings_section + '/songselect username', self.username_edit.text())
Settings().setValue(self.plugin.settings_section + '/songselect password', self.password_edit.text())
else:
Settings().remove(self.plugin.settings_section + '/songselect username')
Settings().remove(self.plugin.settings_section + '/songselect password')
self.stacked_widget.setCurrentIndex(1)
self.login_progress_bar.setVisible(False)
self.login_progress_bar.setValue(0)
self.login_spacer.setVisible(True)
self.login_button.setEnabled(True)
self.search_combobox.setFocus()
self.main_window.application.process_events()
def on_search_button_clicked(self):
"""
Run a search on SongSelect.
"""
self.view_button.setEnabled(False)
self.search_button.setEnabled(False)
self.search_progress_bar.setVisible(True)
self.search_progress_bar.setMinimum(0)
self.search_progress_bar.setMaximum(0)
self.search_progress_bar.setValue(0)
self.search_results_widget.clear()
self.result_count_label.setText(translate('SongsPlugin.SongSelectForm', 'Found %s song(s)') % self.song_count)
self.main_window.application.process_events()
self.song_count = 0
search_history = self.search_combobox.getItems()
Settings().setValue(self.plugin.settings_section + '/songselect searches', '|'.join(search_history))
# Create thread and run search
self.thread = QtCore.QThread()
self.worker = SearchWorker(self.opener, {'SearchTerm': self.search_combobox.currentText(),
'allowredirect': 'false'})
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.start)
self.worker.show_info.connect(self.on_search_show_info)
self.worker.found_song.connect(self.on_search_found_song)
self.worker.finished.connect(self.on_search_finished)
self.worker.quit.connect(self.thread.quit)
self.worker.quit.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.start()
def on_search_show_info(self, title, message):
"""
Show an informational message from the search thread
:param title:
:param message:
"""
QtGui.QMessageBox.information(self, title, message)
def on_search_found_song(self, song):
"""
Add a song to the list when one is found.
:param song:
"""
log.debug('SongSelect (title = "%s"), (link = "%s")', song['title'], song['link'])
self.song_count += 1
self.result_count_label.setText(translate('SongsPlugin.SongSelectForm', 'Found %s song(s)') % self.song_count)
item_title = song['title'] + ' (' + ', '.join(song['authors']) + ')'
song_item = QtGui.QListWidgetItem(item_title, self.search_results_widget)
song_item.setData(QtCore.Qt.UserRole, song)
def on_search_finished(self, songs):
"""
Slot which is called when the search is completed.
:param songs:
"""
self.main_window.application.process_events()
self.search_progress_bar.setVisible(False)
self.search_button.setEnabled(True)
self.main_window.application.process_events()
def on_search_results_widget_selection_changed(self):
"""
Enable or disable the view button when the selection changes.
"""
self.view_button.setEnabled(len(self.search_results_widget.selectedItems()) > 0)
def on_view_button_clicked(self):
"""
View a song from SongSelect.
"""
self._view_song(self.search_results_widget.currentItem())
def on_search_results_widget_double_clicked(self, current_item):
"""
View a song from SongSelect
:param current_item:
"""
self._view_song(current_item)
def on_back_button_clicked(self):
"""
Go back to the search page.
"""
self.stacked_widget.setCurrentIndex(1)
self.search_combobox.setFocus()
def on_import_button_clicked(self):
"""
Import a song from SongSelect.
"""
song = Song.populate(
title=self.song['title'],
copyright=self.song['copyright'],
ccli_number=self.song['ccli_number']
)
song_xml = SongXML()
verse_order = []
for verse in self.song['verses']:
verse_type, verse_number = verse['label'].split(' ')[:2]
verse_type = VerseType.from_loose_input(verse_type)
verse_number = int(verse_number)
song_xml.add_verse_to_lyrics(
VerseType.tags[verse_type],
verse_number,
verse['lyrics']
)
verse_order.append('%s%s' % (VerseType.tags[verse_type], verse_number))
song.verse_order = ' '.join(verse_order)
song.lyrics = song_xml.extract_xml()
clean_song(self.db_manager, song)
self.db_manager.save_object(song)
song.authors = []
for author_name in self.song['authors']:
#author_name = unicode(author_name)
author = self.db_manager.get_object_filtered(Author, Author.display_name == author_name)
if not author:
author = Author.populate(
first_name=author_name.rsplit(' ', 1)[0],
last_name=author_name.rsplit(' ', 1)[1],
display_name=author_name
)
song.authors.append(author)
self.db_manager.save_object(song)
question_dialog = QtGui.QMessageBox()
question_dialog.setWindowTitle(translate('SongsPlugin.SongSelectForm', 'Song Imported'))
question_dialog.setText(translate('SongsPlugin.SongSelectForm', 'Your song has been imported, would you like '
'to exit now, or import more songs?'))
question_dialog.addButton(QtGui.QPushButton(translate('SongsPlugin.SongSelectForm', 'Import More Songs')),
QtGui.QMessageBox.YesRole)
question_dialog.addButton(QtGui.QPushButton(translate('SongsPlugin.SongSelectForm', 'Exit Now')),
QtGui.QMessageBox.NoRole)
if question_dialog.exec_() == QtGui.QMessageBox.Yes:
self.on_back_button_clicked()
else:
self.main_window.application.process_events()
self.done(QtGui.QDialog.Accepted)

View File

@ -38,11 +38,13 @@ import sqlite3
from PyQt4 import QtCore, QtGui
from openlp.core.common import UiStrings, translate
from openlp.core.common import UiStrings, Registry, translate
from openlp.core.lib import Plugin, StringContent, build_icon
from openlp.core.lib.db import Manager
from openlp.core.lib.ui import create_action
from openlp.core.utils.actions import ActionList
from openlp.plugins.songs.forms.duplicatesongremovalform import DuplicateSongRemovalForm
from openlp.plugins.songs.forms.songselectform import SongSelectForm
from openlp.plugins.songs.lib import clean_song, upgrade
from openlp.plugins.songs.lib.db import init_schema, Song
from openlp.plugins.songs.lib.mediaitem import SongSearch
@ -50,30 +52,29 @@ from openlp.plugins.songs.lib.importer import SongFormat
from openlp.plugins.songs.lib.olpimport import OpenLPSongImport
from openlp.plugins.songs.lib.mediaitem import SongMediaItem
from openlp.plugins.songs.lib.songstab import SongsTab
from openlp.plugins.songs.forms.duplicatesongremovalform import DuplicateSongRemovalForm
log = logging.getLogger(__name__)
__default_settings__ = {
'songs/db type': 'sqlite',
'songs/last search type': SongSearch.Entire,
'songs/last import type': SongFormat.OpenLyrics,
'songs/update service on edit': False,
'songs/search as type': False,
'songs/add song from service': True,
'songs/display songbar': True,
'songs/last directory import': '',
'songs/last directory export': ''
}
'songs/db type': 'sqlite',
'songs/last search type': SongSearch.Entire,
'songs/last import type': SongFormat.OpenLyrics,
'songs/update service on edit': False,
'songs/search as type': False,
'songs/add song from service': True,
'songs/display songbar': True,
'songs/last directory import': '',
'songs/last directory export': '',
'songs/songselect username': '',
'songs/songselect password': '',
'songs/songselect searches': ''
}
class SongsPlugin(Plugin):
"""
This is the number 1 plugin, if importance were placed on any
plugins. This plugin enables the user to create, edit and display
songs. Songs are divided into verses, and the verse order can be
specified. Authors, topics and song books can be assigned to songs
as well.
This plugin enables the user to create, edit and display songs. Songs are divided into verses, and the verse order
can be specified. Authors, topics and song books can be assigned to songs as well.
"""
log.info('Song Plugin loaded')
@ -86,6 +87,7 @@ class SongsPlugin(Plugin):
self.weight = -10
self.icon_path = ':/plugins/plugin_songs.png'
self.icon = build_icon(self.icon_path)
self.songselect_form = None
def check_pre_conditions(self):
"""
@ -94,8 +96,12 @@ class SongsPlugin(Plugin):
return self.manager.session is not None
def initialise(self):
"""
Initialise the plugin
"""
log.info('Songs Initialising')
super(SongsPlugin, self).initialise()
self.songselect_form = SongSelectForm(Registry().get('main_window'), self, self.manager)
self.song_import_item.setVisible(True)
self.song_export_item.setVisible(True)
self.tools_reindex_item.setVisible(True)
@ -108,28 +114,27 @@ class SongsPlugin(Plugin):
def add_import_menu_item(self, import_menu):
"""
Give the Songs plugin the opportunity to add items to the
**Import** menu.
Give the Songs plugin the opportunity to add items to the **Import** menu.
``import_menu``
The actual **Import** menu item, so that your actions can
use it as their parent.
:param import_menu: The actual **Import** menu item, so that your actions can use it as their parent.
"""
# Main song import menu item - will eventually be the only one
self.song_import_item = create_action(import_menu, 'songImportItem',
text=translate('SongsPlugin', '&Song'),
tooltip=translate('SongsPlugin', 'Import songs using the import wizard.'),
triggers=self.on_song_import_item_clicked)
self.song_import_item = create_action(import_menu, 'songImportItem', text=translate('SongsPlugin', '&Song'),
tooltip=translate('SongsPlugin', 'Import songs using the import wizard.'),
triggers=self.on_song_import_item_clicked)
import_menu.addAction(self.song_import_item)
self.import_songselect_item = create_action(
import_menu, 'import_songselect_item', text=translate('SongsPlugin', 'CCLI SongSelect'),
statustip=translate('SongsPlugin', 'Import songs from CCLI\'s SongSelect service.'),
triggers=self.on_import_songselect_item_triggered
)
import_menu.addAction(self.import_songselect_item)
def add_export_menu_Item(self, export_menu):
"""
Give the Songs plugin the opportunity to add items to the
**Export** menu.
Give the Songs plugin the opportunity to add items to the **Export** menu.
``export_menu``
The actual **Export** menu item, so that your actions can
use it as their parent.
:param export_menu: The actual **Export** menu item, so that your actions can use it as their parent.
"""
# Main song import menu item - will eventually be the only one
self.song_export_item = create_action(export_menu, 'songExportItem',
@ -140,12 +145,9 @@ class SongsPlugin(Plugin):
def add_tools_menu_item(self, tools_menu):
"""
Give the Songs plugin the opportunity to add items to the
**Tools** menu.
Give the Songs plugin the opportunity to add items to the **Tools** menu.
``tools_menu``
The actual **Tools** menu item, so that your actions can
use it as their parent.
:param tools_menu: The actual **Tools** menu item, so that your actions can use it as their parent.
"""
log.info('add tools menu')
self.tools_reindex_item = create_action(tools_menu, 'toolsReindexItem',
@ -165,11 +167,11 @@ class SongsPlugin(Plugin):
"""
Rebuild each song.
"""
maxSongs = self.manager.get_object_count(Song)
if maxSongs == 0:
max_songs = self.manager.get_object_count(Song)
if max_songs == 0:
return
progress_dialog = QtGui.QProgressDialog(translate('SongsPlugin', 'Reindexing songs...'), UiStrings().Cancel,
0, maxSongs, self.main_window)
0, max_songs, self.main_window)
progress_dialog.setWindowTitle(translate('SongsPlugin', 'Reindexing songs'))
progress_dialog.setWindowModality(QtCore.Qt.WindowModal)
songs = self.manager.get_all_objects(Song)
@ -185,42 +187,61 @@ class SongsPlugin(Plugin):
"""
DuplicateSongRemovalForm(self).exec_()
def on_import_songselect_item_triggered(self):
"""
Run the SongSelect importer.
"""
self.songselect_form.exec_()
self.media_item.on_search_text_button_clicked()
def on_song_import_item_clicked(self):
"""
Run the song import wizard.
"""
if self.media_item:
self.media_item.on_import_click()
def on_song_export_item_clicked(self):
"""
Run the song export wizard.
"""
if self.media_item:
self.media_item.on_export_click()
def about(self):
"""
Provides information for the plugin manager to display.
:return: A translatable string with some basic information about the Songs plugin
"""
return translate('SongsPlugin', '<strong>Songs Plugin</strong>'
'<br />The songs plugin provides the ability to display and manage songs.')
'<br />The songs plugin provides the ability to display and manage songs.')
def uses_theme(self, theme):
"""
Called to find out if the song plugin is currently using a theme.
Returns True if the theme is being used, otherwise returns False.
:param theme: The theme to check for usage
:return: True if the theme is being used, otherwise returns False
"""
if self.manager.get_all_objects(Song, Song.theme_name == theme):
return True
return False
def rename_theme(self, oldTheme, newTheme):
def rename_theme(self, old_theme, new_theme):
"""
Renames a theme the song plugin is using making the plugin use the new
name.
``oldTheme``
``old_theme``
The name of the theme the plugin should stop using.
``newTheme``
``new_theme``
The new name the plugin should now use.
"""
songsUsingTheme = self.manager.get_all_objects(Song, Song.theme_name == oldTheme)
for song in songsUsingTheme:
song.theme_name = newTheme
songs_using_theme = self.manager.get_all_objects(Song, Song.theme_name == old_theme)
for song in songs_using_theme:
song.theme_name = new_theme
self.manager.save_object(song)
def importSongs(self, format, **kwargs):

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

View File

@ -61,6 +61,8 @@
<file>general_email.png</file>
<file>general_revert.png</file>
<file>general_clone.png</file>
<file>general_find.png</file>
<file>general_back.png</file>
</qresource>
<qresource prefix="slides">
<file>slide_close.png</file>