From b8954d7adeb18d07543384af7f3553261af8f868 Mon Sep 17 00:00:00 2001 From: El Mau Date: Mon, 24 Apr 2023 23:09:03 -0600 Subject: [PATCH] Add remove, save, clone images --- doc/docs/dp/drawpage.md | 14 +++ doc/docs/dp/shapes.md | 197 ++++++++++++++++++++++++++++++- doc/docs/examples/index.md | 155 ++++++++++++++++++++++++ doc/mkdocs.yml | 5 +- source/easymacro/easydoc.py | 42 +------ source/easymacro/easydrawpage.py | 3 +- source/easymacro/easyshape.py | 85 +++++++++++-- source/easymacro/easyuno.py | 55 +++++++++ 8 files changed, 505 insertions(+), 51 deletions(-) create mode 100644 doc/docs/examples/index.md diff --git a/doc/docs/dp/drawpage.md b/doc/docs/dp/drawpage.md index e1f6846..b78a29e 100644 --- a/doc/docs/dp/drawpage.md +++ b/doc/docs/dp/drawpage.md @@ -106,4 +106,18 @@ Agrega una nueva forma (un rectángulo) y la devuelve. app.debug(forma) ``` +De forma predeterminada la posición será a 1 cm en Y y X y de 3 cm de ancho y alto. Estos valores se pueden modificar al crear la forma. Todas las unidades en milésimas de centímetro. + +```py + opciones = dict( + Name = 'mi_rectangulo_1', + Width = 5000, + Height = 2000, + X = 3000, + Y = 1000, + ) + + forma = formas.add('Rectangle', opciones) +``` +
\ No newline at end of file diff --git a/doc/docs/dp/shapes.md b/doc/docs/dp/shapes.md index 2e731d4..c0bc240 100644 --- a/doc/docs/dp/shapes.md +++ b/doc/docs/dp/shapes.md @@ -1,7 +1,202 @@ ## Propiedades +Todas las unidades en milésimas de centímetro. + --- +### **anchor** + +Anclaje de la forma, por default se agrega a la página. + +#### Anclaje a una celda + +```py + celda = hoja['A1'] + forma.anchor = celda +``` + +
+ +### **height** + +Alto de la forma. + +```py + forma.height = 2000 +``` + +
+ +### **name** + +Devuelve o establece el nombre de la forma. + +```py + app.debug(forma.name) + forma.name = 'otro_rectangulo' + app.debug(forma.name) +``` + +
+ +### **obj** + +**Solo lectura.** Devuelve el objeto original pyUNO. + +```py + objeto = forma.obj +``` + +
+ +### **possize** + +Devuelve o establece la posición y el tamaño de la forma. + +```py + posicion_y_tamaño = dict( + Width = 5000, + Height = 2000, + X = 3000, + Y = 1000, + ) + forma.possize = posicion_y_tamaño +``` + +
+ +### **properties** + +Devuelve un diccionario con todas las propiedades de la forma. + +```py + for p, v in forma.properties.items(): + app.debug(f'{p} = {v}') +``` + +Al establecer solo aplica las que se pasen en un diccionario. + +```py + propiedades = dict( + LineColor = app.color('red'), + LineWidth = 100, + ) + forma.properties = propiedades +``` + +!!! tip "Atención" + + Aquí hay que usar los nombres "originales" de las propiedades de la forma en el API de LibreOffice. + +
+ +### **resize_with_cell** + +Si el tamaño de la forma cambia con la celda, solo cuando el anclaje es a una celda. + +```py + forma.resize_with_cell = True +``` + +
+ +### **shape_type** + +**Solo lectura**. Devuelve el tipo de forma. + +```py + app.debug(forma.shape_type) +``` + +``` +24/04/2023 13:55:32 - DEBUG - com.sun.star.drawing.RectangleShape +``` + +
+ +### **width** + +Ancho de la forma. + +```py + forma.width = 6000 +``` + +
+ +### **x** + +Posición en el eje X. + +```py + forma.x = 5000 +``` + +
+ +### **y** + +Posición en el eje Y. + +```py + forma.y = 1000 +``` + ## Métodos ---- \ No newline at end of file +--- + +### **clone** + +Clona la imagen en la misma hoja. + +```py + imagen.clone() +``` + +Clona la imagen en otra hoja. + +```py + imagen.clone(doc['Hoja5'].draw_page) +``` + +
+ +### **remove** + +Eliminar la forma. + +```py + forma.remove() +``` + +
+ +### **save** + +Guarda la imagen en disco. Si no se pasa ningún argumento, toma la ruta del documento y el nombre de la imagen. + +!!! tip inline end "Atención" + + El documento ya debe estar guardado + +```py + imagen.save() +``` + +Podemos pasar una ruta diferente, seguira tomando el nombre de la imagen. + +```py + ruta = '/home/elmau/imagenes' + imagen.save(ruta) +``` + +Podemos cambiar el nombre. + +```py + ruta = '/home/elmau/imagenes' + name = f'{hoja.name}_nuevo_nombre' + imagen.save(path, name) +``` + +
\ No newline at end of file diff --git a/doc/docs/examples/index.md b/doc/docs/examples/index.md new file mode 100644 index 0000000..3ab3408 --- /dev/null +++ b/doc/docs/examples/index.md @@ -0,0 +1,155 @@ +--- +title: Índice +--- + +# Ejemplos reales de uso + +--- + +## Calc + +--- + +### Solicitar precios + +Consultar datos en json a una página web y vaciarlos en una hoja. + +=== "EasyMacro" + + ```py + import easymacro as app + + def obtener_precios(): + URL = 'https://api.binance.com/api/v3/ticker/price' + respuesta = app.url.get(URL) + if respuesta.status_code == 200: + datos = respuesta.json() + app.active.active['A1'].dict = datos + return + ``` + +=== "Sin EasyMacro" + + ```py + import uno + import json + from urllib import request + + def obtener_precios(): + URL = 'https://api.binance.com/api/v3/ticker/price' + data = json.loads(request.urlopen(URL).read().decode()) + + doc = XSCRIPTCONTEXT.getDocument() + sheet = doc.CurrentController.ActiveSheet + + sheet[0,0].String = 'symbol' + sheet[0,1].String = 'price' + + for i, row in enumerate(data): + sheet[i + 1,0].String = row['symbol'] + sheet[i + 1,1].Value = float(row['price']) + + return + ``` + +
+ +### Clonar imagenes + +Clonar todas las imagenes de una hoja a otra. + +=== "EasyMacro" + + ```py + doc = app.active + for image in doc[0].dp: + if image.is_image: + image.clone(doc[1].dp) + ``` + +=== "Sin EasyMacro" + + ```py + import uno + import io + import unohelper + from com.sun.star.io import IOException, XOutputStream + from com.sun.star.beans import PropertyValue + + + CTX = uno.getComponentContext() + SM = CTX.getServiceManager() + + + class LOShape(): + + def __init__(self, shape): + self._shape = shape + + class OutputStream(unohelper.Base, XOutputStream): + + def __init__(self): + self._buffer = b'' + self.closed = 0 + + @property + def buffer(self): + return self._buffer + + def closeOutput(self): + self.closed = 1 + + def writeBytes(self, seq): + if seq.value: + self._buffer = seq.value + + def flush(self): + pass + + def clone_image(self, doc, to_sheet): + stream = self._shape.GraphicStream + buffer = self.OutputStream() + size, data = stream.readBytes(buffer, stream.available()) + + stream = SM.createInstanceWithContext('com.sun.star.io.SequenceInputStream', CTX) + stream.initialize((data,)) + + image = doc.createInstance('com.sun.star.drawing.GraphicObjectShape') + gp = SM.createInstance('com.sun.star.graphic.GraphicProvider') + properties = (PropertyValue(Name='InputStream', Value=stream),) + image.Graphic = gp.queryGraphic(properties) + to_sheet.DrawPage.add(image) + image.Size = self._shape.Size + image.Position = self._shape.Position + return + + def clone_shape(self, doc, to_sheet): + for p in self._shape.CustomShapeGeometry: + if p.Name == 'Type': + type_shape = p.Value.title() + + service = f'com.sun.star.drawing.{type_shape}Shape' + shape = doc.createInstance(service) + shape.Size = self._shape.Size + shape.Position = self._shape.Position + to_sheet.DrawPage.add(shape) + return + + + def main(): + IMAGE = 'com.sun.star.drawing.GraphicObjectShape' + SHAPE = 'com.sun.star.drawing.CustomShape' + + doc = XSCRIPTCONTEXT.getDocument() + source = doc.CurrentController.ActiveSheet + target = doc.Sheets[1] + + for shape in source.DrawPage: + s = LOShape(shape) + if shape.ShapeType == IMAGE: + s.clone_image(doc, target) + elif shape.ShapeType == SHAPE: + s.clone_shape(doc, tar + ``` + +
\ No newline at end of file diff --git a/doc/mkdocs.yml b/doc/mkdocs.yml index 0e47fee..3c66251 100644 --- a/doc/mkdocs.yml +++ b/doc/mkdocs.yml @@ -32,6 +32,8 @@ nav: - Página de dibujo: - dp/drawpage.md - Formas: dp/shapes.md + - Ejemplos de uso: + - examples/index.md theme: name: material locale: es @@ -53,7 +55,8 @@ markdown_extensions: pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets - - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true #~ extra: #~ alternate: #~ - name: Español diff --git a/source/easymacro/easydoc.py b/source/easymacro/easydoc.py index ca7d915..5e5c1dc 100644 --- a/source/easymacro/easydoc.py +++ b/source/easymacro/easydoc.py @@ -1,51 +1,11 @@ #!/usr/bin/env python3 import uno -import unohelper -from com.sun.star.io import IOException, XOutputStream from .easymain import (log, BaseObject, LOMain, Paths, dict_to_property, create_instance ) - - -class IOStream(object): - """Classe for input/output stream""" - - class OutputStream(unohelper.Base, XOutputStream): - - def __init__(self): - self._buffer = b'' - self.closed = 0 - - @property - def buffer(self): - return self._buffer - - def closeOutput(self): - self.closed = 1 - - def writeBytes(self, seq): - if seq.value: - self._buffer = seq.value - - def flush(self): - pass - - @classmethod - def buffer(cls): - return io.BytesIO() - - @classmethod - def input(cls, buffer): - service = 'com.sun.star.io.SequenceInputStream' - stream = create_instance(service, True) - stream.initialize((uno.ByteSequence(buffer.getvalue()),)) - return stream - - @classmethod - def output(cls): - return cls.OutputStream() +from .easyuno import IOStream class LODocument(BaseObject): diff --git a/source/easymacro/easydrawpage.py b/source/easymacro/easydrawpage.py index 05dca93..1fa0a9a 100644 --- a/source/easymacro/easydrawpage.py +++ b/source/easymacro/easydrawpage.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +from pathlib import Path from com.sun.star.awt import Size, Point from .easymain import ( @@ -228,7 +229,7 @@ class LODrawPage(BaseObject): image = self._create_instance('com.sun.star.drawing.GraphicObjectShape') if isinstance(path, str): - image.GraphicURL = _P.to_url(path) + image.GraphicURL = Path(path).as_uri() else: gp = create_instance('com.sun.star.graphic.GraphicProvider') stream = IOStream.input(path) diff --git a/source/easymacro/easyshape.py b/source/easymacro/easyshape.py index f3488b4..75039d5 100644 --- a/source/easymacro/easyshape.py +++ b/source/easymacro/easyshape.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 from com.sun.star.awt import Size, Point -from .easymain import BaseObject, set_properties +from .easymain import BaseObject, Paths, create_instance, dict_to_property, set_properties +from .easyuno import IOStream, get_input_stream class LOShapes(object): @@ -43,6 +44,10 @@ class LOShapes(object): class LOShape(BaseObject): IMAGE = 'com.sun.star.drawing.GraphicObjectShape' + MIME_TYPE = { + 'image/png': 'png', + 'image/jpeg': 'jpg', + } def __init__(self, obj): self._obj = obj @@ -52,10 +57,12 @@ class LOShape(BaseObject): @property def obj(self): + """Get original UNO object""" return self._obj @property def anchor(self): + """Get anchor object""" return self.obj.Anchor @anchor.setter def anchor(self, obj): @@ -66,6 +73,7 @@ class LOShape(BaseObject): @property def resize_with_cell(self): + """If resize with cell""" return self.obj.ResizeWithCell @resize_with_cell.setter def resize_with_cell(self, value): @@ -74,8 +82,7 @@ class LOShape(BaseObject): @property def properties(self): - # ~ properties = self.obj.PropertySetInfo.Properties - # ~ data = {p.Name: getattr(self.obj, p.Name) for p in properties} + """Get all properties""" data = self.obj.PropertySetInfo.Properties keys = [p.Name for p in data] values = self.obj.getPropertyValues(keys) @@ -87,10 +94,12 @@ class LOShape(BaseObject): @property def shape_type(self): + """Get type shape""" return self.obj.ShapeType @property def name(self): + """Get name""" return self.obj.Name @name.setter def name(self, value): @@ -114,6 +123,7 @@ class LOShape(BaseObject): @property def width(self): + """Width of shape""" s = self.obj.Size return s.Width @width.setter @@ -124,6 +134,7 @@ class LOShape(BaseObject): @property def height(self): + """Height of shape""" s = self.obj.Size return s.Height @height.setter @@ -137,6 +148,7 @@ class LOShape(BaseObject): return self.obj.Position @property def x(self): + """Position X""" return self.position.X @x.setter def x(self, value): @@ -144,6 +156,7 @@ class LOShape(BaseObject): @property def y(self): + """Position Y""" return self.position.Y @y.setter def y(self, value): @@ -199,11 +212,69 @@ class LOShape(BaseObject): self.obj.LayerID = value @property - def is_range(self): - return False + def type(self): + mt = self.obj.GraphicURL.MimeType + mime_type = self.MIME_TYPE.get(mt, mt) + return mime_type + + # ~ not work + @property + def visible(self): + return self.obj.Visible + @visible.setter + def visible(self, value): + self.obj.Visible = value @property - def is_cell(self): - return False + def doc(self): + return self.obj.Parent.Forms.Parent + + def remove(self): + """Auto remove""" + self.obj.Parent.remove(self.obj) + return + + def save(self, path: str='', name: str=''): + """Save image""" + if not path: + path = Paths(self.doc.URL).path + if not name: + name = self.name.replace(' ', '_') + + path_img = Paths.join(path, f'{name}.{self.type}') + data = IOStream.to_bin(self.obj.GraphicStream) + Paths.save_bin(path_img, data) + + return path_img + + def _get_graphic(self): + stream = self.obj.GraphicStream + buffer = IOStream.output() + _, data = stream.readBytes(buffer, stream.available()) + stream = get_input_stream(data) + gp = create_instance('com.sun.star.graphic.GraphicProvider') + properties = dict_to_property({'InputStream': stream}) + graphic = gp.queryGraphic(properties) + return graphic + + def clone(self, draw_page=None): + """Clone image""" + image = self.doc.createInstance('com.sun.star.drawing.GraphicObjectShape') + image.Graphic = self._get_graphic() + plus = 0 + if draw_page is None: + draw_page = self.obj.Parent + plus = 1000 + else: + if hasattr(draw_page, 'obj'): + draw_page = draw_page.obj + + draw_page.add(image) + image.Size = self.size + position = self.position + position.X += plus + position.Y += plus + image.Position = position + return LOShape(image) diff --git a/source/easymacro/easyuno.py b/source/easymacro/easyuno.py index f25d715..ff6d128 100644 --- a/source/easymacro/easyuno.py +++ b/source/easymacro/easyuno.py @@ -1,5 +1,9 @@ #!/usr/bin/env python3 +import unohelper +from com.sun.star.io import IOException, XOutputStream +from .easymain import create_instance + # UNO Enum class MessageBoxType(): @@ -9,3 +13,54 @@ class MessageBoxType(): """ from com.sun.star.awt.MessageBoxType \ import MESSAGEBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX + + +class IOStream(object): + """Classe for input/output stream""" + + class OutputStream(unohelper.Base, XOutputStream): + + def __init__(self): + self._buffer = b'' + self.closed = 0 + + @property + def buffer(self): + return self._buffer + + def closeOutput(self): + self.closed = 1 + + def writeBytes(self, seq): + if seq.value: + self._buffer = seq.value + + def flush(self): + pass + + @classmethod + def buffer(cls): + return io.BytesIO() + + @classmethod + def input(cls, buffer): + service = 'com.sun.star.io.SequenceInputStream' + stream = create_instance(service, True) + stream.initialize((uno.ByteSequence(buffer.getvalue()),)) + return stream + + @classmethod + def output(cls): + return cls.OutputStream() + + @classmethod + def to_bin(cls, stream): + buffer = cls.OutputStream() + _, data = stream.readBytes(buffer, stream.available()) + return data.value + + +def get_input_stream(data): + stream = create_instance('com.sun.star.io.SequenceInputStream', True) + stream.initialize((data,)) + return stream