4926 lines
133 KiB
Python
4926 lines
133 KiB
Python
![]() |
# ~ FILTER_PDF = '/org.openoffice.Office.Common/Filter/PDF/Export/'
|
||
|
|
||
|
|
||
|
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 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 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
|
||
|
|
||
|
|
||
|
# ~ 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 address(self):
|
||
|
return self.obj.Anchor.AbsoluteName
|
||
|
|
||
|
@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
|
||
|
|
||
|
def _set_cell(self, k='', v=None, cell=None, value=False):
|
||
|
if k:
|
||
|
self._sd.setSearchString(k)
|
||
|
ranges = self._search.findAll(self._sd)
|
||
|
if ranges:
|
||
|
ranges = ranges.getRangeAddressesAsString().split(';')
|
||
|
for r in ranges:
|
||
|
for c in r.split(','):
|
||
|
cell = self._sheet.getCellRangeByName(c)
|
||
|
if v is None:
|
||
|
return cell
|
||
|
if cell.getImplementationName() == 'ScCellObj':
|
||
|
pattern = re.compile(k, re.IGNORECASE)
|
||
|
nv = pattern.sub(v, cell.getString())
|
||
|
if value:
|
||
|
cell.setValue(nv)
|
||
|
else:
|
||
|
cell.setString(nv)
|
||
|
return cell
|
||
|
if cell:
|
||
|
if cell.getImplementationName() == 'ScCellObj':
|
||
|
ca = cell.getCellAddress()
|
||
|
new_cell = self._sheet.getCellByPosition(ca.Column, ca.Row + 1)
|
||
|
if value:
|
||
|
new_cell.setValue(v)
|
||
|
else:
|
||
|
new_cell.setString(v)
|
||
|
return new_cell
|
||
|
|
||
|
def render(self, data, sheet=0):
|
||
|
self.set_search_in(sheet)
|
||
|
|
||
|
for k, v in data.items():
|
||
|
self._render_cells(k, v)
|
||
|
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
|
||
|
|
||
|
def get_range_by_address(self, address):
|
||
|
row_s = address.StartRow
|
||
|
row_e = address.EndRow + 1
|
||
|
col_s = address.StartColumn
|
||
|
col_e = address.EndColumn + 1
|
||
|
return LOCellRange(self.obj[row_s:row_e,col_s:col_e], self.doc)
|
||
|
|
||
|
def render(self, data, rango=None, clean=False):
|
||
|
if rango is None:
|
||
|
ra = self.obj.getPrintAreas()[0]
|
||
|
rango = self.get_range_by_address(ra)
|
||
|
|
||
|
rango.render(data, clean)
|
||
|
return rango
|
||
|
|
||
|
def find(self, search_string, rango=None):
|
||
|
if rango is None:
|
||
|
ra = self.obj.getPrintAreas()[0]
|
||
|
rango = self.get_range_by_address(ra)
|
||
|
|
||
|
cell = rango.find(search_string)
|
||
|
|
||
|
return cell
|
||
|
|
||
|
|
||
|
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)
|
||
|
|
||
|
@property
|
||
|
def selection(self):
|
||
|
sel = self.obj.getCurrentSelection()
|
||
|
return sel
|
||
|
|
||
|
|
||
|
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._sd = None
|
||
|
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(self._doc, '.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 merged_area(self):
|
||
|
cursor = self.sheet.get_cursor(self.obj[0,0])
|
||
|
cursor.collapseToMergedArea()
|
||
|
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)
|
||
|
data = kwargs.get('Data', '')
|
||
|
img = self.doc.create_instance('com.sun.star.drawing.GraphicObjectShape')
|
||
|
img.GraphicURL = _path_url(path)
|
||
|
# ~ img.ResizeWithCell = True
|
||
|
self.draw_page.add(img)
|
||
|
img.setSize(Size(w, h))
|
||
|
img.Anchor = self.obj
|
||
|
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
|
||
|
|
||
|
def _set_new_value(self, cell, search, value):
|
||
|
if not cell.ImplementationName == 'ScCellObj':
|
||
|
return
|
||
|
|
||
|
if isinstance(value, str):
|
||
|
pattern = re.compile(search, re.IGNORECASE)
|
||
|
new_value = pattern.sub(value, cell.String)
|
||
|
cell.String = new_value
|
||
|
else:
|
||
|
cell.Value = value
|
||
|
return
|
||
|
|
||
|
def rows_insert(self, count=0):
|
||
|
if not count:
|
||
|
count = self.rows
|
||
|
self.obj.Rows.insertByIndex(0, count)
|
||
|
return
|
||
|
|
||
|
def rows_merge(self):
|
||
|
for r in range(self.rows):
|
||
|
self[r].obj.merge(True)
|
||
|
return
|
||
|
|
||
|
def copy_format_from(self, rango):
|
||
|
rango.select()
|
||
|
copy(self._doc)
|
||
|
self.select()
|
||
|
args = {
|
||
|
'Flags': 'T',
|
||
|
'MoveMode': 4,
|
||
|
}
|
||
|
args = dict_to_property(args)
|
||
|
url = '.uno:InsertContents'
|
||
|
call_dispatch(self._doc, url, args)
|
||
|
return
|
||
|
|
||
|
def _render_list(self, key, values):
|
||
|
rango = None
|
||
|
total_rows = len(values)
|
||
|
try:
|
||
|
rango = self.sheet[key]
|
||
|
if total_rows > 1:
|
||
|
cells = rango[0,0].offset(1).to_size(total_rows - 1, 1)
|
||
|
name = cells.name
|
||
|
cells.rows_insert()
|
||
|
cells = self.sheet[name]
|
||
|
cells.copy_format_from(rango)
|
||
|
except Exception as e:
|
||
|
print(key, e)
|
||
|
|
||
|
if rango is None:
|
||
|
pass
|
||
|
else:
|
||
|
headers = list(rango.data[0])
|
||
|
positions = {}
|
||
|
for i, v in enumerate(headers):
|
||
|
if not v:
|
||
|
continue
|
||
|
k = v[1:-1]
|
||
|
if k in values[0]:
|
||
|
positions[k] = i
|
||
|
|
||
|
data = []
|
||
|
for row in values:
|
||
|
new_row = headers[:]
|
||
|
for k, v in positions.items():
|
||
|
new_row[v] = row[k]
|
||
|
data.append(new_row)
|
||
|
|
||
|
cell = rango[0,0]
|
||
|
cell.copy_from(data)
|
||
|
return
|
||
|
|
||
|
def _render_value(self, key, value, parent=''):
|
||
|
if isinstance(value, dict):
|
||
|
for k, v in value.items():
|
||
|
self._render_value(k, v, key)
|
||
|
return
|
||
|
elif isinstance(value, (list, tuple)):
|
||
|
self._render_list(key, value)
|
||
|
return
|
||
|
|
||
|
search = f'{{{key}}}'
|
||
|
if parent:
|
||
|
search = f'{{{parent}.{key}}}'
|
||
|
ranges = self.find_all(search)
|
||
|
|
||
|
for cell in ranges or range(0):
|
||
|
self._set_new_value(cell, search, value)
|
||
|
return
|
||
|
|
||
|
def clean_render(self, template='\{(\w.+)\}'):
|
||
|
self._sd.SearchRegularExpression = True
|
||
|
self._sd.setSearchString(template)
|
||
|
self.obj.replaceAll(self._sd)
|
||
|
return
|
||
|
|
||
|
def find(self, search_string):
|
||
|
if self._sd is None:
|
||
|
self._sd = self.sheet.obj.createSearchDescriptor()
|
||
|
self._sd.SearchCaseSensitive = False
|
||
|
|
||
|
self._sd.setSearchString(search_string)
|
||
|
cell = self.obj.findFirst(self._sd)
|
||
|
return LOCellRange(self.sheet[cell.AbsoluteName].obj, self.doc)
|
||
|
|
||
|
def find_all(self, search_string):
|
||
|
if self._sd is None:
|
||
|
self._sd = self.sheet.obj.createSearchDescriptor()
|
||
|
self._sd.SearchCaseSensitive = False
|
||
|
|
||
|
self._sd.setSearchString(search_string)
|
||
|
ranges = self.obj.findAll(self._sd)
|
||
|
return ranges
|
||
|
|
||
|
def render(self, data, clean=False):
|
||
|
self._sd = self.sheet.obj.createSearchDescriptor()
|
||
|
self._sd.SearchCaseSensitive = False
|
||
|
|
||
|
for k, v in data.items():
|
||
|
self._render_value(k, v)
|
||
|
|
||
|
if clean:
|
||
|
self.clean_render()
|
||
|
return
|
||
|
|
||
|
|
||
|
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
|
||
|
|
||
|
|
||
|
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_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 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 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
|
||
|
|
||
|
|
||
|
# ~ 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 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(doc):
|
||
|
call_dispatch(doc, '.uno:Copy')
|
||
|
return
|
||
|
|
||
|
|
||
|
def paste(doc):
|
||
|
call_dispatch(doc, '.uno:Paste')
|
||
|
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_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
|
||
|
|