← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~trb143/openlp/refactor1 into lp:openlp

 

Tim Bentley has proposed merging lp:~trb143/openlp/refactor1 into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~trb143/openlp/refactor1/+merge/202009

Fix errors from previous merge

More Cleanups , Renderer and MainDisplay updated and basic tests added!

PEP8 changes to Alerts and SongUsage.
-- 
https://code.launchpad.net/~trb143/openlp/refactor1/+merge/202009
Your team OpenLP Core is requested to review the proposed merge of lp:~trb143/openlp/refactor1 into lp:openlp.
=== modified file 'openlp/core/__init__.py'
--- openlp/core/__init__.py	2013-12-30 19:50:34 +0000
+++ openlp/core/__init__.py	2014-01-16 22:00:15 +0000
@@ -43,7 +43,7 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Registry, AppLocation, Settings, UiStrings, check_directory_exists
+from openlp.core.common import Registry, OpenLPMixin, AppLocation, Settings, UiStrings, check_directory_exists
 from openlp.core.lib import ScreenList
 from openlp.core.resources import qInitResources
 from openlp.core.ui.mainwindow import MainWindow
@@ -58,6 +58,7 @@
 
 
 log = logging.getLogger()
+
 NT_REPAIR_STYLESHEET = """
 QMainWindow::separator
 {
@@ -81,7 +82,7 @@
 """
 
 
-class OpenLP(QtGui.QApplication):
+class OpenLP(OpenLPMixin, QtGui.QApplication):
     """
     The core application class. This class inherits from Qt's QApplication
     class in order to provide the core of the application.
@@ -101,6 +102,8 @@
     def run(self, args):
         """
         Run the OpenLP application.
+
+        :param args: Some Args
         """
         self.is_event_loop_active = False
         # On Windows, the args passed into the constructor are ignored. Not very handy, so set the ones we want to use.
@@ -172,25 +175,20 @@
             self.shared_memory.create(1)
             return False
 
-    def hook_exception(self, exctype, value, traceback):
+    def hook_exception(self, exc_type, value, traceback):
         """
         Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where
         users cannot see it and cannot report when we encounter these problems.
 
-        ``exctype``
-            The class of exception.
-
-        ``value``
-            The actual exception object.
-
-        ``traceback``
-            A traceback object with the details of where the exception occurred.
+        :param exc_type: The class of exception.
+        :param value: The actual exception object.
+        :param traceback: A traceback object with the details of where the exception occurred.
         """
         # We can't log.exception here because the last exception no longer exists, we're actually busy handling it.
-        log.critical(''.join(format_exception(exctype, value, traceback)))
+        log.critical(''.join(format_exception(exc_type, value, traceback)))
         if not hasattr(self, 'exception_form'):
             self.exception_form = ExceptionForm()
-        self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exctype, value, traceback)))
+        self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exc_type, value, traceback)))
         self.set_normal_cursor()
         self.exception_form.exec_()
 
@@ -198,7 +196,6 @@
         """
         Wrapper to make ProcessEvents visible and named correctly
         """
-        log.debug('processing event flush')
         self.processEvents()
 
     def set_busy_cursor(self):
@@ -218,6 +215,8 @@
     def event(self, event):
         """
         Enables direct file opening on OS X
+
+        :param event: The event
         """
         if event.type() == QtCore.QEvent.FileOpen:
             file_name = event.file()
@@ -231,6 +230,8 @@
 def set_up_logging(log_path):
     """
     Setup our logging using log_path
+
+    :param log_path: the path
     """
     check_directory_exists(log_path, True)
     filename = os.path.join(log_path, 'openlp.log')
@@ -244,7 +245,8 @@
 def main(args=None):
     """
     The main function which parses command line options and then runs
-    the PyQt4 Application.
+
+    :param args: Some args
     """
     # Set up command line options.
     usage = 'Usage: %prog [options] [qt-options]'

=== modified file 'openlp/core/common/__init__.py'
--- openlp/core/common/__init__.py	2013-12-29 20:13:58 +0000
+++ openlp/core/common/__init__.py	2014-01-16 22:00:15 +0000
@@ -38,7 +38,7 @@
 
 from PyQt4 import QtCore
 
-log = logging.getLogger(__name__)
+log = logging.getLogger(__name__+'.__init__')
 
 
 FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')

=== modified file 'openlp/core/common/openlpmixin.py'
--- openlp/core/common/openlpmixin.py	2013-12-30 19:50:34 +0000
+++ openlp/core/common/openlpmixin.py	2014-01-16 22:00:15 +0000
@@ -33,7 +33,8 @@
 import inspect
 
 from openlp.core.common import trace_error_handler
-DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent', 'drag_enter_event', 'drop_event']
+DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent', 'drag_enter_event', 'drop_event', 'on_controller_size_changed',
+                       'preview_size_changed', 'resizeEvent']
 
 
 class OpenLPMixin(object):

=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2014-01-01 09:33:07 +0000
+++ openlp/core/lib/__init__.py	2014-01-16 22:00:15 +0000
@@ -39,7 +39,7 @@
 
 from openlp.core.common import translate
 
-log = logging.getLogger(__name__)
+log = logging.getLogger(__name__+'.__init__')
 
 
 class ServiceItemContext(object):

=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py	2013-12-28 21:33:38 +0000
+++ openlp/core/lib/mediamanageritem.py	2014-01-16 22:00:15 +0000
@@ -332,8 +332,7 @@
         """
         Turn file from Drag and Drop into an array so the Validate code can run it.
 
-        ``data``
-            A dictionary containing the list of files to be loaded and the target
+        :param data: A dictionary containing the list of files to be loaded and the target
         """
         new_files = []
         error_shown = False
@@ -353,9 +352,8 @@
     def dnd_move_internal(self, target):
         """
         Handle internal moving of media manager items
-
-        ``target``
-            The target of the DnD action
+s
+        :param target: The target of the DnD action
         """
         pass
 
@@ -364,11 +362,8 @@
         Process a list for files either from the File Dialog or from Drag and
         Drop
 
-        ``files``
-            The files to be loaded.
-
-        ``target_group``
-            The QTreeWidgetItem of the group that will be the parent of the added files
+        :param files: The files to be loaded.
+        :param target_group: The QTreeWidgetItem of the group that will be the parent of the added files
         """
         names = []
         full_list = []
@@ -399,6 +394,8 @@
     def context_menu(self, point):
         """
         Display a context menu
+
+        :param point: The point the cursor was at
         """
         item = self.list_view.itemAt(point)
         # Decide if we have to show the context menu or not.
@@ -422,6 +419,9 @@
     def load_list(self, load_list, target_group):
         """
         Load a list. Needs to be implemented by the plugin.
+
+        :param load_list: List object to load
+        :param target_group: Group to load
         """
         raise NotImplementedError('MediaManagerItem.loadList needs to be defined by the plugin')
 
@@ -454,6 +454,11 @@
                             context=ServiceItemContext.Live):
         """
         Generate the slide data. Needs to be implemented by the plugin.
+        :param service_item: The service Item to be processed
+        :param item: The database item to be used to build the service item
+        :param xml_version:
+        :param remote: Was this remote triggered (False)
+        :param context: The service context
         """
         raise NotImplementedError('MediaManagerItem.generate_slide_data needs to be defined by the plugin')
 
@@ -471,12 +476,14 @@
         Allows the change of current item in the list to be actioned
         """
         if Settings().value('advanced/single click preview') and self.quick_preview_allowed \
-            and self.list_view.selectedIndexes() and self.auto_select_id == -1:
+                and self.list_view.selectedIndexes() and self.auto_select_id == -1:
             self.on_preview_click(True)
 
     def on_preview_click(self, keep_focus=False):
         """
         Preview an item by building a service item then adding that service item to the preview slide controller.
+
+        :param keep_focus: Do we keep focus (False)
         """
         if not self.list_view.selectedIndexes() and not self.remote_triggered:
             QtGui.QMessageBox.information(self, UiStrings().NISp,
@@ -506,14 +513,16 @@
         """
         Remote Call wrapper
 
-        ``message``
-            The passed data item_id:Remote.
+        :param message: The passed data item_id:Remote.
         """
         self.go_live(message[0], remote=message[1])
 
     def go_live(self, item_id=None, remote=False):
         """
         Make the currently selected item go live.
+
+        :param item_id: item to make live
+        :param remote: From Remote
         """
         log.debug('%s Live requested', self.plugin.name)
         item = None
@@ -530,6 +539,8 @@
     def create_item_from_id(self, item_id):
         """
         Create a media item from an item id.
+
+        :param item_id: Id to make live
         """
         item = QtGui.QListWidgetItem()
         item.setData(QtCore.Qt.UserRole, item_id)
@@ -558,14 +569,17 @@
         """
         Remote Call wrapper
 
-        ``message``
-            The passed data item:Remote.
+        :param message: The passed data item:Remote.
         """
         self.add_to_service(message[0], remote=message[1])
 
     def add_to_service(self, item=None, replace=None, remote=False):
         """
         Add this item to the current service.
+
+        :param item: Item to be processed
+        :param replace: Replace the existing item
+        :param remote: Triggered from remote
         """
         service_item = self.build_service_item(item, True, remote=remote, context=ServiceItemContext.Service)
         if service_item:
@@ -598,6 +612,10 @@
     def build_service_item(self, item=None, xml_version=False, remote=False, context=ServiceItemContext.Live):
         """
         Common method for generating a service item
+        :param item: Service Item to be built.
+        :param xml_version: version of XML (False)
+        :param remote: Remote triggered (False)
+        :param context: The context on which this is called
         """
         service_item = ServiceItem(self.plugin)
         service_item.add_icon(self.plugin.icon_path)
@@ -611,8 +629,7 @@
         Method to add processing when a service has been loaded and individual service items need to be processed by the
         plugins.
 
-        ``item``
-            The item to be processed and returned.
+        :param item: The item to be processed and returned.
         """
         return item
 
@@ -634,11 +651,8 @@
         """
         Utility method to check items being submitted for slide generation.
 
-        ``item``
-            The item to check.
-
-        ``remote_item``
-            The id to assign if the slide generation was remotely triggered.
+        :param item: The item to check.
+        :param remote_item: The id to assign if the slide generation was remotely triggered.
         """
         if item is None:
             if self.remote_triggered is None:
@@ -665,6 +679,9 @@
     def search(self, string, show_error=True):
         """
         Performs a plugin specific search for items containing ``string``
+
+        :param string: String to be displayed
+        :param show_error: Should the error be shown (True)
         """
         raise NotImplementedError('Plugin.search needs to be defined by the plugin')
 

=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2013-12-28 21:33:38 +0000
+++ openlp/core/lib/renderer.py	2014-01-16 22:00:15 +0000
@@ -27,18 +27,15 @@
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
 
-import logging
 
 from PyQt4 import QtGui, QtCore, QtWebKit
 
-from openlp.core.common import Registry, Settings
+from openlp.core.common import Registry, OpenLPMixin, RegistryMixin, Settings
 from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \
     build_lyrics_format_css, build_lyrics_outline_css
 from openlp.core.common import ThemeLevel
 from openlp.core.ui import MainDisplay
 
-log = logging.getLogger(__name__)
-
 VERSE = 'The Lord said to {r}Noah{/r}: \n' \
     'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
     'The Lord said to {g}Noah{/g}:\n' \
@@ -50,43 +47,47 @@
 FOOTER = ['Arky Arky (Unknown)', 'Public Domain', 'CCLI 123456']
 
 
-class Renderer(object):
+class Renderer(OpenLPMixin, RegistryMixin):
     """
     Class to pull all Renderer interactions into one place. The plugins will call helper methods to do the rendering but
     this class will provide display defense code.
     """
-    log.info('Renderer Loaded')
 
     def __init__(self):
         """
         Initialise the renderer.
         """
-        log.debug('Initialisation started')
+        super(Renderer, self).__init__(None)
+        # Need live behaviour if this is also working as a pseudo MainDisplay.
+        self.is_live = True
         self.screens = ScreenList()
-        Registry().register('renderer', self)
         self.theme_level = ThemeLevel.Global
         self.global_theme_name = ''
         self.service_theme_name = ''
         self.item_theme_name = ''
         self.force_page = False
-        self.display = MainDisplay(None, False, self)
-        self.display.setup()
         self._theme_dimensions = {}
         self._calculate_default()
+        self.web = QtWebKit.QWebView()
+        self.web.setVisible(False)
+        self.web_frame = self.web.page().mainFrame()
         Registry().register_function('theme_update_global', self.set_global_theme)
-        self.web = QtWebKit.QWebView()
-        self.web.setVisible(False)
-        self.web_frame = self.web.page().mainFrame()
+
+    def bootstrap_initialise(self):
+        """
+        Initialise functions
+        """
+        self.display = MainDisplay(self)
+        self.display.setup()
 
     def update_display(self):
         """
         Updates the renderer's information about the current screen.
         """
-        log.debug('Update Display')
         self._calculate_default()
         if self.display:
             self.display.close()
-        self.display = MainDisplay(None, False, self)
+        self.display = MainDisplay(self)
         self.display.setup()
         self._theme_dimensions = {}
 
@@ -94,15 +95,11 @@
         """
         This method updates the theme in ``_theme_dimensions`` when a theme has been edited or renamed.
 
-        ``theme_name``
-            The current theme name.
-
-        ``old_theme_name``
-            The old theme name. Has only to be passed, when the theme has been renamed. Defaults to *None*.
-
-        ``only_delete``
-            Only remove the given ``theme_name`` from the ``_theme_dimensions`` list. This can be used when a theme is
-            permanently deleted.
+        :param theme_name: The current theme name.
+        :param old_theme_name: The old theme name. Has only to be passed, when the theme has been renamed.
+        Defaults to *None*.
+        :param only_delete: Only remove the given ``theme_name`` from the ``_theme_dimensions`` list. This can be
+        used when a theme is permanently deleted.
         """
         if old_theme_name is not None and old_theme_name in self._theme_dimensions:
             del self._theme_dimensions[old_theme_name]
@@ -115,9 +112,9 @@
         """
         Helper method to save theme names and theme data.
 
-        ``theme_name``
-            The theme name.
+        :param theme_name: The theme name
         """
+        self.log_debug("_set_theme with theme %s" % theme_name)
         if theme_name not in self._theme_dimensions:
             theme_data = self.theme_manager.get_theme_data(theme_name)
             main_rect = self.get_main_rectangle(theme_data)
@@ -128,16 +125,15 @@
         # if No file do not update cache
         if theme_data.background_filename:
             self.image_manager.add_image(theme_data.background_filename,
-                ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
+                                         ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
 
     def pre_render(self, override_theme_data=None):
         """
         Set up the theme to be used before rendering an item.
 
-        ``override_theme_data``
-            The theme data should be passed, when we want to use our own theme data, regardless of the theme level. This
-            should for example be used in the theme manager. **Note**, this is **not** to be mixed up with the
-            ``set_item_theme`` method.
+        :param override_theme_data: The theme data should be passed, when we want to use our own theme data, regardless
+         of the theme level. This should for example be used in the theme manager. **Note**, this is **not** to
+         be mixed up with the ``set_item_theme`` method.
         """
         # Just assume we use the global theme.
         theme_to_use = self.global_theme_name
@@ -167,17 +163,13 @@
         """
         Sets the theme level.
 
-        ``theme_level``
-            The theme level to be used.
+        :param theme_level: The theme level to be used.
         """
         self.theme_level = theme_level
 
     def set_global_theme(self):
         """
         Set the global-level theme name.
-
-        ``global_theme_name``
-            The global-level theme's name.
         """
         global_theme_name = Settings().value('themes/global theme')
         self._set_theme(global_theme_name)
@@ -187,8 +179,7 @@
         """
         Set the service-level theme.
 
-        ``service_theme_name``
-            The service level theme's name.
+        :param service_theme_name: The service level theme's name.
         """
         self._set_theme(service_theme_name)
         self.service_theme_name = service_theme_name
@@ -197,9 +188,9 @@
         """
         Set the item-level theme. **Note**, this has to be done for each item we are rendering.
 
-        ``item_theme_name``
-            The item theme's name.
+        :param item_theme_name: The item theme's name.
         """
+        self.log_debug("set_item_theme with theme %s" % item_theme_name)
         self._set_theme(item_theme_name)
         self.item_theme_name = item_theme_name
 
@@ -207,13 +198,9 @@
         """
         Generate a preview of a theme.
 
-        ``theme_data``
-            The theme to generated a preview for.
-
-        ``force_page``
-            Flag to tell message lines per page need to be generated.
+        :param theme_data:  The theme to generated a preview for.
+        :param force_page: Flag to tell message lines per page need to be generated.
         """
-        log.debug('generate preview')
         # save value for use in format_slide
         self.force_page = force_page
         # build a service item to generate preview
@@ -245,13 +232,11 @@
         """
         Calculate how much text can fit on a slide.
 
-        ``text``
-            The words to go on the slides.
+        :param text:  The words to go on the slides.
+        :param item: The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
 
-        ``item``
-            The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
         """
-        log.debug('format slide')
+        self.log_debug('format slide')
         # Add line endings after each line of text used for bibles.
         line_end = '<br>'
         if item.is_capable(ItemCapabilities.NoLineBreaks):
@@ -329,7 +314,7 @@
         self.width = screen_size.width()
         self.height = screen_size.height()
         self.screen_ratio = self.height / self.width
-        log.debug('_calculate default %s, %f' % (screen_size, self.screen_ratio))
+        self.log_debug('_calculate default %s, %f' % (screen_size, self.screen_ratio))
         # 90% is start of footer
         self.footer_start = int(self.height * 0.90)
 
@@ -337,8 +322,7 @@
         """
         Calculates the placement and size of the main rectangle.
 
-        ``theme_data``
-            The theme information
+        :param theme_data: The theme information
         """
         if not theme_data.font_main_override:
             return QtCore.QRect(10, 0, self.width - 20, self.footer_start)
@@ -350,8 +334,7 @@
         """
         Calculates the placement and size of the footer rectangle.
 
-        ``theme_data``
-            The theme data.
+        :param theme_data: The theme data.
         """
         if not theme_data.font_footer_override:
             return QtCore.QRect(10, self.footer_start, self.width - 20, self.height - self.footer_start)
@@ -364,16 +347,11 @@
         """
         Sets the rectangle within which text should be rendered.
 
-        ``theme_data``
-            The theme data.
-
-        ``rect_main``
-            The main text block.
-
-        ``rect_footer``
-            The footer text block.
+        :param theme_data: The theme data.
+        :param rect_main: The main text block.
+        :param rect_footer: The footer text block.
         """
-        log.debug('_set_text_rectangle %s , %s' % (rect_main, rect_footer))
+        self.log_debug('_set_text_rectangle %s , %s' % (rect_main, rect_footer))
         self._rect = rect_main
         self._rect_footer = rect_footer
         self.page_width = self._rect.width()
@@ -409,16 +387,13 @@
     def _paginate_slide(self, lines, line_end):
         """
         Figure out how much text can appear on a slide, using the current theme settings.
+
         **Note:** The smallest possible "unit" of text for a slide is one line. If the line is too long it will be cut
         off when displayed.
 
-        ``lines``
-            The text to be fitted on the slide split into lines.
-
-        ``line_end``
-            The text added after each line. Either ``u' '`` or ``u'<br>``.
+        :param lines: The text to be fitted on the slide split into lines.
+        :param line_end: The text added after each line. Either ``u' '`` or ``u'<br>``.
         """
-        log.debug('_paginate_slide - Start')
         formatted = []
         previous_html = ''
         previous_raw = ''
@@ -431,22 +406,18 @@
         else:
             previous_raw = separator.join(lines)
         formatted.append(previous_raw)
-        log.debug('_paginate_slide - End')
         return formatted
 
     def _paginate_slide_words(self, lines, line_end):
         """
         Figure out how much text can appear on a slide, using the current theme settings.
+
         **Note:** The smallest possible "unit" of text for a slide is one word. If one line is too long it will be
         processed word by word. This is sometimes need for **bible** verses.
 
-        ``lines``
-            The text to be fitted on the slide split into lines.
-
-        ``line_end``
-            The text added after each line. Either ``u' '`` or ``u'<br>``. This is needed for **bibles**.
+        :param lines: The text to be fitted on the slide split into lines.
+        :param line_end: The text added after each line. Either ``u' '`` or ``u'<br>``. This is needed for **bibles**.
         """
-        log.debug('_paginate_slide_words - Start')
         formatted = []
         previous_html = ''
         previous_raw = ''
@@ -476,7 +447,6 @@
                 previous_html += html_line + line_end
                 previous_raw += line + line_end
         formatted.append(previous_raw)
-        log.debug('_paginate_slide_words - End')
         return formatted
 
     def _get_start_tags(self, raw_text):
@@ -488,9 +458,8 @@
         The first unicode string is the text, with correct closing tags. The second unicode string are OpenLP's opening
         formatting tags and the third unicode string the html opening formatting tags.
 
-        ``raw_text``
-            The text to test. The text must **not** contain html tags, only OpenLP formatting tags are allowed::
-
+        :param raw_text: The text to test. The text must **not** contain html tags, only OpenLP formatting tags
+        are allowed::
                 {st}{r}Text text text
         """
         raw_tags = []
@@ -522,29 +491,18 @@
         and word based (word by word). It is assumed that this method is **only** called, when the lines/words to be
         rendered do **not** fit as a whole.
 
-        ``formatted``
-            The list to append any slides.
-
-        ``previous_html``
-            The html text which is know to fit on a slide, but is not yet added to the list of slides. (unicode string)
-
-        ``previous_raw``
-            The raw text (with formatting tags) which is know to fit on a slide, but is not yet added to the list of
-            slides. (unicode string)
-
-        ``html_list``
-            The elements which do not fit on a slide and needs to be processed using the binary chop. The text contains
-            html.
-
-        ``raw_list``
-            The elements which do not fit on a slide and needs to be processed using the binary chop. The elements can
-            contain formatting tags.
-
-        ``separator``
-            The separator for the elements. For lines this is ``u'<br>'`` and for words this is ``u' '``.
-
-        ``line_end``
-            The text added after each "element line". Either ``u' '`` or ``u'<br>``. This is needed for bibles.
+        :param formatted: The list to append any slides.
+        :param previous_html: The html text which is know to fit on a slide, but is not yet added to the list of
+        slides. (unicode string)
+        :param previous_raw: The raw text (with formatting tags) which is know to fit on a slide, but is not yet added
+        to the list of slides. (unicode string)
+        :param html_list: The elements which do not fit on a slide and needs to be processed using the binary chop.
+        The text contains html.
+        :param raw_list: The elements which do not fit on a slide and needs to be processed using the binary chop.
+        The elements can contain formatting tags.
+        :param separator: The separator for the elements. For lines this is ``u'<br>'`` and for words this is ``u' '``.
+        :param line_end: The text added after each "element line". Either ``u' '`` or ``u'<br>``. This is needed for
+         bibles.
         """
         smallest_index = 0
         highest_index = len(html_list) - 1
@@ -591,8 +549,7 @@
         """
         Checks if the given ``text`` fits on a slide. If it does ``True`` is returned, otherwise ``False``.
 
-        ``text``
-            The text to check. It may contain HTML tags.
+        :param text:  The text to check. It may contain HTML tags.
         """
         self.web_frame.evaluateJavaScript('show_text("%s")' % text.replace('\\', '\\\\').replace('\"', '\\\"'))
         return self.web_frame.contentsSize().height() <= self.empty_height
@@ -600,6 +557,8 @@
     def _words_split(self, line):
         """
         Split the slide up by word so can wrap better
+
+        :param line: Line to be split
         """
         # this parse we are to be wordy
         line = line.replace('\n', ' ')
@@ -619,7 +578,7 @@
         """
         Adds the theme manager to the class dynamically
         """
-        if not hasattr(self, '_theme_manager'):
+        if not hasattr(self, '_theme_manager') or not self._theme_manager :
             self._theme_manager = Registry().get('theme_manager')
         return self._theme_manager
 

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2013-12-28 21:33:38 +0000
+++ openlp/core/lib/serviceitem.py	2014-01-16 22:00:15 +0000
@@ -31,8 +31,8 @@
 type and capability of an item.
 """
 
-import cgi
 import datetime
+import html
 import logging
 import os
 import uuid
@@ -241,9 +241,8 @@
             self.theme_data, self.main, self.footer = self.renderer.pre_render()
         if self.service_item_type == ServiceItemType.Text:
             log.debug('Formatting slides: %s' % self.title)
-            # Save rendered pages to this dict. In the case that a slide is used
-            # twice we can use the pages saved to the dict instead of rendering
-            # them again.
+            # Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
+            # the dict instead of rendering them again.
             previous_pages = {}
             for slide in self._raw_frames:
                 verse_tag = slide['verseTag']
@@ -254,11 +253,11 @@
                     previous_pages[verse_tag] = (slide['raw_slide'], pages)
                 for page in pages:
                     page = page.replace('<br>', '{br}')
-                    html = expand_tags(cgi.escape(page.rstrip()))
+                    html_data = expand_tags(html.escape(page.rstrip()))
                     self._display_frames.append({
                         'title': clean_tags(page),
                         'text': clean_tags(page.rstrip()),
-                        'html': html.replace('&amp;nbsp;', '&nbsp;'),
+                        'html': html_data.replace('&amp;nbsp;', '&nbsp;'),
                         'verseTag': verse_tag
                     })
         elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:

=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/maindisplay.py	2014-01-16 22:00:15 +0000
@@ -44,7 +44,7 @@
 from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
 from PyQt4.phonon import Phonon
 
-from openlp.core.common import Registry, Settings, translate
+from openlp.core.common import Registry, OpenLPMixin, Settings, translate
 from openlp.core.lib import ServiceItem, ImageSource, build_html, expand_tags, image_to_byte
 from openlp.core.lib.theme import BackgroundType
 
@@ -56,22 +56,23 @@
 
 class Display(QtGui.QGraphicsView):
     """
-    This is a general display screen class. Here the general display settings
-    will done. It will be used as specialized classes by Main Display and
-    Preview display.
+    This is a general display screen class. Here the general display settings will done. It will be used as
+    specialized classes by Main Display and Preview display.
     """
-    def __init__(self, parent, live, controller):
+    def __init__(self, parent):
         """
         Constructor
         """
-        if live:
+        self.is_live = False
+        if hasattr(parent, 'is_live') and parent.is_live:
+            self.is_live = True
+        if self.is_live:
             super(Display, self).__init__()
             # Overwrite the parent() method.
             self.parent = lambda: parent
         else:
             super(Display, self).__init__(parent)
-        self.is_live = live
-        self.controller = controller
+        self.controller = parent
         self.screen = {}
         # 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
@@ -84,9 +85,7 @@
         """
         Set up and build the screen base
         """
-        log.debug('Start Display base setup (live = %s)' % self.is_live)
         self.setGeometry(self.screen['size'])
-        log.debug('Setup webView')
         self.web_view = QtWebKit.QWebView(self)
         self.web_view.setGeometry(0, 0, self.screen['size'].width(), self.screen['size'].height())
         self.web_view.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)
@@ -107,33 +106,34 @@
     def resizeEvent(self, event):
         """
         React to resizing of this display
+
+        :param event: The event to be handled
         """
         self.web_view.setGeometry(0, 0, self.width(), self.height())
 
-    def is_web_loaded(self):
+    def is_web_loaded(self, field=None):
         """
         Called by webView event to show display is fully loaded
         """
-        log.debug('is web loaded')
         self.web_loaded = True
 
 
-class MainDisplay(Display):
+class MainDisplay(OpenLPMixin, Display):
     """
     This is the display screen as a specialized class from the Display class
     """
-    def __init__(self, parent, live, controller):
+    def __init__(self, parent):
         """
         Constructor
         """
-        super(MainDisplay, self).__init__(parent, live, controller)
+        super(MainDisplay, self).__init__(parent)
         self.screens = ScreenList()
         self.rebuild_css = False
         self.hide_mode = None
         self.override = {}
         self.retranslateUi()
         self.media_object = None
-        if live:
+        if self.is_live:
             self.audio_player = AudioPlayer(self)
         else:
             self.audio_player = None
@@ -164,6 +164,8 @@
     def set_transparency(self, enabled):
         """
         Set the transparency of the window
+
+        :param enabled: Is transparency enabled
         """
         if enabled:
             self.setAutoFillBackground(False)
@@ -189,7 +191,7 @@
         """
         Set up and build the output screen
         """
-        log.debug('Start MainDisplay setup (live = %s)' % self.is_live)
+        self.log_debug('Start MainDisplay setup (live = %s)' % self.is_live)
         self.screen = self.screens.current
         self.setVisible(False)
         Display.setup(self)
@@ -216,20 +218,15 @@
             service_item.bg_image_bytes = image_to_byte(self.initial_fame)
             self.web_view.setHtml(build_html(service_item, self.screen, self.is_live, None,
                                   plugins=self.plugin_manager.plugins))
-            self.__hideMouse()
-        log.debug('Finished MainDisplay setup')
+            self._hide_mouse()
 
     def text(self, slide, animate=True):
         """
         Add the slide text from slideController
 
-        ``slide``
-            The slide text to be displayed
-
-        ``animate``
-            Perform transitions if applicable when setting the text
+        :param slide: The slide text to be displayed
+        :param animate: Perform transitions if applicable when setting the text
         """
-        log.debug('text to display')
         # Wait for the webview to update before displaying text.
         while not self.web_loaded:
             self.application.process_events()
@@ -248,10 +245,9 @@
         """
         Display an alert.
 
-        ``text``
-            The text to be displayed.
+        :param text: The text to be displayed.
+        :param location: Where on the screen is the text to be displayed
         """
-        log.debug('alert to display')
         # First we convert <>& marks to html variants, then apply
         # formattingtags, finally we double all backslashes for JavaScript.
         text_prepared = expand_tags(cgi.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"')
@@ -278,6 +274,9 @@
     def direct_image(self, path, background):
         """
         API for replacement backgrounds so Images are added directly to cache.
+
+        :param path: Path to Image
+        :param background: The background color
         """
         self.image_manager.add_image(path, ImageSource.ImagePlugin, background)
         if not hasattr(self, 'service_item'):
@@ -295,12 +294,9 @@
         Add an image as the background. The image has already been added to the
         cache.
 
-        ``path``
-            The path to the image to be displayed. **Note**, the path is only
-            passed to identify the image. If the image has changed it has to be
-            re-added to the image manager.
+        :param path: The path to the image to be displayed. **Note**, the path is only passed to identify the image.
+        If the image has changed it has to be re-added to the image manager.
         """
-        log.debug('image to display')
         image = self.image_manager.get_image_bytes(path, ImageSource.ImagePlugin)
         self.controller.media_controller.media_reset(self.controller)
         self.display_image(image)
@@ -308,6 +304,8 @@
     def display_image(self, image):
         """
         Display an image, as is.
+
+        :param image: The image to be displayed
         """
         self.setGeometry(self.screen['size'])
         if image:
@@ -318,10 +316,8 @@
 
     def reset_image(self):
         """
-        Reset the background image to the service item image. Used after the
-        image plugin has changed the background.
+        Reset the background image to the service item image. Used after the image plugin has changed the background.
         """
-        log.debug('reset_image')
         if hasattr(self, 'service_item'):
             self.display_image(self.service_item.bg_image_bytes)
         else:
@@ -336,7 +332,6 @@
         """
         Generates a preview of the image displayed.
         """
-        log.debug('preview for %s', self.is_live)
         was_visible = self.isVisible()
         self.application.process_events()
         # We must have a service item to preview.
@@ -368,10 +363,11 @@
 
     def build_html(self, service_item, image_path=''):
         """
-        Store the service_item and build the new HTML from it. Add the
-        HTML to the display
+        Store the service_item and build the new HTML from it. Add the HTML to the display
+
+        :param service_item: The Service item to be used
+        :param image_path: Where the image resides.
         """
-        log.debug('build_html')
         self.web_loaded = False
         self.initial_fame = None
         self.service_item = service_item
@@ -393,17 +389,14 @@
                               BackgroundType.to_string(BackgroundType.Transparent))
         if self.service_item.theme_data.background_filename:
             self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
-                self.service_item.theme_data.background_filename, ImageSource.Theme
-            )
+                self.service_item.theme_data.background_filename, ImageSource.Theme)
         if image_path:
             image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
         else:
             image_bytes = None
         html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,
                           plugins=self.plugin_manager.plugins)
-        log.debug('buildHtml - pre setHtml')
         self.web_view.setHtml(html)
-        log.debug('buildHtml - post setHtml')
         if service_item.foot_text:
             self.footer(service_item.foot_text)
         # if was hidden keep it hidden
@@ -412,22 +405,24 @@
                 Registry().execute('slidecontroller_live_unblank')
             else:
                 self.hide_display(self.hide_mode)
-        self.__hideMouse()
+        self._hide_mouse()
 
     def footer(self, text):
         """
         Display the Footer
+
+        :param text: footer text to be displayed
         """
-        log.debug('footer')
         js = 'show_footer(\'' + text.replace('\\', '\\\\').replace('\'', '\\\'') + '\')'
         self.frame.evaluateJavaScript(js)
 
     def hide_display(self, mode=HideMode.Screen):
         """
-        Hide the display by making all layers transparent
-        Store the images so they can be replaced when required
+        Hide the display by making all layers transparent Store the images so they can be replaced when required
+
+        :param mode: How the screen is to be hidden
         """
-        log.debug('hide_display mode = %d', mode)
+        self.log_debug('hide_display mode = %d' % mode)
         if self.screens.display_count == 1:
             # Only make visible if setting enabled.
             if not Settings().value('core/display on monitor'):
@@ -450,7 +445,6 @@
         Show the stored layers so the screen reappears as it was originally.
         Make the stored images None to release memory.
         """
-        log.debug('show_display')
         if self.screens.display_count == 1:
             # Only make visible if setting enabled.
             if not Settings().value('core/display on monitor'):
@@ -463,7 +457,7 @@
         if self.is_live:
             Registry().execute('live_display_active')
 
-    def __hideMouse(self):
+    def _hide_mouse(self):
         """
         Hide mouse cursor when moved over display.
         """
@@ -519,12 +513,10 @@
     live_controller = property(_get_live_controller)
 
 
-class AudioPlayer(QtCore.QObject):
-    """
-    This Class will play audio only allowing components to work with a
-    soundtrack independent of the user interface.
-    """
-    log.info('AudioPlayer Loaded')
+class AudioPlayer(OpenLPMixin, QtCore.QObject):
+    """
+    This Class will play audio only allowing components to work with a soundtrack independent of the user interface.
+    """
 
     def __init__(self, parent):
         """
@@ -533,7 +525,6 @@
         ``parent``
             The parent widget.
         """
-        log.debug('AudioPlayer Initialisation started')
         super(AudioPlayer, self).__init__(parent)
         self.current_index = -1
         self.playlist = []
@@ -567,7 +558,7 @@
         When the audio track finishes.
         """
         if self.repeat:
-            log.debug('Repeat is enabled... here we go again!')
+            self.log_debug('Repeat is enabled... here we go again!')
             self.media_object.clearQueue()
             self.media_object.clear()
             self.current_index = -1
@@ -592,7 +583,6 @@
         """
         We want to play the file so start it
         """
-        log.debug('AudioPlayer.play() called')
         if self.current_index == -1:
             self.on_about_to_finish()
         self.media_object.play()
@@ -601,22 +591,19 @@
         """
         Pause the Audio
         """
-        log.debug('AudioPlayer.pause() called')
         self.media_object.pause()
 
     def stop(self):
         """
         Stop the Audio and clean up
         """
-        log.debug('AudioPlayer.stop() called')
         self.media_object.stop()
 
     def add_to_playlist(self, file_names):
         """
         Add another file to the playlist.
 
-        ``file_names``
-            A list with files to be added to the playlist.
+        :param file_names:  A list with files to be added to the playlist.
         """
         if not isinstance(file_names, list):
             file_names = [file_names]
@@ -641,6 +628,8 @@
     def go_to(self, index):
         """
         Go to a particular track in the list
+
+        :param index: The track to go to
         """
         is_playing = self.media_object.state() == Phonon.PlayingState
         self.media_object.clearQueue()
@@ -653,6 +642,9 @@
     def connectSlot(self, signal, slot):
         """
         Connect a slot to a signal on the media object.  Used by slidecontroller to connect to audio object.
+
+        :param slot: The slot the signal is attached to.
+        :param signal: The signal to be fired
         """
         QtCore.QObject.connect(self.media_object, signal, slot)
 

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2014-01-16 20:34:37 +0000
+++ openlp/core/ui/mainwindow.py	2014-01-16 22:00:15 +0000
@@ -545,6 +545,7 @@
         self.live_controller.panel.setVisible(Settings().value('user interface/live panel'))
         self.load_settings()
         self.restore_current_media_manager_item()
+        Registry().execute('theme_update_global')
 
     def restore_current_media_manager_item(self):
         """
@@ -673,7 +674,7 @@
                 else:
                     self.active_plugin.toggle_status(PluginStatus.Inactive)
         # Set global theme and
-        Registry().execute('theme_update_global', self.theme_manager_contents.global_theme)
+        Registry().execute('theme_update_global')
         self.theme_manager_contents.load_first_time_themes()
         # Check if any Bibles downloaded.  If there are, they will be processed.
         Registry().execute('bibles_load_list', True)

=== modified file 'openlp/core/ui/media/__init__.py'
--- openlp/core/ui/media/__init__.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/media/__init__.py	2014-01-16 22:00:15 +0000
@@ -35,7 +35,7 @@
 
 from PyQt4 import QtCore
 
-log = logging.getLogger(__name__)
+log = logging.getLogger(__name__+'.__init__')
 
 
 class MediaState(object):

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2014-01-04 16:33:20 +0000
+++ openlp/core/ui/servicemanager.py	2014-01-16 22:00:15 +0000
@@ -1020,9 +1020,9 @@
         """
         Called by the SlideController to select the next service item.
         """
-        if not self.service_manager_list.selected_items():
+        if not self.service_manager_list.selectedItems():
             return
-        selected = self.service_manager_list.selected_items()[0]
+        selected = self.service_manager_list.selectedItems()[0]
         look_for = 0
         service_iterator = QtGui.QTreeWidgetItemIterator(self.service_manager_list)
         while service_iterator.value():
@@ -1040,9 +1040,9 @@
 
         :param last_slide: Is this the last slide in the service_item.
         """
-        if not self.service_manager_list.selected_items():
+        if not self.service_manager_list.selectedItems():
             return
-        selected = self.service_manager_list.selected_items()[0]
+        selected = self.service_manager_list.selectedItems()[0]
         prev_item = None
         prev_item_last_slide = None
         service_iterator = QtGui.QTreeWidgetItemIterator(self.service_manager_list)

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2014-01-04 11:50:27 +0000
+++ openlp/core/ui/slidecontroller.py	2014-01-16 22:00:15 +0000
@@ -93,8 +93,7 @@
 
     def send_to_plugins(self, *args):
         """
-        This is the generic function to send signal for control widgets,
-        created from within other plugins
+        This is the generic function to send signal for control widgets, created from within other plugins
         This function is needed to catch the current controller
         """
         sender = self.sender().objectName() if self.sender().objectName() else self.sender().text()
@@ -277,7 +276,8 @@
             self.toolbar.add_toolbar_widget(self.song_menu)
             # Stuff for items with background audio.
             # FIXME: object name should be changed. But this requires that we migrate the shortcut.
-            self.audio_pause_item = self.toolbar.add_toolbar_action('audioPauseItem',
+            self.audio_pause_item = self.toolbar.add_toolbar_action(
+                'audioPauseItem',
                 icon=':/slides/media_playback_pause.png', text=translate('OpenLP.SlideController', 'Pause Audio'),
                 tooltip=translate('OpenLP.SlideController', 'Pause audio.'),
                 checked=False, visible=False, category=self.category, context=QtCore.Qt.WindowShortcut,
@@ -318,7 +318,7 @@
         self.slide_layout.setSpacing(0)
         self.slide_layout.setMargin(0)
         self.slide_layout.setObjectName('SlideLayout')
-        self.preview_display = Display(self, self.is_live, self)
+        self.preview_display = Display(self)
         self.preview_display.setGeometry(QtCore.QRect(0, 0, 300, 300))
         self.preview_display.screen = {'size': self.preview_display.geometry()}
         self.preview_display.setup()
@@ -532,7 +532,7 @@
         # rebuild display as screen size changed
         if self.display:
             self.display.close()
-        self.display = MainDisplay(self, self.is_live, self)
+        self.display = MainDisplay(self)
         self.display.setup()
         if self.is_live:
             self.__add_actions_to_widget(self.display)
@@ -566,17 +566,15 @@
 
     def preview_size_changed(self):
         """
-        Takes care of the SlidePreview's size. Is called when one of the the
-        splitters is moved or when the screen size is changed. Note, that this
-        method is (also) called frequently from the mainwindow *paintEvent*.
+        Takes care of the SlidePreview's size. Is called when one of the the splitters is moved or when the screen
+        size is changed. Note, that this method is (also) called frequently from the mainwindow *paintEvent*.
         """
         if self.ratio < self.preview_frame.width() / self.preview_frame.height():
             # We have to take the height as limit.
             max_height = self.preview_frame.height() - self.grid.margin() * 2
             self.slide_preview.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height))
             self.preview_display.setFixedSize(QtCore.QSize(max_height * self.ratio, max_height))
-            self.preview_display.screen = {
-                'size': self.preview_display.geometry()}
+            self.preview_display.screen = {'size': self.preview_display.geometry()}
         else:
             # We have to take the width as limit.
             max_width = self.preview_frame.width() - self.grid.margin() * 2
@@ -683,6 +681,8 @@
     def enable_preview_tool_bar(self, item):
         """
         Allows the Preview toolbar to be customised
+
+        :param item: The current service item
         """
         # Work-around for OS X, hide and then show the toolbar
         # See bug #791050
@@ -712,6 +712,8 @@
         """
         Method to install the service item into the controller
         Called by plugins
+
+        :param item: The current service item
         """
         item.render()
         slide_no = 0
@@ -723,6 +725,8 @@
     def replace_service_manager_item(self, item):
         """
         Replacement item following a remote edit
+
+        :param item: The current service item
         """
         if item == self.service_item:
             self._process_item(item, self.preview_widget.current_slide_number())
@@ -731,6 +735,9 @@
         """
         Method to install the service item into the controller and request the correct toolbar for the plugin. Called by
         :class:`~openlp.core.ui.ServiceManager`
+
+        :param item: The current service item
+        :param slide_no: The slide number to select
         """
         # If no valid slide number is specified we take the first one, but we remember the initial value to see if we
         # should reload the song or not
@@ -755,6 +762,9 @@
     def _process_item(self, service_item, slide_no):
         """
         Loads a ServiceItem into the system from ServiceManager. Display the slide number passed.
+
+        :param service_item: The current service item
+        :param slide_no: The slide number to select
         """
         self.on_stop_loop()
         old_item = self.service_item
@@ -762,8 +772,9 @@
         self.service_item = copy.copy(service_item)
         if old_item and self.is_live and old_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
             self._reset_blank()
-        Registry().execute(
-            '%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slide_no])
+        if service_item.is_command():
+            Registry().execute(
+                '%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slide_no])
         self.slide_list = {}
         if self.is_live:
             self.song_menu.menu().clear()
@@ -789,7 +800,7 @@
                 self.set_audio_items_visibility(True)
         row = 0
         width = self.main_window.control_splitter.sizes()[self.split]
-        for framenumber, frame in enumerate(self.service_item.get_frames()):
+        for frame_number, frame in enumerate(self.service_item.get_frames()):
             if self.service_item.is_text():
                 if frame['verseTag']:
                     # These tags are already translated.
@@ -798,7 +809,7 @@
                     two_line_def = '%s\n%s' % (verse_def[0], verse_def[1:])
                     row = two_line_def
                     if verse_def not in self.slide_list:
-                        self.slide_list[verse_def] = framenumber
+                        self.slide_list[verse_def] = frame_number
                         if self.is_live:
                             self.song_menu.menu().addAction(verse_def, self.on_song_bar_handler)
                 else:
@@ -808,7 +819,7 @@
                 row += 1
                 self.slide_list[str(row)] = row - 1
                 # If current slide set background to image
-                if not self.service_item.is_command() and framenumber == slide_no:
+                if not self.service_item.is_command() and frame_number == slide_no:
                     self.service_item.bg_image_bytes = \
                         self.image_manager.get_image_bytes(frame['path'], ImageSource.ImagePlugin)
         self.preview_widget.replace_service_item(self.service_item, width, slide_no)
@@ -832,10 +843,11 @@
                 self.on_media_close()
         Registry().execute('slidecontroller_%s_started' % self.type_prefix, [service_item])
 
-    # Screen event methods
     def on_slide_selected_index(self, message):
         """
         Go to the requested slide
+
+        :param message: remote message to be processed.
         """
         index = int(message[0])
         if not self.service_item:
@@ -880,6 +892,8 @@
     def on_blank_display(self, checked=None):
         """
         Handle the blank screen button actions
+
+        :param checked: the new state of the of the widget
         """
         if checked is None:
             checked = self.blank_screen.isChecked()
@@ -899,6 +913,8 @@
     def on_theme_display(self, checked=None):
         """
         Handle the Theme screen button
+
+        :param checked: the new state of the of the widget
         """
         if checked is None:
             checked = self.theme_screen.isChecked()
@@ -918,6 +934,8 @@
     def on_hide_display(self, checked=None):
         """
         Handle the Hide screen button
+
+        :param checked: the new state of the of the widget
         """
         if checked is None:
             checked = self.desktop_screen.isChecked()
@@ -984,8 +1002,9 @@
 
     def slide_selected(self, start=False):
         """
-        Generate the preview when you click on a slide.
-        if this is the Live Controller also display on the screen
+        Generate the preview when you click on a slide. If this is the Live Controller also display on the screen
+
+        :param start:
         """
         row = self.preview_widget.current_slide_number()
         self.selected_row = 0
@@ -1008,12 +1027,13 @@
             self.update_preview()
             self.selected_row = row
             self.preview_widget.change_slide(row)
-        Registry().execute('slidecontroller_%s_changed' % self.type_prefix, row)
         self.display.setFocus()
 
     def on_slide_change(self, row):
         """
         The slide has been changed. Update the slidecontroller accordingly
+
+        :param row: Row to be selected
         """
         self.preview_widget.change_slide(row)
         self.update_preview()
@@ -1046,20 +1066,24 @@
 
     def on_slide_selected_next_action(self, checked):
         """
-        Wrapper function from create_action so we can throw away the
-        incorrect parameter
+        Wrapper function from create_action so we can throw away the incorrect parameter
+
+        :param checked: the new state of the of the widget
         """
         self.on_slide_selected_next()
 
     def on_slide_selected_next(self, wrap=None):
         """
         Go to the next slide.
+
+        :param wrap: Are we wrapping round the service item
         """
         if not self.service_item:
             return
-        Registry().execute('%s_next' % self.service_item.name.lower(), [self.service_item, self.is_live])
-        if self.service_item.is_command() and self.is_live:
-            self.update_preview()
+        if self.service_item.is_command():
+            Registry().execute('%s_next' % self.service_item.name.lower(), [self.service_item, self.is_live])
+            if self.is_live:
+                self.update_preview()
         else:
             row = self.preview_widget.current_slide_number() + 1
             if row == self.preview_widget.slide_count():
@@ -1084,9 +1108,10 @@
         """
         if not self.service_item:
             return
-        Registry().execute('%s_previous' % self.service_item.name.lower(), [self.service_item, self.is_live])
-        if self.service_item.is_command() and self.is_live:
-            self.update_preview()
+        if self.service_item.is_command():
+            Registry().execute('%s_previous' % self.service_item.name.lower(), [self.service_item, self.is_live])
+            if self.is_live:
+                self.update_preview()
         else:
             row = self.preview_widget.current_slide_number() - 1
             if row == -1:
@@ -1129,6 +1154,8 @@
     def on_play_slides_loop(self, checked=None):
         """
         Start or stop 'Play Slides in Loop'
+
+        :param checked: is the check box checked.
         """
         if checked is None:
             checked = self.play_slides_loop.isChecked()
@@ -1150,6 +1177,8 @@
     def on_play_slides_once(self, checked=None):
         """
         Start or stop 'Play Slides to End'
+
+        :param checked: is the check box checked.
         """
         if checked is None:
             checked = self.play_slides_once.isChecked()
@@ -1177,6 +1206,8 @@
     def set_audio_pause_clicked(self, checked):
         """
         Pause the audio player
+
+        :param checked: is the check box checked.
         """
         if not self.audio_pause_item.isVisible():
             return
@@ -1188,6 +1219,8 @@
     def timerEvent(self, event):
         """
         If the timer event is for this window select next slide
+
+        :param event: The triggered event
         """
         if event.timerId() == self.timer_id:
             self.on_slide_selected_next(self.play_slides_loop.isChecked())
@@ -1235,6 +1268,8 @@
     def on_media_start(self, item):
         """
         Respond to the arrival of a media service item
+
+        :param item: The service item to be processed
         """
         self.media_controller.video(self.controller_type, item, self.hide_mode())
         if not self.is_live:
@@ -1288,6 +1323,8 @@
     def on_audio_time_remaining(self, time):
         """
         Update how much time is remaining
+
+        :param time: the time remainings
         """
         seconds = self.display.audio_player.media_object.remainingTime() // 1000
         minutes = seconds // 60
@@ -1335,7 +1372,7 @@
         """
         Adds the service manager to the class dynamically
         """
-        if not hasattr(self, '_service_manager'):
+        if not hasattr(self, '_service_manager') or not self._service_manager:
             self._service_manager = Registry().get('service_manager')
         return self._service_manager
 

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2014-01-01 09:33:07 +0000
+++ openlp/core/ui/thememanager.py	2014-01-16 22:00:15 +0000
@@ -158,7 +158,7 @@
         self.theme_form.path = self.path
         self.file_rename_form = FileRenameForm()
         Registry().register_function('theme_update_global', self.change_global_from_tab)
-        self._push_themes()
+        self.load_themes()
 
     def build_theme_path(self):
         """
@@ -446,7 +446,6 @@
             self._write_theme(theme, None, None)
             Settings().setValue(self.settings_section + '/global theme', theme.theme_name)
         self.application.set_normal_cursor()
-        self.load_themes()
 
     def load_themes(self):
         """
@@ -512,8 +511,9 @@
     def over_write_message_box(self, theme_name):
         """
         Display a warning box to the user that a theme already exists
+
         :param theme_name: Name of the theme.
-        :return Confirm if the theme is to be overeritten.
+        :return Confirm if the theme is to be overwritten.
         """
         ret = QtGui.QMessageBox.question(self, translate('OpenLP.ThemeManager', 'Theme Already Exists'),
                                          translate('OpenLP.ThemeManager',

=== modified file 'openlp/core/ui/themestab.py'
--- openlp/core/ui/themestab.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/themestab.py	2014-01-16 22:00:15 +0000
@@ -156,12 +156,6 @@
             self.settings_form.register_post_process('theme_update_global')
         self.tab_visited = False
 
-    def post_set_up(self):
-        """
-        After setting things up...
-        """
-        Registry().execute('theme_update_global')
-
     def on_song_level_button_clicked(self):
         """
         Set the theme level

=== modified file 'openlp/core/utils/__init__.py'
--- openlp/core/utils/__init__.py	2013-12-28 21:33:38 +0000
+++ openlp/core/utils/__init__.py	2014-01-16 22:00:15 +0000
@@ -56,7 +56,8 @@
 
 from openlp.core.common import translate
 
-log = logging.getLogger(__name__)
+log = logging.getLogger(__name__+'.__init__')
+
 APPLICATION_VERSION = {}
 IMAGES_FILTER = None
 ICU_COLLATOR = None

=== modified file 'openlp/plugins/alerts/forms/alertdialog.py'
--- openlp/plugins/alerts/forms/alertdialog.py	2014-01-01 09:57:06 +0000
+++ openlp/plugins/alerts/forms/alertdialog.py	2014-01-16 22:00:15 +0000
@@ -35,6 +35,9 @@
 
 
 class Ui_AlertDialog(object):
+    """
+    Alert UI Class
+    """
     def setupUi(self, alert_dialog):
         """
         Setup the Alert UI dialog

=== modified file 'openlp/plugins/alerts/forms/alertform.py'
--- openlp/plugins/alerts/forms/alertform.py	2014-01-01 09:57:06 +0000
+++ openlp/plugins/alerts/forms/alertform.py	2014-01-16 22:00:15 +0000
@@ -29,7 +29,7 @@
 
 from PyQt4 import QtGui, QtCore
 
-from openlp.core.common import translate
+from openlp.core.common import Registry, translate
 from openlp.plugins.alerts.lib.db import AlertItem
 
 from .alertdialog import Ui_AlertDialog
@@ -46,8 +46,7 @@
         self.manager = plugin.manager
         self.plugin = plugin
         self.item_id = None
-        # TODO: Use Registry()
-        super(AlertForm, self).__init__(self.plugin.main_window)
+        super(AlertForm, self).__init__( Registry().get('main_window'))
         self.setupUi(self)
         self.display_button.clicked.connect(self.on_display_clicked)
         self.display_close_button.clicked.connect(self.on_display_close_clicked)

=== modified file 'openlp/plugins/alerts/lib/alertsmanager.py'
--- openlp/plugins/alerts/lib/alertsmanager.py	2014-01-01 09:57:06 +0000
+++ openlp/plugins/alerts/lib/alertsmanager.py	2014-01-16 22:00:15 +0000
@@ -31,25 +31,17 @@
 displaying of alerts.
 """
 
-import logging
-
 from PyQt4 import QtCore
 
-from openlp.core.common import Registry, translate
-
-
-log = logging.getLogger(__name__)
-
-
-class AlertsManager(QtCore.QObject):
+from openlp.core.common import OpenLPMixin, RegistryMixin, Registry, translate
+
+
+class AlertsManager(OpenLPMixin, RegistryMixin, QtCore.QObject):
     """
     AlertsManager manages the settings of Alerts.
     """
-    log.info('Alert Manager loaded')
-
     def __init__(self, parent):
         super(AlertsManager, self).__init__(parent)
-        Registry().register('alerts_manager', self)
         self.timer_id = 0
         self.alert_list = []
         Registry().register_function('live_display_active', self.generate_alert)
@@ -71,7 +63,7 @@
 
         :param text: The text to display
         """
-        log.debug('display alert called %s' % text)
+        self.log_debug('display alert called %s' % text)
         if text:
             self.alert_list.append(text)
             if self.timer_id != 0:
@@ -85,7 +77,6 @@
         """
         Format and request the Alert and start the timer.
         """
-        log.debug('Generate Alert called')
         if not self.alert_list:
             return
         text = self.alert_list.pop(0)
@@ -101,10 +92,9 @@
 
         :param event: the QT event that has been triggered.
         """
-        log.debug('timer event')
         if event.timerId() == self.timer_id:
-            alertTab = self.parent().settings_tab
-            self.live_controller.display.alert('', alertTab.location)
+            alert_tab = self.parent().settings_tab
+            self.live_controller.display.alert('', alert_tab.location)
         self.killTimer(self.timer_id)
         self.timer_id = 0
         self.generate_alert()

=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py	2013-12-31 20:29:03 +0000
+++ openlp/plugins/media/lib/mediaitem.py	2014-01-16 22:00:15 +0000
@@ -74,7 +74,7 @@
         self.display_controller.controller_layout = QtGui.QVBoxLayout()
         self.media_controller.register_controller(self.display_controller)
         self.media_controller.set_controls_visible(self.display_controller, False)
-        self.display_controller.preview_display = Display(self.display_controller, False, self.display_controller)
+        self.display_controller.preview_display = Display(self.display_controller)
         self.display_controller.preview_display.hide()
         self.display_controller.preview_display.setGeometry(QtCore.QRect(0, 0, 300, 300))
         self.display_controller.preview_display.screen = {'size': self.display_controller.preview_display.geometry()}

=== modified file 'openlp/plugins/songusage/forms/songusagedeletedialog.py'
--- openlp/plugins/songusage/forms/songusagedeletedialog.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songusage/forms/songusagedeletedialog.py	2014-01-16 22:00:15 +0000
@@ -34,7 +34,15 @@
 
 
 class Ui_SongUsageDeleteDialog(object):
+    """
+    The Song Usage delete dialog
+    """
     def setupUi(self, song_usage_delete_dialog):
+        """
+        Setup the UI
+
+        :param song_usage_delete_dialog:
+        """
         song_usage_delete_dialog.setObjectName('song_usage_delete_dialog')
         song_usage_delete_dialog.resize(291, 243)
         self.vertical_layout = QtGui.QVBoxLayout(song_usage_delete_dialog)
@@ -55,8 +63,12 @@
         self.retranslateUi(song_usage_delete_dialog)
 
     def retranslateUi(self, song_usage_delete_dialog):
+        """
+        Retranslate the strings
+        :param song_usage_delete_dialog:
+        """
         song_usage_delete_dialog.setWindowTitle(
             translate('SongUsagePlugin.SongUsageDeleteForm', 'Delete Song Usage Data'))
         self.delete_label.setText(
             translate('SongUsagePlugin.SongUsageDeleteForm', 'Select the date up to which the song usage data '
-                'should be deleted. All data recorded before this date will be permanently deleted.'))
+                      'should be deleted. \nAll data recorded before this date will be permanently deleted.'))

=== modified file 'openlp/plugins/songusage/forms/songusagedeleteform.py'
--- openlp/plugins/songusage/forms/songusagedeleteform.py	2013-12-28 21:33:38 +0000
+++ openlp/plugins/songusage/forms/songusagedeleteform.py	2014-01-16 22:00:15 +0000
@@ -48,19 +48,25 @@
         self.button_box.clicked.connect(self.on_button_box_clicked)
 
     def on_button_box_clicked(self, button):
+        """
+        The button event has been triggered
+
+        :param button: The button pressed
+        """
         if self.button_box.standardButton(button) == QtGui.QDialogButtonBox.Ok:
-            ret = QtGui.QMessageBox.question(self,
+            ret = QtGui.QMessageBox.question(
+                self,
                 translate('SongUsagePlugin.SongUsageDeleteForm', 'Delete Selected Song Usage Events?'),
                 translate('SongUsagePlugin.SongUsageDeleteForm',
-                    'Are you sure you want to delete selected Song Usage data?'),
+                          'Are you sure you want to delete selected Song Usage data?'),
                 QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.No)
             if ret == QtGui.QMessageBox.Yes:
                 delete_date = self.delete_calendar.selectedDate().toPyDate()
                 self.manager.delete_all_objects(SongUsageItem, SongUsageItem.usagedate <= delete_date)
                 self.main_window.information_message(
                     translate('SongUsagePlugin.SongUsageDeleteForm', 'Deletion Successful'),
-                    translate(
-                        'SongUsagePlugin.SongUsageDeleteForm', 'All requested data has been deleted successfully.')
+                    translate('SongUsagePlugin.SongUsageDeleteForm',
+                              'All requested data has been deleted successfully.')
                 )
                 self.accept()
         else:

=== modified file 'openlp/plugins/songusage/forms/songusagedetaildialog.py'
--- openlp/plugins/songusage/forms/songusagedetaildialog.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songusage/forms/songusagedetaildialog.py	2014-01-16 22:00:15 +0000
@@ -35,7 +35,15 @@
 
 
 class Ui_SongUsageDetailDialog(object):
+    """
+    The Song Usage report details
+    """
     def setupUi(self, song_usage_detail_dialog):
+        """
+        Set up the UI
+
+        :param song_usage_detail_dialog:
+        """
         song_usage_detail_dialog.setObjectName('song_usage_detail_dialog')
         song_usage_detail_dialog.resize(609, 413)
         self.vertical_layout = QtGui.QVBoxLayout(song_usage_detail_dialog)
@@ -82,6 +90,11 @@
         self.save_file_push_button.clicked.connect(song_usage_detail_dialog.define_output_location)
 
     def retranslateUi(self, song_usage_detail_dialog):
+        """
+        Retranslate the UI
+
+        :param song_usage_detail_dialog:
+        """
         song_usage_detail_dialog.setWindowTitle(
             translate('SongUsagePlugin.SongUsageDetailForm', 'Song Usage Extraction'))
         self.date_range_group_box.setTitle(translate('SongUsagePlugin.SongUsageDetailForm', 'Select Date Range'))

=== modified file 'openlp/plugins/songusage/forms/songusagedetailform.py'
--- openlp/plugins/songusage/forms/songusagedetailform.py	2013-12-28 21:33:38 +0000
+++ openlp/plugins/songusage/forms/songusagedetailform.py	2014-01-16 22:00:15 +0000
@@ -66,8 +66,8 @@
         """
         Triggered when the Directory selection button is clicked
         """
-        path = QtGui.QFileDialog.getExistingDirectory(self,
-            translate('SongUsagePlugin.SongUsageDetailForm', 'Output File Location'),
+        path = QtGui.QFileDialog.getExistingDirectory(
+            self, translate('SongUsagePlugin.SongUsageDetailForm', 'Output File Location'),
             Settings().value(self.plugin.settings_section + '/last directory export'))
         if path:
             Settings().setValue(self.plugin.settings_section + '/last directory export', path)
@@ -83,7 +83,7 @@
             self.main_window.error_message(
                 translate('SongUsagePlugin.SongUsageDetailForm', 'Output Path Not Selected'),
                 translate('SongUsagePlugin.SongUsageDetailForm', 'You have not set a valid output location for your'
-                    ' song usage report. Please select an existing path on your computer.')
+                          ' song usage report. \nPlease select an existing path on your computer.')
             )
             return
         check_directory_exists(path)
@@ -109,8 +109,8 @@
                 file_handle.write(record.encode('utf-8'))
             self.main_window.information_message(
                 translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation'),
-                translate('SongUsagePlugin.SongUsageDetailForm', 'Report \n%s \n'
-                    'has been successfully created. ') % report_file_name
+                translate('SongUsagePlugin.SongUsageDetailForm',
+                          'Report \n%s \nhas been successfully created. ') % report_file_name
             )
         except IOError:
             log.exception('Failed to write out song usage records')

=== modified file 'openlp/plugins/songusage/lib/db.py'
--- openlp/plugins/songusage/lib/db.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songusage/lib/db.py	2014-01-16 22:00:15 +0000
@@ -48,21 +48,20 @@
     """
     Setup the songusage database connection and initialise the database schema
 
-    ``url``
-        The database to setup
+    :param url: The database to setup
     """
     session, metadata = init_db(url)
 
     songusage_table = Table('songusage_data', metadata,
-        Column('id', types.Integer(), primary_key=True),
-        Column('usagedate', types.Date, index=True, nullable=False),
-        Column('usagetime', types.Time, index=True, nullable=False),
-        Column('title', types.Unicode(255), nullable=False),
-        Column('authors', types.Unicode(255), nullable=False),
-        Column('copyright', types.Unicode(255)),
-        Column('ccl_number', types.Unicode(65)),
-        Column('plugin_name', types.Unicode(20)),
-        Column('source', types.Unicode(10))
+                            Column('id', types.Integer(), primary_key=True),
+                            Column('usagedate', types.Date, index=True, nullable=False),
+                            Column('usagetime', types.Time, index=True, nullable=False),
+                            Column('title', types.Unicode(255), nullable=False),
+                            Column('authors', types.Unicode(255), nullable=False),
+                            Column('copyright', types.Unicode(255)),
+                            Column('ccl_number', types.Unicode(65)),
+                            Column('plugin_name', types.Unicode(20)),
+                            Column('source', types.Unicode(10))
     )
 
     mapper(SongUsageItem, songusage_table)

=== modified file 'openlp/plugins/songusage/songusageplugin.py'
--- openlp/plugins/songusage/songusageplugin.py	2013-12-28 21:33:38 +0000
+++ openlp/plugins/songusage/songusageplugin.py	2014-01-16 22:00:15 +0000
@@ -58,6 +58,9 @@
 
 
 class SongUsagePlugin(Plugin):
+    """
+    Song Usage Plugin class
+    """
     log.info('SongUsage Plugin loaded')
 
     def __init__(self):
@@ -79,31 +82,33 @@
         """
         Give the SongUsage plugin the opportunity to add items to the **Tools** menu.
 
-        ``tools_menu``
-            The actual **Tools** menu item, so that your actions can use it as their parent.
+        :param tools_menu: The actual **Tools** menu item, so that your actions can use it as their parent.
         """
         log.info('add tools menu')
-        self.toolsMenu = tools_menu
+        self.tools_menu = tools_menu
         self.song_usage_menu = QtGui.QMenu(tools_menu)
         self.song_usage_menu.setObjectName('song_usage_menu')
         self.song_usage_menu.setTitle(translate('SongUsagePlugin', '&Song Usage Tracking'))
         # SongUsage Delete
-        self.song_usage_delete = create_action(tools_menu, 'songUsageDelete',
+        self.song_usage_delete = create_action(
+            tools_menu, 'songUsageDelete',
             text=translate('SongUsagePlugin', '&Delete Tracking Data'),
             statustip=translate('SongUsagePlugin', 'Delete song usage data up to a specified date.'),
             triggers=self.on_song_usage_delete)
         # SongUsage Report
-        self.song_usage_report = create_action(tools_menu, 'songUsageReport',
+        self.song_usage_report = create_action(
+            tools_menu, 'songUsageReport',
             text=translate('SongUsagePlugin', '&Extract Tracking Data'),
             statustip=translate('SongUsagePlugin', 'Generate a report on song usage.'),
             triggers=self.on_song_usage_report)
         # SongUsage activation
-        self.song_usage_status = create_action(tools_menu, 'songUsageStatus',
+        self.song_usage_status = create_action(
+            tools_menu, 'songUsageStatus',
             text=translate('SongUsagePlugin', 'Toggle Tracking'),
             statustip=translate('SongUsagePlugin', 'Toggle the tracking of song usage.'), checked=False,
             can_shortcuts=True, triggers=self.toggle_song_usage_state)
         # Add Menus together
-        self.toolsMenu.addAction(self.song_usage_menu.menuAction())
+        self.tools_menu.addAction(self.song_usage_menu.menuAction())
         self.song_usage_menu.addAction(self.song_usage_status)
         self.song_usage_menu.addSeparator()
         self.song_usage_menu.addAction(self.song_usage_report)
@@ -117,7 +122,7 @@
         self.song_usage_active_button.hide()
         # Signals and slots
         QtCore.QObject.connect(self.song_usage_status, QtCore.SIGNAL('visibilityChanged(bool)'),
-            self.song_usage_status.setChecked)
+                               self.song_usage_status.setChecked)
         self.song_usage_active_button.toggled.connect(self.toggle_song_usage_state)
         self.song_usage_menu.menuAction().setVisible(False)
 
@@ -165,8 +170,7 @@
 
     def set_button_state(self):
         """
-        Keep buttons inline.  Turn of signals to stop dead loop but we need the
-        button and check box set correctly.
+        Keep buttons inline.  Turn of signals to stop dead loop but we need the button and check box set correctly.
         """
         self.song_usage_active_button.blockSignals(True)
         self.song_usage_status.blockSignals(True)
@@ -186,12 +190,16 @@
     def display_song_usage(self, item):
         """
         Song Usage for which has been displayed
+
+        :param item: Item displayed
         """
         self._add_song_usage(translate('SongUsagePlugin', 'display'), item)
 
     def print_song_usage(self, item):
         """
         Song Usage for which has been printed
+
+        :param item: Item printed
         """
         self._add_song_usage(translate('SongUsagePlugin', 'printed'), item)
 
@@ -210,15 +218,28 @@
             self.manager.save_object(song_usage_item)
 
     def on_song_usage_delete(self):
+        """
+        Request the delete form to be displayed
+        """
         self.song_usage_delete_form.exec_()
 
     def on_song_usage_report(self):
+        """
+        Display the song usage report generator screen
+
+        """
         self.song_usage_detail_form.initialise()
         self.song_usage_detail_form.exec_()
 
     def about(self):
+        """
+        The plugin about text
+
+        :return: the text to be displayed
+        """
         about_text = translate('SongUsagePlugin',
-            '<strong>SongUsage Plugin</strong><br />This plugin tracks the usage of songs in services.')
+                               '<strong>SongUsage Plugin</strong><br />'
+                               'This plugin tracks the usage of songs in services.')
         return about_text
 
     def set_plugin_text_strings(self):

=== added file 'tests/functional/openlp_core_lib/test_renderer.py'
--- tests/functional/openlp_core_lib/test_renderer.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_core_lib/test_renderer.py	2014-01-16 22:00:15 +0000
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2014 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
+# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+Package to test the openlp.core.ui.renderer package.
+"""
+from unittest import TestCase
+
+from PyQt4 import QtCore
+
+from openlp.core.common import Registry
+from openlp.core.lib import Renderer, ScreenList
+
+from tests.interfaces import MagicMock
+
+SCREEN = {
+    'primary': False,
+    'number': 1,
+    'size': QtCore.QRect(0, 0, 1024, 768)
+}
+
+
+class TestRenderer(TestCase):
+
+    def setUp(self):
+        """
+        Set up the components need for all tests.
+        """
+        # Mocked out desktop object
+        self.desktop = MagicMock()
+        self.desktop.primaryScreen.return_value = SCREEN['primary']
+        self.desktop.screenCount.return_value = SCREEN['number']
+        self.desktop.screenGeometry.return_value = SCREEN['size']
+        self.screens = ScreenList.create(self.desktop)
+        Registry.create()
+
+    def tearDown(self):
+        """
+        Delete QApplication.
+        """
+        del self.screens
+
+    def initial_renderer_test(self):
+        """
+        Test the initial renderer state .
+        """
+        # GIVEN: A new renderer instance.
+        renderer = Renderer()
+        # WHEN: the default renderer is built.
+        # THEN: The renderer should be a live controller.
+        self.assertEqual(renderer.is_live, True, 'The base renderer should be a live controller')
+
+    def default_screen_layout_test(self):
+        """
+        Test the default layout calculations.
+        """
+        # GIVEN: A new renderer instance.
+        renderer = Renderer()
+        # WHEN: given the default screen size has been created.
+        # THEN: The renderer have created a default screen.
+        self.assertEqual(renderer.width, 1024, 'The base renderer should be a live controller')
+        self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller')
+        self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller')
+        self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller')
\ No newline at end of file

=== added file 'tests/functional/openlp_core_ui/test_maindisplay.py'
--- tests/functional/openlp_core_ui/test_maindisplay.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_core_ui/test_maindisplay.py	2014-01-16 22:00:15 +0000
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2014 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
+# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+Package to test the openlp.core.ui.slidecontroller package.
+"""
+from unittest import TestCase
+
+from PyQt4 import QtCore
+
+from openlp.core.common import Registry
+from openlp.core.lib import ScreenList
+from openlp.core.ui import MainDisplay
+
+from tests.interfaces import MagicMock, patch
+
+SCREEN = {
+    'primary': False,
+    'number': 1,
+    'size': QtCore.QRect(0, 0, 1024, 768)
+}
+
+
+class TestMainDisplay(TestCase):
+
+    def setUp(self):
+        """
+        Set up the components need for all tests.
+        """
+        # Mocked out desktop object
+        self.desktop = MagicMock()
+        self.desktop.primaryScreen.return_value = SCREEN['primary']
+        self.desktop.screenCount.return_value = SCREEN['number']
+        self.desktop.screenGeometry.return_value = SCREEN['size']
+        self.screens = ScreenList.create(self.desktop)
+        Registry.create()
+
+    def tearDown(self):
+        """
+        Delete QApplication.
+        """
+        del self.screens
+
+    def initial_main_display_test(self):
+        """
+        Test the initial Main Display state .
+        """
+        # GIVEN: A new slideController instance.
+        display = MagicMock()
+        display.is_live = True
+
+        # WHEN: the default controller is built.
+        main_display = MainDisplay(display)
+
+        # THEN: The controller should not be a live controller.
+        self.assertEqual(main_display.is_live, True, 'The main display should be a live controller')

=== modified file 'tests/functional/openlp_core_ui/test_servicemanager.py'
--- tests/functional/openlp_core_ui/test_servicemanager.py	2014-01-04 17:19:30 +0000
+++ tests/functional/openlp_core_ui/test_servicemanager.py	2014-01-16 22:00:15 +0000
@@ -44,19 +44,11 @@
         Create the UI
         """
         Registry.create()
-        #self.app = QtGui.QApplication([])
-        #ScreenList.create(self.app.desktop())
-        #Registry().register('application', MagicMock())
-        #with patch('openlp.core.lib.PluginManager'):
-        #    self.main_window = MainWindow()
-        #self.service_manager = Registry().get('service_manager')
 
     def tearDown(self):
         """
         Delete all the C++ objects at the end so that we don't have a segfault
         """
-        #del self.main_window
-        #del self.app
         pass
 
     def initial_service_manager_test(self):

=== modified file 'tests/functional/openlp_plugins/remotes/test_router.py'
--- tests/functional/openlp_plugins/remotes/test_router.py	2013-12-24 11:32:56 +0000
+++ tests/functional/openlp_plugins/remotes/test_router.py	2014-01-16 22:00:15 +0000
@@ -87,7 +87,6 @@
         router = HttpRouter()
         router.initialise()
         test_value = 'b3BlbmxwOnBhc3N3b3Jk'
-        print(router.auth)
 
         # THEN: the function should return the correct password
         self.assertEqual(router.auth, test_value,