openlp/openlp/plugins/presentations/lib/presentationcontroller.py

451 lines
14 KiB
Python

# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2012 Raoul Snyman #
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Edwin Lunando, 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 #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
import os
import shutil
from PyQt4 import QtCore
from openlp.core.lib import Receiver, check_directory_exists, create_thumb, \
validate_thumb
from openlp.core.lib.settings import Settings
from openlp.core.utils import AppLocation
log = logging.getLogger(__name__)
class PresentationDocument(object):
"""
Base class for presentation documents to inherit from.
Loads and closes the presentation as well as triggering the correct
activities based on the users input
**Hook Functions**
``load_presentation(presentation)``
Load a presentation file
``close_presentation()``
Close presentation and clean up objects
``presentation_loaded()``
Returns True if presentation is currently loaded
``is_active()``
Returns True if a presentation is currently running
``blank_screen()``
Blanks the screen, making it black.
``unblank_screen()``
Unblanks the screen, restoring the output
``is_blank``
Returns true if screen is blank
``stop_presentation()``
Stops the presentation, removing it from the output display
``start_presentation()``
Starts the presentation from the beginning
``get_slide_number()``
Returns the current slide number, from 1
``get_slide_count()``
Returns total number of slides
``goto_slide(slide_no)``
Jumps directly to the requested slide.
``next_step()``
Triggers the next effect of slide on the running presentation
``previous_step()``
Triggers the previous slide on the running presentation
``get_thumbnail_path(slide_no, check_exists)``
Returns a path to an image containing a preview for the requested slide
"""
def __init__(self, controller, name):
"""
Constructor for the PresentationController class
"""
self.slidenumber = 0
self.controller = controller
self.filepath = name
check_directory_exists(self.get_thumbnail_folder())
def load_presentation(self):
"""
Called when a presentation is added to the SlideController.
Loads the presentation and starts it
``presentation``
The file name of the presentations to the run.
Returns False if the file could not be opened
"""
return False
def presentation_deleted(self):
"""
Cleans up/deletes any controller specific files created for
a file, e.g. thumbnails
"""
try:
shutil.rmtree(self.get_thumbnail_folder())
shutil.rmtree(self.get_temp_folder())
except OSError:
log.exception(u'Failed to delete presentation controller files')
def get_file_name(self):
"""
Return just the filename of the presention, without the directory
"""
return os.path.split(self.filepath)[1]
def get_thumbnail_folder(self):
"""
The location where thumbnail images will be stored
"""
return os.path.join(
self.controller.thumbnail_folder, self.get_file_name())
def get_temp_folder(self):
"""
The location where thumbnail images will be stored
"""
return os.path.join(
self.controller.temp_folder, self.get_file_name())
def check_thumbnails(self):
"""
Returns ``True`` if the thumbnail images exist and are more recent than
the powerpoint file.
"""
lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
if not (lastimage and os.path.isfile(lastimage)):
return False
return validate_thumb(self.filepath, lastimage)
def close_presentation(self):
"""
Close presentation and clean up objects
Triggered by new object being added to SlideController
"""
self.controller.close_presentation()
def is_active(self):
"""
Returns True if a presentation is currently running
"""
return False
def is_loaded(self):
"""
Returns true if a presentation is loaded
"""
return False
def blank_screen(self):
"""
Blanks the screen, making it black.
"""
pass
def unblank_screen(self):
"""
Unblanks (restores) the presentationn
"""
pass
def is_blank(self):
"""
Returns true if screen is blank
"""
return False
def stop_presentation(self):
"""
Stops the presentation, removing it from the output display
"""
pass
def start_presentation(self):
"""
Starts the presentation from the beginning
"""
pass
def get_slide_number(self):
"""
Returns the current slide number, from 1
"""
return 0
def get_slide_count(self):
"""
Returns total number of slides
"""
return 0
def goto_slide(self, slide_no):
"""
Jumps directly to the requested slide.
``slide_no``
The slide to jump to, starting at 1
"""
pass
def next_step(self):
"""
Triggers the next effect of slide on the running presentation
This might be the next animation on the current slide, or the next slide
"""
pass
def previous_step(self):
"""
Triggers the previous slide on the running presentation
"""
pass
def convert_thumbnail(self, file, idx):
"""
Convert the slide image the application made to a standard 320x240
.png image.
"""
if self.check_thumbnails():
return
if os.path.isfile(file):
thumb_path = self.get_thumbnail_path(idx, False)
create_thumb(file, thumb_path, False, QtCore.QSize(320, 240))
def get_thumbnail_path(self, slide_no, check_exists):
"""
Returns an image path containing a preview for the requested slide
``slide_no``
The slide an image is required for, starting at 1
"""
path = os.path.join(self.get_thumbnail_folder(),
self.controller.thumbnail_prefix + unicode(slide_no) + u'.png')
if os.path.isfile(path) or not check_exists:
return path
else:
return None
def poll_slidenumber(self, is_live):
"""
Check the current slide number
"""
if not self.is_active():
return
current = self.get_slide_number()
if current == self.slidenumber:
return
self.slidenumber = current
if is_live:
prefix = u'live'
else:
prefix = u'preview'
Receiver.send_message(u'slidecontroller_%s_change' % prefix,
self.slidenumber - 1)
def get_slide_text(self, slide_no):
"""
Returns the text on the slide
``slide_no``
The slide the text is required for, starting at 1
"""
return ''
def get_slide_notes(self, slide_no):
"""
Returns the text on the slide
``slide_no``
The slide the notes are required for, starting at 1
"""
return ''
class PresentationController(object):
"""
This class is used to control interactions with presentation applications
by creating a runtime environment. This is a base class for presentation
controllers to inherit from.
To create a new controller, take a copy of this file and name it so it ends
with ``controller.py``, i.e. ``foobarcontroller.py``. Make sure it inherits
:class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`,
and then fill in the blanks. If possible try to make sure it loads on all
platforms, usually by using :mod:``os.name`` checks, although
``__init__``, ``check_available`` and ``presentation_deleted`` should
always be implemented.
See :class:`~openlp.plugins.presentations.lib.impresscontroller.ImpressController`,
:class:`~openlp.plugins.presentations.lib.powerpointcontroller.PowerpointController` or
:class:`~openlp.plugins.presentations.lib.pptviewcontroller.PptviewController`
for examples.
**Basic Attributes**
``name``
The name that appears in the options and the media manager
``enabled``
The controller is enabled
``available``
The controller is available on this machine. Set by init via
call to check_available
``plugin``
The presentationplugin object
``supports``
The primary native file types this application supports
``alsosupports``
Other file types the application can import, although not necessarily
the first choice due to potential incompatibilities
**Hook Functions**
``kill()``
Called at system exit to clean up any running presentations
``check_available()``
Returns True if presentation application is installed/can run on this
machine
``presentation_deleted()``
Deletes presentation specific files, e.g. thumbnails
"""
log.info(u'PresentationController loaded')
def __init__(self, plugin=None, name=u'PresentationController',
document_class=PresentationDocument):
"""
This is the constructor for the presentationcontroller object. This
provides an easy way for descendent plugins to populate common data.
This method *must* be overridden, like so::
class MyPresentationController(PresentationController):
def __init__(self, plugin):
PresentationController.__init(
self, plugin, u'My Presenter App')
``plugin``
Defaults to *None*. The presentationplugin object
``name``
Name of the application, to appear in the application
"""
self.supports = []
self.alsosupports = []
self.docs = []
self.plugin = plugin
self.name = name
self.document_class = document_class
self.settings_section = self.plugin.settingsSection
self.available = None
self.temp_folder = os.path.join(
AppLocation.get_section_data_path(self.settings_section), name)
self.thumbnail_folder = os.path.join(
AppLocation.get_section_data_path(self.settings_section),
u'thumbnails')
self.thumbnail_prefix = u'slide'
check_directory_exists(self.thumbnail_folder)
check_directory_exists(self.temp_folder)
def enabled(self):
"""
Return whether the controller is currently enabled
"""
if Settings().value(
self.settings_section + u'/' + self.name,
QtCore.QVariant(QtCore.Qt.Checked)).toInt()[0] == \
QtCore.Qt.Checked:
return self.is_available()
else:
return False
def is_available(self):
if self.available is None:
self.available = self.check_available()
return self.available
def check_available(self):
"""
Presentation app is able to run on this machine
"""
return False
def start_process(self):
"""
Loads a running version of the presentation application in the
background.
"""
pass
def kill(self):
"""
Called at system exit to clean up any running presentations and
close the application
"""
log.debug(u'Kill')
self.close_presentation()
def add_document(self, name):
"""
Called when a new presentation document is opened
"""
document = self.document_class(self, name)
self.docs.append(document)
return document
def remove_doc(self, doc=None):
"""
Called to remove an open document from the collection
"""
log.debug(u'remove_doc Presentation')
if doc is None:
return
if doc in self.docs:
self.docs.remove(doc)
def close_presentation(self):
pass