Common thumbnails for all presentation types and create when presentation first added

bzr-revno: 932
This commit is contained in:
Jonathan Corwin 2010-07-12 08:25:17 +01:00
commit e77d6ec981
10 changed files with 364 additions and 118 deletions

View File

@ -174,6 +174,8 @@ def resize_image(image, width, height):
""" """
preview = QtGui.QImage(image) preview = QtGui.QImage(image)
if not preview.isNull(): if not preview.isNull():
if preview.width() == width and preview.height == height:
return preview
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio, preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation) QtCore.Qt.SmoothTransformation)
realw = preview.width() realw = preview.width()

View File

@ -379,14 +379,17 @@ class MediaManagerItem(QtGui.QWidget):
""" """
Validates to see if the file still exists or thumbnail is up to date Validates to see if the file still exists or thumbnail is up to date
""" """
if os.path.exists(file): if not os.path.exists(file):
return False
if os.path.exists(thumb):
filedate = os.stat(file).st_mtime filedate = os.stat(file).st_mtime
thumbdate = os.stat(thumb).st_mtime thumbdate = os.stat(thumb).st_mtime
#if file updated rebuild icon #if file updated rebuild icon
if filedate > thumbdate: if filedate > thumbdate:
self.iconFromFile(file, thumb) self.iconFromFile(file, thumb)
return True else:
return False self.iconFromFile(file, thumb)
return True
def iconFromFile(self, file, thumb): def iconFromFile(self, file, thumb):
""" """

View File

@ -74,6 +74,7 @@ class ImpressController(PresentationController):
self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx'] self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx']
self.process = None self.process = None
self.desktop = None self.desktop = None
self.manager = None
def check_available(self): def check_available(self):
""" """
@ -104,6 +105,10 @@ class ImpressController(PresentationController):
self.process.waitForStarted() self.process.waitForStarted()
def get_uno_desktop(self): def get_uno_desktop(self):
"""
On non-Windows platforms, use Uno. Get the OpenOffice desktop
which will be used to manage impress
"""
log.debug(u'get UNO Desktop Openoffice') log.debug(u'get UNO Desktop Openoffice')
ctx = None ctx = None
loop = 0 loop = 0
@ -134,10 +139,19 @@ class ImpressController(PresentationController):
return None return None
def get_com_desktop(self): def get_com_desktop(self):
"""
On Windows platforms, use COM. Return the desktop object which
will be used to manage Impress
"""
log.debug(u'get COM Desktop OpenOffice') log.debug(u'get COM Desktop OpenOffice')
if not self.manager:
return None
return self.manager.createInstance(u'com.sun.star.frame.Desktop') return self.manager.createInstance(u'com.sun.star.frame.Desktop')
def get_com_servicemanager(self): def get_com_servicemanager(self):
"""
Return the OOo service manager for windows
"""
log.debug(u'get_com_servicemanager openoffice') log.debug(u'get_com_servicemanager openoffice')
try: try:
return Dispatch(u'com.sun.star.ServiceManager') return Dispatch(u'com.sun.star.ServiceManager')
@ -171,13 +185,23 @@ class ImpressController(PresentationController):
log.exception(u'Failed to terminate OpenOffice') log.exception(u'Failed to terminate OpenOffice')
def add_doc(self, name): def add_doc(self, name):
"""
Called when a new Impress document is opened
"""
log.debug(u'Add Doc OpenOffice') log.debug(u'Add Doc OpenOffice')
doc = ImpressDocument(self, name) doc = ImpressDocument(self, name)
self.docs.append(doc) self.docs.append(doc)
return doc return doc
class ImpressDocument(PresentationDocument): class ImpressDocument(PresentationDocument):
"""
Class which holds information and controls a single presentation
"""
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
"""
Constructor, store information about the file and initialise
"""
log.debug(u'Init Presentation OpenOffice') log.debug(u'Init Presentation OpenOffice')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
self.document = None self.document = None
@ -208,9 +232,8 @@ class ImpressDocument(PresentationDocument):
desktop = self.controller.get_uno_desktop() desktop = self.controller.get_uno_desktop()
url = uno.systemPathToFileUrl(self.filepath) url = uno.systemPathToFileUrl(self.filepath)
if desktop is None: if desktop is None:
return return False
self.desktop = desktop self.desktop = desktop
#print "s.dsk2 ", self.desktop
properties = [] properties = []
properties.append(self.create_property(u'Minimized', True)) properties.append(self.create_property(u'Minimized', True))
properties = tuple(properties) properties = tuple(properties)
@ -219,12 +242,13 @@ class ImpressDocument(PresentationDocument):
0, properties) 0, properties)
except: except:
log.exception(u'Failed to load presentation') log.exception(u'Failed to load presentation')
return return False
self.presentation = self.document.getPresentation() self.presentation = self.document.getPresentation()
self.presentation.Display = \ self.presentation.Display = \
self.controller.plugin.renderManager.screens.current_display + 1 self.controller.plugin.renderManager.screens.current_display + 1
self.control = None self.control = None
self.create_thumbnails() self.create_thumbnails()
return True
def create_thumbnails(self): def create_thumbnails(self):
""" """
@ -234,30 +258,36 @@ class ImpressDocument(PresentationDocument):
if self.check_thumbnails(): if self.check_thumbnails():
return return
if os.name == u'nt': if os.name == u'nt':
thumbdir = u'file:///' + self.thumbnailpath.replace( thumbdirurl = u'file:///' + self.get_temp_folder().replace(
u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20') u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20')
else: else:
thumbdir = uno.systemPathToFileUrl(self.thumbnailpath) thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder())
props = [] props = []
props.append(self.create_property(u'FilterName', u'impress_png_Export')) props.append(self.create_property(u'FilterName', u'impress_png_Export'))
props = tuple(props) props = tuple(props)
doc = self.document doc = self.document
pages = doc.getDrawPages() pages = doc.getDrawPages()
if not os.path.isdir(self.get_temp_folder()):
os.makedirs(self.get_temp_folder())
for idx in range(pages.getCount()): for idx in range(pages.getCount()):
page = pages.getByIndex(idx) page = pages.getByIndex(idx)
doc.getCurrentController().setCurrentPage(page) doc.getCurrentController().setCurrentPage(page)
path = u'%s/%s%s.png' % (thumbdir, self.controller.thumbnailprefix, urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1))
unicode(idx + 1)) path = os.path.join(self.get_temp_folder(),
unicode(idx + 1) + u'.png')
try: try:
doc.storeToURL(path , props) doc.storeToURL(urlpath, props)
preview = resize_image(path, 640, 480) self.convert_thumbnail(path, idx + 1)
if os.path.exists(path): if os.path.exists(path):
os.remove(path) os.remove(path)
preview.save(path, u'png')
except: except:
log.exception(u'%s - Unable to store openoffice preview' % path) log.exception(u'%s - Unable to store openoffice preview' % path)
def create_property(self, name, value): def create_property(self, name, value):
"""
Create an OOo style property object which are passed into some
Uno methods
"""
log.debug(u'create property OpenOffice') log.debug(u'create property OpenOffice')
if os.name == u'nt': if os.name == u'nt':
prop = self.controller.manager.\ prop = self.controller.manager.\
@ -288,6 +318,9 @@ class ImpressDocument(PresentationDocument):
self.controller.remove_doc(self) self.controller.remove_doc(self)
def is_loaded(self): def is_loaded(self):
"""
Returns true if a presentation is loaded
"""
log.debug(u'is loaded OpenOffice') log.debug(u'is loaded OpenOffice')
#print "is_loaded " #print "is_loaded "
if self.presentation is None or self.document is None: if self.presentation is None or self.document is None:
@ -302,6 +335,9 @@ class ImpressDocument(PresentationDocument):
return True return True
def is_active(self): def is_active(self):
"""
Returns true if a presentation is active and running
"""
log.debug(u'is active OpenOffice') log.debug(u'is active OpenOffice')
#print "is_active " #print "is_active "
if not self.is_loaded(): if not self.is_loaded():
@ -313,10 +349,16 @@ class ImpressDocument(PresentationDocument):
return True return True
def unblank_screen(self): def unblank_screen(self):
"""
Unblanks the screen
"""
log.debug(u'unblank screen OpenOffice') log.debug(u'unblank screen OpenOffice')
return self.control.resume() return self.control.resume()
def blank_screen(self): def blank_screen(self):
"""
Blanks the screen
"""
log.debug(u'blank screen OpenOffice') log.debug(u'blank screen OpenOffice')
self.control.blankScreen(0) self.control.blankScreen(0)
@ -331,6 +373,9 @@ class ImpressDocument(PresentationDocument):
return False return False
def stop_presentation(self): def stop_presentation(self):
"""
Stop the presentation, remove from screen
"""
log.debug(u'stop presentation OpenOffice') log.debug(u'stop presentation OpenOffice')
# deactivate should hide the screen according to docs, but doesn't # deactivate should hide the screen according to docs, but doesn't
#self.control.deactivate() #self.control.deactivate()
@ -338,6 +383,9 @@ class ImpressDocument(PresentationDocument):
self.control = None self.control = None
def start_presentation(self): def start_presentation(self):
"""
Start the presentation from the beginning
"""
log.debug(u'start presentation OpenOffice') log.debug(u'start presentation OpenOffice')
if self.control is None or not self.control.isRunning(): if self.control is None or not self.control.isRunning():
self.presentation.start() self.presentation.start()
@ -354,12 +402,21 @@ class ImpressDocument(PresentationDocument):
self.goto_slide(1) self.goto_slide(1)
def get_slide_number(self): def get_slide_number(self):
"""
Return the current slide number on the screen, from 1
"""
return self.control.getCurrentSlideIndex() + 1 return self.control.getCurrentSlideIndex() + 1
def get_slide_count(self): def get_slide_count(self):
"""
Return the total number of slides
"""
return self.document.getDrawPages().getCount() return self.document.getDrawPages().getCount()
def goto_slide(self, slideno): def goto_slide(self, slideno):
"""
Go to a specific slide (from 1)
"""
self.control.gotoSlideIndex(slideno-1) self.control.gotoSlideIndex(slideno-1)
def next_step(self): def next_step(self):

View File

@ -35,9 +35,13 @@ from openlp.plugins.presentations.lib import MessageListener
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# We have to explicitly create separate classes for each plugin
# in order for DnD to the Service manager to work correctly.
class PresentationListView(BaseListWithDnD): class PresentationListView(BaseListWithDnD):
"""
Class for the list of Presentations
We have to explicitly create separate classes for each plugin
in order for DnD to the Service manager to work correctly.
"""
def __init__(self, parent=None): def __init__(self, parent=None):
self.PluginName = u'Presentations' self.PluginName = u'Presentations'
BaseListWithDnD.__init__(self, parent) BaseListWithDnD.__init__(self, parent)
@ -45,11 +49,14 @@ class PresentationListView(BaseListWithDnD):
class PresentationMediaItem(MediaManagerItem): class PresentationMediaItem(MediaManagerItem):
""" """
This is the Presentation media manager item for Presentation Items. This is the Presentation media manager item for Presentation Items.
It can present files using Openoffice It can present files using Openoffice and Powerpoint
""" """
log.info(u'Presentations Media Item loaded') log.info(u'Presentations Media Item loaded')
def __init__(self, parent, icon, title, controllers): def __init__(self, parent, icon, title, controllers):
"""
Constructor. Setup defaults
"""
self.controllers = controllers self.controllers = controllers
self.PluginNameShort = u'Presentation' self.PluginNameShort = u'Presentation'
self.pluginNameVisible = translate('PresentationPlugin.MediaItem', self.pluginNameVisible = translate('PresentationPlugin.MediaItem',
@ -63,6 +70,9 @@ class PresentationMediaItem(MediaManagerItem):
self.message_listener = MessageListener(self) self.message_listener = MessageListener(self)
def retranslateUi(self): def retranslateUi(self):
"""
The name of the plugin media displayed in UI
"""
self.OnNewPrompt = translate('PresentationPlugin.MediaItem', self.OnNewPrompt = translate('PresentationPlugin.MediaItem',
'Select Presentation(s)') 'Select Presentation(s)')
self.Automatic = translate('PresentationPlugin.MediaItem', self.Automatic = translate('PresentationPlugin.MediaItem',
@ -80,12 +90,18 @@ class PresentationMediaItem(MediaManagerItem):
'Presentations (%s)' % fileType) 'Presentations (%s)' % fileType)
def requiredIcons(self): def requiredIcons(self):
"""
Set which icons the media manager tab should show
"""
MediaManagerItem.requiredIcons(self) MediaManagerItem.requiredIcons(self)
self.hasFileIcon = True self.hasFileIcon = True
self.hasNewIcon = False self.hasNewIcon = False
self.hasEditIcon = False self.hasEditIcon = False
def addEndHeaderBar(self): def addEndHeaderBar(self):
"""
Display custom media manager items for presentations
"""
self.PresentationWidget = QtGui.QWidget(self) self.PresentationWidget = QtGui.QWidget(self)
sizePolicy = QtGui.QSizePolicy( sizePolicy = QtGui.QSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
@ -109,15 +125,13 @@ class PresentationMediaItem(MediaManagerItem):
self.pageLayout.addWidget(self.PresentationWidget) self.pageLayout.addWidget(self.PresentationWidget)
def initialise(self): def initialise(self):
self.servicePath = os.path.join( """
AppLocation.get_section_data_path(self.settingsSection), Populate the media manager tab
u'thumbnails') """
self.listView.setIconSize(QtCore.QSize(88, 50)) self.listView.setIconSize(QtCore.QSize(88, 50))
if not os.path.exists(self.servicePath):
os.mkdir(self.servicePath)
list = SettingsManager.load_list( list = SettingsManager.load_list(
self.settingsSection, u'presentations') self.settingsSection, u'presentations')
self.loadList(list) self.loadList(list, True)
for item in self.controllers: for item in self.controllers:
#load the drop down selection #load the drop down selection
if self.controllers[item].enabled: if self.controllers[item].enabled:
@ -126,7 +140,12 @@ class PresentationMediaItem(MediaManagerItem):
self.DisplayTypeComboBox.insertItem(0, self.Automatic) self.DisplayTypeComboBox.insertItem(0, self.Automatic)
self.DisplayTypeComboBox.setCurrentIndex(0) self.DisplayTypeComboBox.setCurrentIndex(0)
def loadList(self, list): def loadList(self, list, initialLoad=False):
"""
Add presentations into the media manager
This is called both on initial load of the plugin to populate with
existing files, and when the user adds new files via the media manager
"""
currlist = self.getFileList() currlist = self.getFileList()
titles = [] titles = []
for file in currlist: for file in currlist:
@ -136,40 +155,43 @@ class PresentationMediaItem(MediaManagerItem):
continue continue
filename = os.path.split(unicode(file))[1] filename = os.path.split(unicode(file))[1]
if titles.count(filename) > 0: if titles.count(filename) > 0:
QtGui.QMessageBox.critical( if not initialLoad:
self, translate('PresentationPlugin.MediaItem', QtGui.QMessageBox.critical(
'File exists'), self, translate('PresentationPlugin.MediaItem',
'File exists'),
translate('PresentationPlugin.MediaItem', translate('PresentationPlugin.MediaItem',
'A presentation with that filename already exists.'), 'A presentation with that filename already exists.'),
QtGui.QMessageBox.Ok) QtGui.QMessageBox.Ok)
else: continue
icon = None controller_name = self.findControllerByType(filename)
for controller in self.controllers: if controller_name:
thumbPath = os.path.join( controller = self.controllers[controller_name]
AppLocation.get_section_data_path( doc = controller.add_doc(unicode(file))
self.settingsSection), thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png')
u'thumbnails', controller, filename) preview = doc.get_thumbnail_path(1, True)
thumb = os.path.join(thumbPath, u'slide1.png') if not preview and not initialLoad:
preview = os.path.join( doc.load_presentation()
AppLocation.get_section_data_path( preview = doc.get_thumbnail_path(1, True)
self.settingsSection), doc.close_presentation()
controller, u'thumbnails', filename, u'slide1.png') if preview and self.validate(preview, thumb):
if os.path.exists(preview): icon = build_icon(thumb)
if os.path.exists(thumb): else:
if self.validate(preview, thumb):
icon = build_icon(thumb)
else:
icon = build_icon(
u':/general/general_delete.png')
else:
os.makedirs(thumbPath)
icon = self.iconFromFile(preview, thumb)
if not icon:
icon = build_icon(u':/general/general_delete.png') icon = build_icon(u':/general/general_delete.png')
item_name = QtGui.QListWidgetItem(filename) else:
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) if initialLoad:
item_name.setIcon(icon) icon = build_icon(u':/general/general_delete.png')
self.listView.addItem(item_name) else:
QtGui.QMessageBox.critical(
self, translate('PresentationPlugin.MediaItem',
'Unsupported file'),
translate('PresentationPlugin.MediaItem',
'This type of presentation is not supported'),
QtGui.QMessageBox.Ok)
continue
item_name = QtGui.QListWidgetItem(filename)
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
item_name.setIcon(icon)
self.listView.addItem(item_name)
def onDeleteClick(self): def onDeleteClick(self):
""" """
@ -184,8 +206,6 @@ class PresentationMediaItem(MediaManagerItem):
for item in items: for item in items:
filepath = unicode(item.data( filepath = unicode(item.data(
QtCore.Qt.UserRole).toString()) QtCore.Qt.UserRole).toString())
#not sure of this has errors
#John please can you look at .
for cidx in self.controllers: for cidx in self.controllers:
doc = self.controllers[cidx].add_doc(filepath) doc = self.controllers[cidx].add_doc(filepath)
doc.presentation_deleted() doc.presentation_deleted()
@ -196,6 +216,11 @@ class PresentationMediaItem(MediaManagerItem):
self.settingsSection, self.getFileList()) self.settingsSection, self.getFileList())
def generateSlideData(self, service_item, item=None): def generateSlideData(self, service_item, item=None):
"""
Load the relevant information for displaying the presentation
in the slidecontroller. In the case of powerpoints, an image
for each slide
"""
items = self.listView.selectedIndexes() items = self.listView.selectedIndexes()
if len(items) > 1: if len(items) > 1:
return False return False
@ -213,20 +238,27 @@ class PresentationMediaItem(MediaManagerItem):
controller = self.controllers[service_item.shortname] controller = self.controllers[service_item.shortname]
(path, name) = os.path.split(filename) (path, name) = os.path.split(filename)
doc = controller.add_doc(filename) doc = controller.add_doc(filename)
if doc.get_slide_preview_file(1) is None: if doc.get_thumbnail_path(1, True) is None:
doc.load_presentation() doc.load_presentation()
i = 1 i = 1
img = doc.get_slide_preview_file(i) img = doc.get_thumbnail_path(i, True)
while img: while img:
service_item.add_from_command(path, name, img) service_item.add_from_command(path, name, img)
i = i + 1 i = i + 1
img = doc.get_slide_preview_file(i) img = doc.get_thumbnail_path(i, True)
doc.close_presentation() doc.close_presentation()
return True return True
else: else:
return False return False
def findControllerByType(self, filename): def findControllerByType(self, filename):
"""
Determine the default application controller to use for the selected
file type. This is used if "Automatic" is set as the preferred
controller. Find the first (alphabetic) enabled controller which
"supports" the extension. If none found, then look for a controller
which "alsosupports" it instead.
"""
filetype = os.path.splitext(filename)[1] filetype = os.path.splitext(filename)[1]
if not filetype: if not filetype:
return None return None

View File

@ -41,17 +41,28 @@ class Controller(object):
log.info(u'Controller loaded') log.info(u'Controller loaded')
def __init__(self, live): def __init__(self, live):
"""
Constructor
"""
self.is_live = live self.is_live = live
self.doc = None self.doc = None
log.info(u'%s controller loaded' % live) log.info(u'%s controller loaded' % live)
def add_handler(self, controller, file, is_blank): def add_handler(self, controller, file, is_blank):
"""
Add a handler, which is an instance of a presentation and
slidecontroller combination. If the slidecontroller has a display
then load the presentation.
"""
log.debug(u'Live = %s, add_handler %s' % (self.is_live, file)) log.debug(u'Live = %s, add_handler %s' % (self.is_live, file))
self.controller = controller self.controller = controller
if self.doc is not None: if self.doc is not None:
self.shutdown() self.shutdown()
self.doc = self.controller.add_doc(file) self.doc = self.controller.add_doc(file)
self.doc.load_presentation() if not self.doc.load_presentation():
# Display error message to user
# Inform slidecontroller that the action failed?
return
if self.is_live: if self.is_live:
self.doc.start_presentation() self.doc.start_presentation()
if is_blank: if is_blank:
@ -60,6 +71,10 @@ class Controller(object):
self.doc.slidenumber = 0 self.doc.slidenumber = 0
def activate(self): def activate(self):
"""
Active the presentation, and show it on the screen.
Use the last slide number.
"""
log.debug(u'Live = %s, activate' % self.is_live) log.debug(u'Live = %s, activate' % self.is_live)
if self.doc.is_active(): if self.doc.is_active():
return return
@ -71,6 +86,9 @@ class Controller(object):
self.doc.goto_slide(self.doc.slidenumber) self.doc.goto_slide(self.doc.slidenumber)
def slide(self, slide): def slide(self, slide):
"""
Go to a specific slide
"""
log.debug(u'Live = %s, slide' % self.is_live) log.debug(u'Live = %s, slide' % self.is_live)
if not self.is_live: if not self.is_live:
return return
@ -152,6 +170,9 @@ class Controller(object):
#self.timer.stop() #self.timer.stop()
def blank(self): def blank(self):
"""
Instruct the controller to blank the presentation
"""
log.debug(u'Live = %s, blank' % self.is_live) log.debug(u'Live = %s, blank' % self.is_live)
if not self.is_live: if not self.is_live:
return return
@ -162,6 +183,9 @@ class Controller(object):
self.doc.blank_screen() self.doc.blank_screen()
def stop(self): def stop(self):
"""
Instruct the controller to stop and hide the presentation
"""
log.debug(u'Live = %s, stop' % self.is_live) log.debug(u'Live = %s, stop' % self.is_live)
if not self.is_live: if not self.is_live:
return return
@ -172,6 +196,9 @@ class Controller(object):
self.doc.stop_presentation() self.doc.stop_presentation()
def unblank(self): def unblank(self):
"""
Instruct the controller to unblank the presentation
"""
log.debug(u'Live = %s, unblank' % self.is_live) log.debug(u'Live = %s, unblank' % self.is_live)
if not self.is_live: if not self.is_live:
return return
@ -246,6 +273,9 @@ class MessageListener(object):
controller.add_handler(self.controllers[self.handler], file, is_blank) controller.add_handler(self.controllers[self.handler], file, is_blank)
def slide(self, message): def slide(self, message):
"""
React to the message to move to a specific slide
"""
is_live = message[1] is_live = message[1]
slide = message[2] slide = message[2]
if is_live: if is_live:
@ -254,6 +284,9 @@ class MessageListener(object):
self.preview_handler.slide(slide) self.preview_handler.slide(slide)
def first(self, message): def first(self, message):
"""
React to the message to move to the first slide
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.first() self.live_handler.first()
@ -261,6 +294,9 @@ class MessageListener(object):
self.preview_handler.first() self.preview_handler.first()
def last(self, message): def last(self, message):
"""
React to the message to move to the last slide
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.last() self.live_handler.last()
@ -268,6 +304,9 @@ class MessageListener(object):
self.preview_handler.last() self.preview_handler.last()
def next(self, message): def next(self, message):
"""
React to the message to move to the next animation/slide
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.next() self.live_handler.next()
@ -275,6 +314,9 @@ class MessageListener(object):
self.preview_handler.next() self.preview_handler.next()
def previous(self, message): def previous(self, message):
"""
React to the message to move to the previous animation/slide
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.previous() self.live_handler.previous()
@ -282,6 +324,10 @@ class MessageListener(object):
self.preview_handler.previous() self.preview_handler.previous()
def shutdown(self, message): def shutdown(self, message):
"""
React to message to shutdown the presentation. I.e. end the show
and close the file
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
Receiver.send_message(u'maindisplay_show') Receiver.send_message(u'maindisplay_show')
@ -290,19 +336,34 @@ class MessageListener(object):
self.preview_handler.shutdown() self.preview_handler.shutdown()
def hide(self, message): def hide(self, message):
"""
React to the message to show the desktop
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.stop() self.live_handler.stop()
def blank(self, message): def blank(self, message):
"""
React to the message to blank the display
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.blank() self.live_handler.blank()
def unblank(self, message): def unblank(self, message):
"""
React to the message to unblank the display
"""
is_live = message[1] is_live = message[1]
if is_live: if is_live:
self.live_handler.unblank() self.live_handler.unblank()
def timeout(self): def timeout(self):
"""
The presentation may be timed or might be controlled by the
application directly, rather than through OpenLP. Poll occassionally
to check which slide is currently displayed so the slidecontroller
view can be updated
"""
self.live_handler.poll() self.live_handler.poll()

View File

@ -97,13 +97,23 @@ class PowerpointController(PresentationController):
self.process = None self.process = None
def add_doc(self, name): def add_doc(self, name):
"""
Called when a new powerpoint document is opened
"""
log.debug(u'Add Doc PowerPoint') log.debug(u'Add Doc PowerPoint')
doc = PowerpointDocument(self, name) doc = PowerpointDocument(self, name)
self.docs.append(doc) self.docs.append(doc)
return doc return doc
class PowerpointDocument(PresentationDocument): class PowerpointDocument(PresentationDocument):
"""
Class which holds information and controls a single presentation
"""
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
"""
Constructor, store information about the file and initialise
"""
log.debug(u'Init Presentation Powerpoint') log.debug(u'Init Presentation Powerpoint')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
self.presentation = None self.presentation = None
@ -111,22 +121,23 @@ class PowerpointDocument(PresentationDocument):
def load_presentation(self): def load_presentation(self):
""" """
Called when a presentation is added to the SlideController. Called when a presentation is added to the SlideController.
It builds the environment, starts communcations with the background Opens the PowerPoint file using the process created earlier
OpenOffice task started earlier. If OpenOffice is not present is is
started. Once the environment is available the presentation is loaded
and started.
``presentation`` ``presentation``
The file name of the presentations to run. The file name of the presentations to run.
""" """
log.debug(u'LoadPresentation') log.debug(u'LoadPresentation')
if not self.controller.process.Visible: if not self.controller.process or not self.controller.process.Visible:
self.controller.start_process() self.controller.start_process()
self.controller.process.Presentations.Open(self.filepath, False, False, try:
True) self.controller.process.Presentations.Open(self.filepath, False,
False, True)
except pywintypes.com_error:
return False
self.presentation = self.controller.process.Presentations( self.presentation = self.controller.process.Presentations(
self.controller.process.Presentations.Count) self.controller.process.Presentations.Count)
self.create_thumbnails() self.create_thumbnails()
return True
def create_thumbnails(self): def create_thumbnails(self):
""" """
@ -139,8 +150,8 @@ class PowerpointDocument(PresentationDocument):
""" """
if self.check_thumbnails(): if self.check_thumbnails():
return return
self.presentation.Export(os.path.join(self.thumbnailpath, ''), 'png', self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''),
320, 240) 'png', 320, 240)
def close_presentation(self): def close_presentation(self):
""" """
@ -298,4 +309,4 @@ class PowerpointDocument(PresentationDocument):
shape = shapes(idx + 1) shape = shapes(idx + 1)
if shape.HasTextFrame: if shape.HasTextFrame:
text += shape.TextFrame.TextRange.Text + '\n' text += shape.TextFrame.TextRange.Text + '\n'
return text return text

View File

@ -93,13 +93,22 @@ class PptviewController(PresentationController):
self.docs[0].close_presentation() self.docs[0].close_presentation()
def add_doc(self, name): def add_doc(self, name):
"""
Called when a new powerpoint document is opened
"""
log.debug(u'Add Doc PPTView') log.debug(u'Add Doc PPTView')
doc = PptviewDocument(self, name) doc = PptviewDocument(self, name)
self.docs.append(doc) self.docs.append(doc)
return doc return doc
class PptviewDocument(PresentationDocument): class PptviewDocument(PresentationDocument):
"""
Class which holds information and controls a single presentation
"""
def __init__(self, controller, presentation): def __init__(self, controller, presentation):
"""
Constructor, store information about the file and initialise
"""
log.debug(u'Init Presentation PowerPoint') log.debug(u'Init Presentation PowerPoint')
PresentationDocument.__init__(self, controller, presentation) PresentationDocument.__init__(self, controller, presentation)
self.presentation = None self.presentation = None
@ -117,17 +126,31 @@ class PptviewDocument(PresentationDocument):
The file name of the presentations to run. The file name of the presentations to run.
""" """
log.debug(u'LoadPresentation') log.debug(u'LoadPresentation')
#if self.pptid >= 0:
# self.close_presentation()
rendermanager = self.controller.plugin.renderManager rendermanager = self.controller.plugin.renderManager
rect = rendermanager.screens.current[u'size'] rect = rendermanager.screens.current[u'size']
rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom()) rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom())
filepath = str(self.filepath.replace(u'/', u'\\')) filepath = str(self.filepath.replace(u'/', u'\\'))
if not os.path.isdir(self.get_temp_folder()):
os.makedirs(self.get_temp_folder())
self.pptid = self.controller.process.OpenPPT(filepath, None, rect, self.pptid = self.controller.process.OpenPPT(filepath, None, rect,
str(os.path.join(self.thumbnailpath, str(self.get_temp_folder()) + '\\slide')
self.controller.thumbnailprefix))) if self.pptid >= 0:
if self.pptid: self.create_thumbnails()
self.stop_presentation() self.stop_presentation()
return True
else:
return False
def create_thumbnails(self):
"""
PPTviewLib creates large BMP's, but we want small PNG's for consistency.
Convert them here.
"""
if self.check_thumbnails():
return
for idx in range(self.get_slide_count()):
path = u'%s\\slide%s.bmp' % (self.get_temp_folder(), unicode(idx + 1))
self.convert_thumbnail(path, idx + 1)
def close_presentation(self): def close_presentation(self):
""" """
@ -224,17 +247,3 @@ class PptviewDocument(PresentationDocument):
""" """
self.controller.process.PrevStep(self.pptid) self.controller.process.PrevStep(self.pptid)
def get_slide_preview_file(self, slide_no):
"""
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.thumbnailpath,
self.controller.thumbnailprefix + unicode(slide_no) + u'.bmp')
if os.path.isfile(path):
return path
else:
return None

View File

@ -29,7 +29,7 @@ import shutil
from PyQt4 import QtCore from PyQt4 import QtCore
from openlp.core.lib import Receiver from openlp.core.lib import Receiver, resize_image
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -63,6 +63,13 @@ class PresentationController(object):
``plugin`` ``plugin``
The presentationplugin object 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** **Hook Functions**
``kill()`` ``kill()``
@ -109,12 +116,16 @@ class PresentationController(object):
QtCore.Qt.Checked QtCore.Qt.Checked
else: else:
self.enabled = False self.enabled = False
self.thumbnailroot = os.path.join( 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), AppLocation.get_section_data_path(self.settings_section),
name, u'thumbnails') u'thumbnails')
self.thumbnailprefix = u'slide' self.thumbnail_prefix = u'slide'
if not os.path.isdir(self.thumbnailroot): if not os.path.isdir(self.thumbnail_folder):
os.makedirs(self.thumbnailroot) os.makedirs(self.thumbnail_folder)
if not os.path.isdir(self.temp_folder):
os.makedirs(self.temp_folder)
def check_available(self): def check_available(self):
""" """
@ -208,14 +219,19 @@ class PresentationDocument(object):
``previous_step()`` ``previous_step()``
Triggers the previous slide on the running presentation Triggers the previous slide on the running presentation
``get_slide_preview_file(slide_no)`` ``get_thumbnail_path(slide_no, check_exists)``
Returns a path to an image containing a preview for the requested slide Returns a path to an image containing a preview for the requested slide
""" """
def __init__(self, controller, name): def __init__(self, controller, name):
"""
Constructor for the PresentationController class
"""
self.slidenumber = 0 self.slidenumber = 0
self.controller = controller self.controller = controller
self.store_filename(name) self.filepath = name
if not os.path.isdir(self.get_thumbnail_folder()):
os.mkdir(self.get_thumbnail_folder())
def load_presentation(self): def load_presentation(self):
""" """
@ -224,9 +240,10 @@ class PresentationDocument(object):
``presentation`` ``presentation``
The file name of the presentations to the run. The file name of the presentations to the run.
Returns False if the file could not be opened
""" """
pass return False
def presentation_deleted(self): def presentation_deleted(self):
""" """
@ -234,33 +251,37 @@ class PresentationDocument(object):
a file, e.g. thumbnails a file, e.g. thumbnails
""" """
try: try:
shutil.rmtree(self.thumbnailpath) shutil.rmtree(self.get_thumbnail_folder())
shutil.rmtree(self.get_temp_folder())
except OSError: except OSError:
log.exception(u'Failed to delete presentation controller files') log.exception(u'Failed to delete presentation controller files')
def store_filename(self, presentation): def get_file_name(self):
""" """
Set properties for the filename and thumbnail paths Return just the filename of the presention, without the directory
""" """
self.filepath = presentation return os.path.split(self.filepath)[1]
self.filename = self.get_file_name(presentation)
self.thumbnailpath = self.get_thumbnail_path(presentation)
if not os.path.isdir(self.thumbnailpath):
os.mkdir(self.thumbnailpath)
def get_file_name(self, presentation): def get_thumbnail_folder(self):
return os.path.split(presentation)[1] """
The location where thumbnail images will be stored
def get_thumbnail_path(self, presentation): """
return os.path.join( return os.path.join(
self.controller.thumbnailroot, self.get_file_name(presentation)) 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): def check_thumbnails(self):
""" """
Returns true if the thumbnail images look to exist and are more Returns true if the thumbnail images look to exist and are more
recent than the powerpoint recent than the powerpoint
""" """
lastimage = self.get_slide_preview_file(self.get_slide_count()) lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
if not (lastimage and os.path.isfile(lastimage)): if not (lastimage and os.path.isfile(lastimage)):
return False return False
imgdate = os.stat(lastimage).st_mtime imgdate = os.stat(lastimage).st_mtime
@ -350,16 +371,27 @@ class PresentationDocument(object):
""" """
pass pass
def get_slide_preview_file(self, slide_no): 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):
img = resize_image(file, 320, 240)
img.save(self.get_thumbnail_path(idx, False))
def get_thumbnail_path(self, slide_no, check_exists):
""" """
Returns an image path containing a preview for the requested slide Returns an image path containing a preview for the requested slide
``slide_no`` ``slide_no``
The slide an image is required for, starting at 1 The slide an image is required for, starting at 1
""" """
path = os.path.join(self.thumbnailpath, path = os.path.join(self.get_thumbnail_folder(),
self.controller.thumbnailprefix + unicode(slide_no) + u'.png') self.controller.thumbnail_prefix + unicode(slide_no) + u'.png')
if os.path.isfile(path): if os.path.isfile(path) or not check_exists:
return path return path
else: else:
return None return None

View File

@ -32,10 +32,16 @@ class PresentationTab(SettingsTab):
PresentationsTab is the Presentations settings tab in the settings dialog. PresentationsTab is the Presentations settings tab in the settings dialog.
""" """
def __init__(self, title, controllers): def __init__(self, title, controllers):
"""
Constructor
"""
self.controllers = controllers self.controllers = controllers
SettingsTab.__init__(self, title) SettingsTab.__init__(self, title)
def setupUi(self): def setupUi(self):
"""
Create the controls for the settings tab
"""
self.setObjectName(u'PresentationTab') self.setObjectName(u'PresentationTab')
self.tabTitleVisible = translate('PresentationPlugin.PresentationTab', self.tabTitleVisible = translate('PresentationPlugin.PresentationTab',
'Presentations') 'Presentations')
@ -89,6 +95,9 @@ class PresentationTab(SettingsTab):
self.PresentationLayout.addWidget(self.PresentationRightWidget) self.PresentationLayout.addWidget(self.PresentationRightWidget)
def retranslateUi(self): def retranslateUi(self):
"""
Make any translation changes
"""
self.VerseDisplayGroupBox.setTitle( self.VerseDisplayGroupBox.setTitle(
translate('PresentationPlugin.PresentationTab', translate('PresentationPlugin.PresentationTab',
'Available Controllers')) 'Available Controllers'))
@ -100,6 +109,9 @@ class PresentationTab(SettingsTab):
translate('PresentationPlugin.PresentationTab', 'available'))) translate('PresentationPlugin.PresentationTab', 'available')))
def load(self): def load(self):
"""
Load the settings.
"""
for key in self.controllers: for key in self.controllers:
controller = self.controllers[key] controller = self.controllers[key]
if controller.available: if controller.available:
@ -109,6 +121,9 @@ class PresentationTab(SettingsTab):
QtCore.QVariant(0)).toInt()[0]) QtCore.QVariant(0)).toInt()[0])
def save(self): def save(self):
"""
Save the settings.
"""
for key in self.controllers: for key in self.controllers:
controller = self.controllers[key] controller = self.controllers[key]
checkbox = self.PresenterCheckboxes[controller.name] checkbox = self.PresenterCheckboxes[controller.name]

View File

@ -33,9 +33,17 @@ from openlp.plugins.presentations.lib import *
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class PresentationPlugin(Plugin): class PresentationPlugin(Plugin):
"""
This plugin allowed a Presentation to be opened, controlled and displayed
on the output display. The plugin controls third party applications such
as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer
"""
log = logging.getLogger(u'PresentationPlugin') log = logging.getLogger(u'PresentationPlugin')
def __init__(self, plugin_helpers): def __init__(self, plugin_helpers):
"""
PluginPresentation constructor.
"""
log.debug(u'Initialised') log.debug(u'Initialised')
self.controllers = {} self.controllers = {}
Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers) Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers)
@ -51,6 +59,10 @@ class PresentationPlugin(Plugin):
return PresentationTab(self.name, self.controllers) return PresentationTab(self.name, self.controllers)
def initialise(self): def initialise(self):
"""
Initialise the plugin. Determine which controllers are enabled
are start their processes.
"""
log.info(u'Presentations Initialising') log.info(u'Presentations Initialising')
Plugin.initialise(self) Plugin.initialise(self)
self.insertToolboxItem() self.insertToolboxItem()
@ -59,6 +71,10 @@ class PresentationPlugin(Plugin):
self.controllers[controller].start_process() self.controllers[controller].start_process()
def finalise(self): def finalise(self):
"""
Finalise the plugin. Ask all the enabled presentation applications
to close down their applications and release resources.
"""
log.info(u'Plugin Finalise') log.info(u'Plugin Finalise')
#Ask each controller to tidy up #Ask each controller to tidy up
for key in self.controllers: for key in self.controllers:
@ -75,6 +91,10 @@ class PresentationPlugin(Plugin):
self, self.icon, self.name, self.controllers) self, self.icon, self.name, self.controllers)
def registerControllers(self, controller): def registerControllers(self, controller):
"""
Register each presentation controller (Impress, PPT etc) and
store for later use
"""
self.controllers[controller.name] = controller self.controllers[controller.name] = controller
def checkPreConditions(self): def checkPreConditions(self):
@ -109,9 +129,13 @@ class PresentationPlugin(Plugin):
return False return False
def about(self): def about(self):
"""
Return information about this plugin
"""
about_text = translate('PresentationPlugin', about_text = translate('PresentationPlugin',
'<b>Presentation Plugin</b> <br> Delivers ' '<b>Presentation Plugin</b> <br> Delivers '
'the ability to show presentations using a number of different ' 'the ability to show presentations using a number of different '
'programs. The choice of available presentation programs is ' 'programs. The choice of available presentation programs is '
'available to the user in a drop down box.') 'available to the user in a drop down box.')
return about_text return about_text