Merge branch 'develop'

Fix #2, add 3#
This commit is contained in:
Mauricio Baeza 2019-09-20 13:10:27 -05:00
commit 1f323820b0
13 changed files with 781 additions and 82 deletions

View File

@ -1,3 +1,6 @@
v 0.3.0 [18-sep-2019]
- Add support for generate barcode by code
v 0.2.1 [16-sep-2019] v 0.2.1 [16-sep-2019]
- Fix #1 - Fix #1

View File

@ -31,6 +31,9 @@ https://github.com/WhyNotHugo/python-barcode
https://github.com/lincolnloop/python-qrcode https://github.com/lincolnloop/python-qrcode
### Software libre, no gratis
This extension have a cost of maintenance of 1 euro every year. This extension have a cost of maintenance of 1 euro every year.
BCH: `1RPLWHJW34p7pMQV1ft4x7eWhAYw69Dsb` BCH: `1RPLWHJW34p7pMQV1ft4x7eWhAYw69Dsb`
@ -40,3 +43,8 @@ BTC: `3Fe4JuADrAK8Qs7GDAxbSXR8E54avwZJLW`
* [See the wiki](https://gitlab.com/mauriciobaeza/zaz-barcode/wikis/home) * [See the wiki](https://gitlab.com/mauriciobaeza/zaz-barcode/wikis/home)
* [Mira la wiki](https://gitlab.com/mauriciobaeza/zaz-barcode/wikis/home_es) * [Mira la wiki](https://gitlab.com/mauriciobaeza/zaz-barcode/wikis/home_es)
Thanks for translations:
Esperanto: [Guillermo Molleda](https://gitlab.com/gmolleda)

View File

@ -1,2 +1,2 @@
0.2.1 0.3.0

View File

@ -26,7 +26,7 @@ import logging
TYPE_EXTENSION = 1 TYPE_EXTENSION = 1
# ~ https://semver.org/ # ~ https://semver.org/
VERSION = '0.2.1' VERSION = '0.3.0'
# ~ Your great extension name, not used spaces # ~ Your great extension name, not used spaces
NAME = 'ZAZBarCode' NAME = 'ZAZBarCode'
@ -697,7 +697,6 @@ NODE_SHORTCUT = """ {0}<node oor:name="{1}" oor:op="fuse">
{0}</node> {0}</node>
""" """
NODE_SHORTCUTS = '' NODE_SHORTCUTS = ''
if TYPE_EXTENSION == 1: if TYPE_EXTENSION == 1:
node_global = [] node_global = []

View File

@ -22,10 +22,13 @@ import ctypes
import datetime import datetime
import errno import errno
import getpass import getpass
import json
import logging import logging
import os import os
import platform import platform
import re
import shlex import shlex
import shutil
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
@ -33,6 +36,8 @@ import threading
import time import time
import zipfile import zipfile
from collections import OrderedDict
from collections.abc import MutableMapping
from datetime import datetime from datetime import datetime
from functools import wraps from functools import wraps
from pathlib import Path, PurePath from pathlib import Path, PurePath
@ -65,26 +70,18 @@ MSG_LANG = {
} }
FILE_NAME_DEBUG = 'zaz-debug.log'
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
log = logging.getLogger(__name__)
OS = platform.system() OS = platform.system()
USER = getpass.getuser() USER = getpass.getuser()
PC = platform.node() PC = platform.node()
DESKTOP = os.environ.get('DESKTOP_SESSION', '') DESKTOP = os.environ.get('DESKTOP_SESSION', '')
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path)) INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
IS_WIN = OS == 'Windows' IS_WIN = OS == 'Windows'
LOG_NAME = 'ZAZ' LOG_NAME = 'ZAZ'
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16' CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
CALC = 'calc' CALC = 'calc'
WRITER = 'writer' WRITER = 'writer'
OBJ_CELL = 'ScCellObj' OBJ_CELL = 'ScCellObj'
@ -92,6 +89,46 @@ OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj' OBJ_RANGES = 'ScCellRangesObj'
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES) OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
TYPE_DOC = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument',
'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE',
}
NODE_MENUBAR = 'private:resource/menubar/menubar'
MENUS_CALC = {
'file': '.uno:PickList',
'edit': '.uno:EditMenu',
'view': '.uno:ViewMenu',
'insert': '.uno:InsertMenu',
'format': '.uno:FormatMenu',
'styles': '.uno:FormatStylesMenu',
'sheet': '.uno:SheetMenu',
'data': '.uno:DataMenu',
'tools': '.uno:ToolsMenu',
'windows': '.uno:WindowList',
'help': '.uno:HelpMenu',
}
MENUS_APP = {
'calc': MENUS_CALC,
}
FILE_NAME_DEBUG = 'zaz-debug.log'
FILE_NAME_CONFIG = 'zaz-config.json'
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
log = logging.getLogger(__name__)
CTX = uno.getComponentContext() CTX = uno.getComponentContext()
SM = CTX.getServiceManager() SM = CTX.getServiceManager()
@ -105,7 +142,7 @@ def create_instance(name, with_context=False):
return instance return instance
def _get_config(key, node_name): def _get_app_config(key, node_name):
name = 'com.sun.star.configuration.ConfigurationProvider' name = 'com.sun.star.configuration.ConfigurationProvider'
service = 'com.sun.star.configuration.ConfigurationAccess' service = 'com.sun.star.configuration.ConfigurationAccess'
cp = create_instance(name, True) cp = create_instance(name, True)
@ -120,10 +157,10 @@ def _get_config(key, node_name):
return '' return ''
LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/') LANGUAGE = _get_app_config('ooLocale', 'org.openoffice.Setup/L10N/')
LANG = LANGUAGE.split('-')[0] LANG = LANGUAGE.split('-')[0]
NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product') NAME = TITLE = _get_app_config('ooName', 'org.openoffice.Setup/Product')
VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product') VERSION = _get_app_config('ooSetupVersion', 'org.openoffice.Setup/Product')
def mri(obj): def mri(obj):
@ -202,8 +239,34 @@ def run_in_thread(fn):
return run return run
def sleep(sec): def get_config(key=''):
time.sleep(sec) values = {}
path = join(get_config_path('UserConfig'), FILE_NAME_CONFIG)
if not exists_path(path):
return values
with open(path, 'r', encoding='utf-8') as fh:
data = fh.read()
if data:
values = json.loads(data)
if key:
return values.get(key, None)
return values
def set_config(key, value):
path = join(get_config_path('UserConfig'), FILE_NAME_CONFIG)
values = get_config()
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 return
@ -278,30 +341,116 @@ def exists_app(name):
return False return False
return True return True
# ~ Delete
def exists(path): def exists(path):
return Path(path).exists() return Path(path).exists()
def exists_path(path):
return Path(path).exists()
def get_type_doc(obj): def get_type_doc(obj):
services = { # ~ services = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument', # ~ 'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument', # ~ 'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument', # ~ 'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument', # ~ 'draw': 'com.sun.star.drawing.DrawingDocument',
'base': 'com.sun.star.sdb.OfficeDatabaseDocument', # ~ 'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
'math': 'com.sun.star.formula.FormulaProperties', # ~ 'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE', # ~ 'basic': 'com.sun.star.script.BasicIDE',
} # ~ }
for k, v in services.items(): for k, v in TYPE_DOC.items():
if obj.supportsService(v): if obj.supportsService(v):
return k return k
return '' return ''
def _properties(values): # ~ def _properties(values):
p = [PropertyValue(Name=n, Value=v) for n, v in values.items()] # ~ p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
return tuple(p) # ~ return tuple(p)
def dict_to_property(values, uno_any=False):
ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
if uno_any:
ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
return ps
def property_to_dict(values):
d = {i.Name: i.Value for i in values}
return d
# ~ Third classes
# ~ https://github.com/psf/requests/blob/v2.22.0/requests/structures.py
class CaseInsensitiveDict(MutableMapping):
"""A case-insensitive ``dict``-like object.
Implements all methods and operations of
``MutableMapping`` as well as dict's ``copy``. Also
provides ``lower_items``.
All keys are expected to be strings. The structure remembers the
case of the last key to be set, and ``iter(instance)``,
``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``
will contain case-sensitive keys. However, querying and contains
testing is case insensitive::
cid = CaseInsensitiveDict()
cid['Accept'] = 'application/json'
cid['aCCEPT'] == 'application/json' # True
list(cid) == ['Accept'] # True
For example, ``headers['content-encoding']`` will return the
value of a ``'Content-Encoding'`` response header, regardless
of how the header name was originally stored.
If the constructor, ``.update``, or equality comparison
operations are given keys that have equal ``.lower()``s, the
behavior is undefined.
"""
def __init__(self, data=None, **kwargs):
self._store = OrderedDict()
if data is None:
data = {}
self.update(data, **kwargs)
def __setitem__(self, key, value):
# Use the lowercased key for lookups, but store the actual
# key alongside the value.
self._store[key.lower()] = (key, value)
def __getitem__(self, key):
return self._store[key.lower()][1]
def __delitem__(self, key):
del self._store[key.lower()]
def __iter__(self):
return (casedkey for casedkey, mappedvalue in self._store.values())
def __len__(self):
return len(self._store)
def lower_items(self):
"""Like iteritems(), but with all lowercase keys."""
return (
(lowerkey, keyval[1])
for (lowerkey, keyval)
in self._store.items()
)
def __eq__(self, other):
if isinstance(other, Mapping):
other = CaseInsensitiveDict(other)
else:
return NotImplemented
# Compare insensitively
return dict(self.lower_items()) == dict(other.lower_items())
# Copy is required
def copy(self):
return CaseInsensitiveDict(self._store.values())
def __repr__(self):
return str(dict(self.items()))
# ~ Custom classes # ~ Custom classes
@ -371,7 +520,8 @@ class LODocument(object):
return obj return obj
def save(self, path='', **kwargs): def save(self, path='', **kwargs):
opt = _properties(kwargs) # ~ opt = _properties(kwargs)
opt = dict_to_property(kwargs)
if path: if path:
self._obj.storeAsURL(_path_url(path), opt) self._obj.storeAsURL(_path_url(path), opt)
else: else:
@ -1127,9 +1277,30 @@ def set_properties(model, properties):
return return
def get_file(filters=(), multiple=False): def get_config_path(name='Work'):
"""
Return de path name in config
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1util_1_1XPathSettings.html
"""
path = create_instance('com.sun.star.util.PathSettings')
return _path_system(getattr(path, name))
def get_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()
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker') file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
file_picker.setTitle(_('Select file')) file_picker.setTitle(_('Select file'))
file_picker.setDisplayDirectory(init_dir)
file_picker.setMultiSelectionMode(multiple) file_picker.setMultiSelectionMode(multiple)
if filters: if filters:
@ -1242,7 +1413,8 @@ def open_doc(path, **kwargs):
http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
""" """
path = _path_url(path) path = _path_url(path)
opt = _properties(kwargs) # ~ opt = _properties(kwargs)
opt = dict_to_property(kwargs)
doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt) doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt)
if doc is None: if doc is None:
return return
@ -1278,9 +1450,9 @@ def is_created(path):
return is_file(path) and bool(get_file_size(path)) return is_file(path) and bool(get_file_size(path))
def replace_ext(path, ext): def replace_ext(path, extension):
path, _, name, _ = get_info_path(path) path, _, name, _ = get_info_path(path)
return '{}/{}.{}'.format(path, name, ext) return '{}/{}.{}'.format(path, name, extension)
def zip_names(path): def zip_names(path):
@ -1477,8 +1649,8 @@ class TextTransferable(unohelper.Base, XTransferable):
return False return False
def set_clipboard(text): def set_clipboard(value):
ts = TextTransferable(text) ts = TextTransferable(value)
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard') sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
sc.setContents(ts, None) sc.setContents(ts, None)
return return
@ -1499,3 +1671,142 @@ def copy(doc=None):
def get_epoch(): def get_epoch():
now = datetime.datetime.now() now = datetime.datetime.now()
return int(time.mktime(now.timetuple())) return int(time.mktime(now.timetuple()))
def file_copy(source, target='', name=''):
p, f, n, e = get_info_path(source)
if target:
p = target
if name:
n = name
path_new = join(p, '{}{}'.format(n, e))
shutil.copy(source, path_new)
return
def get_files(path, ext='*'):
docs = []
for folder, _, files in os.walk(path):
pattern = re.compile(r'\.{}'.format(ext), re.IGNORECASE)
docs += [join(folder, f) for f in files if pattern.search(f)]
return docs
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('Command', '')
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
_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
# ~ name = 'com.sun.star.configuration.ConfigurationProvider'
# ~ cp = create_instance(name, True)
# ~ node = PropertyValue(Name='nodepath', Value=NODE_SETTING)
# ~ try:
# ~ cua = cp.createInstanceWithArguments(
# ~ 'com.sun.star.configuration.ConfigurationUpdateAccess', (node,))
# ~ cua.setPropertyValue(key, json.dumps(value))
# ~ cua.commitChanges()
# ~ except Exception as e:
# ~ log.error(e, exc_info=True)
# ~ return False

Binary file not shown.

BIN
files/ZAZBarCode_v0.3.0.oxt Normal file

Binary file not shown.

View File

@ -5,10 +5,10 @@
xmlns:xlink="http://www.w3.org/1999/xlink"> xmlns:xlink="http://www.w3.org/1999/xlink">
<identifier value="net.elmau.zaz.BarCode" /> <identifier value="net.elmau.zaz.BarCode" />
<version value="0.2.1" /> <version value="0.3.0" />
<update-download> <update-download>
<src xlink:href="https://gitlab.com/mauriciobaeza/zaz-barcode/raw/master/files/ZAZBarCode_v0.2.1.oxt"/> <src xlink:href="https://gitlab.com/mauriciobaeza/zaz-barcode/raw/master/files/ZAZBarCode_v0.3.0.oxt"/>
</update-download> </update-download>
<release-notes> <release-notes>
</release-notes> </release-notes>

View File

@ -1,7 +1,7 @@
import gettext import gettext
import uno import uno
import unohelper import unohelper
from com.sun.star.task import XJobExecutor from com.sun.star.task import XJobExecutor, XJob
import easymacro as app import easymacro as app
import qrcode import qrcode
@ -22,6 +22,7 @@ try:
lang.install() lang.install()
_ = lang.gettext _ = lang.gettext
except Exception as e: except Exception as e:
from gettext import gettext as _
app.error(e) app.error(e)
@ -46,13 +47,27 @@ class Controllers(object):
return return
class ZAZBarCode(unohelper.Base, XJobExecutor): class ZAZBarCode(unohelper.Base, XJob, XJobExecutor):
def __init__(self, ctx): def __init__(self, ctx):
self.ctx = ctx self.ctx = ctx
self._data = '' self._data = ''
self._type = '' self._type = ''
def execute(self, args):
data = app.property_to_dict(args)
if data['Type'] == QR:
factory = svg.SvgImage
img = qrcode.make(data['Data'], image_factory=factory)
img.save(data['Path'])
else:
try:
generate(data['Type'].lower(), data['Data'], output=data['Path'])
except Exception as e:
app.debug(str(e))
return
def trigger(self, args): def trigger(self, args):
self._type = args self._type = args
if args == 'ask' and not self._get_values(): if args == 'ask' and not self._get_values():

View File

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?> <?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"> <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" /> <identifier value="net.elmau.zaz.BarCode" />
<version value="0.2.1" /> <version value="0.3.0" />
<display-name> <display-name>
<name lang="en">ZAZ Bar Code</name> <name lang="en">ZAZ Bar Code</name>
<name lang="es">ZAZ Códigos de Barras</name> <name lang="es">ZAZ Códigos de Barras</name>

Binary file not shown.

View File

@ -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-20 19:21+0200\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.0.6\n"
"Last-Translator: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: eo\n"
#: source/ZAZBarCode.py:38
msgid "Select barcode type"
msgstr "Elektu strikodan tipon"
#: source/ZAZBarCode.py:43
msgid "Data field is mandatory"
msgstr "Datumkampo estas deviga"
#: source/ZAZBarCode.py:95 source/ZAZBarCode.py:117
msgid "Select data"
msgstr "Elektu datumojn"
#: source/ZAZBarCode.py:149
msgid ""
"Error in: {}\n"
"\n"
"{}"
msgstr ""
"Eraro en: {}\n"
"\n"
"{}"
#: source/ZAZBarCode.py:167
msgid "~Select barcode type"
msgstr "~Elektu strikodan tipon"
#: source/ZAZBarCode.py:179
msgid "~Capture data for barcode"
msgstr "~Kaptu datumojn por strikodo"
#: source/ZAZBarCode.py:212
msgid "~Insert Barcode"
msgstr "~Enmetu Strikodon"

View File

@ -22,10 +22,13 @@ import ctypes
import datetime import datetime
import errno import errno
import getpass import getpass
import json
import logging import logging
import os import os
import platform import platform
import re
import shlex import shlex
import shutil
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
@ -33,6 +36,8 @@ import threading
import time import time
import zipfile import zipfile
from collections import OrderedDict
from collections.abc import MutableMapping
from datetime import datetime from datetime import datetime
from functools import wraps from functools import wraps
from pathlib import Path, PurePath from pathlib import Path, PurePath
@ -65,26 +70,18 @@ MSG_LANG = {
} }
FILE_NAME_DEBUG = 'zaz-debug.log'
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
log = logging.getLogger(__name__)
OS = platform.system() OS = platform.system()
USER = getpass.getuser() USER = getpass.getuser()
PC = platform.node() PC = platform.node()
DESKTOP = os.environ.get('DESKTOP_SESSION', '') DESKTOP = os.environ.get('DESKTOP_SESSION', '')
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path)) INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
IS_WIN = OS == 'Windows' IS_WIN = OS == 'Windows'
LOG_NAME = 'ZAZ' LOG_NAME = 'ZAZ'
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16' CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
CALC = 'calc' CALC = 'calc'
WRITER = 'writer' WRITER = 'writer'
OBJ_CELL = 'ScCellObj' OBJ_CELL = 'ScCellObj'
@ -92,6 +89,46 @@ OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj' OBJ_RANGES = 'ScCellRangesObj'
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES) OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
TYPE_DOC = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument',
'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE',
}
NODE_MENUBAR = 'private:resource/menubar/menubar'
MENUS_CALC = {
'file': '.uno:PickList',
'edit': '.uno:EditMenu',
'view': '.uno:ViewMenu',
'insert': '.uno:InsertMenu',
'format': '.uno:FormatMenu',
'styles': '.uno:FormatStylesMenu',
'sheet': '.uno:SheetMenu',
'data': '.uno:DataMenu',
'tools': '.uno:ToolsMenu',
'windows': '.uno:WindowList',
'help': '.uno:HelpMenu',
}
MENUS_APP = {
'calc': MENUS_CALC,
}
FILE_NAME_DEBUG = 'zaz-debug.log'
FILE_NAME_CONFIG = 'zaz-config.json'
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
LOG_DATE = '%d/%m/%Y %H:%M:%S'
logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
log = logging.getLogger(__name__)
CTX = uno.getComponentContext() CTX = uno.getComponentContext()
SM = CTX.getServiceManager() SM = CTX.getServiceManager()
@ -105,7 +142,7 @@ def create_instance(name, with_context=False):
return instance return instance
def _get_config(key, node_name): def _get_app_config(key, node_name):
name = 'com.sun.star.configuration.ConfigurationProvider' name = 'com.sun.star.configuration.ConfigurationProvider'
service = 'com.sun.star.configuration.ConfigurationAccess' service = 'com.sun.star.configuration.ConfigurationAccess'
cp = create_instance(name, True) cp = create_instance(name, True)
@ -120,10 +157,10 @@ def _get_config(key, node_name):
return '' return ''
LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/') LANGUAGE = _get_app_config('ooLocale', 'org.openoffice.Setup/L10N/')
LANG = LANGUAGE.split('-')[0] LANG = LANGUAGE.split('-')[0]
NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product') NAME = TITLE = _get_app_config('ooName', 'org.openoffice.Setup/Product')
VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product') VERSION = _get_app_config('ooSetupVersion', 'org.openoffice.Setup/Product')
def mri(obj): def mri(obj):
@ -202,8 +239,34 @@ def run_in_thread(fn):
return run return run
def sleep(sec): def get_config(key=''):
time.sleep(sec) values = {}
path = join(get_config_path('UserConfig'), FILE_NAME_CONFIG)
if not exists_path(path):
return values
with open(path, 'r', encoding='utf-8') as fh:
data = fh.read()
if data:
values = json.loads(data)
if key:
return values.get(key, None)
return values
def set_config(key, value):
path = join(get_config_path('UserConfig'), FILE_NAME_CONFIG)
values = get_config()
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 return
@ -278,30 +341,116 @@ def exists_app(name):
return False return False
return True return True
# ~ Delete
def exists(path): def exists(path):
return Path(path).exists() return Path(path).exists()
def exists_path(path):
return Path(path).exists()
def get_type_doc(obj): def get_type_doc(obj):
services = { # ~ services = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument', # ~ 'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument', # ~ 'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument', # ~ 'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument', # ~ 'draw': 'com.sun.star.drawing.DrawingDocument',
'base': 'com.sun.star.sdb.OfficeDatabaseDocument', # ~ 'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
'math': 'com.sun.star.formula.FormulaProperties', # ~ 'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE', # ~ 'basic': 'com.sun.star.script.BasicIDE',
} # ~ }
for k, v in services.items(): for k, v in TYPE_DOC.items():
if obj.supportsService(v): if obj.supportsService(v):
return k return k
return '' return ''
def _properties(values): # ~ def _properties(values):
p = [PropertyValue(Name=n, Value=v) for n, v in values.items()] # ~ p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
return tuple(p) # ~ return tuple(p)
def dict_to_property(values, uno_any=False):
ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
if uno_any:
ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
return ps
def property_to_dict(values):
d = {i.Name: i.Value for i in values}
return d
# ~ Third classes
# ~ https://github.com/psf/requests/blob/v2.22.0/requests/structures.py
class CaseInsensitiveDict(MutableMapping):
"""A case-insensitive ``dict``-like object.
Implements all methods and operations of
``MutableMapping`` as well as dict's ``copy``. Also
provides ``lower_items``.
All keys are expected to be strings. The structure remembers the
case of the last key to be set, and ``iter(instance)``,
``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``
will contain case-sensitive keys. However, querying and contains
testing is case insensitive::
cid = CaseInsensitiveDict()
cid['Accept'] = 'application/json'
cid['aCCEPT'] == 'application/json' # True
list(cid) == ['Accept'] # True
For example, ``headers['content-encoding']`` will return the
value of a ``'Content-Encoding'`` response header, regardless
of how the header name was originally stored.
If the constructor, ``.update``, or equality comparison
operations are given keys that have equal ``.lower()``s, the
behavior is undefined.
"""
def __init__(self, data=None, **kwargs):
self._store = OrderedDict()
if data is None:
data = {}
self.update(data, **kwargs)
def __setitem__(self, key, value):
# Use the lowercased key for lookups, but store the actual
# key alongside the value.
self._store[key.lower()] = (key, value)
def __getitem__(self, key):
return self._store[key.lower()][1]
def __delitem__(self, key):
del self._store[key.lower()]
def __iter__(self):
return (casedkey for casedkey, mappedvalue in self._store.values())
def __len__(self):
return len(self._store)
def lower_items(self):
"""Like iteritems(), but with all lowercase keys."""
return (
(lowerkey, keyval[1])
for (lowerkey, keyval)
in self._store.items()
)
def __eq__(self, other):
if isinstance(other, Mapping):
other = CaseInsensitiveDict(other)
else:
return NotImplemented
# Compare insensitively
return dict(self.lower_items()) == dict(other.lower_items())
# Copy is required
def copy(self):
return CaseInsensitiveDict(self._store.values())
def __repr__(self):
return str(dict(self.items()))
# ~ Custom classes # ~ Custom classes
@ -371,7 +520,8 @@ class LODocument(object):
return obj return obj
def save(self, path='', **kwargs): def save(self, path='', **kwargs):
opt = _properties(kwargs) # ~ opt = _properties(kwargs)
opt = dict_to_property(kwargs)
if path: if path:
self._obj.storeAsURL(_path_url(path), opt) self._obj.storeAsURL(_path_url(path), opt)
else: else:
@ -1127,9 +1277,30 @@ def set_properties(model, properties):
return return
def get_file(filters=(), multiple=False): def get_config_path(name='Work'):
"""
Return de path name in config
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1util_1_1XPathSettings.html
"""
path = create_instance('com.sun.star.util.PathSettings')
return _path_system(getattr(path, name))
def get_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()
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker') file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
file_picker.setTitle(_('Select file')) file_picker.setTitle(_('Select file'))
file_picker.setDisplayDirectory(init_dir)
file_picker.setMultiSelectionMode(multiple) file_picker.setMultiSelectionMode(multiple)
if filters: if filters:
@ -1242,7 +1413,8 @@ def open_doc(path, **kwargs):
http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
""" """
path = _path_url(path) path = _path_url(path)
opt = _properties(kwargs) # ~ opt = _properties(kwargs)
opt = dict_to_property(kwargs)
doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt) doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt)
if doc is None: if doc is None:
return return
@ -1278,9 +1450,9 @@ def is_created(path):
return is_file(path) and bool(get_file_size(path)) return is_file(path) and bool(get_file_size(path))
def replace_ext(path, ext): def replace_ext(path, extension):
path, _, name, _ = get_info_path(path) path, _, name, _ = get_info_path(path)
return '{}/{}.{}'.format(path, name, ext) return '{}/{}.{}'.format(path, name, extension)
def zip_names(path): def zip_names(path):
@ -1477,8 +1649,8 @@ class TextTransferable(unohelper.Base, XTransferable):
return False return False
def set_clipboard(text): def set_clipboard(value):
ts = TextTransferable(text) ts = TextTransferable(value)
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard') sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
sc.setContents(ts, None) sc.setContents(ts, None)
return return
@ -1499,3 +1671,142 @@ def copy(doc=None):
def get_epoch(): def get_epoch():
now = datetime.datetime.now() now = datetime.datetime.now()
return int(time.mktime(now.timetuple())) return int(time.mktime(now.timetuple()))
def file_copy(source, target='', name=''):
p, f, n, e = get_info_path(source)
if target:
p = target
if name:
n = name
path_new = join(p, '{}{}'.format(n, e))
shutil.copy(source, path_new)
return
def get_files(path, ext='*'):
docs = []
for folder, _, files in os.walk(path):
pattern = re.compile(r'\.{}'.format(ext), re.IGNORECASE)
docs += [join(folder, f) for f in files if pattern.search(f)]
return docs
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('Command', '')
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
_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
# ~ name = 'com.sun.star.configuration.ConfigurationProvider'
# ~ cp = create_instance(name, True)
# ~ node = PropertyValue(Name='nodepath', Value=NODE_SETTING)
# ~ try:
# ~ cua = cp.createInstanceWithArguments(
# ~ 'com.sun.star.configuration.ConfigurationUpdateAccess', (node,))
# ~ cua.setPropertyValue(key, json.dumps(value))
# ~ cua.commitChanges()
# ~ except Exception as e:
# ~ log.error(e, exc_info=True)
# ~ return False