We can load presentations via LibreOffice now. Next step: more bugfixing, tests, and sorting out one or two hard-coded paths

This commit is contained in:
Raoul Snyman 2016-11-02 00:41:10 +02:00
parent c5709b9778
commit c7750a5f1a
4 changed files with 95 additions and 108 deletions

View File

@ -150,7 +150,8 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
triggers=self.on_replace_click) triggers=self.on_replace_click)
if 'webkit' not in get_media_players()[0]: if 'webkit' not in get_media_players()[0]:
self.replace_action.setDisabled(True) self.replace_action.setDisabled(True)
self.replace_action_context.setDisabled(True) if hasattr(self, 'replace_action_context'):
self.replace_action_context.setDisabled(True)
self.reset_action = self.toolbar.add_toolbar_action('reset_action', icon=':/system/system_close.png', self.reset_action = self.toolbar.add_toolbar_action('reset_action', icon=':/system/system_close.png',
visible=False, triggers=self.on_reset_click) visible=False, triggers=self.on_reset_click)
self.media_widget = QtWidgets.QWidget(self) self.media_widget = QtWidgets.QWidget(self)

View File

@ -9,10 +9,10 @@ import logging
# Add the vendor directory to sys.path so that we can load Pyro4 # Add the vendor directory to sys.path so that we can load Pyro4
sys.path.append(os.path.join(os.path.dirname(__file__), 'vendor')) sys.path.append(os.path.join(os.path.dirname(__file__), 'vendor'))
from Pyro4 import Daemon, expose, locateNS import uno
from com.sun.star.beans import PropertyValue from com.sun.star.beans import PropertyValue
from com.sun.star.task import ErrorCodeIOException from com.sun.star.task import ErrorCodeIOException
import uno from Pyro4 import Daemon, expose, locateNS
logging.basicConfig(filename=os.path.dirname(__file__) + '/libreofficeserver.log', level=logging.INFO) logging.basicConfig(filename=os.path.dirname(__file__) + '/libreofficeserver.log', level=logging.INFO)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -34,11 +34,15 @@ class LibreOfficeServer(object):
""" """
def __init__(self): def __init__(self):
""" """
Set up the LibreOffice server Set up the server
""" """
self._init_impress() self._desktop = None
self._document = None
self._control = None
self._presentation = None
self._process = None
def _init_impress(self) def start_process(self):
""" """
Initialise Impress Initialise Impress
""" """
@ -49,23 +53,31 @@ class LibreOfficeServer(object):
'--minimized', '--minimized',
'--nodefault', '--nodefault',
'--nofirststartwizard', '--nofirststartwizard',
'"--accept=pipe,name=openlp_pipe;urp;"' '--accept=socket,port=2002;urp;'
] ]
self._process = Popen(uno_command) self._process = Popen(uno_command)
def setup_desktop(self):
"""
Set up an UNO desktop instance
"""
if self.has_desktop():
return
uno_instance = None uno_instance = None
context = uno.getComponentContext() context = uno.getComponentContext()
resolver = context.ServiceManager.createInstanceWithContext('com.sun.star.bridge.UnoUrlResolver', context) resolver = context.ServiceManager.createInstanceWithContext('com.sun.star.bridge.UnoUrlResolver', context)
loop = 0
while uno_instance is None and loop < 3: while uno_instance is None and loop < 3:
try: try:
uno_instance = resolver.resolve('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext') uno_instance = resolver.resolve('uno:socket,port=2002;urp;StarOffice.ComponentContext')
except: except Exception as e:
log.warning('Unable to find running instance ') log.warning('Unable to find running instance ')
loop += 1 loop += 1
try: try:
self._manager = uno_instance.ServiceManager self._manager = uno_instance.ServiceManager
log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop') log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
self._desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance) self._desktop = self._manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
except: except Exception as e:
log.warning('Failed to get UNO desktop') log.warning('Failed to get UNO desktop')
def _create_property(self, name, value): def _create_property(self, name, value):
@ -106,32 +118,35 @@ class LibreOfficeServer(object):
""" """
Say if we have a desktop object Say if we have a desktop object
""" """
return self._desktop is not None return hasattr(self, '_desktop') and self._desktop is not None
def shutdown(self): def shutdown(self):
""" """
Shut down the server Shut down the server
""" """
while self._docs: if hasattr(self, '_docs'):
self._docs[0].close_presentation() while self._docs:
if not self._desktop: self._docs[0].close_presentation()
return if self.has_desktop():
docs = self._desktop.getComponents() docs = self._desktop.getComponents()
count = 0 count = 0
if docs.hasElements(): if docs.hasElements():
list_elements = docs.createEnumeration() list_elements = docs.createEnumeration()
while list_elements.hasMoreElements(): while list_elements.hasMoreElements():
doc = list_elements.nextElement() doc = list_elements.nextElement()
if doc.getImplementationName() != 'com.sun.star.comp.framework.BackingComp': if doc.getImplementationName() != 'com.sun.star.comp.framework.BackingComp':
count += 1 count += 1
if count > 0: if count > 0:
log.debug('LibreOffice not terminated as docs are still open') log.debug('LibreOffice not terminated as docs are still open')
else: else:
try: try:
self._desktop.terminate() self._desktop.terminate()
log.debug('LibreOffice killed') log.debug('LibreOffice killed')
except: except:
log.warning('Failed to terminate LibreOffice') log.warning('Failed to terminate LibreOffice')
if getattr(self, '_process'):
self._process.kill()
def load_presentation(self, file_path, screen_number): def load_presentation(self, file_path, screen_number):
""" """
@ -148,35 +163,33 @@ class LibreOfficeServer(object):
self._presentation = self._document.getPresentation() self._presentation = self._document.getPresentation()
self._presentation.Display = screen_number self._presentation.Display = screen_number
self._control = None self._control = None
self.create_thumbnails()
self.create_titles_and_notes()
return True return True
def create_thumbnails(self, temp_folder): def extract_thumbnails(self, temp_folder):
""" """
Create thumbnails for the presentation Create thumbnails for the presentation
""" """
thumbnails = []
thumb_dir_url = uno.systemPathToFileUrl(temp_folder) thumb_dir_url = uno.systemPathToFileUrl(temp_folder)
properties = (self._create_property('FilterName', 'impress_png_Export'),) properties = (self._create_property('FilterName', 'impress_png_Export'),)
doc = self.document pages = self._document.getDrawPages()
pages = doc.getDrawPages()
if not pages: if not pages:
return return
if not os.path.isdir(temp_folder): if not os.path.isdir(temp_folder):
os.makedirs(temp_folder) os.makedirs(temp_folder)
for index in range(pages.getCount()): for index in range(pages.getCount()):
page = pages.getByIndex(index) page = pages.getByIndex(index)
doc.getCurrentController().setCurrentPage(page) self._document.getCurrentController().setCurrentPage(page)
url_path = '{path}/{name}.png'.format(path=thumb_dir_url, name=str(index + 1)) 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') path = os.path.join(temp_folder, str(index + 1) + '.png')
try: try:
doc.storeToURL(url_path, properties) self._document.storeToURL(url_path, properties)
self.convert_thumbnail(path, index + 1) thumbnails.append(path)
delete_file(path)
except ErrorCodeIOException as exception: except ErrorCodeIOException as exception:
log.exception('ERROR! ErrorCodeIOException {error:d}'.format(error=exception.ErrCode)) log.exception('ERROR! ErrorCodeIOException {error:d}'.format(error=exception.ErrCode))
except: except:
log.exception('{path} - Unable to store openoffice preview'.format(path=path)) log.exception('{path} - Unable to store openoffice preview'.format(path=path))
return thumbnails
def get_title_and_notes(self): def get_title_and_notes(self):
""" """
@ -346,10 +359,8 @@ def main():
""" """
The main function which runs the server The main function which runs the server
""" """
daemon = Daemon() daemon = Daemon(host='localhost', port=4310)
ns = locateNS() uri = daemon.register(LibreOfficeServer, 'openlp.libreofficeserver')
uri = daemon.register(LibreOfficeServer)
ns.register('openlp.libreofficeserver', uri)
try: try:
daemon.requestLoop() daemon.requestLoop()
finally: finally:

View File

@ -35,18 +35,16 @@ import logging
import os import os
import time import time
from subprocess import Popen from subprocess import Popen
from multiprocessing import Process
from openlp.core.common import is_mac, Registry, delete_file from openlp.core.common import is_macosx, Registry, delete_file
if is_mac() and os.path.exists('/Applications/LibreOffice.app'): if is_macosx() and os.path.exists('/Applications/LibreOffice.app'):
macuno_available = True macuno_available = True
else: else:
macuno_available = False macuno_available = False
from PyQt5 import QtCore from PyQt5 import QtCore
from Pyro4 import Proxy from Pyro4 import Proxy
from Pyro4.naming import NameServerDaemon
from openlp.core.lib import ScreenList from openlp.core.lib import ScreenList
from .presentationcontroller import PresentationController, PresentationDocument, TextType from .presentationcontroller import PresentationController, PresentationDocument, TextType
@ -55,18 +53,6 @@ from .presentationcontroller import PresentationController, PresentationDocument
log = logging.getLogger(__name__) 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 MacLOController(PresentationController):
""" """
Class to control interactions with MacLO presentations on Mac OS X via Pyro4. It starts the Pyro4 nameserver, Class to control interactions with MacLO presentations on Mac OS X via Pyro4. It starts the Pyro4 nameserver,
@ -83,18 +69,8 @@ class MacLOController(PresentationController):
self.supports = ['odp'] self.supports = ['odp']
self.also_supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm'] self.also_supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm']
self.server_process = None self.server_process = None
self.nameserver_process = None self._client = None
self.client = None
self._start_nameserver()
self._start_server() 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): def _start_server(self):
""" """
@ -104,11 +80,16 @@ class MacLOController(PresentationController):
libreoffice_server = os.path.join(os.path.dirname(__file__), 'libreofficeserver.py') libreoffice_server = os.path.join(os.path.dirname(__file__), 'libreofficeserver.py')
self.server_process = Popen([libreoffice_python, libreoffice_server]) self.server_process = Popen([libreoffice_python, libreoffice_server])
def _setup_client(self): @property
def client(self):
""" """
Set up a Pyro4 client so that we can talk to the LibreOfficeServer Set up a Pyro4 client so that we can talk to the LibreOfficeServer
""" """
self.client = Proxy('PYRONAME:openlp.libreofficeserver') if not self._client:
self._client = Proxy('PYRO:openlp.libreofficeserver@localhost:4310')
if not self._client._pyroConnection:
self._client._pyroReconnect()
return self._client
def check_available(self): def check_available(self):
""" """
@ -119,19 +100,11 @@ class MacLOController(PresentationController):
def start_process(self): 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 Loads a running version of LibreOffice in the background. It is not displayed to the user but is available to
UNO interface when required. the UNO interface when required.
""" """
log.debug('start process Openoffice') log.debug('Started automatically by the Pyro server')
if is_win(): self.client.start_process()
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): def kill(self):
""" """
@ -139,8 +112,7 @@ class MacLOController(PresentationController):
""" """
log.debug('Kill LibreOffice') log.debug('Kill LibreOffice')
self.client.shutdown() self.client.shutdown()
self.server_process.terminate() self.server_process.kill()
self.nameserver_process.terminate()
class MacLODocument(PresentationDocument): class MacLODocument(PresentationDocument):
@ -155,9 +127,6 @@ class MacLODocument(PresentationDocument):
log.debug('Init Presentation LibreOffice') log.debug('Init Presentation LibreOffice')
super(MacLODocument, self).__init__(controller, presentation) super(MacLODocument, self).__init__(controller, presentation)
self.client = controller.client self.client = controller.client
self.document = None
self.presentation = None
self.control = None
def load_presentation(self): def load_presentation(self):
""" """
@ -166,12 +135,13 @@ class MacLODocument(PresentationDocument):
is available the presentation is loaded and started. is available the presentation is loaded and started.
""" """
log.debug('Load Presentation LibreOffice') log.debug('Load Presentation LibreOffice')
self.client.setup_desktop()
if not self.client.has_desktop(): if not self.client.has_desktop():
return False return False
if not self.client.load_presentation(self.file_path, ScreenList().current['number'] + 1): if not self.client.load_presentation(self.file_path, ScreenList().current['number'] + 1):
return False return False
self.create_thumbnails() self.create_thumbnails()
self.client.create_titles_and_notes() self.create_titles_and_notes()
return True return True
def create_thumbnails(self): def create_thumbnails(self):
@ -181,7 +151,10 @@ class MacLODocument(PresentationDocument):
log.debug('create thumbnails LibreOffice') log.debug('create thumbnails LibreOffice')
if self.check_thumbnails(): if self.check_thumbnails():
return return
self.client.create_thumbnails(self.get_temp_folder()) temp_thumbnails = self.client.extract_thumbnails(self.get_temp_folder())
for index, temp_thumb in enumerate(temp_thumbnails):
self.convert_thumbnail(temp_thumb, index + 1)
delete_file(temp_thumb)
def create_titles_and_notes(self): def create_titles_and_notes(self):
""" """

View File

@ -36,18 +36,20 @@ from openlp.plugins.presentations.lib import PresentationController, Presentatio
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
__default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked, __default_settings__ = {
'presentations/enable_pdf_program': QtCore.Qt.Unchecked, 'presentations/override app': QtCore.Qt.Unchecked,
'presentations/pdf_program': '', 'presentations/enable_pdf_program': QtCore.Qt.Unchecked,
'presentations/Impress': QtCore.Qt.Checked, 'presentations/pdf_program': '',
'presentations/Powerpoint': QtCore.Qt.Checked, 'presentations/MacLO': QtCore.Qt.Checked,
'presentations/Powerpoint Viewer': QtCore.Qt.Checked, 'presentations/Impress': QtCore.Qt.Checked,
'presentations/Pdf': QtCore.Qt.Checked, 'presentations/Powerpoint': QtCore.Qt.Checked,
'presentations/presentations files': [], 'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
'presentations/thumbnail_scheme': '', 'presentations/Pdf': QtCore.Qt.Checked,
'presentations/powerpoint slide click advance': QtCore.Qt.Unchecked, 'presentations/presentations files': [],
'presentations/powerpoint control window': QtCore.Qt.Unchecked 'presentations/thumbnail_scheme': '',
} 'presentations/powerpoint slide click advance': QtCore.Qt.Unchecked,
'presentations/powerpoint control window': QtCore.Qt.Unchecked
}
class PresentationPlugin(Plugin): class PresentationPlugin(Plugin):
@ -87,7 +89,7 @@ class PresentationPlugin(Plugin):
try: try:
self.controllers[controller].start_process() self.controllers[controller].start_process()
except Exception: except Exception:
log.warning('Failed to start controller process') log.exception('Failed to start controller process')
self.controllers[controller].available = False self.controllers[controller].available = False
self.media_item.build_file_mask_string() self.media_item.build_file_mask_string()
@ -132,7 +134,7 @@ class PresentationPlugin(Plugin):
try: try:
__import__(module_name, globals(), locals(), []) __import__(module_name, globals(), locals(), [])
except ImportError: except ImportError:
log.warning('Failed to import {name} on path {path}'.format(name=module_name, path=path)) log.exception('Failed to import {name} on path {path}'.format(name=module_name, path=path))
controller_classes = PresentationController.__subclasses__() controller_classes = PresentationController.__subclasses__()
for controller_class in controller_classes: for controller_class in controller_classes:
controller = controller_class(self) controller = controller_class(self)