forked from openlp/openlp
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:
parent
c5709b9778
commit
c7750a5f1a
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user