#!/usr/bin/env python3 # == Rapid Develop Macros in LibreOffice == # ~ https://git.cuates.net/elmau/easymacro # ~ easymacro 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, either version 3 of the License, or # ~ (at your option) any later version. # ~ easymacro 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 easymacro. If not, see . import datetime import getpass import logging import os import platform import socket import subprocess import sys import time import traceback from functools import wraps from pprint import pprint from typing import Any import uno from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS from com.sun.star.awt.MessageBoxResults import YES from com.sun.star.beans import PropertyValue LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s' LOG_DATE = '%d/%m/%Y %H:%M:%S' logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m') logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m') logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m') logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE) log = logging.getLogger(__name__) # Global variables OS = platform.system() DESKTOP = os.environ.get('DESKTOP_SESSION', '') PC = platform.node() USER = getpass.getuser() IS_WIN = OS == 'Windows' IS_MAC = OS == 'Darwin' _info_debug = f"Python: {sys.version}\n\n{platform.platform()}\n\n" + '\n'.join(sys.path) SALT = b'00a1bfb05353bb3fd8e7aa7fe5efdccc' CTX = uno.getComponentContext() SM = CTX.getServiceManager() def debug(*args) -> None: """ Show messages debug Parameters: args (list): iterable messages for show """ data = [str(a) for a in args] log.debug('\t'.join(data)) return def error(message: Any) -> None: """ Show message error Parameters: message (Any): message show error """ log.error(message) return def info(*args) -> None: """ Show messages info Parameters: args (list): iterable messages for show """ data = [str(a) for a in args] log.info('\t'.join(data)) return def save_log(path: str, data: Any) -> None: """ Save data in file, data append to end Parameters: path (str): path to save data (Any): any info data """ with open(path, 'a') as f: f.write(f'{str(now())[:19]} - ') pprint(data, stream=f) return def create_instance(name: str, with_context: bool=False, args: Any=None) -> Any: if with_context: instance = SM.createInstanceWithContext(name, CTX) elif args: instance = SM.createInstanceWithArguments(name, (args,)) else: instance = SM.createInstance(name) return instance def get_app_config(node_name: str, key: str='', update: bool=False): name = 'com.sun.star.configuration.ConfigurationProvider' service = 'com.sun.star.configuration.ConfigurationAccess' if update: service = 'com.sun.star.configuration.ConfigurationUpdateAccess' cp = create_instance(name, True) node = PropertyValue(Name='nodepath', Value=node_name) try: ca = cp.createInstanceWithArguments(service, (node,)) if ca and not key: return ca if ca and ca.hasByName(key): return ca.getPropertyValue(key) except Exception as e: error(e) return '' # Get info LibO NAME = TITLE = get_app_config('org.openoffice.Setup/Product', 'ooName') VERSION = get_app_config('org.openoffice.Setup/Product','ooSetupVersion') LANGUAGE = get_app_config('org.openoffice.Setup/L10N/', 'ooLocale') LANG = LANGUAGE.split('-')[0] INFO_DEBUG = f"{NAME} v{VERSION} {LANGUAGE}\n\n{_info_debug}" node = '/org.openoffice.Office.Calc/Calculate/Other/Date' year = get_app_config(node, 'YY') month = get_app_config(node, 'MM') day = get_app_config(node, 'DD') DATE_OFFSET = datetime.date(year, month, day).toordinal() def msgbox(message: Any, title: str=TITLE, buttons=MSG_BUTTONS.BUTTONS_OK, type_msg: str='infobox') -> Any: """ Create message box type_msg: infobox, warningbox, errorbox, querybox, messbox http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMessageBoxFactory.html """ toolkit = create_instance('com.sun.star.awt.Toolkit') parent = toolkit.getDesktopWindow() box = toolkit.createMessageBox(parent, type_msg, buttons, title, str(message)) return box.execute() def question(message: Any, title: str=TITLE) -> Any: result = msgbox(message, title, MSG_BUTTONS.BUTTONS_YES_NO, 'querybox') return result == YES def warning(message: Any, title: str=TITLE) -> Any: return msgbox(message, title, type_msg='warningbox') def errorbox(message: Any, title: str=TITLE) -> Any: return msgbox(message, title, type_msg='errorbox') def mri(obj: Any) -> None: """ Inspect object with MRI Extension """ m = create_instance('mytools.Mri') if m is None: msg = 'Extension MRI not found' error(msg) return if hasattr(obj, 'obj'): obj = obj.obj m.inspect(obj) return def catch_exception(f): @wraps(f) def func(*args, **kwargs): try: return f(*args, **kwargs) except Exception as e: name = f.__name__ if IS_WIN: msgbox(traceback.format_exc()) log.error(name, exc_info=True) return func class LOServer(object): HOST = 'localhost' PORT = '8100' ARG = f'socket,host={HOST},port={PORT};urp;StarOffice.ComponentContext' CMD = ['soffice', '-env:SingleAppInstance=false', '-env:UserInstallation=file:///tmp/LO_Process8100', '--headless', '--norestore', '--invisible', f'--accept={ARG}'] def __init__(self): self._server = None self._ctx = None self._sm = None self._start_server() self._init_values() def _init_values(self): global CTX global SM if not self.is_running: return ctx = uno.getComponentContext() service = 'com.sun.star.bridge.UnoUrlResolver' resolver = ctx.ServiceManager.createInstanceWithContext(service, ctx) self._ctx = resolver.resolve('uno:{}'.format(self.ARG)) self._sm = self._ctx.getServiceManager() CTX = self._ctx SM = self._sm return @property def is_running(self): try: s = socket.create_connection((self.HOST, self.PORT), 5.0) s.close() debug('LibreOffice is running...') return True except ConnectionRefusedError: return False def _start_server(self): if self.is_running: return for i in range(3): self._server = subprocess.Popen(self.CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE) time.sleep(3) if self.is_running: break return def stop(self): if self._server is None: print('Search pgrep soffice') else: self._server.terminate() debug('LibreOffice is stop...') return def create_instance(self, name, with_context=True): if with_context: instance = self._sm.createInstanceWithContext(name, self._ctx) else: instance = self._sm.createInstance(name) return instance