openlp/openlp/plugins/presentations/lib/powerpointcontroller.py

431 lines
17 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
2014-01-14 19:25:18 +00:00
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
2012-11-11 21:16:14 +00:00
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
2012-10-21 13:16:22 +00:00
# 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, #
2012-11-07 21:37:01 +00:00
# Frode Woldsund, Martin Zibricky #
# --------------------------------------------------------------------------- #
# 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 os
2010-07-10 22:41:36 +00:00
import logging
if os.name == u'nt':
2009-09-13 07:39:48 +00:00
from win32com.client import Dispatch
import _winreg
2009-10-07 22:49:48 +00:00
import win32ui
2010-06-23 22:26:54 +00:00
import pywintypes
2010-04-30 21:00:17 +00:00
from presentationcontroller import PresentationController, PresentationDocument
from openlp.core.lib.ui import UiStrings, critical_error_message_box
2010-02-27 15:31:23 +00:00
log = logging.getLogger(__name__)
# PPT API documentation:
# http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx
class PowerpointController(PresentationController):
"""
Class to control interactions with PowerPoint Presentations
It creates the runtime Environment , Loads the and Closes the Presentation
As well as triggering the correct activities based on the users input
"""
2010-02-27 15:31:23 +00:00
log.info(u'PowerpointController loaded')
def __init__(self, plugin):
"""
Initialise the class
"""
log.debug(u'Initialising')
2011-02-07 19:37:07 +00:00
PresentationController.__init__(self, plugin, u'Powerpoint',
PowerpointDocument)
self.supports = [u'ppt', u'pps', u'pptx', u'ppsx', u'pptm']
self.process = None
def check_available(self):
"""
PowerPoint is able to run on this machine
"""
log.debug(u'check_available')
if os.name == u'nt':
try:
2010-04-30 21:00:17 +00:00
_winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT,
u'PowerPoint.Application').Close()
return True
2010-06-23 22:26:54 +00:00
except WindowsError:
pass
return False
if os.name == u'nt':
def start_process(self):
"""
Loads PowerPoint process
"""
log.debug(u'start_process')
if not self.process:
self.process = Dispatch(u'PowerPoint.Application')
2011-03-16 23:37:16 +00:00
self.process.Visible = True
self.process.WindowState = 2
def kill(self):
"""
Called at system exit to clean up any running presentations
"""
log.debug(u'Kill powerpoint')
while self.docs:
self.docs[0].close_presentation()
if self.process is None:
return
try:
2012-10-11 22:03:54 +00:00
if self.process.Presentations.Count > 0:
return
self.process.Quit()
except (AttributeError, pywintypes.com_error):
pass
self.process = None
class PowerpointDocument(PresentationDocument):
2010-07-10 22:21:14 +00:00
"""
Class which holds information and controls a single presentation
"""
2010-10-11 20:47:00 +00:00
2010-04-30 21:00:17 +00:00
def __init__(self, controller, presentation):
2010-07-10 22:21:14 +00:00
"""
2010-10-11 20:47:00 +00:00
Constructor, store information about the file and initialise
2010-07-10 22:21:14 +00:00
"""
log.debug(u'Init Presentation Powerpoint')
PresentationDocument.__init__(self, controller, presentation)
self.presentation = None
def load_presentation(self):
"""
Called when a presentation is added to the SlideController.
2012-07-07 14:54:14 +00:00
Opens the PowerPoint file using the process created earlier.
"""
log.debug(u'load_presentation')
2010-07-10 22:21:14 +00:00
try:
if not self.controller.process or not self.controller.process.Visible:
self.controller.start_process()
2010-10-11 20:47:00 +00:00
self.controller.process.Presentations.Open(self.filepath, False,
2010-07-10 22:21:14 +00:00
False, True)
self.presentation = self.controller.process.Presentations(
self.controller.process.Presentations.Count)
self.create_thumbnails()
# Powerpoint 2013 pops up when loading a file, so we minimize it again
if self.presentation.Application.Version == u'15.0':
2014-06-18 11:39:28 +00:00
try:
2014-06-19 07:21:10 +00:00
self.presentation.Application.WindowState = 2
2014-06-18 11:39:28 +00:00
except:
2014-06-24 07:01:41 +00:00
log.exception(u'Failed to minimize main powerpoint window')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
return True
2014-06-24 07:01:41 +00:00
except pywintypes.com_error:
log.exception(u'PPT open failed')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
2010-07-10 22:21:14 +00:00
return False
def create_thumbnails(self):
"""
Create the thumbnail images for the current presentation.
2010-10-11 20:47:00 +00:00
Note an alternative and quicker method would be do::
self.presentation.Slides[n].Copy()
thumbnail = QApplication.clipboard.image()
2010-10-11 20:47:00 +00:00
However, for the moment, we want a physical file since it makes life
easier elsewhere.
"""
log.debug(u'create_thumbnails')
if self.check_thumbnails():
return
for num in range(self.presentation.Slides.Count):
self.presentation.Slides(num + 1).Export(os.path.join(
self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)),
'png', 320, 240)
def close_presentation(self):
"""
2010-10-11 20:47:00 +00:00
Close presentation and clean up objects. This is triggered by a new
object being added to SlideController or OpenLP being shut down.
"""
log.debug(u'ClosePresentation')
if self.presentation:
try:
self.presentation.Close()
2010-06-23 22:26:54 +00:00
except pywintypes.com_error:
pass
self.presentation = None
self.controller.remove_doc(self)
def is_loaded(self):
"""
2010-10-11 20:47:00 +00:00
Returns ``True`` if a presentation is loaded.
"""
log.debug(u'is_loaded')
try:
if not self.controller.process.Visible:
return False
if self.controller.process.Windows.Count == 0:
return False
if self.controller.process.Presentations.Count == 0:
return False
2010-06-24 19:58:45 +00:00
except (AttributeError, pywintypes.com_error):
return False
return True
def is_active(self):
"""
2010-10-11 20:47:00 +00:00
Returns ``True`` if a presentation is currently active.
"""
log.debug(u'is_active')
2010-03-10 22:24:09 +00:00
if not self.is_loaded():
return False
try:
2010-03-04 18:43:53 +00:00
if self.presentation.SlideShowWindow is None:
return False
2010-03-04 18:43:53 +00:00
if self.presentation.SlideShowWindow.View is None:
return False
2010-06-24 19:42:15 +00:00
except (AttributeError, pywintypes.com_error):
return False
return True
def unblank_screen(self):
"""
2010-10-11 20:47:00 +00:00
Unblanks (restores) the presentation.
"""
log.debug(u'unblank_screen')
try:
self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate()
if self.presentation.Application.Version == u'14.0':
# Unblanking is broken in PowerPoint 2010, need to redisplay
slide = self.presentation.SlideShowWindow.View.CurrentShowPosition
click = self.presentation.SlideShowWindow.View.GetClickIndex()
self.presentation.SlideShowWindow.View.GotoSlide(slide)
if click:
self.presentation.SlideShowWindow.View.GotoClick(click)
2014-06-24 07:01:41 +00:00
except pywintypes.com_error:
log.exception(u'COM error while in unblank_screen')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
self.show_error_msg()
def blank_screen(self):
"""
2010-10-11 20:47:00 +00:00
Blanks the screen.
"""
log.debug(u'blank_screen')
try:
self.presentation.SlideShowWindow.View.State = 3
2014-06-24 07:01:41 +00:00
except pywintypes.com_error:
log.exception(u'COM error while in blank_screen')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
self.show_error_msg()
2010-03-10 22:24:09 +00:00
def is_blank(self):
"""
2010-10-11 20:47:00 +00:00
Returns ``True`` if screen is blank.
2010-03-10 22:24:09 +00:00
"""
log.debug(u'is_blank')
if self.is_active():
try:
return self.presentation.SlideShowWindow.View.State == 3
2014-06-24 07:01:41 +00:00
except pywintypes.com_error:
log.exception(u'COM error while in is_blank')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
return False
2010-06-24 19:42:15 +00:00
else:
2010-06-23 22:26:54 +00:00
return False
2010-03-10 22:24:09 +00:00
def stop_presentation(self):
"""
2010-10-11 20:47:00 +00:00
Stops the current presentation and hides the output.
"""
log.debug(u'stop_presentation')
try:
self.presentation.SlideShowWindow.View.Exit()
2014-06-22 19:51:19 +00:00
except pywintypes.com_error as e:
2014-06-24 07:01:41 +00:00
log.exception(u'COM error while in stop_presentation')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
if os.name == u'nt':
def start_presentation(self):
"""
2010-10-11 20:47:00 +00:00
Starts a presentation from the beginning.
"""
log.debug(u'start_presentation')
#SlideShowWindow measures its size/position by points, not pixels
try:
dpi = win32ui.GetActiveWindow().GetDC().GetDeviceCaps(88)
2010-06-23 22:26:54 +00:00
except win32ui.error:
try:
2010-06-08 15:38:09 +00:00
dpi = \
win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88)
2010-06-23 22:26:54 +00:00
except win32ui.error:
dpi = 96
2011-03-28 18:56:39 +00:00
renderer = self.controller.plugin.renderer
rect = renderer.screens.current[u'size']
2011-04-07 22:57:12 +00:00
ppt_window = self.presentation.SlideShowSettings.Run()
2012-10-11 22:03:54 +00:00
if not ppt_window:
return
try:
ppt_window.Top = rect.y() * 72 / dpi
ppt_window.Height = rect.height() * 72 / dpi
ppt_window.Left = rect.x() * 72 / dpi
ppt_window.Width = rect.width() * 72 / dpi
except AttributeError as e:
2014-06-24 07:01:41 +00:00
log.exception(u'AttributeError while in start_presentation')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
# Powerpoint 2013 pops up when starting a file, so we minimize it again
if self.presentation.Application.Version == u'15.0':
2014-06-18 11:39:28 +00:00
try:
2014-06-19 07:21:10 +00:00
self.presentation.Application.WindowState = 2
2014-06-18 11:39:28 +00:00
except:
2014-06-24 07:01:41 +00:00
log.exception(u'Failed to minimize main powerpoint window')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
def get_slide_number(self):
"""
2010-10-11 20:47:00 +00:00
Returns the current slide number.
"""
log.debug(u'get_slide_number')
try:
ret = self.presentation.SlideShowWindow.View.CurrentShowPosition
2014-06-22 19:51:19 +00:00
except pywintypes.com_error as e:
ret = 0
2014-06-24 07:01:41 +00:00
log.exception(u'COM error while in get_slide_number')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
self.show_error_msg()
return ret
def get_slide_count(self):
"""
2010-10-11 20:47:00 +00:00
Returns total number of slides.
"""
log.debug(u'get_slide_count')
try:
ret = self.presentation.Slides.Count
2014-06-22 19:51:19 +00:00
except pywintypes.com_error as e:
ret = 0
2014-06-24 07:01:41 +00:00
log.exception(u'COM error while in get_slide_count')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
self.show_error_msg()
return ret
def goto_slide(self, slideno):
"""
2010-10-11 20:47:00 +00:00
Moves to a specific slide in the presentation.
"""
log.debug(u'goto_slide')
try:
self.presentation.SlideShowWindow.View.GotoSlide(slideno)
2014-06-22 19:51:19 +00:00
except pywintypes.com_error as e:
2014-06-24 07:01:41 +00:00
log.exception(u'COM error while in goto_slide')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
self.show_error_msg()
def next_step(self):
"""
2010-10-11 20:47:00 +00:00
Triggers the next effect of slide on the running presentation.
"""
log.debug(u'next_step')
try:
self.presentation.SlideShowWindow.View.Next()
2014-06-22 19:51:19 +00:00
except pywintypes.com_error as e:
2014-06-24 07:01:41 +00:00
log.exception(u'COM error while in next_step')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
self.show_error_msg()
return
2012-10-12 21:33:10 +00:00
if self.get_slide_number() > self.get_slide_count():
self.previous_step()
def previous_step(self):
"""
2010-10-11 20:47:00 +00:00
Triggers the previous slide on the running presentation.
"""
log.debug(u'previous_step')
try:
self.presentation.SlideShowWindow.View.Previous()
except pywintypes.com_error as e:
2014-06-24 07:01:41 +00:00
log.exception(u'COM error while in previous_step')
exc_type, exc_value, exc_traceback = sys.exc_info()
log.exception(''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
self.show_error_msg()
2009-10-07 22:49:48 +00:00
def get_slide_text(self, slide_no):
"""
2010-10-11 20:47:00 +00:00
Returns the text on the slide.
``slide_no``
2010-10-11 20:47:00 +00:00
The slide the text is required for, starting at 1.
"""
2011-02-06 18:01:47 +00:00
return _get_text_from_shapes(self.presentation.Slides(slide_no).Shapes)
def get_slide_notes(self, slide_no):
"""
2010-10-11 20:47:00 +00:00
Returns the text on the slide.
``slide_no``
2010-10-11 20:47:00 +00:00
The slide the notes are required for, starting at 1.
"""
2011-02-06 18:01:47 +00:00
return _get_text_from_shapes(
self.presentation.Slides(slide_no).NotesPage.Shapes)
def show_error_msg(self):
"""
Stop presentation and display an error message.
"""
self.stop_presentation()
critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument',
'An error occurred in the Powerpoint integration '
'and the presentation will be stopped. '
2014-06-23 08:51:17 +00:00
'Restart the presentation if you wish to present it.'))
2011-02-06 18:01:47 +00:00
def _get_text_from_shapes(shapes):
"""
Returns any text extracted from the shapes on a presentation slide.
``shapes``
A set of shapes to search for text.
"""
text = ''
for idx in range(shapes.Count):
shape = shapes(idx + 1)
if shape.HasTextFrame:
text += shape.TextFrame.TextRange.Text + '\n'
return text