forked from openlp/openlp
Take a stab at writing a presentation controller to control LibreOffice on Mac via Pyro
This commit is contained in:
parent
faeef88315
commit
c5709b9778
361
openlp/plugins/presentations/lib/libreofficeserver.py
Normal file
361
openlp/plugins/presentations/lib/libreofficeserver.py
Normal file
@ -0,0 +1,361 @@
|
||||
"""
|
||||
This module runs a Pyro4 server using LibreOffice's version of Python
|
||||
"""
|
||||
from subprocess import Popen
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
# Add the vendor directory to sys.path so that we can load Pyro4
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'vendor'))
|
||||
|
||||
from Pyro4 import Daemon, expose, locateNS
|
||||
from com.sun.star.beans import PropertyValue
|
||||
from com.sun.star.task import ErrorCodeIOException
|
||||
import uno
|
||||
|
||||
logging.basicConfig(filename=os.path.dirname(__file__) + '/libreofficeserver.log', level=logging.INFO)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TextType(object):
|
||||
"""
|
||||
Type Enumeration for Types of Text to request
|
||||
"""
|
||||
Title = 0
|
||||
SlideText = 1
|
||||
Notes = 2
|
||||
|
||||
|
||||
@expose
|
||||
class LibreOfficeServer(object):
|
||||
"""
|
||||
A Pyro4 server which controls LibreOffice
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
Set up the LibreOffice server
|
||||
"""
|
||||
self._init_impress()
|
||||
|
||||
def _init_impress(self)
|
||||
"""
|
||||
Initialise Impress
|
||||
"""
|
||||
uno_command = [
|
||||
'/Applications/LibreOffice.app/Contents/MacOS/soffice',
|
||||
'--nologo',
|
||||
'--norestore',
|
||||
'--minimized',
|
||||
'--nodefault',
|
||||
'--nofirststartwizard',
|
||||
'"--accept=pipe,name=openlp_pipe;urp;"'
|
||||
]
|
||||
self._process = Popen(uno_command)
|
||||
uno_instance = None
|
||||
context = uno.getComponentContext()
|
||||
resolver = context.ServiceManager.createInstanceWithContext('com.sun.star.bridge.UnoUrlResolver', context)
|
||||
while uno_instance is None and loop < 3:
|
||||
try:
|
||||
uno_instance = resolver.resolve('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext')
|
||||
except:
|
||||
log.warning('Unable to find running instance ')
|
||||
loop += 1
|
||||
try:
|
||||
self._manager = uno_instance.ServiceManager
|
||||
log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
|
||||
self._desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
|
||||
except:
|
||||
log.warning('Failed to get UNO desktop')
|
||||
|
||||
def _create_property(self, name, value):
|
||||
"""
|
||||
Create an OOo style property object which are passed into some Uno methods.
|
||||
"""
|
||||
log.debug('create property')
|
||||
property_object = PropertyValue()
|
||||
property_object.Name = name
|
||||
property_object.Value = value
|
||||
return property_object
|
||||
|
||||
def _get_text_from_page(self, slide_no, text_type=TextType.SlideText):
|
||||
"""
|
||||
Return any text extracted from the presentation page.
|
||||
|
||||
:param slide_no: The slide the notes are required for, starting at 1
|
||||
:param notes: A boolean. If set the method searches the notes of the slide.
|
||||
:param text_type: A TextType. Enumeration of the types of supported text.
|
||||
"""
|
||||
text = ''
|
||||
if TextType.Title <= text_type <= TextType.Notes:
|
||||
pages = self._document.getDrawPages()
|
||||
if 0 < slide_no <= pages.getCount():
|
||||
page = pages.getByIndex(slide_no - 1)
|
||||
if text_type == TextType.Notes:
|
||||
page = page.getNotesPage()
|
||||
for index in range(page.getCount()):
|
||||
shape = page.getByIndex(index)
|
||||
shape_type = shape.getShapeType()
|
||||
if shape.supportsService("com.sun.star.drawing.Text"):
|
||||
# if they requested title, make sure it is the title
|
||||
if text_type != TextType.Title or shape_type == "com.sun.star.presentation.TitleTextShape":
|
||||
text += shape.getString() + '\n'
|
||||
return text
|
||||
|
||||
def has_desktop(self):
|
||||
"""
|
||||
Say if we have a desktop object
|
||||
"""
|
||||
return self._desktop is not None
|
||||
|
||||
def shutdown(self):
|
||||
"""
|
||||
Shut down the server
|
||||
"""
|
||||
while self._docs:
|
||||
self._docs[0].close_presentation()
|
||||
if not self._desktop:
|
||||
return
|
||||
docs = self._desktop.getComponents()
|
||||
count = 0
|
||||
if docs.hasElements():
|
||||
list_elements = docs.createEnumeration()
|
||||
while list_elements.hasMoreElements():
|
||||
doc = list_elements.nextElement()
|
||||
if doc.getImplementationName() != 'com.sun.star.comp.framework.BackingComp':
|
||||
count += 1
|
||||
if count > 0:
|
||||
log.debug('LibreOffice not terminated as docs are still open')
|
||||
else:
|
||||
try:
|
||||
self._desktop.terminate()
|
||||
log.debug('LibreOffice killed')
|
||||
except:
|
||||
log.warning('Failed to terminate LibreOffice')
|
||||
|
||||
def load_presentation(self, file_path, screen_number):
|
||||
"""
|
||||
Load a presentation
|
||||
"""
|
||||
self._file_path = file_path
|
||||
url = uno.systemPathToFileUrl(file_path)
|
||||
properties = (self._create_property('Hidden', True),)
|
||||
try:
|
||||
self._document = self._desktop.loadComponentFromURL(url, '_blank', 0, properties)
|
||||
except:
|
||||
log.warning('Failed to load presentation {url}'.format(url=url))
|
||||
return False
|
||||
self._presentation = self._document.getPresentation()
|
||||
self._presentation.Display = screen_number
|
||||
self._control = None
|
||||
self.create_thumbnails()
|
||||
self.create_titles_and_notes()
|
||||
return True
|
||||
|
||||
def create_thumbnails(self, temp_folder):
|
||||
"""
|
||||
Create thumbnails for the presentation
|
||||
"""
|
||||
thumb_dir_url = uno.systemPathToFileUrl(temp_folder)
|
||||
properties = (self._create_property('FilterName', 'impress_png_Export'),)
|
||||
doc = self.document
|
||||
pages = doc.getDrawPages()
|
||||
if not pages:
|
||||
return
|
||||
if not os.path.isdir(temp_folder):
|
||||
os.makedirs(temp_folder)
|
||||
for index in range(pages.getCount()):
|
||||
page = pages.getByIndex(index)
|
||||
doc.getCurrentController().setCurrentPage(page)
|
||||
url_path = '{path}/{name}.png'.format(path=thumb_dir_url, name=str(index + 1))
|
||||
path = os.path.join(self.get_temp_folder(), str(index + 1) + '.png')
|
||||
try:
|
||||
doc.storeToURL(url_path, properties)
|
||||
self.convert_thumbnail(path, index + 1)
|
||||
delete_file(path)
|
||||
except ErrorCodeIOException as exception:
|
||||
log.exception('ERROR! ErrorCodeIOException {error:d}'.format(error=exception.ErrCode))
|
||||
except:
|
||||
log.exception('{path} - Unable to store openoffice preview'.format(path=path))
|
||||
|
||||
def get_title_and_notes(self):
|
||||
"""
|
||||
Writes the list of titles (one per slide) to 'titles.txt' and the notes to 'slideNotes[x].txt'
|
||||
in the thumbnails directory
|
||||
"""
|
||||
titles = []
|
||||
notes = []
|
||||
pages = self._document.getDrawPages()
|
||||
for slide_no in range(1, pages.getCount() + 1):
|
||||
titles.append(self._get_text_from_page(slide_no, TextType.Title).replace('\n', ' ') + '\n')
|
||||
note = self._get_text_from_page(slide_no, TextType.Notes)
|
||||
if len(note) == 0:
|
||||
note = ' '
|
||||
notes.append(note)
|
||||
return titles, notes
|
||||
|
||||
def close_presentation(self):
|
||||
"""
|
||||
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
|
||||
shutdown.
|
||||
"""
|
||||
log.debug('close Presentation LibreOffice')
|
||||
if self._document:
|
||||
if self._presentation:
|
||||
try:
|
||||
self._presentation.end()
|
||||
self._presentation = None
|
||||
self._document.dispose()
|
||||
except:
|
||||
log.warning("Closing presentation failed")
|
||||
self._document = None
|
||||
|
||||
def is_loaded(self):
|
||||
"""
|
||||
Returns true if a presentation is loaded.
|
||||
"""
|
||||
log.debug('is loaded LibreOffice')
|
||||
if self._presentation is None or self._document is None:
|
||||
log.debug("is_loaded: no presentation or document")
|
||||
return False
|
||||
try:
|
||||
if self._document.getPresentation() is None:
|
||||
log.debug("getPresentation failed to find a presentation")
|
||||
return False
|
||||
except:
|
||||
log.warning("getPresentation failed to find a presentation")
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_active(self):
|
||||
"""
|
||||
Returns true if a presentation is active and running.
|
||||
"""
|
||||
log.debug('is active LibreOffice')
|
||||
if not self.is_loaded():
|
||||
return False
|
||||
return self._control.isRunning() if self._control else False
|
||||
|
||||
def unblank_screen(self):
|
||||
"""
|
||||
Unblanks the screen.
|
||||
"""
|
||||
log.debug('unblank screen LibreOffice')
|
||||
return self._control.resume()
|
||||
|
||||
def blank_screen(self):
|
||||
"""
|
||||
Blanks the screen.
|
||||
"""
|
||||
log.debug('blank screen LibreOffice')
|
||||
self._control.blankScreen(0)
|
||||
|
||||
def is_blank(self):
|
||||
"""
|
||||
Returns true if screen is blank.
|
||||
"""
|
||||
log.debug('is blank LibreOffice')
|
||||
if self._control and self._control.isRunning():
|
||||
return self._control.isPaused()
|
||||
else:
|
||||
return False
|
||||
|
||||
def stop_presentation(self):
|
||||
"""
|
||||
Stop the presentation, remove from screen.
|
||||
"""
|
||||
log.debug('stop presentation LibreOffice')
|
||||
self._presentation.end()
|
||||
self._control = None
|
||||
|
||||
def start_presentation(self):
|
||||
"""
|
||||
Start the presentation from the beginning.
|
||||
"""
|
||||
log.debug('start presentation LibreOffice')
|
||||
if self._control is None or not self._control.isRunning():
|
||||
window = self._document.getCurrentController().getFrame().getContainerWindow()
|
||||
window.setVisible(True)
|
||||
self._presentation.start()
|
||||
self._control = self._presentation.getController()
|
||||
# start() returns before the Component is ready. Try for 15 seconds.
|
||||
sleep_count = 1
|
||||
while not self._control and sleep_count < 150:
|
||||
time.sleep(0.1)
|
||||
sleep_count += 1
|
||||
self._control = self._presentation.getController()
|
||||
window.setVisible(False)
|
||||
else:
|
||||
self._control.activate()
|
||||
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, slide_no):
|
||||
"""
|
||||
Go to a specific slide (from 1).
|
||||
|
||||
:param slide_no: The slide the text is required for, starting at 1
|
||||
"""
|
||||
self._control.gotoSlideIndex(slide_no - 1)
|
||||
|
||||
def next_step(self):
|
||||
"""
|
||||
Triggers the next effect of slide on the running presentation.
|
||||
"""
|
||||
is_paused = self._control.isPaused()
|
||||
self._control.gotoNextEffect()
|
||||
time.sleep(0.1)
|
||||
if not is_paused and self._control.isPaused():
|
||||
self._control.gotoPreviousEffect()
|
||||
|
||||
def previous_step(self):
|
||||
"""
|
||||
Triggers the previous slide on the running presentation.
|
||||
"""
|
||||
self._control.gotoPreviousEffect()
|
||||
|
||||
def get_slide_text(self, slide_no):
|
||||
"""
|
||||
Returns the text on the slide.
|
||||
|
||||
:param slide_no: The slide the text is required for, starting at 1
|
||||
"""
|
||||
return self._get_text_from_page(slide_no)
|
||||
|
||||
def get_slide_notes(self, slide_no):
|
||||
"""
|
||||
Returns the text in the slide notes.
|
||||
|
||||
:param slide_no: The slide the notes are required for, starting at 1
|
||||
"""
|
||||
return self._get_text_from_page(slide_no, TextType.Notes)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
The main function which runs the server
|
||||
"""
|
||||
daemon = Daemon()
|
||||
ns = locateNS()
|
||||
uri = daemon.register(LibreOfficeServer)
|
||||
ns.register('openlp.libreofficeserver', uri)
|
||||
try:
|
||||
daemon.requestLoop()
|
||||
finally:
|
||||
daemon.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
302
openlp/plugins/presentations/lib/maclocontroller.py
Normal file
302
openlp/plugins/presentations/lib/maclocontroller.py
Normal file
@ -0,0 +1,302 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2016 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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 #
|
||||
###############################################################################
|
||||
|
||||
# OOo API documentation:
|
||||
# http://api.openoffice.org/docs/common/ref/com/sun/star/presentation/XSlideShowController.html
|
||||
# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/ProUNO/Basic
|
||||
# /Getting_Information_about_UNO_Objects#Inspecting_interfaces_during_debugging
|
||||
# http://docs.go-oo.org/sd/html/classsd_1_1SlideShow.html
|
||||
# http://www.oooforum.org/forum/viewtopic.phtml?t=5252
|
||||
# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Working_with_Presentations
|
||||
# http://mail.python.org/pipermail/python-win32/2008-January/006676.html
|
||||
# http://www.linuxjournal.com/content/starting-stopping-and-connecting-openoffice-python
|
||||
# http://nxsy.org/comparing-documents-with-openoffice-and-python
|
||||
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from subprocess import Popen
|
||||
from multiprocessing import Process
|
||||
|
||||
from openlp.core.common import is_mac, Registry, delete_file
|
||||
|
||||
if is_mac() and os.path.exists('/Applications/LibreOffice.app'):
|
||||
macuno_available = True
|
||||
else:
|
||||
macuno_available = False
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from Pyro4 import Proxy
|
||||
from Pyro4.naming import NameServerDaemon
|
||||
|
||||
from openlp.core.lib import ScreenList
|
||||
from .presentationcontroller import PresentationController, PresentationDocument, TextType
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def run_nameserver():
|
||||
"""
|
||||
Run a Pyro4 nameserver
|
||||
"""
|
||||
ns_daemon = NameServerDaemon()
|
||||
ns_daemon._pyroHmacKey = 'openlp-libreoffice'
|
||||
try:
|
||||
ns_daemon.requestLoop()
|
||||
finally:
|
||||
ns_daemon.close()
|
||||
|
||||
|
||||
class MacLOController(PresentationController):
|
||||
"""
|
||||
Class to control interactions with MacLO presentations on Mac OS X via Pyro4. It starts the Pyro4 nameserver,
|
||||
starts the LibreOfficeServer, and then controls MacLO via Pyro4.
|
||||
"""
|
||||
log.info('MacLOController loaded')
|
||||
|
||||
def __init__(self, plugin):
|
||||
"""
|
||||
Initialise the class
|
||||
"""
|
||||
log.debug('Initialising')
|
||||
super(MacLOController, self).__init__(plugin, 'MacLO', MacLODocument)
|
||||
self.supports = ['odp']
|
||||
self.also_supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm']
|
||||
self.server_process = None
|
||||
self.nameserver_process = None
|
||||
self.client = None
|
||||
self._start_nameserver()
|
||||
self._start_server()
|
||||
self._setup_client()
|
||||
|
||||
def _start_nameserver(self):
|
||||
"""
|
||||
Start the Pyro4 nameserver
|
||||
"""
|
||||
self.nameserver_process = Process(run_nameserver)
|
||||
self.nameserver_process.start()
|
||||
|
||||
def _start_server(self):
|
||||
"""
|
||||
Start a LibreOfficeServer
|
||||
"""
|
||||
libreoffice_python = '/Applications/LibreOffice.app/Contents/Resources/python'
|
||||
libreoffice_server = os.path.join(os.path.dirname(__file__), 'libreofficeserver.py')
|
||||
self.server_process = Popen([libreoffice_python, libreoffice_server])
|
||||
|
||||
def _setup_client(self):
|
||||
"""
|
||||
Set up a Pyro4 client so that we can talk to the LibreOfficeServer
|
||||
"""
|
||||
self.client = Proxy('PYRONAME:openlp.libreofficeserver')
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
MacLO is able to run on this machine.
|
||||
"""
|
||||
log.debug('check_available')
|
||||
return macuno_available
|
||||
|
||||
def start_process(self):
|
||||
"""
|
||||
Loads a running version of LibreOffice in the background. It is not displayed to the user but is available to the
|
||||
UNO interface when required.
|
||||
"""
|
||||
log.debug('start process Openoffice')
|
||||
if is_win():
|
||||
self.manager = self.get_com_servicemanager()
|
||||
self.manager._FlagAsMethod('Bridge_GetStruct')
|
||||
self.manager._FlagAsMethod('Bridge_GetValueObject')
|
||||
else:
|
||||
# -headless
|
||||
cmd = get_uno_command()
|
||||
self.process = QtCore.QProcess()
|
||||
self.process.startDetached(cmd)
|
||||
|
||||
def kill(self):
|
||||
"""
|
||||
Called at system exit to clean up any running presentations.
|
||||
"""
|
||||
log.debug('Kill LibreOffice')
|
||||
self.client.shutdown()
|
||||
self.server_process.terminate()
|
||||
self.nameserver_process.terminate()
|
||||
|
||||
|
||||
class MacLODocument(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('Init Presentation LibreOffice')
|
||||
super(MacLODocument, self).__init__(controller, presentation)
|
||||
self.client = controller.client
|
||||
self.document = None
|
||||
self.presentation = None
|
||||
self.control = None
|
||||
|
||||
def load_presentation(self):
|
||||
"""
|
||||
Called when a presentation is added to the SlideController. It builds the environment, starts communcations with
|
||||
the background LibreOffice task started earlier. If LibreOffice is not present is is started. Once the environment
|
||||
is available the presentation is loaded and started.
|
||||
"""
|
||||
log.debug('Load Presentation LibreOffice')
|
||||
if not self.client.has_desktop():
|
||||
return False
|
||||
if not self.client.load_presentation(self.file_path, ScreenList().current['number'] + 1):
|
||||
return False
|
||||
self.create_thumbnails()
|
||||
self.client.create_titles_and_notes()
|
||||
return True
|
||||
|
||||
def create_thumbnails(self):
|
||||
"""
|
||||
Create thumbnail images for presentation.
|
||||
"""
|
||||
log.debug('create thumbnails LibreOffice')
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
self.client.create_thumbnails(self.get_temp_folder())
|
||||
|
||||
def create_titles_and_notes(self):
|
||||
"""
|
||||
Writes the list of titles (one per slide) to 'titles.txt' and the notes to 'slideNotes[x].txt'
|
||||
in the thumbnails directory
|
||||
"""
|
||||
titles, notes = self.client.get_titles_and_notes()
|
||||
self.save_titles_and_notes(titles, notes)
|
||||
|
||||
def close_presentation(self):
|
||||
"""
|
||||
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
|
||||
shutdown.
|
||||
"""
|
||||
log.debug('close Presentation LibreOffice')
|
||||
self.client.close_presentation()
|
||||
self.controller.remove_doc(self)
|
||||
|
||||
def is_loaded(self):
|
||||
"""
|
||||
Returns true if a presentation is loaded.
|
||||
"""
|
||||
log.debug('is loaded LibreOffice')
|
||||
return self.client.is_loaded()
|
||||
|
||||
def is_active(self):
|
||||
"""
|
||||
Returns true if a presentation is active and running.
|
||||
"""
|
||||
log.debug('is active LibreOffice')
|
||||
return self.client.is_active()
|
||||
|
||||
def unblank_screen(self):
|
||||
"""
|
||||
Unblanks the screen.
|
||||
"""
|
||||
log.debug('unblank screen LibreOffice')
|
||||
return self.client.unblank_screen()
|
||||
|
||||
def blank_screen(self):
|
||||
"""
|
||||
Blanks the screen.
|
||||
"""
|
||||
log.debug('blank screen LibreOffice')
|
||||
self.client.blank_screen()
|
||||
|
||||
def is_blank(self):
|
||||
"""
|
||||
Returns true if screen is blank.
|
||||
"""
|
||||
log.debug('is blank LibreOffice')
|
||||
return self.client.is_blank()
|
||||
|
||||
def stop_presentation(self):
|
||||
"""
|
||||
Stop the presentation, remove from screen.
|
||||
"""
|
||||
log.debug('stop presentation LibreOffice')
|
||||
self.client.stop_presentation()
|
||||
|
||||
def start_presentation(self):
|
||||
"""
|
||||
Start the presentation from the beginning.
|
||||
"""
|
||||
log.debug('start presentation LibreOffice')
|
||||
self.client.start_presentation()
|
||||
# Make sure impress doesn't steal focus, unless we're on a single screen setup
|
||||
if len(ScreenList().screen_list) > 1:
|
||||
Registry().get('main_window').activateWindow()
|
||||
|
||||
def get_slide_number(self):
|
||||
"""
|
||||
Return the current slide number on the screen, from 1.
|
||||
"""
|
||||
return self.client.get_slide_number()
|
||||
|
||||
def get_slide_count(self):
|
||||
"""
|
||||
Return the total number of slides.
|
||||
"""
|
||||
return self.client.get_slide_count()
|
||||
|
||||
def goto_slide(self, slide_no):
|
||||
"""
|
||||
Go to a specific slide (from 1).
|
||||
|
||||
:param slide_no: The slide the text is required for, starting at 1
|
||||
"""
|
||||
self.client.goto_slide(slide_no)
|
||||
|
||||
def next_step(self):
|
||||
"""
|
||||
Triggers the next effect of slide on the running presentation.
|
||||
"""
|
||||
self.client.next_step()
|
||||
|
||||
def previous_step(self):
|
||||
"""
|
||||
Triggers the previous slide on the running presentation.
|
||||
"""
|
||||
self.client.previous_step()
|
||||
|
||||
def get_slide_text(self, slide_no):
|
||||
"""
|
||||
Returns the text on the slide.
|
||||
|
||||
:param slide_no: The slide the text is required for, starting at 1
|
||||
"""
|
||||
return self.client.get_slide_text(slide_no)
|
||||
|
||||
def get_slide_notes(self, slide_no):
|
||||
"""
|
||||
Returns the text in the slide notes.
|
||||
|
||||
:param slide_no: The slide the notes are required for, starting at 1
|
||||
"""
|
||||
return self.client.get_slide_notes(slide_no)
|
||||
|
Loading…
Reference in New Issue
Block a user