Comment changes

This commit is contained in:
Tim Bentley 2009-07-10 16:41:24 +01:00
commit 7e6ad29306
9 changed files with 547 additions and 137 deletions

View File

@ -31,42 +31,58 @@ class Plugin(object):
Base class for openlp plugins to inherit from. Base class for openlp plugins to inherit from.
Basic attributes are: Basic attributes are:
* name
``name``
The name that should appear in the plugins list. The name that should appear in the plugins list.
* version
``version``
The version number of this iteration of the plugin. The version number of this iteration of the plugin.
* icon
``icon``
An instance of QIcon, which holds an icon for this plugin. An instance of QIcon, which holds an icon for this plugin.
* config
``config``
An instance of PluginConfig, which allows plugins to read and write to An instance of PluginConfig, which allows plugins to read and write to
openlp.org's configuration. This is pre-instantiated. openlp.org's configuration. This is pre-instantiated.
* log
``log``
A log object used to log debugging messages. This is pre-instantiated. A log object used to log debugging messages. This is pre-instantiated.
Hook functions: Hook functions:
* check_pre_conditions()
``check_pre_conditions()``
Provides the Plugin with a handle to check if it can be loaded. Provides the Plugin with a handle to check if it can be loaded.
* get_media_manager_item()
``get_media_manager_item()``
Returns an instance of MediaManagerItem to be used in the Media Manager. Returns an instance of MediaManagerItem to be used in the Media Manager.
* add_import_menu_item(import_menu)
``add_import_menu_item(import_menu)``
Add an item to the Import menu. Add an item to the Import menu.
* add_export_menu_item(export_menu)
``add_export_menu_item(export_menu)``
Add an item to the Export menu. Add an item to the Export menu.
* get_settings_tab()
``get_settings_tab()``
Returns an instance of SettingsTabItem to be used in the Settings dialog. Returns an instance of SettingsTabItem to be used in the Settings dialog.
* add_to_menu(menubar)
``add_to_menu(menubar)``
A method to add a menu item to anywhere in the menu, given the menu bar. A method to add a menu item to anywhere in the menu, given the menu bar.
* handle_event(event)
``handle_event(event)``
A method use to handle events, given an Event object. A method use to handle events, given an Event object.
* about()
``about()``
Used in the plugin manager, when a person clicks on the 'About' button. Used in the plugin manager, when a person clicks on the 'About' button.
* save(data)
``save(data)``
A method to convert the plugin's data to a string to be stored in the A method to convert the plugin's data to a string to be stored in the
Service file. Service file.
* load(string)
``load(string)``
A method to convert the string from a Service file into the plugin's A method to convert the string from a Service file into the plugin's
own data format. own data format.
* render(theme, screen_number)
``render(theme, screen_number)``
A method used to render something to the screen, given the current theme A method used to render something to the screen, given the current theme
and screen number. and screen number.
""" """
@ -78,11 +94,20 @@ class Plugin(object):
""" """
This is the constructor for the plugin object. This provides an easy This is the constructor for the plugin object. This provides an easy
way for descendent plugins to populate common data. This method *must* way for descendent plugins to populate common data. This method *must*
be overridden, like so: be overridden, like so::
class MyPlugin(Plugin):
def __init__(self): class MyPlugin(Plugin):
Plugin.__init(self, 'MyPlugin', '0.1') def __init__(self):
... Plugin.__init(self, u'MyPlugin', u'0.1')
``name``
Defaults to *None*. The name of the plugin.
``version``
Defaults to *None*. The version of the plugin.
``plugin_helpers``
Defaults to *None*. A list of helper objects.
""" """
if name is not None: if name is not None:
self.name = name self.name = name
@ -107,50 +132,59 @@ class Plugin(object):
def check_pre_conditions(self): def check_pre_conditions(self):
""" """
Provides the Plugin with a handle to check if it can be loaded. Provides the Plugin with a handle to check if it can be loaded.
Returns True or False. Returns True or False.
""" """
return True return True
def get_media_manager_item(self): def get_media_manager_item(self):
""" """
Construct a MediaManagerItem object with all the buttons and things you Construct a MediaManagerItem object with all the buttons and things
need, and return it for integration into openlp.org. you need, and return it for integration into openlp.org.
""" """
pass pass
def add_import_menu_item(self, import_menu): def add_import_menu_item(self, import_menu):
""" """
Create a menu item and add it to the "Import" menu. Create a menu item and add it to the "Import" menu.
``import_menu``
The Import menu.
""" """
pass pass
def add_export_menu_item(self, export_menu): def add_export_menu_item(self, export_menu):
""" """
Create a menu item and add it to the "Export" menu. Create a menu item and add it to the "Export" menu.
``export_menu``
The Export menu
""" """
pass pass
def get_settings_tab(self): def get_settings_tab(self):
""" """
Create a menu item and add it to the "Import" menu. Create a tab for the settings window.
""" """
pass pass
def add_to_menu(self, menubar): def add_to_menu(self, menubar):
""" """
Add menu items to the menu, given the menubar. Add menu items to the menu, given the menubar.
``menubar``
The application's menu bar.
""" """
pass pass
def handle_event(self, event):
"""
Handle the event contained in the event object.
"""
def handle_event(self, event): def handle_event(self, event):
""" """
Handle the event contained in the event object. If you want Handle the event contained in the event object. If you want
to use this default behaviour, you must set self.dnd_id equal to use this default behaviour, you must set self.dnd_id equal
to that sent by the dnd source - eg the MediaItem to that sent by the dnd source - eg the MediaItem
``event``
An object describing the event.
""" """
# default behaviour - can be overridden if desired # default behaviour - can be overridden if desired
log.debug(u'Handle event called with event %s with payload %s'%(event.event_type, event.payload)) log.debug(u'Handle event called with event %s with payload %s'%(event.event_type, event.payload))
@ -175,6 +209,9 @@ class Plugin(object):
""" """
Service item data is passed to this function, which should return a Service item data is passed to this function, which should return a
string which can be written to the service file. string which can be written to the service file.
``data``
The data to be saved.
""" """
pass pass
@ -182,12 +219,21 @@ class Plugin(object):
""" """
A string from the service file is passed in. This function parses and A string from the service file is passed in. This function parses and
sets up the internals of the plugin. sets up the internals of the plugin.
``string``
The data to be loaded into the plugin.
""" """
pass pass
def render(self, theme, screen=None): def render(self, theme, screen=None):
""" """
Render the screenth screenful of data using theme settings in theme. Render the screenth screenful of data using theme settings in theme.
``theme``
The theme to use when rendering.
``screen``
Defaults to *None*. The screen to render to.
""" """
pass pass

View File

@ -29,28 +29,50 @@ class PluginConfig(object):
""" """
Initialise the plugin config object, setting the section name to the Initialise the plugin config object, setting the section name to the
plugin name. plugin name.
``plugin_name``
The name of the plugin to use as a section name.
""" """
self.section = plugin_name.lower() self.section = plugin_name.lower()
def get_config(self, key, default=None): def get_config(self, key, default=None):
""" """
Get a configuration value from the configuration registry. Get a configuration value from the configuration registry.
``key``
The name of configuration to load.
``default``
Defaults to *None*. The default value to return if there is no
other value.
""" """
return ConfigHelper.get_config(self.section, key, default) return ConfigHelper.get_config(self.section, key, default)
def delete_config(self, key): def delete_config(self, key):
""" """
Delete a configuration value from the configuration registry. Delete a configuration value from the configuration registry.
``key``
The name of the configuration to remove.
""" """
return ConfigHelper.delete_config(self.section, key) return ConfigHelper.delete_config(self.section, key)
def set_config(self, key, value): def set_config(self, key, value):
""" """
Set a configuration value in the configuration registry. Set a configuration value in the configuration registry.
``key``
The name of the configuration to save.
``value``
The value of the configuration to save.
""" """
return ConfigHelper.set_config(self.section, key, value) return ConfigHelper.set_config(self.section, key, value)
def get_data_path(self): def get_data_path(self):
"""
Dynamically build the data file path for a plugin.
"""
#app_data = ConfigHelper.get_data_path() #app_data = ConfigHelper.get_data_path()
app_data = ConfigHelper.get_data_path() app_data = ConfigHelper.get_data_path()
safe_name = self.section.replace(u' ',u'-') safe_name = self.section.replace(u' ',u'-')
@ -61,9 +83,21 @@ class PluginConfig(object):
return path return path
def set_data_path(self, path): def set_data_path(self, path):
"""
Set the data file path.
``path``
The path to save.
"""
return self.set_config(u'data path', os.path.basename(path)) return self.set_config(u'data path', os.path.basename(path))
def get_files(self, suffix=None): def get_files(self, suffix=None):
"""
Get a list of files from the data files path.
``suffix``
Defaults to *None*. The extension to search for.
"""
try: try:
files = os.listdir(self.get_data_path()) files = os.listdir(self.get_data_path())
except: except:
@ -86,7 +120,10 @@ class PluginConfig(object):
def load_list(self, name): def load_list(self, name):
""" """
Load a list from the config file Load a list from the config file.
``name``
The name of the list.
""" """
list_count = self.get_config(u'%s count' % name) list_count = self.get_config(u'%s count' % name)
if list_count is not None: if list_count is not None:
@ -102,7 +139,13 @@ class PluginConfig(object):
def set_list(self, name, list): def set_list(self, name, list):
""" """
Save a list to the config file Save a list to the config file.
``name``
The name of the list to save.
``list``
The list of values to save.
""" """
old_count = int(self.get_config(u'%s count' % name, int(0))) old_count = int(self.get_config(u'%s count' % name, int(0)))
new_count = len(list) new_count = len(list)
@ -116,7 +159,10 @@ class PluginConfig(object):
def get_last_dir(self, num=None): def get_last_dir(self, num=None):
""" """
Read the last directory used for plugin Read the last directory used for plugin.
``num``
Defaults to *None*. A further qualifier.
""" """
if num is not None: if num is not None:
name = u'last directory %d' % num name = u'last directory %d' % num
@ -129,7 +175,10 @@ class PluginConfig(object):
def set_last_dir(self, directory, num=None): def set_last_dir(self, directory, num=None):
""" """
Save the last directory used for plugin Save the last directory used for plugin.
``num``
Defaults to *None*. A further qualifier.
""" """
if num is not None: if num is not None:
name = u'last directory %d' % num name = u'last directory %d' % num

View File

@ -34,8 +34,11 @@ class PluginManager(object):
def __init__(self, dir): def __init__(self, dir):
""" """
The constructor for the plugin manager. The constructor for the plugin manager. Passes the controllers on to
Passes the controllers on to the plugins for them to interact with via their ServiceItems the plugins for them to interact with via their ServiceItems.
``dir``
The directory to search for plugins.
""" """
log.info(u'Plugin manager initing') log.info(u'Plugin manager initing')
if not dir in sys.path: if not dir in sys.path:
@ -49,7 +52,16 @@ class PluginManager(object):
def find_plugins(self, dir, plugin_helpers, eventmanager): def find_plugins(self, dir, plugin_helpers, eventmanager):
""" """
Scan the directory dir for objects inheriting from openlp.plugin Scan the directory dir for objects inheriting from ``openlp.plugin``.
``dir``
The directory to scan.
``plugin_helpers``
A list of helper objects to pass to the plugins.
``eventmanager``
The event manager to pass to the plugins.
""" """
self.plugin_helpers = plugin_helpers self.plugin_helpers = plugin_helpers
startdepth = len(os.path.abspath(dir).split(os.sep)) startdepth = len(os.path.abspath(dir).split(os.sep))
@ -104,8 +116,8 @@ class PluginManager(object):
def hook_media_manager(self, mediatoolbox): def hook_media_manager(self, mediatoolbox):
""" """
Loop through all the plugins. If a plugin has a valid media manager item, Loop through all the plugins. If a plugin has a valid media manager
add it to the media manager. item, add it to the media manager.
``mediatoolbox`` ``mediatoolbox``
The Media Manager itself. The Media Manager itself.
@ -118,8 +130,11 @@ class PluginManager(object):
def hook_settings_tabs(self, settingsform=None): def hook_settings_tabs(self, settingsform=None):
""" """
Loop through all the plugins. If a plugin has a valid settings tab item, Loop through all the plugins. If a plugin has a valid settings tab
add it to the settings tab. item, add it to the settings tab.
``settingsform``
Defaults to *None*. The settings form to add tabs to.
""" """
for plugin in self.plugins: for plugin in self.plugins:
settings_tab = plugin.get_settings_tab() settings_tab = plugin.get_settings_tab()
@ -131,24 +146,30 @@ class PluginManager(object):
def hook_import_menu(self, import_menu): def hook_import_menu(self, import_menu):
""" """
Loop through all the plugins and give them an opportunity to add an item Loop through all the plugins and give them an opportunity to add an
to the import menu. item to the import menu.
``import_menu``
The Import menu.
""" """
for plugin in self.plugins: for plugin in self.plugins:
plugin.add_import_menu_item(import_menu) plugin.add_import_menu_item(import_menu)
def hook_export_menu(self, export_menu): def hook_export_menu(self, export_menu):
""" """
Loop through all the plugins and give them an opportunity to add an item Loop through all the plugins and give them an opportunity to add an
to the export menu. item to the export menu.
``export_menu``
The Export menu.
""" """
for plugin in self.plugins: for plugin in self.plugins:
plugin.add_export_menu_item(export_menu) plugin.add_export_menu_item(export_menu)
def initialise_plugins(self): def initialise_plugins(self):
""" """
Loop through all the plugins and give them an opportunity to add an item Loop through all the plugins and give them an opportunity to
to the export menu. initialise themselves.
""" """
for plugin in self.plugins: for plugin in self.plugins:
plugin.initialise() plugin.initialise()

View File

@ -18,12 +18,12 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA Place, Suite 330, Boston, MA 02111-1307 USA
""" """
import logging import logging
import os, os.path import os
import sys import sys
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
class Renderer: class Renderer(object):
""" """
Genarates a pixmap image of a array of text. The Text is formatted to Genarates a pixmap image of a array of text. The Text is formatted to
make sure it fits on the screen and if not extra frames a generated. make sure it fits on the screen and if not extra frames a generated.
@ -33,6 +33,9 @@ class Renderer:
log.info(u'Renderer Loaded') log.info(u'Renderer Loaded')
def __init__(self): def __init__(self):
"""
Initialise the renderer.
"""
self._rect = None self._rect = None
self._debug = 0 self._debug = 0
self._right_margin = 64 # the amount of right indent self._right_margin = 64 # the amount of right indent
@ -47,11 +50,20 @@ class Renderer:
self._bg_frame_small = None self._bg_frame_small = None
def set_debug(self, debug): def set_debug(self, debug):
"""
Set the debug mode of the renderer.
``debug``
The debug mode.
"""
self._debug=debug self._debug=debug
def set_theme(self, theme): def set_theme(self, theme):
""" """
External API to pass in the theme to be used Set the theme to be used.
``theme``
The theme to be used.
""" """
log.debug(u'set theme') log.debug(u'set theme')
self._theme = theme self._theme = theme
@ -64,12 +76,21 @@ class Renderer:
self.set_bg_image(theme.background_filename) self.set_bg_image(theme.background_filename)
def set_bg_image(self, filename): def set_bg_image(self, filename):
"""
Set a background image.
``filename``
The name of the image file.
"""
log.debug(u'set bg image %s', filename) log.debug(u'set bg image %s', filename)
self._bg_image_filename = unicode(filename) self._bg_image_filename = unicode(filename)
if self._frame is not None: if self._frame is not None:
self.scale_bg_image() self.scale_bg_image()
def scale_bg_image(self): def scale_bg_image(self):
"""
Scale the background image to fit the screen.
"""
assert self._frame assert self._frame
preview = QtGui.QImage(self._bg_image_filename) preview = QtGui.QImage(self._bg_image_filename)
width = self._frame.width() width = self._frame.width()
@ -89,7 +110,16 @@ class Renderer:
def set_frame_dest(self, frame_width, frame_height, preview=False): def set_frame_dest(self, frame_width, frame_height, preview=False):
""" """
External API to pass the frame size to be painted Set the size of the slide.
``frame_width``
The width of the slide.
``frame_height``
The height of the slide.
``preview``
Defaults to *False*. Whether or not to generate a preview.
""" """
if preview == True: if preview == True:
self._bg_frame = None self._bg_frame = None
@ -103,7 +133,14 @@ class Renderer:
def format_slide(self, words, footer): def format_slide(self, words, footer):
""" """
External API to sort out the text to pe placed on the frame Figure out how much text can appear on a slide, using the current
theme settings.
``words``
The words to be fitted on the slide.
``footer``
The footer of the slide.
""" """
log.debug(u'format_slide - Start') log.debug(u'format_slide - Start')
verses = [] verses = []
@ -120,15 +157,28 @@ class Renderer:
def set_text_rectangle(self, rect_main, rect_footer): def set_text_rectangle(self, rect_main, rect_footer):
""" """
Sets the rectangle within which text should be rendered Sets the rectangle within which text should be rendered.
``rect_main``
The main text block.
``rect_footer``
The footer text block.
""" """
self._rect = rect_main self._rect = rect_main
self._rect_footer = rect_footer self._rect_footer = rect_footer
def generate_frame_from_lines(self, lines, footer_lines=None): def generate_frame_from_lines(self, lines, footer_lines=None):
""" """
Render a set of lines according to the theme, return bounding box Render a set of lines according to the theme, and return the block
""" dimensions.
``lines``
The lines to be rendered.
``footer_lines``
Defaults to *None*. The footer to render.
"""
log.debug(u'generate_frame_from_lines - Start') log.debug(u'generate_frame_from_lines - Start')
#print "Render Lines ", lines #print "Render Lines ", lines
bbox = self._render_lines_unaligned(lines, False) bbox = self._render_lines_unaligned(lines, False)
@ -139,14 +189,15 @@ class Renderer:
x, y = self._correctAlignment(self._rect, bbox) x, y = self._correctAlignment(self._rect, bbox)
bbox = self._render_lines_unaligned(lines, False, (x, y), True) bbox = self._render_lines_unaligned(lines, False, (x, y), True)
if footer_lines is not None: if footer_lines is not None:
bbox = self._render_lines_unaligned(footer_lines, True, (self._rect_footer.left(), self._rect_footer.top()), True ) bbox = self._render_lines_unaligned(footer_lines, True,
(self._rect_footer.left(), self._rect_footer.top()), True)
log.debug(u'generate_frame_from_lines - Finish') log.debug(u'generate_frame_from_lines - Finish')
return self._frame return self._frame
def _generate_background_frame(self): def _generate_background_frame(self):
""" """
Generate a background frame to the same size as the frame to be used Generate a background frame to the same size as the frame to be used.
Results cached for performance reasons. Results are cached for performance reasons.
""" """
assert(self._theme) assert(self._theme)
self._bg_frame = QtGui.QImage(self._frame.width(), self._frame.height(), self._bg_frame = QtGui.QImage(self._frame.width(), self._frame.height(),
@ -196,11 +247,19 @@ class Renderer:
def _split_set_of_lines(self, lines, footer): def _split_set_of_lines(self, lines, footer):
""" """
Given a list of lines, decide how to split them best if they don't all fit on the screen Given a list of lines, decide how to split them best if they don't all
- this is done by splitting at 1/2, 1/3 or 1/4 of the set fit on the screen. This is done by splitting at 1/2, 1/3 or 1/4 of the
If it doesn't fit, even at this size, just split at each opportunity. set. If it doesn't fit, even at this size, just split at each
We'll do this by getting the bounding box of each line, and then summing them appropriately opportunity. We'll do this by getting the bounding box of each line,
Returns a list of [lists of lines], one set for each screenful and then summing them appropriately.
Returns a list of [lists of lines], one set for each screenful.
``lines``
The lines of text to split.
``footer``
The footer text.
""" """
bboxes = [] bboxes = []
for line in lines: for line in lines:
@ -254,6 +313,15 @@ class Renderer:
return retval return retval
def _correctAlignment(self, rect, bbox): def _correctAlignment(self, rect, bbox):
"""
Corrects the vertical alignment of text.
``rect``
The block dimentions.
``bbox``
Footer dimensions?
"""
x = rect.left() x = rect.left()
if int(self._theme.display_verticalAlign) == 0: if int(self._theme.display_verticalAlign) == 0:
# top align # top align
@ -268,13 +336,26 @@ class Renderer:
log.error(u'Invalid value for theme.VerticalAlign:%s' % self._theme.display_verticalAlign) log.error(u'Invalid value for theme.VerticalAlign:%s' % self._theme.display_verticalAlign)
return x, y return x, y
def _render_lines_unaligned(self, lines, footer, tlcorner=(0,0), live=False): def _render_lines_unaligned(self, lines, footer, tlcorner=(0, 0), live=False):
""" """
Given a list of lines to render, render each one in turn Given a list of lines to render, render each one in turn (using the
(using the _render_single_line fn - which may result in going ``_render_single_line`` fn - which may result in going off the bottom).
off the bottom) They are expected to be pre-arranged to less They are expected to be pre-arranged to less than a screenful (eg. by
than a screenful (eg. by using split_set_of_lines) using split_set_of_lines).
Returns the bounding box of the text as QRect
Returns the bounding box of the text as QRect.
``lines``
The lines of text to render.
``footer``
The slide footer.
``tlcorner``
Defaults to *``(0, 0)``*. Co-ordinates of the top left corner.
``live``
Defaults to *False*. Whether or not this is a live screen.
""" """
x, y = tlcorner x, y = tlcorner
brx = x brx = x
@ -282,25 +363,37 @@ class Renderer:
for line in lines: for line in lines:
# render after current bottom, but at original left edge # render after current bottom, but at original left edge
# keep track of right edge to see which is biggest # keep track of right edge to see which is biggest
(thisx, bry) = self._render_and_wrap_single_line(line, footer, (x , bry), live) (thisx, bry) = self._render_and_wrap_single_line(line, footer, (x, bry), live)
if (thisx > brx): if (thisx > brx):
brx = thisx brx = thisx
retval = QtCore.QRect(x, y,brx-x, bry-y) retval = QtCore.QRect(x, y, brx - x, bry - y)
if self._debug: if self._debug:
painter = QtGui.QPainter() painter = QtGui.QPainter()
painter.begin(self._frame) painter.begin(self._frame)
painter.setPen(QtGui.QPen(QtGui.QColor(0,0,255))) painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 255)))
painter.drawRect(retval) painter.drawRect(retval)
painter.end() painter.end()
return retval return retval
def _render_and_wrap_single_line(self, line, footer, tlcorner=(0,0), live=False): def _render_and_wrap_single_line(self, line, footer, tlcorner=(0, 0), live=False):
""" """
Render a single line of words onto the DC, top left corner Render a single line of words onto the DC, top left corner specified.
specified. If the line is too wide for the context, it wraps, but right-aligns
If the line is too wide for the context, it wraps, but the surplus words in the manner of song lyrics.
right-aligns the surplus words in the manner of song lyrics
Returns the bottom-right corner (of what was rendered) as a tuple(x, y). Returns the bottom-right corner (of what was rendered) as a tuple(x, y).
``line``
Line of text to be rendered.
``footer``
The footer of the slide.
``tlcorner``
Defaults to *``(0, 0)``*. The top left corner.
``live``
Defaults to *False*. Whether or not this is a live screen.
""" """
x, y = tlcorner x, y = tlcorner
# We draw the text to see how big it is and then iterate to make it fit # We draw the text to see how big it is and then iterate to make it fit
@ -397,6 +490,9 @@ class Renderer:
return brcorner return brcorner
def _set_theme_font(self): def _set_theme_font(self):
"""
Set the fonts from the current theme settings.
"""
self.footerFont = QtGui.QFont(self._theme.font_footer_name, self.footerFont = QtGui.QFont(self._theme.font_footer_name,
int(self._theme.font_footer_proportion), # size int(self._theme.font_footer_proportion), # size
QtGui.QFont.Normal, # weight QtGui.QFont.Normal, # weight
@ -408,11 +504,26 @@ class Renderer:
0)# italic 0)# italic
self.mainFont.setPixelSize(int(self._theme.font_main_proportion)) self.mainFont.setPixelSize(int(self._theme.font_main_proportion))
def _get_extent_and_render(self, line, footer, tlcorner=(0,0), draw=False, color=None): def _get_extent_and_render(self, line, footer, tlcorner=(0, 0), draw=False, color=None):
""" """
Find bounding box of text - as render_single_line. Find bounding box of text - as render_single_line. If draw is set,
If draw is set, actually draw the text to the current DC as well actually draw the text to the current DC as well return width and
return width and height of text as a tuple (w,h) height of text as a tuple (w, h).
``line``
The line of text to render.
``footer``
The footer text.
``tlcorner``
Defaults to *``(0, 0)``*. The top left corner co-ordinates.
``draw``
Defaults to *False*. Draw the text to the current surface.
``color``
Defaults to *None*. The colour to draw with.
""" """
# setup defaults # setup defaults
painter = QtGui.QPainter() painter = QtGui.QPainter()
@ -424,7 +535,7 @@ class Renderer:
else: else:
font = self.mainFont font = self.mainFont
painter.setFont(font) painter.setFont(font)
if color == None: if color is None:
if footer: if footer:
painter.setPen(QtGui.QColor(self._theme.font_footer_color)) painter.setPen(QtGui.QColor(self._theme.font_footer_color))
else: else:
@ -443,7 +554,13 @@ class Renderer:
def snoop_Image(self, image, image2=None): def snoop_Image(self, image, image2=None):
""" """
Debugging method to allow images to be viewed Debugging method to allow images to be viewed.
``image``
An image to save to disk.
``image2``
Defaults to *None*. Another image to save to disk.
""" """
im = image.toImage() im = image.toImage()
im.save(u'renderer.png', u'png') im.save(u'renderer.png', u'png')

View File

@ -18,26 +18,37 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA Place, Suite 330, Boston, MA 02111-1307 USA
""" """
import logging import logging
import os, os.path import os
import sys
from PyQt4 import QtGui, QtCore
from renderer import Renderer
import sys import sys
import linecache import linecache
class RenderManager: from PyQt4 import QtGui, QtCore
from renderer import Renderer
class RenderManager(object):
""" """
Class to pull all Renderer interactions into one place. Class to pull all Renderer interactions into one place. The plugins will
The plugins will call helper methods to do the rendering but call helper methods to do the rendering but this class will provide
this class will provide display defense code. display defense code.
""" """
global log global log
log=logging.getLogger(u'RenderManager') log=logging.getLogger(u'RenderManager')
log.info(u'RenderManager Loaded') log.info(u'RenderManager Loaded')
def __init__(self, theme_manager, screen_list, screen_number=0): def __init__(self, theme_manager, screen_list, screen_number=0):
"""
Initialise the render manager.
``theme_manager``
The ThemeManager instance, used to get the current theme details.
``screen_list``
The list of screens available.
``screen_number``
Defaults to *0*. The index of the output/display screen.
"""
log.debug(u'Initilisation started') log.debug(u'Initilisation started')
self.screen_list = screen_list self.screen_list = screen_list
self.theme_manager = theme_manager self.theme_manager = theme_manager
@ -52,21 +63,46 @@ class RenderManager:
def update_display(self, screen_number): def update_display(self, screen_number):
""" """
Updates the render manager's information about the current screen. Updates the render manager's information about the current screen.
``screen_number``
The updated index of the output/display screen.
""" """
log.debug(u'Update Display') log.debug(u'Update Display')
if self.current_display != screen_number: if self.current_display != screen_number:
self.current_display = screen_number self.current_display = screen_number
self.calculate_default(self.screen_list[self.current_display][u'size']) self.calculate_default(self.screen_list[self.current_display][u'size'])
def set_global_theme(self, global_theme, global_style = u'Global'): def set_global_theme(self, global_theme, global_style=u'Global'):
"""
Set the global-level theme and the theme level.
``global_theme``
The global-level theme to be set.
``global_style``
Defaults to *"Global"*. The theme level, can be "Global",
"Service" or "Song".
"""
self.global_theme = global_theme self.global_theme = global_theme
self.global_style = global_style self.global_style = global_style
def set_service_theme(self, service_theme): def set_service_theme(self, service_theme):
"""
Set the service-level theme.
``service_theme``
The service-level theme to be set.
"""
self.service_theme = service_theme self.service_theme = service_theme
def set_override_theme(self, theme): def set_override_theme(self, theme):
log.debug(u'set override theme to %s', theme) """
Set the appropriate theme depending on the theme level.
``theme``
The name of the song-level theme.
"""
log.debug(u'set override theme to %s', theme)
if self.global_style == u'Global': if self.global_style == u'Global':
self.theme = self.global_theme self.theme = self.global_theme
elif self.global_style == u'Service': elif self.global_style == u'Service':
@ -84,7 +120,7 @@ class RenderManager:
self.theme = self.service_theme self.theme = self.service_theme
else: else:
self.theme = self.global_theme self.theme = self.global_theme
if self.theme is not self.renderer.theme_name: if self.theme != self.renderer.theme_name:
log.debug(u'theme is now %s', self.theme) log.debug(u'theme is now %s', self.theme)
self.themedata = self.theme_manager.getThemeData(self.theme) self.themedata = self.theme_manager.getThemeData(self.theme)
self.calculate_default(self.screen_list[self.current_display][u'size']) self.calculate_default(self.screen_list[self.current_display][u'size'])
@ -92,7 +128,13 @@ class RenderManager:
self.build_text_rectangle(self.themedata) self.build_text_rectangle(self.themedata)
def build_text_rectangle(self, theme): def build_text_rectangle(self, theme):
log.debug(u'build_text_rectangle ') """
Builds a text block using the settings in ``theme``.
``theme``
The theme to build a text block for.
"""
log.debug(u'build_text_rectangle')
main_rect = None main_rect = None
footer_rect = None footer_rect = None
if theme.font_main_override == False: if theme.font_main_override == False:
@ -108,31 +150,52 @@ class RenderManager:
self.renderer.set_text_rectangle(main_rect,footer_rect) self.renderer.set_text_rectangle(main_rect,footer_rect)
def generate_preview(self, themedata): def generate_preview(self, themedata):
"""
Generate a preview of a theme.
``themedata``
The theme to generated a preview for.
"""
log.debug(u'generate preview') log.debug(u'generate preview')
self.calculate_default(QtCore.QSize(1024, 768)) self.calculate_default(QtCore.QSize(1024, 768))
self.renderer.set_theme(themedata) self.renderer.set_theme(themedata)
self.build_text_rectangle(themedata) self.build_text_rectangle(themedata)
self.renderer.set_frame_dest(self.width, self.height, True) self.renderer.set_frame_dest(self.width, self.height, True)
lines = [] verse = []
lines.append(u'Amazing Grace!') verse.append(u'Amazing Grace!')
lines.append(u'How sweet the sound') verse.append(u'How sweet the sound')
lines.append(u'To save a wretch like me;') verse.append(u'To save a wretch like me;')
lines.append(u'I once was lost but now am found,') verse.append(u'I once was lost but now am found,')
lines.append(u'Was blind, but now I see.') verse.append(u'Was blind, but now I see.')
lines1 = [] footer = []
lines1.append(u'Amazing Grace (John Newton)' ) footer.append(u'Amazing Grace (John Newton)' )
lines1.append(u'Public Domain') footer.append(u'Public Domain')
lines1.append(u'CCLI xxx') footer.append(u'CCLI xxx')
return self.renderer.generate_frame_from_lines(lines, lines1) return self.renderer.generate_frame_from_lines(verse, footer)
def format_slide(self, words): def format_slide(self, words):
"""
Calculate how much text can fid on a slide.
``words``
The words to go on the slides.
"""
log.debug(u'format slide') log.debug(u'format slide')
self.calculate_default(self.screen_list[self.current_display][u'size']) self.calculate_default(self.screen_list[self.current_display][u'size'])
self.build_text_rectangle(self.themedata) self.build_text_rectangle(self.themedata)
self.renderer.set_frame_dest(self.width, self.height) self.renderer.set_frame_dest(self.width, self.height)
return self.renderer.format_slide(words, False) return self.renderer.format_slide(words, False)
def generate_slide(self,main_text, footer_text): def generate_slide(self, main_text, footer_text):
"""
Generate the actual slide image.
``main_text``
The text for the main area of the slide.
``footer_text``
The text for the slide footer.
"""
log.debug(u'generate slide') log.debug(u'generate slide')
self.calculate_default(self.screen_list[self.current_display][u'size']) self.calculate_default(self.screen_list[self.current_display][u'size'])
self.build_text_rectangle(self.themedata) self.build_text_rectangle(self.themedata)
@ -140,6 +203,12 @@ class RenderManager:
return self.renderer.generate_frame_from_lines(main_text, footer_text) return self.renderer.generate_frame_from_lines(main_text, footer_text)
def resize_image(self, image): def resize_image(self, image):
"""
Resize an image to fit on the current screen.
``image``
The image to resize.
"""
preview = QtGui.QImage(image) preview = QtGui.QImage(image)
w = self.width w = self.width
h = self.height h = self.height
@ -154,13 +223,19 @@ class RenderManager:
return newImage return newImage
def calculate_default(self, screen): def calculate_default(self, screen):
log.debug(u'calculate default %s' , screen) """
Calculate the default dimentions of the screen.
``screen``
The QWidget instance of the screen.
"""
log.debug(u'calculate default %s', screen)
if self.current_display == 0: if self.current_display == 0:
self.width = 1024 self.width = 1024
self.height = 768 self.height = 768
else: else:
self.width = screen.width() self.width = screen.width()
self.height = screen.height() self.height = screen.height()
log.debug(u'calculate default %d,%d' , self.width, self.height) log.debug(u'calculate default %d, %d', self.width, self.height)
# 90% is start of footer # 90% is start of footer
self.footer_start = int(self.height * 0.90) self.footer_start = int(self.height * 0.90)

View File

@ -20,10 +20,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA
import logging import logging
import os import os
import time import time
from openlp.core.lib import buildIcon
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
class ServiceItem(): from openlp.core.lib import buildIcon
class ServiceItem(object):
""" """
The service item is a base class for the plugins to use to interact with The service item is a base class for the plugins to use to interact with
the service manager, the slide controller, and the projection screen the service manager, the slide controller, and the projection screen
@ -35,7 +37,10 @@ class ServiceItem():
def __init__(self, hostplugin=None): def __init__(self, hostplugin=None):
""" """
Init Method Set up the service item.
``hostplugin``
The plugin that this service item belongs to.
""" """
self.plugin = hostplugin self.plugin = hostplugin
if hostplugin is not None: if hostplugin is not None:
@ -56,6 +61,14 @@ class ServiceItem():
self.service_frames = [] self.service_frames = []
def addIcon(self, icon): def addIcon(self, icon):
"""
Add an icon to the service item. This is used when displaying the
service item in the service manager.
``icon``
An instance of QIcon or a string to an icon in the resource or on
disk.
"""
self.icon = icon self.icon = icon
self.iconic_representation = buildIcon(icon) self.iconic_representation = buildIcon(icon)
@ -89,27 +102,63 @@ class ServiceItem():
else: else:
log.error(u'Invalid value renderer :%s' % self.service_item_type) log.error(u'Invalid value renderer :%s' % self.service_item_type)
def add_from_image(self, path, frame_title, image): def add_from_image(self, path, frame_title, image):
"""
Add an image slide to the service item.
``path``
The directory in which the image file is located.
``frame_title``
A title for the slide in the service item.
``image``
The actual image file name.
"""
self.service_item_type = u'image' self.service_item_type = u'image'
self.service_item_path = path self.service_item_path = path
self.service_frames.append({u'title': frame_title, u'image': image}) self.service_frames.append({u'title': frame_title, u'image': image})
def add_from_text(self, frame_title, raw_slide): def add_from_text(self, frame_title, raw_slide):
"""
Add a text slide to the service item.
``frame_title``
The title of the slide in the service item.
``raw_slide``
The raw text of the slide.
"""
self.service_item_type = u'text' self.service_item_type = u'text'
frame_title = frame_title.split(u'\n')[0] frame_title = frame_title.split(u'\n')[0]
self.service_frames.append({u'title': frame_title, u'raw_slide': raw_slide}) self.service_frames.append({u'title': frame_title, u'raw_slide': raw_slide})
def add_from_command(self, frame_title, command): def add_from_command(self, frame_title, command):
"""
Add a slide from a command.
``frame_title``
The title of the slide in the service item.
``command``
The command of/for the slide.
"""
self.service_item_type = u'command' self.service_item_type = u'command'
self.service_frames.append({u'title': frame_title, u'command': command}) self.service_frames.append({u'title': frame_title, u'command': command})
def get_oos_repr(self): def get_oos_repr(self):
""" """
This method returns some text which can be saved into the OOS This method returns some text which can be saved into the OOS
file to represent this item file to represent this item.
""" """
oos_header = {u'plugin': self.shortname,u'theme':self.theme, u'title':self.title, oos_header = {
u'icon':self.icon, u'footer':self.raw_footer, u'type':self.service_item_type} u'plugin': self.shortname,
u'theme':self.theme,
u'title':self.title,
u'icon':self.icon,
u'footer':self.raw_footer,
u'type':self.service_item_type
}
oos_data = [] oos_data = []
if self.service_item_type == u'text': if self.service_item_type == u'text':
for slide in self.service_frames: for slide in self.service_frames:
@ -124,8 +173,14 @@ class ServiceItem():
def set_from_oos(self, serviceitem, path=None): def set_from_oos(self, serviceitem, path=None):
""" """
This method takes some oos list (passed from the ServiceManager) This method takes a service item from a saved service file (passed
and extracts the data actually required from the ServiceManager) and extracts the data actually required.
``serviceitem``
The item to extract data from.
``path``
Defaults to *None*. Any path data, usually for images.
""" """
#print "sfs", serviceitem #print "sfs", serviceitem
header = serviceitem[u'serviceitem'][u'header'] header = serviceitem[u'serviceitem'][u'header']

View File

@ -23,11 +23,20 @@ from openlp.core.lib import PluginConfig
class SettingsTab(QtGui.QWidget): class SettingsTab(QtGui.QWidget):
""" """
SettingsTab is a helper widget for plugins to define Tabs for the settings dialog. SettingsTab is a helper widget for plugins to define Tabs for the settings
dialog.
""" """
def __init__(self, title=None, section=None): def __init__(self, title=None, section=None):
""" """
Constructor to create the Steetings tab item. Constructor to create the Settings tab item.
``title``
Defaults to *None*. The title of the tab, which is usually
displayed on the tab.
``section``
Defaults to *None*. This is the section in the configuration file
to write to when the ``save`` method is called.
""" """
QtGui.QWidget.__init__(self) QtGui.QWidget.__init__(self)
self.tabTitle = title self.tabTitle = title
@ -43,6 +52,9 @@ class SettingsTab(QtGui.QWidget):
def setTitle(self, title): def setTitle(self, title):
""" """
Set the title of the tab. Set the title of the tab.
``title``
The title of the tab, which is usually displayed on the tab.
""" """
self.tabTitle = title self.tabTitle = title

View File

@ -30,21 +30,34 @@ from xml.etree.ElementTree import ElementTree, XML, dump
""" """
import logging import logging
from xml.dom.minidom import Document from xml.dom.minidom import Document
from xml.etree.ElementTree import ElementTree, XML, dump from xml.etree.ElementTree import ElementTree, XML, dump
class SongXMLBuilder(): class SongXMLBuilder():
"""
This class builds the XML used to describe songs.
"""
def __init__(self): def __init__(self):
"""
Set up the song builder.
"""
# Create the minidom document # Create the minidom document
self.song_xml = Document() self.song_xml = Document()
def new_document(self): def new_document(self):
"""
Create a new song XML document.
"""
# Create the <song> base element # Create the <song> base element
self.song = self.song_xml.createElement(u'song') self.song = self.song_xml.createElement(u'song')
self.song_xml.appendChild(self.song) self.song_xml.appendChild(self.song)
self.song.setAttribute(u'version', u'1.0') self.song.setAttribute(u'version', u'1.0')
def add_lyrics_to_song(self): def add_lyrics_to_song(self):
"""
Set up and add a ``<lyrics>`` tag which contains the lyrics of the
song.
"""
# Create the main <lyrics> element # Create the main <lyrics> element
self.lyrics = self.song_xml.createElement(u'lyrics') self.lyrics = self.song_xml.createElement(u'lyrics')
self.lyrics.setAttribute(u'language', u'en') self.lyrics.setAttribute(u'language', u'en')
@ -52,50 +65,72 @@ class SongXMLBuilder():
def add_verse_to_lyrics(self, type, number, content): def add_verse_to_lyrics(self, type, number, content):
""" """
type - type of verse (Chorus, Verse , Bridge, Custom etc Add a verse to the ``<lyrics>`` tag.
number - number of item eg verse 1
content - the text to be stored ``type``
A string denoting the type of verse. Possible values are "Chorus",
"Verse", "Bridge", and "Custom".
``number``
An integer denoting the number of the item, for example: verse 1.
``content``
The actual text of the verse to be stored.
""" """
verse = self.song_xml.createElement(u'verse') verse = self.song_xml.createElement(u'verse')
verse.setAttribute(u'type', type) verse.setAttribute(u'type', type)
verse.setAttribute(u'label', number) verse.setAttribute(u'label', number)
self.lyrics.appendChild(verse) self.lyrics.appendChild(verse)
# add data as a CDATA section to protect the XML from special chars
# add data as a CDATA section
cds = self.song_xml.createCDATASection(content) cds = self.song_xml.createCDATASection(content)
verse.appendChild(cds) verse.appendChild(cds)
def dump_xml(self): def dump_xml(self):
# Debugging aid to see what we have """
Debugging aid to dump XML so that we can see what we have.
"""
print self.song_xml.toprettyxml(indent=u' ') print self.song_xml.toprettyxml(indent=u' ')
def extract_xml(self): def extract_xml(self):
# Print our newly created XML """
Extract our newly created XML song.
"""
return self.song_xml.toxml(u'utf-8') return self.song_xml.toxml(u'utf-8')
class SongXMLParser(): class SongXMLParser():
"""
A class to read in and parse a song's XML.
"""
global log global log
log = logging.getLogger(u'SongXMLParser') log = logging.getLogger(u'SongXMLParser')
log.info(u'SongXMLParser Loaded') log.info(u'SongXMLParser Loaded')
def __init__(self, xml): def __init__(self, xml):
#print xml """
Set up our song XML parser.
``xml``
The XML of the song to be parsed.
"""
try: try:
self.song_xml = ElementTree(element=XML(xml)) self.song_xml = ElementTree(element=XML(xml))
except: except:
#print "invalid xml ", xml log.debug(u'Invalid xml %s', xml)
log.debug(u'invalid xml %s', xml)
def get_verses(self): def get_verses(self):
#return a list of verse's and attributes """
iter=self.song_xml.getiterator() Iterates through the verses in the XML and returns a list of verses
and their attributes.
"""
iter = self.song_xml.getiterator()
verse_list = [] verse_list = []
for element in iter: for element in iter:
#print element.tag, element.attrib, element.text
if element.tag == u'verse': if element.tag == u'verse':
verse_list.append([element.attrib, element.text]) verse_list.append([element.attrib, element.text])
return verse_list return verse_list
def dump_xml(self): def dump_xml(self):
# Debugging aid to see what we have """
Debugging aid to dump XML so that we can see what we have.
"""
print dump(self.song_xml) print dump(self.song_xml)

View File

@ -345,5 +345,5 @@ class ThemeXML(object):
s = u'' s = u''
for k in dir(self): for k in dir(self):
if k[0:1] != u'_': if k[0:1] != u'_':
s += u'%30s : %s\n' %(k, getattr(self, k)) s += u'%30s: %s\n' %(k, getattr(self, k))
return s return s