Macros

From The Document Foundation Wiki
< Macros‎ | Python Guide‎ | fr‎ | Guide
Jump to: navigation, search

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 Mac OS :

/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.

Licence de MRI

Puis vérifiez si la version est à jour avec le bouton «Vérifier les mises à jour...».

MRI est installé

Si il y a une mise à jour cliquez sur le bouton «Installer».

Fenêtre de la mise à jour

Une fois l'extension mise à jour cliquez sur le bouton «OK».

L'extension est à jour

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 
Lancer l'extension

Dans la boite de dialogue Scripts python :

La fenêtre principale de l'extension
  1. Sélectionnez avec la souris la liste Mes macros
  2. Cliquez sur le bouton «Menu» et sélectionnez le menu Nouveau module
    Ajouter une nouvelle macro
  3. Dans la boite de saisie Nom du module à créer saisissez mri
    Saisie du nom de la macro
  4. Sélectionnez dans la liste Mes macros avec la souris le module mri nouvellement créé.
  5. Cliquez sur le bouton «Menu» et sélectionnez le menu Modifier
    Pour modifier une macro python

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»

Onglet «Properties»
Onglet «Methods»
Onglet «Interfaces»
Onglet «Services»

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

ouvrir l'application Poedit

cliquez sur ouvrir l'application Poedit

Choisir le fichier /home/utilisateur/.config/libreoffice/4/user/Scripts/python/bonjour.py

ouvrir le fichier à traduire

La fenêtre de traductions s'affiche

fenêtre de traductions

Cliquez sur création d'une nouvelle traduction

Choisir la langue de traduction

langue de traduction

Écrivez votre traduction

Traduction


Génération de la traduction ou mise à jour de la traduction

Cliquez sur bouton enregistrer

Généré la traduction dans /home/utilisateur/.config/libreoffice/4/user/Scripts/python/locale/fr_FR/LC_MESSAGES

génération du fichier

Cliquez sur bouton enregistrer

.
├── 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