Add actions to controls

This commit is contained in:
El Mau 2023-12-07 19:08:49 -06:00
parent 3aaec4df56
commit d0c5f3a8ed
53 changed files with 439 additions and 28 deletions

View File

@ -1,10 +1,15 @@
v 0.5.0 [07-Dec-2023]
---------------------
- Add acctions to controls.
- Refactorize documetation.
v 0.4.0 [20-Sep-2023] v 0.4.0 [20-Sep-2023]
--------------------- ---------------------
- Add control layout manager - Add control layout manager.
v 0.3.0 [23-Apr-2023] v 0.3.0 [23-Apr-2023]
--------------------- ---------------------
- Add method post - Add method post.
v 0.2.0 [23-Sep-2022] v 0.2.0 [23-Sep-2022]
--------------------- ---------------------

View File

@ -14,11 +14,11 @@ This library have a cost of maintenance of 1 euro every year.
In order of preferred. In order of preferred.
* G1: `A5DdXxCKPw3QKWVdDVs7CzkNugNUW1sHu5zDJFWxCU2h`
Mauricio Baeza Mauricio Baeza
``` ```
Euros Euros
IBAN: BE60 9671 0556 5870 IBAN: BE60 9671 0556 5870
SWIFT / BIC: TRWIBEB1XXX SWIFT / BIC: TRWIBEB1XXX
``` ```
* G1: `A5DdXxCKPw3QKWVdDVs7CzkNugNUW1sHu5zDJFWxCU2h`

View File

@ -1 +1 @@
0.4.0 0.5.0

198
docs/en/docs/debug.md Normal file
View File

@ -0,0 +1,198 @@
## Tools for debug
<br>
### **INFO_DEBUG**
Show debugging information in a message box.
If you have any problems in your code during the development of your macros, you can [open a ticket][1] support in the system ticket of this project. Always copy the debugging information shown in your issue.
```py
import easymacro as app
def info():
app.msgbox(app.INFO_DEBUG)
return
```
![Info debug](img/install_01.png)
### **debug**
Show information at the terminal.
```py
import easymacro as app
def test_debug():
msg = 'Verify this information...'
app.debug(msg)
return
```
To view this message, you need to start LibreOffice from the command line:
```
soffice --calc
```
After executing the previous macro, you should see:
```
21/04/2023 17:04:49 - DEBUG - Verify this information...
```
<br>
### **info**
Show information messages at the terminal.
```py
import easymacro as app
def test_info():
msg = 'Starting process...'
app.info(msg)
return
```
```
11/08/2022 18:23:53 - INFO - Starting process...
```
<br>
### **error**
Show error messages at the terminal.
```py
import easymacro as app
def test_error():
msg = 'Error 505'
app.error(msg)
return
```
```
11/08/2022 18:27:34 - ERROR - Error 505
```
<br>
### **save_log**
Save log in file, automatically add date and time.
```py
import easymacro as app
def test_save_log():
app.save_log('/home/mau/log.txt', 'PyUNO')
app.save_log('/home/mau/log.txt', 'World Damn')
return
```
```
cat ~/log.txt
2022-08-11 18:30:11 - 'PyUNO'
2022-08-11 18:30:11 - 'World Damn'
```
<br>
### **msgbox**
Show any information in a message box.
```python
import easymacro as app
def message():
msg = 'Please, consume less.'
app.msgbox(msg)
msg = ('one', 2, 'three')
app.msgbox(msg)
msg = {'name': 'Teresa'}
app.msgbox(msg)
app.msgbox(app)
return
```
<br>
### **catch_exception**
Capture any error that occurs when running a macro.
!!! warning inline end "Caution"
Use this method only in development time. **Do not use it in production**.
```py
import easymacro as app
@app.catch_exception
def test_capture_error():
r = 1 / 0
return
```
```
11/08/2022 18:44:36 - ERROR - test_capture_error
Traceback (most recent call last):
File "/home/mau/.config/libreoffice/4/user/Scripts/python/pythonpath/easymacro/easytools.py", line 115, in func
return f(*args, **kwargs)
File "/home/mau/.config/libreoffice/4/user/Scripts/python/test.py", line 18, in test_capturar_error
r = 1 / 0
ZeroDivisionError: division by zero
```
<br>
### **mri**
[MRI][2] is the best extension to inspect any UNO LibreOffice object. You need to install it first so you can use it.
```py
import easymacro as app
def inspect_object():
obj = app.active
app.mri(obj)
return
```
<br>
### **inspect**
Inspect an object.
```py
import easymacro as app
def inspect_object():
doc = app.active
data = app.inspect(doc)
for p in data.properties:
app.debug(p)
for m in data.methods:
app.debug(m)
return
```
Send information of the object to worksheet.
```python
def inspect_object():
doc = app.active
app.inspect(doc, True)
return
```
[1]: https://git.cuates.net/elmau/easymacro/issues
[2]: https://github.com/hanya/MRI

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 380 B

29
docs/en/docs/index.md Normal file
View File

@ -0,0 +1,29 @@
# Welcome to EasyMacro's documentation
**Free Software, not gratis software**
<br>
**easymacro** is a library to develop more easily macros in LibreOffice with Python. It is an abstraction layer between the extensive and complex UNO LibreOffice API and your code.
You will probably be happier if you use it :)
You can use **easymacro** with any extension or directly on your macros.
<br>
- Symily or related projects in Python:
* [python-ooo-dev-tools](https://python-ooo-dev-tools.readthedocs.io)
* [oooscript](https://oooscript.readthedocs.io)
* [python-ooouno-ex](https://github.com/Amourspirit/python-ooouno-ex)
- Symily or related projects in other languages:
* [Java LibreOffice Programming](https://flywire.github.io/lo-p/)
<br>
Contributions:
- Moneda Libre Ğ1 (Junas):<br>
`A5DdXxCKPw3QKWVdDVs7CzkNugNUW1sHu5zDJFWxCU2h`
<br>

47
docs/en/docs/install.md Normal file
View File

@ -0,0 +1,47 @@
## Clone repository
Clone the repository in your favorite project directory.
```
git clone https://git.cuates.net/elmau/easymacro
```
Move into the directory
```
cd easymacro/source
```
copy the `easymacro` folder into the **pythonpath** folder in the Python macro folder within your user profile. Replace **USER** by your real user.
```
/home/USER/.config/libreoffice/4/user/Scripts/python/pythonpath
```
or use a symbolic link (recommended). Replace **PATH** by the absolute route where **easymacro** is located in your file system and **USER** by your username.
```
ln -s PATH/easymacro/source/easymacro /home/USER/.config/libreoffice/4/user/Scripts/python/pythonpath/
```
## Test
In your favorite macros file, for example **mymacros.py**. Use your favorite plain text editor or IDE. Remember, replace **USER** by your real user.
```
geany /home/USER/.config/libreoffice/4/user/Scripts/python/mymacros.py
```
Copy the following code:
```py
import easymacro as app
def main():
app.msgbox(app.INFO_DEBUG)
return
```
Run the **main** macro in LibreOffice, if you see a message box with information similar to the following!
![Test install](img/install_01.png)
It is all ready to start developing macros with **easymacro**.
Happy programming!

40
docs/en/mkdocs.yml Normal file
View File

@ -0,0 +1,40 @@
site_name: EasyMacro for LibreOffice
site_url: https://doc.cuates.net/easymacro/
repo_url: https://git.cuates.net/elmau/easymacro/
nav:
- Home: index.md
- Install: install.md
- Debug: debug.md
theme:
name: material
locale: en
font: false
highlightjs: true
palette:
primary: green
features:
- navigation.path
- content.code.copy
- content.code.select
markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.tabbed:
alternate_style: true
- tables
- pymdownx.keys
extra:
alternate:
- name: English
link: /
lang: en
- name: Español
link: /langs/es
lang: es

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/es/docs/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

View File

@ -43,7 +43,7 @@ nav:
- examples/index.md - examples/index.md
theme: theme:
name: material name: material
locale: es locale: en
font: false font: false
highlightjs: true highlightjs: true
palette: palette:
@ -66,11 +66,11 @@ markdown_extensions:
alternate_style: true alternate_style: true
- tables - tables
- pymdownx.keys - pymdownx.keys
#~ extra: extra:
#~ alternate: alternate:
#~ - name: Español - name: English
#~ link: / link: /
#~ lang: es lang: en
#~ - name: English - name: Español
#~ link: /en/ link: /langs/es
#~ lang: en lang: es

View File

@ -1,5 +1,22 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# ~ easymacro - for easily develop macros in LibreOffice
# ~ Copyright (C) 2020-2023 Mauricio Baeza (elmau)
# ~ This program 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.
# ~ This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
from .easymain import * from .easymain import *
from .easydialog import * from .easydialog import *
from .easytools import * from .easytools import *
@ -8,6 +25,10 @@ from .easydocs import LODocuments
from .easydrawpage import LOGalleries from .easydrawpage import LOGalleries
__version__ = '0.5.0'
__author__ = 'Mauricio Baeza (elMau)'
def __getattr__(name): def __getattr__(name):
classes = { classes = {
'active': LODocuments().active, 'active': LODocuments().active,

View File

@ -8,7 +8,7 @@ from com.sun.star.sheet import CellFlags
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
from .easymain import (log, DATE_OFFSET, from .easymain import (log, DATE_OFFSET,
BaseObject, Color, BaseObject, Color, LOMain,
dict_to_property, run_in_thread, set_properties dict_to_property, run_in_thread, set_properties
) )
from .easydoc import LODocument from .easydoc import LODocument
@ -624,6 +624,20 @@ class LOCalcRange():
cols = ra.EndColumn - ra.StartColumn + 1 cols = ra.EndColumn - ra.StartColumn + 1
return rows, cols return rows, cols
def select(self):
""" """
self.doc.select(self.obj)
return
def to_image(self):
""" """
self.select()
self.doc.copy()
args = {'SelectedFormat': 141}
url = 'ClipboardFormatItems'
LOMain.dispatch(self.doc.frame, url, args)
return self.sheet.shapes[-1]
class LOCalcSheet(BaseObject): class LOCalcSheet(BaseObject):

View File

@ -101,6 +101,37 @@ def add_listeners(events, control):
return return
# ~ getAccessibleActionKeyBinding
class UnoActions():
ACTIONS = {
'press': 0,
'activate': 0,
}
def __init__(self, obj: Any):
self._obj = obj
self._ac = obj.AccessibleContext
self._actions = hasattr(self._ac, 'AccessibleActionCount')
def __str__(self):
return ', '.join(self.get())
def get(self):
actions = ()
if self._actions:
actions = [self._ac.getAccessibleActionDescription(i) for i in
range(self._ac.AccessibleActionCount)]
return actions
def press(self):
result = self._ac.doAccessibleAction(self.ACTIONS['press'])
return result
def activate(self):
result = self._ac.doAccessibleAction(self.ACTIONS['activate'])
return result
class UnoBaseObject(object): class UnoBaseObject(object):
def __init__(self, obj: Any): def __init__(self, obj: Any):
@ -317,6 +348,10 @@ class UnoBaseObject(object):
def ps(self, ps): def ps(self, ps):
self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE) self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
@property
def actions(self):
return UnoActions(self.obj)
def set_focus(self): def set_focus(self):
self.obj.setFocus() self.obj.setFocus()
return return
@ -469,7 +504,7 @@ class UnoImage(UnoBaseObject):
@property @property
def type(self): def type(self):
return 'button' return 'image'
@property @property
def value(self): def value(self):
@ -547,6 +582,7 @@ class DialogBox(BaseObject):
obj = self._create(properties) obj = self._create(properties)
super().__init__(obj) super().__init__(obj)
self._init_controls() self._init_controls()
self._events = None
self._modal = False self._modal = False
def _create_from_path(self, path: str): def _create_from_path(self, path: str):
@ -705,7 +741,7 @@ class DialogBox(BaseObject):
return Paths.to_url(path) return Paths.to_url(path)
def _special_properties(self, tipo, properties): def _special_properties(self, tipo, properties):
if tipo == 'link' and not 'Label' in properties: if tipo == 'link' and 'URL' in properties and not 'Label' in properties:
properties['Label'] = properties['URL'] properties['Label'] = properties['URL']
return properties return properties

View File

@ -46,6 +46,9 @@ class LOLayoutManager(BaseObject):
self.obj.hideElement(name) self.obj.hideElement(name)
return return
# ~ def create(self, name):
# ~ return self.obj.createElement(name)
class LODocument(BaseObject): class LODocument(BaseObject):
@ -238,6 +241,8 @@ class LODocument(BaseObject):
:type args: dict :type args: dict
:return: None if path or stream in memory :return: None if path or stream in memory
:rtype: bytes or None :rtype: bytes or None
https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
""" """
FILTERS = { FILTERS = {
'xlsx': 'Calc MS Excel 2007 XML', 'xlsx': 'Calc MS Excel 2007 XML',
@ -249,8 +254,11 @@ class LODocument(BaseObject):
stream = None stream = None
path_target = 'private:stream' path_target = 'private:stream'
if filter_name == 'svg':
filter_name = f'{self.type}_svg_Export'
else:
filter_name = FILTERS.get(filter_name, filter_name)
filter_name = FILTERS.get(filter_name, filter_name)
filter_data = dict_to_property(args, True) filter_data = dict_to_property(args, True)
filters = { filters = {
'FilterName': filter_name, 'FilterName': filter_name,
@ -266,7 +274,7 @@ class LODocument(BaseObject):
try: try:
self.obj.storeToURL(path_target, opt) self.obj.storeToURL(path_target, opt)
except Exception as e: except Exception as e:
error(e) log.error(e)
if not stream is None: if not stream is None:
stream = stream.buffer stream = stream.buffer
@ -284,6 +292,11 @@ class LODocument(BaseObject):
LOMain.dispatch(self.frame, 'Copy') LOMain.dispatch(self.frame, 'Copy')
return return
def cut(self):
"""Copy current selection"""
LOMain.dispatch(self.frame, 'Cut')
return
def paste(self): def paste(self):
"""Paste current content in clipboard""" """Paste current content in clipboard"""
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard') sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
@ -314,6 +327,9 @@ class LODocument(BaseObject):
self.obj.getUndoManager().clear() self.obj.getUndoManager().clear()
return return
def replace_ext(self, new_ext):
return Paths.with_suffix(self.path, new_ext)
class LODrawImpress(LODocument): class LODrawImpress(LODocument):

View File

@ -537,7 +537,7 @@ class LOMain():
return cls._set_app_command(command, True) return cls._set_app_command(command, True)
@classmethod @classmethod
def enabled(cls, command) -> bool: def enabled(cls, command: str) -> bool:
"""Enabled UNO command """Enabled UNO command
:param command: UNO command to enabled :param command: UNO command to enabled
@ -599,13 +599,14 @@ class LOMain():
class ClipBoard(object): class ClipBoard(object):
SERVICE = 'com.sun.star.datatransfer.clipboard.SystemClipboard' SERVICE = 'com.sun.star.datatransfer.clipboard.SystemClipboard'
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16' TEXT = 'text/plain;charset=utf-16'
IMAGE = 'application/x-openoffice-bitmap;windows_formatname="Bitmap"'
class TextTransferable(unohelper.Base, XTransferable): class TextTransferable(unohelper.Base, XTransferable):
def __init__(self, text): def __init__(self, text):
df = DataFlavor() df = DataFlavor()
df.MimeType = ClipBoard.CLIPBOARD_FORMAT_TEXT df.MimeType = ClipBoard.TEXT
df.HumanPresentableName = 'encoded text utf-16' df.HumanPresentableName = 'encoded text utf-16'
self.flavors = (df,) self.flavors = (df,)
self._data = text self._data = text
@ -631,7 +632,7 @@ class ClipBoard(object):
transferable = sc.getContents() transferable = sc.getContents()
data = transferable.getTransferDataFlavors() data = transferable.getTransferDataFlavors()
for df in data: for df in data:
if df.MimeType == cls.CLIPBOARD_FORMAT_TEXT: if df.MimeType == cls.TEXT:
break break
if df: if df:
text = transferable.getTransferData(df) text = transferable.getTransferData(df)
@ -1045,10 +1046,13 @@ class Paths(object):
:return: Path with new extension :return: Path with new extension
:rtype: str :rtype: str
""" """
p = Paths(path) if not new_ext.startswith('.'):
name = f'{p.name}.{new_ext}' new_ext = f'.{new_ext}'
path = cls.join(p.path, name) return Path(path).with_suffix(new_ext)
return path
@classmethod
def with_suffix(cls, path: str, new_ext: str):
return cls.replace_ext(path, new_ext)
@classmethod @classmethod
def open(cls, path: str): def open(cls, path: str):

View File

@ -245,6 +245,7 @@ class LOShape(BaseObject):
def save(self, path: str='', name: str=''): def save(self, path: str='', name: str=''):
"""Save image""" """Save image"""
if not path: if not path:
path = Paths(self.doc.URL).path path = Paths(self.doc.URL).path
if not name: if not name: