Compare commits

...

40 Commits

Author SHA1 Message Date
El Mau c2470ddd9d Add support for Flatplak and AppImage versions 2023-11-05 21:35:58 -06:00
El Mau 4b66d38e32 Make new OXT 2023-11-05 21:32:36 -06:00
El Mau d2d60e7281 Update version 2023-11-05 21:30:39 -06:00
Mauricio 0e750c0af0 Merge pull request 'cpython' (#14) from AmourSpirit/zaz-pip:cpython into develop
Reviewed-on: #14
2023-11-03 11:23:41 -06:00
Barry-Thomas-Paul: Moss 8dadb7e62e fix for debug error on Windows 2023-11-02 22:45:55 -04:00
Barry-Thomas-Paul: Moss 9f2ac70cc8 link cpython 2023-11-02 22:05:56 -04:00
Mauricio c93c182953 Merge pull request 'flatpak' (#13) from AmourSpirit/zaz-pip:flatpak into develop
Reviewed-on: #13
2023-11-01 19:18:49 -06:00
Barry-Thomas-Paul: Moss db3466b716 minor correcton for site_packages.exists() 2023-10-31 20:13:43 -04:00
Barry-Thomas-Paul: Moss 0453092831 fix for white text on bright yellow edit box in dark theme 2023-10-31 19:39:45 -04:00
Barry-Thomas-Paul: Moss 7b9ac8eee0 working with flatpak 2023-10-31 19:28:22 -04:00
Barry-Thomas-Paul: Moss 4aa0ca68c8 added pip_runner that add site_packages to sys.path 2023-10-31 16:19:18 -04:00
Barry-Thomas-Paul: Moss 923b39c7f2 pip installing in flatpak 2023-10-31 15:57:41 -04:00
Mauricio 90aa12adf2 Merge pull request 'develop' (#11) from AmourSpirit/zaz-pip:develop into develop
Reviewed-on: #11
2023-10-29 16:51:44 -06:00
AmourSpirit d52472dad7 Merge pull request 'app_image' (#1) from app_image into develop
Reviewed-on: AmourSpirit/zaz-pip#1
2023-10-19 17:09:26 -06:00
Barry-Thomas-Paul: Moss 6ec45c1bb4 Update to work with AppImage
Linxu ppImage Works with this minor fix.
Linux FlatPak still not working yet.
2023-10-19 19:06:50 -04:00
Barry-Thomas-Paul: Moss 910e5f54b1 update for Linxu AppImage 2023-10-19 15:29:02 -04:00
El Mau c9b9bfdf0a Fix issue #10 2022-04-23 21:49:30 -05:00
El Mau 587148553e Fix - Issue #10 2022-04-23 21:48:50 -05:00
El Mau 4b7b0c359f Fix issue #9 2022-03-26 23:03:54 -06:00
El Mau 269f9adc75 Fix - Issue #9 2022-03-26 16:07:22 -06:00
El Mau 668e23353b Update README 2022-03-26 11:46:41 -06:00
El Mau d7d65bb8ba Fix - Issue #9 2022-03-26 11:45:03 -06:00
El Mau b3c783c1fe Add Proxy dialog title to translate 2022-03-25 09:29:59 -06:00
El Mau 2452480457 Add french translation. Issue #8 2022-03-17 12:39:49 -06:00
El Mau afd94fa5fe Add french translation 2022-03-17 12:38:07 -06:00
El Mau c940f6fcc6 Fix issue #6 2022-02-04 21:29:23 -06:00
El Mau 450daa630a Update changelog 2022-02-04 21:27:30 -06:00
El Mau 6ce81f91c6 Fix to open terminal in Gnome 2022-02-04 21:26:17 -06:00
El Mau b7e0d2a0fc Update extension 2022-01-28 13:49:49 -06:00
El Mau 4d518c781e Issue #6 2022-01-28 13:45:10 -06:00
El Mau 354b5e1421 Release v0.9.0 2022-01-27 12:02:38 -06:00
El Mau b1e24d4411 Add minimal version 2022-01-27 09:45:07 -06:00
LibreOfficiant cac0b4fb59 Corrections to the english labels
Note: x-terminal-emulator is a generic command for all *NIX Desktop Environment *terminal commands.

Signed-off-by: LibreOfficiant <libreofficiant@noreply.localhost>
2022-01-27 07:53:37 -06:00
LibreOfficiant 9910b5302b Corrections to english messages
Including minor typos
2022-01-21 09:36:21 -06:00
Mauricio Baeza 9e2f841017 Add config proxy. Issue #1 2021-06-14 16:38:25 -05:00
Mauricio Baeza 223989495e Modify size icons 2021-06-14 16:37:30 -05:00
Mauricio Baeza b31fa8247f Modify size icons 2021-06-14 16:34:30 -05:00
Mauricio Baeza a49a1ad4d8 Add config proxy 2021-06-12 23:04:37 -05:00
Mauricio Baeza 4eb0b51a6d Add button and image for config proxy 2021-06-05 23:03:36 -05:00
Mauricio Baeza e64854c78c Update easymacro.py 2021-06-05 22:06:13 -05:00
32 changed files with 2285 additions and 348 deletions

View File

@ -1,3 +1,36 @@
v 1.0.0 [05-nov-2023]
- Thanks to https://git.cuates.net/AmourSpirit zaz-pip now work in:
* Mac Os
* Windows
* Linux, sudo installed LibreOffice
* Linux, Snap installed LibreOffice
* Linux, Flatpak installed LibreOffice
* Linux, AppImage LibreOffice
v 0.10.2 [22-apr-2022]
- Fix - issue #10
v 0.10.1 [26-mar-2022]
- Fix - issue #9
v 0.10.0 [19-mar-2022]
- Add french translation
v 0.9.0 [04-feb-2022]
- Fix - Add minimal version required, issue #5
- Fix - Corrections to english messages, issue #6
- Fix - Update and complete Spanish translation, thanks to me, :)
v 0.8.0 [13-jun-2021]
- Update easymacro.py
- Add proxy config
v 0.7.0 [05-jan-2020]
- Disable search
v 0.6.0 [18-nov-2020]
- Add install with requirements.txt
v 0.5.0 [09-jul-2020]
- Test install pandas
@ -5,16 +38,12 @@ v 0.4.0 [10-mar-2020]
- Update easymacro.py
- Fix: in OSx Catalina
v 0.3.0 [12-nov-2019]
- Update easymacro.py
- Test install numpy
v 0.2.0 [18-oct-2019]
- Add spanish
- Add Spanish translation
v 0.1.0 [18-oct-2019]
- Initial version

View File

@ -5,25 +5,32 @@ Extension for install and admin Python Pip in LibreOffice.
Thanks!
https://gitlab.com/mauriciobaeza/zaz
https://git.cuates.net/elmau/zaz
### Software libre, no gratis
### Free Software, not gratis software
This extension have a cost of maintenance of 1 euro every year.
BCH: `qztd3l00xle5tffdqvh2snvadkuau2ml0uqm4n875d`
G1: `A5DdXxCKPw3QKWVdDVs7CzkNugNUW1sHu5zDJFWxCU2h`
BTC: `3FhiXcXmAesmQzrNEngjHFnvaJRhU1AGWV`
```
Euros
IBAN: BE60 9671 0556 5870
SWIFT / BIC: TRWIBEB1XXX
```
You have others cryptos, welcome too!
* [Look the wiki](https://git.cuates.net/elmau/zaz-pip/wiki/home)
* [Mira la wiki](https://git.cuates.net/elmau/zaz-pip/wiki/home_es)
* [Look the wiki](https://gitlab.com/mauriciobaeza/zaz-pip/wikis/home)
* [Mira la wiki](https://gitlab.com/mauriciobaeza/zaz-pip/wikis/home_es)
Thanks for code:
* @AmourSpirit
Thanks for translations:
* Help Us
* English - @LibreOfficiant
* French - @LibreOfficiant

View File

@ -1 +1 @@
0.7.0
1.0.0

26
conf.py
View File

@ -26,7 +26,7 @@ import logging
TYPE_EXTENSION = 1
# ~ https://semver.org/
VERSION = '0.7.0'
VERSION = '1.0.0'
# ~ Your great extension name, not used spaces
NAME = 'ZAZPip'
@ -41,14 +41,16 @@ ID = 'net.elmau.zaz.pip'
USE_LOCALES = True
DOMAIN = 'base'
PATH_LOCALES = 'locales'
PATH_PYGETTEXT = '/usr/lib/python3.8/Tools/i18n/pygettext.py'
PATH_PYGETTEXT = '/usr/lib/python3.10/Tools/i18n/pygettext.py'
PATH_MSGMERGE = 'msgmerge'
# ~ Show in extension manager
URL_GIT = 'https://git.cuates.net/elmau/zaz-pip'
PUBLISHER = {
'en': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'},
'es': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'},
'en': {'text': 'El Mau', 'link': URL_GIT},
'es': {'text': 'El Mau', 'link': URL_GIT},
'fr': {'text': 'El Mau', 'link': URL_GIT},
}
# ~ Name in this folder for copy
@ -56,9 +58,7 @@ ICON = 'images/logo.png'
# ~ Name inside extensions
ICON_EXT = f'{NAME.lower()}.png'
# ~ For example
# ~ DEPENDENCIES_MINIMAL = '6.0'
DEPENDENCIES_MINIMAL = ''
DEPENDENCIES_MINIMAL = '7.0'
# ~ Change for you favorite license
LICENSE_EN = f"""This file is part of {NAME}.
@ -76,7 +76,7 @@ LICENSE_EN = f"""This file is part of {NAME}.
You should have received a copy of the GNU General Public License
along with {NAME}. If not, see <https://www.gnu.org/licenses/>.
"""
LICENSE_ES = LICENSE_EN
LICENSE_FR = LICENSE_ES = LICENSE_EN
INFO = {
'en': {
@ -89,6 +89,11 @@ INFO = {
'description': 'Pip para LibreOffice',
'license': LICENSE_ES,
},
'fr': {
'display_name': 'ZAZ Pip',
'description': 'Pip pour LibreOffice',
'license': LICENSE_FR,
},
}
@ -111,7 +116,7 @@ PARENT = 'AddonMenu'
MENU_MAIN = {}
MENUS = (
{
'title': {'en': 'Open Pip', 'es': 'Abrir Pip'},
'title': {'en': 'Open Zaz-Pip', 'es': 'Abrir Zaz-Pip', 'fr': 'Ouvert Zaz-Pip'},
'argument': 'open_dialog_pip',
'context': '',
'icon': 'icon',
@ -153,7 +158,7 @@ DIRS = {
'description': 'description',
'images': 'images',
'registration': 'registration',
'files': 'files',
'files': 'extension',
'office': 'Office',
'locales': PATH_LOCALES,
'pythonpath': True,
@ -397,6 +402,7 @@ DATA_DESCRIPTION = {
'icon': ICON_EXT,
'publisher': PUBLISHER,
'update': URL_XML_UPDATE,
'minimal': DEPENDENCIES_MINIMAL,
}
DATA_ADDONS = {

View File

@ -21,6 +21,7 @@
import base64
import csv
import ctypes
import datetime
import getpass
import gettext
@ -82,8 +83,9 @@ from com.sun.star.util import Time, Date, DateTime
from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
from com.sun.star.awt import XActionListener
from com.sun.star.lang import Locale
from com.sun.star.lang import XEventListener
from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMenuListener
from com.sun.star.awt import XMouseListener
from com.sun.star.awt import XMouseMotionListener
@ -203,6 +205,12 @@ class ConditionOperator():
CO = ConditionOperator
class DataPilotFieldOrientation():
from com.sun.star.sheet.DataPilotFieldOrientation \
import HIDDEN, COLUMN, ROW, PAGE, DATA
DPFO = DataPilotFieldOrientation
OS = platform.system()
IS_WIN = OS == 'Windows'
IS_MAC = OS == 'Darwin'
@ -308,6 +316,11 @@ def get_app_config(node_name: str, key: str=''):
LANGUAGE = get_app_config('org.openoffice.Setup/L10N/', 'ooLocale')
LANG = LANGUAGE.split('-')[0]
try:
COUNTRY = LANGUAGE.split('-')[1]
except:
COUNTRY = ''
LOCALE = Locale(LANG, COUNTRY, '')
NAME = TITLE = get_app_config('org.openoffice.Setup/Product', 'ooName')
VERSION = get_app_config('org.openoffice.Setup/Product','ooSetupVersion')
@ -372,6 +385,8 @@ def mri(obj: Any) -> None:
error(msg)
return
if hasattr(obj, 'obj'):
obj = obj.obj
m.inspect(obj)
return
@ -1166,6 +1181,9 @@ class LODocument(object):
return
def to_pdf(self, path: str='', args: dict={}):
"""
https://wiki.documentfoundation.org/Macros/Python_Guide/PDF_export_filter_data
"""
path_pdf = path
filter_name = '{}_pdf_Export'.format(self.type)
filter_data = dict_to_property(args, True)
@ -1520,6 +1538,142 @@ class LOSheetCharts(object):
return LOChart(name, self.obj[name], self._sheet.draw_page)
class LOSheetTableField(object):
def __init__(self, obj):
self._obj = obj
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
@property
def obj(self):
return self._obj
@property
def name(self):
return self.obj.Name
@property
def orientation(self):
return self.obj.Orientation
@orientation.setter
def orientation(self, value):
self.obj.Orientation = value
# ~ com.sun.star.sheet.DataPilotFieldOrientation.ROW
class LOSheetTable(object):
def __init__(self, obj):
self._obj = obj
self._source = None
def __getitem__(self, index):
field = self.obj.DataPilotFields[index]
return LOSheetTableField(field)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
@property
def obj(self):
return self._obj
@property
def filter(self):
return self.obj.ShowFilterButton
@filter.setter
def filter(self, value):
self.obj.ShowFilterButton = value
@property
def source(self):
return self._source
@source.setter
def source(self, value):
self._source = value
self.obj.SourceRange = value.range_address
@property
def rows(self):
return self.obj.RowFields
@rows.setter
def rows(self, values):
if not isinstance(values, tuple):
values = (values,)
for v in values:
with self[v] as f:
f.orientation = DPFO.ROW
@property
def columns(self):
return self.obj.ColumnFields
@columns.setter
def columns(self, values):
if not isinstance(values, tuple):
values = (values,)
for v in values:
with self[v] as f:
f.orientation = DPFO.COLUMN
@property
def data(self):
return self.obj.DataFields
@data.setter
def data(self, values):
if not isinstance(values, tuple):
values = (values,)
for v in values:
with self[v] as f:
f.orientation = DPFO.DATA
class LOSheetTables(object):
def __init__(self, obj, sheet):
self._obj = obj
self._sheet = sheet
def __getitem__(self, index):
return LOSheetTable(self.obj[index])
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __contains__(self, item):
return item in self.obj
@property
def obj(self):
return self._obj
@property
def count(self):
return self.obj.Count
@property
def names(self):
return self.obj.ElementNames
def new(self, name, target):
table = self.obj.createDataPilotDescriptor()
self.obj.insertNewByName(name, target.address, table)
return LOSheetTable(self.obj[name])
def remove(self, name):
self.obj.removeByName(name)
return
class LOFormControl(LOBaseObject):
EVENTS = {
'action': 'actionPerformed',
@ -2045,6 +2199,10 @@ class LOCalcSheet(object):
def charts(self):
return LOSheetCharts(self.obj.Charts, self)
@property
def tables(self):
return LOSheetTables(self.obj.DataPilotTables, self)
@property
def rows(self):
return LOSheetRows(self, self.obj.Rows)
@ -2558,6 +2716,7 @@ class LOCalcRange(object):
cell = None
if isinstance(value, dict):
for k, v in value.items():
# ~ print(1, 'RENDER', k, v)
cell = self._render_value(k, v, key)
return cell
elif isinstance(value, (list, tuple)):
@ -2569,7 +2728,11 @@ class LOCalcRange(object):
search = f'{{{parent}.{key}}}'
ranges = self.find_all(search)
for cell in ranges or range(0):
if ranges is None:
return
# ~ for cell in ranges or range(0):
for cell in ranges:
self._set_new_value(cell, search, value)
return LOCalcRange(cell)
@ -2718,6 +2881,72 @@ class LOCalcRange(object):
self.obj.fillAuto(0, source)
return
def _cast(self, t, v):
if not t:
return v
if t == datetime.date:
nv = datetime.date.fromordinal(int(v) + DATE_OFFSET)
else:
nv = t(v)
return nv
def get_data(self, types):
values = [
[self._cast(types[i], v) for i, v in enumerate(row)]
for row in self.data
]
return values
class LOWriterStyles(object):
def __init__(self, styles):
self._styles = styles
@property
def names(self):
return {s.DisplayName: s.Name for s in self._styles}
def __str__(self):
return '\n'.join(tuple(self.names.values()))
class LOWriterStylesFamilies(object):
def __init__(self, styles):
self._styles = styles
def __getitem__(self, index):
styles = {
'Character': 'CharacterStyles',
'Paragraph': 'ParagraphStyles',
'Page': 'PageStyles',
'Frame': 'FrameStyles',
'Numbering': 'NumberingStyles',
'Table': 'TableStyles',
'Cell': 'CellStyles',
}
name = styles.get(index, index)
return LOWriterStyles(self._styles[name])
def __iter__(self):
self._index = 0
return self
def __next__(self):
obj = LOWriterStyles(self._styles[self._index])
self._index += 1
return obj
# ~ raise StopIteration
@property
def names(self):
return self._styles.ElementNames
def __str__(self):
return '\n'.join(self.names)
class LOWriterPageStyle(LOBaseObject):
@ -2755,18 +2984,23 @@ class LOWriterTextRange(object):
self._doc = doc
self._is_paragraph = self.obj.ImplementationName == 'SwXParagraph'
self._is_table = self.obj.ImplementationName == 'SwXTextTable'
self._is_text = self.obj.ImplementationName == 'SwXTextPortion'
self._parts = []
if self._is_paragraph:
self._parts = [LOWriterTextRange(p, doc) for p in obj]
def __iter__(self):
self._index = 0
return self
def __next__(self):
for i, p in enumerate(self.obj):
if i == self._index:
obj = LOWriterTextRange(p, self._doc)
self._index += 1
return obj
raise StopIteration
try:
obj = self._parts[self._index]
except IndexError:
raise StopIteration
self._index += 1
return obj
@property
def obj(self):
@ -2785,11 +3019,37 @@ class LOWriterTextRange(object):
@property
def value(self):
return self.string
@value.setter
def value(self, value):
self.string = value
@property
def style(self):
s = ''
if self.is_paragraph:
s = self.obj.ParaStyleName
elif self.is_text:
s = self.obj.CharStyleName
return s
@style.setter
def style(self, value):
if self.is_paragraph:
self.obj.ParaStyleName = value
elif self.is_text:
self.obj.CharStyleName = value
@property
def is_paragraph(self):
return self._is_paragraph
@property
def is_table(self):
return self._is_table
@property
def is_text(self):
return self._is_text
@property
def text(self):
return self.obj.Text
@ -2802,6 +3062,13 @@ class LOWriterTextRange(object):
def dp(self):
return self._doc.dp
def delete(self):
cursor = self.cursor
cursor.gotoStartOfParagraph(False)
cursor.gotoNextParagraph(True)
cursor.String = ''
return
def offset(self):
cursor = self.cursor.getEnd()
return LOWriterTextRange(cursor, self._doc)
@ -2846,25 +3113,26 @@ class LOWriterTextRanges(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
self._paragraphs = [LOWriterTextRange(p, doc) for p in obj]
def __len__(self):
return len(self._paragraphs)
def __getitem__(self, index):
for i, p in enumerate(self.obj):
if i == index:
obj = LOWriterTextRange(p, self._doc)
break
return obj
return self._paragraphs[index]
def __iter__(self):
self._index = 0
return self
def __next__(self):
for i, p in enumerate(self.obj):
if i == self._index:
obj = LOWriterTextRange(p, self._doc)
self._index += 1
return obj
raise StopIteration
try:
obj = self._paragraphs[self._index]
except IndexError:
raise StopIteration
self._index += 1
return obj
@property
def obj(self):
@ -2887,10 +3155,17 @@ class LOWriterTextTable(object):
@property
def data(self):
return self._obj.DataArray
return self.obj.DataArray
@data.setter
def data(self, values):
self._obj.DataArray = values
self.obj.DataArray = values
@property
def style(self):
return self.obj.TableTemplateName
@style.setter
def style(self, value):
self.obj.autoFormat(value)
class LOWriterTextTables(object):
@ -2920,7 +3195,7 @@ class LOWriter(LODocument):
@property
def text(self):
return LOWriterTextRange(self.obj.Text, self)
return self.paragraphs
@property
def paragraphs(self):
@ -2973,6 +3248,10 @@ class LOWriter(LODocument):
ps = self.obj.StyleFamilies['PageStyles']
return LOWriterPageStyles(ps)
@property
def styles(self):
return LOWriterStylesFamilies(self.obj.StyleFamilies)
@property
def search_descriptor(self):
return self.obj.createSearchDescriptor()
@ -3403,15 +3682,19 @@ class BaseRow:
class BaseQuery(object):
PY_TYPES = {
'SQL_LONG': 'getLong',
'SQL_VARYING': 'getString',
'SQL_FLOAT': 'getFloat',
'SQL_BOOLEAN': 'getBoolean',
'SQL_TYPE_DATE': 'getDate',
'SQL_TYPE_TIME': 'getTime',
'SQL_TIMESTAMP': 'getTimestamp',
'VARCHAR': 'getString',
'INTEGER': 'getLong',
'DATE': 'getDate',
# ~ 'SQL_LONG': 'getLong',
# ~ 'SQL_VARYING': 'getString',
# ~ 'SQL_FLOAT': 'getFloat',
# ~ 'SQL_BOOLEAN': 'getBoolean',
# ~ 'SQL_TYPE_DATE': 'getDate',
# ~ 'SQL_TYPE_TIME': 'getTime',
# ~ 'SQL_TIMESTAMP': 'getTimestamp',
}
TYPES_DATE = ('SQL_TYPE_DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
# ~ TYPES_DATE = ('SQL_TYPE_DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
TYPES_DATE = ('DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
def __init__(self, query):
self._query = query
@ -3437,6 +3720,7 @@ class BaseQuery(object):
def _to_python(self, index):
type_field = self._meta.getColumnTypeName(index)
# ~ print('TF', type_field)
value = getattr(self._query, self.PY_TYPES[type_field])(index)
if type_field in self.TYPES_DATE:
value = _struct_to_date(value)
@ -3562,10 +3846,11 @@ class LOBase(object):
self._con.getTables().refresh()
return
def initialize(self, database_proxy, tables):
def initialize(self, database_proxy, tables=[]):
db = FirebirdDatabase(self)
database_proxy.initialize(db)
db.create_tables(tables)
if tables:
db.create_tables(tables)
return
def _validate_sql(self, sql, params):
@ -3676,6 +3961,7 @@ class LODocs(object):
return doc
def __len__(self):
# ~ len(self._desktop.Components)
for i, _ in enumerate(self._desktop.Components):
pass
return i + 1
@ -4445,6 +4731,13 @@ class UnoText(UnoBaseObject):
def value(self, value):
self.model.Text = value
@property
def echochar(self):
return chr(self.model.EchoChar)
@echochar.setter
def echochar(self, value):
self.model.EchoChar = ord(value[0])
def validate(self):
return
@ -5285,6 +5578,11 @@ class LODialog(object):
self.obj.dispose()
return value
def set_values(self, data):
for k, v in data.items():
self._controls[k].value = v
return
class LOSheets(object):
@ -5832,6 +6130,7 @@ _CB = ClipBoard
class Paths(object):
FILE_PICKER = 'com.sun.star.ui.dialogs.FilePicker'
FOLDER_PICKER = 'com.sun.star.ui.dialogs.FolderPicker'
def __init__(self, path=''):
if path.startswith('file://'):
@ -5941,7 +6240,7 @@ class Paths(object):
@classmethod
def get_dir(cls, init_dir=''):
folder_picker = create_instance(cls.FILE_PICKER)
folder_picker = create_instance(cls.FOLDER_PICKER)
if not init_dir:
init_dir = cls.documents
init_dir = cls.to_url(init_dir)
@ -5950,7 +6249,7 @@ class Paths(object):
path = ''
if folder_picker.execute():
path = cls.to_system(folder_picker.getDisplayDirectory())
path = cls.to_system(folder_picker.getDirectory())
return path
@classmethod
@ -6219,6 +6518,50 @@ class Paths(object):
_P = Paths
class Dates(object):
@classmethod
def date(cls, year, month, day):
d = datetime.date(year, month, day)
return d
class SpellChecker(object):
def __init__(self):
service = 'com.sun.star.linguistic2.SpellChecker'
self._spellchecker = create_instance(service, True)
self._locale = LOCALE
@property
def locale(self):
slocal = f'{self._locale.Language}-{self._locale.Country}'
return slocale
@locale.setter
def locale(self, value):
lang = value.split('-')
self._locale = Locale(lang[0], lang[1], '')
def is_valid(self, word):
result = self._spellchecker.isValid(word, self._locale, ())
return result
def spell(self, word):
result = self._spellchecker.spell(word, self._locale, ())
if result:
result = result.getAlternatives()
if not isinstance(result, tuple):
result = ()
return result
def spell(word, locale=''):
sc = SpellChecker()
if locale:
sc.locale = locale
return sc.spell(word)
def __getattr__(name):
if name == 'active':
return LODocs().active
@ -6244,6 +6587,8 @@ def __getattr__(name):
return LOShortCuts()
if name == 'clipboard':
return ClipBoard
if name == 'dates':
return Dates
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
@ -6330,48 +6675,6 @@ def get_fonts():
return device.FontDescriptors
# ~ From request
# ~ https://github.com/psf/requests/blob/master/requests/structures.py#L15
class CaseInsensitiveDict(MutableMapping):
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."""
values = (
(lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items()
)
return values
# Copy is required
def copy(self):
return CaseInsensitiveDict(self._store.values())
def __repr__(self):
return str(dict(self.items()))
# ~ https://en.wikipedia.org/wiki/Web_colors
def get_color(value):
COLORS = {

BIN
extension/ZAZPip_v1.0.0.oxt Normal file

Binary file not shown.

Binary file not shown.

View File

@ -4,8 +4,9 @@
<node oor:name="AddonMenu">
<node oor:name="net.elmau.zaz.pip" oor:op="fuse">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">Open Pip</value>
<value xml:lang="es">Abrir Pip</value>
<value xml:lang="en">Open Zaz-Pip</value>
<value xml:lang="es">Abrir Zaz-Pip</value>
<value xml:lang="fr">Ouvert Zaz-Pip</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value/>

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" manifest:version="1.2">
<manifest:file-entry manifest:full-path="job.xcu" manifest:media-type="application/vnd.sun.star.configuration-data" />
<manifest:file-entry manifest:full-path="ZAZPip.py" manifest:media-type="application/vnd.sun.star.uno-component;type=Python"/>
<manifest:file-entry manifest:full-path="pip_runner.py" manifest:media-type="application/vnd.sun.star.uno-component;type=Python" />
<manifest:file-entry manifest:full-path="Office/Accelerators.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>
<manifest:file-entry manifest:full-path="Addons.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>
</manifest:manifest>

View File

@ -13,9 +13,9 @@ class ZAZPip(unohelper.Base, XJobExecutor):
def __init__(self, ctx):
self.ctx = ctx
def trigger(self, args):
def trigger(self, option):
main.ID_EXTENSION = ID_EXTENSION
main.run(args, __file__)
main.run(__file__)
return

View File

@ -1,26 +1,33 @@
<?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" xmlns:l="http://libreoffice.org/extensions/description/2011">
<identifier value="net.elmau.zaz.pip"/>
<version value="0.7.0"/>
<version value="1.0.0"/>
<dependencies>
<l:LibreOffice-minimal-version value="7.0"/>
</dependencies>
<display-name>
<name lang="en">ZAZ Pip</name>
<name lang="es">ZAZ Pip</name>
<name lang="fr">ZAZ Pip</name>
</display-name>
<extension-description>
<src lang="en" xlink:href="description/desc_en.txt"/>
<src lang="es" xlink:href="description/desc_es.txt"/>
<src lang="fr" xlink:href="description/desc_fr.txt"/>
</extension-description>
<icon>
<default xlink:href="images/zazpip.png"/>
</icon>
<publisher>
<name xlink:href="https://gitlab.com/mauriciobaeza" lang="en">El Mau</name>
<name xlink:href="https://gitlab.com/mauriciobaeza" lang="es">El Mau</name>
<name xlink:href="https://git.cuates.net/elmau/zaz-pip" lang="en">El Mau</name>
<name xlink:href="https://git.cuates.net/elmau/zaz-pip" lang="es">El Mau</name>
<name xlink:href="https://git.cuates.net/elmau/zaz-pip" lang="fr">El Mau</name>
</publisher>
<registration>
<simple-license accept-by="user" suppress-on-update="true">
<license-text xlink:href="registration/license_en.txt" lang="en"/>
<license-text xlink:href="registration/license_es.txt" lang="es"/>
<license-text xlink:href="registration/license_fr.txt" lang="fr"/>
</simple-license>
</registration>
</description>

View File

@ -0,0 +1 @@
Pip pour LibreOffice

4
source/images/delete.svg Executable file
View File

@ -0,0 +1,4 @@
<svg width="24" height="24" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.17218 14.8284L12.0006 12M14.829 9.17157L12.0006 12M12.0006 12L9.17218 9.17157M12.0006 12L14.829 14.8284" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 505 B

54
source/images/proxy.svg Normal file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
x="0px"
y="0px"
viewBox="0 0 18 18"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg10"
width="18"
height="18"
sodipodi:docname="proxy.svg"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1001"
id="namedview8"
showgrid="false"
inkscape:zoom="18.185903"
inkscape:cx="18.640812"
inkscape:cy="9.7053196"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg10"
inkscape:document-rotation="0"
inkscape:pagecheckerboard="0" /><metadata
id="metadata16"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs14" /><g
id="g6"
transform="matrix(0.04557718,0,0,0.03664295,-2.0253858,0.04823028)"><path
d="M 309.713,296.246 C 362.693,271.617 399.53,217.861 399.53,155.331 399.53,69.545 330.345,0 245,0 159.655,0 90.47,69.545 90.47,155.332 c 0,62.523 36.831,116.275 89.801,140.908 16.753,57.946 50.682,125.857 55.671,135.645 l 5.764,11.421 c -72.419,0.308 -130.615,10.624 -130.615,23.332 0,12.903 59.953,23.362 133.909,23.362 73.956,0 133.909,-10.459 133.909,-23.362 0,-12.707 -58.186,-23.022 -130.596,-23.332 l 5.801,-11.421 c 4.945,-9.848 38.843,-77.736 55.599,-135.639 z m -100.04,-8.507 -3.868,-13.378 -12.628,-5.872 c -43.8,-20.369 -72.101,-64.785 -72.101,-113.157 0,-68.775 55.593,-124.727 123.925,-124.727 68.333,0 123.925,55.952 123.925,124.727 0,48.378 -28.306,92.797 -72.113,113.162 l -12.627,5.87 -3.87,13.376 c -8.981,31.034 -23.516,65.872 -35.322,92.015 -11.674,-25.799 -26.267,-60.698 -35.321,-92.016 z"
id="path2" /><ellipse
cx="245"
cy="155.332"
rx="51.351002"
ry="51.617001"
id="ellipse4" /></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
x="0px"
y="0px"
viewBox="0 0 32 32"
viewBox="0 0 18 18"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg12"
sodipodi:docname="python.svg"
width="32"
height="32"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><sodipodi:namedview
width="18"
height="18"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
@ -27,24 +27,25 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1006"
inkscape:window-height="1001"
id="namedview9"
showgrid="false"
inkscape:showpageshadow="false"
inkscape:zoom="4.655591"
inkscape:cx="-28.841168"
inkscape:cy="23.577791"
inkscape:zoom="26.336"
inkscape:cx="10.631835"
inkscape:cy="8.2966283"
inkscape:window-x="0"
inkscape:window-y="37"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg12"
inkscape:document-rotation="0" /><metadata
inkscape:document-rotation="0"
inkscape:pagecheckerboard="0" /><metadata
id="metadata18"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs16" /><g
id="g23"
transform="matrix(0.39999852,0,0,0.39999286,-3.9998495,-3.999893)"><path
transform="matrix(0.22772641,0,0,0.22616483,-2.2771868,-2.2616282)"><path
d="M 86.674,33.332 H 66.66 V 13.327 c -11.106,-4.604 -23.04,-4.265 -33.334,0 v 20.005 h -20 c -4.603,11.107 -4.264,23.039 0,33.336 h 20 v 20.007 c 11.107,4.603 23.041,4.264 33.334,0 V 66.668 h 20.014 c 4.602,-11.11 4.264,-23.043 0,-33.336 z M 33.326,56.668 v 3.333 H 18.053 c -1.863,-6.563 -1.863,-13.445 0,-20.002 h 31.94 v -6.667 h -10 V 18.055 c 3.255,-0.922 6.614,-1.389 10.02,-1.389 3.391,0 6.732,0.466 9.98,1.387 v 25.28 c 0,1.841 -1.494,3.333 -3.333,3.333 H 43.327 c -5.521,0.001 -10.001,4.478 -10.001,10.002 z m 48.62,3.333 H 49.994 v 6.667 h 10 v 15.279 c -3.255,0.921 -6.611,1.387 -10.019,1.387 -3.389,0 -6.732,-0.466 -9.98,-1.387 V 56.668 c 0,-1.844 1.493,-3.334 3.333,-3.334 H 56.66 c 5.521,0 10,-4.476 10,-10 v -3.335 h 15.286 c 1.863,6.561 1.863,13.442 0,20.002 z"
id="path2" /><circle
cx="48.326"

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

8
source/images/save.svg Normal file
View File

@ -0,0 +1,8 @@
<svg width="24" height="24" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 7.5V5C3 3.89543 3.89543 3 5 3H16.1716C16.702 3 17.2107 3.21071 17.5858 3.58579L20.4142 6.41421C20.7893 6.78929 21 7.29799 21 7.82843V19C21 20.1046 20.1046 21 19 21H5C3.89543 21 3 20.1046 3 19V16.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 21V17" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 21V13.6C18 13.2686 17.7314 13 17.4 13H15" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 3V8.4C16 8.73137 15.7314 9 15.4 9H13.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 3V6" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1 12H12M12 12L9 9M12 12L9 15" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

20
source/job.xcu Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE oor:component-data SYSTEM "../../../../component-update.dtd">
<oor:component-data oor:name="Jobs" oor:package="org.openoffice.Office"
xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<node oor:name="Jobs">
<node oor:name="PipRunnerJob" oor:op="fuse">
<prop oor:name="Service">
<value>net.elmau.zaz.pip.PipRunner</value>
</prop>
</node>
</node>
<node oor:name="Events">
<node oor:name="OnStartApp" oor:op="fuse">
<node oor:name="JobList">
<node oor:name="PipRunnerJob" oor:op="fuse" />
</node>
</node>
</node>
</oor:component-data>

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2019-10-19 15:19-0500\n"
"POT-Creation-Date: 2022-03-26 11:23-0600\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"
@ -15,73 +15,144 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
#: source/ZAZPip.py:49
msgid "Do you want install PIP?"
#: source/pythonpath/main.py:81
msgid "Do you want to install 'Python installer package' (PIP)?"
msgstr ""
#: source/ZAZPip.py:67
msgid "Do you have internet connection?"
#: source/pythonpath/main.py:93
msgid "Downloading PIP…"
msgstr ""
#: source/ZAZPip.py:73
msgid "File PIP not save"
#: source/pythonpath/main.py:96
msgid "Unable to connect to internet"
msgstr ""
#: source/ZAZPip.py:76
msgid "PIP save correctly..."
#: source/pythonpath/main.py:102
msgid "Unable to copy PIP installation file"
msgstr ""
#: source/ZAZPip.py:80
msgid "Start installing PIP..."
#: source/pythonpath/main.py:105
msgid "PIP installation file has been saved"
msgstr ""
#: source/ZAZPip.py:94
msgid "PIP installed sucesfully"
#: source/pythonpath/main.py:108
msgid "Starting PIP installation…"
msgstr ""
#: source/ZAZPip.py:97
msgid "PIP not installed, see log"
#: source/pythonpath/main.py:122
msgid "PIP was installed sucessfully"
msgstr ""
#: source/ZAZPip.py:173
msgid "Not found..."
#: source/pythonpath/main.py:125
msgid "PIP installation has failed, see log"
msgstr ""
#: source/ZAZPip.py:209
#: source/pythonpath/main.py:198
msgid "Package not found…"
msgstr ""
#: source/pythonpath/main.py:213
msgid "Enter the package name to install"
msgstr ""
#: source/pythonpath/main.py:218
msgid "Do you really want to install package: {}?"
msgstr ""
#: source/pythonpath/main.py:247
msgid "install"
msgstr ""
#: source/pythonpath/main.py:249
msgid "upgrade"
msgstr ""
#: source/pythonpath/main.py:252
msgid ""
"Do you want {}:\n"
"Do you want to {}:\n"
"\n"
"{} ?"
"{}?"
msgstr ""
#: source/ZAZPip.py:235
#: source/pythonpath/main.py:278
msgid "Select installed package"
msgstr ""
#: source/ZAZPip.py:240
#: source/pythonpath/main.py:283
msgid ""
"Do you want uninstall:\n"
"Do you want to uninstall:\n"
"\n"
"{} ?"
"{}?"
msgstr ""
#: source/ZAZPip.py:327
msgid "PIP not installed"
#: source/pythonpath/main.py:310
msgid ""
"Do you want to install from:\n"
"\n"
"{}"
msgstr ""
#: source/ZAZPip.py:346
#: source/pythonpath/main.py:337
msgid "Do you want to save this Proxy settings?"
msgstr ""
#: source/pythonpath/main.py:343
msgid "Proxy settings saved sucessfully"
msgstr ""
#: source/pythonpath/main.py:349
msgid "Do you want to remove Proxy settings?"
msgstr ""
#: source/pythonpath/main.py:355
msgid "Proxy settings deleted sucessfully"
msgstr ""
#: source/pythonpath/main.py:413
msgid "'Python installer package' (PIP) is not installed"
msgstr ""
#: source/pythonpath/main.py:432
msgid "Admin PIP"
msgstr ""
#: source/ZAZPip.py:359
#: source/pythonpath/main.py:445
msgid "Install PIP"
msgstr ""
#: source/ZAZPip.py:397
#: source/pythonpath/main.py:494
msgid "Packages"
msgstr ""
#: source/ZAZPip.py:429
#: source/pythonpath/main.py:536
msgid "~Close"
msgstr ""
#: source/pythonpath/main.py:635
msgid "Zaz-Pip - Proxy settings"
msgstr ""
#: source/pythonpath/main.py:646
msgid "Host: "
msgstr ""
#: source/pythonpath/main.py:669
msgid "Port: "
msgstr ""
#: source/pythonpath/main.py:690
msgid "User: "
msgstr ""
#: source/pythonpath/main.py:711
msgid "Password: "
msgstr ""
#: source/pythonpath/main.py:733
msgid "Save"
msgstr ""
#: source/pythonpath/main.py:745
msgid "Delete"
msgstr ""

View File

@ -5,84 +5,155 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2019-10-19 15:19-0500\n"
"PO-Revision-Date: 2019-10-19 15:20-0500\n"
"POT-Creation-Date: 2022-03-26 11:23-0600\n"
"PO-Revision-Date: 2021-01-21 16:32-0500\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: en\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.4\n"
"Last-Translator: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: en\n"
#: source/ZAZPip.py:49
msgid "Do you want install PIP?"
#: source/pythonpath/main.py:81
msgid "Do you want to install 'Python installer package' (PIP)?"
msgstr ""
#: source/ZAZPip.py:67
msgid "Do you have internet connection?"
#: source/pythonpath/main.py:93
msgid "Downloading PIP…"
msgstr ""
#: source/ZAZPip.py:73
msgid "File PIP not save"
#: source/pythonpath/main.py:96
msgid "Unable to connect to internet"
msgstr ""
#: source/ZAZPip.py:76
msgid "PIP save correctly..."
#: source/pythonpath/main.py:102
msgid "Unable to copy PIP installation file"
msgstr ""
#: source/ZAZPip.py:80
msgid "Start installing PIP..."
#: source/pythonpath/main.py:105
msgid "PIP installation file has been saved"
msgstr ""
#: source/ZAZPip.py:94
msgid "PIP installed sucesfully"
#: source/pythonpath/main.py:108
msgid "Starting PIP installation…"
msgstr ""
#: source/ZAZPip.py:97
msgid "PIP not installed, see log"
#: source/pythonpath/main.py:122
msgid "PIP was installed sucessfully"
msgstr ""
#: source/ZAZPip.py:173
msgid "Not found..."
#: source/pythonpath/main.py:125
msgid "PIP installation has failed, see log"
msgstr ""
#: source/ZAZPip.py:209
#: source/pythonpath/main.py:198
msgid "Package not found…"
msgstr ""
#: source/pythonpath/main.py:213
msgid "Enter the package name to install"
msgstr ""
#: source/pythonpath/main.py:218
msgid "Do you really want to install package: {}?"
msgstr ""
#: source/pythonpath/main.py:247
msgid "install"
msgstr ""
#: source/pythonpath/main.py:249
msgid "upgrade"
msgstr ""
#: source/pythonpath/main.py:252
msgid ""
"Do you want {}:\n"
"Do you want to {}:\n"
"\n"
"{} ?"
"{}?"
msgstr ""
#: source/ZAZPip.py:235
#: source/pythonpath/main.py:278
msgid "Select installed package"
msgstr ""
#: source/ZAZPip.py:240
#: source/pythonpath/main.py:283
msgid ""
"Do you want uninstall:\n"
"Do you want to uninstall:\n"
"\n"
"{} ?"
"{}?"
msgstr ""
#: source/ZAZPip.py:327
msgid "PIP not installed"
#: source/pythonpath/main.py:310
msgid ""
"Do you want to install from:\n"
"\n"
"{}"
msgstr ""
#: source/ZAZPip.py:346
#: source/pythonpath/main.py:337
msgid "Do you want to save this Proxy settings?"
msgstr ""
#: source/pythonpath/main.py:343
msgid "Proxy settings saved sucessfully"
msgstr ""
#: source/pythonpath/main.py:349
msgid "Do you want to remove Proxy settings?"
msgstr ""
#: source/pythonpath/main.py:355
msgid "Proxy settings deleted sucessfully"
msgstr ""
#: source/pythonpath/main.py:413
msgid "'Python installer package' (PIP) is not installed"
msgstr ""
#: source/pythonpath/main.py:432
msgid "Admin PIP"
msgstr ""
#: source/ZAZPip.py:359
#: source/pythonpath/main.py:445
msgid "Install PIP"
msgstr ""
#: source/ZAZPip.py:397
#: source/pythonpath/main.py:494
msgid "Packages"
msgstr ""
#: source/ZAZPip.py:429
#: source/pythonpath/main.py:536
msgid "~Close"
msgstr ""
#: source/pythonpath/main.py:635
msgid "Zaz-Pip - Proxy settings"
msgstr ""
#: source/pythonpath/main.py:646
msgid "Host: "
msgstr ""
#: source/pythonpath/main.py:669
msgid "Port: "
msgstr ""
#: source/pythonpath/main.py:690
msgid "User: "
msgstr ""
#: source/pythonpath/main.py:711
msgid "Password: "
msgstr ""
#: source/pythonpath/main.py:733
msgid "Save"
msgstr ""
#: source/pythonpath/main.py:745
msgid "Delete"
msgstr ""

View File

@ -5,90 +5,182 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2019-10-19 15:19-0500\n"
"PO-Revision-Date: 2019-10-19 15:24-0500\n"
"POT-Creation-Date: 2022-03-26 11:23-0600\n"
"PO-Revision-Date: 2022-03-26 11:28-0600\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es\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.4\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.0.1\n"
#: source/ZAZPip.py:49
msgid "Do you want install PIP?"
msgstr "¿Desea instalar PIP?"
#: source/pythonpath/main.py:81
msgid "Do you want to install 'Python installer package' (PIP)?"
msgstr "¿Desea instalar el instalador de paquetes Python (PIP)?"
#: source/ZAZPip.py:67
msgid "Do you have internet connection?"
msgstr "¿Tiene conexión a Internet?"
#: source/pythonpath/main.py:93
msgid "Downloading PIP…"
msgstr "Descargando PIP…"
#: source/ZAZPip.py:73
msgid "File PIP not save"
msgstr "No se guardo el archivo PIP"
#: source/pythonpath/main.py:96
msgid "Unable to connect to internet"
msgstr "No se puede conectar a Internet"
#: source/ZAZPip.py:76
msgid "PIP save correctly..."
msgstr "Archivo PIP guardado correctamente..."
#: source/pythonpath/main.py:102
msgid "Unable to copy PIP installation file"
msgstr "No se puede copiar el archivo de instalación de PIP"
#: source/ZAZPip.py:80
msgid "Start installing PIP..."
msgstr "Iniciando instalación de PIP..."
#: source/pythonpath/main.py:105
msgid "PIP installation file has been saved"
msgstr "Se ha guardado el archivo de instalación de PIP"
#: source/ZAZPip.py:94
msgid "PIP installed sucesfully"
#: source/pythonpath/main.py:108
msgid "Starting PIP installation…"
msgstr "Iniciando la instalación de PIP…"
#: source/pythonpath/main.py:122
msgid "PIP was installed sucessfully"
msgstr "PIP instalado correctamente"
#: source/ZAZPip.py:97
msgid "PIP not installed, see log"
msgstr "PIP no fue instalado, vea el registro"
#: source/pythonpath/main.py:125
msgid "PIP installation has failed, see log"
msgstr "Ha fallado la instalación de PIP, vea el registro"
#: source/ZAZPip.py:173
msgid "Not found..."
msgstr "No encontrado..."
#: source/pythonpath/main.py:198
msgid "Package not found…"
msgstr "Paquete no encontrado…"
#: source/ZAZPip.py:209
#: source/pythonpath/main.py:213
msgid "Enter the package name to install"
msgstr "Introduzca el nombre del paquete a instalar"
#: source/pythonpath/main.py:218
msgid "Do you really want to install package: {}?"
msgstr "¿Desea instalar el paquete: {}?"
#: source/pythonpath/main.py:247
msgid "install"
msgstr "instalar"
#: source/pythonpath/main.py:249
msgid "upgrade"
msgstr "actualizar"
#: source/pythonpath/main.py:252
msgid ""
"Do you want {}:\n"
"Do you want to {}:\n"
"\n"
"{} ?"
"{}?"
msgstr ""
"¿Desea {}:\n"
"\n"
"{} ?"
#: source/ZAZPip.py:235
#: source/pythonpath/main.py:278
msgid "Select installed package"
msgstr "Seleccione un paquete instalado"
#: source/ZAZPip.py:240
#: source/pythonpath/main.py:283
msgid ""
"Do you want uninstall:\n"
"Do you want to uninstall:\n"
"\n"
"{} ?"
"{}?"
msgstr ""
"¿Desea desinstalar:\n"
"\n"
"{} ?"
#: source/ZAZPip.py:327
msgid "PIP not installed"
msgstr "PIP no esta instalado"
#: source/pythonpath/main.py:310
msgid ""
"Do you want to install from:\n"
"\n"
"{}"
msgstr ""
"¿Desea desinstalar desde:\n"
"\n"
"{} ?"
#: source/ZAZPip.py:346
#: source/pythonpath/main.py:337
msgid "Do you want to save this Proxy settings?"
msgstr "¿Desea guardar la configuración del Proxy?"
#: source/pythonpath/main.py:343
msgid "Proxy settings saved sucessfully"
msgstr "PIP instalado correctamente"
#: source/pythonpath/main.py:349
msgid "Do you want to remove Proxy settings?"
msgstr "¿Desea eliminar la configuración del Proxy?"
#: source/pythonpath/main.py:355
msgid "Proxy settings deleted sucessfully"
msgstr "Configuración del Proxy borrada correctamente"
#: source/pythonpath/main.py:413
msgid "'Python installer package' (PIP) is not installed"
msgstr "El \"instalador de paquetes de Python\" (PIP) no está instalado"
#: source/pythonpath/main.py:432
msgid "Admin PIP"
msgstr "Administrar PIP"
#: source/ZAZPip.py:359
#: source/pythonpath/main.py:445
msgid "Install PIP"
msgstr "Instalar PIP"
#: source/ZAZPip.py:397
#: source/pythonpath/main.py:494
msgid "Packages"
msgstr "Paquetes"
#: source/ZAZPip.py:429
#: source/pythonpath/main.py:536
msgid "~Close"
msgstr "~Cerrar"
#: source/pythonpath/main.py:635
msgid "Zaz-Pip - Proxy settings"
msgstr "Zaz-Pip - Configuración del Proxy"
#: source/pythonpath/main.py:646
msgid "Host: "
msgstr "Host: "
#: source/pythonpath/main.py:669
msgid "Port: "
msgstr "Puerto: "
#: source/pythonpath/main.py:690
msgid "User: "
msgstr "Usuario: "
#: source/pythonpath/main.py:711
msgid "Password: "
msgstr "Contraseña: "
#: source/pythonpath/main.py:733
msgid "Save"
msgstr "Guardar"
#: source/pythonpath/main.py:745
msgid "Delete"
msgstr "Eliminar"
#~ msgid "Do you have internet connection?"
#~ msgstr "¿Tiene conexión a Internet?"
#~ msgid "File PIP not save"
#~ msgstr "No se guardo el archivo PIP"
#~ msgid "PIP save correctly..."
#~ msgstr "Archivo PIP guardado correctamente..."
#~ msgid "Start installing PIP..."
#~ msgstr "Iniciando instalación de PIP..."
#~ msgid "Not found..."
#~ msgstr "No encontrado..."
#~ msgid "PIP not installed"
#~ msgstr "PIP no esta instalado"

Binary file not shown.

View File

@ -0,0 +1,168 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2022-03-26 11:23-0600\n"
"PO-Revision-Date: 2022-03-26 11:41-0600\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr_FR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.0.1\n"
#: source/pythonpath/main.py:81
msgid "Do you want to install 'Python installer package' (PIP)?"
msgstr "Souhaitez-vous installer 'Python installer package' (PIP) ?"
#: source/pythonpath/main.py:93
msgid "Downloading PIP…"
msgstr "Téléchargement de PIP…"
#: source/pythonpath/main.py:96
msgid "Unable to connect to internet"
msgstr "Pas de connection à internet"
#: source/pythonpath/main.py:102
msgid "Unable to copy PIP installation file"
msgstr "La réception du fichier d'installation de PIP a échoué"
#: source/pythonpath/main.py:105
msgid "PIP installation file has been saved"
msgstr "Fichier d'installation de PIP reçu"
#: source/pythonpath/main.py:108
msgid "Starting PIP installation…"
msgstr "Démarrage de l'installation de PIP…"
#: source/pythonpath/main.py:122
msgid "PIP was installed sucessfully"
msgstr "PIP est maintenant installé"
#: source/pythonpath/main.py:125
msgid "PIP installation has failed, see log"
msgstr "Echec à l'installation de PIP, consultez le fichier Log'"
#: source/pythonpath/main.py:198
msgid "Package not found…"
msgstr "Paquet non trouvé…"
#: source/pythonpath/main.py:213
msgid "Enter the package name to install"
msgstr "Nom du paquet à installer"
#: source/pythonpath/main.py:218
msgid "Do you really want to install package: {}?"
msgstr "Souhaitez-vous désinstaller :{} ?"
#: source/pythonpath/main.py:247
msgid "install"
msgstr "installer"
#: source/pythonpath/main.py:249
msgid "upgrade"
msgstr "mise à niveau"
#: source/pythonpath/main.py:252
msgid ""
"Do you want to {}:\n"
"\n"
"{}?"
msgstr ""
"Souhaitez-vous {}\n"
"\n"
"\"{}"
#: source/pythonpath/main.py:278
msgid "Select installed package"
msgstr "Choisissez un paquet déjà installé"
#: source/pythonpath/main.py:283
msgid ""
"Do you want to uninstall:\n"
"\n"
"{}?"
msgstr ""
"Souhaitez-vous désinstaller :\n"
"\n"
"{} ?"
#: source/pythonpath/main.py:310
msgid ""
"Do you want to install from:\n"
"\n"
"{}"
msgstr ""
"Souhaitez-vous installer :\n"
"\n"
"{} ?"
#: source/pythonpath/main.py:337
msgid "Do you want to save this Proxy settings?"
msgstr "Enregistrer les données du mandataire (Proxy) ?"
#: source/pythonpath/main.py:343
msgid "Proxy settings saved sucessfully"
msgstr "Les données mandataire (Proxy) sont enregistrées"
#: source/pythonpath/main.py:349
msgid "Do you want to remove Proxy settings?"
msgstr "Supprimer les données du mandataire (Proxy)?"
#: source/pythonpath/main.py:355
msgid "Proxy settings deleted sucessfully"
msgstr "Les données mandataire (Proxy) sont supprimées"
#: source/pythonpath/main.py:413
msgid "'Python installer package' (PIP) is not installed"
msgstr "'Python installer package' (PIP) absent"
#: source/pythonpath/main.py:432
msgid "Admin PIP"
msgstr "Administrer PIP"
#: source/pythonpath/main.py:445
msgid "Install PIP"
msgstr "Installer PIP"
#: source/pythonpath/main.py:494
msgid "Packages"
msgstr "Paquets"
#: source/pythonpath/main.py:536
msgid "~Close"
msgstr "~Fermer"
#: source/pythonpath/main.py:635
msgid "Zaz-Pip - Proxy settings"
msgstr "Zaz-PIP Données mandataire (Proxy)"
#: source/pythonpath/main.py:646
msgid "Host: "
msgstr "Hôte: "
#: source/pythonpath/main.py:669
msgid "Port: "
msgstr "Port: "
#: source/pythonpath/main.py:690
msgid "User: "
msgstr "Utilisateur: "
#: source/pythonpath/main.py:711
msgid "Password: "
msgstr "Mot de passe: "
#: source/pythonpath/main.py:733
msgid "Save"
msgstr "Enregistrer"
#: source/pythonpath/main.py:745
msgid "Delete"
msgstr "Supprimer"

57
source/pip_runner.py Normal file
View File

@ -0,0 +1,57 @@
# region Imports
from __future__ import unicode_literals, annotations
import os
import sys
from typing import TYPE_CHECKING, Tuple
from pathlib import Path
import uno
import unohelper
from com.sun.star.task import XJob
if TYPE_CHECKING:
# just for design time
from com.sun.star.beans import NamedValue
# endregion Imports
# region Constants
implementation_name = "net.elmau.zaz.pip.PipRunner"
implementation_services = ("com.sun.star.task.Job",)
# endregion Constants
# region XJob
class PipRunner(unohelper.Base, XJob):
def __init__(self, ctx):
self._is_flatpak = bool(os.getenv("FLATPAK_ID", ""))
if self._is_flatpak:
site_packages = self.get_flatpak_site_packages_dir()
ss = str(site_packages)
if site_packages.exists() and ss not in sys.path:
sys.path.append(ss)
def execute(self, *args: Tuple[NamedValue, ...]) -> None:
pass
def get_flatpak_site_packages_dir(self) -> Path:
# should never be all users
sand_box = os.getenv("FLATPAK_SANDBOX_DIR", "") or str(
Path.home() / ".var/app/org.libreoffice.LibreOffice/sandbox"
)
major_minor = f"{sys.version_info.major}.{sys.version_info.minor}"
site_packages = Path(sand_box) / f"lib/python{major_minor}/site-packages"
site_packages.mkdir(parents=True, exist_ok=True)
return site_packages
# endregion XJob
# region Implementation
g_TypeTable = {}
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(PipRunner, implementation_name, implementation_services)
# endregion Implementation

View File

@ -21,6 +21,7 @@
import base64
import csv
import ctypes
import datetime
import getpass
import gettext
@ -30,6 +31,7 @@ import logging
import os
import platform
import re
import site
import shlex
import shutil
import socket
@ -82,8 +84,9 @@ from com.sun.star.util import Time, Date, DateTime
from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
from com.sun.star.awt import XActionListener
from com.sun.star.lang import Locale
from com.sun.star.lang import XEventListener
from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMenuListener
from com.sun.star.awt import XMouseListener
from com.sun.star.awt import XMouseMotionListener
@ -165,6 +168,9 @@ OBJ_GRAPHIC = 'SwXTextGraphicObject'
OBJ_TEXTS = 'SwXTextRanges'
OBJ_TEXT = 'SwXTextRange'
IS_FLATPAK = bool(os.getenv("FLATPAK_ID", ""))
IS_APP_IMAGE = bool(os.getenv("APPIMAGE", ""))
# ~ from com.sun.star.sheet.FilterOperator import EMPTY, NO_EMPTY, EQUAL, NOT_EQUAL
@ -203,6 +209,12 @@ class ConditionOperator():
CO = ConditionOperator
class DataPilotFieldOrientation():
from com.sun.star.sheet.DataPilotFieldOrientation \
import HIDDEN, COLUMN, ROW, PAGE, DATA
DPFO = DataPilotFieldOrientation
OS = platform.system()
IS_WIN = OS == 'Windows'
IS_MAC = OS == 'Darwin'
@ -308,6 +320,11 @@ def get_app_config(node_name: str, key: str=''):
LANGUAGE = get_app_config('org.openoffice.Setup/L10N/', 'ooLocale')
LANG = LANGUAGE.split('-')[0]
try:
COUNTRY = LANGUAGE.split('-')[1]
except:
COUNTRY = ''
LOCALE = Locale(LANG, COUNTRY, '')
NAME = TITLE = get_app_config('org.openoffice.Setup/Product', 'ooName')
VERSION = get_app_config('org.openoffice.Setup/Product','ooSetupVersion')
@ -372,6 +389,8 @@ def mri(obj: Any) -> None:
error(msg)
return
if hasattr(obj, 'obj'):
obj = obj.obj
m.inspect(obj)
return
@ -580,13 +599,24 @@ def call_macro(args, in_thread=False):
result = _call_macro(args)
return result
def get_env():
"""
Gets Environment used for subprocess.
"""
my_env = os.environ.copy()
py_path = ""
p_sep = ";" if IS_WIN else ":"
for d in sys.path:
py_path = py_path + d + p_sep
my_env["PYTHONPATH"] = py_path
return my_env
def run(command, capture=False, split=True):
if not split:
return subprocess.check_output(command, shell=True).decode()
return subprocess.check_output(command, shell=True, env=get_env()).decode()
cmd = shlex.split(command)
result = subprocess.run(cmd, capture_output=capture, text=True, shell=IS_WIN)
result = subprocess.run(cmd, capture_output=capture, text=True, shell=IS_WIN, env=get_env())
if capture:
result = result.stdout
else:
@ -597,7 +627,7 @@ def run(command, capture=False, split=True):
def popen(command):
try:
proc = subprocess.Popen(shlex.split(command), shell=IS_WIN,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=get_env())
for line in proc.stdout:
yield line.decode().rstrip()
except Exception as e:
@ -610,6 +640,60 @@ def sleep(seconds):
return
def get_site_packages_dir() -> str:
"""Gets the site-packages folder for the current user."""
major_minor = f"{sys.version_info.major}.{sys.version_info.minor}"
def get_windows_site_packages_dir() -> str:
nonlocal major_minor
if site.USER_SITE:
site_packages = Path(site.USER_SITE).resolve()
else:
site_packages = (
Path.home() / f"'/AppData/Roaming/Python/Python{major_minor}/site-packages'"
)
site_packages.mkdir(parents=True, exist_ok=True)
return str(site_packages)
def get_flatpak_site_packages_dir() -> str:
# should never be all users
nonlocal major_minor
sand_box = os.getenv("FLATPAK_SANDBOX_DIR", "") or str(
Path.home() / ".var/app/org.libreoffice.LibreOffice/sandbox"
)
site_packages = Path(sand_box) / f"lib/python{major_minor}/site-packages"
site_packages.mkdir(parents=True, exist_ok=True)
return str(site_packages)
def get_mac_site_packages_dir() -> str:
nonlocal major_minor
if site.USER_SITE:
site_packages = Path(site.USER_SITE).resolve()
else:
site_packages = (
Path.home() / f"Library/LibreOfficePython/{major_minor}/lib/python/site-packages"
)
site_packages.mkdir(parents=True, exist_ok=True)
return str(site_packages)
def get_default_site_packages_dir() -> str:
nonlocal major_minor
if site.USER_SITE:
site_packages = Path(site.USER_SITE).resolve()
else:
site_packages = Path.home() / f".local/lib/python{major_minor}/site-packages"
site_packages.mkdir(parents=True, exist_ok=True)
return str(site_packages)
if IS_WIN:
return get_windows_site_packages_dir()
if IS_MAC:
return get_mac_site_packages_dir()
if IS_FLATPAK:
return get_flatpak_site_packages_dir()
return get_default_site_packages_dir()
class TimerThread(threading.Thread):
def __init__(self, event, seconds, macro):
@ -1166,6 +1250,9 @@ class LODocument(object):
return
def to_pdf(self, path: str='', args: dict={}):
"""
https://wiki.documentfoundation.org/Macros/Python_Guide/PDF_export_filter_data
"""
path_pdf = path
filter_name = '{}_pdf_Export'.format(self.type)
filter_data = dict_to_property(args, True)
@ -1520,6 +1607,142 @@ class LOSheetCharts(object):
return LOChart(name, self.obj[name], self._sheet.draw_page)
class LOSheetTableField(object):
def __init__(self, obj):
self._obj = obj
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
@property
def obj(self):
return self._obj
@property
def name(self):
return self.obj.Name
@property
def orientation(self):
return self.obj.Orientation
@orientation.setter
def orientation(self, value):
self.obj.Orientation = value
# ~ com.sun.star.sheet.DataPilotFieldOrientation.ROW
class LOSheetTable(object):
def __init__(self, obj):
self._obj = obj
self._source = None
def __getitem__(self, index):
field = self.obj.DataPilotFields[index]
return LOSheetTableField(field)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
@property
def obj(self):
return self._obj
@property
def filter(self):
return self.obj.ShowFilterButton
@filter.setter
def filter(self, value):
self.obj.ShowFilterButton = value
@property
def source(self):
return self._source
@source.setter
def source(self, value):
self._source = value
self.obj.SourceRange = value.range_address
@property
def rows(self):
return self.obj.RowFields
@rows.setter
def rows(self, values):
if not isinstance(values, tuple):
values = (values,)
for v in values:
with self[v] as f:
f.orientation = DPFO.ROW
@property
def columns(self):
return self.obj.ColumnFields
@columns.setter
def columns(self, values):
if not isinstance(values, tuple):
values = (values,)
for v in values:
with self[v] as f:
f.orientation = DPFO.COLUMN
@property
def data(self):
return self.obj.DataFields
@data.setter
def data(self, values):
if not isinstance(values, tuple):
values = (values,)
for v in values:
with self[v] as f:
f.orientation = DPFO.DATA
class LOSheetTables(object):
def __init__(self, obj, sheet):
self._obj = obj
self._sheet = sheet
def __getitem__(self, index):
return LOSheetTable(self.obj[index])
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __contains__(self, item):
return item in self.obj
@property
def obj(self):
return self._obj
@property
def count(self):
return self.obj.Count
@property
def names(self):
return self.obj.ElementNames
def new(self, name, target):
table = self.obj.createDataPilotDescriptor()
self.obj.insertNewByName(name, target.address, table)
return LOSheetTable(self.obj[name])
def remove(self, name):
self.obj.removeByName(name)
return
class LOFormControl(LOBaseObject):
EVENTS = {
'action': 'actionPerformed',
@ -2045,6 +2268,10 @@ class LOCalcSheet(object):
def charts(self):
return LOSheetCharts(self.obj.Charts, self)
@property
def tables(self):
return LOSheetTables(self.obj.DataPilotTables, self)
@property
def rows(self):
return LOSheetRows(self, self.obj.Rows)
@ -2558,6 +2785,7 @@ class LOCalcRange(object):
cell = None
if isinstance(value, dict):
for k, v in value.items():
# ~ print(1, 'RENDER', k, v)
cell = self._render_value(k, v, key)
return cell
elif isinstance(value, (list, tuple)):
@ -2569,7 +2797,11 @@ class LOCalcRange(object):
search = f'{{{parent}.{key}}}'
ranges = self.find_all(search)
for cell in ranges or range(0):
if ranges is None:
return
# ~ for cell in ranges or range(0):
for cell in ranges:
self._set_new_value(cell, search, value)
return LOCalcRange(cell)
@ -2718,6 +2950,72 @@ class LOCalcRange(object):
self.obj.fillAuto(0, source)
return
def _cast(self, t, v):
if not t:
return v
if t == datetime.date:
nv = datetime.date.fromordinal(int(v) + DATE_OFFSET)
else:
nv = t(v)
return nv
def get_data(self, types):
values = [
[self._cast(types[i], v) for i, v in enumerate(row)]
for row in self.data
]
return values
class LOWriterStyles(object):
def __init__(self, styles):
self._styles = styles
@property
def names(self):
return {s.DisplayName: s.Name for s in self._styles}
def __str__(self):
return '\n'.join(tuple(self.names.values()))
class LOWriterStylesFamilies(object):
def __init__(self, styles):
self._styles = styles
def __getitem__(self, index):
styles = {
'Character': 'CharacterStyles',
'Paragraph': 'ParagraphStyles',
'Page': 'PageStyles',
'Frame': 'FrameStyles',
'Numbering': 'NumberingStyles',
'Table': 'TableStyles',
'Cell': 'CellStyles',
}
name = styles.get(index, index)
return LOWriterStyles(self._styles[name])
def __iter__(self):
self._index = 0
return self
def __next__(self):
obj = LOWriterStyles(self._styles[self._index])
self._index += 1
return obj
# ~ raise StopIteration
@property
def names(self):
return self._styles.ElementNames
def __str__(self):
return '\n'.join(self.names)
class LOWriterPageStyle(LOBaseObject):
@ -2755,18 +3053,23 @@ class LOWriterTextRange(object):
self._doc = doc
self._is_paragraph = self.obj.ImplementationName == 'SwXParagraph'
self._is_table = self.obj.ImplementationName == 'SwXTextTable'
self._is_text = self.obj.ImplementationName == 'SwXTextPortion'
self._parts = []
if self._is_paragraph:
self._parts = [LOWriterTextRange(p, doc) for p in obj]
def __iter__(self):
self._index = 0
return self
def __next__(self):
for i, p in enumerate(self.obj):
if i == self._index:
obj = LOWriterTextRange(p, self._doc)
self._index += 1
return obj
raise StopIteration
try:
obj = self._parts[self._index]
except IndexError:
raise StopIteration
self._index += 1
return obj
@property
def obj(self):
@ -2785,11 +3088,37 @@ class LOWriterTextRange(object):
@property
def value(self):
return self.string
@value.setter
def value(self, value):
self.string = value
@property
def style(self):
s = ''
if self.is_paragraph:
s = self.obj.ParaStyleName
elif self.is_text:
s = self.obj.CharStyleName
return s
@style.setter
def style(self, value):
if self.is_paragraph:
self.obj.ParaStyleName = value
elif self.is_text:
self.obj.CharStyleName = value
@property
def is_paragraph(self):
return self._is_paragraph
@property
def is_table(self):
return self._is_table
@property
def is_text(self):
return self._is_text
@property
def text(self):
return self.obj.Text
@ -2802,6 +3131,13 @@ class LOWriterTextRange(object):
def dp(self):
return self._doc.dp
def delete(self):
cursor = self.cursor
cursor.gotoStartOfParagraph(False)
cursor.gotoNextParagraph(True)
cursor.String = ''
return
def offset(self):
cursor = self.cursor.getEnd()
return LOWriterTextRange(cursor, self._doc)
@ -2846,25 +3182,26 @@ class LOWriterTextRanges(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
self._paragraphs = [LOWriterTextRange(p, doc) for p in obj]
def __len__(self):
return len(self._paragraphs)
def __getitem__(self, index):
for i, p in enumerate(self.obj):
if i == index:
obj = LOWriterTextRange(p, self._doc)
break
return obj
return self._paragraphs[index]
def __iter__(self):
self._index = 0
return self
def __next__(self):
for i, p in enumerate(self.obj):
if i == self._index:
obj = LOWriterTextRange(p, self._doc)
self._index += 1
return obj
raise StopIteration
try:
obj = self._paragraphs[self._index]
except IndexError:
raise StopIteration
self._index += 1
return obj
@property
def obj(self):
@ -2887,10 +3224,17 @@ class LOWriterTextTable(object):
@property
def data(self):
return self._obj.DataArray
return self.obj.DataArray
@data.setter
def data(self, values):
self._obj.DataArray = values
self.obj.DataArray = values
@property
def style(self):
return self.obj.TableTemplateName
@style.setter
def style(self, value):
self.obj.autoFormat(value)
class LOWriterTextTables(object):
@ -2920,7 +3264,7 @@ class LOWriter(LODocument):
@property
def text(self):
return LOWriterTextRange(self.obj.Text, self)
return self.paragraphs
@property
def paragraphs(self):
@ -2973,6 +3317,10 @@ class LOWriter(LODocument):
ps = self.obj.StyleFamilies['PageStyles']
return LOWriterPageStyles(ps)
@property
def styles(self):
return LOWriterStylesFamilies(self.obj.StyleFamilies)
@property
def search_descriptor(self):
return self.obj.createSearchDescriptor()
@ -3403,15 +3751,19 @@ class BaseRow:
class BaseQuery(object):
PY_TYPES = {
'SQL_LONG': 'getLong',
'SQL_VARYING': 'getString',
'SQL_FLOAT': 'getFloat',
'SQL_BOOLEAN': 'getBoolean',
'SQL_TYPE_DATE': 'getDate',
'SQL_TYPE_TIME': 'getTime',
'SQL_TIMESTAMP': 'getTimestamp',
'VARCHAR': 'getString',
'INTEGER': 'getLong',
'DATE': 'getDate',
# ~ 'SQL_LONG': 'getLong',
# ~ 'SQL_VARYING': 'getString',
# ~ 'SQL_FLOAT': 'getFloat',
# ~ 'SQL_BOOLEAN': 'getBoolean',
# ~ 'SQL_TYPE_DATE': 'getDate',
# ~ 'SQL_TYPE_TIME': 'getTime',
# ~ 'SQL_TIMESTAMP': 'getTimestamp',
}
TYPES_DATE = ('SQL_TYPE_DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
# ~ TYPES_DATE = ('SQL_TYPE_DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
TYPES_DATE = ('DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
def __init__(self, query):
self._query = query
@ -3437,6 +3789,7 @@ class BaseQuery(object):
def _to_python(self, index):
type_field = self._meta.getColumnTypeName(index)
# ~ print('TF', type_field)
value = getattr(self._query, self.PY_TYPES[type_field])(index)
if type_field in self.TYPES_DATE:
value = _struct_to_date(value)
@ -3562,10 +3915,11 @@ class LOBase(object):
self._con.getTables().refresh()
return
def initialize(self, database_proxy, tables):
def initialize(self, database_proxy, tables=[]):
db = FirebirdDatabase(self)
database_proxy.initialize(db)
db.create_tables(tables)
if tables:
db.create_tables(tables)
return
def _validate_sql(self, sql, params):
@ -3676,6 +4030,7 @@ class LODocs(object):
return doc
def __len__(self):
# ~ len(self._desktop.Components)
for i, _ in enumerate(self._desktop.Components):
pass
return i + 1
@ -3860,6 +4215,7 @@ class EventsFocus(EventsListenerBase, XFocusListener):
if service in self.CONTROLS:
obj = event.Source.Model
obj.BackgroundColor = COLOR_ON_FOCUS
obj.TextColor = TEXT_COLOR_ON_FOCUS
return
def focusLost(self, event):
@ -4445,6 +4801,13 @@ class UnoText(UnoBaseObject):
def value(self, value):
self.model.Text = value
@property
def echochar(self):
return chr(self.model.EchoChar)
@echochar.setter
def echochar(self, value):
self.model.EchoChar = ord(value[0])
def validate(self):
return
@ -5285,6 +5648,11 @@ class LODialog(object):
self.obj.dispose()
return value
def set_values(self, data):
for k, v in data.items():
self._controls[k].value = v
return
class LOSheets(object):
@ -5832,6 +6200,7 @@ _CB = ClipBoard
class Paths(object):
FILE_PICKER = 'com.sun.star.ui.dialogs.FilePicker'
FOLDER_PICKER = 'com.sun.star.ui.dialogs.FolderPicker'
def __init__(self, path=''):
if path.startswith('file://'):
@ -5885,7 +6254,10 @@ class Paths(object):
elif IS_MAC:
path = self.join(self.config('Module'), '..', 'Resources', PYTHON)
else:
path = sys.executable
if IS_APP_IMAGE:
path = self.join(self.config("Module"), PYTHON)
else:
path = sys.executable
return path
@classmethod
@ -5941,7 +6313,7 @@ class Paths(object):
@classmethod
def get_dir(cls, init_dir=''):
folder_picker = create_instance(cls.FILE_PICKER)
folder_picker = create_instance(cls.FOLDER_PICKER)
if not init_dir:
init_dir = cls.documents
init_dir = cls.to_url(init_dir)
@ -5950,7 +6322,7 @@ class Paths(object):
path = ''
if folder_picker.execute():
path = cls.to_system(folder_picker.getDisplayDirectory())
path = cls.to_system(folder_picker.getDirectory())
return path
@classmethod
@ -6007,7 +6379,7 @@ class Paths(object):
if IS_WIN:
os.startfile(path)
else:
pid = subprocess.Popen(['xdg-open', path]).pid
pid = subprocess.Popen(['xdg-open', path, ], env=get_env()).pid
return
@classmethod
@ -6219,6 +6591,50 @@ class Paths(object):
_P = Paths
class Dates(object):
@classmethod
def date(cls, year, month, day):
d = datetime.date(year, month, day)
return d
class SpellChecker(object):
def __init__(self):
service = 'com.sun.star.linguistic2.SpellChecker'
self._spellchecker = create_instance(service, True)
self._locale = LOCALE
@property
def locale(self):
slocal = f'{self._locale.Language}-{self._locale.Country}'
return slocale
@locale.setter
def locale(self, value):
lang = value.split('-')
self._locale = Locale(lang[0], lang[1], '')
def is_valid(self, word):
result = self._spellchecker.isValid(word, self._locale, ())
return result
def spell(self, word):
result = self._spellchecker.spell(word, self._locale, ())
if result:
result = result.getAlternatives()
if not isinstance(result, tuple):
result = ()
return result
def spell(word, locale=''):
sc = SpellChecker()
if locale:
sc.locale = locale
return sc.spell(word)
def __getattr__(name):
if name == 'active':
return LODocs().active
@ -6244,6 +6660,8 @@ def __getattr__(name):
return LOShortCuts()
if name == 'clipboard':
return ClipBoard
if name == 'dates':
return Dates
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
@ -6330,48 +6748,6 @@ def get_fonts():
return device.FontDescriptors
# ~ From request
# ~ https://github.com/psf/requests/blob/master/requests/structures.py#L15
class CaseInsensitiveDict(MutableMapping):
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."""
values = (
(lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items()
)
return values
# Copy is required
def copy(self):
return CaseInsensitiveDict(self._store.values())
def __repr__(self):
return str(dict(self.items()))
# ~ https://en.wikipedia.org/wiki/Web_colors
def get_color(value):
COLORS = {
@ -6536,6 +6912,7 @@ def get_color(value):
COLOR_ON_FOCUS = get_color('LightYellow')
TEXT_COLOR_ON_FOCUS = get_color('black')
class LOServer(object):
@ -6587,7 +6964,7 @@ class LOServer(object):
for i in range(3):
self._server = subprocess.Popen(self.CMD,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=get_env())
time.sleep(3)
if self.is_running:
break

View File

@ -0,0 +1,214 @@
from __future__ import annotations
from typing import cast
import os
import sys
import tempfile
import subprocess
from pathlib import Path
from typing import Any, List, Dict
import easymacro as app
from contextlib import contextmanager
import uno
@contextmanager
def change_dir(directory):
"""
A context manager that changes the current working directory to the specified directory
temporarily and then changes it back when the context is exited.
"""
current_dir = os.getcwd()
os.chdir(directory)
try:
yield
finally:
os.chdir(current_dir)
class InstallPipFromWheel:
"""Download and install PIP from wheel url"""
def __init__(self, pip_wheel_url: str, lo_identifier: str) -> None:
self._pip_url = pip_wheel_url
self._lo_identifier = lo_identifier
def install(self, dst: str | Path = "") -> None:
"""
Install pip from wheel file.
Downloads the pip wheel file from the url provided in the config file and unzips it to the destination directory.
Args:
dst (str | Path, Optional): The destination directory where the pip wheel file will be installed. If not provided, the ``pythonpath`` location will be used.
Returns:
None:
Raises:
None:
"""
if not self._pip_url:
app.error("PIP installation has failed - No wheel url")
return
if not dst:
root_pth = Path(app.Paths.from_id(self._lo_identifier))
dst = root_pth / "pythonpath"
with tempfile.TemporaryDirectory() as temp_dir:
# temp_dir = tempfile.gettempdir()
path_pip = Path(temp_dir)
filename = path_pip / "pip-wheel.whl"
data, _, err = app.url_open(self._pip_url, verify=False)
if err:
app.error("Unable to download PIP installation wheel file")
return
filename.write_bytes(data)
if filename.exists():
app.info("PIP wheel file has been saved")
else:
app.error("PIP wheel file has not been saved")
return
try:
self._unzip_wheel(filename=filename, dst=dst)
except Exception:
return
# now that pip has been installed from wheel force a reinstall to ensure it is the latest version
self._force_install_pip()
def _unzip_wheel(self, filename: Path, dst: str | Path) -> None:
"""Unzip the downloaded wheel file"""
if isinstance(dst, str):
dst = Path(dst)
try:
# app.zip.unzip(source=str(filename), target=str(dst))
self.unzip_file(zip_file=filename, dest_dir=dst)
if dst.exists():
app.debug(f"PIP wheel file has been unzipped to {dst}")
else:
app.error("PIP wheel file has not been unzipped")
raise Exception("PIP wheel file has not been unzipped")
except Exception as err:
app.error(f"Unable to unzip wheel file: {err}", exc_info=True)
raise
def _force_install_pip(self) -> None:
"""Now that pip has been installed, force reinstall it to ensure it is the latest version"""
cmd = [app.paths.python, "-m", "pip", "install", "--upgrade", "pip"]
app.popen(command=" ".join(cmd))
def unzip_file(self, zip_file: str | Path, dest_dir: str | Path = "") -> None:
"""
Unzip the given zip file to the specified destination directory.
Args:
zip_file (str | Path): The zip file to unzip.
dest_dir (str | Path, optional): The destination directory to unzip to.
Returns:
None:
"""
from zipfile import ZipFile
zip_file_path = Path(zip_file) if isinstance(zip_file, str) else zip_file
if not zip_file_path.is_file():
raise ValueError(f"Expected file, got '{zip_file_path}'")
if not zip_file_path.is_absolute():
zip_file_path = zip_file_path.absolute()
if not zip_file_path.exists():
raise FileNotFoundError(f"File '{zip_file_path}' not found")
if isinstance(dest_dir, str):
dest_dir = zip_file_path.parent if dest_dir == "" else Path(dest_dir)
else:
dest_dir = dest_dir.absolute()
if not dest_dir.is_dir():
raise ValueError(f"Expected folder, got '{dest_dir}'")
if not dest_dir.exists():
try:
dest_dir.mkdir(parents=True)
except Exception as e:
raise FileNotFoundError(
f"Folder '{dest_dir}' not found, unable to create folder."
) from e
if not dest_dir.exists():
raise FileNotFoundError(f"Folder '{dest_dir}' not found")
with change_dir(dest_dir):
with ZipFile(zip_file_path) as f:
f.extractall(dest_dir)
# with change_dir(dest_dir):
# shutil.unpack_archive(zip_file_path, dest_dir)
class FlatpakInstaller:
"""class for the PIP install."""
def __init__(self, pip_wheel_url: str, lo_identifier: str) -> None:
self.path_python = app.Paths.python
app.debug(f"Python path: {self.path_python}")
self._pip_url = pip_wheel_url
self._lo_identifier = lo_identifier
self._site_packages = cast(str, app.get_flatpak_site_packages_dir())
def install_pip(self) -> None:
if self.is_pip_installed():
app.info("PIP is already installed")
return
if self._install_wheel():
if self.is_pip_installed():
app.info("PIP was installed successfully")
else:
app.error("PIP installation has failed")
return
def _get_pip_cmd(self, filename: Path) -> List[str]:
return [str(self.path_python), f"{filename}", "--user"]
def _get_env(self) -> Dict[str, str]:
"""
Gets Environment used for subprocess.
"""
my_env = os.environ.copy()
py_path = ""
p_sep = ";" if app.IS_WIN else ":"
for d in sys.path:
py_path = py_path + d + p_sep
my_env["PYTHONPATH"] = py_path
return my_env
def _cmd_pip(self, *args: str) -> List[str]:
cmd: List[str] = [str(self.path_python), "-m", "pip", *args]
return cmd
def _install_wheel(self) -> bool:
result = False
installer = InstallPipFromWheel(
pip_wheel_url=self._pip_url, lo_identifier=self._lo_identifier
)
try:
installer.install(self._site_packages)
if self._site_packages not in sys.path:
sys.path.append(self._site_packages)
result = True
except Exception as err:
app.error(err)
return result
return result
def is_pip_installed(self) -> bool:
"""Check if PIP is installed."""
# cmd = self._cmd_pip("--version")
# cmd = '"{}" -m pip -V'.format(self.path_python)
cmd = [str(self.path_python), "-m", "pip", "-V"]
result = subprocess.run(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self._get_env()
)
return result.returncode == 0

View File

@ -0,0 +1,146 @@
"""
On some systems such as Mac and AppImage (Linux) the python extension suffix does not match the
cpython suffix used by the embedded python interpreter.
This class creates symlinks for all .so files in dest folder that match the current python embedded suffix.
For example a file named ``indexers.cpython-38-x86_64-linux-gnu.so`` would be symlinked to ``indexers.cpython-3.8.so``.
This renaming allows the python interpreter to find the import.
"""
from __future__ import annotations
from typing import List
from pathlib import Path
from importlib import machinery
import easymacro as app
class LinkCPython:
def __init__(self, pth: str) -> None:
"""
Constructor
Args:
pth (str): Path to site-packages folder.
"""
self._current_suffix = self._get_current_suffix()
app.debug("CPythonLink.__init__")
self._link_root = Path(pth)
if not self._link_root.exists():
raise FileNotFoundError(f"Path does not exist {self._link_root}")
app.debug("CPythonLink.__init__ done")
def _get_current_suffix(self) -> str:
"""Gets suffix currently used by the embedded python interpreter such as ``cpython-3.8``"""
for suffix in machinery.EXTENSION_SUFFIXES:
if suffix.startswith(".cpython-") and suffix.endswith(".so"):
# remove leading . and trailing .so
return suffix[1:][:-3]
return ""
def _get_all_files(self, path: Path) -> List[Path]:
return [p for p in path.glob(f"**/*{self.file_suffix}.so") if p.is_file()]
def _get_all_links(self, path: Path) -> List[Path]:
return [p for p in path.glob(f"**/*{self.current_suffix}.so") if p.is_symlink()]
def _create_symlink(self, src: Path, dst: Path, overwrite: bool) -> None:
if dst.is_symlink():
if overwrite:
app.debug(f"Removing existing symlink {dst}")
dst.unlink()
else:
app.debug(f"Symlink already exists {dst}")
return
dst.symlink_to(src)
app.debug(f"Created symlink {dst} -> {src}")
def _find_current_installed_suffix(self, path: Path) -> str:
"""
Finds the current suffix from the current installed python so files such as ``cpython-38-x86_64-linux-gnu``.
Args:
path (Path): Path to search in. Usually site-packages.
Returns:
str: suffix if found, otherwise empty string.
"""
return next(
(str(p).rsplit(".", 2)[1] for p in path.glob("**/*.cpython-*.so") if not p.is_symlink()),
"",
)
def link(self, overwrite:bool = False) -> None:
"""
Creates symlinks for all .so files in site-packages that match the current suffix.
Args:
overwrite (bool, optional): Override any existing sys links. Defaults to False.
"""
app.debug("CPythonLink.link starting")
if not self._link_root:
app.debug("No site-packages found")
return
if not self.file_suffix:
app.debug("No current file suffix found")
return
if not self._link_root.exists():
app.debug(f"Site-packages does not exist {self._link_root}")
return
app.debug(f"Python current suffix: {self._current_suffix}")
app.debug(f"Found file suffix: {self.file_suffix}")
files = self._get_all_files(self._link_root)
if not files:
app.debug(f"No files found in {self._link_root}")
return
cp_old = self.file_suffix
cp_new = self._current_suffix
if cp_old == cp_new:
app.debug(f"Suffixes match, no need to link: {cp_old} == {cp_new}")
return
for file in files:
ln_name = file.name.replace(cp_old, cp_new)
src = file
if not src.is_absolute():
src = file.resolve()
dst = src.parent / ln_name
self._create_symlink(src, dst, overwrite)
app.debug("CPythonLink.link done")
def unlink(self) -> None:
"""Unlinks all broken sys links"""
links = self._get_all_links(self._link_root)
if not links:
app.debug(f"No links found in {self._link_root}")
return
for link in links:
if not link.exists():
app.debug(f"Removing broken symlink {link}")
link.unlink()
# region Properties
@property
def cpy_name(self) -> str:
"""Gets/Sets CPython name, e.g. cpython-3.8"""
return self._current_suffix
@cpy_name.setter
def cpy_name(self, value: str) -> None:
self._current_suffix = value
@property
def current_suffix(self) -> str:
"""Current Suffix such as ``cpython-3.8``"""
return self._current_suffix
@property
def file_suffix(self) -> str:
"""Current Suffix such as ``cpython-38-x86_64-linux-gnu``"""
try:
return self._file_suffix
except AttributeError:
self._file_suffix = self._find_current_installed_suffix(self._link_root)
return self._file_suffix
# endregion Properties

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python3
import os
import easymacro as app
@ -9,8 +10,10 @@ _ = None
TITLE = 'ZAZ-PIP'
URL_PIP = 'https://bootstrap.pypa.io/get-pip.py'
URL_PIP_WHEEL = 'https://files.pythonhosted.org/packages/47/6a/453160888fab7c6a432a6e25f8afe6256d0d9f2cbd25971021da6491d899/pip-23.3.1-py3-none-any.whl'
URL_TEST = 'http://duckduckgo.com'
PIP = 'pip'
URL_GIT = 'https://git.elmau.net/elmau'
URL_GIT = 'https://git.cuates.net/elmau/zaz-pip'
ICON_OK = 'ok.svg'
ICON_QUESTION = 'question.svg'
@ -31,14 +34,28 @@ PACKAGES = {
def open_dialog_pip():
dialog = _create_dialog()
proxy = app.get_config('proxy', {}, TITLE)
if proxy:
app.debug(proxy)
HOST = proxy['txt_proxy_host']
PORT = proxy['txt_proxy_port']
USER = proxy['txt_proxy_user']
PASS = proxy['txt_proxy_pass']
os.environ['http_proxy'] = f'http://{USER}:{PASS}@{HOST}:{PORT}'
os.environ['https_proxy'] = f'http://{USER}:{PASS}@{HOST}:{PORT}'
dialog.open()
return
def run(args, path_locales):
def run(path_locales):
global _
_ = app.install_locales(path_locales)
globals()[args]()
open_dialog_pip()
return
@ -62,34 +79,33 @@ class Controllers(object):
return
def cmd_install_pip_action(self, event):
msg = _('Do you want install PIP?')
msg = _("Do you want to install 'Python installer package' (PIP)?")
if not app.question(msg, TITLE):
return
self._install_pip()
return
@app.run_in_thread
def _install_pip(self):
def _install_pip_normal(self) -> None:
self.d.link_proyect.visible = False
self.d.lst_log.visible = True
path_pip = app.paths.tmp()
self.d.lst_log.insert(_('Download PIP...'))
self.d.lst_log.insert(_('Downloading PIP…'))
data, h, err = app.url_open(URL_PIP, verify=False)
if err:
msg = _('Do you have internet connection?')
msg = _('Unable to connect to internet')
app.errorbox('{}\n\n{}'.format(msg, err))
return
app.paths.save_bin(path_pip, data)
if not app.paths.exists(path_pip):
msg = _('File PIP not save')
msg = _('Unable to copy PIP installation file')
app.errorbox(msg)
return
self.d.lst_log.insert(_('PIP save correctly...'))
self.d.lst_log.insert(_('PIP installation file has been saved'))
try:
self.d.lst_log.insert(_('Start installing PIP...'))
self.d.lst_log.insert(_("Starting PIP installation…"))
cmd = f'"{self.path_python}" "{path_pip}" --user'
for line in app.popen(cmd):
if isinstance(line, tuple):
@ -103,16 +119,45 @@ class Controllers(object):
self.d.lbl_pip.value = label
self.d.cmd_install_pip.visible = False
self.d.cmd_admin_pip.visible = True
msg = _('PIP installed sucesfully')
msg = _('PIP was installed successfully')
app.msgbox(msg)
else:
msg = _('PIP not installed, see log')
msg = _('PIP installation has failed, see log')
app.warning(msg)
except Exception as e:
app.errorbox(e)
return
def _install_pip_flatpak(self) -> None:
try:
from install_flatpak import FlatpakInstaller
installer = FlatpakInstaller(pip_wheel_url=URL_PIP_WHEEL, lo_identifier=ID_EXTENSION)
installer.install_pip()
msg = _('PIP was installed successfully')
# app.msgbox(msg)
cmd = self._cmd_pip('-V')
label = app.run(cmd, True)
if label:
self.d.lbl_pip.value = label
self.d.cmd_install_pip.visible = False
self.d.cmd_admin_pip.visible = True
msg = _('PIP was installed successfully')
app.msgbox(msg)
else:
msg = _('PIP installation has failed, see log')
app.warning(msg)
except Exception as e:
app.errorbox(e)
@app.run_in_thread
def _install_pip(self):
if app.IS_FLATPAK:
self._install_pip_flatpak()
else:
self._install_pip_normal()
return
def _cmd_pip(self, args):
cmd = '"{}" -m pip {}'.format(self.path_python, args)
return cmd
@ -135,7 +180,8 @@ class Controllers(object):
def txt_search_key_released(self, event):
if event.KeyCode == app.KEY['enter']:
self.cmd_search_action(None)
# ~ self.cmd_search_action(None)
self.cmd_install_action(None)
return
if not self.d.txt_search.value.strip():
@ -179,7 +225,7 @@ class Controllers(object):
if line:
self.d.lst_package.select()
else:
self.d.lst_package.insert(_('Not found...'), 'error.png', show=False)
self.d.lst_package.insert(_('Package not found…'), 'error.png', show=False)
return
def cmd_search_action(self, event):
@ -194,12 +240,12 @@ class Controllers(object):
def cmd_install_action(self, event):
name = self.d.txt_search.value.strip()
if not name:
msg = _('Enter package name to install')
msg = _('Enter the package name to install')
app.warning(msg)
self.d.txt_search.set_focus()
return
msg = _(f'Install package: {name} ?')
msg = _('Do you really want to install package: {}?').format(name)
if not app.question(msg):
return
@ -213,7 +259,10 @@ class Controllers(object):
self.d.lst_log.visible = True
line = ''
cmd = ' install --upgrade --user'
if app.IS_FLATPAK:
cmd = f' install --upgrade --target={app.get_site_packages_dir()}'
else:
cmd = ' install --upgrade --user'
if value:
name = value.split(' ')[0].strip()
cmd = self._cmd_pip(f'{cmd} {name}')
@ -225,15 +274,40 @@ class Controllers(object):
self.d.lst_log.insert(line, 'ok.png')
else:
self.d.lst_log.insert(line)
self._link_cpython()
return
def _link_cpython(self):
if not app.IS_MAC and not app.IS_APP_IMAGE:
return
try:
app.debug("Linking CPython")
from link_cpython import LinkCPython
cpy_link = LinkCPython(pth=app.get_site_packages_dir())
cpy_link.link()
except Exception as err:
app.error(err)
return
def _unlink_cpython(self):
if not app.IS_MAC and not app.IS_APP_IMAGE:
return
try:
app.debug("Unlinking CPython")
from link_cpython import LinkCPython
cpy_link = LinkCPython(pth=app.get_site_packages_dir())
cpy_link.unlink()
except Exception as err:
app.error(err)
return
def lst_package_double_click(self, event):
opt = 'install'
opt = _('install')
if self._states['list']:
opt = 'upgrade'
opt = _('upgrade')
name = self.d.lst_package.value
msg = _('Do you want {}:\n\n{} ?').format(opt, name)
msg = _('Do you want to {}:\n\n{}?').format(opt, name)
if not app.question(msg, TITLE):
return
@ -255,6 +329,7 @@ class Controllers(object):
self.d.lst_log.insert(line, 'ok.png')
else:
self.d.lst_log.insert(line)
self._unlink_cpython()
return
def cmd_uninstall_action(self, event):
@ -264,7 +339,7 @@ class Controllers(object):
return
name = self.d.lst_package.value
msg = _('Do you want uninstall:\n\n{} ?').format(name)
msg = _('Do you want to uninstall:\n\n{}?').format(name)
if not app.question(msg):
return
@ -291,13 +366,56 @@ class Controllers(object):
if not path:
return
msg = _(f'Confirm install from:\n\n{path}')
msg = _('Do you want to install from:\n\n{}').format(path)
if not app.question(msg):
return
self._install(path=path)
return
def cmd_proxy_action(self, event):
dialog = _create_dialog_proxy()
data = app.get_config('proxy', {}, TITLE)
dialog.set_values(data)
dialog.open()
return
class ControllersProxy(object):
def __init__(self, dialog):
self.d = dialog
def cmd_proxy_save_action(self, event):
data = dict(
txt_proxy_host = self.d.txt_proxy_host.value,
txt_proxy_port = self.d.txt_proxy_port.value,
txt_proxy_user = self.d.txt_proxy_user.value,
txt_proxy_pass = self.d.txt_proxy_pass.value,
)
msg = _('Do you want to save this Proxy settings?')
if not app.question(msg):
return
app.set_config('proxy', data, TITLE)
msg = _('Proxy settings saved sucessfully')
app.msgbox(msg)
self.d.close()
return
def cmd_proxy_delete_action(self, event):
msg = _("Do you want to remove Proxy settings?")
if not app.question(msg):
return
app.set_config('proxy', {}, TITLE)
msg = _('Proxy settings deleted sucessfully')
app.msgbox(msg)
self.d.close()
return
def _create_dialog():
BUTTON_WH = 20
@ -351,7 +469,7 @@ def _create_dialog():
exists_pip = True
if not label:
exists_pip = False
label = _('PIP not installed')
label = _("'Python installer package' (PIP) is not installed")
args = {
'Type': 'Label',
'Name': 'lbl_pip',
@ -408,6 +526,17 @@ def _create_dialog():
dialog.add_control(args)
dialog.center(dialog.link_proyect, y=-5)
args = {
'Type': 'Button',
'Name': 'cmd_proxy',
'Width': 15,
'Height': 15,
'Step': 10,
'ImageURL': 'proxy.svg',
'FocusOnClick': False,
}
dialog.add_control(args)
args = {
'Type': 'Listbox',
'Name': 'lst_log',
@ -534,6 +663,10 @@ def _create_dialog():
}
dialog.add_control(args)
dialog.cmd_proxy.move(dialog.link_proyect, x=5, y=0)
controls = (dialog.link_proyect, dialog.cmd_proxy)
dialog.center(controls)
controls = (dialog.cmd_home, dialog.cmd_search,
dialog.cmd_install, dialog.cmd_uninstall, dialog.cmd_shell)
dialog.lbl_package.move(dialog.cmd_home)
@ -544,12 +677,150 @@ def _create_dialog():
dialog.lst_package.center()
dialog.txt_search.center()
dialog.center(controls)
dialog.center((dialog.txt_search, dialog.cmd_explore))
controls = (dialog.txt_search, dialog.cmd_explore)
dialog.center(controls)
dialog.step = 10
dialog.cmd_install_pip.visible = not exists_pip
dialog.cmd_admin_pip.visible = exists_pip
dialog.lst_log.visible = False
return dialog
def _create_dialog_proxy():
args= {
'Name': 'dlg_proxy',
'Title': _('Zaz-Pip - Proxy settings'),
'Width': 150,
'Height': 100,
}
dialog = app.create_dialog(args)
dialog.id = ID_EXTENSION
dialog.events = ControllersProxy
args = {
'Type': 'Label',
'Name': 'lbl_proxy_host',
'Label': _('Host: '),
'Width': 40,
'Height': 12,
'Border': 1,
'Align': 2,
'VerticalAlign': 1,
'X': 5,
'Y': 5,
}
dialog.add_control(args)
args = {
'Type': 'Text',
'Name': 'txt_proxy_host',
'Width': 95,
'Height': 12,
'Border': 0,
}
dialog.add_control(args)
args = {
'Type': 'Label',
'Name': 'lbl_proxy_port',
'Label': _('Port: '),
'Width': 40,
'Height': 12,
'Border': 1,
'Align': 2,
'VerticalAlign': 1,
}
dialog.add_control(args)
args = {
'Type': 'Text',
'Name': 'txt_proxy_port',
'Width': 95,
'Height': 12,
'Border': 0,
}
dialog.add_control(args)
args = {
'Type': 'Label',
'Name': 'lbl_proxy_user',
'Label': _('User: '),
'Width': 40,
'Height': 12,
'Border': 1,
'Align': 2,
'VerticalAlign': 1,
}
dialog.add_control(args)
args = {
'Type': 'Text',
'Name': 'txt_proxy_user',
'Width': 95,
'Height': 12,
'Border': 0,
}
dialog.add_control(args)
args = {
'Type': 'Label',
'Name': 'lbl_proxy_pass',
'Label': _('Password: '),
'Width': 40,
'Height': 12,
'Border': 1,
'Align': 2,
'VerticalAlign': 1,
}
dialog.add_control(args)
args = {
'Type': 'Text',
'Name': 'txt_proxy_pass',
'Width': 95,
'Height': 12,
'Border': 0,
}
txt = dialog.add_control(args)
txt.echochar = '*'
args = {
'Type': 'Button',
'Name': 'cmd_proxy_save',
'Label': _('Save'),
'Width': 50,
'Height': 15,
'ImageURL': 'save.svg',
'ImagePosition': 1,
'FocusOnClick': False,
}
dialog.add_control(args)
args = {
'Type': 'Button',
'Name': 'cmd_proxy_delete',
'Label': _('Delete'),
'Width': 50,
'Height': 15,
'ImageURL': 'delete.svg',
'ImagePosition': 1,
'FocusOnClick': False,
}
dialog.add_control(args)
dialog.txt_proxy_host.move(dialog.lbl_proxy_host, x=5, y=0)
dialog.lbl_proxy_port.move(dialog.lbl_proxy_host)
dialog.txt_proxy_port.move(dialog.lbl_proxy_port, x=5, y=0)
dialog.lbl_proxy_user.move(dialog.lbl_proxy_port)
dialog.txt_proxy_user.move(dialog.lbl_proxy_user, x=5, y=0)
dialog.lbl_proxy_pass.move(dialog.lbl_proxy_user)
dialog.txt_proxy_pass.move(dialog.lbl_proxy_pass, x=5, y=0)
dialog.cmd_proxy_save.move(dialog.lbl_proxy_pass, y=10)
dialog.cmd_proxy_delete.move(dialog.lbl_proxy_pass, y=10)
controls = (dialog.cmd_proxy_save, dialog.cmd_proxy_delete)
dialog.center(controls)
return dialog

View File

@ -0,0 +1,14 @@
This file is part of ZAZPip.
ZAZPip is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ZAZPip is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ZAZPip. If not, see <https://www.gnu.org/licenses/>.

29
zaz.py
View File

@ -75,10 +75,16 @@ class LiboXML(object):
'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
'xmlns:loext': 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0',
}
xmlns = 'http://openoffice.org/extensions/description/2006'
xmlns_xlink = 'http://www.w3.org/1999/xlink'
xmlns_l = 'http://libreoffice.org/extensions/description/2011'
NS_DESCRIPTION = {
'xmlns': 'http://openoffice.org/extensions/description/2006',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'xmlns:d': 'http://openoffice.org/extensions/description/2006',
'xmlns': xmlns,
'xmlns:xlink': xmlns_xlink,
'xmlns:d': xmlns,
'xmlns:l': xmlns_l,
}
NS_ADDONS = {
'xmlns:xs': 'http://www.w3.org/2001/XMLSchema',
@ -87,7 +93,7 @@ class LiboXML(object):
NS_UPDATE = {
'xmlns': 'http://openoffice.org/extensions/update/2006',
'xmlns:d': 'http://openoffice.org/extensions/description/2006',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'xmlns:xlink': xmlns_xlink,
}
def __init__(self):
@ -158,6 +164,13 @@ class LiboXML(object):
key = 'version'
ET.SubElement(doc, key, data[key])
key = 'minimal'
if data[key]:
node = ET.SubElement(doc, 'dependencies')
# ~ attr = {'value': data[key], 'name': f'LibreOffice {data[key]}'}
attr = {'value': data[key]}
ET.SubElement(node, 'l:LibreOffice-minimal-version', attr)
key = 'display-name'
node = ET.SubElement(doc, key)
for k, v in data[key].items():
@ -413,14 +426,14 @@ def _compress_oxt():
z.write(fullpath, file_name, zipfile.ZIP_DEFLATED)
z.close()
log.info('Extension OXT created sucesfully...')
log.info('Extension OXT created successfully...')
return
def _install_and_test():
path_oxt = (_join(DIRS['files'], FILES['oxt']),)
call(PATHS['install'] + path_oxt)
log.info('Install extension sucesfully...')
log.info('Install extension successfully...')
log.info('Start LibreOffice...')
call(PATHS['soffice'])
return
@ -525,7 +538,7 @@ def _compile_idl():
call([PATHS['regmerge'], path_rdb, '/UCR', path_urd])
os.remove(path_urd)
log.info('Compilate IDL sucesfully...')
log.info('Compilate IDL successfully...')
return
@ -742,7 +755,7 @@ def _new(args):
path = _join(args.target, args.name)
_mkdir(path)
_mkdir(_join(path, 'files'))
_mkdir(_join(path, 'ext'))
_mkdir(_join(path, 'images'))
path_logo = 'images/pymacros.png'
copyfile(path_logo, _join(path, 'images/logo.png'))