← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~trb143/openlp/bug-1097898 into lp:openlp

 

Tim Bentley has proposed merging lp:~trb143/openlp/bug-1097898 into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)
Related bugs:
  Bug #1097898 in OpenLP: "Exiting liveview with Alt-F4 causes OpenLP to become unstable"
  https://bugs.launchpad.net/openlp/+bug/1097898

For more details, see:
https://code.launchpad.net/~trb143/openlp/bug-1097898/+merge/144400

This code is for review but it is not ready for merging
It needs good testing as it changes the internals of the code.

Introduce the use of a Registry singleton and register components against it.
Add code the classes to use the registry instead of passing objects.
Clean up API's to remove passing objects when the Registry is done.

To Do.
Testing and code reviews

-- 
https://code.launchpad.net/~trb143/openlp/bug-1097898/+merge/144400
Your team OpenLP Core is requested to review the proposed merge of lp:~trb143/openlp/bug-1097898 into lp:openlp.
=== modified file 'openlp/core/__init__.py'
--- openlp/core/__init__.py	2012-12-29 20:56:56 +0000
+++ openlp/core/__init__.py	2013-01-22 22:01:19 +0000
@@ -43,7 +43,7 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import Receiver, Settings, check_directory_exists
+from openlp.core.lib import Receiver, Settings, check_directory_exists, Registry
 from openlp.core.lib.ui import UiStrings
 from openlp.core.resources import qInitResources
 from openlp.core.ui.mainwindow import MainWindow
@@ -288,6 +288,7 @@
         portable_settings.sync()
     else:
         app.setApplicationName(u'OpenLP')
+    registry = Registry.create()
     app.setApplicationVersion(get_application_version()[u'version'])
     # Instance check
     if not options.testing:

=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2013-01-02 11:26:21 +0000
+++ openlp/core/lib/__init__.py	2013-01-22 22:01:19 +0000
@@ -112,8 +112,7 @@
         Settings.__filePath__ = iniFile
 
     def __init__(self, *args):
-        if not args and Settings.__filePath__ and \
-            Settings.defaultFormat() == Settings.IniFormat:
+        if not args and Settings.__filePath__ and Settings.defaultFormat() == Settings.IniFormat:
             QtCore.QSettings.__init__(self, Settings.__filePath__, Settings.IniFormat)
         else:
             QtCore.QSettings.__init__(self, *args)
@@ -459,6 +458,7 @@
             u'Locale list separator: start') % (stringlist[0], merged)
 
 
+from registry import Registry
 from eventreceiver import Receiver
 from listwidgetwithdnd import ListWidgetWithDnD
 from formattingtags import FormattingTags

=== modified file 'openlp/core/lib/imagemanager.py'
--- openlp/core/lib/imagemanager.py	2012-12-29 20:56:56 +0000
+++ openlp/core/lib/imagemanager.py	2013-01-22 22:01:19 +0000
@@ -39,7 +39,7 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.lib import resize_image, image_to_byte, Receiver
+from openlp.core.lib import resize_image, image_to_byte, Receiver, Registry
 from openlp.core.ui import ScreenList
 
 log = logging.getLogger(__name__)
@@ -183,6 +183,7 @@
 
     def __init__(self):
         QtCore.QObject.__init__(self)
+        Registry().register(u'image_manager', self)
         currentScreen = ScreenList().current
         self.width = currentScreen[u'size'].width()
         self.height = currentScreen[u'size'].height()

=== modified file 'openlp/core/lib/pluginmanager.py'
--- openlp/core/lib/pluginmanager.py	2012-12-29 20:56:56 +0000
+++ openlp/core/lib/pluginmanager.py	2013-01-22 22:01:19 +0000
@@ -33,7 +33,7 @@
 import sys
 import logging
 
-from openlp.core.lib import Plugin, PluginStatus
+from openlp.core.lib import Plugin, PluginStatus, Registry
 
 log = logging.getLogger(__name__)
 
@@ -43,13 +43,6 @@
     and executes all the hooks, as and when necessary.
     """
     log.info(u'Plugin manager loaded')
-    __instance__ = None
-    @staticmethod
-    def get_instance():
-        """
-        Obtain a single instance of class.
-        """
-        return PluginManager.__instance__
 
     def __init__(self, plugin_dir):
         """
@@ -60,7 +53,7 @@
             The directory to search for plugins.
         """
         log.info(u'Plugin manager Initialising')
-        PluginManager.__instance__ = self
+        Registry().register(u'plugin_manager', self)
         if not plugin_dir in sys.path:
             log.debug(u'Inserting %s into sys.path', plugin_dir)
             sys.path.insert(0, plugin_dir)

=== added file 'openlp/core/lib/registry.py'
--- openlp/core/lib/registry.py	1970-01-01 00:00:00 +0000
+++ openlp/core/lib/registry.py	2013-01-22 22:01:19 +0000
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2013 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
+# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+Provide Registry Services
+"""
+import logging
+
+log = logging.getLogger(__name__)
+
+class Registry(object):
+    """
+    This is the Component Registry.  It is a singleton object and is used to provide a
+    look up service for common objects.
+    """
+    log.info(u'Registry loaded')
+    __instance__ = None
+
+    def __new__(cls):
+        if not cls.__instance__:
+            cls.__instance__ = object.__new__(cls)
+        return cls.__instance__
+
+    @classmethod
+    def create(self):
+        """
+        The constructor for the component registry providing a single registry of objects.
+        """
+        log.info(u'Registry Initialising')
+        self.service_list = {}
+
+    def get(self, key):
+        """
+        Extracts the registry value from the list based on the key passed in
+        """
+        if key in self.service_list:
+            return self.service_list[key]
+        else:
+            log.error(u'Service %s not found in list' % key)
+            return None
+
+    def register(self, key, reference):
+        """
+        Registers a component against a key.
+        """
+        if key in self.service_list:
+            log.error(u'Duplicate service exception %s' % key)
+            raise Exception(u'Duplicate service exception %s' % key)
+        else:
+            self.service_list[key] = reference

=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2012-12-29 20:56:56 +0000
+++ openlp/core/lib/renderer.py	2013-01-22 22:01:19 +0000
@@ -32,7 +32,7 @@
 from PyQt4 import QtGui, QtCore, QtWebKit
 
 from openlp.core.lib import ServiceItem, expand_tags, build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
-    ItemCapabilities, FormattingTags, ImageSource
+    ItemCapabilities, FormattingTags, ImageSource, Registry
 from openlp.core.lib.theme import ThemeLevel
 from openlp.core.ui import MainDisplay, ScreenList
 
@@ -56,7 +56,7 @@
     """
     log.info(u'Renderer Loaded')
 
-    def __init__(self, image_manager, theme_manager):
+    def __init__(self):
         """
         Initialise the renderer.
 
@@ -68,15 +68,14 @@
             The theme_manager instance, used to get the current theme details.
         """
         log.debug(u'Initialisation started')
-        self.theme_manager = theme_manager
-        self.image_manager = image_manager
         self.screens = ScreenList()
+        Registry().register(u'renderer', self)
         self.theme_level = ThemeLevel.Global
         self.global_theme_name = u''
         self.service_theme_name = u''
         self.item_theme_name = u''
         self.force_page = False
-        self.display = MainDisplay(None, self.image_manager, False, self)
+        self.display = MainDisplay(None, False, self)
         self.display.setup()
         self._theme_dimensions = {}
         self._calculate_default()
@@ -93,7 +92,7 @@
         self._calculate_default()
         if self.display:
             self.display.close()
-        self.display = MainDisplay(None, self.image_manager, False, self)
+        self.display = MainDisplay(None, False, self)
         self.display.setup()
         self._theme_dimensions = {}
 
@@ -235,7 +234,6 @@
             serviceItem.add_from_text(VERSE_FOR_LINE_COUNT)
         else:
             serviceItem.add_from_text(VERSE)
-        serviceItem.renderer = self
         serviceItem.raw_footer = FOOTER
         # if No file do not update cache
         if theme_data.background_filename:
@@ -643,3 +641,23 @@
         # this parse we are to be wordy
         line = line.replace(u'\n', u' ')
         return line.split(u' ')
+
+    def _get_image_manager(self):
+        """
+        Adds the image manager to the class dynamically
+        """
+        if not hasattr(self, u'_image_manager'):
+            self._image_manager = Registry().get(u'image_manager')
+        return self._image_manager
+
+    image_manager = property(_get_image_manager)
+
+    def _get_theme_manager(self):
+        """
+        Adds the theme manager to the class dynamically
+        """
+        if not hasattr(self, u'_theme_manager'):
+            self._theme_manager = Registry().get(u'theme_manager')
+        return self._theme_manager
+
+    theme_manager = property(_get_theme_manager)
\ No newline at end of file

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2013-01-21 06:45:00 +0000
+++ openlp/core/lib/serviceitem.py	2013-01-22 22:01:19 +0000
@@ -39,7 +39,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import build_icon, clean_tags, expand_tags, translate, ImageSource, Settings
+from openlp.core.lib import build_icon, clean_tags, expand_tags, translate, ImageSource, Settings, Registry
 
 log = logging.getLogger(__name__)
 
@@ -148,7 +148,6 @@
             The plugin that this service item belongs to.
         """
         if plugin:
-            self.renderer = plugin.renderer
             self.name = plugin.name
         self.title = u''
         self.shortname = u''
@@ -293,7 +292,7 @@
             self.image_border = background
         self.service_item_type = ServiceItemType.Image
         self._raw_frames.append({u'title': title, u'path': path})
-        self.renderer.image_manager.addImage(path, ImageSource.ImagePlugin, self.image_border)
+        self.image_manager.addImage(path, ImageSource.ImagePlugin, self.image_border)
         self._new_item()
 
     def add_from_text(self, raw_slide, verse_tag=None):
@@ -644,3 +643,23 @@
                     type = frame[u'title'].split(u'.')[-1]
                     if type.lower() not in suffix_list:
                         self.is_valid = False
+
+    def _get_renderer(self):
+        """
+        Adds the Renderer to the class dynamically
+        """
+        if not hasattr(self, u'_renderer'):
+            self._renderer = Registry().get(u'renderer')
+        return self._renderer
+
+    renderer = property(_get_renderer)
+
+    def _get_image_manager(self):
+        """
+        Adds the image manager to the class dynamically
+        """
+        if not hasattr(self, u'_image_manager'):
+            self._image_manager = Registry().get(u'image_manager')
+        return self._image_manager
+
+    image_manager = property(_get_image_manager)
\ No newline at end of file

=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py	2013-01-02 22:32:41 +0000
+++ openlp/core/ui/maindisplay.py	2013-01-22 22:01:19 +0000
@@ -38,8 +38,8 @@
 from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
 from PyQt4.phonon import Phonon
 
-from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, translate, PluginManager, expand_tags,\
-    Settings, ImageSource
+from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, translate, expand_tags,\
+    Settings, ImageSource, Registry
 from openlp.core.lib.theme import BackgroundType
 
 from openlp.core.ui import HideMode, ScreenList, AlertLocation
@@ -65,7 +65,6 @@
         self.isLive = live
         self.controller = controller
         self.screen = {}
-        self.plugins = PluginManager.get_instance().plugins
         # FIXME: On Mac OS X (tested on 10.7) the display screen is corrupt with
         # OpenGL. Only white blank screen is shown on the 2nd monitor all the
         # time. We need to investigate more how to use OpenGL properly on Mac OS
@@ -115,9 +114,8 @@
     """
     This is the display screen as a specialized class from the Display class
     """
-    def __init__(self, parent, imageManager, live, controller):
+    def __init__(self, parent, live, controller):
         Display.__init__(self, parent, live, controller)
-        self.imageManager = imageManager
         self.screens = ScreenList()
         self.rebuildCSS = False
         self.hideMode = None
@@ -182,8 +180,8 @@
         Call the plugins to rebuild the Live display CSS as the screen has
         not been rebuild on exit of config.
         """
-        if self.rebuildCSS and self.plugins:
-            for plugin in self.plugins:
+        if self.rebuildCSS and self.plugin_manager.plugins:
+            for plugin in self.plugin_manager.plugins:
                 plugin.refreshCss(self.frame)
         self.rebuildCSS = False
 
@@ -222,8 +220,8 @@
                 splash_image)
             serviceItem = ServiceItem()
             serviceItem.bg_image_bytes = image_to_byte(self.initialFrame)
-            self.webView.setHtml(build_html(serviceItem, self.screen,
-                self.isLive, None, plugins=self.plugins))
+            self.webView.setHtml(build_html(serviceItem, self.screen, self.isLive, None,
+                plugins=self.plugin_manager.plugins))
             self.__hideMouse()
         log.debug(u'Finished MainDisplay setup')
 
@@ -289,7 +287,7 @@
         """
         API for replacement backgrounds so Images are added directly to cache.
         """
-        self.imageManager.addImage(path, ImageSource.ImagePlugin, background)
+        self.image_manager.addImage(path, ImageSource.ImagePlugin, background)
         if not hasattr(self, u'serviceItem'):
             return False
         self.override[u'image'] = path
@@ -311,7 +309,7 @@
             re-added to the image manager.
         """
         log.debug(u'image to display')
-        image = self.imageManager.getImageBytes(path, ImageSource.ImagePlugin)
+        image = self.image_manager.getImageBytes(path, ImageSource.ImagePlugin)
         self.controller.mediaController.media_reset(self.controller)
         self.displayImage(image)
 
@@ -392,17 +390,18 @@
                 self.override = {}
             else:
                 # replace the background
-                background = self.imageManager.getImageBytes(self.override[u'image'], ImageSource.ImagePlugin)
+                background = self.image_manager.getImageBytes(self.override[u'image'], ImageSource.ImagePlugin)
         self.setTransparency(self.serviceItem.themedata.background_type ==
             BackgroundType.to_string(BackgroundType.Transparent))
         if self.serviceItem.themedata.background_filename:
-            self.serviceItem.bg_image_bytes = self.imageManager.getImageBytes(
+            self.serviceItem.bg_image_bytes = self.image_manager.getImageBytes(
                 self.serviceItem.themedata.background_filename,ImageSource.Theme)
         if image_path:
-            image_bytes = self.imageManager.getImageBytes(image_path, ImageSource.ImagePlugin)
+            image_bytes = self.image_manager.getImageBytes(image_path, ImageSource.ImagePlugin)
         else:
             image_bytes = None
-        html = build_html(self.serviceItem, self.screen, self.isLive, background, image_bytes, self.plugins)
+        html = build_html(self.serviceItem, self.screen, self.isLive, background, image_bytes,
+            plugins=self.plugin_manager.plugins)
         log.debug(u'buildHtml - pre setHtml')
         self.webView.setHtml(html)
         log.debug(u'buildHtml - post setHtml')
@@ -477,6 +476,26 @@
             self.setCursor(QtCore.Qt.ArrowCursor)
             self.frame.evaluateJavaScript('document.body.style.cursor = "auto"')
 
+    def _get_plugin_manager(self):
+        """
+        Adds the Renderer to the class dynamically
+        """
+        if not hasattr(self, u'_plugin_manager'):
+            self._plugin_manager = Registry().get(u'plugin_manager')
+        return self._plugin_manager
+
+    plugin_manager = property(_get_plugin_manager)
+
+    def _get_image_manager(self):
+        """
+        Adds the image manager to the class dynamically
+        """
+        if not hasattr(self, u'_image_manager'):
+            self._image_manager = Registry().get(u'image_manager')
+        return self._image_manager
+
+    image_manager = property(_get_image_manager)
+
 
 class AudioPlayer(QtCore.QObject):
     """
@@ -600,3 +619,4 @@
     #@todo is this used?
     def connectSlot(self, signal, slot):
         QtCore.QObject.connect(self.mediaObject, signal, slot)
+

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2013-01-02 22:32:41 +0000
+++ openlp/core/ui/mainwindow.py	2013-01-22 22:01:19 +0000
@@ -542,7 +542,7 @@
         # warning cyclic dependency
         # renderer needs to call ThemeManager and
         # ThemeManager needs to call Renderer
-        self.renderer = Renderer(self.imageManager, self.themeManagerContents)
+        self.renderer = Renderer()
         # Define the media Dock Manager
         self.mediaDockManager = MediaDockManager(self.mediaToolBox)
         log.info(u'Load Plugins')

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2013-01-21 06:45:00 +0000
+++ openlp/core/ui/servicemanager.py	2013-01-22 22:01:19 +0000
@@ -40,7 +40,7 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.lib import OpenLPToolbar, ServiceItem, Receiver, build_icon, ItemCapabilities, SettingsManager, \
-    translate, str_to_bool, check_directory_exists, Settings, PluginStatus
+    translate, str_to_bool, check_directory_exists, Settings, PluginStatus, Registry
 from openlp.core.lib.theme import ThemeLevel
 from openlp.core.lib.ui import UiStrings, critical_error_message_box, create_widget_action, find_and_set_in_combo_box
 from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
@@ -313,8 +313,7 @@
         Setter for service file.
         """
         self._fileName = unicode(fileName)
-        self.mainwindow.setServiceModified(self.isModified(),
-            self.shortFileName())
+        self.mainwindow.setServiceModified(self.isModified(), self.shortFileName())
         Settings().setValue(u'servicemanager/last file', fileName)
         self._saveLite = self._fileName.endswith(u'.oszl')
 
@@ -384,8 +383,8 @@
         if not loadFile:
             fileName = QtGui.QFileDialog.getOpenFileName(self.mainwindow,
                 translate('OpenLP.ServiceManager', 'Open File'),
-                SettingsManager.get_last_dir(self.mainwindow.serviceManagerSettingsSection),
-                translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz *.oszl)'))
+                    SettingsManager.get_last_dir(self.mainwindow.serviceManagerSettingsSection),
+                    translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz *.oszl)'))
             if not fileName:
                 return False
         else:
@@ -703,7 +702,6 @@
                 for item in items:
                     self.mainwindow.incrementProgressBar()
                     serviceItem = ServiceItem()
-                    serviceItem.renderer = self.mainwindow.renderer
                     if self._saveLite:
                         serviceItem.set_from_service(item)
                     else:
@@ -806,13 +804,13 @@
                 self.autoStartAction.setText(translate('OpenLP.ServiceManager', '&Auto Start - active'))
                 self.autoStartAction.setIcon(self.active)
         if serviceItem[u'service_item'].is_text():
-            for plugin in self.mainwindow.pluginManager.plugins:
+            for plugin in self.plugin_manager.plugins:
                 if plugin.name == u'custom' and plugin.status == PluginStatus.Active:
                     self.create_custom_action.setVisible(True)
                     break
         self.themeMenu.menuAction().setVisible(False)
         # Set up the theme menu.
-        if serviceItem[u'service_item'].is_text() and self.mainwindow.renderer.theme_level == ThemeLevel.Song:
+        if serviceItem[u'service_item'].is_text() and self.renderer.theme_level == ThemeLevel.Song:
             self.themeMenu.menuAction().setVisible(True)
             # The service item does not have a theme, check the "Default".
             if serviceItem[u'service_item'].theme is None:
@@ -1197,7 +1195,7 @@
         """
         log.debug(u'onThemeComboBoxSelected')
         self.service_theme = self.themeComboBox.currentText()
-        self.mainwindow.renderer.set_service_theme(self.service_theme)
+        self.renderer.set_service_theme(self.service_theme)
         Settings().setValue(self.mainwindow.serviceManagerSettingsSection + u'/service theme', self.service_theme)
         self.regenerateServiceItems(True)
 
@@ -1207,7 +1205,7 @@
         sure the theme combo box is in the correct state.
         """
         log.debug(u'themeChange')
-        visible = self.mainwindow.renderer.theme_level == ThemeLevel.Global
+        visible = self.renderer.theme_level == ThemeLevel.Global
         self.themeLabel.setVisible(visible)
         self.themeComboBox.setVisible(visible)
 
@@ -1270,7 +1268,7 @@
                 newItem.merge(item[u'service_item'])
                 item[u'service_item'] = newItem
                 self.repaintServiceList(itemcount + 1, 0)
-                self.mainwindow.liveController.replaceServiceManagerItem(newItem)
+                self.live_controller.replaceServiceManagerItem(newItem)
                 self.setModified()
 
     def addServiceItem(self, item, rebuild=False, expand=None, replace=False, repaint=True, selected=False):
@@ -1292,7 +1290,7 @@
             item.merge(self.serviceItems[sitem][u'service_item'])
             self.serviceItems[sitem][u'service_item'] = item
             self.repaintServiceList(sitem, child)
-            self.mainwindow.liveController.replaceServiceManagerItem(item)
+            self.live_controller.replaceServiceManagerItem(item)
         else:
             item.render()
             # nothing selected for dnd
@@ -1315,7 +1313,7 @@
                 self.repaintServiceList(self.dropPosition, -1)
             # if rebuilding list make sure live is fixed.
             if rebuild:
-                self.mainwindow.liveController.replaceServiceManagerItem(item)
+                self.live_controller.replaceServiceManagerItem(item)
         self.dropPosition = 0
         self.setModified()
 
@@ -1326,8 +1324,7 @@
         Receiver.send_message(u'cursor_busy')
         item, child = self.findServiceItem()
         if self.serviceItems[item][u'service_item'].is_valid:
-            self.mainwindow.previewController.addServiceManagerItem(
-                self.serviceItems[item][u'service_item'], child)
+            self.preview_controller.addServiceManagerItem(self.serviceItems[item][u'service_item'], child)
         else:
             critical_error_message_box(translate('OpenLP.ServiceManager', 'Missing Display Handler'),
                 translate('OpenLP.ServiceManager',
@@ -1367,16 +1364,15 @@
             child = row
         Receiver.send_message(u'cursor_busy')
         if self.serviceItems[item][u'service_item'].is_valid:
-            self.mainwindow.liveController.addServiceManagerItem(
-                self.serviceItems[item][u'service_item'], child)
+            self.live_controller.addServiceManagerItem(self.serviceItems[item][u'service_item'], child)
             if Settings().value(self.mainwindow.generalSettingsSection + u'/auto preview', False):
                 item += 1
                 if self.serviceItems and item < len(self.serviceItems) and \
                         self.serviceItems[item][u'service_item'].is_capable(ItemCapabilities.CanPreview):
-                    self.mainwindow.previewController.addServiceManagerItem(self.serviceItems[item][u'service_item'], 0)
+                    self.preview_controller.addServiceManagerItem(self.serviceItems[item][u'service_item'], 0)
                     next_item = self.serviceManagerList.topLevelItem(item)
                     self.serviceManagerList.setCurrentItem(next_item)
-                    self.mainwindow.liveController.previewListWidget.setFocus()
+                    self.live_controller.previewListWidget.setFocus()
         else:
             critical_error_message_box(translate('OpenLP.ServiceManager', 'Missing Display Handler'),
                 translate('OpenLP.ServiceManager',
@@ -1520,7 +1516,7 @@
             themeGroup.addAction(create_widget_action(self.themeMenu, theme, text=theme, checked=False,
                 triggers=self.onThemeChangeAction))
         find_and_set_in_combo_box(self.themeComboBox, self.service_theme)
-        self.mainwindow.renderer.set_service_theme(self.service_theme)
+        self.renderer.set_service_theme(self.service_theme)
         self.regenerateServiceItems()
 
     def onThemeChangeAction(self):
@@ -1545,3 +1541,43 @@
         """
         settingDialog = PrintServiceForm(self.mainwindow, self)
         settingDialog.exec_()
+
+    def _get_renderer(self):
+        """
+        Adds the Renderer to the class dynamically
+        """
+        if not hasattr(self, u'_renderer'):
+            self._renderer = Registry().get(u'renderer')
+        return self._renderer
+
+    renderer = property(_get_renderer)
+
+    def _get_live_controller(self):
+        """
+        Adds the live controller to the class dynamically
+        """
+        if not hasattr(self, u'_live_controller'):
+            self._live_controller = Registry().get(u'live_controller')
+        return self._live_controller
+
+    live_controller = property(_get_live_controller)
+
+    def _get_preview_controller(self):
+        """
+        Adds the preview controller to the class dynamically
+        """
+        if not hasattr(self, u'_preview_controller'):
+            self._preview_controller = Registry().get(u'preview_controller')
+        return self._preview_controller
+
+    preview_controller = property(_get_preview_controller)
+
+    def _get_plugin_manager(self):
+        """
+        Adds the plugin manager to the class dynamically
+        """
+        if not hasattr(self, u'_plugin_manager'):
+            self._plugin_manager = Registry().get(u'plugin_manager')
+        return self._plugin_manager
+
+    plugin_manager = property(_get_plugin_manager)
\ No newline at end of file

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2013-01-21 06:45:00 +0000
+++ openlp/core/ui/slidecontroller.py	2013-01-22 22:01:19 +0000
@@ -34,14 +34,10 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, \
-    translate, build_icon, build_html, PluginManager, ServiceItem, \
-    ImageSource, SlideLimits, ServiceItemAction, Settings
-from openlp.core.ui import HideMode, MainDisplay, Display, ScreenList
+from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, translate, build_icon, build_html, \
+    ServiceItem, ImageSource, SlideLimits, ServiceItemAction, Settings, Registry
+from openlp.core.ui import HideMode, MainDisplay, Display, ScreenList, DisplayControllerType
 from openlp.core.lib.ui import UiStrings, create_action
-from openlp.core.lib import SlideLimits, ServiceItemAction
-from openlp.core.ui import HideMode, MainDisplay, Display, ScreenList, \
-    DisplayControllerType
 from openlp.core.utils.actions import ActionList, CategoryOrder
 
 log = logging.getLogger(__name__)
@@ -113,6 +109,7 @@
         # Type label for the top of the slide controller
         self.typeLabel = QtGui.QLabel(self.panel)
         if self.isLive:
+            Registry().register(u'live_controller', self)
             self.typeLabel.setText(UiStrings().Live)
             self.split = 1
             self.typePrefix = u'live'
@@ -121,6 +118,7 @@
             self.category = UiStrings().LiveToolbar
             ActionList.get_instance().add_category(unicode(self.category), CategoryOrder.standardToolbar)
         else:
+            Registry().register(u'preview_controller', self)
             self.typeLabel.setText(UiStrings().Preview)
             self.split = 0
             self.typePrefix = u'preview'
@@ -510,7 +508,7 @@
         # rebuild display as screen size changed
         if self.display:
             self.display.close()
-        self.display = MainDisplay(self, self.imageManager, self.isLive, self)
+        self.display = MainDisplay(self, self.isLive, self)
         self.display.setup()
         if self.isLive:
             self.__addActionsToWidget(self.display)
@@ -525,7 +523,7 @@
         self.previewDisplay.setup()
         serviceItem = ServiceItem()
         self.previewDisplay.webView.setHtml(build_html(serviceItem, self.previewDisplay.screen, None, self.isLive,
-            plugins=PluginManager.get_instance().plugins))
+            plugins=self.plugin_manager.plugins))
         self.mediaController.setup_display(self.previewDisplay,True)
         if self.serviceItem:
             self.refreshServiceItem()
@@ -1283,3 +1281,13 @@
     def onTrackTriggered(self):
         action = self.sender()
         self.display.audioPlayer.goTo(action.data())
+
+    def _get_plugin_manager(self):
+        """
+        Adds the plugin manager to the class dynamically
+        """
+        if not hasattr(self, u'_plugin_manager'):
+            self._plugin_manager = Registry().get(u'plugin_manager')
+        return self._plugin_manager
+
+    plugin_manager = property(_get_plugin_manager)
\ No newline at end of file

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2012-12-29 20:56:56 +0000
+++ openlp/core/ui/thememanager.py	2013-01-22 22:01:19 +0000
@@ -37,7 +37,7 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, Receiver, SettingsManager, translate, \
-    check_item_selected, check_directory_exists, create_thumb, validate_thumb, ImageSource, Settings
+    check_item_selected, check_directory_exists, create_thumb, validate_thumb, ImageSource, Settings, Registry
 from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, BackgroundGradientType
 from openlp.core.lib.ui import UiStrings, critical_error_message_box, create_widget_action
 from openlp.core.theme import Theme
@@ -52,6 +52,7 @@
     """
     def __init__(self, mainwindow, parent=None):
         QtGui.QWidget.__init__(self, parent)
+        Registry().register(u'theme_manager', self)
         self.mainwindow = mainwindow
         self.settingsSection = u'themes'
         self.themeForm = ThemeForm(self)
@@ -261,11 +262,10 @@
                     old_theme_data = self.getThemeData(old_theme_name)
                     self.cloneThemeData(old_theme_data, new_theme_name)
                     self.deleteTheme(old_theme_name)
-                    for plugin in self.mainwindow.pluginManager.plugins:
+                    for plugin in self.plugin_manager.plugins:
                         if plugin.usesTheme(old_theme_name):
                             plugin.renameTheme(old_theme_name, new_theme_name)
-                    self.mainwindow.renderer.update_theme(
-                        new_theme_name, old_theme_name)
+                    self.renderer.update_theme(new_theme_name, old_theme_name)
                     self.loadThemes()
 
     def onCopyTheme(self):
@@ -312,7 +312,7 @@
             self.themeForm.theme = theme
             self.themeForm.exec_(True)
             self.oldBackgroundImage = None
-            self.mainwindow.renderer.update_theme(theme.theme_name)
+            self.renderer.update_theme(theme.theme_name)
             self.loadThemes()
 
     def onDeleteTheme(self):
@@ -327,7 +327,7 @@
             row = self.themeListWidget.row(item)
             self.themeListWidget.takeItem(row)
             self.deleteTheme(theme)
-            self.mainwindow.renderer.update_theme(theme, only_delete=True)
+            self.renderer.update_theme(theme, only_delete=True)
             # As we do not reload the themes, push out the change. Reload the
             # list as the internal lists and events need to be triggered.
             self._pushThemes()
@@ -631,9 +631,9 @@
         """
         self._writeTheme(theme, image_from, image_to)
         if theme.background_type == BackgroundType.to_string(BackgroundType.Image):
-            self.mainwindow.imageManager.updateImageBorder(theme.background_filename,
+            self.image_manager.updateImageBorder(theme.background_filename,
                 ImageSource.Theme, QtGui.QColor(theme.background_border_color))
-            self.mainwindow.imageManager.processUpdates()
+            self.image_manager.processUpdates()
 
     def _writeTheme(self, theme, image_from, image_to):
         """
@@ -698,7 +698,7 @@
             Flag to tell message lines per page need to be generated.
         """
         log.debug(u'generateImage \n%s ', theme_data)
-        return self.mainwindow.renderer.generate_preview(theme_data, forcePage)
+        return self.renderer.generate_preview(theme_data, forcePage)
 
     def getPreviewImage(self, theme):
         """
@@ -748,7 +748,7 @@
                 return False
             # check for use in the system else where.
             if testPlugin:
-                for plugin in self.mainwindow.pluginManager.plugins:
+                for plugin in self.plugin_manager.plugins:
                     if plugin.usesTheme(theme):
                         critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
                             translate('OpenLP.ThemeManager', 'Theme %s is used in the %s plugin.') %
@@ -806,3 +806,32 @@
         new_theme.display_vertical_align = vAlignCorrection
         return new_theme.extract_xml()
 
+    def _get_renderer(self):
+        """
+        Adds the Renderer to the class dynamically
+        """
+        if not hasattr(self, u'_renderer'):
+            self._renderer = Registry().get(u'renderer')
+        return self._renderer
+
+    renderer = property(_get_renderer)
+
+    def _get_image_manager(self):
+        """
+        Adds the image manager to the class dynamically
+        """
+        if not hasattr(self, u'_image_manager'):
+            self._image_manager = Registry().get(u'image_manager')
+        return self._image_manager
+
+    image_manager = property(_get_image_manager)
+
+    def _get_plugin_manager(self):
+        """
+        Adds the Renderer to the class dynamically
+        """
+        if not hasattr(self, u'_plugin_manager'):
+            self._plugin_manager = Registry().get(u'plugin_manager')
+        return self._plugin_manager
+
+    plugin_manager = property(_get_plugin_manager)
\ No newline at end of file

=== added file 'tests/functional/openlp_core_lib/test_registry.py'
--- tests/functional/openlp_core_lib/test_registry.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_core_lib/test_registry.py	2013-01-22 22:01:19 +0000
@@ -0,0 +1,33 @@
+"""
+    Package to test the openlp.core.lib package.
+"""
+import os
+
+from unittest import TestCase
+from mock import MagicMock
+from openlp.core.lib import ServiceItem, Registry
+
+TESTPATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
+
+class TestServiceItem(TestCase):
+
+    def registry_basic_test(self):
+        """
+        Test the Service Item basic test
+        """
+        # GIVEN: A new registry
+        registry = Registry.create()
+
+        # WHEN:A service item is created (without a plugin)
+        mock_1 = MagicMock()
+        Registry().register(u'test1', mock_1)
+
+        # THEN: we should be able retrieve the saved object
+        assert Registry().get(u'test1') == mock_1, u'The saved object can be retrieved'
+        #assert service_item.missing_frames() is True, u'There should not be any frames in the service item'
+
+        # THEN: We should get back a valid service item
+        try:
+            assert Registry().get(u'test2') == mock_1, u'This should not be fired'
+        except Exception, e:
+            pass
\ No newline at end of file

=== modified file 'tests/functional/openlp_core_lib/test_serviceitem.py'
--- tests/functional/openlp_core_lib/test_serviceitem.py	2013-01-20 20:53:58 +0000
+++ tests/functional/openlp_core_lib/test_serviceitem.py	2013-01-22 22:01:19 +0000
@@ -5,7 +5,7 @@
 
 from unittest import TestCase
 from mock import MagicMock
-from openlp.core.lib import ServiceItem
+from openlp.core.lib import ServiceItem, Registry
 
 VERSE = u'The Lord said to {r}Noah{/r}: \n'\
         'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\
@@ -20,6 +20,17 @@
 
 class TestServiceItem(TestCase):
 
+    def setUp(self):
+        """
+        Set up the Registry
+        """
+        registry = Registry.create()
+        mocked_renderer =  MagicMock()
+        mocked_image_manager =  MagicMock()
+        mocked_renderer.format_slide.return_value = [VERSE]
+        Registry().register(u'renderer', mocked_renderer)
+        Registry().register(u'image_manager', mocked_image_manager)
+
     def serviceitem_basic_test(self):
         """
         Test the Service Item basic test
@@ -48,11 +59,6 @@
         assert service_item.is_valid is True, u'The new service item should be valid'
         assert service_item.missing_frames() is False, u'check frames loaded '
 
-        # GIVEN: A service item with text
-        mocked_renderer =  MagicMock()
-        mocked_renderer.format_slide.return_value = [VERSE]
-        service_item.renderer = mocked_renderer
-
         # WHEN: Render called
         assert len(service_item._display_frames) == 0, u'A blank Service Item with no display frames'
         service_item.render(True)
@@ -68,8 +74,6 @@
         # GIVEN: A new service item and a mocked renderer
         service_item = ServiceItem(None)
         service_item.name = u'test'
-        mocked_renderer =  MagicMock()
-        service_item.renderer = mocked_renderer
 
         # WHEN: adding image to a service item
         test_image = os.path.join(TESTPATH, u'church.jpg')
@@ -125,8 +129,6 @@
         # GIVEN: A new service item and a mocked renderer
         service_item = ServiceItem(None)
         service_item.name = u'test'
-        mocked_renderer =  MagicMock()
-        service_item.renderer = mocked_renderer
 
         # WHEN: adding image to a service item
         test_file = os.path.join(TESTPATH, u'church.jpg')


Follow ups