Macros
TDF LibreOffice Document Liberation Project Blogs communautaires Weblate Nextcloud Redmine Ask LibreOffice Donner
⇐ Introduction au développement en Python |Table des matières| Identifier son système ⇒
Principes Généraux
Conception et Développement d'Applications Python
Pour parler de conception et de développement il faut s'instruire sur ce que sont les paradigmes de programmation et leur périmètre sur le langage Python
Ce tutoriel vous propose comme paradigme une approche procédurale du développement pour des raisons pédagogiques. Il est souvent plus facile de comprendre un cheminement que de percevoir une globalité. Le processus étape par étape est une approche procédurale, alors que la gestion d'états possibles, qu'est l'objet, demande une vision d'ensemble que nous aborderons à la fin de chaque parties du tutoriel. Et comme le diraient les anciens, il faut commencer par des fondations avant de faire la maison, puis il faut avoir fait une maison pour pouvoir en parler.
Si vous souhaitez vous lancer dans un développement lourd de macros python, je vous conseille de lire concevoir des projets LibreOffice complexes et/ou collaboratifs
Analyse du code
Pour commencer à coder des macros il faut s'équiper avec des outils d'analyse du code. Ces outils vont nous aider à comprendre nos erreurs et à deviner comment programmer pour aboutir au résultat recherché.
En mode interactif
Propre aux langage interactif, Python nous permet d'exécuter notre code au fur et à mesure.
D'abord nous allons exécuter Python en mode interactif avec LibreOffice dans un terminal shell :
Sous Linux :
libreoffice --accept="socket,host=localhost,port=2002;urp;StarOffice.ServiceManager" & python3
Sous macOS :
/Applications/LibreOffice.app/Contents/MacOS/soffice --accept="socket,host=localhost,port=2002;urp;StarOffice.ServiceManager" & /Applications/LibreOffice.app/Contents/MacOS/python
Sous Windows :
"C:\\Program Files (x86)\LibreOffice 5\program\soffice.exe" --accept="socket,host=localhost,port=2002;urp;" "C:\\Program Files (x86)\LibreOffice 5\program\python.exe"
L'interpréteur python s'exécute dans son environnement :
Python 3.5.2+ (default, Sep 22 2016, 12:18:14) [GCC 6.2.0 20160927] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
Importons la librairie UNO
>>> import uno
Nous pouvons optenir de l'aide sur la gestion de la librairie uno
>>> help(uno)
Ce qui nous affiche :
Help on module uno: NAME uno CLASSES builtins.object Any Bool ByteSequence Char Enum Type class Any(builtins.object) | use only in connection with uno.invoke() to pass an explicit typed any | | Methods defined here: | | __init__(self, type, value) | Initialize self. See help(type(self)) for accurate signature. | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) class Bool(builtins.object) | Represents a UNO boolean, use an instance of this class to explicitly :
Déplacez-vous avec les flêches vers le haut «↑» et vers le bas «↓» du clavier pour consulter l'aide. Tapez la touche «q» pour sortir de l'aide.
Nous pouvons aussi visuliser l'objet avec la commande dir()
>>> dir(uno) ['Any', 'Bool', 'ByteSequence', 'Char', 'Enum', 'Type', '_ConstantGroup', '__builtin__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_g_ctx', '_g_delegatee', '_impl_extractName', '_impl_getConstantGroupByName', '_uno_extract_printable_stacktrace', '_uno_import', '_uno_struct__eq__', '_uno_struct__getattr__', '_uno_struct__init__', '_uno_struct__repr__', '_uno_struct__setattr__', '_uno_struct__str__', 'absolutize', 'createUnoStruct', 'fileUrlToSystemPath', 'generateUuid', 'getClass', 'getComponentContext', 'getConstantByName', 'getCurrentContext', 'getTypeByName', 'invoke', 'isInterface', 'os', 'pyuno', 'setCurrentContext', 'socket', 'sys', 'systemPathToFileUrl', 'unicode']
MRI l'explorateur d'API
MRI est un outil d'introspection d'objets UNO, écrit en Python. Si vous voulez connaître les objets UNO, essayez d'utiliser MRI.
Les cibles de cette extension sont les macro, le développement d'extension et ainsi de suite. MRI est un service UNO qui peut être utilisé facilement depuis n'importe quel autre langage.
MRI nécessite l'installation d'une liaison Python-UNO.
Installation de l'extension MRI
Téléchargez l'extension ici
Ouvrez le fichier téléchargé mri-x.x.x.oxt avec LibreOffice.
Faite défiler vers le bas avec le bouton «Défiler vers le bas» puis cliquez sur le bouton «Accepter» si vous êtes d'accord avec la licence.
Puis vérifiez si la version est à jour avec le bouton «Vérifier les mises à jour...».
Si il y a une mise à jour cliquez sur le bouton «Installer».
Une fois l'extension mise à jour cliquez sur le bouton «OK».
Puis cliquez sur le bouton «Fermer» de la boite de dialogue Gestionnaire des extensions.
Utilisation de MRI
Voici le code de la fonction d'appel de l'environnement MRI
def mri(ctx, cible):
try:
mri = ctx.ServiceManager.createInstanceWithContext('mytools.Mri', ctx)
mri.inspect(cible)
except:
raise _rtex("L'extension Python MRI n'est pas installée", uno.getComponentContext())
Pour tester MRI vous devez créer un nouvelle macro avec l'éditeur de macros.
Pour cela ouvrez le menu dans LibreOffice :
Outils/Macros/Gérer les scripts python ou au clavier Alt+Maj+F11
Dans la boite de dialogue Scripts python :
- Sélectionnez avec la souris la liste Mes macros
- Cliquez sur le bouton «Menu» et sélectionnez le menu Nouveau module
- Dans la boite de saisie Nom du module à créer saisissez mri
- Sélectionnez dans la liste Mes macros avec la souris le module mri nouvellement créé.
- Cliquez sur le bouton «Menu» et sélectionnez le menu Modifier
Voici un code d'exemple d'utilisation
#!
#coding: utf8
#Macro: mri.py
#Objectif: Inspecte un objet document avec MRI
#Auteur: Franc SERRES
#Licence: https://www.gnu.org/licenses/gpl-3.0.fr.html
def Mri_test():
ctx = XSCRIPTCONTEXT.getComponentContext()
document = XSCRIPTCONTEXT.getDocument()
mri(ctx, document)
def mri(ctx, cible):
try:
mri = ctx.ServiceManager.createInstanceWithContext('mytools.Mri', ctx)
mri.inspect(cible)
except:
raise _rtex("L'extension Python MRI n'est pas installée", uno.getComponentContext())
g_exportedScripts = Mri_test,
Sauvegardez le fichier et fermez votre éditeur.
Dans la fenêtre Scripts Python sélectionnez avec la souris le dossier mri puis la macro Mri_test et cliquez sur le bouton «Exécuter»
Explorateur de configuration
def getConfigSetting (sNodeConfig, bUpdate):
"get a configuration node"
# example: xNode = getConfigSetting( "/org.openoffice.Office.Common/Path/Current", False)
xSvMgr = uno.getComponentContext().ServiceManager
xConfigProvider = xSvMgr.createInstanceWithContext("com.sun.star.configuration.ConfigurationProvider", uno.getComponentContext())
xPropertyValue = uno.createUnoStruct("com.sun.star.beans.PropertyValue")
xPropertyValue.Name = "nodepath"
xPropertyValue.Value = sNodeConfig
if bUpdate:
sService = "com.sun.star.configuration.ConfigurationUpdateAccess"
else:
sService = "com.sun.star.configuration.ConfigurationAccess"
xNode = xConfigProvider.createInstanceWithArguments(sService, (xPropertyValue,))
return xNode
Contexte
La passerelle Python-UNO s'exécute dans un contexte différent suivant comment le script python est exécuté.
C'est à dire que les environnements d'exécution sont distincts dans le cas du mode interactif, et encapsulé dans LibreOffice lors de l'exécution d'une macro par l'interface graphique.
Scripts
Nous avons vu qu'il existait deux modes d'exécution des scripts Python. Nous allons voir ici comment récupérer les contextes dont nous avons besoin pour gérer des objets.
Mode interactif
Python communique avec LibreOffice en échangeant des appels de procédures distant RPC (Remote Procedure Call).
C'est pour cela qu'il nous faut toujours récupérer le contexte de composant correct pour indiquer à la fabrique de composants multiples d'instancier un service pour créer les objets au bon endroit.
Comme nous l'avons vu dans Python et le mode interactif avec LibreOffice, il nous faut initier le code Python minimal suivant pour récupérer le contexte de LibreOffice :
import uno
# Récupération du contexte d'exécution PyUNO
ctxlocal = uno.getComponentContext()
# Récupération du gestionnaire de services du contexte local PyUNO
gslocal = ctxlocal.ServiceManager
# création d'un connecteur logiciel UnoUrlResolver pour dialoguer en appel distant de procédures RPC
connecteur = gslocal.createInstanceWithContext( "com.sun.star.bridge.UnoUrlResolver", ctxlocal )
# Récupération du contexte de LibreOffice
ctx = connecteur.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
Gestionnaire de Services
En Python cette instance est la plus importante. Elle permet d'instancier les services et de travailler avec les API du bureau.
# Récupération du gestionnaire de services du contexte de Libreoffice
gs = ctx.ServiceManager
Les éléments de LibreOffice
Avec le contexte, nous pouvons récupérer l'environnement de bureau de LibreOffice :
bureau = gs.createInstanceWithContext('com.sun.star.frame.Desktop', ctx)
Et enfin nous pouvons travailler sur le document actif :
document = bureau.getCurrentComponent()
>>> document.getIdentifier() 'com.sun.star.text.TextDocument'
Macro exécutée depuis LibreOffice
Pour l'exécution d'une macro directement depuis LibreOffice, nous utilisons de contexte XSCRIPTCONTEXT avec le code suivant pour récupérer le contexte de LibreOffice :
import uno
# Récupération du contexte de LibreOffice
ctx = XSCRIPTCONTEXT.getComponentContext()
Gestionnaire de Services
# Récupération du gestionnaire de services du contexte de Libreoffice
gs = ctx.ServiceManager
Les éléments de LibreOffice
Avec XSCRIPTCONTEXT, nous pouvons récupérer l'environnement de bureau de LibreOffice :
bureau = XSCRIPTCONTEXT.getDesktop()
Et enfin nous pouvons travailler sur le document actif :
document = XSCRIPTCONTEXT.getDocument()
Composants
De façon générale, après pour obtenir le contexte d'un composant dans le dévelopement de vos scripts, il suffira d'utiliser «.getComponentContext()».
comme :
ctx = XSCRIPTCONTEXT.getComponentContext()
Instances
Service
Structure
Énumérations
LibreOffice dans ses API contient des constantes que vous pouvez utiliser à la place de nombres pour donner plus de sens à votre code lors de la lecture.
Ce sont les énumérations.
Ces énumérations sont accéssibles grace à la librairie PyUno
import uno
Il existe trois syntaxes possibles pour les récupérer :
from com.sun.star.awt.MessageBoxType import MESSAGEBOX as MESSAGEBOITE
ou
MESSAGEBOITE = uno.getConstantByName('com.sun.star.awt.MessageBoxType.MESSAGEBOX')
ou
MESSAGEBOITE = uno.Enum('com.sun.star.awt.MessageBoxType', 'MESSAGEBOX')
Constantes
Internationaliser ses macros python
Commencer à coder une macro en pensant à la partager c'est l'esprit du logiciel libre.
La recherche du partage est aussi utile pour corriger, améliorer et apprendre le langage python des macros LibreOffice.
C'est pour cela que plutôt on intègre l'internationnalisation dans son code, plus on a de chance d'être aidé et d'apprendre mieux par le partage le langage python des macros LibreOffice.
Création de la structure de la macro
.
├── bonjour.pot
├── bonjour.py
└── locale
¦
├── de_DE
│ └── LC_MESSAGES
¦
├── en_US
│ └── LC_MESSAGES
├── en_GB
│ └── LC_MESSAGES
¦
├── es_ES
│ └── LC_MESSAGES
¦
└── fr_FR
└── LC_MESSAGES
Modifier sa macro pour l'internationalisation
#librairies pour l'internationalisation du texte
import os
import gettext
#Récupération du chemin des locales de l'application
cheminscript = os.path.dirname(__file__).replace('file://', '')
REP_LANGUES = os.path.join(cheminscript, 'locale')
#gettext.bindtextdomain('nom_fichier_traduit', localedir='chemin_répertoire_locales')
gettext.bindtextdomain('bonjour', localedir=REP_LANGUES)
#Récupération de la traduction locale pour le message
message = gettext.dgettext('bonjour', _('Hello world !'))
#!
#coding: utf8
#Macro: bonjour.py
#Objectif: Affiche une boîte de dialogue avec le message «Bonjour à tous»
#Auteur: Franc SERRES
#Licence: https://www.gnu.org/licenses/gpl-3.0.fr.html
#librairies pour l'internationalisation du texte
import os
import gettext
import uno
from com.sun.star.awt.MessageBoxType import MESSAGEBOX
#Typedeboite :
# Boite de message : MESSAGEBOX
# Boite d'information : INFOBOX
# Boite d'erreur : ERRORBOX
# Boite d'alerte : WARNINGBOX
# Boite d'intérogation : QUERYBOX
def bonjour():
#Récupération du chemin des locales de l'application
cheminscript = os.path.dirname(__file__).replace('file://', '')
REP_LANGUES = os.path.join(cheminscript, 'locale')
#gettext.bindtextdomain('nom_fichier_traduit', localedir='chemin_répertoire_locales')
gettext.bindtextdomain('bonjour', localedir=REP_LANGUES)
#Récupération de la traduction locale pour le message
message = gettext.dgettext('bonjour', _('Hello world !'))
#
bureau = XSCRIPTCONTEXT.getDesktop()
cadre = bureau.getCurrentFrame()
fenêtre = cadre.getContainerWindow()
boîteàoutils = fenêtre.getToolkit()
titre = 'Python'
# boîtemessage = boîteàoutils.createMessageBox(fenêtreapplication, Typedeboite, Typedebouton, titreboîteàmessase, messageboîte)
#
#Typedebouton :
# OK : 1
# OK/CANCEL : 2
# Oui/Non : 3
# Oui/Non/CANCEL : 4
# Retry/CANCEL : 5
# CANCEL/Retry/Ignore : 6
boîtemessage = boîteàoutils.createMessageBox(fenêtre, MESSAGEBOX, 1, titre, message)
return boîtemessage.execute()
g_exportedScripts = bonjour,
Traduction
Génération du modèle de traduction
Sous linux :
cd /home/utilisateur/.config/libreoffice/4/user/Scripts/python
pygettext -d bonjour bonjour.py
Traduire le vocabulaire de la macro
ouvrir l'application Poedit
Choisir le fichier /home/utilisateur/.config/libreoffice/4/user/Scripts/python/bonjour.py
La fenêtre de traductions s'affiche
Choisir la langue de traduction
Écrivez votre traduction
Génération de la traduction ou mise à jour de la traduction
Généré la traduction dans /home/utilisateur/.config/libreoffice/4/user/Scripts/python/locale/fr_FR/LC_MESSAGES
.
├── bonjour.pot
├── bonjour.py
└── locale
├── en_GB
│ └── LC_MESSAGES
│ ├── bonjour.mo
│ └── bonjour.po
└── fr_FR
└── LC_MESSAGES
├── bonjour.mo
└── bonjour.po
Changer de langue dans la macro
#!
#coding: utf8
#Macro: bonjour_locales.py
#Objectif: Affiche une une boite de dialogue avec comme titre la langue et en message la traduction d'«Hello world !»
#Auteur: Franc SERRES
#Licence: https://www.gnu.org/licenses/gpl-3.0.fr.html
import os, sys, traceback
#internationalisation du texte
import gettext
#LibreOffice
import uno
from com.sun.star.awt.MessageBoxType import \
MESSAGEBOX as BOÎTEDIALOGUE, \
INFOBOX as BOÎTEINFORMATION, \
ERRORBOX as BOÎTEERREUR, \
WARNINGBOX as BOÎTEAVERTISSEMENT, \
QUERYBOX as BOÎTEQUESTION
from com.sun.star.awt.MessageBoxButtons import \
BUTTONS_OK as BOUTON_OK, \
BUTTONS_OK_CANCEL as BOUTONS_OK_ANNULER, \
BUTTONS_YES_NO as BOUTONS_OUI_NON, \
BUTTONS_YES_NO_CANCEL as BOUTONS_OUI_NON_ANNULER, \
BUTTONS_RETRY_CANCEL as BOUTONS_RÉESSAYER_ANNULER, \
BUTTONS_ABORT_IGNORE_RETRY as BOUTONS_INTERROMPRE_REESSAYER_IGNORER, \
DEFAULT_BUTTON_OK as OK_PAR_DEFAUT, \
DEFAULT_BUTTON_CANCEL as ANNULER_PAR_DEFAUT, \
DEFAULT_BUTTON_RETRY as RÉESSAYER_PAR_DEFAUT, \
DEFAULT_BUTTON_YES as OUI_PAR_DEFAUT, \
DEFAULT_BUTTON_NO as NON_PAR_DEFAUT, \
DEFAULT_BUTTON_IGNORE as IGNORER_PAR_DEFAUT
from com.sun.star.awt.MessageBoxResults import CANCEL, OK, YES, NO, RETRY, IGNORE
#from com.sun.star.beans import PropertyValue
def afficheFenêtre(messageboîte, titreboîteàmessage, typedebouton=BOUTON_OK, typedeboite=BOÎTEDIALOGUE):
#typedeboite :
# Boite de message : BOÎTEDIALOGUE
# Boite d'information : BOÎTEINFORMATION
# Boite d'erreur : BOÎTEERREUR
# Boite d'alerte : BOÎTEAVERTISSEMENT
# Boite d'intérogation : BOÎTEQUESTION
#
#typedebouton :
# OK : BOUTON_OK
# OK/ANNULER : BOUTONS_OK_ANNULER
# OUI/NON : BOUTONS_OUI_NON
# OUI/NON/ANNULER : BOUTONS_OUI_NON_ANNULER
# RÉESSAYER/ANNULER : BOUTONS_RÉESSAYER_ANNULER
# ANNULER/RÉESSAYER/IGNORER : BOUTONS_INTERROMPRE_REESSAYER_IGNORER
bureau = XSCRIPTCONTEXT.getDesktop()
cadre = bureau.getCurrentFrame()
fenêtre = cadre.getContainerWindow()
boîteàoutils = fenêtre.getToolkit()
fenêtremessage = boîteàoutils.createMessageBox(fenêtre, typedeboite, typedebouton, titreboîteàmessage, messageboîte)
return fenêtremessage.execute()
def affiche():
#Récupération du chemin des locales de l'application
cheminscript = os.path.dirname(__file__).replace('file://', '')
REP_LANGUES = os.path.join(cheminscript, 'locale')
gettext.bindtextdomain('bonjour', localedir=REP_LANGUES)
#Récupération de la traduction locale pour le message
message = gettext.dgettext('bonjour', 'Hello world !')
#Récupération de la locale par défaut
localeorigin = os.environ['LANG']
titre = str(localeorigin)[0:5]
result = afficheFenêtre(message, titre)
#Affectation de la locale anglaise «locale -a» sous Linux
os.environ['LANG'] = 'en_GB.utf8'
titre = str(os.environ['LANG'])[0:5]
#Récupération de la traduction locale pour le message
message = gettext.dgettext('bonjour', 'Hello world !')
result = afficheFenêtre(message, titre)
os.environ['LANG'] = localeorigin
return True
g_exportedScripts = affiche,
⇐ Introduction au développement en Python
|
Identifier son système ⇒