diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 4718e289e..f62778820 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -174,6 +174,8 @@ def resize_image(image, width, height): """ preview = QtGui.QImage(image) if not preview.isNull(): + if preview.width() == width and preview.height == height: + return preview preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) realw = preview.width() diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 1a31efa32..7ad6a9b17 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -379,14 +379,17 @@ class MediaManagerItem(QtGui.QWidget): """ 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 thumbdate = os.stat(thumb).st_mtime #if file updated rebuild icon if filedate > thumbdate: self.iconFromFile(file, thumb) - return True - return False + else: + self.iconFromFile(file, thumb) + return True def iconFromFile(self, file, thumb): """ diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 8dc4861aa..30a95e3b8 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -74,6 +74,7 @@ class ImpressController(PresentationController): self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx'] self.process = None self.desktop = None + self.manager = None def check_available(self): """ @@ -104,6 +105,10 @@ class ImpressController(PresentationController): self.process.waitForStarted() 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') ctx = None loop = 0 @@ -134,10 +139,19 @@ class ImpressController(PresentationController): return None 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') + if not self.manager: + return None return self.manager.createInstance(u'com.sun.star.frame.Desktop') def get_com_servicemanager(self): + """ + Return the OOo service manager for windows + """ log.debug(u'get_com_servicemanager openoffice') try: return Dispatch(u'com.sun.star.ServiceManager') @@ -171,13 +185,23 @@ class ImpressController(PresentationController): log.exception(u'Failed to terminate OpenOffice') def add_doc(self, name): + """ + Called when a new Impress document is opened + """ log.debug(u'Add Doc OpenOffice') doc = ImpressDocument(self, name) self.docs.append(doc) return doc class ImpressDocument(PresentationDocument): + """ + Class which holds information and controls a single presentation + """ + def __init__(self, controller, presentation): + """ + Constructor, store information about the file and initialise + """ log.debug(u'Init Presentation OpenOffice') PresentationDocument.__init__(self, controller, presentation) self.document = None @@ -208,9 +232,8 @@ class ImpressDocument(PresentationDocument): desktop = self.controller.get_uno_desktop() url = uno.systemPathToFileUrl(self.filepath) if desktop is None: - return + return False self.desktop = desktop - #print "s.dsk2 ", self.desktop properties = [] properties.append(self.create_property(u'Minimized', True)) properties = tuple(properties) @@ -219,12 +242,13 @@ class ImpressDocument(PresentationDocument): 0, properties) except: log.exception(u'Failed to load presentation') - return + return False self.presentation = self.document.getPresentation() self.presentation.Display = \ self.controller.plugin.renderManager.screens.current_display + 1 self.control = None self.create_thumbnails() + return True def create_thumbnails(self): """ @@ -234,30 +258,36 @@ class ImpressDocument(PresentationDocument): if self.check_thumbnails(): return 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') else: - thumbdir = uno.systemPathToFileUrl(self.thumbnailpath) + thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder()) props = [] props.append(self.create_property(u'FilterName', u'impress_png_Export')) props = tuple(props) doc = self.document pages = doc.getDrawPages() + if not os.path.isdir(self.get_temp_folder()): + os.makedirs(self.get_temp_folder()) for idx in range(pages.getCount()): page = pages.getByIndex(idx) doc.getCurrentController().setCurrentPage(page) - path = u'%s/%s%s.png' % (thumbdir, self.controller.thumbnailprefix, - unicode(idx + 1)) + urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1)) + path = os.path.join(self.get_temp_folder(), + unicode(idx + 1) + u'.png') try: - doc.storeToURL(path , props) - preview = resize_image(path, 640, 480) + doc.storeToURL(urlpath, props) + self.convert_thumbnail(path, idx + 1) if os.path.exists(path): os.remove(path) - preview.save(path, u'png') except: log.exception(u'%s - Unable to store openoffice preview' % path) 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') if os.name == u'nt': prop = self.controller.manager.\ @@ -288,6 +318,9 @@ class ImpressDocument(PresentationDocument): self.controller.remove_doc(self) def is_loaded(self): + """ + Returns true if a presentation is loaded + """ log.debug(u'is loaded OpenOffice') #print "is_loaded " if self.presentation is None or self.document is None: @@ -302,6 +335,9 @@ class ImpressDocument(PresentationDocument): return True def is_active(self): + """ + Returns true if a presentation is active and running + """ log.debug(u'is active OpenOffice') #print "is_active " if not self.is_loaded(): @@ -313,10 +349,16 @@ class ImpressDocument(PresentationDocument): return True def unblank_screen(self): + """ + Unblanks the screen + """ log.debug(u'unblank screen OpenOffice') return self.control.resume() def blank_screen(self): + """ + Blanks the screen + """ log.debug(u'blank screen OpenOffice') self.control.blankScreen(0) @@ -331,6 +373,9 @@ class ImpressDocument(PresentationDocument): return False def stop_presentation(self): + """ + Stop the presentation, remove from screen + """ log.debug(u'stop presentation OpenOffice') # deactivate should hide the screen according to docs, but doesn't #self.control.deactivate() @@ -338,6 +383,9 @@ class ImpressDocument(PresentationDocument): self.control = None def start_presentation(self): + """ + Start the presentation from the beginning + """ log.debug(u'start presentation OpenOffice') if self.control is None or not self.control.isRunning(): self.presentation.start() @@ -354,12 +402,21 @@ class ImpressDocument(PresentationDocument): self.goto_slide(1) def get_slide_number(self): + """ + Return the current slide number on the screen, from 1 + """ return self.control.getCurrentSlideIndex() + 1 def get_slide_count(self): + """ + Return the total number of slides + """ return self.document.getDrawPages().getCount() def goto_slide(self, slideno): + """ + Go to a specific slide (from 1) + """ self.control.gotoSlideIndex(slideno-1) def next_step(self): diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index e4993b91a..3587cc0c4 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -35,9 +35,13 @@ from openlp.plugins.presentations.lib import MessageListener 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 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): self.PluginName = u'Presentations' BaseListWithDnD.__init__(self, parent) @@ -45,11 +49,14 @@ class PresentationListView(BaseListWithDnD): class PresentationMediaItem(MediaManagerItem): """ 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') def __init__(self, parent, icon, title, controllers): + """ + Constructor. Setup defaults + """ self.controllers = controllers self.PluginNameShort = u'Presentation' self.pluginNameVisible = translate('PresentationPlugin.MediaItem', @@ -63,6 +70,9 @@ class PresentationMediaItem(MediaManagerItem): self.message_listener = MessageListener(self) def retranslateUi(self): + """ + The name of the plugin media displayed in UI + """ self.OnNewPrompt = translate('PresentationPlugin.MediaItem', 'Select Presentation(s)') self.Automatic = translate('PresentationPlugin.MediaItem', @@ -80,12 +90,18 @@ class PresentationMediaItem(MediaManagerItem): 'Presentations (%s)' % fileType) def requiredIcons(self): + """ + Set which icons the media manager tab should show + """ MediaManagerItem.requiredIcons(self) self.hasFileIcon = True self.hasNewIcon = False self.hasEditIcon = False def addEndHeaderBar(self): + """ + Display custom media manager items for presentations + """ self.PresentationWidget = QtGui.QWidget(self) sizePolicy = QtGui.QSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) @@ -109,15 +125,13 @@ class PresentationMediaItem(MediaManagerItem): self.pageLayout.addWidget(self.PresentationWidget) def initialise(self): - self.servicePath = os.path.join( - AppLocation.get_section_data_path(self.settingsSection), - u'thumbnails') + """ + Populate the media manager tab + """ self.listView.setIconSize(QtCore.QSize(88, 50)) - if not os.path.exists(self.servicePath): - os.mkdir(self.servicePath) list = SettingsManager.load_list( self.settingsSection, u'presentations') - self.loadList(list) + self.loadList(list, True) for item in self.controllers: #load the drop down selection if self.controllers[item].enabled: @@ -126,7 +140,12 @@ class PresentationMediaItem(MediaManagerItem): self.DisplayTypeComboBox.insertItem(0, self.Automatic) 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() titles = [] for file in currlist: @@ -136,40 +155,43 @@ class PresentationMediaItem(MediaManagerItem): continue filename = os.path.split(unicode(file))[1] if titles.count(filename) > 0: - QtGui.QMessageBox.critical( - self, translate('PresentationPlugin.MediaItem', - 'File exists'), + if not initialLoad: + QtGui.QMessageBox.critical( + self, translate('PresentationPlugin.MediaItem', + 'File exists'), translate('PresentationPlugin.MediaItem', 'A presentation with that filename already exists.'), - QtGui.QMessageBox.Ok) - else: - icon = None - for controller in self.controllers: - thumbPath = os.path.join( - AppLocation.get_section_data_path( - self.settingsSection), - u'thumbnails', controller, filename) - thumb = os.path.join(thumbPath, u'slide1.png') - preview = os.path.join( - AppLocation.get_section_data_path( - self.settingsSection), - controller, u'thumbnails', filename, u'slide1.png') - if os.path.exists(preview): - if os.path.exists(thumb): - 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: + QtGui.QMessageBox.Ok) + continue + controller_name = self.findControllerByType(filename) + if controller_name: + controller = self.controllers[controller_name] + doc = controller.add_doc(unicode(file)) + thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png') + preview = doc.get_thumbnail_path(1, True) + if not preview and not initialLoad: + doc.load_presentation() + preview = doc.get_thumbnail_path(1, True) + doc.close_presentation() + if preview and self.validate(preview, thumb): + icon = build_icon(thumb) + else: icon = build_icon(u':/general/general_delete.png') - item_name = QtGui.QListWidgetItem(filename) - item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) - item_name.setIcon(icon) - self.listView.addItem(item_name) + else: + if initialLoad: + icon = build_icon(u':/general/general_delete.png') + 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): """ @@ -184,8 +206,6 @@ class PresentationMediaItem(MediaManagerItem): for item in items: filepath = unicode(item.data( QtCore.Qt.UserRole).toString()) - #not sure of this has errors - #John please can you look at . for cidx in self.controllers: doc = self.controllers[cidx].add_doc(filepath) doc.presentation_deleted() @@ -196,6 +216,11 @@ class PresentationMediaItem(MediaManagerItem): self.settingsSection, self.getFileList()) 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() if len(items) > 1: return False @@ -213,20 +238,27 @@ class PresentationMediaItem(MediaManagerItem): controller = self.controllers[service_item.shortname] (path, name) = os.path.split(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() i = 1 - img = doc.get_slide_preview_file(i) + img = doc.get_thumbnail_path(i, True) while img: service_item.add_from_command(path, name, img) i = i + 1 - img = doc.get_slide_preview_file(i) + img = doc.get_thumbnail_path(i, True) doc.close_presentation() return True else: return False 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] if not filetype: return None diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 2f8a1aec7..a7e6f10ce 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -41,17 +41,28 @@ class Controller(object): log.info(u'Controller loaded') def __init__(self, live): + """ + Constructor + """ self.is_live = live self.doc = None log.info(u'%s controller loaded' % live) 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)) self.controller = controller if self.doc is not None: self.shutdown() 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: self.doc.start_presentation() if is_blank: @@ -60,6 +71,10 @@ class Controller(object): self.doc.slidenumber = 0 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) if self.doc.is_active(): return @@ -71,6 +86,9 @@ class Controller(object): self.doc.goto_slide(self.doc.slidenumber) def slide(self, slide): + """ + Go to a specific slide + """ log.debug(u'Live = %s, slide' % self.is_live) if not self.is_live: return @@ -152,6 +170,9 @@ class Controller(object): #self.timer.stop() def blank(self): + """ + Instruct the controller to blank the presentation + """ log.debug(u'Live = %s, blank' % self.is_live) if not self.is_live: return @@ -162,6 +183,9 @@ class Controller(object): self.doc.blank_screen() def stop(self): + """ + Instruct the controller to stop and hide the presentation + """ log.debug(u'Live = %s, stop' % self.is_live) if not self.is_live: return @@ -172,6 +196,9 @@ class Controller(object): self.doc.stop_presentation() def unblank(self): + """ + Instruct the controller to unblank the presentation + """ log.debug(u'Live = %s, unblank' % self.is_live) if not self.is_live: return @@ -246,6 +273,9 @@ class MessageListener(object): controller.add_handler(self.controllers[self.handler], file, is_blank) def slide(self, message): + """ + React to the message to move to a specific slide + """ is_live = message[1] slide = message[2] if is_live: @@ -254,6 +284,9 @@ class MessageListener(object): self.preview_handler.slide(slide) def first(self, message): + """ + React to the message to move to the first slide + """ is_live = message[1] if is_live: self.live_handler.first() @@ -261,6 +294,9 @@ class MessageListener(object): self.preview_handler.first() def last(self, message): + """ + React to the message to move to the last slide + """ is_live = message[1] if is_live: self.live_handler.last() @@ -268,6 +304,9 @@ class MessageListener(object): self.preview_handler.last() def next(self, message): + """ + React to the message to move to the next animation/slide + """ is_live = message[1] if is_live: self.live_handler.next() @@ -275,6 +314,9 @@ class MessageListener(object): self.preview_handler.next() def previous(self, message): + """ + React to the message to move to the previous animation/slide + """ is_live = message[1] if is_live: self.live_handler.previous() @@ -282,6 +324,10 @@ class MessageListener(object): self.preview_handler.previous() def shutdown(self, message): + """ + React to message to shutdown the presentation. I.e. end the show + and close the file + """ is_live = message[1] if is_live: Receiver.send_message(u'maindisplay_show') @@ -290,19 +336,34 @@ class MessageListener(object): self.preview_handler.shutdown() def hide(self, message): + """ + React to the message to show the desktop + """ is_live = message[1] if is_live: self.live_handler.stop() def blank(self, message): + """ + React to the message to blank the display + """ is_live = message[1] if is_live: self.live_handler.blank() def unblank(self, message): + """ + React to the message to unblank the display + """ is_live = message[1] if is_live: self.live_handler.unblank() 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() diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 00ded85f6..6c725e583 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -97,13 +97,23 @@ class PowerpointController(PresentationController): self.process = None def add_doc(self, name): + """ + Called when a new powerpoint document is opened + """ log.debug(u'Add Doc PowerPoint') doc = PowerpointDocument(self, name) self.docs.append(doc) return doc class PowerpointDocument(PresentationDocument): + """ + Class which holds information and controls a single presentation + """ + def __init__(self, controller, presentation): + """ + Constructor, store information about the file and initialise + """ log.debug(u'Init Presentation Powerpoint') PresentationDocument.__init__(self, controller, presentation) self.presentation = None @@ -111,22 +121,23 @@ class PowerpointDocument(PresentationDocument): def load_presentation(self): """ Called when a presentation is added to the SlideController. - It builds the environment, starts communcations with the background - OpenOffice task started earlier. If OpenOffice is not present is is - started. Once the environment is available the presentation is loaded - and started. + Opens the PowerPoint file using the process created earlier ``presentation`` The file name of the presentations to run. """ 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.process.Presentations.Open(self.filepath, False, False, - True) + try: + self.controller.process.Presentations.Open(self.filepath, False, + False, True) + except pywintypes.com_error: + return False self.presentation = self.controller.process.Presentations( self.controller.process.Presentations.Count) self.create_thumbnails() + return True def create_thumbnails(self): """ @@ -139,8 +150,8 @@ class PowerpointDocument(PresentationDocument): """ if self.check_thumbnails(): return - self.presentation.Export(os.path.join(self.thumbnailpath, ''), 'png', - 320, 240) + self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''), + 'png', 320, 240) def close_presentation(self): """ @@ -298,4 +309,4 @@ class PowerpointDocument(PresentationDocument): shape = shapes(idx + 1) if shape.HasTextFrame: text += shape.TextFrame.TextRange.Text + '\n' - return text \ No newline at end of file + return text diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 2c108333c..2810c88d2 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -93,13 +93,22 @@ class PptviewController(PresentationController): self.docs[0].close_presentation() def add_doc(self, name): + """ + Called when a new powerpoint document is opened + """ log.debug(u'Add Doc PPTView') doc = PptviewDocument(self, name) self.docs.append(doc) return doc class PptviewDocument(PresentationDocument): + """ + Class which holds information and controls a single presentation + """ def __init__(self, controller, presentation): + """ + Constructor, store information about the file and initialise + """ log.debug(u'Init Presentation PowerPoint') PresentationDocument.__init__(self, controller, presentation) self.presentation = None @@ -117,17 +126,31 @@ class PptviewDocument(PresentationDocument): The file name of the presentations to run. """ log.debug(u'LoadPresentation') - #if self.pptid >= 0: - # self.close_presentation() rendermanager = self.controller.plugin.renderManager rect = rendermanager.screens.current[u'size'] rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom()) 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, - str(os.path.join(self.thumbnailpath, - self.controller.thumbnailprefix))) - if self.pptid: + str(self.get_temp_folder()) + '\\slide') + if self.pptid >= 0: + self.create_thumbnails() 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): """ @@ -224,17 +247,3 @@ class PptviewDocument(PresentationDocument): """ 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 - diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 501776a19..ed4081ed6 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -29,7 +29,7 @@ import shutil from PyQt4 import QtCore -from openlp.core.lib import Receiver +from openlp.core.lib import Receiver, resize_image from openlp.core.utils import AppLocation log = logging.getLogger(__name__) @@ -63,6 +63,13 @@ class PresentationController(object): ``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()`` @@ -109,12 +116,16 @@ class PresentationController(object): QtCore.Qt.Checked else: 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), - name, u'thumbnails') - self.thumbnailprefix = u'slide' - if not os.path.isdir(self.thumbnailroot): - os.makedirs(self.thumbnailroot) + u'thumbnails') + self.thumbnail_prefix = u'slide' + if not os.path.isdir(self.thumbnail_folder): + os.makedirs(self.thumbnail_folder) + if not os.path.isdir(self.temp_folder): + os.makedirs(self.temp_folder) def check_available(self): """ @@ -208,14 +219,19 @@ class PresentationDocument(object): ``previous_step()`` 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 """ def __init__(self, controller, name): + """ + Constructor for the PresentationController class + """ self.slidenumber = 0 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): """ @@ -224,9 +240,10 @@ class PresentationDocument(object): ``presentation`` 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): """ @@ -234,33 +251,37 @@ class PresentationDocument(object): a file, e.g. thumbnails """ try: - shutil.rmtree(self.thumbnailpath) + shutil.rmtree(self.get_thumbnail_folder()) + shutil.rmtree(self.get_temp_folder()) except OSError: 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 - 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) + return os.path.split(self.filepath)[1] - def get_file_name(self, presentation): - return os.path.split(presentation)[1] - - def get_thumbnail_path(self, presentation): + def get_thumbnail_folder(self): + """ + The location where thumbnail images will be stored + """ 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): """ Returns true if the thumbnail images look to exist and are more 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)): return False imgdate = os.stat(lastimage).st_mtime @@ -350,16 +371,27 @@ class PresentationDocument(object): """ 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 ``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'.png') - if os.path.isfile(path): + 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 diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index 099376ce9..980c62f47 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -32,10 +32,16 @@ class PresentationTab(SettingsTab): PresentationsTab is the Presentations settings tab in the settings dialog. """ def __init__(self, title, controllers): + """ + Constructor + """ self.controllers = controllers SettingsTab.__init__(self, title) def setupUi(self): + """ + Create the controls for the settings tab + """ self.setObjectName(u'PresentationTab') self.tabTitleVisible = translate('PresentationPlugin.PresentationTab', 'Presentations') @@ -89,6 +95,9 @@ class PresentationTab(SettingsTab): self.PresentationLayout.addWidget(self.PresentationRightWidget) def retranslateUi(self): + """ + Make any translation changes + """ self.VerseDisplayGroupBox.setTitle( translate('PresentationPlugin.PresentationTab', 'Available Controllers')) @@ -100,6 +109,9 @@ class PresentationTab(SettingsTab): translate('PresentationPlugin.PresentationTab', 'available'))) def load(self): + """ + Load the settings. + """ for key in self.controllers: controller = self.controllers[key] if controller.available: @@ -109,6 +121,9 @@ class PresentationTab(SettingsTab): QtCore.QVariant(0)).toInt()[0]) def save(self): + """ + Save the settings. + """ for key in self.controllers: controller = self.controllers[key] checkbox = self.PresenterCheckboxes[controller.name] diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 7bb256af7..50784ed51 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -33,9 +33,17 @@ from openlp.plugins.presentations.lib import * log = logging.getLogger(__name__) 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') def __init__(self, plugin_helpers): + """ + PluginPresentation constructor. + """ log.debug(u'Initialised') self.controllers = {} Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers) @@ -51,6 +59,10 @@ class PresentationPlugin(Plugin): return PresentationTab(self.name, self.controllers) def initialise(self): + """ + Initialise the plugin. Determine which controllers are enabled + are start their processes. + """ log.info(u'Presentations Initialising') Plugin.initialise(self) self.insertToolboxItem() @@ -59,6 +71,10 @@ class PresentationPlugin(Plugin): self.controllers[controller].start_process() 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') #Ask each controller to tidy up for key in self.controllers: @@ -75,6 +91,10 @@ class PresentationPlugin(Plugin): self, self.icon, self.name, self.controllers) def registerControllers(self, controller): + """ + Register each presentation controller (Impress, PPT etc) and + store for later use + """ self.controllers[controller.name] = controller def checkPreConditions(self): @@ -109,9 +129,13 @@ class PresentationPlugin(Plugin): return False def about(self): + """ + Return information about this plugin + """ about_text = translate('PresentationPlugin', 'Presentation Plugin
Delivers ' 'the ability to show presentations using a number of different ' 'programs. The choice of available presentation programs is ' 'available to the user in a drop down box.') - return about_text \ No newline at end of file + return about_text +