[Python] (bien) débuter avec LibreOffice ou OpenOffice

Venez ici afin d'enrichir la documentation de nos suites bureautiques préférées. Déposez une demande ou y répondre par la création ou la traduction d'un tutoriel.

Modérateur : Vilains modOOs

Répondre
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python] (bien) débuter avec LibreOffice ou OpenOffice

Message par jeanmi2403 »

Bonjour,
Lorsque j’ai voulu commencer à programmer en Python, c’était pour étudier une extension écrite dans ce langage, et qui ne fonctionnait plus.
J’ai donc commencé par apprendre un peu le langage, et ça s’est plutôt bien passé.
J’ai ensuite essayé de faire interagir des scripts avec Libre Office, et c’est là que les difficultés ont vraiment commencé.
Je compte faire ici le récit de mes galères et de mes succès (en fait, peu de choses de mon cru, mais des solutions et des réponses trouvées un peu partout) en espérant que cela puisse servir à d’autres, et éviter qu’ils ne renoncent en raison de difficultés qui semblent rédhibitoires et qui trouvent toujours solution.

Je tiens à remercier les collègues du forum sans qui j’aurais mis beaucoup plus longtemps pour avancer.

Aborder un nouveau langage de programmation n’est pas trop difficile tant qu’il s’agit de :
  • syntaxe, mots-clés
  • structures de contrôle
Ce qui est plus délicat :
  • les entrées sorties (ou la communication avec l’utilisateur)
  • la bibliothèque d’objets, de méthodes et de fonctions
  • Avec LibO et AoO, l’interaction avec les documents (ce qui est le but des macros, oui ?)
Pour commencer, je me suis attaqué à de « petits » sujets, et j’ai fait à chaque fois un exemple dans un document en attachant le script à un bouton, de manière à pouvoir tester rapidement le fonctionnement, sans passer par tous les menus Outis/Macros/Gérer les macros/Python/ etc.
Ce sont ces documents-exemples que je vais partager ici.
Ce sont des solutions qui fonctionnent, pas nécessairement les meilleures, ni les plus optimisées.
Préliminaire (Ajout du 9/04/2023)
Un outil indispensable à installer avant de commencer : L'extension APSO.
Un exemple de configuration et d'utilisation avec Calc est donné par Jurassic Pork dans ce message.
Sommaire
Où sont les scripts ?
Où est l'éditeur de textes ?
Des ressources et des cours
Caractères accentués, indentation, des causes d'échec !
Accès aux documents : le contexte de script
Les exemple disponibles dans LibreOffice
Les boîtes de message
Utiliser une bibliothèque d'outils
Utiliser une bibliothèque d'outils dans un document
Boîtes de dialogue
Boîte de dialogue d'accès aux fichier
Dernière modification par jeanmi2403 le 09 avr. 2023 00:48, modifié 9 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python] Où sont les scripts ?

Message par jeanmi2403 »

C’est la première question que l’on se pose en voulant utiliser python dans LibO
Les fichiers/scripts peuvent se trouver à trois endroits différents.
Avec LibreOffice
Dans le sous-dossier LibreOffice du dossier personnel de l’utilisateur ;
  • Windows : C:\Users\<nom utilisateur>\AppData\Roaming\LibreOffice\4\user\Scripts \Python
  • Linux : ~/.config/libreoffice/4/user/Scripts/python/
Le dossier C:\Users\<nom utilisateur>\AppData\Roaming\LibreOffice\4\user est créé à la première utilisation de Libre Office par l’utilisateur. Il faudra créer soi-même le dossier Scripts et le sous-dossier python. Idem pour Linux
Dans le sous-dossier python du dossier du logiciel
  • Windows : C:\Program Files\LibreOffice\share\Scripts\python pour une version x64,
  • Linux : /opt/libreofficeXX/share/Scripts/python/ (XX selon version du logiciel)
Dans le dossier python d’un document.

Personnellement je ne travaille que dans le dossier personnel. Une fois le script opérationnel, je le mets dans un document, s’il ne doit fonctionner que dans ce cadre, ou bien pour le distribuer (comme en Basic…) et jamais dans le dossier du logiciel *Office.
Pour accéder rapidement à ce dossier, un raccourci sur le bureau facilite les choses.
Avec Apache OpenOffice
Dans le sous-dossier OpenOffice du dossier personnel de l’utilisateur
  • C:\Users\<nom utilisateur>\AppData\Roaming\OpenOffice\4\user\Scripts\Python.
Le dossier C:\Users\<nom utilisateur>\AppData\Roaming\OpenOffice\4\user\Scripts est créé à la première utilisation de Open Office par l’utilisateur. Il faudra créer soi-même le sous-dossier python.
Dans le sous-dossier python du dossier du logiciel
  • C:\Program Files (x86)\OpenOffice 4\share\Scripts\python, AoO est 32 bits.
Dans le dossier python d’un document.

4 : Les développeurs ont décidé de ne pas créer un nouveau dossier à chaque changement de version majeure (4/5/6/etc) avec les problèmes de migration de profil qui surviennent parfois.
Comme cela a commencé avec la version 5.0.0, le dossier utilisateur reste figé à 4. (Gérard24)

Sommaire
Dernière modification par jeanmi2403 le 12 févr. 2024 18:44, modifié 8 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python] Où est l’éditeur de textes ?

Message par jeanmi2403 »

Réponse simple, il n’y en a pas. Personnellement, j’utilise NOTEPAD++ (sous Windows) ou Geany (sous Linux et Windows) qui mettent en évidence la syntaxe python et permettent le contrôle visuel de l’indentation, en refermant/développant les structures de contrôle.
NOTEPAD++ permet aussi l’auto-complétion des mots et des fonctions.
La coloration n'est active que si le fichier a l'extension "py". Il faut donc que le fichier soit enregistré au moins une fois, ce qui est le cas si le fichier est créé par APSO.
Exemple de coloration avec Notepad
Exemple de coloration avec Notepad
Sommaire
Dernière modification par jeanmi2403 le 19 févr. 2024 16:53, modifié 6 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python]Ressources et cours

Message par jeanmi2403 »

Où trouver de l'aide et de la documentation. Liste non exhaustive !
Documentation python
La référence absolue : python.org : Cours
OpenClassrooms (anciennement le site du zéro) Developpez.com (choisir programmation puis tutoriels python) Apprendre Python Programmation orientée interface sous LibreOffice/OpenOffice : Automatiser les tâches avec python Libre Office/Open Office
Python comme langage macro Transfert de BASIC à PYTHON (A lire impérativement même si on n'a pas programmé en OObasic) LibreOffice SDK (pauvre en python, peu d'exemples) PyUno (Python et OpenOffice) Bibliothèques
PyooCalc (Créer des documents calc, des rapports) UNO et API
The Apache OpenOffice API Project Passerelle Python-UNO SDK API reference LibreOffice SDK API référence OpenOffice Outils
Analyseur de syntaxe en ligne Sommaire
Dernière modification par jeanmi2403 le 07 févr. 2024 16:09, modifié 7 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python] caractères accentués, indentation : des causes d'échec

Message par jeanmi2403 »

Caractères accentués
Mon premier travail a été de tester les exemples fournis avec LibreOffice (et d’autres glanés çà et là).
Pour bien comprendre leur fonctionnement, j’ai traduit ou complété les commentaires, évidemment avec des caractères accentués.
La surprise c’est qu’ensuite les fichiers sont affichés dans l’interface de gestion des scripts, mais on ne voit aucune des fonctions à l’intérieur, bien entendu pas question de tester ou d’exécuter quoi que ce soit, le bouton Exécuter est grisé.
L’outil de gestion des macros python d’Hubert Lambert renvoie un message d’erreur relativement compréhensible.
 Ajout : La dernière version est disponible sur le site des extensions
Le souci vient de ce que python ne comprend pas nativement ces caractères, mais uniquement ASCII7 bits. Il faut convertir le fichier en UTF-8, et commencer le fichier avec l’instruction suivante :

Code : Tout sélectionner

# -*- coding: utf-8 -*-
L'indentation
Le même phénomène peut se produire dans le cas d’une erreur d’indentation.
Cette erreur peut -être très sournoise ! En modifiant un script écrit par quelqu'un d'autre, récupéré sur le forum, dans une documentation, l'indentation sera réalisée avec des espaces (souvent trois). Un saut de ligne dans Notepad++ introduira une tabulation et l'indentation deviendra incohérente pour l'interpréteur (un seul caractère au lieu de 3).
Dans ce cas, l’extension APSO de Hubert Lambert donne un message d’erreur explicite.
Si on souhaite que les macros soient portables sur AoO (prise en compte des caractères accentués à l’affichage sans écrire de vermicelle à la place), il faudra rajouter la ligne :

Code : Tout sélectionner

from __future__ import unicode_literals
Sommaire
Dernière modification par jeanmi2403 le 09 avr. 2023 01:12, modifié 4 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

Re: [Python] Le contexte de script

Message par jeanmi2403 »

Références :
AoO : http://www.openoffice.org/api/docs/comm ... ntext.html
LibO : http://api.libreoffice.org/docs/idl/ref ... ntext.html
En macro Python, on doit utiliser la variable XSCRIPTCONTEXT, qui fournit aux scripts une interface, et des moyens d’accès aux diverses interfaces qui permettent d’agir sur les documents.
Plusieurs méthodes disponibles, entre autres :
  • getDocument() récupère l’objet document courant (interface de type xModel)
  • getDesktop() récupère une instance du service Desktop
  • getComponentContext() récupère le contexte du composant courant
Agir sur les documents
Après avoir récupéré le document model=XSCRIPTCONTEXT.getDocument(), on pourra déterminer le type de document, avec la fonction Python hasattr()
  • if hasattr(model, "Text") vrai pour un document Writer
  • if hasattr(model, "Sheets") vrai pour un document Calc
  • if hasattr(model, "Presentation"), etc..
Pour un document texte, on récupère une interface XtextRange, à laquelle sont attachées un certain nombre de méthodes, dont
  • getText() renvoie l’interface Xtext permettant l’accès au contenu
  • getStart() début du texte
  • getEnd() fin du texte
  • getString() récupère le contenu de la chaîne
  • setString() pose le contenu de la chaîne
Exemple :

Code : Tout sélectionner

 # python permet un code compact sans utiliser de variables intermédiaires
XSCRIPTCONTEXT.getDocument().getText().setString("Bonjour, j'ai écrasé tout le contenu entier du document !")
réalise exactement ce que dit le message !
Exemple-BonjourEcrase.odt
L'exemple ci-dessus, dans un fichier texte
(13.53 Kio) Téléchargé 777 fois
 Ajout : Attention, les exemples ne fonctionnent que s'ils sont enregistrés, sinon les macros sont bloquées... 
Sommaire
Dernière modification par jeanmi2403 le 09 juin 2020 17:38, modifié 3 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python] Les exemples disponibles

Message par jeanmi2403 »

On trouvera dans le dossier C:\Program Files\LibreOffice\share\Scripts\python des exemples de scripts
  • Capitalise.py (mise en majusculers/minuscules du texte sélctionné)
  • HelloWorld.py (écrit "hello world" à la fin du document
  • InsertText.py (insère "Hello" à la place de toutes les zones sélectionnées)
  • NamedRanges.py (crée un fichier Calc avec une feuille valeurs et une feuille descriptif)
  • SetCellColor.py (crée une feuille Calc et colorie quelques cellules)
  • TableSample (crée un fichier Writer avec une table et une zone de texte)
et également LibreLogo un interpréteur pour la tortue Logo
On peut tester ces scripts depuis l’interface de LibreOffice on trouvera ici d’autres exemples : Plutôt que le traditionnel "Hello world", je propose un exemple avec deux procédures qui affichent la version de python et la localisation de l’exécutable, dans un document Writer et un document Calc. Ce qui permet d'utiliser deux paramètres de la bibliothèque sys.

En Python, il faut définir une fonction Python avec un argument (ou une liste variable d’arguments de longueur variable) pour prendre l’argument passé lorsque la fonction est exécutée via le bouton de la barre d'outils assignée ou un bouton d’une boîte de dialogue, sinon, on obtient un message d'erreur
Exemple :
Hello() takes 0 positional arguments but 1 was given
La fonction doit être déclarée comme recevant une liste d’arguments :

Code : Tout sélectionner

def hello(*args) :
Exemple-VersionPython.odt
(15.79 Kio) Téléchargé 875 fois
Exemple-Capitalise.odt
(22 Kio) Téléchargé 675 fois
Exemple-TableSample.odt
(17.52 Kio) Téléchargé 733 fois
Pour lire les scripts, les extraire du document en rajoutant l'extension zip, ou bien installer l'extension apso de Hubert Lambert
Sommaire
Dernière modification par jeanmi2403 le 19 févr. 2024 16:57, modifié 9 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python] Les boîtes de message

Message par jeanmi2403 »

Les boîtes de message sont destinées à créer des interactions courtes avec l’utilisateur. En retour la fonction renvoie un nombre correspondant au bouton choisi.
On utilise les constantes de la bibliothèque plutôt que les valeurs numériques, plus difficiles à mémoriser.
L’icône est déterminée par le type de boîte.
Les icônes sont différentes, selon qu’on est avec LibO ou Ao0
Références Une boîte générique :
On a très souvent besoin d’une boîte simple, avec le seul bouton OK. La fonction suivante utilise les trois derniers paramètres de manière optionnelle (merci Hubert)

Code : Tout sélectionner

import uno
from com.sun.star.awt.MessageBoxType import MESSAGEBOX, INFOBOX, ERRORBOX, WARNINGBOX, QUERYBOX
from com.sun.star.awt.MessageBoxButtons import BUTTONS_OK
#createUnoService n’existe pas en Python...
def createUnoService(service, ctx):
    return ctx.ServiceManager.createInstanceWithContext(service, ctx)

def MsgBox(message, titre="Message", boxtype='message', boutons=BUTTONS_OK):
    ctx = uno.getComponentContext()
    win = createUnoService("com.sun.star.frame.Desktop", ctx).getCurrentFrame().ContainerWindow
    types = {'message': MESSAGEBOX, 'info': INFOBOX, 'error': ERRORBOX,
             'warn': WARNINGBOX, 'query': QUERYBOX}
    tk = createUnoService("com.sun.star.awt.Toolkit", ctx)
    box = tk.createMessageBox(win, types[boxtype], boutons, titre, message)
    return box.execute()
Exemple-MsgBox.odt
Exemples de boîtes MsgBox pour tester les différents paramètres
(20.67 Kio) Téléchargé 679 fois
Sommaire
Dernière modification par jeanmi2403 le 13 févr. 2018 16:17, modifié 2 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python] Bibliothèque d’outils

Message par jeanmi2403 »

La boîte de message MsgBox(), et l’outil Xray sont typiques du besoin. Dès qu’un programme en cours de développement a besoin de donner de l’information où d’examiner des objets, il faut que cela puisse se faire rapidement, sans avoir à copier un bout de code dans le module en cours de test.
Il faut donc pouvoir appeler une fonction depuis un autre module que celui où elle se trouve.
En Basic
il suffit d’une instruction BasicLibraries.LoadLibrary("NomDuModule") pour que les fonctions de ce module soient accessibles.
Par exemple :

Code : Tout sélectionner

 BasicLibraries.LoadLibrary("XrayTool") 'dans une fonction, et l’assigner à l’événement « Démarrer l’application »
En Python
il suffit de placer le module dans un dossier « pythonpath » dans Scripts\python (ce dossier est invisible dans le gestionnaire de macros)
et d’ajouter l’instruction import « NomDuModule ». Les fonctions sont ensuite accessibles en préfixant leur nom par « NomDuModule »
Exemple :

Code : Tout sélectionner

# le module bibliothèque s’appelle Outils.py
import Outils 
# pour appeler la fonction
Outils.MsgBox(<le message>)
Un détail : cela fonctionne si le premier import se fait depuis un module situé au même niveau que le dossier « pythonpath » (le dossier est rajouté au PATH système) mais pas depuis un autre dossier. En revanche, tout fonctionne correctement ensuite.

Pour ajouter le chemin, s’il n’est pas encore pris en compte :

Code : Tout sélectionner

   try:
        import Outils
    except ImportError:
        import sys
        pythonpath = "C:\Users\<utilisateur>\AppData\Roaming\LibreOffice\4\user\Scripts\python\pythonpath"
        sys.path.append(pythonpath)
        importOutils
On serait tenté d’assigner cette instruction au démarrage de l’application, mais il y déjà une fonction Basic (pour le chargement automatique de Xray)!
Et comme il ne semble pas possible de modifier l’environnement Python depuis un script Basic….
Dans ce cas, on appelle la fonction Python depuis la fonction Basic

Code : Tout sélectionner

‘ fonction Basic assignée au démarrage de l’application
Sub LoadingLibraries
 Dim scriptPro As Object
 BasicLibraries.LoadLibrary("XrayTool") ‘Ca c’est pour Xray
‘et ça c’est pour lancer le script Python
 scriptPro =  createUnoService("com.sun.star.script.provider.MasterScriptProvider")
 oScript =  ScriptPro.GetScript("vnd.sun.star.script:InitPython.py$SetPath?language=Python&location=user")
 oScript.invoke(Array(), Array(), Array() )
End Sub
Et en python

Code : Tout sélectionner

#Fonction SetPath, dans le fichier InitPython.py
def SetPath(*args):
    import sys
    pythonpath = "C:\\Users\\<utilisateur>\AppData\\Roaming\\LibreOffice\\4\\user\Scripts\\python\\pythonpath"
    sys.path.append(pythonpath)
    return None
Exemple-Bibli-MsgBox.odt
Test bibliothèque
(19.95 Kio) Téléchargé 663 fois
Sommaire
Dernière modification par jeanmi2403 le 09 juin 2020 18:19, modifié 11 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

Re: [Python] Bibliothèque dans un document

Message par jeanmi2403 »

On peut avoir à transmettre un document contenant aussi la bibliothèque (ou construire un programme en plusieurs modules), plutôt que d’intégrer les outils dans le script.
Pour cela, dans le script, (en supposant que la bibliothèque est dans le fichier Outils.py) insérer le code suivant,

Code : Tout sélectionner

import sys
import unohelper

if not 'Outils' in sys.modules:
    doc = XSCRIPTCONTEXT.getDocument()
    url = unohelper.fileUrlToSystemPath('{}/{}'.format(doc.URL,'Scripts/python’))
    sys.path.insert(0, url)
import Outils
Sommaire
Dernière modification par jeanmi2403 le 07 déc. 2016 15:11, modifié 1 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python] Boîte de saisie

Message par jeanmi2403 »

Pour une communication plus aboutie, on peut avoir à demander une réponse autrement que par un bouton :lol:
Il est possible de construire les boîtes de dialogue intégralement par script.
Il est également possible d’utiliser les boîtes de dialogue créées avec l’outil de gestion (Outils/Macros/Gérer les boîtes de dialogue).
Le code préliminaire :

Code : Tout sélectionner

    model = XSCRIPTCONTEXT.getDocument()
    smgr = uno.getComponentContext().ServiceManager
    dp = smgr.createInstanceWithArguments("com.sun.star.awt.DialogProvider",(model,) )
Céation de la boîte :

Code : Tout sélectionner

   # en supposant que la boîte DialogInput.xdl se trouve dans le document
   dialog = dp.createDialog("vnd.sun.star.script:Standard.DialogInput?location=document")
Dans le cas d’une boîte de dialogue dans le profil utilisateur ou dans le dossier du logicel.:

Code : Tout sélectionner

    ctx = XSCRIPTCONTEXT.getComponentContext()
    smgr = ctx.getServiceManager()
    dp = smgr.createInstanceWithContext("com.sun.star.awt.DialogProvider", ctx)
    dialog = dp.createDialog("vnd.sun.star.script:Standard.DialogUser?location=application")

Il faudra manipuler quelques fichiers de configuration pour faire prendre en compte des boîtes de dialogue placées dans le dossier logiciel de LibreOffice.
Pour les déplacer, utiliser Importer/exporter du menu de gestion.
Exemple-DialogueEntree.odt
Boîte de saisie
(20.91 Kio) Téléchargé 639 fois
Sommaire
Dernière modification par jeanmi2403 le 13 févr. 2018 16:18, modifié 2 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

[Python] Boîte de dialogue d'accès aux fichiers

Message par jeanmi2403 »

Le contrôle Fichier des boîtes de dialogues standard est un peu limité. Il faudrait en plus construire un parcours de répertoires avec le contrôle Tree.
Le service compétent pour des boîtes de dialogue complètes est le service FilePicker :
Le service FilePicker décrit dans l’API présente un manque sous Windows, la méthode setDisplayDirectory n’agit pas, il faut utiliser OfficeFilePicker, qui possède les mêmes propriétés et méthodes.
Sur le document , on trouvera deux boutons :
Ouvrir
Définit le dossier du document comme dossier pour l’affichage.
Présente une boîte de dialogue Ouvrir, avec un filtre à deux composants, odt et ods.
Renvoie le nom du fichier choisi. Ne charge pas le fichier.
Enregistrer
Définit le dossier du document comme dossier pour l’affichage.
Présente une boîte de dialogue Ouvrir, avec un nom de fichier par défaut, un filtre à trois composants, odt, ods et odp, et la case à cocher pour l’auto extension du nom de fichier.
Filepicker prévient si l’on choisit un fichier existant.
Renvoie le nom de fichier choisi, avec l’extension choisie dans la liste déroulante.
Remarque importante
Ces deux scripts ne font rien que renvoyer le numéro du type de bouton choisi.
Ils ne chargent rien et n’enregistrent rien ! On peut donc les exécuter jusqu’au bout sans crainte….
Exemple-FilePicker.odt
(22.15 Kio) Téléchargé 655 fois
Sommaire
Dernière modification par jeanmi2403 le 15 mai 2020 18:14, modifié 1 fois.
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
LibreOfficiant
Membre lOOyal
Membre lOOyal
Messages : 40
Inscription : 03 janv. 2017 14:54

[Python] Guide du Programmeur Python

Message par LibreOfficiant »

Bonjour à tous

Je m'apprête à complémenter/rédiger en français, en anglais un Guide du Programmeur Python ( sur la base de mon parcours du combattant ma toute petite expérience en la matière ) en mettant à jour le Wiki de LibreOffice ainsi que ce forum. Son contenu devrait être pour commencer:

o Documentations (API,forums, ..)
o Installation
o Modélisation (UML)
o Outils (extensions..)
o Mise au Point (IDEs)
o Examples multilangages (Basic, Python, Javascript, ..)
o Ecriture d'extensions

Je privilégie et utilise tout ce qui peut être gratuit, de préférence 'Open Source' et multi-plateforme. Ainsi à ce jour mes outils sont StarUML. PyCharm, LibreOffice, VirtualBox, GNU/Linux.

Je ne souhaite pas limiter mon contenu aux seuls outils que je connais ou pratique. Ainsi je serai heureux que vous m'indiquiez quels sont les vôtres et surtout pourquoi vous les préférez à d'autres. Les liens sur les forums français ou anglais sont les bienvenus.

Merci par avance

cf. https://wiki.documentfoundation.org/Mac ... n_Guide/fr
libO 5.4 64bit, (PortableApps: libO 6.0, aOO 4.1, OOo 3.2 32bit) sur Win7/Win10 x64 | aOO 4.1.x et libO 5.4.x sur Mint 18 Sarah et OSX 10.9 Mavericks x64
Boîte à Outils Python: Geany, PyCharm et bien sûr APSO, MRI..
https://wiki.documentfoundation.org/Mac ... n_Guide/fr
Avatar de l’utilisateur
Bidouille
RespOOnsable forum
RespOOnsable forum
Messages : 12196
Inscription : 08 nov. 2005 17:23
Localisation : Caen, France
Contact :

Re: [Python] Guide du Programmeur Python

Message par Bidouille »

Bonjour,

Question déplacée dans la bonne section et fusionnée avec un fil sur le même sujet.
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

Re: [Python] (bien) débuter avec LibreOffice ou OpenOffice

Message par jeanmi2403 »

Bonjour,
J'aimerais autant que ce fil reste "propre" , puisqu'il ne contient que des exemples d'utilisation.
Cela dit, je serais ravi de participer à cette documentation, puisque avec Libreofficiant, nous intervenons aux mêmes endroits des wiki Aoo et LibO
Cordialement,
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
Bidouille
RespOOnsable forum
RespOOnsable forum
Messages : 12196
Inscription : 08 nov. 2005 17:23
Localisation : Caen, France
Contact :

Re: [Python] (bien) débuter avec LibreOffice ou OpenOffice

Message par Bidouille »

jeanmi2403 a écrit :J'aimerais autant que ce fil reste "propre" , puisqu'il ne contient que des exemples d'utilisation.
Le fil peut très bien être enrichi par les participations d'autres utilisateurs.
C'est le principe de cette section.
Une fois que vous estimez le travail terminé, il sera "lavé" des messages inutiles puis placé dans la section Tutoriels.
Avatar de l’utilisateur
jeanmi2403
SuppOOrter
SuppOOrter
Messages : 1398
Inscription : 18 janv. 2008 10:02
Localisation : Val de Marne

Re: [Python] (bien) débuter avec LibreOffice ou OpenOffice

Message par jeanmi2403 »

Bonjour,
Bidouille a écrit : Le fil peut très bien être enrichi par les participations d'autres utilisateurs.
C'est le principe de cette section.
Une fois que vous estimez le travail terminé, il sera "lavé" des messages inutiles puis placé dans la section Tutoriels.
Complètement d'accord avec ça.
Merci,
Jean-Michel
LibO 7.5.9 et AoO 4.1.15 sur Windows 11 & Ubuntu 22.04
LibO 7.6 sur OpenSuse & Linux MX
Avatar de l’utilisateur
Hubert Lambert
SuppOOrter
SuppOOrter
Messages : 1214
Inscription : 06 avr. 2016 09:26

PropertyValue : heurs et malheurs

Message par Hubert Lambert »

 
Heurs
Il est très simple d'instancier un objet struct à partir de python, en particulier le très fréquent com.sun.star.beans.PropertyValue.
Un exemple parmi d'autres :

Code : Tout sélectionner

def createPropertyValues(**kwargs):
    from com.sun.star.beans import PropertyValue
    return tuple(PropertyValue(k,0,kwargs[k],0) for k in kwargs)
qui permet de passer intuitivement les paires nom/valeur en tant qu'arguments nommés, comme dans cet exemple :

Code : Tout sélectionner

    props = createPropertyValues(ActiveConnection=oConnection, OpenMode=sMode)
Malheurs
Dans certains cas particulier, notamment lorsqu'on doit passer une tableau de PropertyValue à une méthode du type replaceByName() ou replaceByIndex(), l'instruction suivante coincera :

Code : Tout sélectionner

    # affecter une macro à l'événement "vue créée" du document courant
    doc = XSCRIPTCONTEXT.getDocument()
    events = doc.Events
    script = "vnd.sun.star.script:Standard.Module1.mamacro?language=Basic&location=document"
    eventtype = "Script"
    event = createPropertyValues(Script=script, EventType=eventtype)
    events.replaceByName("OnViewCreated", event)    #--> IllegalArgumentException
Le problème (et sa solution) est évoqué et expliqué à plusieurs endroits sur cette page.
La cause se trouve dans le typage contextuel ("duck typing") propre à python, qui dans de rares cas est incapable de transmettre le type attendu par le programme. Le contournement consiste à invoquer la fonction en passant, à l'aide de la classe Any du module uno, un objet précisant le type et la valeur du paramètre. Dans l'exemple ci-dessus, la dernière ligne devra ainsi être remplacée par celle-ci :

Code : Tout sélectionner

    uno.invoke(events, "replaceByName", ("OnViewCreated", uno.Any("[]com.sun.star.beans.PropertyValue", event)))    #--> ok
Un détail encore
Si, comme moi, par commodité ou pour une meilleure compatibilité avec LibreOffice, vous avez pris l'habitude de forcer les chaînes de caractères en unicode sous OpenOffice avec la ligne d'import

Code : Tout sélectionner

from __future__ import unicode_literals
le contournement ci-dessus renverra une erreur de type RuntimeException : le fonction uno.invoke attend en effet un chaîne normale en second paramètre, et la chaîne unicode est rejetée. Il faudra donc la transformer à l'aide de la fonction str :

Code : Tout sélectionner

    uno.invoke(events,str("replaceByName"), ("OnViewCreated",uno.Any("[]com.sun.star.beans.PropertyValue", event)))
AOOo 4.1.7 sur Win10
AOOo 4.1.x sur Linux Mint
LibreOffice 5.x/6.x sur Linux Mint
--
| « Nos défauts devraient nous donner une qualité : l'indulgence pour les défauts des autres » (Rivarol)
Avatar de l’utilisateur
LibreOfficiant
Membre lOOyal
Membre lOOyal
Messages : 40
Inscription : 03 janv. 2017 14:54

Re: [Python] (bien) débuter avec LibreOffice ou OpenOffice

Message par LibreOfficiant »

A propos des boîtes de message ci-dessus, on peut ajouter:

Code : Tout sélectionner

from com.sun.star.awt.MessageBoxResults import CANCEL as ANNULER, OK, YES as OUI, NO as NON, RETRY as RÉESSAYER, IGNORE as IGNORER
http://api.libreoffice.org/docs/idl/ref ... sults.html

Cordialement
libO 5.4 64bit, (PortableApps: libO 6.0, aOO 4.1, OOo 3.2 32bit) sur Win7/Win10 x64 | aOO 4.1.x et libO 5.4.x sur Mint 18 Sarah et OSX 10.9 Mavericks x64
Boîte à Outils Python: Geany, PyCharm et bien sûr APSO, MRI..
https://wiki.documentfoundation.org/Mac ... n_Guide/fr
Avatar de l’utilisateur
LibreOfficiant
Membre lOOyal
Membre lOOyal
Messages : 40
Inscription : 03 janv. 2017 14:54

[Python] XSCRIPTCONTEXT revisité

Message par LibreOfficiant »

 Ajout : 4 Juillet 2018
Recommandation: L'approche développée ci-après dans [Python] Développement de macros *Office dans un EDI est plus simple et offre plus de personnalisations. 
Comme le savent les programmeurs non Basic (sic), *Office greffe à leur macros une variable globale XSCRIPTCONTEXT, véritable point d'entrée aux fonctions de l'API. Charge ensuite à eux de coder, de mettre au point dans *Office mais sans filet sinon le célèbre xRay, ou bien hors *Office via un (EDI) Environnement de Développement Intégré (re-sic). User d'un EDI signifie passer par la passerelle Python et donc du code supplémentaire avant de pouvoir utiliser les API.

Je simplifie tout cela au sein d'un module que j'utilise comme suit afin de faciliter mes validations futures au sein d' *Office :

Code : Tout sélectionner

from Office.Bridge import XSCRIPTCONTEXT  # Please comment this line when running within (Libre|Open)Office
J'insère cette ligne pour tester mes macros hors *Office. J'inhibe ou ôte cette instruction pour mes tests fonctionnels dans *Office.

Pré-requis : Le module 'Bridge.py' tente de se connecter à deux instances différentes d' *Office, une visible une cachée, lancées comme suit :

Code : Tout sélectionner

"C:\Program Files (x86)\LibreOffice 5\program\soffice.exe" --accept="pipe,name=LibreOffice;urp;"
"C:\Program Files (x86)\LibreOffice 5\program\soffice.exe" --accept="socket,host=localhost,port=2017;urp;" --norestore --nologo --headless --nodefault
S'il échoue il lance une exception signalant qu'il ne trouve pas ces instances d' *Office. Autrement il instancie un singleton nommé XSCRIPTCONTEXT supportant l'interface com.sun.star.script.provider.XScriptContext. Il suffit ensuite de recourir aux méthodes courantes de XSCRIPTCONTEXT :
  • XSCRIPTCONTEXT.getComponentContext()
  • XSCRIPTCONTEXT.getDesktop()
  • XSCRIPTCONTEXT.getDocument()
 Ajout : Le lancement de deux instances est un choix personnel, justifié par l'usage d'options de démarrage différentes. Une instance seule suffit ! 
Usage : Il est possible de se connecter à d'autres sessions d' *Office de cette manière :

Code : Tout sélectionner

XSCRIPTCONTEXT.connect(pipe="OpenOffice")  # returns object supporting com.sun.star.uno.XComponentContext interface
XSCRIPTCONTEXT.connect(host='localhost', port=1515)



Le code du module 'Bridge.py' est le suivant :

Code : Tout sélectionner

""" Module header

Module name : Bridge
Purpose     : Substitute to XSCRIPTCONTEXT when running Python scripts outside of (Libre|Open)Office.
Author      : Alain H. Romedenne
Description : The XSCRIPTCONTEXT object facilitates connections to running instances of (Libre|Open)Office.
    It connects to a piped instance of (Libre|Open)Office named 'LibreOffice'
    It connects to a localhost instance of (Libre|Open)Office using port=yyyy (yyyy=current year)
    It can connect to any variation of pipe name or port# used by a started (Libre|Open)Office.
    .ComponentContext, .CurrentDocument, .Desktop properties can be used.
    .connect(..): obj, .createInstance(name: str), .createUNOService(service: str) methods are available.
Intended    : Enable SSH connections to remote instances of (Libre|Open)Office.

Usage       :
    from Office.Bridge import XSCRIPTCONTEXT  # Please comment this line when running within (Libre|Open)Office

Credits     :
    christopher5106.github.io/office/2015/12/06/openoffice-libreoffice-automate-your-office-tasks-with-python-macros.html
    http://www.linuxjournal.com/content/starting-stopping-and-connecting-openoffice-python
    pyoo (c) 2014 Seznam.cz, a.s.

"""

#region Imports, constants

import datetime
import sys
# import socket  # only needed on win32-OOo3.0.0

#region UNO imports
import uno
# UNO interfaces
from com.sun.star.script.provider import XScriptContext                     # Script Context
from com.sun.star.frame import XModel                                       # - Current document object
from com.sun.star.document import XScriptInvocationContext                  # - Invocation dependent object
from com.sun.star.frame import XDesktop                                     # - Desktop object
from com.sun.star.uno import XComponentContext                              # - Component object
# UNO Exceptions
from com.sun.star.connection import NoConnectException
#from com.sun.star.container import NoSuchElementException

_NoConnectException = uno.getClass('com.sun.star.connection.NoConnectException')
# We try to catch them and to re-throw Python standard exceptions, if running outside of (Libre|Open)Office

#endregion

_VERBOSE = False
_MY_OFFICE = 'LibreOffice'
_MY_HOST = 'localhost'
_MY_PORT = datetime.date.today().year
_MY_PIPE = _MY_OFFICE
NOCONNECT_EXCEPTION = 'Failed to connect to %s on host=%s,port=%d,pipe=%s'
NODESKTOP_EXCEPTION = 'Failed to create % desktop on host=%s,port=%d,pipe=%s'
_UNSUPPORTED_PLATFORM_MSG = "'%s' platform is not supported yet."
_NOSTARTUP_EXCEPTION_MSG = 'Failed to start %s on host=%s,port=%d,pipe=%s'

#endregion

if _VERBOSE: print(datetime.date.today())

#region Object-Oriented Code
class Singleton(type):
    """
    A Singleton design pattern
    Credits: « Python in a Nutshell » by Alex Martelli, O'Reilly
    """
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
            return cls._instances[cls]
        #else:  # Remove comments if you expect explicit coding rejection.
        #   raise Exception("An instance of that object already exists.")

class _OfficeSession(XScriptContext, object, metaclass=Singleton,):
    """
    This class substitutes XSCRIPTCONTEXT when running Python scripts outside (Libre|Open)Office
    Usage:
    from LibreOffice import XSCRIPTCONTEXT  #  Please comment this line when running within (Libre|Open)Office
    """
    def __init__(self):  # Class constructor
        _OfficeSession.connect(hostname=_MY_HOST, port=_MY_PORT)
        _OfficeSession.connect(pipe=_MY_PIPE)
    #region Properties
    @property
    def ComponentContext(self) -> XComponentContext :
        """
        Connects to a running listening 'LibreOffice' IPC instance.
        :return: XSCRIPTCONTEXT substitute
        """
        return _OfficeSession._sessions[_MY_PIPE]
    @property
    def CurrentDocument(self) -> XModel :
        return self.getDocument() #  Office current base/IDE/calc/draw/impress/math/writer document Or 'None'
    @property
    def Desktop(self) -> XDesktop :
        return self.createInstance("com.sun.star.frame.Desktop")
    def InvocationContext(self) -> XScriptInvocationContext :
        return None
    #endregion

    #region Methods
    _sessions = {}  # (Libre|Open)Office listening IPC instances identified by pipe name or port #
    @staticmethod
    def connect(hostname=_MY_HOST, port=_MY_PORT, pipe=None) -> XComponentContext :
        """
        Connects to a running listening instance of (Libre|Open)Office
        e.g. ctx = obj.connect(port=2016) connects to 'started LibreOffice on port#2016' throws exception otherwise
        When specified pipe argument takes precedence over hostname:port arguments pair
        :param hostname: defaults to 'localhost'
        :param port: defaults to this year e.g. 2016
        :param pipe: .. 'LibreOffice' ..
        :return: last established context: defaults to 'LibreOffice' piped visible instance
        """
        local_context = uno.getComponentContext()
        resolver = local_context.ServiceManager.createInstanceWithContext(
            'com.sun.star.bridge.UnoUrlResolver', local_context)
        try:  # conn = 'pipe,name=%s' % pipe if pipe: else 'socket,host=%s,port=%d' % (hostname, port)
            if pipe:
                conn = 'pipe,name=%s' % pipe
            else:
                conn = 'socket,host=%s,port=%d' % (hostname, port)
            connection_url = 'uno:%s;urp;StarOffice.ComponentContext' % conn
            if _VERBOSE: print(connection_url)
            _established_context = resolver.resolve(connection_url)
        except NoConnectException:  # thrown when LibreOffice specified instance isn't started
            tb = sys.exc_info()[2]
            raise Exception(NOCONNECT_EXCEPTION % (_MY_OFFICE, hostname, port, pipe)).with_traceback(tb) \
                from _NoConnectException
        if pipe:
            _key = pipe
        elif port:
            _key = port
        else:
            _key = _MY_PORT
        _OfficeSession._sessions[_key] = _established_context
        if _VERBOSE: print(__name__ + ' (yyyy-mmm-dd hh:mm:ss) Connection to %s established with %s' % (_MY_OFFICE, conn))
        return _established_context
    def createInstance(self, name):
        _remote_context = self.ComponentContext
        obj = _remote_context.ServiceManager.createInstanceWithContext(name, _remote_context)
        return obj
    def createUNOService(self, name):  # as offered in Office Basic
        return self.createInstance(name)
    def getComponentContext(self):
        return self.ComponentContext
    def getDesktop(self):
        return self.Desktop
    def getDocument(self):
        return self.Desktop.CurrentComponent
    #endregion
#endregion

XSCRIPTCONTEXT = _OfficeSession()

#region Functions
def createObject(class_name: str):
    """
    LibreOffice class objects factory.
    Substitute to LibreOffice Basic CreateUnoService(), equivalent to OLE/COM createObject()...
    Throws uno.RuntimeException
    :param class_name: e.g. 'com.sun.star.sdb.DatabaseContext'
    :return: a LibreOffice instance of 'class_name'.
    """
    ctx = XSCRIPTCONTEXT.getComponentContext()
    return ctx.getServiceManager().createInstanceWithContext(class_name, ctx)

def CreateObject(class_name: str):
    """
    LibreOffice class objects factory.
    Substitute to LibreOffice Basic CreateUnoService(), equivalent to OLE/COM createObject()...
    Throws uno.RuntimeException
    :param class_name: e.g. 'com.sun.star.sdb.DatabaseContext'
    :return: a LibreOffice instance of 'class_name'.
    """
    ctx = uno.getComponentContext()
    return ctx.getServiceManager().createInstanceWithContext(class_name, ctx)

#endregion


""" Change Log aka Summary of changes

Date        Full name           Description                                                                     Ref. #
2016-Dec-12 Alain H. Romedenne  Added createObject() public function in place of <session>.createXXX() methods
2016-Oct-15 Alain H. Romedenne  Module creation                                                                 ... ...

"""

# Quick n Dirty Testing
if __name__ == '__main__':

    ctx = XSCRIPTCONTEXT.ComponentContext
    dsk = XSCRIPTCONTEXT.getDesktop()
    doc = XSCRIPTCONTEXT.getDocument()  # May return None
    obj = createObject("com.sun.star.frame.Desktop")
    print(ctx)
    print(dsk)  # com.sun.star.uno.XInterface
    print(doc)  # com.sun.star.lang.XComponent - base, calc, draw, basicIDE, impress, math, writer, ..

    #  print(XSCRIPTCONTEXT.ComponentContext.ImplementationName)
    print(dsk.ImplementationName)  #  com.sun.star.comp.framework.Desktop
    if doc is not None:
        print(doc.ImplementationName)  # com.sun.star.comp.*

Bonus: Voici les tests unitaires 'Bridge_UnitTests.py' que j'ai cru bon devoir écrire pour valider ce module :

Code : Tout sélectionner

import unittest

from Office.Bridge import XSCRIPTCONTEXT as XSC

VERBOSE = True
_DESKTOP = 'com.sun.star.comp.framework.Desktop'
_PIPE_NAME = 'LibreOffice'

class TestSessionProperties(unittest.TestCase):
    def setUp(self):
        self.y = XSC
    def tearDown(self):
        del self.y

    def test_constructor(self):
        print(XSC.ComponentContext)
        print(XSC.Desktop)  # com.sun.star.uno.XInterface
        print(XSC.CurrentDocument)  # com.sun.star.lang.XComponent - base, calc, draw, impress, math, writer, ..
    def test_ComponentContext(self):
        ctx = XSC.ComponentContext
        #self.assertIsInstance(ctx, ('com.sun.star.uno.XInterface'))
        self.assertEqual(XSC.ComponentContext, XSC.getComponentContext())
    def test_Desktop(self):
        #self.assertTrue(LibreOffice.Desktop.SupportedServices=='com.sun.star.frame.Desktop')
        self.assertTrue(XSC.getDesktop().ImplementationName == _DESKTOP)
        self.assertEqual(XSC.Desktop, XSC.getDesktop())
    def test_doc(self):
        if not XSC.getDocument() == None:
            self.assertTrue(XSC.CurrentDocument.ImplementationName ==
                            XSC.getDocument().ImplementationName)
        else:
            self.assertTrue(XSC.CurrentDocument == XSC.getDocument())
        self.assertEqual(XSC.CurrentDocument, XSC.getDocument())
    def test_InvocationContext(self):
        #self.assertTrue( XSC.InvocationContext is None)
        pass

class TestSessionMethods(unittest.TestCase):
    def test_connect(self):
        _xsc = XSC.connect(port=2016)
        _xsc = XSC.connect(pipe=_PIPE_NAME)

class TestSessionEvents(unittest.TestCase):
    pass

"""
class TestExceptions(TestCase):

    def setUp(self):
        src._DEBUG = True

    def test_Exceptions(self):
        self.assertRaises(self, src._NoConnectException, x.connect('localhost', 445))
        self.assertRaises(self, src._NoConnectException, x.connect(pipe='aPipename'))
        self.assertRaises(self, src._NoConnectException, x.connect(pipe='LibreOffice'))
"""

if __name__ == '__main__':

    unittest.main()



J'ai testé celà avec LibreOffice 5 sous Windows. Celà doit fonctionner aussi avec OpenOffice ainsi que sur d'autres plates-formes.
Usez-en, abusez-en .. et dîtes moi ce que vous en pensez !

Cordialement
Dernière modification par LibreOfficiant le 04 juil. 2018 10:34, modifié 2 fois.
libO 5.4 64bit, (PortableApps: libO 6.0, aOO 4.1, OOo 3.2 32bit) sur Win7/Win10 x64 | aOO 4.1.x et libO 5.4.x sur Mint 18 Sarah et OSX 10.9 Mavericks x64
Boîte à Outils Python: Geany, PyCharm et bien sûr APSO, MRI..
https://wiki.documentfoundation.org/Mac ... n_Guide/fr
Avatar de l’utilisateur
LibreOfficiant
Membre lOOyal
Membre lOOyal
Messages : 40
Inscription : 03 janv. 2017 14:54

Re: [Python] (bien) débuter avec LibreOffice ou OpenOffice

Message par LibreOfficiant »

A propos de Bibliothèques d'outils ci-dessus,

Il est possible d'appeler xRay et Mri depuis un script python.
xRay interrompra le cours la macro, mais pas Mri d'après mes tests.

On peut bien sûr recourir à *.createMessageBox, mais ces 2 outils sont tellement supérieurs.

Il faudrait (que je) traduire la documentation (en anglais) de Mri.

Cordialement
libO 5.4 64bit, (PortableApps: libO 6.0, aOO 4.1, OOo 3.2 32bit) sur Win7/Win10 x64 | aOO 4.1.x et libO 5.4.x sur Mint 18 Sarah et OSX 10.9 Mavericks x64
Boîte à Outils Python: Geany, PyCharm et bien sûr APSO, MRI..
https://wiki.documentfoundation.org/Mac ... n_Guide/fr
Avatar de l’utilisateur
LibreOfficiant
Membre lOOyal
Membre lOOyal
Messages : 40
Inscription : 03 janv. 2017 14:54

[Python] Développement de macros *Office dans un EDI

Message par LibreOfficiant »

 Ajout : Message totalement ré-édité le 12 mars 2018. 
Préambule

Utiliser un Environnement de Développement Intégré (EDI) pour tester/déboguer une macro Python est possible comme précisé dans Conception et Développement d'Applications Python.

Une macro Python ressemble à:

Code : Tout sélectionner

import uno
def my_1st_macro():
    # Pas exécutable directement dans Anaconda, Geany, KDevelop, PyCharm etc..
    doc = XSCRIPTCONTEXT.getDocument()
    doc.getText().setString("Hello World!")

g_exportedScripts = my_1st_macro,
L'édition directe et le test dans *Office ne nécessite qu'APSO(i). Dans ce cas un EDI n'est pas forcément nécessaire.

Pour qu'une macro s'exécute dans un EDI, et donc hors *Office, une passerelle est nécessaire entre (Libre/Open)Office et l'EDI. Alors les objets UNO deviennent accessibles, et toutes les fonctions de votre EDI préféré sont disponibles.

Jusqu'à 5 étapes peuvent être nécessaires pour réaliser cela:
  1. Démarrer LibreOffice comme un service
  2. Se connecter à ce service
  3. Créer un adaptateur pour XSCRIPTCONTEXT
  4. Lancer la macro
  5. Stopper le service LibreOffice
Cela se traduit par du code Python, présent en plus ou moins grande quantité, qu'il faut prendre soin d'enlever une fois prêt à valider son code dans *Office.

Fonctionnalités

Le module IDE_utils.py s'intègre de façon transparente dans vos macros et est inoffensif lors des validations au sein d' *Office. Il offre les possibilités suivantes :
  • Les étapes démarrer, connecter, adapter, lancer et stopper restent facultatives,
  • Toutes les plateformes sont supportées i.e. essentiellement Linux, Mac OS et Windows,
  • Les options de lancement de soffice sont paramétrables,
  • Les connections pipe et socket sont permises,
  • Le code Python n'est pas intrusif pour *Office,
  • Service pooling et context pooling,
  • KISS (keep it simple, stupid)
Le contenu d'IDE_utils est le suivant: Une classe Runner() de type context manager assure le lancement et l'arrêt des instances de soffice. Une fonction connect() lie l'EDI (IDE en anglais) et les instances de (Libre/Open)Office. Un object ScriptContext() est injectable dans XSCRIPTCONTEXT. Des fonctions start() et stop() englobent leurs homologues de la classe Runner().

Utilisation

De cette manière, une bibliothèque de macros Python *Office devient :

Code : Tout sélectionner

import uno
def my_macro(): pass  # Your code goes here

g_exportedScripts = my_macro, 

if __name__ == "__main__": 
    ''' IDE runnable code: *Office as a service '''
    import IDE_utils as geany
    with geany.Runner() as jesse_owens:  # Start/Stop
        XSCRIPTCONTEXT = geany.XSCRIPTCONTEXT  # Connect/Adapt
        my_macro()  # Run
Description : jesse_owens met en place une passerelle par défaut(ii), un contexte valide est injecté dans XSCRIPTCONTEXT, my_macro est lancée puis la passerelle est retirée par jesse_owens.

La documentation de la fonction bootstrap() d'officehelper(ii) indique ce qui se passe ici: «soffice ouvre un conduit (pipe en anglais) au nom variable, un contexte local construit un contexte distant lequel restitue un objet ServiceManager.».

On peut vouloir lancer un service avec d'autres options que celles utilisés par officehelper, qui sont --nodefault et --nologo. De même, démarrer et stopper un service ne convient pas à toutes les situations, ainsi ces deux étapes peuvent être omises. Personnaliser les conditions d'exécution de son service est possible en remplaçant celui par défaut. Ces différentes manières d'utiliser IDE_utils sont documentées et disponibles ici depuis le projet IDE_utils.

Installation
  1. Copier IDE_utils.py dans le répertoire <OFFICE>/program/ ou bien votre répertoire projet
  2. Copier l'un ou l'autre des exemples de la prise en main d'IDE_utils dans votre bibliothèque/module Python
  3. Lancer votre macro (Libre/Open)Office depuis votre EDI préféré
Les observations ayant motivé le développement d'IDE_utils sont documentées, en anglais. IDE_utils.py est disponible chez GitLab.

Notes :
  1. L'extension Alternative Python Script Organizer
  2. officehelper est un module standard de LibreOffice et OpenOffice qui réalise une connection vers *Office
Dernière modification par LibreOfficiant le 24 août 2018 10:58, modifié 10 fois.
libO 5.4 64bit, (PortableApps: libO 6.0, aOO 4.1, OOo 3.2 32bit) sur Win7/Win10 x64 | aOO 4.1.x et libO 5.4.x sur Mint 18 Sarah et OSX 10.9 Mavericks x64
Boîte à Outils Python: Geany, PyCharm et bien sûr APSO, MRI..
https://wiki.documentfoundation.org/Mac ... n_Guide/fr
Avatar de l’utilisateur
LibreOfficiant
Membre lOOyal
Membre lOOyal
Messages : 40
Inscription : 03 janv. 2017 14:54

Re: Appel de routines Basic avec Python .. et réciproquement

Message par LibreOfficiant »

 Ajout : Ajout d'une routine FileLen() retournant Currency plutôt que Long, le 30 Août 2018. 
Préambule
Les suites (Libre/Open)Office permettent le développement de macros en Basic, en Beanshell, en Javascript ou en Python. Leur exécution dans un document s’effectue sans considération du langage. Il est possible d’appeler une routine écrite en Basic depuis une fonction écrite en Python. La routine Basic doit être soit personnelle, soit partagée, elle ne peut pas être stockée dans un document. La passerelle pyuno doit être présente.
Il est également possible d’appeler une routine écrite en Python depuis une fonction écrite en Basic.La routine Python doit être soit personnelle, soit partagée, elle ne peut pas être stockée dans un document.
Appels Basic depuis Python
La manière d’appeler xRay, écrit en Basic, depuis Python fournit la base de nos exemples. Cet appel ne retourne aucune valeur :

Code : Tout sélectionner

import uno
from com.sun.star.uno import RuntimeException as _rtex
def xray(myObject):
  try:
    sm = uno.getComponentContext().ServiceManager
    mspf = sm.createInstanceWithContext("com.sun.star.script.provider.MasterScriptProviderFactory", uno.getComponentContext())
    scriptPro = mspf.createScriptProvider("")
    xScript = scriptPro.getScript("vnd.sun.star.script:XrayTool._Main.Xray?language=Basic&location=application")
    xScript.invoke((myObject,), (), ())
    return
  except:
     raise _rtex("\nBasic library Xray is not installed", uno.getComponentContext())
Il est possible d’appeler les fonctions Basic comme MsgBox, InputBox ou Print depuis Python, et d'exploiter leurs résultats. Des propriétés telles que ComputerName, disponible seulement sous Windows, ou bien UserName, disponible depuis LibreOffice 5.2, peuvent être étendues avec Python à toutes les plate-formes et généralisées aux produits OpenOffice et LibreOffice. La fonction Basic FileLen(), limitée aux fichiers de 2 Gio max, peut retourner jusqu'à 856 484 Gio en recourant au langage Python. Les exemples qui suivent l’illustrent de manière volontairement simplifiée. Les macros en pièce jointe détaillent ces exemples plus précisement, en incluant par exemple une gestion des erreurs.
Deux bibliothèques _Python et _Basic contiennent chacune un module appelé devTools. Chaque module est organisé comme suit :
  • _Python / devTools
    • Computername(): str
    • UserName(): str
    • getScript ( .. ): ..script.provider.XScript
    • MessageBox( .. ): int
    • FileLen( .. ): int
    • MsgBox( .. ): int
    • InputBox( .. ): str
    • Print( .. ):
    • macros exemples
  • _Basic /devTools
    • ComputerName() As String
    • UserName() As String
    • getScript( .. ) As Object
    • getURI( .. ) As String
    • FileLen( .. ) As Currency
    • MessageBox( .. ) As Integer
    • _MsgBox( .. ) As Integer
    • _InputBox(.. ) As String
    • _Print ..
    • macros exemples
Les macros en gras appellent leur homologues en Basic ou en Python.
Il est nécessaire d’exporter la bibliothèque _Basic comme personnelle ou partagée pour lancer les macros exemples en Python.
Il faut exporter le module devTools de _Python comme personnel ou partagé pour lancer les macros exemples en Basic.
Les macros exemples Python et Basic s’exécutent depuis le menu Outils – Macros… - Exécuter la Macro
Dialogue d'appel de routines Basic depuis Python
Dialogue d'appel de routines Basic depuis Python
Si la bibliothèque _Python n’apparaît pas sous Unix, il convient d’installer la passerelle pyuno comme suit :

Code : Tout sélectionner

sudo apt-get install libreoffice-script-provider-python
Appel de MsgBox, InputBox et Print depuis Python
Les programmes Basic ..

Code : Tout sélectionner

Private Function _MsgBox( msg As String, Optional options As Integer, Optional title As String ) As Integer
	_MsgBox = MsgBox( msg, options, title )
End Function
Private Function _InputBox( prompt As String, Optional title As String, Optional defaultInput As String) As String
	_InputBox = InputBox( prompt, title, defaultInput )
End Function
Private Sub _Print( msg As String )
	Print msg
End Sub
.. sont appelés en Python comme suit :

Code : Tout sélectionner

def MsgBox(message: str, type_buttons_default=0, title='LibreOffice') -> int:
	xScript = _getScript("_MsgBox")
	res = xScript.invoke((message,type_buttons_default,title), (), ()) 
	return res[0]
def InputBox(prompt: str, title='LibreOffice', defaultValue='') -> str:
	xScript = _getScript("_InputBox")
	res = xScript.invoke((prompt,title,defaultValue), (), ())
	return res[0]
def Print(message: str):
	xScript = getScript("_Print")
	xScript.invoke((message), (), ())
L’attribut Private indique une fonction interne à ne pas utiliser en Basic, il n’a pas d’effet. Le caractère préfixe souligné (_) pratiqué en Python traduit la même convention de codage, il est également informatif. Les paramètres facultatifs en Basic sont gérés depuis Python à l’aide d’arguments nommés valorisés aux valeurs par défaut :
  • types_buttons_default=0
  • title='LibreOffice'
  • defaultValue=''
Une fonction getScript() est responsable de la localisation et du chargement mémoire des scripts Basic :

Code : Tout sélectionner

from com.sun.star.script.provider import XScript
def getScript(script: str, library='_Basic', module='devTools') -> XScript: 
	sm = uno.getComponentContext().ServiceManager
	mspf = sm.createInstanceWithContext("com.sun.star.script.provider.MasterScriptProviderFactory", uno.getComponentContext())
	scriptPro = mspf.createScriptProvider("")
	scriptName = "vnd.sun.star.script:"+library+"."+module+"."+script+"?language=Basic&location=application"
	xScript = scriptPro.getScript(scriptName)
	return xScript
Convention d’appel Python vers Basic
La documentation de l’interface com.sun.star.script.provider.Xscript du SDK nous fournit la description de trois tuples paramètres d’appel de la méthode invoke() dans cet ordre :
  1. aParams (i.e. *args en Python) représente les paramètres passés positionnellement
  2. aOutParamIndex représente les indices des paramètres modifiés suite à l’appel
  3. aOutParam représente le(s) résultat(s) de l’appel
Les conventions d’appel du Python vers le Basic retournent un tuple résultat inversé qui se présente ainsi :
  • (aOutParam, aOutParamIndex, aParams)
Le résultat des fonctions Basic MsgBox et InputBox est récupéré par :
  • return res[0]
Appels de MRI, ObjectInspector et X-Ray depuis Python
Le module _Python.devTools illustre par ailleurs comment appeler les extensions MRI, ObjectInspector et X-Ray respectivement écrites en Python, Basic et Java.

Appel de ComputerName, FileLen, MessageBox et UserName depuis Basic
Les programmes Python ..

Code : Tout sélectionner

def ComputerName(): 
    import platform
    return platform.node()
def FileLen(systemFilePath):
    import os.path
    return str(os.path.getsize(systemFilePath))
def UserName():
    from getpass import getuser as userName
    return userName()
def MessageBox( box_type: int, buttons: int, title: str, message: str) -> int :
    desktop = uno.getDesktop()
    frame = desktop.getCurrentFrame()
    window = frame.getContainerWindow()
    toolkit = window.getToolkit()
    messagebox = toolkit.createMessageBox(window, box_type, buttons, title, message)
    return messagebox.execute()
.. sont appelés en Basic comme suit :

Code : Tout sélectionner

Public Function ComputerName As String
	Dim pyScript As Object
	pyScript = getScript(getURI( "devTools.py$ComputerName", "Python", "user" ))
	ComputerName = pyScript.invoke( Array(), Array(), Array() )
End Function ' ComputerName
Public Function FileLen(systemFilePath As String) As Currency
	Dim pyScript As Object
	pyScript = getScript(getURI( "devTools.py$FileLen", "Python", "user" ))
	FileLen = pyScript.invoke( Array(systemFilePath), Array(), Array() )
End Function ' _Basic.devTools.FileLen()
Public Function UserName As String
	Dim pyScript As Object
	pyScript = getScript(getURI( "devTools.py$UserName", "Python", "user" ))
	UserName = pyScript.invoke( Array(), Array(), Array() )
End Function ' UserName
Public Function MessageBox( box_type As Integer, _
				buttons As integer, _
				title As String, _
				message As String _
				) As Integer
	Dim pyScript As Object ' com.sun.star.script.provider.XScript
	pyScript = getScript(getURI( "devtools.py$MessageBox", "Python", "user" ))
	MessageBox = pyScript.invoke( Array(Cint(box_type), Cint(buttons), Cstr(title), _
					Cstr(message) ), Array(), Array() )
End Function
Les fonctions getScript() et getURI() sont responsables de la localisation et du chargement mémoire des scripts Python.

Sources
Pièces jointes
1. Python2Basic.Basic2Python.odt
Appel de routines Basic depuis Python.. et réciproquement
(81.45 Kio) Téléchargé 466 fois
Dernière modification par LibreOfficiant le 23 déc. 2018 15:26, modifié 6 fois.
libO 5.4 64bit, (PortableApps: libO 6.0, aOO 4.1, OOo 3.2 32bit) sur Win7/Win10 x64 | aOO 4.1.x et libO 5.4.x sur Mint 18 Sarah et OSX 10.9 Mavericks x64
Boîte à Outils Python: Geany, PyCharm et bien sûr APSO, MRI..
https://wiki.documentfoundation.org/Mac ... n_Guide/fr
Averell
Fraîchement OOthentifié
Messages : 1
Inscription : 11 févr. 2018 13:07

Re: [Python] (bien) débuter avec LibreOffice ou OpenOffice

Message par Averell »

Mille merci à vous, LibreOfficiant pour votre contribution à la connexion d'un IDE pour les macros Python. En suivant soigneusement, j'ai pu connecter PyCharm et bénéficier d'un IDE complet :bravo:
Pour ceux qui voudront faire de même, voici quelques conseils.

Mieux vaut séparer en deux la tâche.

1) Commencez par connecter votre IDE sur le python de Libre Office. Le test à faire est simple :

Code : Tout sélectionner

import uno
Pour Geany et PyCharm, vous trouvez les infos ici : https://wiki.documentfoundation.org/Mac ... sign_Guide
J'ai testé avec PyScripter : Cela marche en moteur externe (pas distant, ce n'est pas pareil). Menu Exécuter / configurer l'exécution externe.
Il suffit d'entrer l'adresse du python de votre installation office : C:\Program Files (x86)\LibreOffice 5\program\python.exe
Lancez l'exécution externe (ALT + F9)
Si vous n'avez pas d'erreur pour "import uno", c'est que vous êtes bien connecté sur le python de (Libre/Open)Office, vous pouvez passer à l'étape suivante.

2) Une fois cette étape assurée, suivez les indications plus haut :
  • Copiez Bridge.py dans le répertoire des scripts Python
  • Lancez *Office avec le paramètre --accept, en prenant l'une des deux options présentées plus haut. Au début j'ai cru qu'il fallait lancer les deux, mais non, c'est au choix. Par exemple :

    Code : Tout sélectionner

    "C:\Program Files (x86)\LibreOffice 5\program\swriter.exe" --accept="socket,host=localhost,port=2002;urp;StarOffice.ServiceManager" 
  • Faites bien attention d'accorder le numéro de port : par défaut, Bridge.py prend l'année, c'est à dire 2018 au moment où j'écris, alors que la ligne de commande indiquée plus haut pour lancer *Office indique 2017. Il est conseillé de modifier Bridge.py pour indiquer un port fixe.
Insérez la ligne demandée dans votre propre script pour assurer la connexion :

Code : Tout sélectionner

from Office.Bridge import XSCRIPTCONTEXT  # Please comment this line when running within (Libre|Open)Office
Pour éviter d'avoir à commenter cette ligne à chaque fois qu'on veut tester directement dans *Office, il est plus simple de la mettre à la fin :

Code : Tout sélectionner

if __name__ == '__main__':    
    from Bridge import XSCRIPTCONTEXT  
    main()
Ainsi on peut lancer le script indifférement dans *Office ou dans l'IDE, c'est plus pratique.


A ce stade, on a un IDE complet avec pas à pas, exploration des variables et des objets, etc. dans PyCharm
Parce que je me suis laissé tromper au début, je précise : PyCharm existe en version commerciale et en version gratuite.
Pour PyScripter, on peut lancer le script python, et bénéficier donc d'un bon éditeur, mais le moteur externe n'a pas de débogueur. On devrait pouvoir définir le python de Libre Office comme moteur distant, mais pour l'instant je n'ai pas réussi à le faire fonctionner.
Dernière modification par Averell le 16 févr. 2018 16:03, modifié 5 fois.
OpenOffice 4 - Windows 8.1
OlivierR
SuppOOrter
SuppOOrter
Messages : 1037
Inscription : 24 mai 2006 22:34
Localisation : Lorraine, France
Contact :

Re: [Python] (bien) débuter avec LibreOffice ou OpenOffice

Message par OlivierR »

Tutoriel pour créer une extension en Python :
https://forum.openoffice.org/fr/forum/d ... p?id=22781 [Fichier PDF]
LibreOffice 7.1Windows 10Grammalecte, correcteur grammatical et orthotypographique
Répondre