zaz-pip/easymacro.py

5547 lines
148 KiB
Python

#!/usr/bin/env python3
# == Rapid Develop Macros in LibreOffice ==
# ~ This file is part of ZAZ.
# ~ ZAZ 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.
# ~ ZAZ 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 ZAZ. If not, see <https://www.gnu.org/licenses/>.
import base64
import csv
import ctypes
import datetime
import errno
import gettext
import getpass
import hashlib
import json
import logging
import os
import platform
import re
import shlex
import shutil
import socket
import subprocess
import ssl
import sys
import tempfile
import threading
import time
import traceback
import zipfile
from functools import wraps
from pathlib import Path, PurePath
from pprint import pprint
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
from string import Template
from subprocess import PIPE
import smtplib
from smtplib import SMTPException, SMTPAuthenticationError
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import formatdate
from email import encoders
import mailbox
import uno
import unohelper
from com.sun.star.util import Time, Date, DateTime
from com.sun.star.beans import PropertyValue, NamedValue
from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
from com.sun.star.awt.MessageBoxResults import YES
from com.sun.star.awt.PosSize import POSSIZE, SIZE
from com.sun.star.awt import Size, Point
from com.sun.star.awt import Rectangle
from com.sun.star.awt import KeyEvent
from com.sun.star.awt.KeyFunction import QUIT
from com.sun.star.datatransfer import XTransferable, DataFlavor
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
from com.sun.star.script import ScriptEventDescriptor
from com.sun.star.lang import XEventListener
from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMouseListener
from com.sun.star.awt import XMouseMotionListener
from com.sun.star.util import XModifyListener
from com.sun.star.awt import XTopWindowListener
from com.sun.star.awt import XWindowListener
from com.sun.star.awt import XMenuListener
from com.sun.star.awt import XKeyListener
from com.sun.star.awt import XItemListener
from com.sun.star.awt import XFocusListener
from com.sun.star.awt import XTabListener
from com.sun.star.awt.grid import XGridDataListener
from com.sun.star.awt.grid import XGridSelectionListener
try:
from fernet import Fernet, InvalidToken
except ImportError:
pass
ID_EXTENSION = ''
DIR = {
'images': 'images',
'locales': 'locales',
}
KEY = {
'enter': 1280,
}
SEPARATION = 5
MSG_LANG = {
'es': {
'OK': 'Aceptar',
'Cancel': 'Cancelar',
'Select file': 'Seleccionar archivo',
'Incorrect user or password': 'Nombre de usuario o contraseña inválidos',
'Allow less secure apps in GMail': 'Activa: Permitir aplicaciones menos segura en GMail',
}
}
OS = platform.system()
USER = getpass.getuser()
PC = platform.node()
DESKTOP = os.environ.get('DESKTOP_SESSION', '')
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
IS_WIN = OS == 'Windows'
IS_MAC = OS == 'Darwin'
LOG_NAME = 'ZAZ'
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
PYTHON = 'python'
if IS_WIN:
PYTHON = 'python.exe'
CALC = 'calc'
WRITER = 'writer'
OBJ_CELL = 'ScCellObj'
OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj'
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
TEXT_RANGE = 'SwXTextRange'
TEXT_RANGES = 'SwXTextRanges'
TEXT_TYPE_RANGES = (TEXT_RANGE, TEXT_RANGES)
TYPE_DOC = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument',
'base': 'com.sun.star.sdb.DocumentDataSource',
'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE',
'main': 'com.sun.star.frame.StartModule',
}
NODE_MENUBAR = 'private:resource/menubar/menubar'
MENUS_MAIN = {
'file': '.uno:PickList',
'tools': '.uno:ToolsMenu',
'help': '.uno:HelpMenu',
}
MENUS_CALC = {
'file': '.uno:PickList',
'edit': '.uno:EditMenu',
'view': '.uno:ViewMenu',
'insert': '.uno:InsertMenu',
'format': '.uno:FormatMenu',
'styles': '.uno:FormatStylesMenu',
'sheet': '.uno:SheetMenu',
'data': '.uno:DataMenu',
'tools': '.uno:ToolsMenu',
'windows': '.uno:WindowList',
'help': '.uno:HelpMenu',
}
MENUS_WRITER = {
'file': '.uno:PickList',
'edit': '.uno:EditMenu',
'view': '.uno:ViewMenu',
'insert': '.uno:InsertMenu',
'format': '.uno:FormatMenu',
'styles': '.uno:FormatStylesMenu',
'sheet': '.uno:TableMenu',
'data': '.uno:FormatFormMenu',
'tools': '.uno:ToolsMenu',
'windows': '.uno:WindowList',
'help': '.uno:HelpMenu',
}
MENUS_APP = {
'main': MENUS_MAIN,
'calc': MENUS_CALC,
'writer': MENUS_WRITER,
}
EXT = {
'pdf': 'pdf',
}
FILE_NAME_DEBUG = 'debug.odt'
FILE_NAME_CONFIG = 'zaz-{}.json'
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__)
_start = 0
_stop_thread = {}
TIMEOUT = 10
SECONDS_DAY = 60 * 60 * 24
CTX = uno.getComponentContext()
SM = CTX.getServiceManager()
def create_instance(name, with_context=False):
if with_context:
instance = SM.createInstanceWithContext(name, CTX)
else:
instance = SM.createInstance(name)
return instance
def get_app_config(node_name, key=''):
name = 'com.sun.star.configuration.ConfigurationProvider'
service = 'com.sun.star.configuration.ConfigurationAccess'
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 ''
# ~ FILTER_PDF = '/org.openoffice.Office.Common/Filter/PDF/Export/'
LANGUAGE = get_app_config('org.openoffice.Setup/L10N/', 'ooLocale')
LANG = LANGUAGE.split('-')[0]
NAME = TITLE = get_app_config('org.openoffice.Setup/Product', 'ooName')
VERSION = get_app_config('org.openoffice.Setup/Product','ooSetupVersion')
nd = '/org.openoffice.Office.Calc/Calculate/Other/Date'
d = get_app_config(nd, 'DD')
m = get_app_config(nd, 'MM')
y = get_app_config(nd, 'YY')
DATE_OFFSET = datetime.date(y, m, d).toordinal()
def mri(obj):
m = create_instance('mytools.Mri')
if m is None:
msg = 'Extension MRI not found'
error(msg)
return
m.inspect(obj)
return
def inspect(obj):
zaz = create_instance('net.elmau.zaz.inspect')
zaz.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:
debug(traceback.format_exc())
log.error(name, exc_info=True)
return func
class LogWin(object):
def __init__(self, doc):
self.doc = doc
def write(self, info):
text = self.doc.Text
cursor = text.createTextCursor()
cursor.gotoEnd(False)
text.insertString(cursor, str(info) + '\n\n', 0)
return
def info(data):
log.info(data)
return
def debug(*info):
if IS_WIN:
doc = get_document(FILE_NAME_DEBUG)
if doc is None:
return
doc = LogWin(doc.obj)
doc.write(str(info))
return
data = [str(d) for d in info]
log.debug('\t'.join(data))
return
def error(info):
log.error(info)
return
def save_log(path, data):
with open(path, 'a') as out:
out.write('{} -{}- '.format(str(now())[:19], LOG_NAME))
pprint(data, stream=out)
return
def run_in_thread(fn):
def run(*k, **kw):
t = threading.Thread(target=fn, args=k, kwargs=kw)
t.start()
return t
return run
def now(only_time=False):
now = datetime.datetime.now()
if only_time:
return now.time()
return now
def today():
return datetime.date.today()
def get_date(year, month, day, hour=-1, minute=-1, second=-1):
if hour > -1 or minute > -1 or second > -1:
h = hour
m = minute
s = second
if h == -1:
h = 0
if m == -1:
m = 0
if s == -1:
s = 0
d = datetime.datetime(year, month, day, h, m, s)
else:
d = datetime.date(year, month, day)
return d
def get_config(key='', default=None, prefix='config'):
path_json = FILE_NAME_CONFIG.format(prefix)
values = None
path = join(get_config_path('UserConfig'), path_json)
if not exists_path(path):
return default
with open(path, 'r', encoding='utf-8') as fh:
data = fh.read()
values = json.loads(data)
if key:
return values.get(key, default)
return values
def set_config(key, value, prefix='config'):
path_json = FILE_NAME_CONFIG.format(prefix)
path = join(get_config_path('UserConfig'), path_json)
values = get_config(default={}, prefix=prefix)
values[key] = value
with open(path, 'w', encoding='utf-8') as fh:
json.dump(values, fh, ensure_ascii=False, sort_keys=True, indent=4)
return True
def sleep(seconds):
time.sleep(seconds)
return
def _(msg):
L = LANGUAGE.split('-')[0]
if L == 'en':
return msg
if not L in MSG_LANG:
return msg
return MSG_LANG[L][msg]
def msgbox(message, title=TITLE, buttons=MSG_BUTTONS.BUTTONS_OK, type_msg='infobox'):
""" 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()
mb = toolkit.createMessageBox(parent, type_msg, buttons, title, str(message))
return mb.execute()
def question(message, title=TITLE):
res = msgbox(message, title, MSG_BUTTONS.BUTTONS_YES_NO, 'querybox')
return res == YES
def warning(message, title=TITLE):
return msgbox(message, title, type_msg='warningbox')
def errorbox(message, title=TITLE):
return msgbox(message, title, type_msg='errorbox')
def get_desktop():
return create_instance('com.sun.star.frame.Desktop', True)
def get_dispatch():
return create_instance('com.sun.star.frame.DispatchHelper')
def call_dispatch(url, args=()):
frame = get_document().frame
dispatch = get_dispatch()
dispatch.executeDispatch(frame, url, '', 0, args)
return
def get_temp_file(only_name=False):
delete = True
if IS_WIN:
delete = False
tmp = tempfile.NamedTemporaryFile(delete=delete)
if only_name:
tmp = tmp.name
return tmp
def _path_url(path):
if path.startswith('file://'):
return path
return uno.systemPathToFileUrl(path)
def _path_system(path):
if path.startswith('file://'):
return os.path.abspath(uno.fileUrlToSystemPath(path))
return path
def exists_app(name):
try:
dn = subprocess.DEVNULL
subprocess.Popen([name, ''], stdout=dn, stderr=dn).terminate()
except OSError as e:
if e.errno == errno.ENOENT:
return False
return True
def exists_path(path):
return Path(path).exists()
def get_type_doc(obj):
for k, v in TYPE_DOC.items():
if obj.supportsService(v):
return k
return ''
def dict_to_property(values, uno_any=False):
ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
if uno_any:
ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
return ps
def dict_to_named(values):
ps = tuple([NamedValue(n, v) for n, v in values.items()])
return ps
def property_to_dict(values):
d = {i.Name: i.Value for i in values}
return d
def set_properties(model, properties):
if 'X' in properties:
properties['PositionX'] = properties.pop('X')
if 'Y' in properties:
properties['PositionY'] = properties.pop('Y')
keys = tuple(properties.keys())
values = tuple(properties.values())
model.setPropertyValues(keys, values)
return
def array_to_dict(values):
d = {r[0]: r[1] for r in values}
return d
# ~ Custom classes
class ObjectBase(object):
def __init__(self, obj):
self._obj = obj
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __getitem__(self, index):
return self.obj[index]
def __getattr__(self, name):
a = None
if name == 'obj':
a = super().__getattr__(name)
else:
if hasattr(self.obj, name):
a = getattr(self.obj, name)
return a
@property
def obj(self):
return self._obj
@obj.setter
def obj(self, value):
self._obj = value
class LOObjectBase(object):
def __init__(self, obj):
self.__dict__['_obj'] = obj
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
return True
def __setattr__(self, name, value):
print('BASE__setattr__', name)
if name == '_obj':
super().__setattr__(name, value)
else:
self.obj.setPropertyValue(name, value)
# ~ def _try_for_method(self, name):
# ~ a = None
# ~ m = 'get{}'.format(name)
# ~ if hasattr(self.obj, m):
# ~ a = getattr(self.obj, m)()
# ~ else:
# ~ a = getattr(self.obj, name)
# ~ return a
def __getattr__(self, name):
print('BASE__getattr__', name)
if name == 'obj':
a = super().__getattr__(name)
else:
a = self.obj.getPropertyValue(name)
# ~ Bug
if a is None:
msg = 'Error get: {} - {}'.format(self.obj.ImplementationName, name)
error(msg)
raise Exception(msg)
return a
@property
def obj(self):
return self._obj
class LODocument(object):
def __init__(self, obj):
self._obj = obj
self._init_values()
def _init_values(self):
self._type_doc = get_type_doc(self.obj)
self._cc = self.obj.getCurrentController()
return
@property
def obj(self):
return self._obj
@property
def title(self):
return self.obj.getTitle()
@title.setter
def title(self, value):
self.obj.setTitle(value)
@property
def uid(self):
return self.obj.RuntimeUID
@property
def type(self):
return self._type_doc
@property
def frame(self):
return self._cc.getFrame()
@property
def is_saved(self):
return self.obj.hasLocation()
@property
def is_modified(self):
return self.obj.isModified()
@property
def is_read_only(self):
return self.obj.isReadOnly()
@property
def path(self):
return _path_system(self.obj.getURL())
@property
def statusbar(self):
return self._cc.getStatusIndicator()
@property
def visible(self):
w = self._cc.getFrame().getContainerWindow()
return w.isVisible()
@visible.setter
def visible(self, value):
w = self._cc.getFrame().getContainerWindow()
w.setVisible(value)
@property
def zoom(self):
return self._cc.ZoomValue
@zoom.setter
def zoom(self, value):
self._cc.ZoomValue = value
@property
def table_auto_formats(self):
taf = create_instance('com.sun.star.sheet.TableAutoFormats')
return taf.ElementNames
def create_instance(self, name):
obj = self.obj.createInstance(name)
return obj
def save(self, path='', **kwargs):
# ~ opt = _properties(kwargs)
opt = dict_to_property(kwargs)
if path:
self._obj.storeAsURL(_path_url(path), opt)
else:
self._obj.store()
return True
def close(self):
self.obj.close(True)
return
def focus(self):
w = self._cc.getFrame().getComponentWindow()
w.setFocus()
return
def paste(self):
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
transferable = sc.getContents()
self._cc.insertTransferable(transferable)
return self.obj.getCurrentSelection()
def to_pdf(self, path, **kwargs):
path_pdf = path
if path:
if is_dir(path):
_, _, n, _ = get_info_path(self.path)
path_pdf = join(path, '{}.{}'.format(n, EXT['pdf']))
else:
path_pdf = replace_ext(self.path, EXT['pdf'])
filter_name = '{}_pdf_Export'.format(self.type)
filter_data = dict_to_property(kwargs, True)
args = {
'FilterName': filter_name,
'FilterData': filter_data,
}
args = dict_to_property(args)
try:
self.obj.storeToURL(_path_url(path_pdf), args)
except Exception as e:
error(e)
path_pdf = ''
return path_pdf
# ~ If location="document" Then
# ~ sp = ThisComponent.getScriptProvider()
class FormControlBase(object):
EVENTS = {
'action': 'actionPerformed',
'click': 'mousePressed',
}
TYPES = {
'actionPerformed': 'XActionListener',
'mousePressed': 'XMouseListener',
}
def __init__(self, obj):
self._obj = obj
self._index = -1
self._rules = {}
@property
def obj(self):
return self._obj
@property
def name(self):
return self.obj.Name
@property
def form(self):
return self.obj.getParent()
@property
def index(self):
return self._index
@index.setter
def index(self, value):
self._index = value
@property
def events(self):
return self.form.getScriptEvents(self.index)
def remove_event(self, name=''):
for ev in self.events:
if name and \
ev.EventMethod == self.EVENTS[name] and \
ev.ListenerType == self.TYPES[ev.EventMethod]:
self.form.revokeScriptEvent(self.index,
ev.ListenerType, ev.EventMethod, ev.AddListenerParam)
break
else:
self.form.revokeScriptEvent(self.index,
ev.ListenerType, ev.EventMethod, ev.AddListenerParam)
return
def add_event(self, name, macro):
if not 'name' in macro:
macro['name'] = '{}_{}'.format(self.name, name)
event = ScriptEventDescriptor()
event.AddListenerParam = ''
event.EventMethod = self.EVENTS[name]
event.ListenerType = self.TYPES[event.EventMethod]
event.ScriptCode = _get_url_script(macro)
event.ScriptType = 'Script'
for ev in self.events:
if ev.EventMethod == event.EventMethod and \
ev.ListenerType == event.ListenerType:
self.form.revokeScriptEvent(self.index,
event.ListenerType, event.EventMethod, event.AddListenerParam)
break
self.form.registerScriptEvent(self.index, event)
return
class FormButton(FormControlBase):
def __init__(self, obj):
super().__init__(obj)
class LOForm(ObjectBase):
def __init__(self, obj):
super().__init__(obj)
self._init_controls()
def __getitem__(self, index):
if isinstance(index, int):
return self._controls[index]
else:
return getattr(self, index)
def _get_type_control(self, name):
types = {
# ~ 'stardiv.Toolkit.UnoFixedTextControl': 'label',
'com.sun.star.form.OButtonModel': 'formbutton',
# ~ 'stardiv.Toolkit.UnoEditControl': 'text',
# ~ 'stardiv.Toolkit.UnoRoadmapControl': 'roadmap',
# ~ 'stardiv.Toolkit.UnoFixedHyperlinkControl': 'link',
# ~ 'stardiv.Toolkit.UnoListBoxControl': 'listbox',
}
return types[name]
def _init_controls(self):
self._controls = []
for i, c in enumerate(self.obj.ControlModels):
tipo = self._get_type_control(c.ImplementationName)
control = get_custom_class(tipo, c)
control.index = i
self._controls.append(control)
setattr(self, c.Name, control)
@property
def name(self):
return self._obj.getName()
@name.setter
def name(self, value):
self._obj.setName(value)
class LOForms(ObjectBase):
def __init__(self, obj, doc):
self._doc = doc
super().__init__(obj)
def __getitem__(self, index):
form = super().__getitem__(index)
return LOForm(form)
@property
def doc(self):
return self._doc
@property
def count(self):
return self.obj.getCount()
@property
def names(self):
return self.obj.getElementNames()
def exists(self, name):
return name in self.names
def insert(self, name):
form = self.doc.create_instance('com.sun.star.form.component.Form')
self.obj.insertByName(name, form)
return self[name]
def remove(self, index):
if isinstance(index, int):
self.obj.removeByIndex(index)
else:
self.obj.removeByName(index)
return
class LOCellStyle(LOObjectBase):
def __init__(self, obj):
super().__init__(obj)
@property
def name(self):
return self.obj.Name
def apply(self, properties):
set_properties(self.obj, properties)
return
class LOCellStyles(object):
def __init__(self, obj):
self._obj = obj
def __len__(self):
return len(self.obj)
def __getitem__(self, index):
return LOCellStyle(self.obj[index])
def __setitem__(self, key, value):
self.obj[key] = value
def __delitem__(self, key):
if not isinstance(key, str):
key = key.Name
del self.obj[key]
def __contains__(self, item):
return item in self.obj
@property
def obj(self):
return self._obj
@property
def names(self):
return self.obj.ElementNames
def apply(self, style, properties):
set_properties(style, properties)
return
class LOImage(object):
TYPES = {
'image/png': 'png',
'image/jpeg': 'jpg',
}
def __init__(self, obj):
self._obj = obj
@property
def obj(self):
return self._obj
@property
def address(self):
return self.obj.Anchor.AbsoluteName
@property
def name(self):
return self.obj.Name
@property
def mimetype(self):
return self.obj.Bitmap.MimeType
@property
def url(self):
return _path_system(self.obj.URL)
@url.setter
def url(self, value):
self.obj.URL = _path_url(value)
@property
def path(self):
return _path_system(self.obj.GraphicURL)
@path.setter
def path(self, value):
self.obj.GraphicURL = _path_url(value)
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self_obj.Visible = value
def save(self, path):
if is_dir(path):
p = path
n = self.name
else:
p, fn, n, e = get_info_path(path)
ext = self.TYPES[self.mimetype]
path = join(p, '{}.{}'.format(n, ext))
size = len(self.obj.Bitmap.DIB)
data = self.obj.GraphicStream.readBytes((), size)
data = data[-1].value
save_file(path, 'wb', data)
return path
class LOCalc(LODocument):
def __init__(self, obj):
super().__init__(obj)
self._sheets = obj.getSheets()
def __getitem__(self, index):
if isinstance(index, str):
code_name = [s.Name for s in self._sheets if s.CodeName == index]
if code_name:
index = code_name[0]
return LOCalcSheet(self._sheets[index], self)
def __setitem__(self, key, value):
self._sheets[key] = value
def __contains__(self, item):
return item in self.obj.Sheets
@property
def headers(self):
return self._cc.ColumnRowHeaders
@headers.setter
def headers(self, value):
self._cc.ColumnRowHeaders = value
@property
def tabs(self):
return self._cc.SheetTabs
@tabs.setter
def tabs(self, value):
self._cc.SheetTabs = value
@property
def active(self):
return LOCalcSheet(self._cc.getActiveSheet(), self)
def activate(self, sheet):
obj = sheet
if isinstance(sheet, LOCalcSheet):
obj = sheet.obj
elif isinstance(sheet, str):
obj = self[sheet].obj
self._cc.setActiveSheet(obj)
return
@property
def selection(self):
sel = self.obj.getCurrentSelection()
if sel.ImplementationName in OBJ_TYPE_RANGES:
sel = LOCellRange(sel, self)
return sel
@property
def sheets(self):
return LOCalcSheets(self._sheets, self)
@property
def names(self):
return self.sheets.names
@property
def cell_style(self):
obj = self.obj.getStyleFamilies()['CellStyles']
return LOCellStyles(obj)
def create(self):
return self.obj.createInstance('com.sun.star.sheet.Spreadsheet')
def insert(self, name, pos=-1):
# ~ sheet = obj.createInstance('com.sun.star.sheet.Spreadsheet')
# ~ obj.Sheets['New'] = sheet
index = pos
if pos < 0:
index = self._sheets.Count + pos + 1
if isinstance(name, str):
self._sheets.insertNewByName(name, index)
else:
for n in name:
self._sheets.insertNewByName(n, index)
name = n
return LOCalcSheet(self._sheets[name], self)
def move(self, name, pos=-1):
return self.sheets.move(name, pos)
def remove(self, name):
return self.sheets.remove(name)
def copy(self, source='', target='', pos=-1):
index = pos
if pos < 0:
index = self._sheets.Count + pos + 1
names = source
if not names:
names = self.names
elif isinstance(source, str):
names = (source,)
new_names = target
if not target:
new_names = [n + '_2' for n in names]
elif isinstance(target, str):
new_names = (target,)
for i, ns in enumerate(names):
self.sheets.copy(ns, new_names[i], index + i)
return LOCalcSheet(self._sheets[index], self)
def copy_from(self, doc, source='', target='', pos=-1):
index = pos
if pos < 0:
index = self._sheets.Count + pos + 1
names = source
if not names:
names = doc.names
elif isinstance(source, str):
names = (source,)
new_names = target
if not target:
new_names = names
elif isinstance(target, str):
new_names = (target,)
for i, n in enumerate(names):
self._sheets.importSheet(doc.obj, n, index + i)
self.sheets[index + i].name = new_names[i]
# ~ doc.getCurrentController().setActiveSheet(sheet)
# ~ For controls in sheet
# ~ doc.getCurrentController().setFormDesignMode(False)
return LOCalcSheet(self._sheets[index], self)
def sort(self, reverse=False):
names = sorted(self.names, reverse=reverse)
for i, n in enumerate(names):
self.sheets.move(n, i)
return
def get_cell(self, index=None):
"""
index is str 'A1'
index is tuple (row, col)
"""
if index is None:
cell = self.selection.first
else:
cell = LOCellRange(self.active[index].obj, self)
return cell
def select(self, rango):
r = rango
if hasattr(rango, 'obj'):
r = rango.obj
elif isinstance(rango, str):
r = self.get_cell(rango).obj
self._cc.select(r)
return
def create_cell_style(self, name=''):
obj = self.create_instance('com.sun.star.style.CellStyle')
if name:
self.cell_style[name] = obj
return LOCellStyle(obj)
def clear_undo(self):
self.obj.getUndoManager().clear()
return
def filter_by_color(self, cell=None):
if cell is None:
cell = self.selection.first
cr = cell.current_region
col = cell.column - cr.column
rangos = cell.get_column(col).visible
for r in rangos:
for row in range(r.rows):
c = r[row, 0]
if c.back_color != cell.back_color:
c.rows_visible = False
return
class LOCalcSheets(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
def __getitem__(self, index):
return LOCalcSheet(self.obj[index], self.doc)
@property
def obj(self):
return self._obj
@property
def doc(self):
return self._doc
@property
def count(self):
return self.obj.Count
@property
def names(self):
return self.obj.ElementNames
def copy(self, name, new_name, pos):
self.obj.copyByName(name, new_name, pos)
return
def move(self, name, pos):
index = pos
if pos < 0:
index = self.count + pos + 1
sheet = self.obj[name]
self.obj.moveByName(sheet.Name, index)
return
def remove(self, name):
sheet = self.obj[name]
self.obj.removeByName(sheet.Name)
return
class LOCalcSheet(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
self._init_values()
def __getitem__(self, index):
return LOCellRange(self.obj[index], self.doc)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def _init_values(self):
self._events = None
self._dp = self.obj.getDrawPage()
self._images = {i.Name: LOImage(i) for i in self._dp}
@property
def obj(self):
return self._obj
@property
def doc(self):
return self._doc
@property
def images(self):
return self._images
@property
def name(self):
return self._obj.Name
@name.setter
def name(self, value):
self._obj.Name = value
@property
def code_name(self):
return self._obj.CodeName
@code_name.setter
def code_name(self, value):
self._obj.CodeName = value
@property
def color(self):
return self._obj.TabColor
@color.setter
def color(self, value):
self._obj.TabColor = get_color(value)
@property
def active(self):
return self.doc.selection.first
def activate(self):
self.doc.activate(self.obj)
return
@property
def visible(self):
return self.obj.IsVisible
@visible.setter
def visible(self, value):
self.obj.IsVisible = value
@property
def is_protected(self):
return self._obj.isProtected()
@property
def password(self):
return ''
@visible.setter
def password(self, value):
self.obj.protect(value)
def unprotect(self, value):
try:
self.obj.unprotect(value)
return True
except:
pass
return False
def get_cursor(self, cell):
return self.obj.createCursorByRange(cell)
def exists_chart(self, name):
return name in self.obj.Charts.ElementNames
@property
def forms(self):
return LOForms(self._dp.getForms(), self.doc)
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers
self._connect_listeners()
def _connect_listeners(self):
if self.events is None:
return
listeners = {
'addModifyListener': EventsModify,
}
for key, value in listeners.items():
getattr(self.obj, key)(listeners[key](self.events))
print('add_listener')
return
class LOWriter(LODocument):
def __init__(self, obj):
super().__init__(obj)
@property
def obj(self):
return self._obj
@property
def string(self):
return self._obj.getText().String
@property
def text(self):
return self._obj.getText()
@property
def cursor(self):
return self.text.createTextCursor()
@property
def paragraphs(self):
return [LOTextRange(p) for p in self.text]
@property
def selection(self):
sel = self.obj.getCurrentSelection()
if sel.ImplementationName == TEXT_RANGES:
return LOTextRange(sel[0])
elif sel.ImplementationName == TEXT_RANGE:
return LOTextRange(sel)
return sel
def write(self, data, cursor=None):
cursor = cursor or self.selection.cursor.getEnd()
if data.startswith('\n'):
c = data.split('\n')
for i in range(len(c)-1):
self.text.insertControlCharacter(cursor, PARAGRAPH_BREAK, False)
else:
self.text.insertString(cursor, data, False)
return
def insert_table(self, data, cursor=None):
cursor = cursor or self.selection.cursor.getEnd()
table = self.obj.createInstance('com.sun.star.text.TextTable')
rows = len(data)
cols = len(data[0])
table.initialize(rows, cols)
self.insert_content(cursor, table)
table.DataArray = data
return WriterTable(table)
def create_chart(self, tipo, cursor=None):
cursor = cursor or self.selection.cursor.getEnd()
chart = LOChart(None, tipo)
chart.cursor = cursor
chart.doc = self
return chart
def insert_content(self, cursor, data, replace=False):
self.text.insertTextContent(cursor, data, replace)
return
# ~ f = doc.createInstance('com.sun.star.text.TextFrame')
# ~ f.setSize(Size(10000, 500))
def insert_image(self, path, **kwargs):
cursor = kwargs.get('cursor', self.selection.cursor.getEnd())
w = kwargs.get('width', 1000)
h = kwargs.get('Height', 1000)
image = self.create_instance('com.sun.star.text.GraphicObject')
image.GraphicURL = _path_url(path)
image.AnchorType = AS_CHARACTER
image.Width = w
image.Height = h
self.insert_content(cursor, image)
return
def go_start(self):
cursor = self._cc.getViewCursor()
cursor.gotoStart(False)
return cursor
def go_end(self):
cursor = self._cc.getViewCursor()
cursor.gotoEnd(False)
return cursor
def select(self, text):
self._cc.select(text)
return
def search(self, options):
descriptor = self.obj.createSearchDescriptor()
descriptor.setSearchString(options.get('Search', ''))
descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
descriptor.SearchWords = options.get('Words', False)
if 'Attributes' in options:
attr = dict_to_property(options['Attributes'])
descriptor.setSearchAttributes(attr)
if hasattr(descriptor, 'SearchRegularExpression'):
descriptor.SearchRegularExpression = options.get('RegularExpression', False)
if hasattr(descriptor, 'SearchType') and 'Type' in options:
descriptor.SearchType = options['Type']
if options.get('First', False):
found = self.obj.findFirst(descriptor)
else:
found = self.obj.findAll(descriptor)
return found
def replace(self, options):
descriptor = self.obj.createReplaceDescriptor()
descriptor.setSearchString(options['Search'])
descriptor.setReplaceString(options['Replace'])
descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
descriptor.SearchWords = options.get('Words', False)
if 'Attributes' in options:
attr = dict_to_property(options['Attributes'])
descriptor.setSearchAttributes(attr)
if hasattr(descriptor, 'SearchRegularExpression'):
descriptor.SearchRegularExpression = options.get('RegularExpression', False)
if hasattr(descriptor, 'SearchType') and 'Type' in options:
descriptor.SearchType = options['Type']
found = self.obj.replaceAll(descriptor)
return found
class LOTextRange(object):
def __init__(self, obj):
self._obj = obj
self._is_paragraph = self.obj.ImplementationName == 'SwXParagraph'
self._is_table = self.obj.ImplementationName == 'SwXTextTable'
@property
def obj(self):
return self._obj
@property
def is_paragraph(self):
return self._is_paragraph
@property
def is_table(self):
return self._is_table
@property
def string(self):
return self.obj.String
@property
def text(self):
return self.obj.getText()
@property
def cursor(self):
return self.text.createTextCursorByRange(self.obj)
class LOBase(object):
TYPES = {
str: 'setString',
int: 'setInt',
float: 'setFloat',
bool: 'setBoolean',
Date: 'setDate',
Time: 'setTime',
DateTime: 'setTimestamp',
}
# ~ setArray
# ~ setBinaryStream
# ~ setBlob
# ~ setByte
# ~ setBytes
# ~ setCharacterStream
# ~ setClob
# ~ setNull
# ~ setObject
# ~ setObjectNull
# ~ setObjectWithInfo
# ~ setPropertyValue
# ~ setRef
def __init__(self, name, path='', **kwargs):
self._name = name
self._path = path
self._dbc = create_instance('com.sun.star.sdb.DatabaseContext')
if path:
path_url = _path_url(path)
db = self._dbc.createInstance()
db.URL = 'sdbc:embedded:firebird'
db.DatabaseDocument.storeAsURL(path_url, ())
if not self.exists:
self._dbc.registerDatabaseLocation(name, path_url)
else:
if name.startswith('odbc:'):
self._con = self._odbc(name, kwargs)
else:
db = self._dbc.getByName(name)
self.path = _path_system(self._dbc.getDatabaseLocation(name))
self._con = db.getConnection('', '')
if self._con is None:
msg = 'Not connected to: {}'.format(name)
else:
msg = 'Connected to: {}'.format(name)
debug(msg)
def _odbc(self, name, kwargs):
dm = create_instance('com.sun.star.sdbc.DriverManager')
args = dict_to_property(kwargs)
try:
con = dm.getConnectionWithInfo('sdbc:{}'.format(name), args)
return con
except Exception as e:
error(str(e))
return None
@property
def obj(self):
return self._obj
@property
def name(self):
return self._name
@property
def connection(self):
return self._con
@property
def path(self):
return self._path
@path.setter
def path(self, value):
self._path = value
@property
def exists(self):
return self._dbc.hasRegisteredDatabase(self.name)
@classmethod
def register(self, path, name):
if not self._dbc.hasRegisteredDatabase(name):
self._dbc.registerDatabaseLocation(name, _path_url(path))
return
def revoke(self, name):
self._dbc.revokeDatabaseLocation(name)
return True
def save(self):
# ~ self._db.connection.commit()
# ~ self._db.connection.getTables().refresh()
# ~ oDisp.executeDispatch(oFrame,".uno:DBRefreshTables", "", 0, Array())
self._obj.DatabaseDocument.store()
self.refresh()
return
def close(self):
self._con.close()
return
def refresh(self):
self._con.getTables().refresh()
return
def get_tables(self):
tables = self._con.getTables()
tables = [tables.getByIndex(i) for i in range(tables.Count)]
return tables
def cursor(self, sql, params):
cursor = self._con.prepareStatement(sql)
for i, v in enumerate(params, 1):
if not type(v) in self.TYPES:
error('Type not support')
debug((i, type(v), v, self.TYPES[type(v)]))
getattr(cursor, self.TYPES[type(v)])(i, v)
return cursor
def execute(self, sql, params):
debug(sql)
if params:
cursor = self.cursor(sql, params)
cursor.execute()
else:
cursor = self._con.createStatement()
cursor.execute(sql)
# ~ resulset = cursor.executeQuery(sql)
# ~ rows = cursor.executeUpdate(sql)
self.save()
return cursor
class LODrawImpress(LODocument):
def __init__(self, obj):
super().__init__(obj)
@property
def draw_page(self):
return self._cc.getCurrentPage()
def insert_image(self, path, **kwargs):
w = kwargs.get('width', 3000)
h = kwargs.get('Height', 3000)
x = kwargs.get('X', 1000)
y = kwargs.get('Y', 1000)
image = self.create_instance('com.sun.star.drawing.GraphicObjectShape')
image.GraphicURL = _path_url(path)
image.Size = Size(w, h)
image.Position = Point(x, y)
self.draw_page.add(image)
return
class LOImpress(LODrawImpress):
def __init__(self, obj):
super().__init__(obj)
class LODraw(LODrawImpress):
def __init__(self, obj):
super().__init__(obj)
class LOMath(LODocument):
def __init__(self, obj):
super().__init__(obj)
class LOBasicIde(LODocument):
def __init__(self, obj):
super().__init__(obj)
@property
def selection(self):
sel = self._cc.getSelection()
return sel
class LOCellRange(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
self._init_values()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __getitem__(self, index):
return LOCellRange(self.obj[index], self.doc)
def __contains__(self, item):
return item.in_range(self)
def _init_values(self):
self._type_obj = self.obj.ImplementationName
self._type_content = EMPTY
if self._type_obj == OBJ_CELL:
self._type_content = self.obj.getType()
return
@property
def obj(self):
return self._obj
@property
def doc(self):
return self._doc
@property
def type(self):
return self._type_obj
@property
def type_content(self):
return self._type_content
@property
def first(self):
if self.type == OBJ_RANGES:
obj = LOCellRange(self.obj[0][0,0], self.doc)
else:
obj = LOCellRange(self.obj[0,0], self.doc)
return obj
@property
def value(self):
v = None
if self._type_content == VALUE:
v = self.obj.getValue()
elif self._type_content == TEXT:
v = self.obj.getString()
elif self._type_content == FORMULA:
v = self.obj.getFormula()
return v
@value.setter
def value(self, data):
if isinstance(data, str):
if data.startswith('='):
self.obj.setFormula(data)
else:
self.obj.setString(data)
elif isinstance(data, (int, float, bool)):
self.obj.setValue(data)
elif isinstance(data, datetime.datetime):
d = data.toordinal()
t = (data - datetime.datetime.fromordinal(d)).seconds / SECONDS_DAY
self.obj.setValue(d - DATE_OFFSET + t)
elif isinstance(data, datetime.date):
d = data.toordinal()
self.obj.setValue(d - DATE_OFFSET)
elif isinstance(data, datetime.time):
d = (data.hour * 3600 + data.minute * 60 + data.second) / SECONDS_DAY
self.obj.setValue(d)
@property
def data(self):
return self.obj.getDataArray()
@data.setter
def data(self, values):
self.obj.setDataArray(values)
@property
def formula(self):
return self.obj.getFormulaArray()
@formula.setter
def formula(self, values):
self.obj.setFormulaArray(values)
@property
def column(self):
a = self.address
if hasattr(a, 'Column'):
c = a.Column
else:
c = a.StartColumn
return c
@property
def columns(self):
return self._obj.Columns.Count
@property
def row(self):
a = self.address
if hasattr(a, 'Row'):
r = a.Row
else:
r = a.StartRow
return r
@property
def rows(self):
return self._obj.Rows.Count
def to_size(self, rows, cols):
cursor = self.sheet.get_cursor(self.obj[0,0])
cursor.collapseToSize(cols, rows)
return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc)
def copy_from(self, rango, formula=False):
data = rango
if isinstance(rango, LOCellRange):
if formula:
data = rango.formula
else:
data = rango.data
rows = len(data)
cols = len(data[0])
if formula:
self.to_size(rows, cols).formula = data
else:
self.to_size(rows, cols).data = data
return
def copy_to(self, cell, formula=False):
rango = cell.to_size(self.rows, self.columns)
if formula:
rango.formula = self.data
else:
rango.data = self.data
return
def copy(self, source):
self.sheet.obj.copyRange(self.address, source.range_address)
return
def transpose(self, formula=False):
data = self.data
if formula:
data = self.formula
data = tuple(zip(*data))
self.clear(1023)
self[0,0].copy_from(data, formula=formula)
return
def transpose2(self):
# ~ 'Flags': 'A',
# ~ 'FormulaCommand': 0,
# ~ 'SkipEmptyCells': False,
# ~ 'AsLink': False,
# ~ 'MoveMode': 4,
args = {
'Transpose': True,
}
args = dict_to_property(args)
self.select()
copy()
self.clear(1023)
self[0,0].select()
call_dispatch('.uno:InsertContents', args)
set_clipboard('')
return
def offset(self, row=1, col=0):
ra = self.obj.getRangeAddress()
col = ra.EndColumn + col
row = ra.EndRow + row
return LOCellRange(self.sheet[row, col].obj, self.doc)
@property
def next_cell(self):
a = self.current_region.address
if hasattr(a, 'StartColumn'):
col = a.StartColumn
else:
col = a.Column
if hasattr(a, 'EndRow'):
row = a.EndRow + 1
else:
row = a.Row + 1
return LOCellRange(self.sheet[row, col].obj, self.doc)
@property
def sheet(self):
return LOCalcSheet(self.obj.Spreadsheet, self.doc)
@property
def charts(self):
return self.obj.Spreadsheet.Charts
@property
def ps(self):
ps = Rectangle()
s = self.obj.Size
p = self.obj.Position
ps.X = p.X
ps.Y = p.Y
ps.Width = s.Width
ps.Height = s.Height
return ps
@property
def draw_page(self):
return self.sheet.obj.getDrawPage()
@property
def name(self):
return self.obj.AbsoluteName
@property
def address(self):
if self._type_obj == OBJ_CELL:
a = self.obj.getCellAddress()
elif self._type_obj == OBJ_RANGE:
a = self.obj.getRangeAddress()
else:
a = self.obj.getRangeAddressesAsString()
return a
@property
def range_address(self):
return self.obj.getRangeAddress()
@property
def current_region(self):
cursor = self.sheet.get_cursor(self.obj[0,0])
cursor.collapseToCurrentRegion()
return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc)
@property
def visible(self):
cursor = self.sheet.get_cursor(self.obj)
rangos = [LOCellRange(self.sheet[r.AbsoluteName].obj, self.doc)
for r in cursor.queryVisibleCells()]
return tuple(rangos)
@property
def empty(self):
cursor = self.sheet.get_cursor(self.obj)
rangos = [LOCellRange(self.sheet[r.AbsoluteName].obj, self.doc)
for r in cursor.queryEmptyCells()]
return tuple(rangos)
@property
def back_color(self):
return self._obj.CellBackColor
@back_color.setter
def back_color(self, value):
self._obj.CellBackColor = get_color(value)
@property
def cell_style(self):
return self.obj.CellStyle
@cell_style.setter
def cell_style(self, value):
self.obj.CellStyle = value
@property
def auto_format(self):
return self.obj.CellStyle
@auto_format.setter
def auto_format(self, value):
self.obj.autoFormat(value)
def auto_width(self):
self.obj.Columns.OptimalWidth = True
return
def insert_image(self, path, **kwargs):
s = self.obj.Size
w = kwargs.get('width', s.Width)
h = kwargs.get('Height', s.Height)
img = self.doc.create_instance('com.sun.star.drawing.GraphicObjectShape')
img.GraphicURL = _path_url(path)
self.draw_page.add(img)
img.Anchor = self.obj
img.setSize(Size(w, h))
return
def insert_shape(self, tipo, **kwargs):
s = self.obj.Size
w = kwargs.get('width', s.Width)
h = kwargs.get('Height', s.Height)
img = self.doc.create_instance('com.sun.star.drawing.{}Shape'.format(tipo))
set_properties(img, kwargs)
self.draw_page.add(img)
img.Anchor = self.obj
img.setSize(Size(w, h))
return
def select(self):
self.doc._cc.select(self.obj)
return
def in_range(self, rango):
if isinstance(rango, LOCellRange):
address = rango.address
else:
address = rango.getRangeAddress()
cursor = self.sheet.get_cursor(self.obj)
result = cursor.queryIntersection(address)
return bool(result.Count)
def fill(self, source=1):
self.obj.fillAuto(0, source)
return
def clear(self, what=31):
# ~ http://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
self.obj.clearContents(what)
return
@property
def rows_visible(self):
return self._obj.getRows().IsVisible
@rows_visible.setter
def rows_visible(self, value):
self._obj.getRows().IsVisible = value
@property
def columns_visible(self):
return self._obj.getColumns().IsVisible
@columns_visible.setter
def columns_visible(self, value):
self._obj.getColumns().IsVisible = value
def get_column(self, index=0, first=False):
ca = self.address
ra = self.current_region.address
if hasattr(ca, 'Column'):
col = ca.Column
else:
col = ca.StartColumn + index
start = 1
if first:
start = 0
if hasattr(ra, 'Row'):
row_start = ra.Row + start
row_end = ra.Row + 1
else:
row_start = ra.StartRow + start
row_end = ra.EndRow + 1
return LOCellRange(self.sheet[row_start:row_end, col:col+1].obj, self.doc)
def import_csv(self, path, **kwargs):
data = import_csv(path, **kwargs)
self.copy_from(data)
return
def export_csv(self, path, **kwargs):
data = self.current_region.data
export_csv(path, data, **kwargs)
return
def create_chart(self, tipo):
chart = LOChart(None, tipo)
chart.cell = self
return chart
def search(self, options):
descriptor = self.obj.Spreadsheet.createSearchDescriptor()
descriptor.setSearchString(options.get('Search', ''))
descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
descriptor.SearchWords = options.get('Words', False)
if hasattr(descriptor, 'SearchRegularExpression'):
descriptor.SearchRegularExpression = options.get('RegularExpression', False)
if hasattr(descriptor, 'SearchType') and 'Type' in options:
descriptor.SearchType = options['Type']
if options.get('First', False):
found = self.obj.findFirst(descriptor)
else:
found = self.obj.findAll(descriptor)
return found
def replace(self, options):
descriptor = self.obj.Spreadsheet.createReplaceDescriptor()
descriptor.setSearchString(options['Search'])
descriptor.setReplaceString(options['Replace'])
descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
descriptor.SearchWords = options.get('Words', False)
if hasattr(descriptor, 'SearchRegularExpression'):
descriptor.SearchRegularExpression = options.get('RegularExpression', False)
if hasattr(descriptor, 'SearchType') and 'Type' in options:
descriptor.SearchType = options['Type']
found = self.obj.replaceAll(descriptor)
return found
@property
def validation(self):
return self.obj.Validation
@validation.setter
def validation(self, values):
is_list = False
current = self.validation
for k, v in values.items():
if k == 'Type' and v == 6:
is_list = True
if k == 'Formula1' and is_list:
if isinstance(v, (tuple, list)):
v = ';'.join(['"{}"'.format(i) for i in v])
setattr(current, k, v)
self.obj.Validation = current
class EventsListenerBase(unohelper.Base, XEventListener):
def __init__(self, controller, name, window=None):
self._controller = controller
self._name = name
self._window = window
@property
def name(self):
return self._name
def disposing(self, event):
self._controller = None
if not self._window is None:
self._window.setMenuBar(None)
class EventsButton(EventsListenerBase, XActionListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def actionPerformed(self, event):
event_name = '{}_action'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsMouse(EventsListenerBase, XMouseListener, XMouseMotionListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def mousePressed(self, event):
event_name = '{}_click'.format(self._name)
if event.ClickCount == 2:
event_name = '{}_double_click'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def mouseReleased(self, event):
pass
def mouseEntered(self, event):
pass
def mouseExited(self, event):
pass
# ~ XMouseMotionListener
def mouseMoved(self, event):
pass
def mouseDragged(self, event):
pass
class EventsMouseLink(EventsMouse):
def mouseEntered(self, event):
obj = event.Source.Model
obj.TextColor = get_color('blue')
return
def mouseExited(self, event):
obj = event.Source.Model
obj.TextColor = 0
return
class EventsMouseGrid(EventsMouse):
selected = False
def mousePressed(self, event):
super().mousePressed(event)
# ~ obj = event.Source
# ~ col = obj.getColumnAtPoint(event.X, event.Y)
# ~ row = obj.getRowAtPoint(event.X, event.Y)
# ~ print(col, row)
# ~ if col == -1 and row == -1:
# ~ if self.selected:
# ~ obj.deselectAllRows()
# ~ else:
# ~ obj.selectAllRows()
# ~ self.selected = not self.selected
return
def mouseReleased(self, event):
# ~ obj = event.Source
# ~ col = obj.getColumnAtPoint(event.X, event.Y)
# ~ row = obj.getRowAtPoint(event.X, event.Y)
# ~ if row == -1 and col > -1:
# ~ gdm = obj.Model.GridDataModel
# ~ for i in range(gdm.RowCount):
# ~ gdm.updateRowHeading(i, i + 1)
return
class EventsModify(EventsListenerBase, XModifyListener):
def __init__(self, controller):
super().__init__(controller)
def modified(self, event):
event_name = '{}_modified'.format(event.Source.Name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsItem(EventsListenerBase, XItemListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def disposing(self, event):
pass
def itemStateChanged(self, event):
event_name = '{}_item_changed'.format(self.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsItemRoadmap(EventsItem):
def itemStateChanged(self, event):
dialog = event.Source.Context.Model
dialog.Step = event.ItemId + 1
return
class EventsFocus(EventsListenerBase, XFocusListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def focusGained(self, event):
service = event.Source.Model.ImplementationName
if service == 'stardiv.Toolkit.UnoControlListBoxModel':
return
obj = event.Source.Model
obj.BackgroundColor = COLOR_ON_FOCUS
def focusLost(self, event):
obj = event.Source.Model
obj.BackgroundColor = -1
class EventsKey(EventsListenerBase, XKeyListener):
"""
event.KeyChar
event.KeyCode
event.KeyFunc
event.Modifiers
"""
def __init__(self, controller, name):
super().__init__(controller, name)
def keyPressed(self, event):
pass
def keyReleased(self, event):
event_name = '{}_key_released'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsTab(EventsListenerBase, XTabListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def activated(self, id):
event_name = '{}_activated'.format(self.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(id)
return
class EventsGrid(EventsListenerBase, XGridDataListener, XGridSelectionListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def dataChanged(self, event):
event_name = '{}_data_changed'.format(self.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def rowHeadingChanged(self, event):
pass
def rowsInserted(self, event):
pass
def rowsRemoved(self, evemt):
pass
def selectionChanged(self, event):
event_name = '{}_selection_changed'.format(self.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsKeyWindow(EventsListenerBase, XKeyListener):
"""
event.KeyChar
event.KeyCode
event.KeyFunc
event.Modifiers
"""
def __init__(self, cls):
super().__init__(cls.events, cls.name)
self._cls = cls
def keyPressed(self, event):
pass
def keyReleased(self, event):
event_name = '{}_key_released'.format(self._cls.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
else:
if event.KeyFunc == QUIT and hasattr(self._cls, 'close'):
self._cls.close()
return
class EventsWindow(EventsListenerBase, XTopWindowListener, XWindowListener):
def __init__(self, cls):
self._cls = cls
super().__init__(cls.events, cls.name, cls._window)
def windowOpened(self, event):
event_name = '{}_opened'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def windowActivated(self, event):
control_name = '{}_activated'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
def windowDeactivated(self, event):
control_name = '{}_deactivated'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
def windowMinimized(self, event):
pass
def windowNormalized(self, event):
pass
def windowClosing(self, event):
if self._window:
control_name = 'window_closing'
else:
control_name = '{}_closing'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
# ~ else:
# ~ if not self._modal and not self._block:
# ~ event.Source.Visible = False
return
def windowClosed(self, event):
control_name = '{}_closed'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
# ~ XWindowListener
def windowResized(self, event):
sb = self._cls._subcont
sb.setPosSize(0, 0, event.Width, event.Height, SIZE)
event_name = '{}_resized'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def windowMoved(self, event):
pass
def windowShown(self, event):
pass
def windowHidden(self, event):
pass
class EventsMenu(EventsListenerBase, XMenuListener):
def __init__(self, controller):
super().__init__(controller, '')
def itemHighlighted(self, event):
pass
def itemSelected(self, event):
name = event.Source.getCommand(event.MenuId)
if name.startswith('menu'):
event_name = '{}_selected'.format(name)
else:
event_name = 'menu_{}_selected'.format(name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def itemActivated(self, event):
return
def itemDeactivated(self, event):
return
class UnoBaseObject(object):
def __init__(self, obj):
self._obj = obj
self._model = self.obj.Model
self._rules = {}
@property
def obj(self):
return self._obj
@property
def model(self):
return self._model
@property
def name(self):
return self.model.Name
@property
def parent(self):
ps = self.obj.getContext().PosSize
return self.obj.getContext()
def _get_possize(self, name):
ps = self.obj.getPosSize()
return getattr(ps, name)
def _set_possize(self, name, value):
ps = self.obj.getPosSize()
setattr(ps, name, value)
self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
return
@property
def x(self):
if hasattr(self.model, 'PositionX'):
return self.model.PositionX
return self._get_possize('X')
@x.setter
def x(self, value):
if hasattr(self.model, 'PositionX'):
self.model.PositionX = value
else:
self._set_possize('X', value)
@property
def y(self):
if hasattr(self.model, 'PositionY'):
return self.model.PositionY
return self._get_possize('Y')
@y.setter
def y(self, value):
if hasattr(self.model, 'PositionY'):
self.model.PositionY = value
else:
self._set_possize('Y', value)
@property
def width(self):
return self._model.Width
@width.setter
def width(self, value):
self.model.Width = value
@property
def ps_width(self):
return self._get_possize('Width')
@ps_width.setter
def ps_width(self, value):
self._set_possize('Width', value)
@property
def height(self):
return self.model.Height
@height.setter
def height(self, value):
self.model.Height = value
@property
def ps_height(self):
return self._get_possize('Height')
@ps_height.setter
def ps_height(self, value):
self._set_possize('Height', value)
@property
def size(self):
ps = self.obj.getPosSize()
return (ps.Width, ps.Height)
@size.setter
def size(self, value):
ps = self.obj.getPosSize()
ps.Width = value[0]
ps.Height = value[1]
self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, SIZE)
@property
def tag(self):
return self.model.Tag
@tag.setter
def tag(self, value):
self.model.Tag = value
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self.obj.setVisible(value)
@property
def enabled(self):
return self.model.Enabled
@enabled.setter
def enabled(self, value):
self.model.Enabled = value
@property
def step(self):
return self.model.Step
@step.setter
def step(self, value):
self.model.Step = value
@property
def back_color(self):
return self.model.BackgroundColor
@back_color.setter
def back_color(self, value):
self.model.BackgroundColor = value
@property
def rules(self):
return self._rules
@rules.setter
def rules(self, value):
self._rules = value
def set_focus(self):
self.obj.setFocus()
return
def center(self, horizontal=True, vertical=False):
p = self.parent.Model
w = p.Width
h = p.Height
if horizontal:
x = w / 2 - self.width / 2
self.x = x
if vertical:
y = h / 2 - self.height / 2
self.y = y
return
def move(self, origin, x=0, y=5):
if x:
self.x = origin.x + origin.width + x
else:
self.x = origin.x
if y:
self.y = origin.y + origin.height + y
else:
self.y = origin.y
return
def possize(self, origin):
self.x = origin.x
self.y = origin.y
self.width = origin.width
self.height = origin.height
return
class UnoLabel(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'label'
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
class UnoLabelLink(UnoLabel):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'link'
class UnoButton(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'button'
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
class UnoText(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'text'
@property
def value(self):
return self.model.Text
@value.setter
def value(self, value):
self.model.Text = value
def validate(self):
return
class UnoListBox(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'listbox'
@property
def value(self):
return self.obj.getSelectedItem()
@property
def count(self):
return len(self.data)
@property
def data(self):
return self.model.StringItemList
@data.setter
def data(self, values):
self.model.StringItemList = list(sorted(values))
return
def unselect(self):
self.obj.selectItem(self.value, False)
return
def select(self, pos=0):
if isinstance(pos, str):
self.obj.selectItem(pos, True)
else:
self.obj.selectItemPos(pos, True)
return
def clear(self):
self.model.removeAllItems()
return
def _set_image_url(self, image):
if exists_path(image):
return _path_url(image)
if not ID_EXTENSION:
return ''
path = get_path_extension(ID_EXTENSION)
path = join(path, DIR['images'], image)
return _path_url(path)
def insert(self, value, path='', pos=-1, show=True):
if pos < 0:
pos = self.count
if path:
self.model.insertItem(pos, value, self._set_image_url(path))
else:
self.model.insertItemText(pos, value)
if show:
self.select(pos)
return
class UnoGrid(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._gdm = self._model.GridDataModel
# ~ self._data = []
self._columns = {}
# ~ self._format_columns = ()
def __getitem__(self, index):
value = self._gdm.getCellData(index[0], index[1])
return value
@property
def type(self):
return 'grid'
def _format_cols(self):
rows = tuple(tuple(
self._format_columns[i].format(r) for i, r in enumerate(row)) for row in self._data
)
return rows
# ~ @property
# ~ def format_columns(self):
# ~ return self._format_columns
# ~ @format_columns.setter
# ~ def format_columns(self, value):
# ~ self._format_columns = value
@property
def value(self):
return self[self.column, self.row]
@property
def data(self):
return self._data
@data.setter
def data(self, values):
# ~ self._data = values
self.clear()
headings = tuple(range(1, len(values) + 1))
self._gdm.addRows(headings, values)
# ~ rows = range(grid_dm.RowCount)
# ~ colors = [COLORS['GRAY'] if r % 2 else COLORS['WHITE'] for r in rows]
# ~ grid.Model.RowBackgroundColors = tuple(colors)
return
@property
def row(self):
return self.obj.CurrentRow
@property
def rows(self):
return self._gdm.RowCount
@property
def column(self):
return self.obj.CurrentColumn
@property
def columns(self):
return self._gdm.ColumnCount
def set_cell_tooltip(self, col, row, value):
self._gdm.updateCellToolTip(col, row, value)
return
def get_cell_tooltip(self, col, row):
value = self._gdm.getCellToolTip(col, row)
return value
def _validate_column(self, data):
row = []
for i, d in enumerate(data):
if i in self._columns:
if 'image' in self._columns[i]:
row.append(self._columns[i]['image'])
else:
row.append(d)
return tuple(row)
def clear(self):
self._gdm.removeAllRows()
return
def add_row(self, data):
# ~ self._data.append(data)
data = self._validate_column(data)
self._gdm.addRow(self.rows + 1, data)
return
def remove_row(self, row):
self._gdm.removeRow(row)
# ~ del self._data[row]
self.update_row_heading()
return
def update_row_heading(self):
for i in range(self.rows):
self._gdm.updateRowHeading(i, i + 1)
return
def sort(self, column, asc=True):
self._gdm.sortByColumn(column, asc)
self.update_row_heading()
return
def set_column_image(self, column, path):
gp = create_instance('com.sun.star.graphic.GraphicProvider')
data = dict_to_property({'URL': _path_url(path)})
image = gp.queryGraphic(data)
if not column in self._columns:
self._columns[column] = {}
self._columns[column]['image'] = image
return
class UnoRoadmap(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._options = ()
@property
def options(self):
return self._options
@options.setter
def options(self, values):
self._options = values
for i, v in enumerate(values):
opt = self.model.createInstance()
opt.ID = i
opt.Label = v
self.model.insertByIndex(i, opt)
return
@property
def enabled(self):
return True
@enabled.setter
def enabled(self, value):
for m in self.model:
m.Enabled = value
return
def set_enabled(self, index, value):
self.model.getByIndex(index).Enabled = value
return
class UnoTree(UnoBaseObject):
def __init__(self, obj, ):
super().__init__(obj)
self._tdm = None
self._data = []
@property
def selection(self):
return self.obj.Selection
@property
def root(self):
if self._tdm is None:
return ''
return self._tdm.Root.DisplayValue
@root.setter
def root(self, value):
self._add_data_model(value)
def _add_data_model(self, name):
tdm = create_instance('com.sun.star.awt.tree.MutableTreeDataModel')
root = tdm.createNode(name, True)
root.DataValue = 0
tdm.setRoot(root)
self.model.DataModel = tdm
self._tdm = self.model.DataModel
self._add_data()
return
@property
def data(self):
return self._data
@data.setter
def data(self, values):
self._data = list(values)
self._add_data()
def _add_data(self):
if not self.data:
return
parents = {}
for node in self.data:
parent = parents.get(node[1], self._tdm.Root)
child = self._tdm.createNode(node[2], False)
child.DataValue = node[0]
parent.appendChild(child)
parents[node[0]] = child
self.obj.expandNode(self._tdm.Root)
return
class UnoTab(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._events = None
def __getitem__(self, index):
return self.get_sheet(index)
@property
def current(self):
return self.obj.getActiveTabID()
@property
def active(self):
return self.current
def get_sheet(self, id):
if isinstance(id, int):
sheet = self.obj.Controls[id-1]
else:
sheet = self.obj.getControl(id.lower())
return sheet
@property
def sheets(self):
return self._sheets
@sheets.setter
def sheets(self, values):
i = len(self.obj.Controls)
for title in values:
i += 1
sheet = self.model.createInstance('com.sun.star.awt.UnoPageModel')
sheet.Title = title
self.model.insertByName('sheet{}'.format(i), sheet)
return
def insert(self, title):
id = len(self.obj.Controls) + 1
sheet = self.model.createInstance('com.sun.star.awt.UnoPageModel')
sheet.Title = title
self.model.insertByName('sheet{}'.format(id), sheet)
return id
def remove(self, id):
sheet = self.get_sheet(id)
for control in sheet.getControls():
sheet.Model.removeByName(control.Model.Name)
sheet.removeControl(control)
# ~ self._model.removeByName('page_{}'.format(ID))
self.obj.removeTab(id)
return
def activate(self, id):
self.obj.activateTab(id)
return
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers
def _special_properties(self, tipo, properties):
columns = properties.pop('Columns', ())
if tipo == 'grid':
properties['ColumnModel'] = _set_column_model(columns)
if not 'Width' in properties:
properties['Width'] = self.width
if not 'Height' in properties:
properties['Height'] = self.height
elif tipo == 'button' and 'ImageURL' in properties:
properties['ImageURL'] = self._set_image_url(properties['ImageURL'])
elif tipo == 'roadmap':
if not 'Height' in properties:
properties['Height'] = self.height
if 'Title' in properties:
properties['Text'] = properties.pop('Title')
elif tipo == 'pages':
if not 'Width' in properties:
properties['Width'] = self.width
if not 'Height' in properties:
properties['Height'] = self.height
return properties
def add_control(self, id, properties):
tipo = properties.pop('Type').lower()
root = properties.pop('Root', '')
sheets = properties.pop('Sheets', ())
properties = self._special_properties(tipo, properties)
sheet = self.get_sheet(id)
sheet_model = sheet.getModel()
model = sheet_model.createInstance(get_control_model(tipo))
set_properties(model, properties)
name = properties['Name']
sheet_model.insertByName(name, model)
control = sheet.getControl(name)
add_listeners(self.events, control, name)
control = get_custom_class(tipo, control)
if tipo == 'tree' and root:
control.root = root
elif tipo == 'pages' and sheets:
control.sheets = sheets
setattr(self, name, control)
return
def get_custom_class(tipo, obj):
classes = {
'label': UnoLabel,
'button': UnoButton,
'text': UnoText,
'listbox': UnoListBox,
'grid': UnoGrid,
'link': UnoLabelLink,
'roadmap': UnoRoadmap,
'tree': UnoTree,
'tab': UnoTab,
# ~ 'image': UnoImage,
# ~ 'radio': UnoRadio,
# ~ 'groupbox': UnoGroupBox,
'formbutton': FormButton,
}
return classes[tipo](obj)
def get_control_model(control):
services = {
'label': 'com.sun.star.awt.UnoControlFixedTextModel',
'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
'text': 'com.sun.star.awt.UnoControlEditModel',
'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
'button': 'com.sun.star.awt.UnoControlButtonModel',
'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
'tree': 'com.sun.star.awt.tree.TreeControlModel',
'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
'image': 'com.sun.star.awt.UnoControlImageControlModel',
'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
'tab': 'com.sun.star.awt.UnoMultiPageModel',
}
return services[control]
def add_listeners(events, control, name=''):
listeners = {
'addActionListener': EventsButton,
'addMouseListener': EventsMouse,
'addItemListener': EventsItem,
'addFocusListener': EventsFocus,
'addKeyListener': EventsKey,
'addTabListener': EventsTab,
}
if hasattr(control, 'obj'):
control = contro.obj
# ~ debug(control.ImplementationName)
is_grid = control.ImplementationName == 'stardiv.Toolkit.GridControl'
is_link = control.ImplementationName == 'stardiv.Toolkit.UnoFixedHyperlinkControl'
is_roadmap = control.ImplementationName == 'stardiv.Toolkit.UnoRoadmapControl'
for key, value in listeners.items():
if hasattr(control, key):
if is_grid and key == 'addMouseListener':
control.addMouseListener(EventsMouseGrid(events, name))
continue
if is_link and key == 'addMouseListener':
control.addMouseListener(EventsMouseLink(events, name))
continue
if is_roadmap and key == 'addItemListener':
control.addItemListener(EventsItemRoadmap(events, name))
continue
getattr(control, key)(listeners[key](events, name))
if is_grid:
controllers = EventsGrid(events, name)
control.addSelectionListener(controllers)
control.Model.GridDataModel.addGridDataListener(controllers)
return
class WriterTable(ObjectBase):
def __init__(self, obj):
super().__init__(obj)
def __getitem__(self, key):
obj = super().__getitem__(key)
return WriterTableRange(obj, key, self.name)
@property
def name(self):
return self.obj.Name
@name.setter
def name(self, value):
self.obj.Name = value
class WriterTableRange(ObjectBase):
def __init__(self, obj, index, table_name):
self._index = index
self._table_name = table_name
super().__init__(obj)
self._is_cell = hasattr(self.obj, 'CellName')
def __getitem__(self, key):
obj = super().__getitem__(key)
return WriterTableRange(obj, key, self._table_name)
@property
def value(self):
return self.obj.String
@value.setter
def value(self, value):
self.obj.String = value
@property
def data(self):
return self.obj.getDataArray()
@data.setter
def data(self, values):
if isinstance(values, list):
values = tuple(values)
self.obj.setDataArray(values)
@property
def rows(self):
return len(self.data)
@property
def columns(self):
return len(self.data[0])
@property
def name(self):
if self._is_cell:
name = '{}.{}'.format(self._table_name, self.obj.CellName)
elif isinstance(self._index, str):
name = '{}.{}'.format(self._table_name, self._index)
else:
c1 = self.obj[0,0].CellName
c2 = self.obj[self.rows-1,self.columns-1].CellName
name = '{}.{}:{}'.format(self._table_name, c1, c2)
return name
def get_cell(self, *index):
return self[index]
def get_column(self, index=0, start=1):
return self[start:self.rows,index:index+1]
def get_series(self):
class Serie():
pass
series = []
for i in range(self.columns):
serie = Serie()
serie.label = self.get_cell(0,i).name
serie.data = self.get_column(i).data
serie.values = self.get_column(i).name
series.append(serie)
return series
class ChartFormat(object):
def __call__(self, obj):
for k, v in self.__dict__.items():
if hasattr(obj, k):
setattr(obj, k, v)
class LOChart(object):
BASE = 'com.sun.star.chart.{}Diagram'
def __init__(self, obj, tipo=''):
self._obj = obj
self._type = tipo
self._name = ''
self._table = None
self._data = ()
self._data_series = ()
self._cell = None
self._cursor = None
self._doc = None
self._title = ChartFormat()
self._subtitle = ChartFormat()
self._legend = ChartFormat()
self._xaxistitle = ChartFormat()
self._yaxistitle = ChartFormat()
self._xaxis = ChartFormat()
self._yaxis = ChartFormat()
self._xmaingrid = ChartFormat()
self._ymaingrid = ChartFormat()
self._xhelpgrid = ChartFormat()
self._yhelpgrid = ChartFormat()
self._area = ChartFormat()
self._wall = ChartFormat()
self._dim3d = False
self._series = ()
self._labels = ()
return
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.insert()
@property
def obj(self):
return self._obj
@obj.setter
def obj(self, value):
self._obj = value
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def type(self):
return self._type
@type.setter
def type(self, value):
self._type = value
@property
def table(self):
return self._table
@table.setter
def table(self, value):
self._table = value
@property
def data(self):
return self._data
@data.setter
def data(self, value):
self._data = value
@property
def cell(self):
return self._cell
@cell.setter
def cell(self, value):
self._cell = value
self.doc = value.doc
@property
def cursor(self):
return self._cursor
@cursor.setter
def cursor(self, value):
self._cursor = value
@property
def doc(self):
return self._doc
@doc.setter
def doc(self, value):
self._doc = value
@property
def width(self):
return self._width
@width.setter
def width(self, value):
self._width = value
@property
def height(self):
return self._height
@height.setter
def height(self, value):
self._height = value
@property
def title(self):
return self._title
@property
def subtitle(self):
return self._subtitle
@property
def legend(self):
return self._legend
@property
def xaxistitle(self):
return self._xaxistitle
@property
def yaxistitle(self):
return self._yaxistitle
@property
def xaxis(self):
return self._xaxis
@property
def yaxis(self):
return self._yaxis
@property
def xmaingrid(self):
return self._xmaingrid
@property
def ymaingrid(self):
return self._ymaingrid
@property
def xhelpgrid(self):
return self._xhelpgrid
@property
def yhelpgrid(self):
return self._yhelpgrid
@property
def area(self):
return self._area
@property
def wall(self):
return self._wall
@property
def dim3d(self):
return self._dim3d
@dim3d.setter
def dim3d(self, value):
self._dim3d = value
@property
def series(self):
return self._series
@series.setter
def series(self, value):
self._series = value
@property
def data_series(self):
return self._series
@data_series.setter
def data_series(self, value):
self._data_series = value
@property
def labels(self):
return self._labels
@labels.setter
def labels(self, value):
self._labels = value
def _add_series_writer(self, chart):
dp = self.doc.create_instance('com.sun.star.chart2.data.DataProvider')
chart.attachDataProvider(dp)
chart_type = chart.getFirstDiagram().getCoordinateSystems()[0].getChartTypes()[0]
self._data_series = self.table[self.data].get_series()
series = [self._create_serie(dp, s) for s in self._data_series[1:]]
chart_type.setDataSeries(tuple(series))
chart_data = chart.getData()
chart_data.ComplexRowDescriptions = self._data_series[0].data
return
def _get_series(self):
rango = self._data_series
class Serie():
pass
series = []
for i in range(0, rango.columns, 2):
serie = Serie()
serie.label = rango[0, i+1].name
serie.xvalues = rango.get_column(i).name
serie.values = rango.get_column(i+1).name
series.append(serie)
return series
def _add_series_calc(self, chart):
dp = self.doc.create_instance('com.sun.star.chart2.data.DataProvider')
chart.attachDataProvider(dp)
chart_type = chart.getFirstDiagram().getCoordinateSystems()[0].getChartTypes()[0]
series = self._get_series()
series = [self._create_serie(dp, s) for s in series]
chart_type.setDataSeries(tuple(series))
return
def _create_serie(self, dp, data):
serie = create_instance('com.sun.star.chart2.DataSeries')
rango = data.values
is_x = hasattr(data, 'xvalues')
if is_x:
xrango = data.xvalues
rango_label = data.label
lds = create_instance('com.sun.star.chart2.data.LabeledDataSequence')
values = self._create_data(dp, rango, 'values-y')
lds.setValues(values)
if data.label:
label = self._create_data(dp, rango_label, '')
lds.setLabel(label)
xlds = ()
if is_x:
xlds = create_instance('com.sun.star.chart2.data.LabeledDataSequence')
values = self._create_data(dp, xrango, 'values-x')
xlds.setValues(values)
if is_x:
serie.setData((lds, xlds))
else:
serie.setData((lds,))
return serie
def _create_data(self, dp, rango, role):
data = dp.createDataSequenceByRangeRepresentation(rango)
if not data is None:
data.Role = role
return data
def _from_calc(self):
ps = self.cell.ps
ps.Width = self.width
ps.Height = self.height
charts = self.cell.charts
data = ()
if self.data:
data = (self.data.address,)
charts.addNewByName(self.name, ps, data, True, True)
self.obj = charts.getByName(self.name)
chart = self.obj.getEmbeddedObject()
chart.setDiagram(chart.createInstance(self.BASE.format(self.type)))
if not self.data:
self._add_series_calc(chart)
return chart
def _from_writer(self):
obj = self.doc.create_instance('com.sun.star.text.TextEmbeddedObject')
obj.setPropertyValue('CLSID', '12DCAE26-281F-416F-a234-c3086127382e')
obj.Name = self.name
obj.setSize(Size(self.width, self.height))
self.doc.insert_content(self.cursor, obj)
self.obj = obj
chart = obj.getEmbeddedObject()
tipo = self.type
if self.type == 'Column':
tipo = 'Bar'
chart.Diagram.Vertical = True
chart.setDiagram(chart.createInstance(self.BASE.format(tipo)))
chart.DataSourceLabelsInFirstColumn = True
if isinstance(self.data, str):
self._add_series_writer(chart)
else:
chart_data = chart.getData()
labels = [r[0] for r in self.data]
data = [(r[1],) for r in self.data]
chart_data.setData(data)
chart_data.RowDescriptions = labels
# ~ Bug
if tipo == 'Pie':
chart.setDiagram(chart.createInstance(self.BASE.format('Bar')))
chart.setDiagram(chart.createInstance(self.BASE.format('Pie')))
return chart
def insert(self):
if not self.cell is None:
chart = self._from_calc()
elif not self.cursor is None:
chart = self._from_writer()
diagram = chart.Diagram
if self.type == 'Bar':
diagram.Vertical = True
if hasattr(self.title, 'String'):
chart.HasMainTitle = True
self.title(chart.Title)
if hasattr(self.subtitle, 'String'):
chart.HasSubTitle = True
self.subtitle(chart.SubTitle)
if self.legend.__dict__:
chart.HasLegend = True
self.legend(chart.Legend)
if self.xaxistitle.__dict__:
diagram.HasXAxisTitle = True
self.xaxistitle(diagram.XAxisTitle)
if self.yaxistitle.__dict__:
diagram.HasYAxisTitle = True
self.yaxistitle(diagram.YAxisTitle)
if self.dim3d:
diagram.Dim3D = True
if self.series:
data_series = chart.getFirstDiagram(
).getCoordinateSystems(
)[0].getChartTypes()[0].DataSeries
for i, serie in enumerate(data_series):
for k, v in self.series[i].items():
if hasattr(serie, k):
setattr(serie, k, v)
return self
def _set_column_model(columns):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html
column_model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True)
for column in columns:
grid_column = create_instance('com.sun.star.awt.grid.GridColumn', True)
for k, v in column.items():
setattr(grid_column, k, v)
column_model.addColumn(grid_column)
return column_model
def _set_image_url(image, id_extension=''):
if exists_path(image):
return _path_url(image)
if not id_extension:
return ''
path = get_path_extension(id_extension)
path = join(path, DIR['images'], image)
return _path_url(path)
class LODialog(object):
def __init__(self, **properties):
self._obj = self._create(properties)
self._init_values()
def _init_values(self):
self._model = self._obj.Model
self._init_controls()
self._events = None
self._color_on_focus = -1
self._id_extension = ''
self._images = 'images'
return
def _create(self, properties):
path = properties.pop('Path', '')
if path:
dp = create_instance('com.sun.star.awt.DialogProvider', True)
return dp.createDialog(_path_url(path))
if 'Location' in properties:
location = properties.get('Location', 'application')
library = properties.get('Library', 'Standard')
if location == 'user':
location = 'application'
dp = create_instance('com.sun.star.awt.DialogProvider', True)
path = 'vnd.sun.star.script:{}.{}?location={}'.format(
library, properties['Name'], location)
if location == 'document':
uid = get_document().uid
path = 'vnd.sun.star.tdoc:/{}/Dialogs/{}/{}.xml'.format(
uid, library, properties['Name'])
return dp.createDialog(path)
dlg = create_instance('com.sun.star.awt.UnoControlDialog', True)
model = create_instance('com.sun.star.awt.UnoControlDialogModel', True)
toolkit = create_instance('com.sun.star.awt.Toolkit', True)
set_properties(model, properties)
dlg.setModel(model)
dlg.setVisible(False)
dlg.createPeer(toolkit, None)
return dlg
def _get_type_control(self, name):
types = {
'stardiv.Toolkit.UnoFixedTextControl': 'label',
'stardiv.Toolkit.UnoFixedHyperlinkControl': 'link',
'stardiv.Toolkit.UnoEditControl': 'text',
'stardiv.Toolkit.UnoButtonControl': 'button',
'stardiv.Toolkit.UnoListBoxControl': 'listbox',
'stardiv.Toolkit.UnoRoadmapControl': 'roadmap',
'stardiv.Toolkit.UnoMultiPageControl': 'pages',
}
return types[name]
def _init_controls(self):
for control in self.obj.getControls():
tipo = self._get_type_control(control.ImplementationName)
name = control.Model.Name
control = get_custom_class(tipo, control)
setattr(self, name, control)
return
@property
def obj(self):
return self._obj
@property
def model(self):
return self._model
@property
def id_extension(self):
return self._id_extension
@id_extension.setter
def id_extension(self, value):
global ID_EXTENSION
ID_EXTENSION = value
self._id_extension = value
@property
def images(self):
return self._images
@images.setter
def images(self, value):
self._images = value
@property
def height(self):
return self.model.Height
@height.setter
def height(self, value):
self.model.Height = value
@property
def width(self):
return self.model.Width
@width.setter
def width(self, value):
self.model.Width = value
@property
def color_on_focus(self):
return self._color_on_focus
@color_on_focus.setter
def color_on_focus(self, value):
global COLOR_ON_FOCUS
COLOR_ON_FOCUS = get_color(value)
self._color_on_focus = COLOR_ON_FOCUS
@property
def step(self):
return self.model.Step
@step.setter
def step(self, value):
self.model.Step = value
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers
self._connect_listeners()
def _connect_listeners(self):
for control in self.obj.getControls():
add_listeners(self._events, control, control.Model.Name)
return
def open(self):
return self.obj.execute()
def close(self, value=0):
return self.obj.endDialog(value)
def _get_control_model(self, control):
services = {
'label': 'com.sun.star.awt.UnoControlFixedTextModel',
'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
'text': 'com.sun.star.awt.UnoControlEditModel',
'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
'button': 'com.sun.star.awt.UnoControlButtonModel',
'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
'tree': 'com.sun.star.awt.tree.TreeControlModel',
'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
'image': 'com.sun.star.awt.UnoControlImageControlModel',
'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
'pages': 'com.sun.star.awt.UnoMultiPageModel',
}
return services[control]
def _set_column_model(self, columns):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html
column_model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True)
for column in columns:
grid_column = create_instance('com.sun.star.awt.grid.GridColumn', True)
for k, v in column.items():
setattr(grid_column, k, v)
column_model.addColumn(grid_column)
return column_model
def _set_image_url(self, image):
if exists_path(image):
return _path_url(image)
if not self.id_extension:
return ''
path = get_path_extension(self.id_extension)
path = join(path, self.images, image)
return _path_url(path)
def _special_properties(self, tipo, properties):
columns = properties.pop('Columns', ())
if tipo == 'grid':
properties['ColumnModel'] = self._set_column_model(columns)
elif tipo == 'button' and 'ImageURL' in properties:
properties['ImageURL'] = self._set_image_url(properties['ImageURL'])
elif tipo == 'roadmap':
if not 'Height' in properties:
properties['Height'] = self.height
if 'Title' in properties:
properties['Text'] = properties.pop('Title')
elif tipo == 'tab':
if not 'Width' in properties:
properties['Width'] = self.width
if not 'Height' in properties:
properties['Height'] = self.height
return properties
def add_control(self, properties):
tipo = properties.pop('Type').lower()
root = properties.pop('Root', '')
sheets = properties.pop('Sheets', ())
properties = self._special_properties(tipo, properties)
model = self.model.createInstance(self._get_control_model(tipo))
set_properties(model, properties)
name = properties['Name']
self.model.insertByName(name, model)
control = self.obj.getControl(name)
add_listeners(self.events, control, name)
control = get_custom_class(tipo, control)
if tipo == 'tree' and root:
control.root = root
elif tipo == 'pages' and sheets:
control.sheets = sheets
control.events = self.events
setattr(self, name, control)
return
def center(self, control, x=0, y=0):
w = self.width
h = self.height
if isinstance(control, tuple):
wt = SEPARATION * -1
for c in control:
wt += c.width + SEPARATION
x = w / 2 - wt / 2
for c in control:
c.x = x
x = c.x + c.width + SEPARATION
return
if x < 0:
x = w + x - control.width
elif x == 0:
x = w / 2 - control.width / 2
if y < 0:
y = h + y - control.height
elif y == 0:
y = h / 2 - control.height / 2
control.x = x
control.y = y
return
class LOWindow(object):
EMPTY = b"""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dlg:window PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "dialog.dtd">
<dlg:window xmlns:dlg="http://openoffice.org/2000/dialog" xmlns:script="http://openoffice.org/2000/script" dlg:id="empty" dlg:left="0" dlg:top="0" dlg:width="0" dlg:height="0" dlg:closeable="true" dlg:moveable="true" dlg:withtitlebar="false"/>"""
def __init__(self, **kwargs):
self._events = None
self._menu = None
self._container = None
self._id_extension = ''
self._obj = self._create(kwargs)
@property
def id_extension(self):
return self._id_extension
@id_extension.setter
def id_extension(self, value):
global ID_EXTENSION
ID_EXTENSION = value
self._id_extension = value
def _create(self, properties):
ps = (
properties.get('X', 0),
properties.get('Y', 0),
properties.get('Width', 500),
properties.get('Height', 500),
)
self._title = properties.get('Title', TITLE)
self._create_frame(ps)
self._create_container(ps)
self._create_subcontainer(ps)
# ~ self._create_splitter(ps)
return
def _create_frame(self, ps):
service = 'com.sun.star.frame.TaskCreator'
tc = create_instance(service, True)
self._frame = tc.createInstanceWithArguments((
NamedValue('FrameName', 'EasyMacroWin'),
NamedValue('PosSize', Rectangle(*ps)),
))
self._window = self._frame.getContainerWindow()
self._toolkit = self._window.getToolkit()
desktop = get_desktop()
self._frame.setCreator(desktop)
desktop.getFrames().append(self._frame)
self._frame.Title = self._title
return
def _create_container(self, ps):
# ~ toolkit = self._window.getToolkit()
service = 'com.sun.star.awt.UnoControlContainer'
self._container = create_instance(service, True)
service = 'com.sun.star.awt.UnoControlContainerModel'
model = create_instance(service, True)
model.BackgroundColor = get_color(225, 225, 225)
self._container.setModel(model)
self._container.createPeer(self._toolkit, self._window)
self._container.setPosSize(*ps, POSSIZE)
self._frame.setComponent(self._container, None)
return
def _create_subcontainer(self, ps):
service = 'com.sun.star.awt.ContainerWindowProvider'
cwp = create_instance(service, True)
with get_temp_file() as f:
f.write(self.EMPTY)
f.flush()
subcont = cwp.createContainerWindow(
_path_url(f.name), '', self._container.getPeer(), None)
# ~ service = 'com.sun.star.awt.UnoControlDialog'
# ~ subcont2 = create_instance(service, True)
# ~ service = 'com.sun.star.awt.UnoControlDialogModel'
# ~ model = create_instance(service, True)
# ~ service = 'com.sun.star.awt.UnoControlContainer'
# ~ context = create_instance(service, True)
# ~ subcont2.setModel(model)
# ~ subcont2.setContext(context)
# ~ subcont2.createPeer(self._toolkit, self._container.getPeer())
subcont.setPosSize(0, 0, 500, 500, POSSIZE)
subcont.setVisible(True)
self._container.addControl('subcont', subcont)
self._subcont = subcont
return
def _get_base_control(self, tipo):
services = {
'label': 'com.sun.star.awt.UnoControlFixedText',
'button': 'com.sun.star.awt.UnoControlButton',
'text': 'com.sun.star.awt.UnoControlEdit',
'listbox': 'com.sun.star.awt.UnoControlListBox',
'link': 'com.sun.star.awt.UnoControlFixedHyperlink',
'roadmap': 'com.sun.star.awt.UnoControlRoadmap',
'image': 'com.sun.star.awt.UnoControlImageControl',
'groupbox': 'com.sun.star.awt.UnoControlGroupBox',
'radio': 'com.sun.star.awt.UnoControlRadioButton',
'tree': 'com.sun.star.awt.tree.TreeControl',
'grid': 'com.sun.star.awt.grid.UnoControlGrid',
'tab': 'com.sun.star.awt.tab.UnoControlTabPage',
}
return services[tipo]
def _special_properties(self, tipo, properties):
columns = properties.pop('Columns', ())
if tipo == 'grid':
properties['ColumnModel'] = self._set_column_model(columns)
elif tipo == 'button' and 'ImageURL' in properties:
properties['ImageURL'] = _set_image_url(
properties['ImageURL'], self.id_extension)
elif tipo == 'roadmap':
if not 'Height' in properties:
properties['Height'] = self.height
if 'Title' in properties:
properties['Text'] = properties.pop('Title')
elif tipo == 'tab':
if not 'Width' in properties:
properties['Width'] = self.width - 20
if not 'Height' in properties:
properties['Height'] = self.height - 20
return properties
def add_control(self, properties):
tipo = properties.pop('Type').lower()
root = properties.pop('Root', '')
sheets = properties.pop('Sheets', ())
properties = self._special_properties(tipo, properties)
model = self._subcont.Model.createInstance(get_control_model(tipo))
set_properties(model, properties)
name = properties['Name']
self._subcont.Model.insertByName(name, model)
control = self._subcont.getControl(name)
add_listeners(self.events, control, name)
control = get_custom_class(tipo, control)
if tipo == 'tree' and root:
control.root = root
elif tipo == 'tab' and sheets:
control.sheets = sheets
control.events = self.events
setattr(self, name, control)
return
def _create_popupmenu(self, menus):
menu = create_instance('com.sun.star.awt.PopupMenu', True)
for i, m in enumerate(menus):
label = m['label']
cmd = m.get('event', '')
if not cmd:
cmd = label.lower().replace(' ', '_')
if label == '-':
menu.insertSeparator(i)
else:
menu.insertItem(i, label, m.get('style', 0), i)
menu.setCommand(i, cmd)
# ~ menu.setItemImage(i, path?, True)
menu.addMenuListener(EventsMenu(self.events))
return menu
def _create_menu(self, menus):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMenu.html
#~ nItemId specifies the ID of the menu item to be inserted.
#~ aText specifies the label of the menu item.
#~ nItemStyle 0 = Standard, CHECKABLE = 1, RADIOCHECK = 2, AUTOCHECK = 4
#~ nItemPos specifies the position where the menu item will be inserted.
self._menu = create_instance('com.sun.star.awt.MenuBar', True)
for i, m in enumerate(menus):
self._menu.insertItem(i, m['label'], m.get('style', 0), i)
cmd = m['label'].lower().replace(' ', '_')
self._menu.setCommand(i, cmd)
submenu = self._create_popupmenu(m['submenu'])
self._menu.setPopupMenu(i, submenu)
self._window.setMenuBar(self._menu)
return
def add_menu(self, menus):
self._create_menu(menus)
return
def _add_listeners(self, control=None):
if self.events is None:
return
controller = EventsWindow(self)
self._window.addTopWindowListener(controller)
self._window.addWindowListener(controller)
self._container.addKeyListener(EventsKeyWindow(self))
return
@property
def name(self):
return self._title.lower().replace(' ', '_')
@property
def events(self):
return self._events
@events.setter
def events(self, value):
self._events = value
self._add_listeners()
@property
def width(self):
return self._container.Size.Width
@property
def height(self):
return self._container.Size.Height
def open(self):
self._window.setVisible(True)
return
def close(self):
self._window.setMenuBar(None)
self._window.dispose()
self._frame.close(True)
return
# ~ Python >= 3.7
# ~ def __getattr__(name):
def _get_class_doc(obj):
classes = {
'calc': LOCalc,
'writer': LOWriter,
'base': LOBase,
'impress': LOImpress,
'draw': LODraw,
'math': LOMath,
'basic': LOBasicIde,
}
type_doc = get_type_doc(obj)
return classes[type_doc](obj)
# ~ Export ok
def get_document(title=''):
doc = None
desktop = get_desktop()
if not title:
doc = _get_class_doc(desktop.getCurrentComponent())
return doc
for d in desktop.getComponents():
if hasattr(d, 'Title') and d.Title == title:
doc = d
break
if doc is None:
return
return _get_class_doc(doc)
def get_documents(custom=True):
docs = []
desktop = get_desktop()
for doc in desktop.getComponents():
if custom:
docs.append(_get_class_doc(doc))
else:
docs.append(doc)
return docs
def get_selection():
return get_document().selection
def get_cell(*args):
if args:
index = args
if len(index) == 1:
index = args[0]
cell = get_document().get_cell(index)
else:
cell = get_selection().first
return cell
def active_cell():
return get_cell()
def active_sheet():
return get_document().active
def create_dialog(properties):
return LODialog(**properties)
def create_window(kwargs):
return LOWindow(**kwargs)
# ~ Export ok
def get_config_path(name='Work'):
"""
Return de path name in config
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1util_1_1XPathSettings.html
"""
path = create_instance('com.sun.star.util.PathSettings')
return _path_system(getattr(path, name))
def get_path_python():
path = get_config_path('Module')
if IS_MAC:
path = join(path, '..', 'Resources', PYTHON)
else:
path = join(path, PYTHON)
cmd = '"{}" -V'.format(path)
if run(cmd, True):
return path
path = PYTHON
cmd = '"{}" -V'.format(path)
result = run(cmd, True)
if 'Python 3' in result:
return path
path = PYTHON + '3'
cmd = '"{}" -V'.format(path)
result = run(cmd, True)
if 'Python 3' in result:
return path
return ''
# ~ Export ok
def get_file(init_dir='', multiple=False, filters=()):
"""
init_folder: folder default open
multiple: True for multiple selected
filters: Example
(
('XML', '*.xml'),
('TXT', '*.txt'),
)
"""
if not init_dir:
init_dir = get_config_path()
init_dir = _path_url(init_dir)
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
file_picker.setTitle(_('Select file'))
file_picker.setDisplayDirectory(init_dir)
file_picker.setMultiSelectionMode(multiple)
path = ''
if filters:
file_picker.setCurrentFilter(filters[0][0])
for f in filters:
file_picker.appendFilter(f[0], f[1])
if file_picker.execute():
path = _path_system(file_picker.getSelectedFiles()[0])
if multiple:
path = [_path_system(f) for f in file_picker.getSelectedFiles()]
return path
# ~ Export ok
def get_path(init_dir='', filters=()):
"""
Options: http://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1ui_1_1dialogs_1_1TemplateDescription.html
filters: Example
(
('XML', '*.xml'),
('TXT', '*.txt'),
)
"""
if not init_dir:
init_dir = get_config_path()
init_dir = _path_url(init_dir)
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
file_picker.setTitle(_('Select file'))
file_picker.setDisplayDirectory(init_dir)
file_picker.initialize((2,))
if filters:
file_picker.setCurrentFilter(filters[0][0])
for f in filters:
file_picker.appendFilter(f[0], f[1])
path = ''
if file_picker.execute():
path = _path_system(file_picker.getSelectedFiles()[0])
return path
# ~ Export ok
def get_dir(init_dir=''):
folder_picker = create_instance('com.sun.star.ui.dialogs.FolderPicker')
if not init_dir:
init_dir = get_config_path()
init_dir = _path_url(init_dir)
folder_picker.setDisplayDirectory(init_dir)
path = ''
if folder_picker.execute():
path = _path_system(folder_picker.getDirectory())
return path
# ~ Export ok
def get_info_path(path):
path, filename = os.path.split(path)
name, extension = os.path.splitext(filename)
return (path, filename, name, extension)
# ~ Export ok
def read_file(path, mode='r', array=False):
data = ''
with open(path, mode) as f:
if array:
data = tuple(f.read().splitlines())
else:
data = f.read()
return data
# ~ Export ok
def save_file(path, mode='w', data=None):
with open(path, mode) as f:
f.write(data)
return
# ~ Export ok
def to_json(path, data):
with open(path, 'w') as f:
f.write(json.dumps(data, indent=4, sort_keys=True))
return
# ~ Export ok
def from_json(path):
with open(path) as f:
data = json.loads(f.read())
return data
# ~ Export ok
def json_dumps(data):
return json.dumps(data, indent=4, sort_keys=True)
# ~ Export ok
def json_loads(data):
return json.loads(data)
def get_path_extension(id):
path = ''
pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider')
try:
path = _path_system(pip.getPackageLocation(id))
except Exception as e:
error(e)
return path
def get_home():
return Path.home()
# ~ Export ok
def inputbox(message, default='', title=TITLE, echochar=''):
class ControllersInput(object):
def __init__(self, dlg):
self.d = dlg
def cmd_ok_action(self, event):
self.d.close(1)
return
args = {
'Title': title,
'Width': 200,
'Height': 80,
}
dlg = LODialog(**args)
dlg.events = ControllersInput(dlg)
args = {
'Type': 'Label',
'Name': 'lbl_msg',
'Label': message,
'Width': 140,
'Height': 50,
'X': 5,
'Y': 5,
'MultiLine': True,
'Border': 1,
}
dlg.add_control(args)
args = {
'Type': 'Text',
'Name': 'txt_value',
'Text': default,
'Width': 190,
'Height': 15,
}
if echochar:
args['EchoChar'] = ord(echochar[0])
dlg.add_control(args)
dlg.txt_value.move(dlg.lbl_msg)
args = {
'Type': 'button',
'Name': 'cmd_ok',
'Label': _('OK'),
'Width': 40,
'Height': 15,
'DefaultButton': True,
'PushButtonType': 1,
}
dlg.add_control(args)
dlg.cmd_ok.move(dlg.lbl_msg, 10, 0)
args = {
'Type': 'button',
'Name': 'cmd_cancel',
'Label': _('Cancel'),
'Width': 40,
'Height': 15,
'PushButtonType': 2,
}
dlg.add_control(args)
dlg.cmd_cancel.move(dlg.cmd_ok)
if dlg.open():
return dlg.txt_value.value
return ''
# ~ Export ok
def new_doc(type_doc=CALC, **kwargs):
path = 'private:factory/s{}'.format(type_doc)
opt = dict_to_property(kwargs)
doc = get_desktop().loadComponentFromURL(path, '_default', 0, opt)
return _get_class_doc(doc)
# ~ Export ok
def new_db(path, name=''):
p, fn, n, e = get_info_path(path)
if not name:
name = n
return LOBase(name, path)
# ~ Todo
def exists_db(name):
dbc = create_instance('com.sun.star.sdb.DatabaseContext')
return dbc.hasRegisteredDatabase(name)
# ~ Todo
def register_db(name, path):
dbc = create_instance('com.sun.star.sdb.DatabaseContext')
dbc.registerDatabaseLocation(name, _path_url(path))
return
# ~ Todo
def get_db(name):
return LOBase(name)
# ~ Export ok
def open_doc(path, **kwargs):
""" Open document in path
Usually options:
Hidden: True or False
AsTemplate: True or False
ReadOnly: True or False
Password: super_secret
MacroExecutionMode: 4 = Activate macros
Preview: True or False
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1frame_1_1XComponentLoader.html
http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
"""
path = _path_url(path)
opt = dict_to_property(kwargs)
doc = get_desktop().loadComponentFromURL(path, '_default', 0, opt)
if doc is None:
return
return _get_class_doc(doc)
# ~ Export ok
def open_file(path):
if IS_WIN:
os.startfile(path)
else:
pid = subprocess.Popen(['xdg-open', path]).pid
return
# ~ Export ok
def join(*paths):
return os.path.join(*paths)
# ~ Export ok
def is_dir(path):
return Path(path).is_dir()
# ~ Export ok
def is_file(path):
return Path(path).is_file()
# ~ Export ok
def get_file_size(path):
return Path(path).stat().st_size
# ~ Export ok
def is_created(path):
return is_file(path) and bool(get_file_size(path))
# ~ Export ok
def replace_ext(path, extension):
path, _, name, _ = get_info_path(path)
return '{}/{}.{}'.format(path, name, extension)
# ~ Export ok
def zip_content(path):
with zipfile.ZipFile(path) as z:
names = z.namelist()
return names
def popen(command, stdin=None):
try:
proc = subprocess.Popen(shlex.split(command), shell=IS_WIN,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in proc.stdout:
yield line.decode().rstrip()
except Exception as e:
error(e)
yield (e.errno, e.strerror)
def url_open(url, options={}, verify=True, json=False):
data = ''
err = ''
req = Request(url)
try:
if verify:
response = urlopen(req)
else:
context = ssl._create_unverified_context()
response = urlopen(req, context=context)
except HTTPError as e:
error(e)
err = str(e)
except URLError as e:
error(e.reason)
err = str(e.reason)
else:
if json:
data = json_loads(response.read())
else:
data = response.read()
return data, err
def run(command, wait=False):
try:
if wait:
result = subprocess.check_output(command, shell=True)
else:
p = subprocess.Popen(shlex.split(command), stdin=None,
stdout=None, stderr=None, close_fds=True)
result, er = p.communicate()
except subprocess.CalledProcessError as e:
msg = ("%s\nrun [ERROR]: output = %s, error code = %s\n"
% (command, e.output, e.returncode))
error(msg)
return False
if result is None:
return True
return result.decode()
def _zippwd(source, target, pwd):
if IS_WIN:
return False
if not exists_app('zip'):
return False
cmd = 'zip'
opt = '-j '
args = "{} --password {} ".format(cmd, pwd)
if isinstance(source, (tuple, list)):
if not target:
return False
args += opt + target + ' ' + ' '.join(source)
else:
if is_file(source) and not target:
target = replace_ext(source, 'zip')
elif is_dir(source) and not target:
target = join(PurePath(source).parent,
'{}.zip'.format(PurePath(source).name))
opt = '-r '
args += opt + target + ' ' + source
result = run(args, True)
if not result:
return False
return is_created(target)
# ~ Export ok
def zip_files(source, target='', mode='w', pwd=''):
if pwd:
return _zippwd(source, target, pwd)
if isinstance(source, (tuple, list)):
if not target:
return False
with zipfile.ZipFile(target, mode, compression=zipfile.ZIP_DEFLATED) as z:
for path in source:
_, name, _, _ = get_info_path(path)
z.write(path, name)
return is_created(target)
if is_file(source):
if not target:
target = replace_ext(source, 'zip')
z = zipfile.ZipFile(target, mode, compression=zipfile.ZIP_DEFLATED)
_, name, _, _ = get_info_path(source)
z.write(source, name)
z.close()
return is_created(target)
if not target:
target = join(
PurePath(source).parent,
'{}.zip'.format(PurePath(source).name))
z = zipfile.ZipFile(target, mode, compression=zipfile.ZIP_DEFLATED)
root_len = len(os.path.abspath(source))
for root, dirs, files in os.walk(source):
relative = os.path.abspath(root)[root_len:]
for f in files:
fullpath = join(root, f)
file_name = join(relative, f)
z.write(fullpath, file_name)
z.close()
return is_created(target)
# ~ Export ok
def unzip(source, path='', members=None, pwd=None):
if not path:
path, _, _, _ = get_info_path(source)
with zipfile.ZipFile(source) as z:
if not pwd is None:
pwd = pwd.encode()
if isinstance(members, str):
members = (members,)
z.extractall(path, members=members, pwd=pwd)
return True
# ~ Export ok
def merge_zip(target, zips):
try:
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as t:
for path in zips:
with zipfile.ZipFile(path, compression=zipfile.ZIP_DEFLATED) as s:
for name in s.namelist():
t.writestr(name, s.open(name).read())
except Exception as e:
error(e)
return False
return True
# ~ Export ok
def kill(path):
p = Path(path)
try:
if p.is_file():
p.unlink()
elif p.is_dir():
shutil.rmtree(path)
except OSError as e:
log.error(e)
return
def get_size_screen():
if IS_WIN:
user32 = ctypes.windll.user32
res = '{}x{}'.format(user32.GetSystemMetrics(0), user32.GetSystemMetrics(1))
else:
args = 'xrandr | grep "*" | cut -d " " -f4'
res = run(args, True)
return res.strip()
def get_clipboard():
df = None
text = ''
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
transferable = sc.getContents()
data = transferable.getTransferDataFlavors()
for df in data:
if df.MimeType == CLIPBOARD_FORMAT_TEXT:
break
if df:
text = transferable.getTransferData(df)
return text
class TextTransferable(unohelper.Base, XTransferable):
"""Keep clipboard data and provide them."""
def __init__(self, text):
df = DataFlavor()
df.MimeType = CLIPBOARD_FORMAT_TEXT
df.HumanPresentableName = "encoded text utf-16"
self.flavors = [df]
self.data = [text]
def getTransferData(self, flavor):
if not flavor:
return
for i, f in enumerate(self.flavors):
if flavor.MimeType == f.MimeType:
return self.data[i]
return
def getTransferDataFlavors(self):
return tuple(self.flavors)
def isDataFlavorSupported(self, flavor):
if not flavor:
return False
mtype = flavor.MimeType
for f in self.flavors:
if mtype == f.MimeType:
return True
return False
# ~ Export ok
def set_clipboard(value):
ts = TextTransferable(value)
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
sc.setContents(ts, None)
return
# ~ Export ok
def copy():
call_dispatch('.uno:Copy')
return
# ~ Export ok
def get_epoch():
n = now()
return int(time.mktime(n.timetuple()))
# ~ Export ok
def file_copy(source, target='', name=''):
p, f, n, e = get_info_path(source)
if target:
p = target
if name:
e = ''
n = name
path_new = join(p, '{}{}'.format(n, e))
shutil.copy(source, path_new)
return path_new
def get_path_content(path, filters=''):
paths = []
if filters in ('*', '*.*'):
filters = ''
for folder, _, files in os.walk(path):
if filters:
pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE)
paths += [join(folder, f) for f in files if pattern.search(f)]
else:
paths += files
return paths
def _get_menu(type_doc, name_menu):
instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
service = TYPE_DOC[type_doc]
manager = create_instance(instance, True)
ui = manager.getUIConfigurationManager(service)
menus = ui.getSettings(NODE_MENUBAR, True)
command = MENUS_APP[type_doc][name_menu]
for menu in menus:
data = property_to_dict(menu)
if data.get('CommandURL', '') == command:
idc = data.get('ItemDescriptorContainer', None)
return ui, menus, idc
return None, None, None
def _get_index_menu(menu, command):
for i, m in enumerate(menu):
data = property_to_dict(m)
cmd = data.get('CommandURL', '')
if cmd == command:
return i
# ~ submenu = data.get('ItemDescriptorContainer', None)
# ~ if not submenu is None:
# ~ get_index_menu(submenu, command, count + 1)
return 0
def _store_menu(ui, menus, menu, index, data=(), remove=False):
if remove:
uno.invoke(menu, 'removeByIndex', (index,))
else:
properties = dict_to_property(data, True)
uno.invoke(menu, 'insertByIndex', (index + 1, properties))
ui.replaceSettings(NODE_MENUBAR, menus)
ui.store()
return
def insert_menu(type_doc, name_menu, **kwargs):
ui, menus, menu = _get_menu(type_doc, name_menu.lower())
if menu is None:
return 0
label = kwargs.get('Label', '-')
separator = False
if label == '-':
separator = True
command = kwargs.get('CommandURL', '')
index = kwargs.get('Index', 0)
if not index:
index = _get_index_menu(menu, kwargs['After'])
if separator:
data = {'Type': 1}
_store_menu(ui, menus, menu, index, data)
return index + 1
index_menu = _get_index_menu(menu, command)
if index_menu:
msg = 'Exists: %s' % command
debug(msg)
return 0
sub_menu = kwargs.get('Submenu', ())
idc = None
if sub_menu:
idc = ui.createSettings()
data = {
'CommandURL': command,
'Label': label,
'Style': 0,
'Type': 0,
'ItemDescriptorContainer': idc
}
_store_menu(ui, menus, menu, index, data)
if sub_menu:
_add_sub_menus(ui, menus, idc, sub_menu)
return True
def _add_sub_menus(ui, menus, menu, sub_menu):
for i, sm in enumerate(sub_menu):
submenu = sm.pop('Submenu', ())
sm['Type'] = 0
if submenu:
idc = ui.createSettings()
sm['ItemDescriptorContainer'] = idc
if sm['Label'] == '-':
sm = {'Type': 1}
_store_menu(ui, menus, menu, i - 1, sm)
if submenu:
_add_sub_menus(ui, menus, idc, submenu)
return
def remove_menu(type_doc, name_menu, command):
ui, menus, menu = _get_menu(type_doc, name_menu.lower())
if menu is None:
return False
index = _get_index_menu(menu, command)
if not index:
debug('Not exists: %s' % command)
return False
_store_menu(ui, menus, menu, index, remove=True)
return True
def _get_app_submenus(menus, count=0):
for i, menu in enumerate(menus):
data = property_to_dict(menu)
cmd = data.get('CommandURL', '')
msg = ' ' * count + '├─' + cmd
debug(msg)
submenu = data.get('ItemDescriptorContainer', None)
if not submenu is None:
_get_app_submenus(submenu, count + 1)
return
def get_app_menus(name_app, index=-1):
instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
service = TYPE_DOC[name_app]
manager = create_instance(instance, True)
ui = manager.getUIConfigurationManager(service)
menus = ui.getSettings(NODE_MENUBAR, True)
if index == -1:
for menu in menus:
data = property_to_dict(menu)
debug(data.get('CommandURL', ''))
else:
menus = property_to_dict(menus[index])['ItemDescriptorContainer']
_get_app_submenus(menus)
return menus
# ~ Export ok
def start():
global _start
_start = now()
log.info(_start)
return
# ~ Export ok
def end():
global _start
e = now()
return str(e - _start).split('.')[0]
# ~ Export ok
# ~ https://en.wikipedia.org/wiki/Web_colors
def get_color(*value):
if len(value) == 1 and isinstance(value[0], int):
return value[0]
if len(value) == 1 and isinstance(value[0], tuple):
value = value[0]
COLORS = {
'aliceblue': 15792383,
'antiquewhite': 16444375,
'aqua': 65535,
'aquamarine': 8388564,
'azure': 15794175,
'beige': 16119260,
'bisque': 16770244,
'black': 0,
'blanchedalmond': 16772045,
'blue': 255,
'blueviolet': 9055202,
'brown': 10824234,
'burlywood': 14596231,
'cadetblue': 6266528,
'chartreuse': 8388352,
'chocolate': 13789470,
'coral': 16744272,
'cornflowerblue': 6591981,
'cornsilk': 16775388,
'crimson': 14423100,
'cyan': 65535,
'darkblue': 139,
'darkcyan': 35723,
'darkgoldenrod': 12092939,
'darkgray': 11119017,
'darkgreen': 25600,
'darkgrey': 11119017,
'darkkhaki': 12433259,
'darkmagenta': 9109643,
'darkolivegreen': 5597999,
'darkorange': 16747520,
'darkorchid': 10040012,
'darkred': 9109504,
'darksalmon': 15308410,
'darkseagreen': 9419919,
'darkslateblue': 4734347,
'darkslategray': 3100495,
'darkslategrey': 3100495,
'darkturquoise': 52945,
'darkviolet': 9699539,
'deeppink': 16716947,
'deepskyblue': 49151,
'dimgray': 6908265,
'dimgrey': 6908265,
'dodgerblue': 2003199,
'firebrick': 11674146,
'floralwhite': 16775920,
'forestgreen': 2263842,
'fuchsia': 16711935,
'gainsboro': 14474460,
'ghostwhite': 16316671,
'gold': 16766720,
'goldenrod': 14329120,
'gray': 8421504,
'grey': 8421504,
'green': 32768,
'greenyellow': 11403055,
'honeydew': 15794160,
'hotpink': 16738740,
'indianred': 13458524,
'indigo': 4915330,
'ivory': 16777200,
'khaki': 15787660,
'lavender': 15132410,
'lavenderblush': 16773365,
'lawngreen': 8190976,
'lemonchiffon': 16775885,
'lightblue': 11393254,
'lightcoral': 15761536,
'lightcyan': 14745599,
'lightgoldenrodyellow': 16448210,
'lightgray': 13882323,
'lightgreen': 9498256,
'lightgrey': 13882323,
'lightpink': 16758465,
'lightsalmon': 16752762,
'lightseagreen': 2142890,
'lightskyblue': 8900346,
'lightslategray': 7833753,
'lightslategrey': 7833753,
'lightsteelblue': 11584734,
'lightyellow': 16777184,
'lime': 65280,
'limegreen': 3329330,
'linen': 16445670,
'magenta': 16711935,
'maroon': 8388608,
'mediumaquamarine': 6737322,
'mediumblue': 205,
'mediumorchid': 12211667,
'mediumpurple': 9662683,
'mediumseagreen': 3978097,
'mediumslateblue': 8087790,
'mediumspringgreen': 64154,
'mediumturquoise': 4772300,
'mediumvioletred': 13047173,
'midnightblue': 1644912,
'mintcream': 16121850,
'mistyrose': 16770273,
'moccasin': 16770229,
'navajowhite': 16768685,
'navy': 128,
'oldlace': 16643558,
'olive': 8421376,
'olivedrab': 7048739,
'orange': 16753920,
'orangered': 16729344,
'orchid': 14315734,
'palegoldenrod': 15657130,
'palegreen': 10025880,
'paleturquoise': 11529966,
'palevioletred': 14381203,
'papayawhip': 16773077,
'peachpuff': 16767673,
'peru': 13468991,
'pink': 16761035,
'plum': 14524637,
'powderblue': 11591910,
'purple': 8388736,
'red': 16711680,
'rosybrown': 12357519,
'royalblue': 4286945,
'saddlebrown': 9127187,
'salmon': 16416882,
'sandybrown': 16032864,
'seagreen': 3050327,
'seashell': 16774638,
'sienna': 10506797,
'silver': 12632256,
'skyblue': 8900331,
'slateblue': 6970061,
'slategray': 7372944,
'slategrey': 7372944,
'snow': 16775930,
'springgreen': 65407,
'steelblue': 4620980,
'tan': 13808780,
'teal': 32896,
'thistle': 14204888,
'tomato': 16737095,
'turquoise': 4251856,
'violet': 15631086,
'wheat': 16113331,
'white': 16777215,
'whitesmoke': 16119285,
'yellow': 16776960,
'yellowgreen': 10145074,
}
if len(value) == 3:
color = (value[0] << 16) + (value[1] << 8) + value[2]
else:
value = value[0]
if value[0] == '#':
r, g, b = bytes.fromhex(value[1:])
color = (r << 16) + (g << 8) + b
else:
color = COLORS.get(value.lower(), -1)
return color
COLOR_ON_FOCUS = get_color('LightYellow')
# ~ Export ok
def render(template, data):
s = Template(template)
return s.safe_substitute(**data)
def _to_date(value):
new_value = value
if isinstance(value, Time):
new_value = datetime.time(value.Hours, value.Minutes, value.Seconds)
elif isinstance(value, Date):
new_value = datetime.date(value.Year, value.Month, value.Day)
elif isinstance(value, DateTime):
new_value = datetime.datetime(
value.Year, value.Month, value.Day,
value.Hours, value.Minutes, value.Seconds)
return new_value
def date_to_struct(value):
# ~ print(type(value))
if isinstance(value, datetime.datetime):
d = DateTime()
d.Seconds = value.second
d.Minutes = value.minute
d.Hours = value.hour
d.Day = value.day
d.Month = value.month
d.Year = value.year
elif isinstance(value, datetime.date):
d = Date()
d.Day = value.day
d.Month = value.month
d.Year = value.year
return d
# ~ Export ok
def format(template, data):
"""
https://pyformat.info/
"""
if isinstance(data, (str, int, float)):
# ~ print(template.format(data))
return template.format(data)
if isinstance(data, (Time, Date, DateTime)):
return template.format(_to_date(data))
if isinstance(data, tuple) and isinstance(data[0], tuple):
data = {r[0]: _to_date(r[1]) for r in data}
return template.format(**data)
data = [_to_date(v) for v in data]
result = template.format(*data)
return result
def _get_url_script(macro):
macro['language'] = macro.get('language', 'Python')
macro['location'] = macro.get('location', 'user')
data = macro.copy()
if data['language'] == 'Python':
data['module'] = '.py$'
elif data['language'] == 'Basic':
data['module'] = '.{}.'.format(macro['module'])
if macro['location'] == 'user':
data['location'] = 'application'
else:
data['module'] = '.'
url = 'vnd.sun.star.script:{library}{module}{name}?language={language}&location={location}'
path = url.format(**data)
return path
def _call_macro(macro):
#~ https://wiki.openoffice.org/wiki/Documentation/DevGuide/Scripting/Scripting_Framework_URI_Specification
name = 'com.sun.star.script.provider.MasterScriptProviderFactory'
factory = create_instance(name, False)
macro['language'] = macro.get('language', 'Python')
macro['location'] = macro.get('location', 'user')
data = macro.copy()
if data['language'] == 'Python':
data['module'] = '.py$'
elif data['language'] == 'Basic':
data['module'] = '.{}.'.format(macro['module'])
if macro['location'] == 'user':
data['location'] = 'application'
else:
data['module'] = '.'
args = macro.get('args', ())
url = 'vnd.sun.star.script:{library}{module}{name}?language={language}&location={location}'
path = url.format(**data)
script = factory.createScriptProvider('').getScript(path)
return script.invoke(args, None, None)[0]
# ~ Export ok
def call_macro(macro):
in_thread = macro.pop('thread')
if in_thread:
t = threading.Thread(target=_call_macro, args=(macro,))
t.start()
return
return _call_macro(macro)
class TimerThread(threading.Thread):
def __init__(self, event, seconds, macro):
threading.Thread.__init__(self)
self.stopped = event
self.seconds = seconds
self.macro = macro
def run(self):
info('Timer started... {}'.format(self.macro['name']))
while not self.stopped.wait(self.seconds):
_call_macro(self.macro)
info('Timer stopped... {}'.format(self.macro['name']))
return
# ~ Export ok
def timer(name, seconds, macro):
global _stop_thread
_stop_thread[name] = threading.Event()
thread = TimerThread(_stop_thread[name], seconds, macro)
thread.start()
return
# ~ Export ok
def stop_timer(name):
global _stop_thread
_stop_thread[name].set()
del _stop_thread[name]
return
def _get_key(password):
digest = hashlib.sha256(password.encode()).digest()
key = base64.urlsafe_b64encode(digest)
return key
# ~ Export ok
def encrypt(data, password):
f = Fernet(_get_key(password))
token = f.encrypt(data).decode()
return token
# ~ Export ok
def decrypt(token, password):
data = ''
f = Fernet(_get_key(password))
try:
data = f.decrypt(token.encode()).decode()
except InvalidToken as e:
error('Invalid Token')
return data
class SmtpServer(object):
def __init__(self, config):
self._server = None
self._error = ''
self._sender = ''
self._is_connect = self._login(config)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
@property
def is_connect(self):
return self._is_connect
@property
def error(self):
return self._error
def _login(self, config):
name = config['server']
port = config['port']
is_ssl = config['ssl']
self._sender = config['user']
hosts = ('gmail' in name or 'outlook' in name)
try:
if is_ssl and hosts:
self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
self._server.ehlo()
self._server.starttls()
self._server.ehlo()
elif is_ssl:
self._server = smtplib.SMTP_SSL(name, port, timeout=TIMEOUT)
self._server.ehlo()
else:
self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
self._server.login(self._sender, config['pass'])
msg = 'Connect to: {}'.format(name)
debug(msg)
return True
except smtplib.SMTPAuthenticationError as e:
if '535' in str(e):
self._error = _('Incorrect user or password')
return False
if '534' in str(e) and 'gmail' in name:
self._error = _('Allow less secure apps in GMail')
return False
except smtplib.SMTPException as e:
self._error = str(e)
return False
except Exception as e:
self._error = str(e)
return False
return False
def _body(self, msg):
body = msg.replace('\\n', '<BR>')
return body
def send(self, message):
file_name = 'attachment; filename={}'
email = MIMEMultipart()
email['From'] = self._sender
email['To'] = message['to']
email['Cc'] = message.get('cc', '')
email['Subject'] = message['subject']
email['Date'] = formatdate(localtime=True)
if message.get('confirm', False):
email['Disposition-Notification-To'] = email['From']
email.attach(MIMEText(self._body(message['body']), 'html'))
for path in message.get('files', ()):
_, fn, _, _ = get_info_path(path)
part = MIMEBase('application', 'octet-stream')
part.set_payload(read_file(path, 'rb'))
encoders.encode_base64(part)
part.add_header('Content-Disposition', file_name.format(fn))
email.attach(part)
receivers = (
email['To'].split(',') +
email['CC'].split(',') +
message.get('bcc', '').split(','))
try:
self._server.sendmail(self._sender, receivers, email.as_string())
msg = 'Email sent...'
debug(msg)
if message.get('path', ''):
self.save_message(email, message['path'])
return True
except Exception as e:
self._error = str(e)
return False
return False
def save_message(self, email, path):
mbox = mailbox.mbox(path, create=True)
mbox.lock()
try:
msg = mailbox.mboxMessage(email)
mbox.add(msg)
mbox.flush()
finally:
mbox.unlock()
return
def close(self):
try:
self._server.quit()
msg = 'Close connection...'
debug(msg)
except:
pass
return
def _send_email(server, messages):
with SmtpServer(server) as server:
if server.is_connect:
for msg in messages:
server.send(msg)
else:
error(server.error)
return server.error
def send_email(server, message):
messages = message
if isinstance(message, dict):
messages = (message,)
t = threading.Thread(target=_send_email, args=(server, messages))
t.start()
return
def server_smtp_test(config):
with SmtpServer(config) as server:
if server.error:
error(server.error)
return server.error
def import_csv(path, **kwargs):
"""
See https://docs.python.org/3.5/library/csv.html#csv.reader
"""
with open(path) as f:
rows = tuple(csv.reader(f, **kwargs))
return rows
def export_csv(path, data, **kwargs):
with open(path, 'w') as f:
writer = csv.writer(f, **kwargs)
writer.writerows(data)
return
def install_locales(path, domain='base', dir_locales=DIR['locales']):
p, *_ = get_info_path(path)
path_locales = join(p, dir_locales)
try:
lang = gettext.translation(domain, path_locales, languages=[LANG])
lang.install()
_ = lang.gettext
except Exception as e:
from gettext import gettext as _
error(e)
return _
class LIBOServer(object):
HOST = 'localhost'
PORT = '8100'
ARG = 'socket,host={},port={};urp;StarOffice.ComponentContext'.format(HOST, PORT)
CMD = ['soffice',
'-env:SingleAppInstance=false',
'-env:UserInstallation=file:///tmp/LIBO_Process8100',
'--headless', '--norestore', '--invisible',
'--accept={}'.format(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
# ~ controls = {
# ~ 'CheckBox': 'com.sun.star.awt.UnoControlCheckBoxModel',
# ~ 'ComboBox': 'com.sun.star.awt.UnoControlComboBoxModel',
# ~ 'CurrencyField': 'com.sun.star.awt.UnoControlCurrencyFieldModel',
# ~ 'DateField': 'com.sun.star.awt.UnoControlDateFieldModel',
# ~ 'FileControl': 'com.sun.star.awt.UnoControlFileControlModel',
# ~ 'FormattedField': 'com.sun.star.awt.UnoControlFormattedFieldModel',
# ~ 'GroupBox': 'com.sun.star.awt.UnoControlGroupBoxModel',
# ~ 'ImageControl': 'com.sun.star.awt.UnoControlImageControlModel',
# ~ 'NumericField': 'com.sun.star.awt.UnoControlNumericFieldModel',
# ~ 'PatternField': 'com.sun.star.awt.UnoControlPatternFieldModel',
# ~ 'ProgressBar': 'com.sun.star.awt.UnoControlProgressBarModel',
# ~ 'ScrollBar': 'com.sun.star.awt.UnoControlScrollBarModel',
# ~ 'SimpleAnimation': 'com.sun.star.awt.UnoControlSimpleAnimationModel',
# ~ 'SpinButton': 'com.sun.star.awt.UnoControlSpinButtonModel',
# ~ 'Throbber': 'com.sun.star.awt.UnoControlThrobberModel',
# ~ 'TimeField': 'com.sun.star.awt.UnoControlTimeFieldModel',
# ~ }