Macros

    From The Document Foundation Wiki
    < Macros‎ | Python Guide‎ | fr‎ | Guide

    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.

    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