diff --git a/openlp/.version b/openlp/.version index 2007f03af..8fdcf3869 100644 --- a/openlp/.version +++ b/openlp/.version @@ -1 +1 @@ -1.9.1-bzr821 +1.9.2 diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 5408c611d..d6f805c98 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -22,7 +22,6 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - """ The :mod:`lib` module contains most of the components and libraries that make OpenLP work. @@ -47,6 +46,10 @@ def translate(context, text, comment=None): ``text`` The text to put into the translation tables for translation. + + ``comment`` + An identifying string for when the same text is used in different roles + within the same context. """ return QtCore.QCoreApplication.translate(context, text, comment) @@ -115,6 +118,18 @@ def build_icon(icon): def context_menu_action(base, icon, text, slot): """ Utility method to help build context menus for plugins + + ``base`` + The parent menu to add this menu item to + + ``icon`` + An icon for this action + + ``text`` + The text to display for this action + + ``slot`` + The code to run when this action is triggered """ action = QtGui.QAction(text, base) if icon: @@ -125,6 +140,15 @@ def context_menu_action(base, icon, text, slot): def context_menu(base, icon, text): """ Utility method to help build context menus for plugins + + ``base`` + The parent object to add this menu to + + ``icon`` + An icon for this menu + + ``text`` + The text to display for this menu """ action = QtGui.QMenu(text, base) action.setIcon(build_icon(icon)) @@ -133,6 +157,9 @@ def context_menu(base, icon, text): def context_menu_separator(base): """ Add a separator to a context menu + + ``base`` + The menu object to add the separator to """ action = QtGui.QAction(u'', base) action.setSeparator(True) @@ -200,5 +227,4 @@ from themexmlhandler import ThemeXML from renderer import Renderer from rendermanager import RenderManager from mediamanageritem import MediaManagerItem -from basemodel import BaseModel from baselistwithdnd import BaseListWithDnD diff --git a/openlp/core/lib/basemodel.py b/openlp/core/lib/basemodel.py deleted file mode 100644 index f56336c7b..000000000 --- a/openlp/core/lib/basemodel.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# 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 # -############################################################################### - -class BaseModel(object): - """ - BaseModel provides a base object with a set of generic functions - """ - - @classmethod - def populate(cls, **kwargs): - """ - Creates an instance of a class and populates it, returning the instance - """ - me = cls() - for key in kwargs: - me.__setattr__(key, kwargs[key]) - return me - diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py new file mode 100644 index 000000000..f20f3ab38 --- /dev/null +++ b/openlp/core/lib/db.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # +# Thompson, Jon Tibble, Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# 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:`db` module provides the core database functionality for OpenLP +""" +import logging +import os + +from PyQt4 import QtCore +from sqlalchemy import create_engine, MetaData +from sqlalchemy.exceptions import InvalidRequestError +from sqlalchemy.orm import scoped_session, sessionmaker + +from openlp.core.utils import AppLocation + +log = logging.getLogger(__name__) + +def init_db(url, auto_flush=True, auto_commit=False): + """ + Initialise and return the session and metadata for a database + + ``url`` + The database to initialise connection with + + ``auto_flush`` + Sets the flushing behaviour of the session + + ``auto_commit`` + Sets the commit behaviour of the session + """ + engine = create_engine(url) + metadata = MetaData(bind=engine) + session = scoped_session(sessionmaker(autoflush=auto_flush, + autocommit=auto_commit, bind=engine)) + return session, metadata + +def delete_database(plugin_name, db_file_name=None): + """ + Remove a database file from the system. + + ``plugin_name`` + The name of the plugin to remove the database for + + ``db_file_name`` + The database file name. Defaults to None resulting in the + plugin_name being used. + """ + db_file_path = None + if db_file_name: + db_file_path = os.path.join( + AppLocation.get_section_data_path(plugin_name), db_file_name) + else: + db_file_path = os.path.join( + AppLocation.get_section_data_path(plugin_name), plugin_name) + try: + os.remove(db_file_path) + return True + except OSError: + return False + +class BaseModel(object): + """ + BaseModel provides a base object with a set of generic functions + """ + @classmethod + def populate(cls, **kwargs): + """ + Creates an instance of a class and populates it, returning the instance + """ + me = cls() + for key in kwargs: + me.__setattr__(key, kwargs[key]) + return me + +class Manager(object): + """ + Provide generic object persistence management + """ + def __init__(self, plugin_name, init_schema, db_file_name=None): + """ + Runs the initialisation process that includes creating the connection + to the database and the tables if they don't exist. + + ``plugin_name`` + The name to setup paths and settings section names + + ``init_schema`` + The init_schema function for this database + + ``db_file_name`` + The file name to use for this database. Defaults to None resulting + in the plugin_name being used. + """ + settings = QtCore.QSettings() + settings.beginGroup(plugin_name) + self.db_url = u'' + db_type = unicode( + settings.value(u'db type', QtCore.QVariant(u'sqlite')).toString()) + if db_type == u'sqlite': + if db_file_name: + self.db_url = u'sqlite:///%s/%s' % ( + AppLocation.get_section_data_path(plugin_name), + db_file_name) + else: + self.db_url = u'sqlite:///%s/%s.sqlite' % ( + AppLocation.get_section_data_path(plugin_name), plugin_name) + else: + self.db_url = u'%s://%s:%s@%s/%s' % (db_type, + unicode(settings.value(u'db username').toString()), + unicode(settings.value(u'db password').toString()), + unicode(settings.value(u'db hostname').toString()), + unicode(settings.value(u'db database').toString())) + settings.endGroup() + self.session = init_schema(self.db_url) + + def save_object(self, object_instance): + """ + Save an object to the database + + ``object_instance`` + The object to save + """ + try: + self.session.add(object_instance) + self.session.commit() + return True + except InvalidRequestError: + self.session.rollback() + log.exception(u'Object save failed') + return False + + def get_object(self, object_class, key=None): + """ + Return the details of an object + + ``object_class`` + The type of object to return + + ``key`` + The unique reference or primary key for the instance to return + """ + if not key: + return object_class() + else: + return self.session.query(object_class).get(key) + + def get_object_filtered(self, object_class, filter_clause): + """ + Returns an object matching specified criteria + + ``object_class`` + The type of object to return + + ``filter_clause`` + The criteria to select the object by + """ + return self.session.query(object_class).filter(filter_clause).first() + + def get_all_objects(self, object_class, order_by_ref=None): + """ + Returns all the objects from the database + + ``object_class`` + The type of objects to return + + ``order_by_ref`` + Any parameters to order the returned objects by. Defaults to None. + """ + query = self.session.query(object_class) + if order_by_ref: + return query.order_by(order_by_ref).all() + return query.all() + + def get_all_objects_filtered(self, object_class, filter_clause, + order_by_ref=None): + """ + Returns a selection of objects from the database + + ``object_class`` + The type of objects to return + + ``filter_clause`` + The filter governing selection of objects to return + + ``order_by_ref`` + Any parameters to order the returned objects by. Defaults to None. + """ + query = self.session.query(object_class).filter(filter_clause) + if order_by_ref: + return query.order_by(order_by_ref).all() + return query.all() + + def delete_object(self, object_class, key): + """ + Delete an object from the database + + ``object_class`` + The type of object to delete + + ``key`` + The unique reference or primary key for the instance to be deleted + """ + if key != 0: + object_instance = self.get_object(object_class, key) + try: + self.session.delete(object_instance) + self.session.commit() + return True + except InvalidRequestError: + self.session.rollback() + log.exception(u'Failed to delete object') + return False + else: + return True + + def delete_all_objects(self, object_class): + """ + Delete all object records + + ``object_class`` + The type of object to delete + """ + try: + self.session.query(object_class).delete(synchronize_session=False) + self.session.commit() + return True + except InvalidRequestError: + self.session.rollback() + log.exception(u'Failed to delete all %s records', + object_class.__name__) + return False diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 2983eaca2..322ce8cd0 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -525,8 +525,7 @@ class MediaManagerItem(QtGui.QWidget): if self.ServiceItemIconName: service_item.add_icon(self.ServiceItemIconName) else: - service_item.add_icon( - u':/media/media_' + self.PluginNameShort.lower() + u'.png') + service_item.add_icon(self.parent.icon) if self.generateSlideData(service_item, item): return service_item else: diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index a5d712dfb..1450ef1fe 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -242,19 +242,22 @@ class Plugin(QtCore.QObject): """ if self.media_item: self.media_item.initialise() + self.insert_toolbox_item() def finalise(self): """ Called by the plugin Manager to cleanup things. """ - pass + self.remove_toolbox_item() def remove_toolbox_item(self): """ Called by the plugin to remove toolbar """ - self.mediadock.remove_dock(self.name) - self.settings_form.removeTab(self.name) + if self.media_item: + self.mediadock.remove_dock(self.name) + if self.settings_tab: + self.settings_form.removeTab(self.name) def insert_toolbox_item(self): """ diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index aa8c0a95a..c7f96528c 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -22,7 +22,10 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - +""" +The :mod:`renderer` module enables OpenLP to take the input from plugins and +format it for the output display. +""" import logging from PyQt4 import QtGui, QtCore diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index dfcad984a..c107a1872 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -35,6 +35,7 @@ import uuid from PyQt4 import QtGui from openlp.core.lib import build_icon, resize_image +from openlp.core.utils import AppLocation log = logging.getLogger(__name__) @@ -92,6 +93,7 @@ class ServiceItem(object): self.is_valid = True self.cache = {} self.icon = None + self.serviceItemPath = AppLocation.get_section_data_path(u'serviceItems') def add_capability(self, capability): """ @@ -153,9 +155,12 @@ class ServiceItem(object): del self.cache[len(self._display_frames)] log.log(15, u'Formatting took %4s' % (time.time() - before)) elif self.service_item_type == ServiceItemType.Image: - for slide in self._raw_frames: + for count, slide in enumerate(self._raw_frames): slide[u'image'] = resize_image(slide[u'image'], self.render_manager.width, self.render_manager.height) + path = os.path.join(self.serviceItemPath, self._uuid + unicode(count) + u'.png') + slide[u'image'].save(path) + slide[u'display'] = path elif self.service_item_type == ServiceItemType.Command: pass else: @@ -371,7 +376,8 @@ class ServiceItem(object): if self.service_item_type == ServiceItemType.Text: return self.render_individual(row) else: - return {u'main':self._raw_frames[row][u'image'], u'trans':None} + return {u'main':self._raw_frames[row][u'image'], + u'trans':None, u'display':self._raw_frames[row][u'display']} def get_frame_title(self, row=0): """ diff --git a/openlp/core/theme/theme.py b/openlp/core/theme/theme.py index 2ea13d01b..6cddbcb45 100644 --- a/openlp/core/theme/theme.py +++ b/openlp/core/theme/theme.py @@ -168,7 +168,7 @@ class Theme(object): theme_strings.append(u'_%s_' % (getattr(self, key))) return u''.join(theme_strings) - def _set_from_XML(self, xml): + def _set_from_xml(self, xml): """ Set theme class attributes with data from XML diff --git a/openlp/core/ui/amendthemedialog.py b/openlp/core/ui/amendthemedialog.py index a75f5e686..2bb202964 100644 --- a/openlp/core/ui/amendthemedialog.py +++ b/openlp/core/ui/amendthemedialog.py @@ -178,13 +178,11 @@ class Ui_AmendThemeDialog(object): self.MainFontLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.FontMainSize) self.FontMainSizeSpinBox = QtGui.QSpinBox(self.FontMainGroupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, + defaultSizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( + defaultSizePolicy.setHeightForWidth( self.FontMainSizeSpinBox.sizePolicy().hasHeightForWidth()) - self.FontMainSizeSpinBox.setSizePolicy(sizePolicy) + self.FontMainSizeSpinBox.setSizePolicy(defaultSizePolicy) self.FontMainSizeSpinBox.setMinimumSize(QtCore.QSize(70, 0)) self.FontMainSizeSpinBox.setProperty(u'value', QtCore.QVariant(16)) self.FontMainSizeSpinBox.setMaximum(999) @@ -230,8 +228,7 @@ class Ui_AmendThemeDialog(object): self.FontMainLineSpacingSpinBox) self.FontMainLinesPageLabel = QtGui.QLabel(self.FontMainGroupBox) self.FontMainLinesPageLabel.setObjectName(u'FontMainLinesPageLabel') - self.MainFontLayout.setWidget(6, QtGui.QFormLayout.LabelRole, - self.FontMainLinesPageLabel) + self.MainFontLayout.addRow(self.FontMainLinesPageLabel) spacerItem1 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.MainLeftLayout.addItem(spacerItem1) @@ -275,13 +272,9 @@ class Ui_AmendThemeDialog(object): self.MainLocationLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.FontMainHeightLabel) self.FontMainXSpinBox = QtGui.QSpinBox(self.MainLocationGroupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, - QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( + defaultSizePolicy.setHeightForWidth( self.FontMainXSpinBox.sizePolicy().hasHeightForWidth()) - self.FontMainXSpinBox.setSizePolicy(sizePolicy) + self.FontMainXSpinBox.setSizePolicy(defaultSizePolicy) self.FontMainXSpinBox.setMinimumSize(QtCore.QSize(78, 0)) self.FontMainXSpinBox.setProperty(u'value', QtCore.QVariant(0)) self.FontMainXSpinBox.setMaximum(9999) @@ -289,39 +282,27 @@ class Ui_AmendThemeDialog(object): self.MainLocationLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.FontMainXSpinBox) self.FontMainYSpinBox = QtGui.QSpinBox(self.MainLocationGroupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, - QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( + defaultSizePolicy.setHeightForWidth( self.FontMainYSpinBox.sizePolicy().hasHeightForWidth()) - self.FontMainYSpinBox.setSizePolicy(sizePolicy) + self.FontMainYSpinBox.setSizePolicy(defaultSizePolicy) self.FontMainYSpinBox.setMinimumSize(QtCore.QSize(78, 0)) self.FontMainYSpinBox.setMaximum(9999) self.FontMainYSpinBox.setObjectName(u'FontMainYSpinBox') self.MainLocationLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.FontMainYSpinBox) self.FontMainWidthSpinBox = QtGui.QSpinBox(self.MainLocationGroupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, - QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( + defaultSizePolicy.setHeightForWidth( self.FontMainWidthSpinBox.sizePolicy().hasHeightForWidth()) - self.FontMainWidthSpinBox.setSizePolicy(sizePolicy) + self.FontMainWidthSpinBox.setSizePolicy(defaultSizePolicy) self.FontMainWidthSpinBox.setMinimumSize(QtCore.QSize(78, 0)) self.FontMainWidthSpinBox.setMaximum(9999) self.FontMainWidthSpinBox.setObjectName(u'FontMainWidthSpinBox') self.MainLocationLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.FontMainWidthSpinBox) self.FontMainHeightSpinBox = QtGui.QSpinBox(self.MainLocationGroupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, - QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( + defaultSizePolicy.setHeightForWidth( self.FontMainHeightSpinBox.sizePolicy().hasHeightForWidth()) - self.FontMainHeightSpinBox.setSizePolicy(sizePolicy) + self.FontMainHeightSpinBox.setSizePolicy(defaultSizePolicy) self.FontMainHeightSpinBox.setMinimumSize(QtCore.QSize(78, 0)) self.FontMainHeightSpinBox.setMaximum(9999) self.FontMainHeightSpinBox.setObjectName(u'FontMainHeightSpinBox') @@ -378,13 +359,9 @@ class Ui_AmendThemeDialog(object): self.FooterFontLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.FontFooterSizeLabel) self.FontFooterSizeSpinBox = QtGui.QSpinBox(self.FooterFontGroupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, - QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( + defaultSizePolicy.setHeightForWidth( self.FontFooterSizeSpinBox.sizePolicy().hasHeightForWidth()) - self.FontFooterSizeSpinBox.setSizePolicy(sizePolicy) + self.FontFooterSizeSpinBox.setSizePolicy(defaultSizePolicy) self.FontFooterSizeSpinBox.setMinimumSize(QtCore.QSize(70, 0)) self.FontFooterSizeSpinBox.setProperty(u'value', QtCore.QVariant(10)) self.FontFooterSizeSpinBox.setMaximum(999) @@ -453,13 +430,9 @@ class Ui_AmendThemeDialog(object): self.LocationFooterLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.FontFooterHeightLabel) self.FontFooterXSpinBox = QtGui.QSpinBox(self.LocationFooterGroupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, - QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( + defaultSizePolicy.setHeightForWidth( self.FontFooterXSpinBox.sizePolicy().hasHeightForWidth()) - self.FontFooterXSpinBox.setSizePolicy(sizePolicy) + self.FontFooterXSpinBox.setSizePolicy(defaultSizePolicy) self.FontFooterXSpinBox.setMinimumSize(QtCore.QSize(78, 0)) self.FontFooterXSpinBox.setProperty(u'value', QtCore.QVariant(0)) self.FontFooterXSpinBox.setMaximum(9999) @@ -467,13 +440,9 @@ class Ui_AmendThemeDialog(object): self.LocationFooterLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.FontFooterXSpinBox) self.FontFooterYSpinBox = QtGui.QSpinBox(self.LocationFooterGroupBox) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, - QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( - self.FontFooterYSpinBox.sizePolicy().hasHeightForWidth()) - self.FontFooterYSpinBox.setSizePolicy(sizePolicy) + defaultSizePolicy.setHeightForWidth( + self.FontFooterXSpinBox.sizePolicy().hasHeightForWidth()) + self.FontFooterYSpinBox.setSizePolicy(defaultSizePolicy) self.FontFooterYSpinBox.setMinimumSize(QtCore.QSize(78, 0)) self.FontFooterYSpinBox.setProperty(u'value', QtCore.QVariant(0)) self.FontFooterYSpinBox.setMaximum(9999) @@ -901,4 +870,3 @@ class Ui_AmendThemeDialog(object): self.ThemeTabWidget.indexOf(self.OtherOptionsTab), translate('AmendThemeForm', '&Other Options')) self.PreviewGroupBox.setTitle(translate('AmendThemeForm', 'Preview')) - diff --git a/openlp/core/ui/displaytab.py b/openlp/core/ui/displaytab.py index 8a028bbc8..0fb16a9f7 100644 --- a/openlp/core/ui/displaytab.py +++ b/openlp/core/ui/displaytab.py @@ -242,4 +242,3 @@ class DisplayTab(SettingsTab): if self.amend_display_start != self.amend_display: self.amend_display_start = self.amend_display Receiver.send_message(u'config_screen_changed') - diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index 4c42bf90c..340753707 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -32,13 +32,16 @@ class GeneralTab(SettingsTab): GeneralTab is the general settings tab in the settings dialog. """ def __init__(self, screens): + """ + Initialise the general settings tab + """ self.screens = screens + self.MonitorNumber = 0 SettingsTab.__init__(self, u'General') def preLoad(self): """ - Set up the display screen and set correct screen - values. + Set up the display screen and set correct screen values. If not set before default to last screen. """ settings = QtCore.QSettings() @@ -47,12 +50,14 @@ class GeneralTab(SettingsTab): QtCore.QVariant(self.screens.display_count - 1)).toInt()[0] self.screens.set_current_display(self.MonitorNumber) self.screens.monitor_number = self.MonitorNumber - self.DisplayOnMonitor = settings.value( + self.screens.display = settings.value( u'display on monitor', QtCore.QVariant(True)).toBool() - self.screens.display = self.DisplayOnMonitor settings.endGroup() def setupUi(self): + """ + Create the user interface for the general settings tab + """ self.setObjectName(u'GeneralTab') self.tabTitleVisible = translate('GeneralTab', 'General') self.GeneralLayout = QtGui.QHBoxLayout(self) @@ -152,31 +157,11 @@ class GeneralTab(SettingsTab): QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) self.GeneralRightLayout.addItem(self.GeneralRightSpacer) self.GeneralLayout.addWidget(self.GeneralRightWidget) - QtCore.QObject.connect(self.MonitorComboBox, - QtCore.SIGNAL(u'activated(int)'), self.onMonitorComboBoxChanged) - QtCore.QObject.connect(self.DisplayOnMonitorCheck, - QtCore.SIGNAL(u'stateChanged(int)'), - self.onDisplayOnMonitorCheckChanged) - QtCore.QObject.connect(self.WarningCheckBox, - QtCore.SIGNAL(u'stateChanged(int)'), self.onWarningCheckBoxChanged) - QtCore.QObject.connect(self.AutoOpenCheckBox, - QtCore.SIGNAL(u'stateChanged(int)'), self.onAutoOpenCheckBoxChanged) - QtCore.QObject.connect(self.ShowSplashCheckBox, - QtCore.SIGNAL(u'stateChanged(int)'), - self.onShowSplashCheckBoxChanged) - QtCore.QObject.connect(self.SaveCheckServiceCheckBox, - QtCore.SIGNAL(u'stateChanged(int)'), - self.onSaveCheckServiceCheckBox) - QtCore.QObject.connect(self.AutoPreviewCheckBox, - QtCore.SIGNAL(u'stateChanged(int)'), self.onAutoPreviewCheckBox) - QtCore.QObject.connect(self.NumberEdit, - QtCore.SIGNAL(u'editingFinished()'), self.onNumberEditLostFocus) - QtCore.QObject.connect(self.UsernameEdit, - QtCore.SIGNAL(u'editingFinished()'), self.onUsernameEditLostFocus) - QtCore.QObject.connect(self.PasswordEdit, - QtCore.SIGNAL(u'editingFinished()'), self.onPasswordEditLostFocus) def retranslateUi(self): + """ + Translate the general settings tab to the currently selected language + """ self.MonitorGroupBox.setTitle(translate('GeneralTab', 'Monitors')) self.MonitorLabel.setText( translate('GeneralTab', 'Select monitor for output display:')) @@ -204,37 +189,10 @@ class GeneralTab(SettingsTab): self.PasswordLabel.setText( translate('GeneralTab', 'SongSelect Password:')) - def onMonitorComboBoxChanged(self): - self.MonitorNumber = self.MonitorComboBox.currentIndex() - - def onDisplayOnMonitorCheckChanged(self, value): - self.DisplayOnMonitor = (value == QtCore.Qt.Checked) - - def onAutoOpenCheckBoxChanged(self, value): - self.AutoOpen = (value == QtCore.Qt.Checked) - - def onShowSplashCheckBoxChanged(self, value): - self.ShowSplash = (value == QtCore.Qt.Checked) - - def onWarningCheckBoxChanged(self, value): - self.Warning = (value == QtCore.Qt.Checked) - - def onSaveCheckServiceCheckBox(self, value): - self.PromptSaveService = (value == QtCore.Qt.Checked) - - def onAutoPreviewCheckBox(self, value): - self.AutoPreview = (value == QtCore.Qt.Checked) - - def onNumberEditLostFocus(self): - self.CCLINumber = self.NumberEdit.displayText() - - def onUsernameEditLostFocus(self): - self.Username = self.UsernameEdit.displayText() - - def onPasswordEditLostFocus(self): - self.Password = self.PasswordEdit.displayText() - def load(self): + """ + Load the settings to populate the form + """ settings = QtCore.QSettings() settings.beginGroup(self.settingsSection) for screen in self.screens.screen_list: @@ -244,55 +202,54 @@ class GeneralTab(SettingsTab): screen_name = u'%s (%s)' % (screen_name, translate('GeneralTab', 'primary')) self.MonitorComboBox.addItem(screen_name) - # Get the configs - self.Warning = settings.value( - u'blank warning', QtCore.QVariant(False)).toBool() - self.AutoOpen = settings.value( - u'auto open', QtCore.QVariant(False)).toBool() - self.ShowSplash = settings.value( - u'show splash', QtCore.QVariant(True)).toBool() - self.PromptSaveService = settings.value( - u'save prompt', QtCore.QVariant(False)).toBool() - self.AutoPreview = settings.value( - u'auto preview', QtCore.QVariant(False)).toBool() - self.CCLINumber = unicode(settings.value( - u'ccli number', QtCore.QVariant(u'')).toString()) - self.Username = unicode(settings.value( - u'songselect username', QtCore.QVariant(u'')).toString()) - self.Password = unicode(settings.value( - u'songselect password', QtCore.QVariant(u'')).toString()) - settings.endGroup() - self.SaveCheckServiceCheckBox.setChecked(self.PromptSaveService) - # Set a few things up + self.NumberEdit.setText(unicode(settings.value( + u'ccli number', QtCore.QVariant(u'')).toString())) + self.UsernameEdit.setText(unicode(settings.value( + u'songselect username', QtCore.QVariant(u'')).toString())) + self.PasswordEdit.setText(unicode(settings.value( + u'songselect password', QtCore.QVariant(u'')).toString())) + self.SaveCheckServiceCheckBox.setChecked(settings.value(u'save prompt', + QtCore.QVariant(False)).toBool()) self.MonitorComboBox.setCurrentIndex(self.MonitorNumber) - self.DisplayOnMonitorCheck.setChecked(self.DisplayOnMonitor) - self.WarningCheckBox.setChecked(self.Warning) - self.AutoOpenCheckBox.setChecked(self.AutoOpen) - self.ShowSplashCheckBox.setChecked(self.ShowSplash) - self.AutoPreviewCheckBox.setChecked(self.AutoPreview) - self.NumberEdit.setText(self.CCLINumber) - self.UsernameEdit.setText(self.Username) - self.PasswordEdit.setText(self.Password) + self.DisplayOnMonitorCheck.setChecked(self.screens.display) + self.WarningCheckBox.setChecked(settings.value(u'blank warning', + QtCore.QVariant(False)).toBool()) + self.AutoOpenCheckBox.setChecked(settings.value(u'auto open', + QtCore.QVariant(False)).toBool()) + self.ShowSplashCheckBox.setChecked(settings.value(u'show splash', + QtCore.QVariant(True)).toBool()) + self.AutoPreviewCheckBox.setChecked(settings.value(u'auto preview', + QtCore.QVariant(False)).toBool()) + settings.endGroup() def save(self): + """ + Save the settings from the form + """ + self.MonitorNumber = self.MonitorComboBox.currentIndex() settings = QtCore.QSettings() settings.beginGroup(self.settingsSection) settings.setValue(u'monitor', QtCore.QVariant(self.MonitorNumber)) settings.setValue(u'display on monitor', - QtCore.QVariant(self.DisplayOnMonitor)) - settings.setValue(u'blank warning', QtCore.QVariant(self.Warning)) - settings.setValue(u'auto open', QtCore.QVariant(self.AutoOpen)) - settings.setValue(u'show splash', QtCore.QVariant(self.ShowSplash)) + QtCore.QVariant(self.DisplayOnMonitorCheck.isChecked())) + settings.setValue(u'blank warning', + QtCore.QVariant(self.WarningCheckBox.isChecked())) + settings.setValue(u'auto open', + QtCore.QVariant(self.AutoOpenCheckBox.isChecked())) + settings.setValue(u'show splash', + QtCore.QVariant(self.ShowSplashCheckBox.isChecked())) settings.setValue(u'save prompt', - QtCore.QVariant(self.PromptSaveService)) - settings.setValue(u'auto preview', QtCore.QVariant(self.AutoPreview)) - settings.setValue(u'ccli number', QtCore.QVariant(self.CCLINumber)) + QtCore.QVariant(self.SaveCheckServiceCheckBox.isChecked())) + settings.setValue(u'auto preview', + QtCore.QVariant(self.AutoPreviewCheckBox.isChecked())) + settings.setValue(u'ccli number', + QtCore.QVariant(self.NumberEdit.displayText())) settings.setValue(u'songselect username', - QtCore.QVariant(self.Username)) + QtCore.QVariant(self.UsernameEdit.displayText())) settings.setValue(u'songselect password', - QtCore.QVariant(self.Password)) + QtCore.QVariant(self.PasswordEdit.displayText())) settings.endGroup() - self.screens.display = self.DisplayOnMonitor + self.screens.display = self.DisplayOnMonitorCheck.isChecked() #Monitor Number has changed. if self.screens.monitor_number != self.MonitorNumber: self.screens.monitor_number = self.MonitorNumber diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index a3c444097..ae6d400fe 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -26,14 +26,38 @@ import logging import os -from PyQt4 import QtCore, QtGui +from PyQt4 import QtCore, QtGui, QtWebKit from PyQt4.phonon import Phonon from openlp.core.lib import Receiver, resize_image from openlp.core.ui import HideMode +from openlp.core.utils import AppLocation log = logging.getLogger(__name__) +HTMLIMAGE = """ + + + + """ + +#http://www.steveheffernan.com/html5-video-player/demo-video-player.html +HTMLVIDEO = u""" + + \" + + + """ + class DisplayManager(QtGui.QWidget): """ Wrapper class to hold the display widgets. @@ -45,18 +69,79 @@ class DisplayManager(QtGui.QWidget): QtGui.QWidget.__init__(self) self.screens = screens self.videoDisplay = VideoDisplay(self, screens) + self.audioPlayer = AudioPlayer(self) self.mainDisplay = MainDisplay(self, screens) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'videodisplay_start'), self.onStartVideo) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'videodisplay_stop'), self.onStopVideo) def setup(self): self.videoDisplay.setup() self.mainDisplay.setup() + def hideDisplay(self, message): + """ + Hide the output displays + """ + self.videoDisplay.mediaHide(message) + self.mainDisplay.hideDisplay(message) + + def showDisplay(self, message): + """ + Hide the output displays + """ + self.videoDisplay.mediaShow(message) + self.mainDisplay.showDisplay(message) + + def addAlert(self, alertMessage, location): + """ + Handles the addition of an Alert Message to the Displays + """ + self.mainDisplay.addAlert(alertMessage, location) + + def displayImage(self, path): + """ + Handles the addition of a background Image to the displays + """ + self.mainDisplay.displayImage(path) + + def displayVideo(self, path): + """ + Handles the addition of a background Video to the displays + """ + self.mainDisplay.displayVideo(path) + + def onStartVideo(self, item): + """ + Handles the Starting of a Video and Display Management + """ + self.videoDisplay.setVisible(True) + self.mainDisplay.setVisible(False) + self.videoDisplay.onMediaQueue(item) + + def onStopVideo(self): + """ + Handles the Stopping of a Video and Display Management + """ + self.mainDisplay.setVisible(True) + self.videoDisplay.setVisible(False) + self.videoDisplay.onMediaStop() + def close(self): + """ + Handles the closure of the displays + """ self.videoDisplay.close() + self.audioPlayer.close() self.mainDisplay.close() -class DisplayWidget(QtGui.QWidget): +class DisplayWidget(QtGui.QGraphicsView): """ Customised version of QTableWidget which can respond to keyboard events. @@ -117,32 +202,26 @@ class MainDisplay(DisplayWidget): log.debug(u'Initialisation started') DisplayWidget.__init__(self, parent) self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint) - self.setWindowState(QtCore.Qt.WindowFullScreen) + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.parent = parent - self.setWindowTitle(u'OpenLP Display') # WA_TranslucentBackground is not available in QT4.4 try: self.setAttribute(QtCore.Qt.WA_TranslucentBackground) except AttributeError: pass self.screens = screens - self.display_image = QtGui.QLabel(self) - self.display_image.setScaledContents(True) - self.display_text = QtGui.QLabel(self) - self.display_text.setScaledContents(True) - self.display_alert = QtGui.QLabel(self) - self.display_alert.setScaledContents(True) + self.setupScene() + self.setupImage() + self.setupText() + self.setupAlert() + self.setupBlank() self.primary = True self.blankFrame = None self.frame = None - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'videodisplay_start'), self.hideDisplayForVideo) - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay) - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay) - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'videodisplay_background'), self.hideDisplayForVideo) + #Hide desktop for now until we know where to put it + #and what size it should be. + self.setVisible(False) def setup(self): """ @@ -153,12 +232,9 @@ class MainDisplay(DisplayWidget): self.setVisible(False) self.screen = self.screens.current #Sort out screen locations and sizes - self.display_alert.setGeometry(self.screen[u'size']) - self.display_image.resize( - self.screen[u'size'].width(), self.screen[u'size'].height()) - self.display_text.resize( - self.screen[u'size'].width(), self.screen[u'size'].height()) self.setGeometry(self.screen[u'size']) + self.scene.setSceneRect(0,0,self.size().width(), self.size().height()) + self.webView.setGeometry(0, 0, self.size().width(), self.size().height()) #Build a custom splash screen self.InitialFrame = QtGui.QImage( self.screen[u'size'].width(), @@ -172,7 +248,7 @@ class MainDisplay(DisplayWidget): (self.screen[u'size'].width() - splash_image.width()) / 2, (self.screen[u'size'].height() - splash_image.height()) / 2, splash_image) - self.display_image.setPixmap(QtGui.QPixmap.fromImage(self.InitialFrame)) + #self.display_image.setPixmap(QtGui.QPixmap.fromImage(self.InitialFrame)) self.repaint() #Build a Black screen painter = QtGui.QPainter() @@ -186,30 +262,65 @@ class MainDisplay(DisplayWidget): self.transparent = QtGui.QPixmap( self.screen[u'size'].width(), self.screen[u'size'].height()) self.transparent.fill(QtCore.Qt.transparent) - self.display_alert.setPixmap(self.transparent) - self.display_text.setPixmap(self.transparent) - self.frameView(self.transparent) +# self.display_text.setPixmap(self.transparent) + #self.frameView(self.transparent) # To display or not to display? if not self.screen[u'primary']: - self.showFullScreen() + self.setVisible(True) self.primary = False else: self.setVisible(False) self.primary = True + def setupScene(self): + self.scene = QtGui.QGraphicsScene(self) + self.scene.setSceneRect(0,0,self.size().width(), self.size().height()) + self.setScene(self.scene) + + def setupImage(self): + self.webView = QtWebKit.QWebView() + self.page = self.webView.page() + self.imageDisplay = self.page.mainFrame() + self.imageDisplay.setScrollBarPolicy(QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) + self.imageDisplay.setScrollBarPolicy(QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff) + self.proxy = QtGui.QGraphicsProxyWidget() + self.proxy.setWidget(self.webView) + self.proxy.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint) + self.proxy.setZValue(2) + self.scene.addItem(self.proxy) + + def setupText(self): + #self.display_text = QtGui.QGraphicsTextItem() + self.display_text = QtGui.QGraphicsPixmapItem() + #self.display_text.setPos(0,0) + #self.display_text.setTextWidth(self.size().width()) + self.display_text.setZValue(4) + self.scene.addItem(self.display_text) + + def setupAlert(self): + self.alertText = QtGui.QGraphicsTextItem() + self.alertText.setTextWidth(self.size().width()) + self.alertText.setZValue(8) + self.scene.addItem(self.alertText) + + def setupBlank(self): + self.display_blank = QtGui.QGraphicsPixmapItem() + self.display_blank.setZValue(10) + self.scene.addItem(self.display_blank) + def resetDisplay(self): log.debug(u'resetDisplay') Receiver.send_message(u'slidecontroller_live_stop_loop') if self.primary: self.setVisible(False) else: - self.showFullScreen() + self.setVisible(True) - def hideDisplayForVideo(self): - """ - Hides the main display if for the video to be played - """ - self.hideDisplay(HideMode.Screen) +# def hideDisplayForVideo(self): +# """ +# Hides the main display if for the video to be played +# """ +# self.hideDisplay(HideMode.Screen) def hideDisplay(self, mode=HideMode.Screen): """ @@ -217,45 +328,30 @@ class MainDisplay(DisplayWidget): Store the images so they can be replaced when required """ log.debug(u'hideDisplay mode = %d', mode) - self.storeImage = QtGui.QPixmap(self.display_image.pixmap()) - self.storeText = QtGui.QPixmap(self.display_text.pixmap()) - self.display_alert.setPixmap(self.transparent) - self.display_text.setPixmap(self.transparent) + #self.display_text.setPixmap(self.transparent) if mode == HideMode.Screen: - self.display_image.setPixmap(self.transparent) + #self.display_image.setPixmap(self.transparent) + self.setVisible(False) elif mode == HideMode.Blank: - self.display_image.setPixmap( + self.display_blank.setPixmap( QtGui.QPixmap.fromImage(self.blankFrame)) else: if self.parent.renderManager.renderer.bg_frame: - self.display_image.setPixmap(QtGui.QPixmap.fromImage( + self.display_blank.setPixmap(QtGui.QPixmap.fromImage( self.parent.renderManager.renderer.bg_frame)) else: - self.display_image.setPixmap( + self.display_blank.setPixmap( QtGui.QPixmap.fromImage(self.blankFrame)) - self.moveToTop() - def moveToTop(self): - log.debug(u'moveToTop') - self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint | - QtCore.Qt.FramelessWindowHint | QtCore.Qt.Dialog) - self.show() - - def showDisplay(self): + def showDisplay(self, message=u''): """ Show the stored layers so the screen reappears as it was originally. Make the stored images None to release memory. """ log.debug(u'showDisplay') - if self.storeImage: - self.display_image.setPixmap(self.storeImage) - self.display_alert.setPixmap(self.transparent) - if self.storeText: - self.display_text.setPixmap(self.storeText) - self.storeImage = None - self.store = None - self.moveToTop() + self.display_blank.setPixmap(self.transparent) + #Trigger actions when display is active again Receiver.send_message(u'maindisplay_active') def addImageWithText(self, frame): @@ -263,57 +359,91 @@ class MainDisplay(DisplayWidget): frame = resize_image( frame, self.screen[u'size'].width(), self.screen[u'size'].height()) self.display_image.setPixmap(QtGui.QPixmap.fromImage(frame)) - self.moveToTop() - def setAlertSize(self, top, height): - log.debug(u'setAlertSize') - self.display_alert.setGeometry( - QtCore.QRect(0, top, - self.screen[u'size'].width(), height)) - - def addAlertImage(self, frame, blank=False): + def addAlert(self, message, location): + """ + Places the Alert text on the display at the correct location + ``message`` + Text to be displayed + ``location`` + Where on the screen the text should be. From the AlertTab + Combo box. + """ log.debug(u'addAlertImage') - if blank: - self.display_alert.setPixmap(self.transparent) + if location == 0: + self.alertText.setPos(0, 0) + elif location == 1: + self.alertText.setPos(0,self.size().height()/2) else: - self.display_alert.setPixmap(frame) - self.moveToTop() + self.alertText.setPos(0,self.size().height() - 76) + self.alertText.setHtml(message) - def frameView(self, frame, transition=False, display=True): + def displayImage(self, path): + """ + Places the Image passed on the display screen + ``path`` + The path to the image to be displayed + """ + log.debug(u'adddisplayImage') + self.imageDisplay.setHtml(HTMLIMAGE % path) + + def displayVideo(self, path): + """ + Places the Video passed on the display screen + ``path`` + The path to the image to be displayed + """ + log.debug(u'adddisplayVideo') + self.imageDisplay.setHtml(HTMLVIDEO % + (path, self.screen[u'size'].width(), self.screen[u'size'].height())) + + def frameView(self, frame, transition=False): """ Called from a slide controller to display a frame if the alert is in progress the alert is added on top ``frame`` Image frame to be rendered + ``transition`` + Are transitions required. """ - log.debug(u'frameView %d' % (display)) - if display: - if transition: - if self.frame is not None: - self.display_text.setPixmap( - QtGui.QPixmap.fromImage(self.frame)) - self.repaint() - self.frame = None - if frame[u'trans'] is not None: - self.display_text.setPixmap( - QtGui.QPixmap.fromImage(frame[u'trans'])) - self.repaint() - self.frame = frame[u'trans'] + log.debug(u'frameView') + if transition: + if self.frame is not None: self.display_text.setPixmap( - QtGui.QPixmap.fromImage(frame[u'main'])) - self.display_frame = frame[u'main'] + QtGui.QPixmap.fromImage(self.frame)) + self.update() + self.frame = None + if frame[u'trans'] is not None: + self.display_text.setPixmap( + QtGui.QPixmap.fromImage(frame[u'trans'])) self.repaint() - else: - if isinstance(frame, QtGui.QPixmap): - self.display_text.setPixmap(frame) - else: - self.display_text.setPixmap(QtGui.QPixmap.fromImage(frame)) - self.display_frame = frame - if not self.isVisible() and self.screens.display: - self.setVisible(True) - self.showFullScreen() + self.frame = frame[u'trans'] + self.display_text.setPixmap( + QtGui.QPixmap.fromImage(frame[u'main'])) + self.display_frame = frame[u'main'] + self.repaint() else: - self.storeText = QtGui.QPixmap.fromImage(frame[u'main']) + if isinstance(frame, QtGui.QPixmap): + self.display_text.setPixmap(frame) + else: + self.display_text.setPixmap(QtGui.QPixmap.fromImage(frame)) + self.display_frame = frame + if not self.isVisible() and self.screens.display: + self.setVisible(True) + + def closeEvent(self, event): + """ + Shutting down cleans up background files + """ + serviceItemPath = AppLocation.get_section_data_path(u'serviceItems') + for file in os.listdir(serviceItemPath): + file_path = os.path.join(serviceItemPath, file) + try: + if os.path.isfile(file_path): + os.remove(file_path) + except OSError: + log.exception(u'Failed to clean up servicePath') + class VideoDisplay(Phonon.VideoWidget): """ @@ -339,37 +469,29 @@ class VideoDisplay(Phonon.VideoWidget): self.screens = screens self.hidden = False self.message = None + self.mediaActive = False self.mediaObject = Phonon.MediaObject() self.setAspectRatio(aspect) self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory) Phonon.createPath(self.mediaObject, self) Phonon.createPath(self.mediaObject, self.audioObject) flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Dialog - # WindowsStaysOnBottomHint is not available in QT4.4 - try: - flags = flags | QtCore.Qt.WindowStaysOnBottomHint - except AttributeError: - pass +## # WindowsStaysOnBottomHint is not available in QT4.4 +# try: +# flags = flags | QtCore.Qt.WindowStaysOnBottomHint +# except AttributeError: +# pass self.setWindowFlags(flags) - - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'maindisplay_hide'), self.mediaHide) - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'maindisplay_show'), self.mediaShow) - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'videodisplay_start'), self.onMediaQueue) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'videodisplay_play'), self.onMediaPlay) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'videodisplay_pause'), self.onMediaPause) - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'videodisplay_stop'), self.onMediaStop) - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'videodisplay_background'), self.onMediaBackground) +# QtCore.QObject.connect(Receiver.get_receiver(), +# QtCore.SIGNAL(u'videodisplay_background'), self.onMediaBackground) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'config_updated'), self.setup) QtCore.QObject.connect(self.mediaObject, - QtCore.SIGNAL(u'finished()'), self.onMediaBackground) + QtCore.SIGNAL(u'finished()'), self.onMediaStop) self.setVisible(False) def keyPressEvent(self, event): @@ -392,38 +514,48 @@ class VideoDisplay(Phonon.VideoWidget): #Sort out screen locations and sizes self.setGeometry(self.screen[u'size']) # To display or not to display? - if not self.screen[u'primary'] and self.isVisible(): - self.showFullScreen() + if not self.screen[u'primary']: # and self.isVisible(): + #self.showFullScreen() + self.setVisible(False) self.primary = False else: self.setVisible(False) self.primary = True - def onMediaBackground(self, message=None): + def closeEvent(self, event): """ - Play a video triggered from the video plugin with the - file name passed in on the event. - Also triggered from the Finish event so the video will loop - if it is triggered from the plugin + Shutting down so clean up connections """ - log.debug(u'VideoDisplay Queue new media message %s' % message) - #If not file take the stored one - if not message: - message = self.message - # still no file name then stop as it was a normal video stopping - if message: - self.mediaObject.setCurrentSource(Phonon.MediaSource(message)) - self.message = message - self._play() + self.onMediaStop() + for pth in self.outputPaths(): + disconnected = pth.disconnect() + +# def onMediaBackground(self, message=None): +# """ +# Play a video triggered from the video plugin with the +# file name passed in on the event. +# Also triggered from the Finish event so the video will loop +# if it is triggered from the plugin +# """ +# log.debug(u'VideoDisplay Queue new media message %s' % message) +# #If not file take the stored one +# if not message: +# message = self.message +# # still no file name then stop as it was a normal video stopping +# if message: +# self.mediaObject.setCurrentSource(Phonon.MediaSource(message)) +# self.message = message +# self._play() def onMediaQueue(self, message): """ Set up a video to play from the serviceitem. """ log.debug(u'VideoDisplay Queue new media message %s' % message) - file = os.path.join(message[0].get_frame_path(), - message[0].get_frame_title()) + file = os.path.join(message.get_frame_path(), + message.get_frame_title()) self.mediaObject.setCurrentSource(Phonon.MediaSource(file)) + self.mediaActive = True self._play() def onMediaPlay(self): @@ -442,7 +574,6 @@ class VideoDisplay(Phonon.VideoWidget): log.debug(u'VideoDisplay _play called') self.mediaObject.play() self.setVisible(True) - self.showFullScreen() def onMediaPause(self): """ @@ -458,6 +589,7 @@ class VideoDisplay(Phonon.VideoWidget): """ log.debug(u'VideoDisplay Media stopped by user') self.message = None + self.mediaActive = False self.mediaObject.stop() self.onMediaFinish() @@ -469,7 +601,7 @@ class VideoDisplay(Phonon.VideoWidget): self.mediaObject.clearQueue() self.setVisible(False) - def mediaHide(self): + def mediaHide(self, message=u''): """ Hide the video display """ @@ -477,10 +609,90 @@ class VideoDisplay(Phonon.VideoWidget): self.hidden = True self.setVisible(False) - def mediaShow(self): + def mediaShow(self, message=''): """ Show the video disaply if it was already hidden """ if self.hidden: self.hidden = False - self._play() + if self.mediaActive: + self._play() + +class AudioPlayer(QtCore.QObject): + """ + This Class will play audio only allowing components to work witn a + soundtrack which does not take over the user interface. + """ + log.info(u'AudioPlayer Loaded') + + def __init__(self, parent): + """ + The constructor for the display form. + + ``parent`` + The parent widget. + + ``screens`` + The list of screens. + """ + log.debug(u'AudioPlayer Initialisation started') + QtCore.QObject.__init__(self) + self.parent = parent + self.message = None + self.mediaObject = Phonon.MediaObject() + self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory) + Phonon.createPath(self.mediaObject, self.audioObject) + + def setup(self): + """ + Sets up the Audio Player for use + """ + log.debug(u'AudioPlayer Setup') + + def close(self): + """ + Shutting down so clean up connections + """ + self.onMediaStop() + for pth in self.mediaObject.outputPaths(): + disconnected = pth.disconnect() + + def onMediaQueue(self, message): + """ + Set up a video to play from the serviceitem. + """ + log.debug(u'AudioPlayer Queue new media message %s' % message) + file = os.path.join(message[0].get_frame_path(), + message[0].get_frame_title()) + self.mediaObject.setCurrentSource(Phonon.MediaSource(file)) + self.onMediaPlay() + + def onMediaPlay(self): + """ + We want to play the play so start it + """ + log.debug(u'AudioPlayer _play called') + self.mediaObject.play() + + def onMediaPause(self): + """ + Pause the Audio + """ + log.debug(u'AudioPlayer Media paused by user') + self.mediaObject.pause() + + def onMediaStop(self): + """ + Stop the Audio and clean up + """ + log.debug(u'AudioPlayer Media stopped by user') + self.message = None + self.mediaObject.stop() + self.onMediaFinish() + + def onMediaFinish(self): + """ + Clean up the Object queue + """ + log.debug(u'AudioPlayer Reached end of media playlist') + self.mediaObject.clearQueue() diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 0f21b7307..f4d573d20 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -63,7 +63,7 @@ class VersionThread(QtCore.QThread): self.parent = parent self.app_version = app_version self.version_splitter = re.compile( - r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))') + r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))?') def run(self): """ @@ -79,14 +79,14 @@ class VersionThread(QtCore.QThread): remote_version[u'major'] = int(match.group(1)) remote_version[u'minor'] = int(match.group(2)) remote_version[u'release'] = int(match.group(3)) - if len(match.groups()) > 3: + if len(match.groups()) > 3 and match.group(4): remote_version[u'revision'] = int(match.group(4)) match = self.version_splitter.match(self.app_version[u'full']) if match: local_version[u'major'] = int(match.group(1)) local_version[u'minor'] = int(match.group(2)) local_version[u'release'] = int(match.group(3)) - if len(match.groups()) > 3: + if len(match.groups()) > 3 and match.group(4): local_version[u'revision'] = int(match.group(4)) if remote_version[u'major'] > local_version[u'major'] or \ remote_version[u'minor'] > local_version[u'minor'] or \ @@ -845,7 +845,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): recentFileCount = QtCore.QSettings().value( self.generalSettingsSection + u'/max recent files', QtCore.QVariant(4)).toInt()[0] - if filename and filename not in self.recentFiles: + if filename: + position = self.recentFiles.indexOf(filename) + if position != -1: + self.recentFiles.removeAt(position) self.recentFiles.insert(0, QtCore.QString(filename)) while self.recentFiles.count() > recentFileCount: self.recentFiles.removeLast() diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 540be0ff4..837415921 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -33,7 +33,7 @@ log = logging.getLogger(__name__) from PyQt4 import QtCore, QtGui from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \ - Receiver, build_icon, ItemCapabilities, SettingsManager, translate + Receiver, build_icon, ItemCapabilities, SettingsManager, translate, ThemeLevel from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm from openlp.core.utils import AppLocation @@ -134,15 +134,13 @@ class ServiceManager(QtGui.QWidget): self.ThemeLabel = QtGui.QLabel(translate('ServiceManager', 'Theme:'), self) self.ThemeLabel.setMargin(3) - self.Toolbar.addWidget(self.ThemeLabel) + self.Toolbar.addToolbarWidget(u'ThemeLabel', self.ThemeLabel) self.ThemeComboBox = QtGui.QComboBox(self.Toolbar) self.ThemeComboBox.setToolTip(translate('ServiceManager', 'Select a theme for the service')) self.ThemeComboBox.setSizeAdjustPolicy( QtGui.QComboBox.AdjustToContents) - self.ThemeWidget = QtGui.QWidgetAction(self.Toolbar) - self.ThemeWidget.setDefaultWidget(self.ThemeComboBox) - self.Toolbar.addAction(self.ThemeWidget) + self.Toolbar.addToolbarWidget(u'ThemeWidget', self.ThemeComboBox) self.Layout.addWidget(self.Toolbar) # Create the service manager list self.ServiceManagerList = ServiceManagerList(self) @@ -214,6 +212,8 @@ class ServiceManager(QtGui.QWidget): QtCore.SIGNAL(u'servicemanager_list_request'), self.listRequest) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'config_updated'), self.regenerateServiceItems) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'theme_update_global'), self.themeChange) # Last little bits of setting up self.service_theme = unicode(QtCore.QSettings().value( self.parent.serviceSettingsSection + u'/service theme', @@ -625,13 +625,13 @@ class ServiceManager(QtGui.QWidget): def onLoadService(self, lastService=False): if lastService: - filename = SettingsManager.get_last_dir( - self.parent.serviceSettingsSection) + filename = self.parent.recentFiles[0] else: filename = QtGui.QFileDialog.getOpenFileName( self, translate('ServiceManager', 'Open Service'), SettingsManager.get_last_dir( self.parent.serviceSettingsSection), u'Services (*.osz)') + filename = QtCore.QDir.toNativeSeparators(filename) self.loadService(filename) def loadService(self, filename=None): @@ -756,6 +756,18 @@ class ServiceManager(QtGui.QWidget): QtCore.QVariant(self.service_theme)) self.regenerateServiceItems() + def themeChange(self): + """ + The theme may have changed in the settings dialog so make + sure the theme combo box is in the correct state. + """ + if self.parent.RenderManager.theme_level == ThemeLevel.Global: + self.Toolbar.actions[u'ThemeLabel'].setVisible(False) + self.Toolbar.actions[u'ThemeWidget'].setVisible(False) + else: + self.Toolbar.actions[u'ThemeLabel'].setVisible(True) + self.Toolbar.actions[u'ThemeWidget'].setVisible(True) + def regenerateServiceItems(self): """ Rebuild the service list as things have changed and a diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index c2ec38069..5a34d09f8 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -105,9 +105,9 @@ class SlideController(QtGui.QWidget): self.isLive = isLive self.parent = parent self.mainDisplay = self.parent.displayManager.mainDisplay + self.displayManager = self.parent.displayManager self.loopList = [ u'Start Loop', - u'Stop Loop', u'Loop Separator', u'Image SpinBox' ] @@ -196,18 +196,25 @@ class SlideController(QtGui.QWidget): self.onSlideSelectedLast) if self.isLive: self.Toolbar.addToolbarSeparator(u'Close Separator') - self.blankButton = self.Toolbar.addToolbarButton( - u'Blank Screen', u':/slides/slide_blank.png', - translate('SlideController', 'Blank Screen'), - self.onBlankDisplay, True) - self.themeButton = self.Toolbar.addToolbarButton( - u'Display Theme', u':/slides/slide_theme.png', - translate('SlideController', 'Theme Screen'), - self.onThemeDisplay, True) - self.hideButton = self.Toolbar.addToolbarButton( - u'Hide screen', u':/slides/slide_desktop.png', - translate('SlideController', 'Hide Screen'), - self.onHideDisplay, True) + self.HideMenu = QtGui.QToolButton(self.Toolbar) + self.HideMenu.setText(translate('SlideController', 'Hide')) + self.HideMenu.setPopupMode(QtGui.QToolButton.MenuButtonPopup) + self.Toolbar.addToolbarWidget(u'Hide Menu', self.HideMenu) + self.HideMenu.setMenu(QtGui.QMenu( + translate('SlideController', 'Hide'), self.Toolbar)) + self.BlankScreen = QtGui.QAction(QtGui.QIcon( u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu) + self.BlankScreen.setCheckable(True) + QtCore.QObject.connect(self.BlankScreen, QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay) + self.ThemeScreen = QtGui.QAction(QtGui.QIcon(u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu) + self.ThemeScreen.setCheckable(True) + QtCore.QObject.connect(self.ThemeScreen, QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay) + self.DesktopScreen = QtGui.QAction(QtGui.QIcon(u':/slides/slide_desktop.png'), u'Show Desktop', self.HideMenu) + self.DesktopScreen.setCheckable(True) + QtCore.QObject.connect(self.DesktopScreen, QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay) + self.HideMenu.setDefaultAction(self.BlankScreen) + self.HideMenu.menu().addAction(self.BlankScreen) + self.HideMenu.menu().addAction(self.ThemeScreen) + self.HideMenu.menu().addAction(self.DesktopScreen) if not self.isLive: self.Toolbar.addToolbarSeparator(u'Close Separator') self.Toolbar.addToolbarButton( @@ -252,19 +259,6 @@ class SlideController(QtGui.QWidget): u'Media Stop', u':/slides/media_playback_stop.png', translate('SlideController', 'Start playing media'), self.onMediaStop) - if self.isLive: - self.blankButton = self.Mediabar.addToolbarButton( - u'Blank Screen', u':/slides/slide_blank.png', - translate('SlideController', 'Blank Screen'), - self.onBlankDisplay, True) - self.themeButton = self.Mediabar.addToolbarButton( - u'Display Theme', u':/slides/slide_theme.png', - translate('SlideController', 'Theme Screen'), - self.onThemeDisplay, True) - self.hideButton = self.Mediabar.addToolbarButton( - u'Hide screen', u':/slides/slide_desktop.png', - translate('SlideController', 'Hide Screen'), - self.onHideDisplay, True) if not self.isLive: self.seekSlider = Phonon.SeekSlider() self.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24)) @@ -340,6 +334,7 @@ class SlideController(QtGui.QWidget): self.receiveSpinDelay) if isLive: self.Toolbar.makeWidgetsInvisible(self.loopList) + self.Toolbar.actions[u'Stop Loop'].setVisible(False) else: self.Toolbar.makeWidgetsInvisible(self.songEditList) self.Mediabar.setVisible(False) @@ -436,8 +431,8 @@ class SlideController(QtGui.QWidget): self.Mediabar.setVisible(False) self.Toolbar.makeWidgetsInvisible([u'Song Menu']) self.Toolbar.makeWidgetsInvisible(self.loopList) + self.Toolbar.actions[u'Stop Loop'].setVisible(False) if item.is_text(): - self.Toolbar.makeWidgetsInvisible(self.loopList) if QtCore.QSettings().value( self.parent.songsSettingsSection + u'/show songbar', QtCore.QVariant(True)).toBool() and len(self.slideList) > 0: @@ -519,21 +514,21 @@ class SlideController(QtGui.QWidget): """ log.debug(u'processManagerItem') self.onStopLoop() - #If old item was a command tell it to stop + #If old item was a command tell it to stop if self.serviceItem: if self.serviceItem.is_command(): - Receiver.send_message(u'%s_stop' % + Receiver.send_message(u'%s_stop' % self.serviceItem.name.lower(), [serviceItem, self.isLive]) if self.serviceItem.is_media(): self.onMediaStop() if serviceItem.is_media(): self.onMediaStart(serviceItem) - if self.isLive: - blanked = self.blankButton.isChecked() - else: - blanked = False +# if self.isLive: +# blanked = self.blankButton.isChecked() +# else: +# blanked = False Receiver.send_message(u'%s_start' % serviceItem.name.lower(), - [serviceItem, self.isLive, blanked, slideno]) + [serviceItem, self.isLive, True, slideno]) self.slideList = {} width = self.parent.ControlSplitter.sizes()[self.split] #Set pointing cursor when we have somthing to point at @@ -661,7 +656,7 @@ class SlideController(QtGui.QWidget): """ log.debug(u'mainDisplaySetBackground') if not self.mainDisplay.primary: - self.blankButton.setChecked(True) + self.onBlankDisplay(True) def onSlideBlank(self): """ @@ -679,55 +674,55 @@ class SlideController(QtGui.QWidget): """ Handle the blank screen button actions """ - log.debug(u'onBlankDisplay %d' % checked) - self.hideButton.setChecked(False) - self.themeButton.setChecked(False) - self.canDisplay = not checked + log.debug(u'onBlankDisplay %s' % checked) + self.HideMenu.setDefaultAction(self.BlankScreen) + self.BlankScreen.setChecked(checked) + self.ThemeScreen.setChecked(False) + self.DesktopScreen.setChecked(False) QtCore.QSettings().setValue( self.parent.generalSettingsSection + u'/screen blank', QtCore.QVariant(checked)) if checked: Receiver.send_message(u'maindisplay_hide', HideMode.Blank) - self.blankPlugin(True) else: Receiver.send_message(u'maindisplay_show') - self.blankPlugin(False) + self.blankPlugin(checked) def onThemeDisplay(self, checked): """ Handle the Theme screen button """ - log.debug(u'onThemeDisplay %d' % checked) - self.blankButton.setChecked(False) - self.hideButton.setChecked(False) - self.canDisplay = False + log.debug(u'onThemeDisplay %s' % checked) + self.HideMenu.setDefaultAction(self.ThemeScreen) + self.BlankScreen.setChecked(False) + self.ThemeScreen.setChecked(checked) + self.DesktopScreen.setChecked(False) if checked: Receiver.send_message(u'maindisplay_hide', HideMode.Theme) - self.blankPlugin(True) else: Receiver.send_message(u'maindisplay_show') - self.blankPlugin(False) + self.blankPlugin(checked) def onHideDisplay(self, checked): """ Handle the Hide screen button """ - log.debug(u'onHideDisplay %d' % checked) - self.blankButton.setChecked(False) - self.themeButton.setChecked(False) - self.canDisplay = False + log.debug(u'onHideDisplay %s' % checked) + self.HideMenu.setDefaultAction(self.DesktopScreen) + self.BlankScreen.setChecked(False) + self.ThemeScreen.setChecked(False) + self.DesktopScreen.setChecked(checked) if checked: Receiver.send_message(u'maindisplay_hide', HideMode.Screen) - self.hidePlugin(True) else: Receiver.send_message(u'maindisplay_show') - self.hidePlugin(False) + self.hidePlugin(checked) def blankPlugin(self, blank): """ Blank the display screen within a plugin if required. """ - log.debug(u'blankPlugin %d ', blank) + log.debug(u'blankPlugin %s ', blank) if self.serviceItem is not None: if blank: Receiver.send_message(u'%s_blank' @@ -740,8 +735,9 @@ class SlideController(QtGui.QWidget): def hidePlugin(self, hide): """ - Blank the display screen. + Tell the plugin to hide the display screen. """ + log.debug(u'hidePlugin %s ', hide) if self.serviceItem is not None: if hide: Receiver.send_message(u'%s_hide' @@ -786,7 +782,10 @@ class SlideController(QtGui.QWidget): log.log( 15, u'Slide Rendering took %4s' % (time.time() - before)) if self.isLive: - self.mainDisplay.frameView(frame, True, self.canDisplay) + if self.serviceItem.is_text(): + self.mainDisplay.frameView(frame, True) + else: + self.displayManager.displayImage(frame[u'display']) self.selectedRow = row Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix, row) @@ -889,6 +888,8 @@ class SlideController(QtGui.QWidget): if self.PreviewListWidget.rowCount() > 1: self.timer_id = self.startTimer( int(self.DelaySpinBox.value()) * 1000) + self.Toolbar.actions[u'Stop Loop'].setVisible(True) + self.Toolbar.actions[u'Start Loop'].setVisible(False) def onStopLoop(self): """ @@ -897,6 +898,8 @@ class SlideController(QtGui.QWidget): if self.timer_id != 0: self.killTimer(self.timer_id) self.timer_id = 0 + self.Toolbar.actions[u'Start Loop'].setVisible(True) + self.Toolbar.actions[u'Stop Loop'].setVisible(False) def timerEvent(self, event): """ @@ -928,8 +931,7 @@ class SlideController(QtGui.QWidget): """ log.debug(u'SlideController onMediaStart') if self.isLive: - Receiver.send_message(u'videodisplay_start', - [item, self.blankButton.isChecked()]) + Receiver.send_message(u'videodisplay_start', item) else: self.mediaObject.stop() self.mediaObject.clearQueue() diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 73fbfeb88..007a51fd6 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -150,9 +150,9 @@ class ThemesTab(SettingsTab): settings.setValue(u'global theme', QtCore.QVariant(self.global_theme)) settings.endGroup() - Receiver.send_message(u'theme_update_global', self.global_theme) self.parent.RenderManager.set_global_theme( self.global_theme, self.theme_level) + Receiver.send_message(u'theme_update_global', self.global_theme) def postSetUp(self): Receiver.send_message(u'theme_update_global', self.global_theme) diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index e556e7e7c..c0315559f 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -22,7 +22,10 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - +""" +The :mod:`languagemanager` module provides all the translation settings and +language file loading for OpenLP. +""" import logging import os @@ -42,50 +45,74 @@ class LanguageManager(object): @staticmethod def get_translator(language): + """ + Set up a translator to use in this instance of OpenLP + + ``language`` + The language to load into the translator + """ if LanguageManager.AutoLanguage: language = QtCore.QLocale.system().name() lang_Path = AppLocation.get_directory(AppLocation.AppDir) lang_Path = os.path.join(lang_Path, u'resources', u'i18n') - appTranslator = QtCore.QTranslator() - if appTranslator.load("openlp_" + language, lang_Path): - return appTranslator + app_translator = QtCore.QTranslator() + if app_translator.load("openlp_" + language, lang_Path): + return app_translator @staticmethod def find_qm_files(): + """ + Find all available language files in this OpenLP install + """ trans_dir = AppLocation.get_directory(AppLocation.AppDir) trans_dir = QtCore.QDir(os.path.join(trans_dir, u'resources', u'i18n')) - fileNames = trans_dir.entryList(QtCore.QStringList("*.qm"), + file_names = trans_dir.entryList(QtCore.QStringList("*.qm"), QtCore.QDir.Files, QtCore.QDir.Name) - for name in fileNames: - fileNames.replaceInStrings(name, trans_dir.filePath(name)) - return fileNames + for name in file_names: + file_names.replaceInStrings(name, trans_dir.filePath(name)) + return file_names @staticmethod - def language_name(qmFile): + def language_name(qm_file): + """ + Load the language name from a language file + + ``qm_file`` + The file to obtain the name from + """ translator = QtCore.QTranslator() - translator.load(qmFile) + translator.load(qm_file) return translator.translate('MainWindow', 'English') @staticmethod def get_language(): + """ + Retrieve a saved language to use from settings + """ settings = QtCore.QSettings(u'OpenLP', u'OpenLP') language = unicode(settings.value( u'general/language', QtCore.QVariant(u'[en]')).toString()) log.info(u'Language file: \'%s\' Loaded from conf file' % language) - regEx = QtCore.QRegExp("^\[(.*)\]") - if regEx.exactMatch(language): + reg_ex = QtCore.QRegExp("^\[(.*)\]") + if reg_ex.exactMatch(language): LanguageManager.AutoLanguage = True - language = regEx.cap(1) + language = reg_ex.cap(1) return language @staticmethod def set_language(action): - actionName = u'%s' % action.objectName() - qmList = LanguageManager.get_qm_list() + """ + Set the language to translate OpenLP into + + ``action`` + The language menu option + """ + action_name = u'%s' % action.objectName() + qm_list = LanguageManager.get_qm_list() if LanguageManager.AutoLanguage: - language = u'[%s]' % qmList[actionName] + language = u'[%s]' % qm_list[action_name] else: - language = u'%s' % qmList[actionName] + language = u'%s' % qm_list[action_name] QtCore.QSettings().setValue( u'general/language', QtCore.QVariant(language)) log.info(u'Language file: \'%s\' written to conf file' % language) @@ -96,17 +123,23 @@ class LanguageManager(object): @staticmethod def init_qm_list(): + """ + Initialise the list of available translations + """ LanguageManager.__qmList__ = {} - qmFiles = LanguageManager.find_qm_files() - for i, qmf in enumerate(qmFiles): - regEx = QtCore.QRegExp("^.*openlp_(.*).qm") - if regEx.exactMatch(qmf): - langName = regEx.cap(1) + qm_files = LanguageManager.find_qm_files() + for i, qmf in enumerate(qm_files): + reg_ex = QtCore.QRegExp("^.*openlp_(.*).qm") + if reg_ex.exactMatch(qmf): + lang_name = reg_ex.cap(1) LanguageManager.__qmList__[u'%#2i %s' % (i+1, - LanguageManager.language_name(qmf))] = langName + LanguageManager.language_name(qmf))] = lang_name @staticmethod def get_qm_list(): + """ + Return the list of available translations + """ if LanguageManager.__qmList__ is None: LanguageManager.init_qm_list() return LanguageManager.__qmList__ diff --git a/openlp/plugins/alerts/__init__.py b/openlp/plugins/alerts/__init__.py index cb376ec38..76ca202dd 100644 --- a/openlp/plugins/alerts/__init__.py +++ b/openlp/plugins/alerts/__init__.py @@ -24,5 +24,5 @@ ############################################################################### """ The :mod:`alerts` module provides the Alerts plugin for producing impromptu -on-screen announcements during a service +on-screen announcements during a service. """ diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index 4127afaac..57388c538 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -28,7 +28,9 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, build_icon, PluginStatus, translate -from openlp.plugins.alerts.lib import AlertsManager, AlertsTab, DBManager +from openlp.core.lib.db import Manager +from openlp.plugins.alerts.lib import AlertsManager, AlertsTab +from openlp.plugins.alerts.lib.db import init_schema from openlp.plugins.alerts.forms import AlertForm log = logging.getLogger(__name__) @@ -39,13 +41,16 @@ class alertsPlugin(Plugin): def __init__(self, plugin_helpers): Plugin.__init__(self, u'Alerts', u'1.9.2', plugin_helpers) self.weight = -3 - self.icon = build_icon(u':/media/media_image.png') + self.icon = build_icon(u':/plugins/plugin_alerts.png') self.alertsmanager = AlertsManager(self) - self.manager = DBManager() + self.manager = Manager(u'alerts', init_schema) self.alertForm = AlertForm(self.manager, self) self.status = PluginStatus.Active def get_settings_tab(self): + """ + Return the settings tab for the Alerts plugin + """ self.alertsTab = AlertsTab(self) return self.alertsTab @@ -60,7 +65,7 @@ class alertsPlugin(Plugin): """ log.info(u'add tools menu') self.toolsAlertItem = QtGui.QAction(tools_menu) - AlertIcon = build_icon(u':/tools/tools_alert.png') + AlertIcon = build_icon(u':/plugins/plugin_alerts.png') self.toolsAlertItem.setIcon(AlertIcon) self.toolsAlertItem.setObjectName(u'toolsAlertItem') self.toolsAlertItem.setText( @@ -80,8 +85,8 @@ class alertsPlugin(Plugin): def finalise(self): log.info(u'Plugin Finalise') + Plugin.finalise(self) self.toolsAlertItem.setVisible(False) - #stop any events being processed def togglealertsState(self): self.alertsActive = not self.alertsActive @@ -97,4 +102,4 @@ class alertsPlugin(Plugin): about_text = translate('AlertsPlugin', 'Alerts Plugin
This plugin ' 'controls the displaying of alerts on the presentations screen') - return about_text \ No newline at end of file + return about_text diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index e783d718a..bf75f9ced 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -25,8 +25,8 @@ from PyQt4 import QtGui, QtCore -from openlp.plugins.alerts.lib.models import AlertItem from openlp.core.lib import translate +from openlp.plugins.alerts.lib.db import AlertItem from alertdialog import Ui_AlertDialog @@ -62,7 +62,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): def loadList(self): self.AlertListWidget.clear() - alerts = self.manager.get_all_alerts() + alerts = self.manager.get_all_objects(AlertItem, AlertItem.text) for alert in alerts: item_name = QtGui.QListWidgetItem(alert.text) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(alert.id)) @@ -82,7 +82,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): item = self.AlertListWidget.currentItem() if item: item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] - self.parent.manager.delete_alert(item_id) + self.manager.delete_object(AlertItem, item_id) row = self.AlertListWidget.row(item) self.AlertListWidget.takeItem(row) self.AlertTextEdit.setText(u'') @@ -98,7 +98,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): else: alert = AlertItem() alert.text = unicode(self.AlertTextEdit.text()) - self.manager.save_alert(alert) + self.manager.save_object(alert) self.AlertTextEdit.setText(u'') self.loadList() @@ -107,9 +107,9 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): Save an alert """ if self.item_id: - alert = self.manager.get_alert(self.item_id) + alert = self.manager.get_object(AlertItem, self.item_id) alert.text = unicode(self.AlertTextEdit.text()) - self.manager.save_alert(alert) + self.manager.save_object(alert) self.item_id = None self.loadList() else: diff --git a/openlp/plugins/alerts/lib/__init__.py b/openlp/plugins/alerts/lib/__init__.py index 81a2641f6..2c22e5375 100644 --- a/openlp/plugins/alerts/lib/__init__.py +++ b/openlp/plugins/alerts/lib/__init__.py @@ -25,4 +25,3 @@ from alertsmanager import AlertsManager from alertstab import AlertsTab -from manager import DBManager diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index ecd41bbd1..aac3eccb9 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -25,12 +25,21 @@ import logging -from PyQt4 import QtCore, QtGui +from PyQt4 import QtCore from openlp.core.lib import Receiver, translate log = logging.getLogger(__name__) +HTMLCODE = u""" +

+ %s +

+""" + class AlertsManager(QtCore.QObject): """ AlertsTab is the Alerts settings tab in the settings dialog. @@ -47,28 +56,6 @@ class AlertsManager(QtCore.QObject): QtCore.SIGNAL(u'maindisplay_active'), self.generateAlert) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'alerts_text'), self.onAlertText) - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'config_screen_changed'), self.screenChanged) - - def screenChanged(self): - log.debug(u'screen changed') - self.alertTab = self.parent.alertsTab - self.screen = self.parent.maindisplay.screens.current - self.font = QtGui.QFont() - self.font.setFamily(self.alertTab.font_face) - self.font.setBold(True) - self.font.setPointSize(self.alertTab.font_size) - self.metrics = QtGui.QFontMetrics(self.font) - self.alertHeight = self.metrics.height() + 4 - if self.alertTab.location == 0: - self.alertScreenPosition = 0 - else: - self.alertScreenPosition = self.screen[u'size'].height() \ - - self.alertHeight - self.alertHeight = self.screen[u'size'].height() \ - - self.alertScreenPosition - self.parent.maindisplay.setAlertSize(self.alertScreenPosition, - self.alertHeight) def onAlertText(self, message): """ @@ -88,8 +75,6 @@ class AlertsManager(QtCore.QObject): display text """ log.debug(u'display alert called %s' % text) - if not self.screen: - self.screenChanged() self.alertList.append(text) if self.timer_id != 0: Receiver.send_message(u'maindisplay_status_text', @@ -100,37 +85,35 @@ class AlertsManager(QtCore.QObject): self.generateAlert() def generateAlert(self): + """ + Format and request the Alert and start the timer + """ log.debug(u'Generate Alert called') if len(self.alertList) == 0: return text = self.alertList.pop(0) alertTab = self.parent.alertsTab - alertframe = \ - QtGui.QPixmap(self.screen[u'size'].width(), self.alertHeight) - alertframe.fill(QtCore.Qt.transparent) - painter = QtGui.QPainter(alertframe) - painter.fillRect(alertframe.rect(), QtCore.Qt.transparent) - painter.setRenderHint(QtGui.QPainter.Antialiasing) - painter.fillRect( - QtCore.QRect( - 0, 0, alertframe.rect().width(), - alertframe.rect().height()), - QtGui.QColor(self.alertTab.bg_color)) - painter.setFont(self.font) - painter.setPen(QtGui.QColor(self.alertTab.font_color)) - x, y = (0, 2) - painter.drawText( - x, y + self.metrics.height() - self.metrics.descent() - 1, text) - painter.end() - self.parent.maindisplay.addAlertImage(alertframe) + text = HTMLCODE % (alertTab.font_color, alertTab.bg_color, + alertTab.font_face, alertTab.font_size, text) + self.parent.preview_controller.parent.displayManager.addAlert(text, + alertTab.location) # check to see if we have a timer running if self.timer_id == 0: self.timer_id = self.startTimer(int(alertTab.timeout) * 1000) def timerEvent(self, event): + """ + Time has finished so if our time then request the next Alert + if there is one and reset the timer. + ``event`` + the QT event that has been triggered. + + """ log.debug(u'timer event') + alertTab = self.parent.alertsTab if event.timerId() == self.timer_id: - self.parent.maindisplay.addAlertImage(None, True) + self.parent.preview_controller.parent.displayManager.addAlert(u'', + alertTab.location) self.killTimer(self.timer_id) self.timer_id = 0 self.generateAlert() diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index 31a9f7a3e..e03bbfabe 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -128,6 +128,7 @@ class AlertsTab(SettingsTab): self.LocationComboBox = QtGui.QComboBox(self.LocationWidget) self.LocationComboBox.addItem(QtCore.QString()) self.LocationComboBox.addItem(QtCore.QString()) + self.LocationComboBox.addItem(QtCore.QString()) self.LocationComboBox.setObjectName(u'LocationComboBox') self.LocationLayout.addWidget(self.LocationComboBox) self.LocationSpacer = QtGui.QSpacerItem(147, 20, @@ -208,9 +209,11 @@ class AlertsTab(SettingsTab): translate('AlertsPlugin.AlertsTab', 'Preview')) self.FontPreview.setText( translate('AlertsPlugin.AlertsTab', 'openlp.org')) - self.LocationComboBox.setItemText(0, + self.LocationComboBox.setItemText(0, translate('AlertsPlugin.AlertsTab', 'Top')) - self.LocationComboBox.setItemText(1, + self.LocationComboBox.setItemText(1, + translate('AlertsPlugin.AlertsTab', 'Middle')) + self.LocationComboBox.setItemText(2, translate('AlertsPlugin.AlertsTab', 'Bottom')) def onBackgroundColorButtonClicked(self): @@ -295,4 +298,4 @@ class AlertsTab(SettingsTab): font.setPointSize(self.font_size) self.FontPreview.setFont(font) self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' % - (self.bg_color, self.font_color)) \ No newline at end of file + (self.bg_color, self.font_color)) diff --git a/openlp/plugins/alerts/lib/classes.py b/openlp/plugins/alerts/lib/classes.py deleted file mode 100644 index e58f80829..000000000 --- a/openlp/plugins/alerts/lib/classes.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from openlp.core.lib import BaseModel - -class AlertItem(BaseModel): - """ - Custom Slide model - """ - pass diff --git a/openlp/plugins/alerts/lib/models.py b/openlp/plugins/alerts/lib/db.py similarity index 72% rename from openlp/plugins/alerts/lib/models.py rename to openlp/plugins/alerts/lib/db.py index f222345f1..5e4b1a99a 100644 --- a/openlp/plugins/alerts/lib/models.py +++ b/openlp/plugins/alerts/lib/db.py @@ -22,18 +22,36 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`db` module provides the database and schema that is the backend for +the Alerts plugin +""" -from sqlalchemy import create_engine -from sqlalchemy.orm import scoped_session, sessionmaker, mapper +from sqlalchemy import Column, Table, types +from sqlalchemy.orm import mapper -from openlp.plugins.alerts.lib.meta import metadata -from openlp.plugins.alerts.lib.tables import * -from openlp.plugins.alerts.lib.classes import * +from openlp.core.lib.db import BaseModel, init_db + +class AlertItem(BaseModel): + """ + AlertItem model + """ + pass + +def init_schema(url): + """ + Setup the alerts database connection and initialise the database schema + + ``url`` + The database to setup + """ + session, metadata = init_db(url) + + alerts_table = Table(u'alerts', metadata, + Column(u'id', types.Integer(), primary_key=True), + Column(u'text', types.UnicodeText, nullable=False)) -def init_models(url): - engine = create_engine(url) - metadata.bind = engine - session = scoped_session(sessionmaker(autoflush=True, autocommit=False, - bind=engine)) mapper(AlertItem, alerts_table) + + metadata.create_all(checkfirst=True) return session diff --git a/openlp/plugins/alerts/lib/manager.py b/openlp/plugins/alerts/lib/manager.py deleted file mode 100644 index 088f0cbae..000000000 --- a/openlp/plugins/alerts/lib/manager.py +++ /dev/null @@ -1,114 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -import logging - -from PyQt4 import QtCore -from sqlalchemy.exceptions import InvalidRequestError - -from openlp.core.utils import AppLocation -from openlp.plugins.alerts.lib.models import init_models, metadata, AlertItem - -log = logging.getLogger(__name__) - -class DBManager(object): - """ - The Song Manager provides a central location for all database code. This - class takes care of connecting to the database and running all the queries. - """ - log.info(u'Alerts DB loaded') - - def __init__(self): - """ - Creates the connection to the database, and creates the tables if they - don't exist. - """ - log.debug(u'Alerts Initialising') - settings = QtCore.QSettings() - settings.beginGroup(u'alerts') - self.db_url = u'' - db_type = unicode( - settings.value(u'db type', QtCore.QVariant(u'sqlite')).toString()) - if db_type == u'sqlite': - self.db_url = u'sqlite:///%s/alerts.sqlite' % \ - AppLocation.get_section_data_path(u'alerts') - else: - self.db_url = u'%s://%s:%s@%s/%s' % (db_type, - unicode(settings.value(u'db username').toString()), - unicode(settings.value(u'db password').toString()), - unicode(settings.value(u'db hostname').toString()), - unicode(settings.value(u'db database').toString())) - settings.endGroup() - self.session = init_models(self.db_url) - metadata.create_all(checkfirst=True) - log.debug(u'Alerts Initialised') - - def get_all_alerts(self): - """ - Returns the details of a Alert Show - """ - return self.session.query(AlertItem).order_by(AlertItem.text).all() - - def save_alert(self, alert_item): - """ - Saves a Alert show to the database - """ - log.debug(u'Alert added') - try: - self.session.add(alert_item) - self.session.commit() - log.debug(u'Alert saved') - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Alert save failed') - return False - - def get_alert(self, id=None): - """ - Returns the details of a Alert - """ - if id is None: - return AlertItem() - else: - return self.session.query(AlertItem).get(id) - - def delete_alert(self, id): - """ - Delete a Alert show - """ - if id != 0: - alert_item = self.get_alert(id) - try: - self.session.delete(alert_item) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Alert deleton failed') - return False - else: - return True - diff --git a/openlp/plugins/alerts/lib/meta.py b/openlp/plugins/alerts/lib/meta.py deleted file mode 100644 index affa31969..000000000 --- a/openlp/plugins/alerts/lib/meta.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from sqlalchemy import MetaData - -__all__ = ['session', 'metadata', 'engine'] - -# SQLAlchemy database engine. Updated by model.init_model() -engine = None - -# SQLAlchemy session manager. Updated by model.init_model() -session = None - -# Global metadata. If you have multiple databases with overlapping table -# names, you'll need a metadata for each database -metadata = MetaData() diff --git a/openlp/plugins/alerts/lib/tables.py b/openlp/plugins/alerts/lib/tables.py deleted file mode 100644 index 0e707570d..000000000 --- a/openlp/plugins/alerts/lib/tables.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from sqlalchemy import Column, Table, types - -from openlp.plugins.alerts.lib.meta import metadata - -# Definition of the "alerts" table -alerts_table = Table(u'alerts', metadata, - Column(u'id', types.Integer(), primary_key=True), - Column(u'text', types.UnicodeText, nullable=False)) diff --git a/openlp/plugins/bibles/__init__.py b/openlp/plugins/bibles/__init__.py index ca5ff7508..2491c4142 100644 --- a/openlp/plugins/bibles/__init__.py +++ b/openlp/plugins/bibles/__init__.py @@ -23,6 +23,6 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`bibles' modules provides the Bible plugin to enable OpenLP to display -scripture +The :mod:`bibles' module provides the Bible plugin to enable OpenLP to display +scripture. """ diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 2398ead9c..9f4f034ab 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -38,7 +38,7 @@ class BiblePlugin(Plugin): def __init__(self, plugin_helpers): Plugin.__init__(self, u'Bibles', u'1.9.2', plugin_helpers) self.weight = -9 - self.icon = build_icon(u':/media/media_bible.png') + self.icon = build_icon(u':/plugins/plugin_bibles.png') #Register the bible Manager self.status = PluginStatus.Active self.manager = None @@ -48,14 +48,12 @@ class BiblePlugin(Plugin): if self.manager is None: self.manager = BibleManager(self) Plugin.initialise(self) - self.insert_toolbox_item() self.ImportBibleItem.setVisible(True) self.ExportBibleItem.setVisible(True) def finalise(self): log.info(u'Plugin Finalise') Plugin.finalise(self) - self.remove_toolbox_item() self.ImportBibleItem.setVisible(False) self.ExportBibleItem.setVisible(False) @@ -99,4 +97,4 @@ class BiblePlugin(Plugin): def can_delete_theme(self, theme): if self.settings_tab.bible_theme == theme: return False - return True \ No newline at end of file + return True diff --git a/openlp/plugins/bibles/forms/importwizardform.py b/openlp/plugins/bibles/forms/importwizardform.py index 12b7bbaae..da225b782 100644 --- a/openlp/plugins/bibles/forms/importwizardform.py +++ b/openlp/plugins/bibles/forms/importwizardform.py @@ -32,6 +32,7 @@ from PyQt4 import QtCore, QtGui from bibleimportwizard import Ui_BibleImportWizard from openlp.core.lib import Receiver, SettingsManager, translate +from openlp.core.lib.db import delete_database from openlp.core.utils import AppLocation from openlp.plugins.bibles.lib.manager import BibleFormat @@ -224,7 +225,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): Show the file open dialog for the OSIS file. """ self.getFileName( - translate('BiblesPlugin.ImportWizardForm', 'Open OSIS File'), + translate('BiblesPlugin.ImportWizardForm', 'Open OSIS File'), self.OSISLocationEdit) def onBooksFileButtonClicked(self): @@ -239,10 +240,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): """ Show the file open dialog for the verses CSV file. """ - self.getFileName( - translate('BiblesPlugin.ImportWizardForm', - 'Open Verses CSV File'), - self.CsvVerseLocationEdit) + self.getFileName(translate('BiblesPlugin.ImportWizardForm', + 'Open Verses CSV File'), self.CsvVerseLocationEdit) def onOpenSongBrowseButtonClicked(self): """ @@ -451,7 +450,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): self.ImportProgressLabel.setText( translate('BiblesPlugin.ImportWizardForm', 'Your Bible import failed.')) - importer.delete() + delete_database(self.bibleplugin.settingsSection, importer.file) def postImport(self): self.ImportProgressBar.setValue(self.ImportProgressBar.maximum()) diff --git a/openlp/plugins/bibles/lib/csvbible.py b/openlp/plugins/bibles/lib/csvbible.py index 3cfe9dea5..9a504f48f 100644 --- a/openlp/plugins/bibles/lib/csvbible.py +++ b/openlp/plugins/bibles/lib/csvbible.py @@ -97,11 +97,11 @@ class CSVBible(BibleDB): book_ptr = book.name self.wizard.incrementProgressBar( u'Importing %s %s' % (book.name, line[1])) - self.commit() + self.session.commit() self.create_verse(book.id, line[1], line[2], unicode(line[3], details['encoding'])) Receiver.send_message(u'openlp_process_events') - self.commit() + self.session.commit() except IOError: log.exception(u'Loading verses from file failed') success = False @@ -113,5 +113,3 @@ class CSVBible(BibleDB): return False else: return success - - diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 863f7f40a..a453d5bcc 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -23,20 +23,99 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -import os import logging import chardet import re -from sqlalchemy import or_ from PyQt4 import QtCore, QtGui +from sqlalchemy import Column, ForeignKey, or_, Table, types +from sqlalchemy.orm import class_mapper, mapper, relation +from sqlalchemy.orm.exc import UnmappedClassError from openlp.core.lib import translate -from openlp.plugins.bibles.lib.models import * +from openlp.core.lib.db import BaseModel, init_db, Manager log = logging.getLogger(__name__) -class BibleDB(QtCore.QObject): +class BibleMeta(BaseModel): + """ + Bible Meta Data + """ + pass + +class Testament(BaseModel): + """ + Bible Testaments + """ + pass + +class Book(BaseModel): + """ + Song model + """ + pass + +class Verse(BaseModel): + """ + Topic model + """ + pass + +def init_schema(url): + """ + Setup a bible database connection and initialise the database schema + + ``url`` + The database to setup + """ + session, metadata = init_db(url) + + meta_table = Table(u'metadata', metadata, + Column(u'key', types.Unicode(255), primary_key=True, index=True), + Column(u'value', types.Unicode(255)), + ) + testament_table = Table(u'testament', metadata, + Column(u'id', types.Integer, primary_key=True), + Column(u'name', types.Unicode(50)), + ) + book_table = Table(u'book', metadata, + Column(u'id', types.Integer, primary_key=True), + Column(u'testament_id', types.Integer, ForeignKey(u'testament.id')), + Column(u'name', types.Unicode(50), index=True), + Column(u'abbreviation', types.Unicode(5), index=True), + ) + verse_table = Table(u'verse', metadata, + Column(u'id', types.Integer, primary_key=True, index=True), + Column(u'book_id', types.Integer, ForeignKey(u'book.id'), index=True), + Column(u'chapter', types.Integer, index=True), + Column(u'verse', types.Integer, index=True), + Column(u'text', types.UnicodeText, index=True), + ) + + try: + class_mapper(BibleMeta) + except UnmappedClassError: + mapper(BibleMeta, meta_table) + try: + class_mapper(Testament) + except UnmappedClassError: + mapper(Testament, testament_table, + properties={'books': relation(Book, backref='testament')}) + try: + class_mapper(Book) + except UnmappedClassError: + mapper(Book, book_table, + properties={'verses': relation(Verse, backref='book')}) + try: + class_mapper(Verse) + except UnmappedClassError: + mapper(Verse, verse_table) + + metadata.create_all(checkfirst=True) + return session + + +class BibleDB(QtCore.QObject, Manager): """ This class represents a database-bound Bible. It is used as a base class for all the custom importers, so that the can implement their own import @@ -73,26 +152,10 @@ class BibleDB(QtCore.QObject): self.file = self.clean_filename(self.name) if u'file' in kwargs: self.file = kwargs[u'file'] - self.db_file = os.path.join(kwargs[u'path'], self.file) - log.debug(u'Load bible %s on path %s', self.file, self.db_file) - settings = QtCore.QSettings() - settings.beginGroup(u'bibles') - db_type = unicode( - settings.value(u'db type', QtCore.QVariant(u'sqlite')).toString()) - db_url = u'' - if db_type == u'sqlite': - db_url = u'sqlite:///' + self.db_file - else: - db_url = u'%s://%s:%s@%s/%s' % (db_type, - unicode(settings.value(u'db username').toString()), - unicode(settings.value(u'db password').toString()), - unicode(settings.value(u'db hostname').toString()), - unicode(settings.value(u'db database').toString())) - settings.endGroup() - self.session = init_models(db_url) - metadata.create_all(checkfirst=True) + Manager.__init__(self, u'bibles', init_schema, self.file) if u'file' in kwargs: self.get_name() + self.wizard = None def stop_import(self): """ @@ -105,7 +168,7 @@ class BibleDB(QtCore.QObject): """ Returns the version name of the Bible. """ - version_name = self.get_meta(u'Version') + version_name = self.get_object(BibleMeta, u'Version') if version_name: self.name = version_name.value else: @@ -125,16 +188,6 @@ class BibleDB(QtCore.QObject): old_filename = re.sub(r'[^\w]+', u'_', old_filename).strip(u'_') return old_filename + u'.sqlite' - def delete(self): - """ - Remove the Bible database file. Used when a Bible import fails. - """ - try: - os.remove(self.db_file) - return True - except OSError: - return False - def register(self, wizard): """ This method basically just initialialises the database. It is called @@ -146,36 +199,11 @@ class BibleDB(QtCore.QObject): The actual Qt wizard form. """ self.wizard = wizard - self.create_tables() - return self.name - - def commit(self): - """ - Perform a database commit. - """ - log.debug('Committing...') - self.session.commit() - - def create_tables(self): - """ - Create some initial metadata. - """ - log.debug(u'createTables') self.create_meta(u'dbversion', u'2') - self.create_testament(u'Old Testament') - self.create_testament(u'New Testament') - self.create_testament(u'Apocrypha') - - def create_testament(self, testament): - """ - Add a testament to the database. - - ``testament`` - The testament name. - """ - log.debug(u'BibleDB.create_testament("%s")', testament) - self.session.add(Testament.populate(name=testament)) - self.commit() + self.save_object(Testament.populate(name=u'Old Testament')) + self.save_object(Testament.populate(name=u'New Testament')) + self.save_object(Testament.populate(name=u'Apocrypha')) + return self.name def create_book(self, name, abbrev, testament=1): """ @@ -193,8 +221,7 @@ class BibleDB(QtCore.QObject): log.debug(u'create_book %s,%s', name, abbrev) book = Book.populate(name=name, abbreviation=abbrev, testament_id=testament) - self.session.add(book) - self.commit() + self.save_object(book) return book def create_chapter(self, book_id, chapter, textlist): @@ -221,7 +248,7 @@ class BibleDB(QtCore.QObject): text = verse_text ) self.session.add(verse) - self.commit() + self.session.commit() def create_verse(self, book_id, chapter, verse, text): """ @@ -252,31 +279,31 @@ class BibleDB(QtCore.QObject): return verse def create_meta(self, key, value): - log.debug(u'save_meta %s/%s', key, value) - self.session.add(BibleMeta.populate(key=key, value=value)) - self.commit() + """ + Utility method to save BibleMeta objects in a Bible database - def get_books(self): - log.debug(u'BibleDB.get_books()') - return self.session.query(Book).order_by(Book.id).all() + ``key`` + The key for this instance + + ``value`` + The value for this instance + """ + log.debug(u'save_meta %s/%s', key, value) + self.save_object(BibleMeta.populate(key=key, value=value)) def get_book(self, book): - log.debug(u'BibleDb.get_book("%s")', book) - db_book = self.session.query(Book)\ - .filter(Book.name.like(book + u'%'))\ - .first() - if db_book is None: - db_book = self.session.query(Book)\ - .filter(Book.abbreviation.like(book + u'%'))\ - .first() - return db_book + """ + Return a book object from the database - def get_chapter(self, id, chapter): - log.debug(u'BibleDB.get_chapter("%s", %s)', id, chapter) - return self.session.query(Verse)\ - .filter_by(chapter=chapter)\ - .filter_by(book_id=id)\ - .first() + ``book`` + The name of the book to return + """ + log.debug(u'BibleDb.get_book("%s")', book) + db_book = self.get_object_filtered(Book, Book.name.like(book + u'%')) + if db_book is None: + db_book = self.get_object_filtered(Book, + Book.abbreviation.like(book + u'%')) + return db_book def get_verses(self, reference_list): """ @@ -351,6 +378,12 @@ class BibleDB(QtCore.QObject): return verses def get_chapter_count(self, book): + """ + Return the number of chapters in a book + + ``book`` + The book to get the chapter count for + """ log.debug(u'BibleDB.get_chapter_count("%s")', book) count = self.session.query(Verse.chapter).join(Book)\ .filter(Book.name==book)\ @@ -361,6 +394,15 @@ class BibleDB(QtCore.QObject): return count def get_verse_count(self, book, chapter): + """ + Return the number of verses in a chapter + + ``book`` + The book containing the chapter + + ``chapter`` + The chapter to get the verse count for + """ log.debug(u'BibleDB.get_verse_count("%s", %s)', book, chapter) count = self.session.query(Verse).join(Book)\ .filter(Book.name==book)\ @@ -371,20 +413,10 @@ class BibleDB(QtCore.QObject): else: return count - def get_meta(self, key): - log.debug(u'get meta %s', key) - return self.session.query(BibleMeta).get(key) - - def delete_meta(self, metakey): - biblemeta = self.get_meta(metakey) - try: - self.session.delete(biblemeta) - self.commit() - return True - except: - return False - def dump_bible(self): + """ + Utility debugging method to dump the contents of a bible + """ log.debug(u'.........Dumping Bible Database') log.debug('...............................Books ') books = self.session.query(Book).all() diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 073b03118..8bf7ac63e 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -35,8 +35,7 @@ from openlp.core.lib import Receiver from openlp.core.utils import AppLocation from openlp.plugins.bibles.lib.common import BibleCommon, SearchResults, \ unescape -from openlp.plugins.bibles.lib.db import BibleDB -from openlp.plugins.bibles.lib.models import Book +from openlp.plugins.bibles.lib.db import BibleDB, Book log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 7ef18a604..39f3c255b 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -29,12 +29,12 @@ from PyQt4 import QtCore from openlp.core.lib import SettingsManager from openlp.core.utils import AppLocation +from openlp.plugins.bibles.lib.db import BibleDB, Book, BibleMeta from common import parse_reference from opensong import OpenSongBible from osis import OSISBible from csvbible import CSVBible -from db import BibleDB from http import HTTPBible log = logging.getLogger(__name__) @@ -137,11 +137,13 @@ class BibleManager(object): log.debug(u'Bible Name: "%s"', name) self.db_cache[name] = bible # look to see if lazy load bible exists and get create getter. - source = self.db_cache[name].get_meta(u'download source') + source = self.db_cache[name].get_object(BibleMeta, + u'download source') if source: - download_name = \ - self.db_cache[name].get_meta(u'download name').value - meta_proxy = self.db_cache[name].get_meta(u'proxy url') + download_name = self.db_cache[name].get_object(BibleMeta, + u'download name').value + meta_proxy = self.db_cache[name].get_object(BibleMeta, + u'proxy url') web_bible = HTTPBible(self.parent, path=self.path, file=filename, download_source=source.value, download_name=download_name) @@ -196,7 +198,7 @@ class BibleManager(object): u'name': book.name, u'chapters': self.db_cache[bible].get_chapter_count(book.name) } - for book in self.db_cache[bible].get_books() + for book in self.db_cache[bible].get_all_objects(Book, Book.id) ] def get_chapter_count(self, bible, book): @@ -249,7 +251,7 @@ class BibleManager(object): Returns the meta data for a given key """ log.debug(u'get_meta %s,%s', bible, key) - return self.db_cache[bible].get_meta(key) + return self.db_cache[bible].get_object(BibleMeta, key) def exists(self, name): """ diff --git a/openlp/plugins/bibles/lib/models.py b/openlp/plugins/bibles/lib/models.py deleted file mode 100644 index d970bce08..000000000 --- a/openlp/plugins/bibles/lib/models.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from sqlalchemy import Column, Table, MetaData, ForeignKey, types, \ - create_engine -from sqlalchemy.orm import mapper, relation, sessionmaker, scoped_session - -from openlp.core.lib import BaseModel - - -class BibleMeta(BaseModel): - """ - Bible Meta Data - """ - pass - - -class Testament(BaseModel): - """ - Bible Testaments - """ - pass - - -class Book(BaseModel): - """ - Song model - """ - pass - - -class Verse(BaseModel): - """ - Topic model - """ - pass - -def init_models(db_url): - engine = create_engine(db_url) - metadata.bind = engine - session = scoped_session(sessionmaker(autoflush=True, autocommit=False, - bind=engine)) - return session - -metadata = MetaData() -meta_table = Table(u'metadata', metadata, - Column(u'key', types.Unicode(255), primary_key=True, index=True), - Column(u'value', types.Unicode(255)), -) -testament_table = Table(u'testament', metadata, - Column(u'id', types.Integer, primary_key=True), - Column(u'name', types.Unicode(50)), -) -book_table = Table(u'book', metadata, - Column(u'id', types.Integer, primary_key=True), - Column(u'testament_id', types.Integer, ForeignKey(u'testament.id')), - Column(u'name', types.Unicode(50), index=True), - Column(u'abbreviation', types.Unicode(5), index=True), -) -verse_table = Table(u'verse', metadata, - Column(u'id', types.Integer, primary_key=True, index=True), - Column(u'book_id', types.Integer, ForeignKey(u'book.id'), index=True), - Column(u'chapter', types.Integer, index=True), - Column(u'verse', types.Integer, index=True), - Column(u'text', types.UnicodeText, index=True), -) -mapper(BibleMeta, meta_table) -mapper(Testament, testament_table, - properties={'books': relation(Book, backref='testament')}) -mapper(Book, book_table, - properties={'verses': relation(Verse, backref='book')}) -mapper(Verse, verse_table) diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index d4458815f..7c7beb7f6 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -90,7 +90,7 @@ class OpenSongBible(BibleDB): QtCore.QString('%s %s %s' % ( translate('BiblesPlugin.Opensong', 'Importing'), \ db_book.name, chapter.attrib[u'n']))) - self.commit() + self.session.commit() except IOError: log.exception(u'Loading bible from OpenSong file failed') success = False diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py index 844d31052..8437fd843 100644 --- a/openlp/plugins/bibles/lib/osis.py +++ b/openlp/plugins/bibles/lib/osis.py @@ -140,7 +140,7 @@ class OSISBible(BibleDB): self.wizard.ImportProgressBar.setMaximum(260) if last_chapter != chapter: if last_chapter != 0: - self.commit() + self.session.commit() self.wizard.incrementProgressBar( u'Importing %s %s...' % \ (self.books[match.group(1)][0], chapter)) @@ -169,7 +169,7 @@ class OSISBible(BibleDB): verse_text = self.spaces_regex.sub(u' ', verse_text) self.create_verse(db_book.id, chapter, verse, verse_text) Receiver.send_message(u'openlp_process_events') - self.commit() + self.session.commit() self.wizard.incrementProgressBar(u'Finishing import...') if match_count == 0: success = False diff --git a/openlp/plugins/custom/__init__.py b/openlp/plugins/custom/__init__.py index 1a348a0df..2b9a101f1 100644 --- a/openlp/plugins/custom/__init__.py +++ b/openlp/plugins/custom/__init__.py @@ -22,3 +22,8 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`custom` module provides the Custom plugin which allows custom, +themed, text based items to be displayed without having to misuse another item +type. +""" diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index d4f18058e..60a0b312c 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -26,8 +26,11 @@ import logging from forms import EditCustomForm + from openlp.core.lib import Plugin, build_icon, PluginStatus, translate -from openlp.plugins.custom.lib import CustomManager, CustomMediaItem, CustomTab +from openlp.core.lib.db import Manager +from openlp.plugins.custom.lib import CustomMediaItem, CustomTab +from openlp.plugins.custom.lib.db import CustomSlide, init_schema log = logging.getLogger(__name__) @@ -45,9 +48,9 @@ class CustomPlugin(Plugin): def __init__(self, plugin_helpers): Plugin.__init__(self, u'Custom', u'1.9.2', plugin_helpers) self.weight = -5 - self.custommanager = CustomManager() + self.custommanager = Manager(u'custom', init_schema) self.edit_custom_form = EditCustomForm(self.custommanager) - self.icon = build_icon(u':/media/media_custom.png') + self.icon = build_icon(u':/plugins/plugin_custom.png') self.status = PluginStatus.Active def get_settings_tab(self): @@ -57,15 +60,6 @@ class CustomPlugin(Plugin): # Create the CustomManagerItem object return CustomMediaItem(self, self.icon, self.name) - def initialise(self): - log.info(u'Plugin Initialising') - Plugin.initialise(self) - self.insert_toolbox_item() - - def finalise(self): - log.info(u'Plugin Finalise') - self.remove_toolbox_item() - def about(self): about_text = translate('CustomPlugin', 'Custom Plugin
This plugin ' @@ -75,6 +69,7 @@ class CustomPlugin(Plugin): return about_text def can_delete_theme(self, theme): - if len(self.custommanager.get_customs_for_theme(theme)) == 0: + if not self.custommanager.get_all_objects_filtered(CustomSlide, + CustomSlide.theme_name == theme): return True - return False \ No newline at end of file + return False diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index d387e7543..a266c44c5 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -29,7 +29,7 @@ from PyQt4 import QtCore, QtGui from editcustomdialog import Ui_customEditDialog from openlp.core.lib import SongXMLBuilder, SongXMLParser, Receiver, translate -from openlp.plugins.custom.lib.models import CustomSlide +from openlp.plugins.custom.lib.db import CustomSlide log = logging.getLogger(__name__) @@ -116,7 +116,7 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): self.customSlide = CustomSlide() self.initialise() if id != 0: - self.customSlide = self.custommanager.get_custom(id) + self.customSlide = self.custommanager.get_object(CustomSlide, id) self.TitleEdit.setText(self.customSlide.title) self.CreditEdit.setText(self.customSlide.credits) songXML = SongXMLParser(self.customSlide.text) @@ -166,8 +166,7 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): u'utf-8') self.customSlide.theme_name = unicode(self.ThemeComboBox.currentText(), u'utf-8') - self.custommanager.save_slide(self.customSlide) - return True + return self.custommanager.save_object(self.customSlide) def onUpButtonPressed(self): selectedRow = self.VerseListView.currentRow() diff --git a/openlp/plugins/custom/lib/__init__.py b/openlp/plugins/custom/lib/__init__.py index e62669ad3..0d9de3173 100644 --- a/openlp/plugins/custom/lib/__init__.py +++ b/openlp/plugins/custom/lib/__init__.py @@ -23,6 +23,5 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -from manager import CustomManager from mediaitem import CustomMediaItem from customtab import CustomTab diff --git a/openlp/plugins/custom/lib/classes.py b/openlp/plugins/custom/lib/classes.py deleted file mode 100644 index dc6c5c1b8..000000000 --- a/openlp/plugins/custom/lib/classes.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from openlp.core.lib import BaseModel - -class CustomSlide(BaseModel): - """ - Custom Slide model - """ - pass diff --git a/openlp/plugins/custom/lib/models.py b/openlp/plugins/custom/lib/db.py similarity index 67% rename from openlp/plugins/custom/lib/models.py rename to openlp/plugins/custom/lib/db.py index 3bd2886bd..39d935d9a 100644 --- a/openlp/plugins/custom/lib/models.py +++ b/openlp/plugins/custom/lib/db.py @@ -22,18 +22,40 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`db` module provides the database and schema that is the backend for +the Custom plugin +""" -from sqlalchemy import create_engine -from sqlalchemy.orm import scoped_session, sessionmaker, mapper +from sqlalchemy import Column, Table, types +from sqlalchemy.orm import mapper -from openlp.plugins.custom.lib.meta import metadata -from openlp.plugins.custom.lib.tables import * -from openlp.plugins.custom.lib.classes import * +from openlp.core.lib.db import BaseModel, init_db + +class CustomSlide(BaseModel): + """ + CustomSlide model + """ + pass + +def init_schema(url): + """ + Setup the custom database connection and initialise the database schema + + ``url`` + The database to setup + """ + session, metadata = init_db(url) + + custom_slide_table = Table(u'custom_slide', metadata, + Column(u'id', types.Integer(), primary_key=True), + Column(u'title', types.Unicode(255), nullable=False), + Column(u'text', types.UnicodeText, nullable=False), + Column(u'credits', types.UnicodeText), + Column(u'theme_name', types.Unicode(128)) + ) -def init_models(url): - engine = create_engine(url) - metadata.bind = engine - session = scoped_session(sessionmaker(autoflush=True, autocommit=False, - bind=engine)) mapper(CustomSlide, custom_slide_table) + + metadata.create_all(checkfirst=True) return session diff --git a/openlp/plugins/custom/lib/manager.py b/openlp/plugins/custom/lib/manager.py deleted file mode 100644 index 793cd8699..000000000 --- a/openlp/plugins/custom/lib/manager.py +++ /dev/null @@ -1,117 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -import logging - -from PyQt4 import QtCore -from sqlalchemy.exceptions import InvalidRequestError - -from openlp.core.utils import AppLocation -from openlp.plugins.custom.lib.models import init_models, metadata, CustomSlide - -log = logging.getLogger(__name__) - -class CustomManager(object): - """ - The Song Manager provides a central location for all database code. This - class takes care of connecting to the database and running all the queries. - """ - log.info(u'Custom manager loaded') - - def __init__(self): - """ - Creates the connection to the database, and creates the tables if they - don't exist. - """ - log.debug(u'Custom Initialising') - settings = QtCore.QSettings() - settings.beginGroup(u'custom') - self.db_url = u'' - db_type = unicode( - settings.value(u'db type', QtCore.QVariant(u'sqlite')).toString()) - if db_type == u'sqlite': - self.db_url = u'sqlite:///%s/custom.sqlite' % \ - AppLocation.get_section_data_path(u'custom') - else: - self.db_url = u'%s://%s:%s@%s/%s' % (db_type, - unicode(settings.value(u'db username').toString()), - unicode(settings.value(u'db password').toString()), - unicode(settings.value(u'db hostname').toString()), - unicode(settings.value(u'db database').toString())) - self.session = init_models(self.db_url) - metadata.create_all(checkfirst=True) - settings.endGroup() - log.debug(u'Custom Initialised') - - def get_all_slides(self): - """ - Returns the details of a Custom Slide Show - """ - return self.session.query(CustomSlide).order_by(CustomSlide.title).all() - - def save_slide(self, customslide): - """ - Saves a Custom slide show to the database - """ - log.debug(u'Custom Slide added') - try: - self.session.add(customslide) - self.session.commit() - log.debug(u'Custom Slide saved') - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Custom Slide save failed') - return False - - def get_custom(self, id=None): - """ - Returns the details of a Custom Slide - """ - if id is None: - return CustomSlide() - else: - return self.session.query(CustomSlide).get(id) - - def delete_custom(self, id): - """ - Delete a Custom slide show - """ - if id != 0: - customslide = self.get_custom(id) - try: - self.session.delete(customslide) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Custom Slide deleton failed') - return False - else: - return True - - def get_customs_for_theme(self, theme): - return self.session.query( - CustomSlide).filter(CustomSlide.theme_name == theme).all() diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index a4fb54932..f2ac04c1b 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -29,6 +29,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, SongXMLParser, BaseListWithDnD, \ Receiver, ItemCapabilities, translate, check_item_selected +from openlp.plugins.custom.lib.db import CustomSlide log = logging.getLogger(__name__) @@ -72,7 +73,8 @@ class CustomMediaItem(MediaManagerItem): MediaManagerItem.requiredIcons(self) def initialise(self): - self.loadCustomListView(self.parent.custommanager.get_all_slides()) + self.loadCustomListView(self.parent.custommanager.get_all_objects( + CustomSlide, CustomSlide.title)) #Called to redisplay the song list screen edith from a search #or from the exit of the Song edit dialog. If remote editing is active #Trigger it and clean up so it will not update again. @@ -84,10 +86,10 @@ class CustomMediaItem(MediaManagerItem): def loadCustomListView(self, list): self.ListView.clear() - for CustomSlide in list: - custom_name = QtGui.QListWidgetItem(CustomSlide.title) + for customSlide in list: + custom_name = QtGui.QListWidgetItem(customSlide.title) custom_name.setData( - QtCore.Qt.UserRole, QtCore.QVariant(CustomSlide.id)) + QtCore.Qt.UserRole, QtCore.QVariant(customSlide.id)) self.ListView.addItem(custom_name) def onNewClick(self): @@ -106,7 +108,7 @@ class CustomMediaItem(MediaManagerItem): type of display is required. """ fields = customid.split(u':') - valid = self.parent.custommanager.get_custom(fields[1]) + valid = self.parent.custommanager.get_object(CustomSlide, fields[1]) if valid: self.remoteCustom = fields[1] self.remoteTriggered = fields[0] @@ -134,11 +136,14 @@ class CustomMediaItem(MediaManagerItem): if check_item_selected(self.ListView, translate('CustomPlugin.MediaItem', 'You must select an item to delete.')): - item = self.ListView.currentItem() - item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] - self.parent.custommanager.delete_custom(item_id) - row = self.ListView.row(item) - self.ListView.takeItem(row) + row_list = [item.row() for item in self.ListView.selectedIndexes()] + row_list.sort(reverse=True) + id_list = [(item.data(QtCore.Qt.UserRole)).toInt()[0] + for item in self.ListView.selectedIndexes()] + for id in id_list: + self.parent.custommanager.delete_custom(id) + for row in row_list: + self.ListView.takeItem(row) def generateSlideData(self, service_item, item=None): raw_slides = [] @@ -158,7 +163,7 @@ class CustomMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.AllowsEdit) service_item.add_capability(ItemCapabilities.AllowsPreview) service_item.add_capability(ItemCapabilities.AllowsLoop) - customSlide = self.parent.custommanager.get_custom(item_id) + customSlide = self.parent.custommanager.get_object(CustomSlide, item_id) title = customSlide.title credit = customSlide.credits service_item.editId = item_id diff --git a/openlp/plugins/custom/lib/meta.py b/openlp/plugins/custom/lib/meta.py deleted file mode 100644 index affa31969..000000000 --- a/openlp/plugins/custom/lib/meta.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from sqlalchemy import MetaData - -__all__ = ['session', 'metadata', 'engine'] - -# SQLAlchemy database engine. Updated by model.init_model() -engine = None - -# SQLAlchemy session manager. Updated by model.init_model() -session = None - -# Global metadata. If you have multiple databases with overlapping table -# names, you'll need a metadata for each database -metadata = MetaData() diff --git a/openlp/plugins/custom/lib/tables.py b/openlp/plugins/custom/lib/tables.py deleted file mode 100644 index bb86d9d6d..000000000 --- a/openlp/plugins/custom/lib/tables.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from sqlalchemy import Column, Table, types - -from openlp.plugins.custom.lib.meta import metadata - -# Definition of the "custom slide" table -custom_slide_table = Table(u'custom_slide', metadata, - Column(u'id', types.Integer(), primary_key=True), - Column(u'title', types.Unicode(255), nullable=False), - Column(u'text', types.UnicodeText, nullable=False), - Column(u'credits', types.UnicodeText), - Column(u'theme_name', types.Unicode(128)) -) diff --git a/openlp/plugins/images/__init__.py b/openlp/plugins/images/__init__.py index 1a348a0df..58cfb69b5 100644 --- a/openlp/plugins/images/__init__.py +++ b/openlp/plugins/images/__init__.py @@ -22,3 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`images` module provides the Images plugin. The Images plugin +provides the facility to display images from OpenLP. +""" diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 8e9f9e220..be3863bda 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -36,18 +36,9 @@ class ImagePlugin(Plugin): def __init__(self, plugin_helpers): Plugin.__init__(self, u'Images', u'1.9.2', plugin_helpers) self.weight = -7 - self.icon = build_icon(u':/media/media_image.png') + self.icon = build_icon(u':/plugins/plugin_images.png') self.status = PluginStatus.Active - def initialise(self): - log.info(u'Plugin Initialising') - Plugin.initialise(self) - self.insert_toolbox_item() - - def finalise(self): - log.info(u'Plugin Finalise') - self.remove_toolbox_item() - def get_settings_tab(self): return ImageTab(self.name) @@ -64,4 +55,4 @@ class ImagePlugin(Plugin): 'an image is selected any songs which are rendered will use the ' 'selected image from the background instead of the one provied by ' 'the theme.
') - return about_text \ No newline at end of file + return about_text diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 29985a9ed..ee7a22106 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -119,9 +119,10 @@ class ImageMediaItem(MediaManagerItem): """ if check_item_selected(self.ListView, translate('ImagePlugin.MediaItem', 'You must select an item to delete.')): - items = self.ListView.selectedIndexes() - for item in items: - text = self.ListView.item(item.row()) + row_list = [item.row() for item in self.ListView.selectedIndexes()] + row_list.sort(reverse=True) + for row in row_list: + text = self.ListView.item(row) if text: try: os.remove(os.path.join(self.servicePath, @@ -129,9 +130,9 @@ class ImageMediaItem(MediaManagerItem): except OSError: #if not present do not worry pass - self.ListView.takeItem(item.row()) - SettingsManager.set_list(self.settingsSection, - self.settingsSection, self.getFileList()) + self.ListView.takeItem(row) + SettingsManager.set_list(self.settingsSection, + self.settingsSection, self.getFileList()) def loadList(self, list): for file in list: @@ -169,17 +170,14 @@ class ImageMediaItem(MediaManagerItem): return False def onReplaceClick(self): - if not self.ListView.selectedIndexes(): - QtGui.QMessageBox.information(self, - translate('ImagePlugin.MediaItem', 'No item selected'), - translate('ImagePlugin.MediaItem', - 'You must select one item')) - items = self.ListView.selectedIndexes() - for item in items: - bitem = self.ListView.item(item.row()) - filename = unicode(bitem.data(QtCore.Qt.UserRole).toString()) - frame = QtGui.QImage(unicode(filename)) - self.parent.maindisplay.addImageWithText(frame) + if check_item_selected(self.ListView, + translate('ImagePlugin.MediaItem', + 'You must select an item to process.')): + item = self.buildServiceItem() + item.render() + self.parent.live_controller.displayManager. \ + displayImage(item.get_rendered_frame(0)[u'display']) + def onPreviewClick(self): MediaManagerItem.onPreviewClick(self) diff --git a/openlp/plugins/media/__init__.py b/openlp/plugins/media/__init__.py index 1a348a0df..28ea0e960 100644 --- a/openlp/plugins/media/__init__.py +++ b/openlp/plugins/media/__init__.py @@ -22,3 +22,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`media` module provides the Media plugin which allows OpenLP to +display videos. The media supported depends not only on the Python support +but also extensively on the codecs installed on the underlying operating system +being picked up and usable by Python. +""" diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index b6de22712..10c398912 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -29,7 +29,8 @@ import os from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \ - ItemCapabilities, SettingsManager, translate, check_item_selected + ItemCapabilities, SettingsManager, translate, check_item_selected, \ + context_menu_action log = logging.getLogger(__name__) @@ -73,13 +74,13 @@ class MediaMediaItem(MediaManagerItem): self.hasNewIcon = False self.hasEditIcon = False -# def addListViewToToolBar(self): -# MediaManagerItem.addListViewToToolBar(self) -# self.ListView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) -# self.ListView.addAction( -# context_menu_action(self.ListView, u':/slides/slide_blank.png', -# translate('MediaPlugin.MediaItem', 'Replace Live Background'), -# self.onReplaceClick)) + def addListViewToToolBar(self): + MediaManagerItem.addListViewToToolBar(self) + self.ListView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) + self.ListView.addAction( + context_menu_action(self.ListView, u':/slides/slide_blank.png', + translate('MediaPlugin.MediaItem', 'Replace Live Background'), + self.onReplaceClick)) def addEndHeaderBar(self): self.ImageWidget = QtGui.QWidget(self) @@ -92,24 +93,25 @@ class MediaMediaItem(MediaManagerItem): self.ImageWidget.setSizePolicy(sizePolicy) self.ImageWidget.setObjectName(u'ImageWidget') #Replace backgrounds do not work at present so remove functionality. -# self.blankButton = self.Toolbar.addToolbarButton( -# u'Replace Background', u':/slides/slide_blank.png', -# translate('MediaPlugin.MediaItem', 'Replace Live Background'), -# self.onReplaceClick, False) + self.blankButton = self.Toolbar.addToolbarButton( + u'Replace Background', u':/slides/slide_blank.png', + translate('MediaPlugin.MediaItem', 'Replace Live Background'), + self.onReplaceClick, False) # Add the song widget to the page layout self.PageLayout.addWidget(self.ImageWidget) -# def onReplaceClick(self): + def onReplaceClick(self): # if self.background: # self.background = False # Receiver.send_message(u'videodisplay_stop') # else: # self.background = True -# if not self.ListView.selectedIndexes(): -# QtGui.QMessageBox.information(self, -# translate('MediaPlugin.MediaItem', 'No item selected'), -# translate('MediaPlugin.MediaItem', -# 'You must select one item')) + if check_item_selected(self.ListView, + translate('ImagePlugin.MediaItem', + 'You must select an item to process.')): + item = self.ListView.currentItem() + filename = unicode(item.data(QtCore.Qt.UserRole).toString()) + self.parent.live_controller.displayManager.displayVideo(filename) # items = self.ListView.selectedIndexes() # for item in items: # bitem = self.ListView.item(item.row()) @@ -143,9 +145,10 @@ class MediaMediaItem(MediaManagerItem): """ if check_item_selected(self.ListView, translate('MediaPlugin.MediaItem', 'You must select an item to delete.')): - item = self.ListView.currentItem() - row = self.ListView.row(item) - self.ListView.takeItem(row) + row_list = [item.row() for item in self.ListView.selectedIndexes()] + row_list.sort(reverse=True) + for row in row_list: + self.ListView.takeItem(row) SettingsManager.set_list(self.settingsSection, self.settingsSection, self.getFileList()) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index f7c2c998f..24138a0f5 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -38,7 +38,7 @@ class MediaPlugin(Plugin): def __init__(self, plugin_helpers): Plugin.__init__(self, u'Media', u'1.9.2', plugin_helpers) self.weight = -6 - self.icon = build_icon(u':/media/media_video.png') + self.icon = build_icon(u':/plugins/plugin_media.png') # passed with drag and drop messages self.dnd_id = u'Media' self.status = PluginStatus.Active @@ -67,15 +67,6 @@ class MediaPlugin(Plugin): type = u'' return list, type - def initialise(self): - log.info(u'Plugin Initialising') - Plugin.initialise(self) - self.insert_toolbox_item() - - def finalise(self): - log.info(u'Plugin Finalise') - self.remove_toolbox_item() - def get_media_manager_item(self): # Create the MediaManagerItem object return MediaMediaItem(self, self.icon, self.name) diff --git a/openlp/plugins/presentations/__init__.py b/openlp/plugins/presentations/__init__.py index 1a348a0df..b7f26b39b 100644 --- a/openlp/plugins/presentations/__init__.py +++ b/openlp/plugins/presentations/__init__.py @@ -22,3 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`presentations` module provides the Presentations plugin which allows +OpenLP to show presentations from most popular presentation packages. +""" diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 52de42ca7..c4250f27a 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -41,10 +41,15 @@ from openlp.core.lib import resize_image if os.name == u'nt': from win32com.client import Dispatch + import pywintypes else: - import uno - from com.sun.star.beans import PropertyValue - + try: + import uno + from com.sun.star.beans import PropertyValue + uno_available = True + except ImportError: + uno_available = False + from PyQt4 import QtCore from presentationcontroller import PresentationController, PresentationDocument @@ -78,9 +83,7 @@ class ImpressController(PresentationController): if os.name == u'nt': return self.get_com_servicemanager() is not None else: - # If not windows, and we've got this far then probably - # installed else the import uno would likely have failed - return True + return uno_available def start_process(self): """ @@ -322,7 +325,10 @@ class ImpressDocument(PresentationDocument): Returns true if screen is blank """ log.debug(u'is blank OpenOffice') - return self.control.isPaused() + if self.control: + return self.control.isPaused() + else: + return False def stop_presentation(self): log.debug(u'stop presentation OpenOffice') diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 384d28c4a..ee50dd556 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -180,18 +180,22 @@ class PresentationMediaItem(MediaManagerItem): if check_item_selected(self.ListView, translate('PresentationPlugin.MediaItem', 'You must select an item to delete.')): - item = self.ListView.currentItem() - row = self.ListView.row(item) - self.ListView.takeItem(row) + items = self.ListView.selectedIndexes() + row_list = [item.row() for item in items] + row_list.sort(reverse=True) + for item in items: + filepath = unicode(item.data( + QtCore.Qt.UserRole).toString()) + #not sure of this has errors + #John please can you look at . + for cidx in self.controllers: + doc = self.controllers[cidx].add_doc(filepath) + doc.presentation_deleted() + doc.close_presentation() + for row in row_list: + self.ListView.takeItem(row) SettingsManager.set_list(self.settingsSection, self.settingsSection, self.getFileList()) - filepath = unicode(item.data(QtCore.Qt.UserRole).toString()) - #not sure of this has errors - #John please can you look at . - for cidx in self.controllers: - doc = self.controllers[cidx].add_doc(filepath) - doc.presentation_deleted() - doc.close_presentation() def generateSlideData(self, service_item, item=None): items = self.ListView.selectedIndexes() diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 10ab41fd0..e8f0d9390 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -27,7 +27,7 @@ import os import logging if os.name == u'nt': - from ctypes import * + from ctypes import cdll from ctypes.wintypes import RECT from presentationcontroller import PresentationController, PresentationDocument diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 9303d5921..22c7c554e 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -40,7 +40,7 @@ class PresentationPlugin(Plugin): self.controllers = {} Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers) self.weight = -8 - self.icon = build_icon(u':/media/media_presentation.png') + self.icon = build_icon(u':/plugins/plugin_presentations.png') self.status = PluginStatus.Active def get_settings_tab(self): @@ -64,7 +64,7 @@ class PresentationPlugin(Plugin): controller = self.controllers[key] if controller.enabled: controller.kill() - self.remove_toolbox_item() + Plugin.finalise(self) def get_media_manager_item(self): """ diff --git a/openlp/plugins/remotes/__init__.py b/openlp/plugins/remotes/__init__.py index 1a348a0df..59b771c0d 100644 --- a/openlp/plugins/remotes/__init__.py +++ b/openlp/plugins/remotes/__init__.py @@ -22,3 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`remotes` plugin allows OpenLP to be controlled from another machine +over a network connection. +""" diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 7004207e4..808714fb3 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -25,7 +25,7 @@ import logging -from openlp.core.lib import Plugin, translate +from openlp.core.lib import Plugin, translate, build_icon from openlp.plugins.remotes.lib import RemoteTab, HttpServer log = logging.getLogger(__name__) @@ -38,6 +38,7 @@ class RemotesPlugin(Plugin): remotes constructor """ Plugin.__init__(self, u'Remotes', u'1.9.2', plugin_helpers) + self.icon = build_icon(u':/plugins/plugin_remote.png') self.weight = -1 self.server = None @@ -55,7 +56,7 @@ class RemotesPlugin(Plugin): Tidy up and close down the http server """ log.debug(u'finalise') - self.remove_toolbox_item() + Plugin.finalise(self) if self.server: self.server.close() @@ -74,4 +75,4 @@ class RemotesPlugin(Plugin): 'provides the ability to send messages to a running version of ' 'openlp on a different computer via a web browser or other app
' 'The Primary use for this would be to send alerts from a creche') - return about_text \ No newline at end of file + return about_text diff --git a/openlp/plugins/songs/__init__.py b/openlp/plugins/songs/__init__.py index 1a348a0df..4cd5537eb 100644 --- a/openlp/plugins/songs/__init__.py +++ b/openlp/plugins/songs/__init__.py @@ -22,3 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +The :mod:`songs` module provides the Songs plugin. The Songs plugin provides +the main lyric projection function of OpenLP. +""" diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index 972cab053..76e9dda55 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -24,6 +24,7 @@ ############################################################################### from PyQt4 import QtCore, QtGui + from openlp.core.lib import translate class Ui_AuthorsDialog(object): diff --git a/openlp/plugins/songs/forms/authorsform.py b/openlp/plugins/songs/forms/authorsform.py index 5c5f019d9..c219b12ac 100644 --- a/openlp/plugins/songs/forms/authorsform.py +++ b/openlp/plugins/songs/forms/authorsform.py @@ -28,7 +28,6 @@ from PyQt4 import QtGui, QtCore from openlp.core.lib import translate from openlp.plugins.songs.forms.authorsdialog import Ui_AuthorsDialog - class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog): """ Class to control the Maintenance of Authors Dialog diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 1d23a4df9..ce3291988 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -24,9 +24,8 @@ ############################################################################### from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate -from openlp.core.lib import build_icon +from openlp.core.lib import build_icon, translate class Ui_EditSongDialog(object): def setupUi(self, EditSongDialog): diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index cbb79bff4..8c04bed0a 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import SongXMLBuilder, SongXMLParser, Receiver, translate from openlp.plugins.songs.forms import EditVerseForm from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.models import Song, Author, Topic, Book +from openlp.plugins.songs.lib.db import Book, Song, Author, Topic from editsongdialog import Ui_EditSongDialog log = logging.getLogger(__name__) @@ -118,7 +118,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.TopicRemoveButton.setEnabled(False) def loadAuthors(self): - authors = self.songmanager.get_authors() + authors = self.songmanager.get_all_objects(Author, Author.display_name) self.AuthorsSelectionComboItem.clear() self.AuthorsSelectionComboItem.addItem(u'') for author in authors: @@ -128,7 +128,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): row, QtCore.QVariant(author.id)) def loadTopics(self): - topics = self.songmanager.get_topics() + topics = self.songmanager.get_all_objects(Topic, Topic.name) self.SongTopicCombo.clear() self.SongTopicCombo.addItem(u'') for topic in topics: @@ -137,7 +137,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.SongTopicCombo.setItemData(row, QtCore.QVariant(topic.id)) def loadBooks(self): - books = self.songmanager.get_books() + books = self.songmanager.get_all_objects(Book, Book.name) self.SongbookCombo.clear() self.SongbookCombo.addItem(u'') for book in books: @@ -178,11 +178,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.loadAuthors() self.loadTopics() self.loadBooks() - self.song = self.songmanager.get_song(id) + self.song = self.songmanager.get_object(Song, id) self.TitleEditItem.setText(self.song.title) title = self.song.search_title.split(u'@') if self.song.song_book_id != 0: - book_name = self.songmanager.get_book(self.song.song_book_id) + book_name = self.songmanager.get_object(Book, + self.song.song_book_id) id = self.SongbookCombo.findText( unicode(book_name.name), QtCore.Qt.MatchExactly) if id == -1: @@ -289,7 +290,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes: author = Author.populate(first_name=text.rsplit(u' ', 1)[0], last_name=text.rsplit(u' ', 1)[1], display_name=text) - self.songmanager.save_author(author) + self.songmanager.save_object(author) self.song.authors.append(author) author_item = QtGui.QListWidgetItem( unicode(author.display_name)) @@ -302,7 +303,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): return elif item > 0: item_id = (self.AuthorsSelectionComboItem.itemData(item)).toInt()[0] - author = self.songmanager.get_author(item_id) + author = self.songmanager.get_object(Author, item_id) self.song.authors.append(author) author_item = QtGui.QListWidgetItem(unicode(author.display_name)) author_item.setData(QtCore.Qt.UserRole, QtCore.QVariant(author.id)) @@ -325,7 +326,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.AuthorRemoveButton.setEnabled(False) item = self.AuthorsListView.currentItem() author_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] - author = self.songmanager.get_author(author_id) + author = self.songmanager.get_object(Author, author_id) self.song.authors.remove(author) row = self.AuthorsListView.row(item) self.AuthorsListView.takeItem(row) @@ -341,7 +342,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes: topic = Topic.populate(name=text) - self.songmanager.save_topic(topic) + self.songmanager.save_object(topic) self.song.topics.append(topic) topic_item = QtGui.QListWidgetItem(unicode(topic.name)) topic_item.setData(QtCore.Qt.UserRole, @@ -353,7 +354,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): return elif item > 0: item_id = (self.SongTopicCombo.itemData(item)).toInt()[0] - topic = self.songmanager.get_topic(item_id) + topic = self.songmanager.get_object(Topic, item_id) self.song.topics.append(topic) topic_item = QtGui.QListWidgetItem(unicode(topic.name)) topic_item.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id)) @@ -375,7 +376,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.TopicRemoveButton.setEnabled(False) item = self.TopicsListView.currentItem() topic_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] - topic = self.songmanager.get_topic(topic_id) + topic = self.songmanager.get_object(Topic, topic_id) self.song.topics.remove(topic) row = self.TopicsListView.row(item) self.TopicsListView.takeItem(row) @@ -391,12 +392,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes: book = Book.populate(name=text) - self.songmanager.save_book(book) + self.songmanager.save_object(book) self.song.book = book self.loadBooks() else: return - elif item > 1: + elif item >= 1: item = int(self.SongbookCombo.currentIndex()) self.song.song_book_id = \ (self.SongbookCombo.itemData(item)).toInt()[0] @@ -630,7 +631,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): if self._validate_song(): self.processLyrics() self.processTitle() - self.songmanager.save_song(self.song) + self.songmanager.save_object(self.song) return True return False diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py index eb791cd69..2ab223b34 100644 --- a/openlp/plugins/songs/forms/songbookdialog.py +++ b/openlp/plugins/songs/forms/songbookdialog.py @@ -24,6 +24,7 @@ ############################################################################### from PyQt4 import QtCore, QtGui + from openlp.core.lib import translate class Ui_SongBookDialog(object): diff --git a/openlp/plugins/songs/forms/songbookform.py b/openlp/plugins/songs/forms/songbookform.py index fd447db22..6316a8002 100644 --- a/openlp/plugins/songs/forms/songbookform.py +++ b/openlp/plugins/songs/forms/songbookform.py @@ -28,7 +28,6 @@ from PyQt4 import QtGui from openlp.core.lib import translate from openlp.plugins.songs.forms.songbookdialog import Ui_SongBookDialog - class SongBookForm(QtGui.QDialog, Ui_SongBookDialog): """ Class documentation goes here. diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 8a830d720..09db3dcb8 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -30,7 +30,7 @@ from PyQt4 import QtCore, QtGui from songimportwizard import Ui_SongImportWizard from openlp.core.lib import Receiver, SettingsManager, translate #from openlp.core.utils import AppLocation -from openlp.plugins.songs.lib.manager import SongFormat +from openlp.plugins.songs.lib import SongFormat log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 4a666c3c5..54a8d539f 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -25,12 +25,10 @@ from PyQt4 import QtGui, QtCore -from openlp.plugins.songs.lib.classes import Author, Book, Topic -from songmaintenancedialog import Ui_SongMaintenanceDialog -from authorsform import AuthorsForm -from topicsform import TopicsForm -from songbookform import SongBookForm from openlp.core.lib import translate +from openlp.plugins.songs.forms import AuthorsForm, TopicsForm, SongBookForm +from openlp.plugins.songs.lib.db import Author, Book, Topic +from songmaintenancedialog import Ui_SongMaintenanceDialog class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ @@ -81,17 +79,17 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): else: return -1 - def _deleteItem(self, list_widget, get_func, del_func, reset_func, - dlg_title, del_text, err_text, sel_text): + def _deleteItem(self, item_class, list_widget, reset_func, dlg_title, + del_text, err_text, sel_text): item_id = self._getCurrentItemId(list_widget) if item_id != -1: - item = get_func(item_id) + item = self.songmanager.get_object(item_class, item_id) if item and len(item.songs) == 0: if QtGui.QMessageBox.warning(self, dlg_title, del_text, QtGui.QMessageBox.StandardButtons( QtGui.QMessageBox.No | QtGui.QMessageBox.Yes) ) == QtGui.QMessageBox.Yes: - del_func(item.id) + self.songmanager.delete_object(item_class, item.id) reset_func() else: QtGui.QMessageBox.critical(self, dlg_title, err_text) @@ -100,7 +98,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def resetAuthors(self): self.AuthorsListWidget.clear() - authors = self.songmanager.get_authors() + authors = self.songmanager.get_all_objects(Author, Author.display_name) for author in authors: if author.display_name: author_name = QtGui.QListWidgetItem(author.display_name) @@ -112,7 +110,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def resetTopics(self): self.TopicsListWidget.clear() - topics = self.songmanager.get_topics() + topics = self.songmanager.get_all_objects(Topic, Topic.name) for topic in topics: topic_name = QtGui.QListWidgetItem(topic.name) topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id)) @@ -120,7 +118,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def resetBooks(self): self.BooksListWidget.clear() - books = self.songmanager.get_books() + books = self.songmanager.get_all_objects(Book, Book.name) for book in books: book_name = QtGui.QListWidgetItem(book.name) book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id)) @@ -133,7 +131,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): first_name=unicode(self.authorform.FirstNameEdit.text()), last_name=unicode(self.authorform.LastNameEdit.text()), display_name=unicode(self.authorform.DisplayEdit.text())) - if self.songmanager.save_author(author): + if self.songmanager.save_object(author): self.resetAuthors() else: QtGui.QMessageBox.critical( @@ -145,7 +143,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onTopicAddButtonClick(self): if self.topicform.exec_(): topic = Topic.populate(name=unicode(self.topicform.NameEdit.text())) - if self.songmanager.save_topic(topic): + if self.songmanager.save_object(topic): self.resetTopics() else: QtGui.QMessageBox.critical( @@ -159,7 +157,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): book = Book.populate( name=unicode(self.bookform.NameEdit.text()), publisher=unicode(self.bookform.PublisherEdit.text())) - if self.songmanager.save_book(book): + if self.songmanager.save_object(book): self.resetBooks() else: QtGui.QMessageBox.critical( @@ -171,7 +169,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onAuthorEditButtonClick(self): author_id = self._getCurrentItemId(self.AuthorsListWidget) if author_id != -1: - author = self.songmanager.get_author(author_id) + author = self.songmanager.get_object(Author, author_id) # Just make sure none of the fields is None if author.first_name is None: author.first_name = u'' @@ -189,7 +187,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): author.last_name = unicode(self.authorform.LastNameEdit.text()) author.display_name = unicode( self.authorform.DisplayEdit.text()) - if self.songmanager.save_author(author): + if self.songmanager.save_object(author): self.resetAuthors() else: QtGui.QMessageBox.critical( @@ -201,11 +199,11 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onTopicEditButtonClick(self): topic_id = self._getCurrentItemId(self.TopicsListWidget) if topic_id != -1: - topic = self.songmanager.get_topic(topic_id) + topic = self.songmanager.get_object(Topic, topic_id) self.topicform.NameEdit.setText(topic.name) if self.topicform.exec_(False): topic.name = unicode(self.topicform.NameEdit.text()) - if self.songmanager.save_topic(topic): + if self.songmanager.save_object(topic): self.resetTopics() else: QtGui.QMessageBox.critical( @@ -217,13 +215,13 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onBookEditButtonClick(self): book_id = self._getCurrentItemId(self.BooksListWidget) if book_id != -1: - book = self.songmanager.get_book(book_id) + book = self.songmanager.get_object(Book, book_id) self.bookform.NameEdit.setText(book.name) self.bookform.PublisherEdit.setText(book.publisher) if self.bookform.exec_(False): book.name = unicode(self.bookform.NameEdit.text()) book.publisher = unicode(self.bookform.PublisherEdit.text()) - if self.songmanager.save_book(book): + if self.songmanager.save_object(book): self.resetBooks() else: QtGui.QMessageBox.critical( @@ -236,11 +234,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ Delete the author if the author is not attached to any songs """ - self._deleteItem( - self.AuthorsListWidget, self.songmanager.get_author, - self.songmanager.delete_author, self.resetAuthors, + self._deleteItem(Author, self.AuthorsListWidget, self.resetAuthors, translate('SongsPlugin.SongMaintenanceForm', 'Delete Author'), - translate('SongsPlugin.SongMaintenanceForm', + translate('SongsPlugin.SongMaintenanceForm', 'Are you sure you want to delete the selected author?'), translate('SongsPlugin.SongMaintenanceForm', 'This author can\'t be deleted, they are currently ' @@ -252,13 +248,11 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ Delete the Book is the Book is not attached to any songs """ - self._deleteItem( - self.TopicsListWidget, self.songmanager.get_topic, - self.songmanager.delete_topic, self.resetTopics, + self._deleteItem(Topic, self.TopicsListWidget, self.resetTopics, translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'), - translate('SongsPlugin.SongMaintenanceForm', + translate('SongsPlugin.SongMaintenanceForm', 'Are you sure you want to delete the selected topic?'), - translate('SongsPlugin.SongMaintenanceForm', + translate('SongsPlugin.SongMaintenanceForm', 'This topic can\'t be deleted, it is currently ' 'assigned to at least one song.'), translate('SongsPlugin.SongMaintenanceForm', @@ -268,13 +262,11 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ Delete the Book is the Book is not attached to any songs """ - self._deleteItem( - self.BooksListWidget, self.songmanager.get_book, - self.songmanager.delete_book, self.resetBooks, + self._deleteItem(Book, self.BooksListWidget, self.resetBooks, translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'), translate('SongsPlugin.SongMaintenanceForm', 'Are you sure you want to delete the selected book?'), - translate('SongsPlugin.SongMaintenanceForm', + translate('SongsPlugin.SongMaintenanceForm', 'This book can\'t be deleted, it is currently ' 'assigned to at least one song.'), - translate('SongsPlugin.SongMaintenanceForm', 'No book selected!')) + translate('SongsPlugin.SongMaintenanceForm', u'No book selected!')) diff --git a/openlp/plugins/songs/forms/topicsdialog.py b/openlp/plugins/songs/forms/topicsdialog.py index 463367683..6ee06c9c0 100644 --- a/openlp/plugins/songs/forms/topicsdialog.py +++ b/openlp/plugins/songs/forms/topicsdialog.py @@ -24,6 +24,7 @@ ############################################################################### from PyQt4 import QtCore, QtGui + from openlp.core.lib import translate class Ui_TopicsDialog(object): diff --git a/openlp/plugins/songs/forms/topicsform.py b/openlp/plugins/songs/forms/topicsform.py index cf93ff0f2..51d649ebe 100644 --- a/openlp/plugins/songs/forms/topicsform.py +++ b/openlp/plugins/songs/forms/topicsform.py @@ -28,7 +28,6 @@ from PyQt4 import QtGui from openlp.core.lib import translate from openlp.plugins.songs.forms.topicsdialog import Ui_TopicsDialog - class TopicsForm(QtGui.QDialog, Ui_TopicsDialog): """ Class documentation goes here. diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index dddb23b4e..8c07c31c7 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -25,6 +25,52 @@ from openlp.core.lib import translate +#from openlp.plugins.songs.lib import OpenLyricsSong, OpenSongSong, CCLISong, \ +# CSVSong + +class SongFormat(object): + """ + This is a special enumeration class that holds the various types of songs, + plus a few helper functions to facilitate generic handling of song types + for importing. + """ + Unknown = -1 + OpenLyrics = 0 + OpenSong = 1 + CCLI = 2 + CSV = 3 + + @staticmethod + def get_class(id): + """ + Return the appropriate imeplementation class. + + ``id`` + The song format. + """ +# if id == SongFormat.OpenLyrics: +# return OpenLyricsSong +# elif id == SongFormat.OpenSong: +# return OpenSongSong +# elif id == SongFormat.CCLI: +# return CCLISong +# elif id == SongFormat.CSV: +# return CSVSong +# else: + return None + + @staticmethod + def list(): + """ + Return a list of the supported song formats. + """ + return [ + SongFormat.OpenLyrics, + SongFormat.OpenSong, + SongFormat.CCLI, + SongFormat.CSV + ] + class VerseType(object): """ VerseType provides an enumeration for the tags that may be associated @@ -91,7 +137,6 @@ class VerseType(object): unicode(VerseType.to_string(VerseType.Other)).lower(): return VerseType.Other -from manager import SongManager from songstab import SongsTab from mediaitem import SongMediaItem from songimport import SongImport diff --git a/openlp/plugins/songs/lib/classes.py b/openlp/plugins/songs/lib/classes.py deleted file mode 100644 index c465588a4..000000000 --- a/openlp/plugins/songs/lib/classes.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from openlp.core.lib import BaseModel - -class Author(BaseModel): - """ - Author model - """ - pass - -class Book(BaseModel): - """ - Book model - """ - def __repr__(self): - return u'' % ( - str(self.id), self.name, self.publisher) - -class Song(BaseModel): - """ - Song model - """ - pass - -class Topic(BaseModel): - """ - Topic model - """ - pass diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py new file mode 100644 index 000000000..655043144 --- /dev/null +++ b/openlp/plugins/songs/lib/db.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # +# Thompson, Jon Tibble, Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# 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:`db` module provides the database and schema that is the backend for +the Songs plugin +""" + +from sqlalchemy import Column, ForeignKey, Index, Table, types +from sqlalchemy.orm import mapper, relation + +from openlp.core.lib.db import BaseModel, init_db + +class Author(BaseModel): + """ + Author model + """ + pass + +class Book(BaseModel): + """ + Book model + """ + def __repr__(self): + return u'' % ( + str(self.id), self.name, self.publisher) + +class Song(BaseModel): + """ + Song model + """ + pass + +class Topic(BaseModel): + """ + Topic model + """ + pass + +def init_schema(url): + """ + Setup the songs database connection and initialise the database schema + + ``url`` + The database to setup + """ + session, metadata = init_db(url, auto_flush=False) + + # Definition of the "authors" table + authors_table = Table(u'authors', metadata, + Column(u'id', types.Integer, primary_key=True), + Column(u'first_name', types.Unicode(128)), + Column(u'last_name', types.Unicode(128)), + Column(u'display_name', types.Unicode(255), nullable=False) + ) + + # Definition of the "song_books" table + song_books_table = Table(u'song_books', metadata, + Column(u'id', types.Integer, primary_key=True), + Column(u'name', types.Unicode(128), nullable=False), + Column(u'publisher', types.Unicode(128)) + ) + + # Definition of the "songs" table + songs_table = Table(u'songs', metadata, + Column(u'id', types.Integer, primary_key=True), + Column(u'song_book_id', types.Integer, + ForeignKey(u'song_books.id'), default=0), + Column(u'title', types.Unicode(255), nullable=False), + Column(u'lyrics', types.UnicodeText, nullable=False), + Column(u'verse_order', types.Unicode(128)), + Column(u'copyright', types.Unicode(255)), + Column(u'comments', types.UnicodeText), + Column(u'ccli_number', types.Unicode(64)), + Column(u'song_number', types.Unicode(64)), + Column(u'theme_name', types.Unicode(128)), + Column(u'search_title', types.Unicode(255), index=True, nullable=False), + Column(u'search_lyrics', types.UnicodeText, index=True, nullable=False) + ) + + # Definition of the "topics" table + topics_table = Table(u'topics', metadata, + Column(u'id', types.Integer, primary_key=True), + Column(u'name', types.Unicode(128), nullable=False) + ) + + # Definition of the "authors_songs" table + authors_songs_table = Table(u'authors_songs', metadata, + Column(u'author_id', types.Integer, + ForeignKey(u'authors.id'), primary_key=True), + Column(u'song_id', types.Integer, + ForeignKey(u'songs.id'), primary_key=True) + ) + + # Definition of the "songs_topics" table + songs_topics_table = Table(u'songs_topics', metadata, + Column(u'song_id', types.Integer, + ForeignKey(u'songs.id'), primary_key=True), + Column(u'topic_id', types.Integer, + ForeignKey(u'topics.id'), primary_key=True) + ) + + # Define table indexes + Index(u'authors_id', authors_table.c.id) + Index(u'authors_display_name_id', authors_table.c.display_name, + authors_table.c.id) + Index(u'song_books_id', song_books_table.c.id) + Index(u'songs_id', songs_table.c.id) + Index(u'topics_id', topics_table.c.id) + Index(u'authors_songs_author', authors_songs_table.c.author_id, + authors_songs_table.c.song_id) + Index(u'authors_songs_song', authors_songs_table.c.song_id, + authors_songs_table.c.author_id) + Index(u'topics_song_topic', songs_topics_table.c.topic_id, + songs_topics_table.c.song_id) + Index(u'topics_song_song', songs_topics_table.c.song_id, + songs_topics_table.c.topic_id) + + mapper(Author, authors_table) + mapper(Book, song_books_table) + mapper(Song, songs_table, + properties={'authors': relation(Author, backref='songs', + secondary=authors_songs_table), + 'book': relation(Book, backref='songs'), + 'topics': relation(Topic, backref='songs', + secondary=songs_topics_table)}) + mapper(Topic, topics_table) + + metadata.create_all(checkfirst=True) + return session diff --git a/openlp/plugins/songs/lib/manager.py b/openlp/plugins/songs/lib/manager.py deleted file mode 100644 index 4276303d5..000000000 --- a/openlp/plugins/songs/lib/manager.py +++ /dev/null @@ -1,318 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -import logging - -from PyQt4 import QtCore -from sqlalchemy.exceptions import InvalidRequestError - -from openlp.core.utils import AppLocation -from openlp.plugins.songs.lib.models import init_models, metadata, Song, \ - Author, Topic, Book -#from openlp.plugins.songs.lib import OpenLyricsSong, OpenSongSong, CCLISong, \ -# CSVSong - -log = logging.getLogger(__name__) - -class SongFormat(object): - """ - This is a special enumeration class that holds the various types of songs, - plus a few helper functions to facilitate generic handling of song types - for importing. - """ - Unknown = -1 - OpenLyrics = 0 - OpenSong = 1 - CCLI = 2 - CSV = 3 - - @staticmethod - def get_class(id): - """ - Return the appropriate imeplementation class. - - ``id`` - The song format. - """ -# if id == SongFormat.OpenLyrics: -# return OpenLyricsSong -# elif id == SongFormat.OpenSong: -# return OpenSongSong -# elif id == SongFormat.CCLI: -# return CCLISong -# elif id == SongFormat.CSV: -# return CSVSong -# else: - return None - - @staticmethod - def list(): - """ - Return a list of the supported song formats. - """ - return [ - SongFormat.OpenLyrics, - SongFormat.OpenSong, - SongFormat.CCLI, - SongFormat.CSV - ] - - -class SongManager(object): - """ - The Song Manager provides a central location for all database code. This - class takes care of connecting to the database and running all the queries. - """ - log.info(u'Song manager loaded') - - def __init__(self): - """ - Creates the connection to the database, and creates the tables if they - don't exist. - """ - log.debug(u'Song Initialising') - settings = QtCore.QSettings() - settings.beginGroup(u'songs') - self.db_url = u'' - db_type = unicode(settings.value(u'db type', - QtCore.QVariant(u'sqlite')).toString()) - if db_type == u'sqlite': - self.db_url = u'sqlite:///%s/songs.sqlite' % \ - AppLocation.get_section_data_path(u'songs') - else: - self.db_url = u'%s://%s:%s@%s/%s' % (db_type, - unicode(settings.value( - u'db username', QtCore.QVariant(u'')).toString()), - unicode(settings.value( - u'db password', QtCore.QVariant(u'')).toString()), - unicode(settings.value( - u'db hostname', QtCore.QVariant(u'')).toString()), - unicode(settings.value( - u'db database', QtCore.QVariant(u'')).toString())) - self.session = init_models(self.db_url) - metadata.create_all(checkfirst=True) - settings.endGroup() - log.debug(u'Song Initialised') - - def get_songs(self): - """ - Returns the details of a song - """ - return self.session.query(Song).order_by(Song.title).all() - - def search_song_title(self, keywords): - """ - Searches the song title for keywords. - """ - return self.session.query(Song).filter( - Song.search_title.like(u'%' + keywords + u'%')).order_by( - Song.search_title.asc()).all() - - def search_song_lyrics(self, keywords): - """ - Searches the song lyrics for keywords. - """ - return self.session.query(Song).filter( - Song.search_lyrics.like(u'%' + keywords + u'%')).order_by( - Song.search_lyrics.asc()).all() - - def get_song_from_author(self, keywords): - """ - Searches the song authors for keywords. - """ - return self.session.query(Author).filter(Author.display_name.like( - u'%' + keywords + u'%')).order_by(Author.display_name.asc()).all() - - def get_song(self, id=None): - """ - Returns the details of a song - """ - if id is None: - return Song() - else: - return self.session.query(Song).get(id) - - def save_song(self, song): - """ - Saves a song to the database - """ - try: - self.session.add(song) - self.session.commit() - return True - except InvalidRequestError: - log.exception(u'Could not save song to song database') - self.session.rollback() - return False - - def delete_song(self, songid): - song = self.get_song(songid) - try: - self.session.delete(song) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Could not delete song from song database') - return False - - def get_authors(self): - """ - Returns a list of all the authors - """ - return self.session.query(Author).order_by(Author.display_name).all() - - def get_author(self, id): - """ - Details of the Author - """ - return self.session.query(Author).get(id) - - def get_author_by_name(self, name): - """ - Get author by display name - """ - return self.session.query(Author).filter_by(display_name=name).first() - - def save_author(self, author): - """ - Save the Author and refresh the cache - """ - try: - self.session.add(author) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Could not save author to song database') - return False - - def delete_author(self, authorid): - """ - Delete the author - """ - author = self.get_author(authorid) - try: - self.session.delete(author) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Could not delete author from song database') - return False - - def get_topics(self): - """ - Returns a list of all the topics - """ - return self.session.query(Topic).order_by(Topic.name).all() - - def get_topic(self, id): - """ - Details of the Topic - """ - return self.session.query(Topic).get(id) - - def get_topic_by_name(self, name): - """ - Get topic by name - """ - return self.session.query(Topic).filter_by(name=name).first() - - def save_topic(self, topic): - """ - Save the Topic - """ - try: - self.session.add(topic) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Could not save topic to song database') - return False - - def delete_topic(self, topicid): - """ - Delete the topic - """ - topic = self.get_topic(topicid) - try: - self.session.delete(topic) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Could not delete topic from song database') - return False - - def get_books(self): - """ - Returns a list of all the Books - """ - return self.session.query(Book).order_by(Book.name).all() - - def get_book(self, id): - """ - Details of the Books - """ - return self.session.query(Book).get(id) - - def get_book_by_name(self, name): - """ - Get book by name - """ - return self.session.query(Book).filter_by(name=name).first() - - def save_book(self, book): - """ - Save the Book - """ - try: - self.session.add(book) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Could not save book to song database') - return False - - def delete_book(self, bookid): - """ - Delete the Book - """ - book = self.get_book(bookid) - try: - self.session.delete(book) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Could not delete book from song database') - return False - - def get_songs_for_theme(self, theme): - return self.session.query(Song).filter(Song.theme_name == theme).all() - diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 385e391d3..4557f53fa 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -31,6 +31,7 @@ from openlp.core.lib import MediaManagerItem, SongXMLParser, \ BaseListWithDnD, Receiver, ItemCapabilities, translate, check_item_selected from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ ImportWizardForm +from openlp.plugins.songs.lib.db import Song log = logging.getLogger(__name__) @@ -164,18 +165,21 @@ class SongMediaItem(MediaManagerItem): search_type = self.SearchTypeComboBox.currentIndex() if search_type == 0: log.debug(u'Titles Search') - search_results = self.parent.manager.search_song_title( - search_keywords) + search_results = self.parent.manager.get_all_objects_filtered(Song, + Song.search_title.like(u'%' + search_keywords + u'%'), + Song.search_title.asc()) self.displayResultsSong(search_results) elif search_type == 1: log.debug(u'Lyrics Search') - search_results = self.parent.manager.search_song_lyrics( - search_keywords) + search_results = self.parent.manager.get_all_objects_filtered(Song, + Song.search_lyrics.like(u'%' + search_keywords + u'%'), + Song.search_lyrics.asc()) self.displayResultsSong(search_results) elif search_type == 2: log.debug(u'Authors Search') - search_results = self.parent.manager.get_song_from_author( - search_keywords) + search_results = self.parent.manager.get_all_objects_filtered( + Author, Author.display_name.like(u'%' + search_keywords + u'%'), + Author.display_name.asc()) self.displayResultsAuthor(search_results) #Called to redisplay the song list screen edith from a search #or from the exit of the Song edit dialog. If remote editing is active @@ -268,7 +272,7 @@ class SongMediaItem(MediaManagerItem): type of display is required. """ fields = songid.split(u':') - valid = self.parent.manager.get_song(fields[1]) + valid = self.parent.manager.get_object(Song, fields[1]) if valid: self.remoteSong = fields[1] self.remoteTriggered = fields[0] @@ -310,7 +314,7 @@ class SongMediaItem(MediaManagerItem): return for item in items: item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] - self.parent.manager.delete_song(item_id) + self.parent.manager.delete_object(Song, item_id) self.onSearchTextButtonClick() def generateSlideData(self, service_item, item=None): @@ -331,7 +335,7 @@ class SongMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.AllowsEdit) service_item.add_capability(ItemCapabilities.AllowsPreview) service_item.add_capability(ItemCapabilities.AllowsLoop) - song = self.parent.manager.get_song(item_id) + song = self.parent.manager.get_object(Song, item_id) service_item.theme = song.theme_name service_item.editId = item_id if song.lyrics.startswith(u'= start_date.toPyDate()) \ .filter(SongUsageItem.usagedate < end_date.toPyDate()) \ - .order_by(SongUsageItem.usagedate, SongUsageItem.usagetime ).all() - - def insert_songusage(self, songusageitem): - """ - Saves an SongUsage to the database - """ - log.debug(u'SongUsage added') - try: - self.session.add(songusageitem) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'SongUsage item failed to save') - return False - - def get_songusage(self, id=None): - """ - Returns the details of a SongUsage - """ - if id is None: - return SongUsageItem() - else: - return self.session.query(SongUsageItem).get(id) - - def delete_songusage(self, id): - """ - Delete a SongUsage record - """ - if id != 0: - songusageitem = self.get_songusage(id) - try: - self.session.delete(songusageitem) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'SongUsage Item failed to delete') - return False - else: - return True - - def delete_all(self): - """ - Delete all Song Usage records - """ - try: - self.session.query(SongUsageItem).delete(synchronize_session=False) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Failed to delete all Song Usage items') - return False + .order_by(SongUsageItem.usagedate, SongUsageItem.usagetime).all() def delete_to_date(self, date): """ Delete SongUsage records before given date """ try: - self.session.query(SongUsageItem)\ - .filter(SongUsageItem.usagedate <= date)\ + self.session.query(SongUsageItem) \ + .filter(SongUsageItem.usagedate <= date) \ .delete(synchronize_session=False) self.session.commit() return True @@ -146,4 +79,3 @@ class SongUsageManager(object): self.session.rollback() log.exception(u'Failed to delete all Song Usage items to %s' % date) return False - diff --git a/openlp/plugins/songusage/lib/meta.py b/openlp/plugins/songusage/lib/meta.py deleted file mode 100644 index affa31969..000000000 --- a/openlp/plugins/songusage/lib/meta.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from sqlalchemy import MetaData - -__all__ = ['session', 'metadata', 'engine'] - -# SQLAlchemy database engine. Updated by model.init_model() -engine = None - -# SQLAlchemy session manager. Updated by model.init_model() -session = None - -# Global metadata. If you have multiple databases with overlapping table -# names, you'll need a metadata for each database -metadata = MetaData() diff --git a/openlp/plugins/songusage/lib/tables.py b/openlp/plugins/songusage/lib/tables.py deleted file mode 100644 index 008c722b1..000000000 --- a/openlp/plugins/songusage/lib/tables.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # -# Thompson, Jon Tibble, Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -from sqlalchemy import Column, Table, types - -from openlp.plugins.songusage.lib.meta import metadata - -# Definition of the "songusage" table -songusage_table = Table(u'songusage_data', metadata, - Column(u'id', types.Integer(), primary_key=True), - Column(u'usagedate', types.Date, index=True, nullable=False), - Column(u'usagetime', types.Time, index=True, nullable=False), - Column(u'title', types.Unicode(255), nullable=False), - Column(u'authors', types.Unicode(255), nullable=False), - Column(u'copyright', types.Unicode(255)), - Column(u'ccl_number', types.Unicode(65)) -) diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index 743a333d3..a9994902a 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -23,16 +23,16 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -from datetime import datetime import logging +from datetime import datetime from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, Receiver, build_icon, translate from openlp.plugins.songusage.lib import SongUsageManager from openlp.plugins.songusage.forms import SongUsageDetailForm, \ SongUsageDeleteForm -from openlp.plugins.songusage.lib.models import SongUsageItem +from openlp.plugins.songusage.lib.db import SongUsageItem log = logging.getLogger(__name__) @@ -42,7 +42,7 @@ class SongUsagePlugin(Plugin): def __init__(self, plugin_helpers): Plugin.__init__(self, u'SongUsage', u'1.9.2', plugin_helpers) self.weight = -4 - self.icon = build_icon(u':/media/media_image.png') + self.icon = build_icon(u':/plugins/plugin_songusage.png') self.songusagemanager = None self.songusageActive = False @@ -76,7 +76,7 @@ class SongUsagePlugin(Plugin): translate('SongUsagePlugin', 'Generate report on Song Usage')) self.SongUsageReport.setObjectName(u'SongUsageReport') #SongUsage activation - SongUsageIcon = build_icon(u':/tools/tools_alert.png') + SongUsageIcon = build_icon(u':/plugins/plugin_songusage.png') self.SongUsageStatus = QtGui.QAction(tools_menu) self.SongUsageStatus.setIcon(SongUsageIcon) self.SongUsageStatus.setCheckable(True) @@ -148,7 +148,7 @@ class SongUsagePlugin(Plugin): song_usage_item.authors = u'' for author in audit[1]: song_usage_item.authors += author + u' ' - self.songusagemanager.insert_songusage(song_usage_item) + self.songusagemanager.save_object(song_usage_item) def onSongUsageDelete(self): self.SongUsagedeleteform.exec_() @@ -162,4 +162,4 @@ class SongUsagePlugin(Plugin): 'SongUsage Plugin
This plugin ' 'records the use of songs and when they have been used during ' 'a live service') - return about_text \ No newline at end of file + return about_text diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 49309b8d5..118a30eae 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -1,5 +1,5 @@ - + topic_edit.png author_add.png author_delete.png @@ -17,7 +17,18 @@ song_topic_edit.png song_book_edit.png - + + plugin_alerts.png + plugin_bibles.png + plugin_custom.png + plugin_images.png + plugin_media.png + plugin_presentations.png + plugin_songs.png + plugin_remote.png + plugin_songusage.png + + general_preview.png general_live.png general_add.png @@ -29,7 +40,7 @@ general_open.png general_save.png - + slide_close.png slide_first.png slide_last.png @@ -42,7 +53,7 @@ media_playback_stop.png media_playback_pause.png - + openlp-logo-16x16.png openlp-logo-32x32.png openlp-logo-48x48.png @@ -50,27 +61,27 @@ openlp-logo-128x128.png openlp-logo-256x256.png - + openlp-about-logo.png openlp-splash-screen.png - + import_selectall.png import_move_to_list.png import_remove.png import_load.png - + export_selectall.png export_remove.png export_load.png export_move_to_list.png - + wizard_importsong.bmp wizard_importbible.bmp - + service_notes.png service_item_notes.png service_bottom.png @@ -78,7 +89,7 @@ service_top.png service_up.png - + system_close.png system_about.png system_help_contents.png @@ -89,27 +100,21 @@ system_exit.png system_settings.png - - media_custom.png - media_presentation.png - media_image.png - media_song.png - media_bible.png - media_video.png + media_time.png media_stop.png image_clapperboard.png - + messagebox_critical.png messagebox_info.png messagebox_warning.png - + tools_add.png tools_alert.png - + theme_delete.png theme_new.png theme_edit.png diff --git a/resources/images/plugin_alerts.png b/resources/images/plugin_alerts.png new file mode 100644 index 000000000..331aa2687 Binary files /dev/null and b/resources/images/plugin_alerts.png differ diff --git a/resources/images/media_bible.png b/resources/images/plugin_bibles.png similarity index 100% rename from resources/images/media_bible.png rename to resources/images/plugin_bibles.png diff --git a/resources/images/media_custom.png b/resources/images/plugin_custom.png similarity index 100% rename from resources/images/media_custom.png rename to resources/images/plugin_custom.png diff --git a/resources/images/media_image.png b/resources/images/plugin_images.png similarity index 100% rename from resources/images/media_image.png rename to resources/images/plugin_images.png diff --git a/resources/images/media_video.png b/resources/images/plugin_media.png similarity index 100% rename from resources/images/media_video.png rename to resources/images/plugin_media.png diff --git a/resources/images/media_presentation.png b/resources/images/plugin_presentations.png similarity index 100% rename from resources/images/media_presentation.png rename to resources/images/plugin_presentations.png diff --git a/resources/images/plugin_remote.png b/resources/images/plugin_remote.png new file mode 100644 index 000000000..d70f0f6de Binary files /dev/null and b/resources/images/plugin_remote.png differ diff --git a/resources/images/media_song.png b/resources/images/plugin_songs.png similarity index 100% rename from resources/images/media_song.png rename to resources/images/plugin_songs.png diff --git a/resources/images/plugin_songusage.png b/resources/images/plugin_songusage.png new file mode 100644 index 000000000..bf110aa6d Binary files /dev/null and b/resources/images/plugin_songusage.png differ diff --git a/resources/images/song_maintenance.png b/resources/images/song_maintenance.png index 860e1717e..992e1b638 100644 Binary files a/resources/images/song_maintenance.png and b/resources/images/song_maintenance.png differ diff --git a/scripts/generate_resources.sh b/scripts/generate_resources.sh old mode 100644 new mode 100755