Add support for locales
This commit is contained in:
parent
614d80e479
commit
358937cd19
|
@ -0,0 +1,14 @@
|
|||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
*.log
|
||||
images/
|
||||
|
||||
docs/
|
||||
|
||||
# Virtualenv
|
||||
.env/
|
||||
virtual/
|
||||
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
70
conf.py
70
conf.py
|
@ -26,7 +26,7 @@ import logging
|
|||
TYPE_EXTENSION = 1
|
||||
|
||||
# ~ https://semver.org/
|
||||
VERSION = '0.1.0'
|
||||
VERSION = '0.2.0'
|
||||
|
||||
# ~ Your great extension name, not used spaces
|
||||
NAME = 'ZAZBarCode'
|
||||
|
@ -34,6 +34,9 @@ NAME = 'ZAZBarCode'
|
|||
# ~ Should be unique, used URL inverse
|
||||
ID = 'net.elmau.zaz.BarCode'
|
||||
|
||||
# ~ If you extension will be multilanguage set True
|
||||
USE_LOCALES = False
|
||||
|
||||
PUBLISHER = {
|
||||
'en': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'},
|
||||
'es': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'},
|
||||
|
@ -66,7 +69,6 @@ LICENSE_EN = f"""This file is part of {NAME}.
|
|||
"""
|
||||
LICENSE_ES = LICENSE_EN
|
||||
|
||||
|
||||
INFO = {
|
||||
'en': {
|
||||
'display_name': 'ZAZ Bar Code',
|
||||
|
@ -258,6 +260,7 @@ DIRS = {
|
|||
'registration': 'registration',
|
||||
'files': 'files',
|
||||
'pythonpath': False,
|
||||
'locales': 'locales',
|
||||
}
|
||||
|
||||
|
||||
|
@ -279,16 +282,21 @@ FILES = {
|
|||
|
||||
# ~ URLs for update for example
|
||||
# ~ URL_XML_UPDATE = 'https://gitlab.com/USER/PROYECT/raw/BRANCH/FOLDERs/FILE_NAME'
|
||||
URL_XML_UPDATE = ''
|
||||
URL_OXT = ''
|
||||
URL_BASE = 'https://gitlab.com/mauriciobaeza/zaz-barcode/raw/master/files/'
|
||||
URL_XML_UPDATE = URL_BASE + FILES['update']
|
||||
URL_OXT = URL_BASE + FILES['oxt']
|
||||
|
||||
|
||||
FILE_TEST = '/home/mau/test/test.ods'
|
||||
# ~ Default program for test: --calc, --writer, --draw
|
||||
PROGRAM = '--calc'
|
||||
# ~ Path to file for test
|
||||
FILE_TEST = ''
|
||||
|
||||
PATHS = {
|
||||
'idlc': '/usr/lib/libreoffice/sdk/bin/idlc',
|
||||
'include': '/usr/share/idl/libreoffice',
|
||||
'regmerge': '/usr/lib/libreoffice/program/regmerge',
|
||||
'soffice': ('soffice', '--calc', FILE_TEST),
|
||||
'soffice': ('soffice', PROGRAM, FILE_TEST),
|
||||
'install': ('unopkg', 'add', '-v', '-f', '-s'),
|
||||
}
|
||||
|
||||
|
@ -445,6 +453,8 @@ if PARENT == 'OfficeMenuBar':
|
|||
|
||||
|
||||
def _get_context(args):
|
||||
if not args:
|
||||
return ''
|
||||
c = []
|
||||
for v in args.split(','):
|
||||
c.append(CONTEXT[v])
|
||||
|
@ -472,29 +482,31 @@ for i, m in enumerate(MENUS):
|
|||
toolbar.append(NODE_MENU.format(**values))
|
||||
|
||||
NODE_TOOLBAR = ''
|
||||
if PARENT == 'AddonMenu':
|
||||
NODE_MENUS = '\n'.join(menus)
|
||||
else:
|
||||
tmp = ' <value xml:lang="{}">{}</value>'
|
||||
titles = '\n'.join([tmp.format(k, v) for k, v in MENU_MAIN.items()])
|
||||
SUBMENUS = '<node oor:name="Submenu">\n ' + '\n'.join(menus) + '\n </node>'
|
||||
NODE_MENUS = f""" <node oor:name="{ID}" oor:op="replace">
|
||||
<prop oor:name="Title" oor:type="xs:string">
|
||||
{titles}
|
||||
</prop>
|
||||
<prop oor:name="Target" oor:type="xs:string">
|
||||
<value>_self</value>
|
||||
</prop>
|
||||
{SUBMENUS}
|
||||
</node>"""
|
||||
NODE_MENUS = ''
|
||||
if TYPE_EXTENSION == 1:
|
||||
if PARENT == 'AddonMenu':
|
||||
NODE_MENUS = '\n'.join(menus)
|
||||
else:
|
||||
tmp = ' <value xml:lang="{}">{}</value>'
|
||||
titles = '\n'.join([tmp.format(k, v) for k, v in MENU_MAIN.items()])
|
||||
SUBMENUS = '<node oor:name="Submenu">\n ' + '\n'.join(menus) + '\n </node>'
|
||||
NODE_MENUS = f""" <node oor:name="{ID}" oor:op="replace">
|
||||
<prop oor:name="Title" oor:type="xs:string">
|
||||
{titles}
|
||||
</prop>
|
||||
<prop oor:name="Target" oor:type="xs:string">
|
||||
<value>_self</value>
|
||||
</prop>
|
||||
{SUBMENUS}
|
||||
</node>"""
|
||||
|
||||
if toolbar:
|
||||
node_toolbars = '\n'.join(toolbar)
|
||||
NODE_TOOLBAR = f""" <node oor:name="OfficeToolBar">
|
||||
<node oor:name="{ID}" oor:op="replace">
|
||||
{node_toolbars}
|
||||
</node>
|
||||
</node>"""
|
||||
if toolbar:
|
||||
node_toolbars = '\n'.join(toolbar)
|
||||
NODE_TOOLBAR = f""" <node oor:name="OfficeToolBar">
|
||||
<node oor:name="{ID}" oor:op="replace">
|
||||
{node_toolbars}
|
||||
</node>
|
||||
</node>"""
|
||||
|
||||
FILE_ADDONS = f"""<?xml version='1.0' encoding='UTF-8'?>
|
||||
<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="Addons" oor:package="org.openoffice.Office">
|
||||
|
@ -668,5 +680,5 @@ DATA = {
|
|||
}
|
||||
|
||||
|
||||
# ~ LICENSE_ACCEPT_BY = 'admin' # or admin
|
||||
# ~ LICENSE_ACCEPT_BY = 'user' # or admin
|
||||
# ~ LICENSE_SUPPRESS_ON_UPDATE = True
|
||||
|
|
540
easymacro.py
540
easymacro.py
|
@ -18,15 +18,26 @@
|
|||
# ~ along with ZAZ. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import ctypes
|
||||
import datetime
|
||||
import errno
|
||||
import getpass
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
from pathlib import Path, PurePath
|
||||
from pprint import pprint
|
||||
from subprocess import PIPE
|
||||
|
||||
import uno
|
||||
import unohelper
|
||||
|
@ -35,6 +46,7 @@ from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
|
|||
from com.sun.star.awt.MessageBoxResults import YES
|
||||
from com.sun.star.awt.PosSize import POSSIZE, SIZE
|
||||
from com.sun.star.awt import Size, Point
|
||||
from com.sun.star.datatransfer import XTransferable, DataFlavor
|
||||
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
|
||||
|
||||
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
|
||||
|
@ -44,31 +56,46 @@ from com.sun.star.awt import XActionListener
|
|||
from com.sun.star.awt import XMouseListener
|
||||
|
||||
|
||||
FILE_NAME_DEBUG = 'debug.log'
|
||||
MSG_LANG = {
|
||||
'es': {
|
||||
'OK': 'Aceptar',
|
||||
'Cancel': 'Cancelar',
|
||||
'Select file': 'Seleccionar archivo',
|
||||
}
|
||||
}
|
||||
|
||||
FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
|
||||
DATE = '%d/%m/%Y %H:%M:%S'
|
||||
|
||||
FILE_NAME_DEBUG = 'zaz-debug.log'
|
||||
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
|
||||
LOG_DATE = '%d/%m/%Y %H:%M:%S'
|
||||
LEVEL_ERROR = logging.getLevelName(logging.ERROR)
|
||||
LEVEL_DEBUG = logging.getLevelName(logging.DEBUG)
|
||||
LEVEL_INFO = logging.getLevelName(logging.INFO)
|
||||
logging.addLevelName(logging.ERROR, f'\033[1;41m{LEVEL_ERROR}\033[1;0m')
|
||||
logging.addLevelName(logging.DEBUG, f'\x1b[33m{LEVEL_DEBUG}\033[1;0m')
|
||||
logging.addLevelName(logging.INFO, f'\x1b[32m{LEVEL_INFO}\033[1;0m')
|
||||
logging.basicConfig(level=logging.DEBUG, format=FORMAT, datefmt=DATE)
|
||||
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
OS = platform.system()
|
||||
USER = getpass.getuser()
|
||||
PC = platform.node()
|
||||
DESKTOP = os.environ.get('DESKTOP_SESSION', '')
|
||||
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
|
||||
|
||||
IS_WIN = OS == 'Windows'
|
||||
LOG_NAME = 'ZAZ'
|
||||
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
|
||||
|
||||
CALC = 'calc'
|
||||
WRITER = 'writer'
|
||||
OBJ_CELL = 'ScCellObj'
|
||||
OBJ_RANGE = 'ScCellRangeObj'
|
||||
OBJ_RANGES = 'ScCellRangesObj'
|
||||
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
|
||||
|
||||
|
||||
CTX = uno.getComponentContext()
|
||||
SM = CTX.getServiceManager()
|
||||
|
||||
|
@ -96,17 +123,10 @@ def _get_config(key, node_name):
|
|||
return ''
|
||||
|
||||
|
||||
OS = sys.platform
|
||||
USER = getpass.getuser()
|
||||
PC = platform.node()
|
||||
|
||||
LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/')
|
||||
LANG = LANGUAGE.split('-')[0]
|
||||
NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product')
|
||||
VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product')
|
||||
# ~ DESKTOP = create_instance('com.sun.star.frame.Desktop', True)
|
||||
|
||||
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(
|
||||
sys.version, platform.platform(), '\n'.join(sys.path))
|
||||
|
||||
|
||||
def mri(obj):
|
||||
|
@ -120,7 +140,45 @@ def mri(obj):
|
|||
return
|
||||
|
||||
|
||||
def catch_exception(f):
|
||||
@wraps(f)
|
||||
def func(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except Exception as e:
|
||||
log.error(f.__name__, exc_info=True)
|
||||
return func
|
||||
|
||||
|
||||
class LogWin(object):
|
||||
|
||||
def __init__(self, doc):
|
||||
self.doc = doc
|
||||
|
||||
def write(self, info):
|
||||
text = self.doc.Text
|
||||
cursor = text.createTextCursor()
|
||||
cursor.gotoEnd(False)
|
||||
text.insertString(cursor, str(info), 0)
|
||||
return
|
||||
|
||||
|
||||
def info(data):
|
||||
log.info(data)
|
||||
return
|
||||
|
||||
|
||||
def debug(info):
|
||||
if IS_WIN:
|
||||
# ~ app = LOApp(self.ctx, self.sm, self.desktop, self.toolkit)
|
||||
# ~ doc = app.getDoc(FILE_NAME_DEBUG)
|
||||
# ~ if not doc:
|
||||
# ~ doc = app.newDoc(WRITER)
|
||||
# ~ out = OutputDoc(doc)
|
||||
# ~ sys.stdout = out
|
||||
pprint(info)
|
||||
return
|
||||
|
||||
log.debug(info)
|
||||
return
|
||||
|
||||
|
@ -130,14 +188,11 @@ def error(info):
|
|||
return
|
||||
|
||||
|
||||
def catch_exception(f):
|
||||
@wraps(f)
|
||||
def func(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except Exception as e:
|
||||
log.error(f.__name__, exc_info=True)
|
||||
return func
|
||||
def save_log(path, data):
|
||||
with open(path, 'a') as out:
|
||||
out.write('{} -{}- '.format(str(datetime.now())[:19], LOG_NAME))
|
||||
pprint(data, stream=out)
|
||||
return
|
||||
|
||||
|
||||
def run_in_thread(fn):
|
||||
|
@ -187,6 +242,10 @@ def get_desktop():
|
|||
return create_instance('com.sun.star.frame.Desktop', True)
|
||||
|
||||
|
||||
def get_dispatch():
|
||||
return create_instance('com.sun.star.frame.DispatchHelper')
|
||||
|
||||
|
||||
def get_temp_file():
|
||||
return tempfile.NamedTemporaryFile()
|
||||
|
||||
|
@ -203,6 +262,20 @@ def _path_system(path):
|
|||
return path
|
||||
|
||||
|
||||
def exists_app(name):
|
||||
try:
|
||||
dn = subprocess.DEVNULL
|
||||
subprocess.Popen([name, ''], stdout=dn, stderr=dn).terminate()
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def exists(path):
|
||||
return Path(path).exists()
|
||||
|
||||
|
||||
def get_type_doc(obj):
|
||||
services = {
|
||||
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
|
||||
|
@ -219,6 +292,11 @@ def get_type_doc(obj):
|
|||
return ''
|
||||
|
||||
|
||||
def _properties(values):
|
||||
p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
|
||||
return tuple(p)
|
||||
|
||||
|
||||
# ~ Custom classes
|
||||
|
||||
|
||||
|
@ -245,6 +323,10 @@ class LODocument(object):
|
|||
def title(self):
|
||||
return self.obj.getTitle()
|
||||
|
||||
@property
|
||||
def frame(self):
|
||||
return self._cc.getFrame()
|
||||
|
||||
@property
|
||||
def is_saved(self):
|
||||
return self.obj.hasLocation()
|
||||
|
@ -281,6 +363,29 @@ class LODocument(object):
|
|||
obj = self.obj.createInstance(name)
|
||||
return obj
|
||||
|
||||
def save(self, path='', **kwargs):
|
||||
opt = _properties(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()
|
||||
|
||||
|
||||
class LOCalc(LODocument):
|
||||
|
||||
|
@ -313,9 +418,14 @@ class LOCalc(LODocument):
|
|||
cell = LOCellRange(self.active[index].obj, self)
|
||||
return cell
|
||||
|
||||
# ~ def create_instance(self, name):
|
||||
# ~ obj = self.obj.createInstance(name)
|
||||
# ~ return obj
|
||||
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
|
||||
|
||||
|
||||
class LOCalcSheet(object):
|
||||
|
@ -464,6 +574,11 @@ class LOBasicIde(LODocument):
|
|||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
@property
|
||||
def selection(self):
|
||||
sel = self._cc.getSelection()
|
||||
return sel
|
||||
|
||||
|
||||
class LOCellRange(object):
|
||||
|
||||
|
@ -587,6 +702,10 @@ class LOCellRange(object):
|
|||
img.setSize(Size(w, h))
|
||||
return
|
||||
|
||||
def select(self):
|
||||
self.doc._cc.select(self.obj)
|
||||
return
|
||||
|
||||
|
||||
class EventsListenerBase(unohelper.Base, XEventListener):
|
||||
|
||||
|
@ -926,6 +1045,7 @@ class LODialog(object):
|
|||
}
|
||||
return classes[tipo](obj)
|
||||
|
||||
@catch_exception
|
||||
def add_control(self, properties):
|
||||
tipo = properties.pop('Type').lower()
|
||||
model = self.model.createInstance(self._get_control_model(tipo))
|
||||
|
@ -998,3 +1118,377 @@ def set_properties(model, properties):
|
|||
values = tuple(properties.values())
|
||||
model.setPropertyValues(keys, values)
|
||||
return
|
||||
|
||||
|
||||
def get_file(filters=(), multiple=False):
|
||||
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
|
||||
file_picker.setTitle(_('Select file'))
|
||||
file_picker.setMultiSelectionMode(multiple)
|
||||
|
||||
if filters:
|
||||
file_picker.setCurrentFilter(filters[0][0])
|
||||
for f in filters:
|
||||
file_picker.appendFilter(f[0], f[1])
|
||||
|
||||
if file_picker.execute():
|
||||
if multiple:
|
||||
return [_path_system(f) for f in file_picker.getSelectedFiles()]
|
||||
return _path_system(file_picker.getSelectedFiles()[0])
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def get_info_path(path):
|
||||
path, filename = os.path.split(path)
|
||||
name, extension = os.path.splitext(filename)
|
||||
return (path, filename, name, extension)
|
||||
|
||||
|
||||
def inputbox(message, default='', title=TITLE):
|
||||
|
||||
class ControllersInput(object):
|
||||
|
||||
def __init__(self, dlg):
|
||||
self.d = dlg
|
||||
|
||||
def cmd_ok_action(self, event):
|
||||
self.d.close(1)
|
||||
return
|
||||
|
||||
args = {
|
||||
'Title': title,
|
||||
'Width': 200,
|
||||
'Height': 80,
|
||||
}
|
||||
dlg = LODialog(args)
|
||||
dlg.events = ControllersInput(dlg)
|
||||
|
||||
args = {
|
||||
'Type': 'Label',
|
||||
'Name': 'lbl_msg',
|
||||
'Label': message,
|
||||
'Width': 140,
|
||||
'Height': 50,
|
||||
'X': 5,
|
||||
'Y': 5,
|
||||
'MultiLine': True,
|
||||
'Border': 1,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'Text',
|
||||
'Name': 'txt_value',
|
||||
'Text': default,
|
||||
'Width': 190,
|
||||
'Height': 15,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
dlg.txt_value.move(dlg.lbl_msg)
|
||||
|
||||
args = {
|
||||
'Type': 'button',
|
||||
'Name': 'cmd_ok',
|
||||
'Label': _('OK'),
|
||||
'Width': 40,
|
||||
'Height': 15,
|
||||
'DefaultButton': True,
|
||||
'PushButtonType': 1,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
dlg.cmd_ok.move(dlg.lbl_msg, 10, 0)
|
||||
|
||||
args = {
|
||||
'Type': 'button',
|
||||
'Name': 'cmd_cancel',
|
||||
'Label': _('Cancel'),
|
||||
'Width': 40,
|
||||
'Height': 15,
|
||||
'PushButtonType': 2,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
dlg.cmd_cancel.move(dlg.cmd_ok)
|
||||
|
||||
if dlg.open():
|
||||
return dlg.txt_value.value
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def new_doc(type_doc=CALC):
|
||||
path = 'private:factory/s{}'.format(type_doc)
|
||||
doc = get_desktop().loadComponentFromURL(path, '_default', 0, ())
|
||||
return _get_class_doc(doc)
|
||||
|
||||
|
||||
def open_doc(path, **kwargs):
|
||||
""" Open document in path
|
||||
Usually options:
|
||||
Hidden: True or False
|
||||
AsTemplate: True or False
|
||||
ReadOnly: True or False
|
||||
Password: super_secret
|
||||
MacroExecutionMode: 4 = Activate macros
|
||||
Preview: True or False
|
||||
|
||||
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1frame_1_1XComponentLoader.html
|
||||
http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
|
||||
"""
|
||||
path = _path_url(path)
|
||||
opt = _properties(kwargs)
|
||||
doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt)
|
||||
if doc is None:
|
||||
return
|
||||
|
||||
return _get_class_doc(doc)
|
||||
|
||||
|
||||
def open_file(path):
|
||||
if IS_WIN:
|
||||
os.startfile(path)
|
||||
else:
|
||||
subprocess.Popen(['xdg-open', path])
|
||||
return
|
||||
|
||||
|
||||
def join(*paths):
|
||||
return os.path.join(*paths)
|
||||
|
||||
|
||||
def is_dir(path):
|
||||
return Path(path).is_dir()
|
||||
|
||||
|
||||
def is_file(path):
|
||||
return Path(path).is_file()
|
||||
|
||||
|
||||
def get_file_size(path):
|
||||
return Path(path).stat().st_size
|
||||
|
||||
|
||||
def is_created(path):
|
||||
return is_file(path) and bool(get_file_size(path))
|
||||
|
||||
|
||||
def replace_ext(path, ext):
|
||||
path, _, name, _ = get_info_path(path)
|
||||
return '{}/{}.{}'.format(path, name, ext)
|
||||
|
||||
|
||||
def zip_names(path):
|
||||
with zipfile.ZipFile(path) as z:
|
||||
names = z.namelist()
|
||||
return names
|
||||
|
||||
|
||||
def run(command, wait=False):
|
||||
# ~ debug(command)
|
||||
# ~ debug(shlex.split(command))
|
||||
try:
|
||||
if wait:
|
||||
# ~ p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
|
||||
# ~ p.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 = ("run [ERROR]: output = %s, error code = %s\n"
|
||||
% (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)
|
||||
|
||||
|
||||
def zip(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)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def kill(path):
|
||||
p = Path(path)
|
||||
if p.is_file():
|
||||
try:
|
||||
p.unlink()
|
||||
except:
|
||||
pass
|
||||
elif p.is_dir():
|
||||
p.rmdir()
|
||||
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
|
||||
|
||||
|
||||
def set_clipboard(text):
|
||||
ts = TextTransferable(text)
|
||||
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
|
||||
sc.setContents(ts, None)
|
||||
return
|
||||
|
||||
|
||||
def copy(doc=None):
|
||||
if doc is None:
|
||||
doc = get_document()
|
||||
if hasattr(doc, 'frame'):
|
||||
frame = doc.frame
|
||||
else:
|
||||
frame = doc.getCurrentController().getFrame()
|
||||
dispatch = get_dispatch()
|
||||
dispatch.executeDispatch(frame, '.uno:Copy', '', 0, ())
|
||||
return
|
||||
|
||||
|
||||
def get_epoch():
|
||||
now = datetime.datetime.now()
|
||||
return int(time.mktime(now.timetuple()))
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<description
|
||||
xmlns="http://openoffice.org/extensions/update/2006"
|
||||
xmlns:d="http://openoffice.org/extensions/description/2006"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<identifier value="net.elmau.zaz.BarCode" />
|
||||
<version value="0.2.0" />
|
||||
|
||||
<update-download>
|
||||
<src xlink:href="https://gitlab.com/mauriciobaeza/zaz-barcode/raw/master/files/ZAZBarCode_v0.2.0.oxt"/>
|
||||
</update-download>
|
||||
<release-notes>
|
||||
</release-notes>
|
||||
|
||||
</description>
|
|
@ -3,14 +3,14 @@
|
|||
<node oor:name="AddonUI">
|
||||
<node oor:name="OfficeMenuBar">
|
||||
<node oor:name="net.elmau.zaz.BarCode" oor:op="replace">
|
||||
<prop oor:name="Title" oor:type="xs:string">
|
||||
<value xml:lang="en">ZAZ BarCode</value>
|
||||
<prop oor:name="Title" oor:type="xs:string">
|
||||
<value xml:lang="en">ZAZ BarCode</value>
|
||||
<value xml:lang="es">ZAZ BarCode</value>
|
||||
</prop>
|
||||
<prop oor:name="Target" oor:type="xs:string">
|
||||
<value>_self</value>
|
||||
</prop>
|
||||
<node oor:name="Submenu">
|
||||
</prop>
|
||||
<prop oor:name="Target" oor:type="xs:string">
|
||||
<value>_self</value>
|
||||
</prop>
|
||||
<node oor:name="Submenu">
|
||||
<node oor:name="net.elmau.zaz.BarCode.101" oor:op="replace">
|
||||
<prop oor:name="Title" oor:type="xs:string">
|
||||
<value xml:lang="en">Insert BarCode</value>
|
||||
|
@ -318,7 +318,7 @@
|
|||
</prop>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
||||
</node>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import gettext
|
||||
import uno
|
||||
import unohelper
|
||||
from com.sun.star.task import XJobExecutor
|
||||
import easymacro as app
|
||||
|
||||
import qrcode
|
||||
import qrcode.image.svg as svg
|
||||
from barcode import generate
|
||||
|
||||
import easymacro as app
|
||||
|
||||
|
||||
ID_EXTENSION = 'net.elmau.zaz.BarCode'
|
||||
SERVICE = ('com.sun.star.task.Job',)
|
||||
|
@ -15,20 +15,31 @@ TITLE = 'ZAZ BarCode'
|
|||
QR = 'qrcode'
|
||||
|
||||
|
||||
p, *_ = app.get_info_path(__file__)
|
||||
path_locales = app.join(p, 'locales')
|
||||
try:
|
||||
lang = gettext.translation('base', path_locales, languages=[app.LANG])
|
||||
lang.install()
|
||||
_ = lang.gettext
|
||||
except Exception as e:
|
||||
app.error(e)
|
||||
|
||||
|
||||
class Controllers(object):
|
||||
|
||||
@app.catch_exception
|
||||
def __init__(self, dlg):
|
||||
self.d = dlg
|
||||
|
||||
def button_action(self, event):
|
||||
if not self.d.listbox.value:
|
||||
self.d.listbox.set_focus()
|
||||
msg = 'Select barcode type'
|
||||
msg = _('Select barcode type')
|
||||
app.warning(msg, TITLE)
|
||||
return
|
||||
if not self.d.text.value.strip():
|
||||
self.d.text.set_focus()
|
||||
msg = 'Data field is mandatory'
|
||||
msg = _('Data field is mandatory')
|
||||
app.warning(msg, TITLE)
|
||||
return
|
||||
|
||||
|
@ -43,6 +54,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
|
|||
self._data = ''
|
||||
self._type = ''
|
||||
|
||||
@app.catch_exception
|
||||
def trigger(self, args):
|
||||
self._type = args
|
||||
if args == 'ask' and not self._get_values():
|
||||
|
@ -79,7 +91,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
|
|||
if not self._data:
|
||||
self._data = cell.value
|
||||
if not self._data:
|
||||
msg = 'Select data'
|
||||
msg = _('Select data')
|
||||
self._show_error(msg)
|
||||
return
|
||||
|
||||
|
@ -101,7 +113,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
|
|||
if not self._data:
|
||||
self._data = sel.string
|
||||
if not self._data:
|
||||
msg = 'Select data'
|
||||
msg = _('Select data')
|
||||
self._show_error(msg)
|
||||
return
|
||||
|
||||
|
@ -133,7 +145,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
|
|||
return
|
||||
|
||||
def _show_error(self, error):
|
||||
msg = 'Error in: {}\n\n{}'.format(self._type, error)
|
||||
msg = _('Error in: {}\n\n{}').format(self._type, error)
|
||||
app.error(error)
|
||||
app.errorbox(msg, TITLE)
|
||||
return
|
||||
|
@ -151,12 +163,11 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
|
|||
args = {
|
||||
'Type': 'Label',
|
||||
'Name': 'lbl_barcode',
|
||||
'Label': 'Select barcode type',
|
||||
'Width': 55,
|
||||
'Label': _('~Select barcode type'),
|
||||
'Width': 70,
|
||||
'Height': 15,
|
||||
'X': 10,
|
||||
'Y': 10,
|
||||
'Align': 2,
|
||||
'VerticalAlign': 1,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
@ -164,8 +175,8 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
|
|||
args = {
|
||||
'Type': 'Label',
|
||||
'Name': 'lbl_data',
|
||||
'Label': 'Capture data for barcode',
|
||||
'Width': 70,
|
||||
'Label': _('~Capture data for barcode'),
|
||||
'Width': 100,
|
||||
'Height': 15,
|
||||
'VerticalAlign': 1,
|
||||
}
|
||||
|
@ -174,7 +185,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
|
|||
args = {
|
||||
'Type': 'ListBox',
|
||||
'Name': 'listbox',
|
||||
'Width': 80,
|
||||
'Width': 65,
|
||||
'Height': 15,
|
||||
'Dropdown': True,
|
||||
}
|
||||
|
@ -197,7 +208,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
|
|||
args = {
|
||||
'Type': 'Button',
|
||||
'Name': 'button',
|
||||
'Label': 'Insert Barcode',
|
||||
'Label': _('~Insert Barcode'),
|
||||
'Width': 60,
|
||||
'Height': 15,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<description xmlns="http://openoffice.org/extensions/description/2006" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:d="http://openoffice.org/extensions/description/2006">
|
||||
<identifier value="net.elmau.zaz.BarCode" />
|
||||
<version value="0.1.0" />
|
||||
<version value="0.2.0" />
|
||||
<display-name>
|
||||
<name lang="en">ZAZ Bar Code</name>
|
||||
<name lang="es">ZAZ Códigos de Barras</name>
|
||||
|
@ -23,4 +23,7 @@
|
|||
<license-text xlink:href="registration/license_es.txt" lang="es" />
|
||||
</simple-license>
|
||||
</registration>
|
||||
<update-information>
|
||||
<src xlink:href="https://gitlab.com/mauriciobaeza/zaz-barcode/raw/master/files/zazbarcode.update.xml" />
|
||||
</update-information>
|
||||
</description>
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: 2019-09-14 14:23-0500\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
||||
#: source/ZAZBarCode.py:38
|
||||
msgid "Select barcode type"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:43
|
||||
msgid "Data field is mandatory"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:95 source/ZAZBarCode.py:117
|
||||
msgid "Select data"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:149
|
||||
msgid ""
|
||||
"Error in: {}\n"
|
||||
"\n"
|
||||
"{}"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:167
|
||||
msgid "~Select barcode type"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:179
|
||||
msgid "~Capture data for barcode"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:212
|
||||
msgid "~Insert Barcode"
|
||||
msgstr ""
|
||||
|
Binary file not shown.
|
@ -0,0 +1,49 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2019-09-14 14:23-0500\n"
|
||||
"PO-Revision-Date: 2019-09-14 14:45-0500\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 2.2.1\n"
|
||||
"Last-Translator: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: en\n"
|
||||
|
||||
#: source/ZAZBarCode.py:38
|
||||
msgid "Select barcode type"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:43
|
||||
msgid "Data field is mandatory"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:95 source/ZAZBarCode.py:117
|
||||
msgid "Select data"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:149
|
||||
msgid ""
|
||||
"Error in: {}\n"
|
||||
"\n"
|
||||
"{}"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:167
|
||||
msgid "~Select barcode type"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:179
|
||||
msgid "~Capture data for barcode"
|
||||
msgstr ""
|
||||
|
||||
#: source/ZAZBarCode.py:212
|
||||
msgid "~Insert Barcode"
|
||||
msgstr ""
|
Binary file not shown.
|
@ -0,0 +1,52 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2019-09-14 14:23-0500\n"
|
||||
"PO-Revision-Date: 2019-09-14 14:44-0500\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 2.2.1\n"
|
||||
"Last-Translator: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: es\n"
|
||||
|
||||
#: source/ZAZBarCode.py:38
|
||||
msgid "Select barcode type"
|
||||
msgstr "Seleccione el tipo de código de barras"
|
||||
|
||||
#: source/ZAZBarCode.py:43
|
||||
msgid "Data field is mandatory"
|
||||
msgstr "Los datos son obligatorios"
|
||||
|
||||
#: source/ZAZBarCode.py:95 source/ZAZBarCode.py:117
|
||||
msgid "Select data"
|
||||
msgstr "Seleccione los datos"
|
||||
|
||||
#: source/ZAZBarCode.py:149
|
||||
msgid ""
|
||||
"Error in: {}\n"
|
||||
"\n"
|
||||
"{}"
|
||||
msgstr ""
|
||||
"Error en: {}\n"
|
||||
"\n"
|
||||
"{}"
|
||||
|
||||
#: source/ZAZBarCode.py:167
|
||||
msgid "~Select barcode type"
|
||||
msgstr "~Tipo de código de barras"
|
||||
|
||||
#: source/ZAZBarCode.py:179
|
||||
msgid "~Capture data for barcode"
|
||||
msgstr "~Datos del código de barras"
|
||||
|
||||
#: source/ZAZBarCode.py:212
|
||||
msgid "~Insert Barcode"
|
||||
msgstr "~Insertar Código"
|
|
@ -18,15 +18,26 @@
|
|||
# ~ along with ZAZ. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import ctypes
|
||||
import datetime
|
||||
import errno
|
||||
import getpass
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
from pathlib import Path, PurePath
|
||||
from pprint import pprint
|
||||
from subprocess import PIPE
|
||||
|
||||
import uno
|
||||
import unohelper
|
||||
|
@ -35,6 +46,7 @@ from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
|
|||
from com.sun.star.awt.MessageBoxResults import YES
|
||||
from com.sun.star.awt.PosSize import POSSIZE, SIZE
|
||||
from com.sun.star.awt import Size, Point
|
||||
from com.sun.star.datatransfer import XTransferable, DataFlavor
|
||||
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
|
||||
|
||||
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
|
||||
|
@ -44,31 +56,46 @@ from com.sun.star.awt import XActionListener
|
|||
from com.sun.star.awt import XMouseListener
|
||||
|
||||
|
||||
FILE_NAME_DEBUG = 'debug.log'
|
||||
MSG_LANG = {
|
||||
'es': {
|
||||
'OK': 'Aceptar',
|
||||
'Cancel': 'Cancelar',
|
||||
'Select file': 'Seleccionar archivo',
|
||||
}
|
||||
}
|
||||
|
||||
FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
|
||||
DATE = '%d/%m/%Y %H:%M:%S'
|
||||
|
||||
FILE_NAME_DEBUG = 'zaz-debug.log'
|
||||
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
|
||||
LOG_DATE = '%d/%m/%Y %H:%M:%S'
|
||||
LEVEL_ERROR = logging.getLevelName(logging.ERROR)
|
||||
LEVEL_DEBUG = logging.getLevelName(logging.DEBUG)
|
||||
LEVEL_INFO = logging.getLevelName(logging.INFO)
|
||||
logging.addLevelName(logging.ERROR, f'\033[1;41m{LEVEL_ERROR}\033[1;0m')
|
||||
logging.addLevelName(logging.DEBUG, f'\x1b[33m{LEVEL_DEBUG}\033[1;0m')
|
||||
logging.addLevelName(logging.INFO, f'\x1b[32m{LEVEL_INFO}\033[1;0m')
|
||||
logging.basicConfig(level=logging.DEBUG, format=FORMAT, datefmt=DATE)
|
||||
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
OS = platform.system()
|
||||
USER = getpass.getuser()
|
||||
PC = platform.node()
|
||||
DESKTOP = os.environ.get('DESKTOP_SESSION', '')
|
||||
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
|
||||
|
||||
IS_WIN = OS == 'Windows'
|
||||
LOG_NAME = 'ZAZ'
|
||||
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
|
||||
|
||||
CALC = 'calc'
|
||||
WRITER = 'writer'
|
||||
OBJ_CELL = 'ScCellObj'
|
||||
OBJ_RANGE = 'ScCellRangeObj'
|
||||
OBJ_RANGES = 'ScCellRangesObj'
|
||||
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
|
||||
|
||||
|
||||
CTX = uno.getComponentContext()
|
||||
SM = CTX.getServiceManager()
|
||||
|
||||
|
@ -96,17 +123,10 @@ def _get_config(key, node_name):
|
|||
return ''
|
||||
|
||||
|
||||
OS = sys.platform
|
||||
USER = getpass.getuser()
|
||||
PC = platform.node()
|
||||
|
||||
LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/')
|
||||
LANG = LANGUAGE.split('-')[0]
|
||||
NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product')
|
||||
VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product')
|
||||
# ~ DESKTOP = create_instance('com.sun.star.frame.Desktop', True)
|
||||
|
||||
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(
|
||||
sys.version, platform.platform(), '\n'.join(sys.path))
|
||||
|
||||
|
||||
def mri(obj):
|
||||
|
@ -120,7 +140,45 @@ def mri(obj):
|
|||
return
|
||||
|
||||
|
||||
def catch_exception(f):
|
||||
@wraps(f)
|
||||
def func(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except Exception as e:
|
||||
log.error(f.__name__, exc_info=True)
|
||||
return func
|
||||
|
||||
|
||||
class LogWin(object):
|
||||
|
||||
def __init__(self, doc):
|
||||
self.doc = doc
|
||||
|
||||
def write(self, info):
|
||||
text = self.doc.Text
|
||||
cursor = text.createTextCursor()
|
||||
cursor.gotoEnd(False)
|
||||
text.insertString(cursor, str(info), 0)
|
||||
return
|
||||
|
||||
|
||||
def info(data):
|
||||
log.info(data)
|
||||
return
|
||||
|
||||
|
||||
def debug(info):
|
||||
if IS_WIN:
|
||||
# ~ app = LOApp(self.ctx, self.sm, self.desktop, self.toolkit)
|
||||
# ~ doc = app.getDoc(FILE_NAME_DEBUG)
|
||||
# ~ if not doc:
|
||||
# ~ doc = app.newDoc(WRITER)
|
||||
# ~ out = OutputDoc(doc)
|
||||
# ~ sys.stdout = out
|
||||
pprint(info)
|
||||
return
|
||||
|
||||
log.debug(info)
|
||||
return
|
||||
|
||||
|
@ -130,14 +188,11 @@ def error(info):
|
|||
return
|
||||
|
||||
|
||||
def catch_exception(f):
|
||||
@wraps(f)
|
||||
def func(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except Exception as e:
|
||||
log.error(f.__name__, exc_info=True)
|
||||
return func
|
||||
def save_log(path, data):
|
||||
with open(path, 'a') as out:
|
||||
out.write('{} -{}- '.format(str(datetime.now())[:19], LOG_NAME))
|
||||
pprint(data, stream=out)
|
||||
return
|
||||
|
||||
|
||||
def run_in_thread(fn):
|
||||
|
@ -187,6 +242,10 @@ def get_desktop():
|
|||
return create_instance('com.sun.star.frame.Desktop', True)
|
||||
|
||||
|
||||
def get_dispatch():
|
||||
return create_instance('com.sun.star.frame.DispatchHelper')
|
||||
|
||||
|
||||
def get_temp_file():
|
||||
return tempfile.NamedTemporaryFile()
|
||||
|
||||
|
@ -203,6 +262,20 @@ def _path_system(path):
|
|||
return path
|
||||
|
||||
|
||||
def exists_app(name):
|
||||
try:
|
||||
dn = subprocess.DEVNULL
|
||||
subprocess.Popen([name, ''], stdout=dn, stderr=dn).terminate()
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def exists(path):
|
||||
return Path(path).exists()
|
||||
|
||||
|
||||
def get_type_doc(obj):
|
||||
services = {
|
||||
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
|
||||
|
@ -219,6 +292,11 @@ def get_type_doc(obj):
|
|||
return ''
|
||||
|
||||
|
||||
def _properties(values):
|
||||
p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
|
||||
return tuple(p)
|
||||
|
||||
|
||||
# ~ Custom classes
|
||||
|
||||
|
||||
|
@ -245,6 +323,10 @@ class LODocument(object):
|
|||
def title(self):
|
||||
return self.obj.getTitle()
|
||||
|
||||
@property
|
||||
def frame(self):
|
||||
return self._cc.getFrame()
|
||||
|
||||
@property
|
||||
def is_saved(self):
|
||||
return self.obj.hasLocation()
|
||||
|
@ -281,6 +363,29 @@ class LODocument(object):
|
|||
obj = self.obj.createInstance(name)
|
||||
return obj
|
||||
|
||||
def save(self, path='', **kwargs):
|
||||
opt = _properties(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()
|
||||
|
||||
|
||||
class LOCalc(LODocument):
|
||||
|
||||
|
@ -313,9 +418,14 @@ class LOCalc(LODocument):
|
|||
cell = LOCellRange(self.active[index].obj, self)
|
||||
return cell
|
||||
|
||||
# ~ def create_instance(self, name):
|
||||
# ~ obj = self.obj.createInstance(name)
|
||||
# ~ return obj
|
||||
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
|
||||
|
||||
|
||||
class LOCalcSheet(object):
|
||||
|
@ -464,6 +574,11 @@ class LOBasicIde(LODocument):
|
|||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
@property
|
||||
def selection(self):
|
||||
sel = self._cc.getSelection()
|
||||
return sel
|
||||
|
||||
|
||||
class LOCellRange(object):
|
||||
|
||||
|
@ -587,6 +702,10 @@ class LOCellRange(object):
|
|||
img.setSize(Size(w, h))
|
||||
return
|
||||
|
||||
def select(self):
|
||||
self.doc._cc.select(self.obj)
|
||||
return
|
||||
|
||||
|
||||
class EventsListenerBase(unohelper.Base, XEventListener):
|
||||
|
||||
|
@ -926,6 +1045,7 @@ class LODialog(object):
|
|||
}
|
||||
return classes[tipo](obj)
|
||||
|
||||
@catch_exception
|
||||
def add_control(self, properties):
|
||||
tipo = properties.pop('Type').lower()
|
||||
model = self.model.createInstance(self._get_control_model(tipo))
|
||||
|
@ -998,3 +1118,377 @@ def set_properties(model, properties):
|
|||
values = tuple(properties.values())
|
||||
model.setPropertyValues(keys, values)
|
||||
return
|
||||
|
||||
|
||||
def get_file(filters=(), multiple=False):
|
||||
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
|
||||
file_picker.setTitle(_('Select file'))
|
||||
file_picker.setMultiSelectionMode(multiple)
|
||||
|
||||
if filters:
|
||||
file_picker.setCurrentFilter(filters[0][0])
|
||||
for f in filters:
|
||||
file_picker.appendFilter(f[0], f[1])
|
||||
|
||||
if file_picker.execute():
|
||||
if multiple:
|
||||
return [_path_system(f) for f in file_picker.getSelectedFiles()]
|
||||
return _path_system(file_picker.getSelectedFiles()[0])
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def get_info_path(path):
|
||||
path, filename = os.path.split(path)
|
||||
name, extension = os.path.splitext(filename)
|
||||
return (path, filename, name, extension)
|
||||
|
||||
|
||||
def inputbox(message, default='', title=TITLE):
|
||||
|
||||
class ControllersInput(object):
|
||||
|
||||
def __init__(self, dlg):
|
||||
self.d = dlg
|
||||
|
||||
def cmd_ok_action(self, event):
|
||||
self.d.close(1)
|
||||
return
|
||||
|
||||
args = {
|
||||
'Title': title,
|
||||
'Width': 200,
|
||||
'Height': 80,
|
||||
}
|
||||
dlg = LODialog(args)
|
||||
dlg.events = ControllersInput(dlg)
|
||||
|
||||
args = {
|
||||
'Type': 'Label',
|
||||
'Name': 'lbl_msg',
|
||||
'Label': message,
|
||||
'Width': 140,
|
||||
'Height': 50,
|
||||
'X': 5,
|
||||
'Y': 5,
|
||||
'MultiLine': True,
|
||||
'Border': 1,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'Text',
|
||||
'Name': 'txt_value',
|
||||
'Text': default,
|
||||
'Width': 190,
|
||||
'Height': 15,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
dlg.txt_value.move(dlg.lbl_msg)
|
||||
|
||||
args = {
|
||||
'Type': 'button',
|
||||
'Name': 'cmd_ok',
|
||||
'Label': _('OK'),
|
||||
'Width': 40,
|
||||
'Height': 15,
|
||||
'DefaultButton': True,
|
||||
'PushButtonType': 1,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
dlg.cmd_ok.move(dlg.lbl_msg, 10, 0)
|
||||
|
||||
args = {
|
||||
'Type': 'button',
|
||||
'Name': 'cmd_cancel',
|
||||
'Label': _('Cancel'),
|
||||
'Width': 40,
|
||||
'Height': 15,
|
||||
'PushButtonType': 2,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
dlg.cmd_cancel.move(dlg.cmd_ok)
|
||||
|
||||
if dlg.open():
|
||||
return dlg.txt_value.value
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def new_doc(type_doc=CALC):
|
||||
path = 'private:factory/s{}'.format(type_doc)
|
||||
doc = get_desktop().loadComponentFromURL(path, '_default', 0, ())
|
||||
return _get_class_doc(doc)
|
||||
|
||||
|
||||
def open_doc(path, **kwargs):
|
||||
""" Open document in path
|
||||
Usually options:
|
||||
Hidden: True or False
|
||||
AsTemplate: True or False
|
||||
ReadOnly: True or False
|
||||
Password: super_secret
|
||||
MacroExecutionMode: 4 = Activate macros
|
||||
Preview: True or False
|
||||
|
||||
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1frame_1_1XComponentLoader.html
|
||||
http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
|
||||
"""
|
||||
path = _path_url(path)
|
||||
opt = _properties(kwargs)
|
||||
doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt)
|
||||
if doc is None:
|
||||
return
|
||||
|
||||
return _get_class_doc(doc)
|
||||
|
||||
|
||||
def open_file(path):
|
||||
if IS_WIN:
|
||||
os.startfile(path)
|
||||
else:
|
||||
subprocess.Popen(['xdg-open', path])
|
||||
return
|
||||
|
||||
|
||||
def join(*paths):
|
||||
return os.path.join(*paths)
|
||||
|
||||
|
||||
def is_dir(path):
|
||||
return Path(path).is_dir()
|
||||
|
||||
|
||||
def is_file(path):
|
||||
return Path(path).is_file()
|
||||
|
||||
|
||||
def get_file_size(path):
|
||||
return Path(path).stat().st_size
|
||||
|
||||
|
||||
def is_created(path):
|
||||
return is_file(path) and bool(get_file_size(path))
|
||||
|
||||
|
||||
def replace_ext(path, ext):
|
||||
path, _, name, _ = get_info_path(path)
|
||||
return '{}/{}.{}'.format(path, name, ext)
|
||||
|
||||
|
||||
def zip_names(path):
|
||||
with zipfile.ZipFile(path) as z:
|
||||
names = z.namelist()
|
||||
return names
|
||||
|
||||
|
||||
def run(command, wait=False):
|
||||
# ~ debug(command)
|
||||
# ~ debug(shlex.split(command))
|
||||
try:
|
||||
if wait:
|
||||
# ~ p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
|
||||
# ~ p.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 = ("run [ERROR]: output = %s, error code = %s\n"
|
||||
% (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)
|
||||
|
||||
|
||||
def zip(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)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def kill(path):
|
||||
p = Path(path)
|
||||
if p.is_file():
|
||||
try:
|
||||
p.unlink()
|
||||
except:
|
||||
pass
|
||||
elif p.is_dir():
|
||||
p.rmdir()
|
||||
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
|
||||
|
||||
|
||||
def set_clipboard(text):
|
||||
ts = TextTransferable(text)
|
||||
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
|
||||
sc.setContents(ts, None)
|
||||
return
|
||||
|
||||
|
||||
def copy(doc=None):
|
||||
if doc is None:
|
||||
doc = get_document()
|
||||
if hasattr(doc, 'frame'):
|
||||
frame = doc.frame
|
||||
else:
|
||||
frame = doc.getCurrentController().getFrame()
|
||||
dispatch = get_dispatch()
|
||||
dispatch.executeDispatch(frame, '.uno:Copy', '', 0, ())
|
||||
return
|
||||
|
||||
|
||||
def get_epoch():
|
||||
now = datetime.datetime.now()
|
||||
return int(time.mktime(now.timetuple()))
|
||||
|
|
Loading…
Reference in New Issue