Journal Écriture d'une macro dans OpenOffice.org

Posté par  .
Étiquettes :
13
22
sept.
2010
Cher journal

Avant d'aller plus loin, je te conseille très sincèrement de prendre commande de quelques kilogrammes d'aspirine. Tu vas avoir tellement mal à la tête…

À la base, je voulais écrire un journal pour montrer qu'il est simple de faire des macros dans OpenOffice.org. Je ne suis pas magicien, je ferai donc un journal pour expliquer comment j'ai écrit une macro dans OpenOffice.org...

Post-scriptum : l'indentation et Linuxfr ne sont pas très copains apparemment, vous m'en voyez navré…

0) L'objectif
La problématique est simple : j'ai déjà du, à plusieurs reprises, copier un tableau de calc dans writer. Et que ça soit avec un objet calc, une image ou autre, le résultat ne m'a jamais satisfait. Texte flou, mal gérable depuis le traitement de texte… Une hécatombe. La seule alternative : tout copier, cellule par cellule… Hé oui, c'est fastidieux…
Ainsi, j'ai décidé de faire comme dans l'ancien temps où j'écrivais des macros Word pour contourner les restrictions d'utilisation du système au lycée : écrire une macro OpenOffice.org pour faire ce travail à ma place (et la proposer en addon sur le site des extensions d'OpenOffice.org).
Le fonctionnement de la macro, d'un point de vue utilisateur, sera dans un premier temps le suivant :
- sélection dans calc du tableau
- lancement de la macro : un nouveau document writer est créé contenant notre beau tableau mis en forme et tout et tout...
OpenOffice.org est programmable en plusieurs langages de programmation, mais je ne me focaliserai ici que sur le StarBasic, simple et surtout ne dépendant pas d'un interpréteur externe...

1) Pré-requis techniques
- Savoir programmer en Basic
- Avoir beaucoup de patience
- Comprendre la programmation orientée objet, la notion de composants et d'interfaces
- Avoir une patience incalculable
- OpenOffice.org, un navigateur web, un moteur de recherche et des nerfs solides
- Recommandé : le livre Programmation OpenOffice.org, éditions Eyrolles

2) Première étape : construction d'une macro basique
Une fonctionnalité très très utile de Microsoft Office est l'enregistreur de macros. Essayez même pas l'enregistreur de macros d'OpenOffice.org, il est lamentable... De plus, il n'y a pas de complétion dans le code, et rien pour aider à l'exploration de l'API. Vous devriez, si vous tenez à vos neurones, installer XRay : il s'agit d'un outil permettant d'explorer à l'exécution les propriétés et méthodes d'un objet.
La première étape est la réalisation d'une macro qui va créer un document writer quand la sélection de l'utilisateur sera un ensemble de cellules. Cela permet de voir des premières manipulations de l'API...
Pour créer la macro, il suffit d'aller sur le menu Outils, puis Macros, Gérer les macros et sélectionner Basic. Un menu vous permettra de créer un module (c'est-à-dire une sorte de bibliothèque de macros), que vous ouvrez alors avec l'horrible éditeur Basic d'OpenOffice.org (moins horrible que l'éditeur de VBA quand même).

Voici à quoi ressemble cette première macro :
Sub CopyToWriter
Dim document as Object
Dim textDocument As Object
Dim sel as Object
Dim propertiesDoc()

document = thisComponent
sel = document.currentSelection

if sel.supportsService("com.sun.star.table.CellRange") then
textDocument = StarDesktop.LoadComponentFromURL("private:factory/swriter", "_blank", 0, propertiesDoc)
else
MsgBox "Select a range of cells"
end if
End Sub

Derrière le code de cette macro, pas de magie : il faut, hélas, lire la doc. Dans mon cas, j'ai utilisé le livre sur l'API d'OpenOffice.org, mais vous trouverez moultes informations sur http://api.openoffice.org et dans des documents comme http://api.openoffice.org/basic/man/tutorial/tutorial.pdf
La variable document est ici un objet référençant le document (Calc) courant. sel contient une référence à la sélection de l'utilisateur dans le document, textDocument est le document Writer créé, et propertiesDoc() est un tableau vide, parce qu'il faut bien donner des paramètres vides des fois… Plus sérieusement, StarDesktop est une sorte de représentation de l'instance globale d'OpenOffice.org, permettant de créer des documents, d'en ouvrir… Cet objet est document à l'adresse http://api.openoffice.org/docs/common/ref/com/sun/star/frame(...) mais une version plus pratique est disponible sur http://wiki.services.openoffice.org/wiki/Documentation/BASIC(...) .
Le test sur l'objet sel illustre bien, je pense, la notion de service : l'objet sel est inconnu, il pourrait très bien être un objet image, un graphique… Dans chacun de ces cas, d'autres services se retrouveront alors supportés par l'objet. On demande donc ici une zone de cellules.

Nour allons donc passer à l'étape suivante, plus amusante déjà, créer un tableau de NxM cellules dans le document texte.

3) Insérer un tableau dans du texte
Pour insèrer un tableau dans du texte, il faut tout d'abord créer l'objet correspondant au tableau, puis insérer cet objet dans le texte.
Décomposons donc ces deux étapes...
a) Création de l'objet tableau
C'est l'étape la plus simple : un tableau est «lié» au document, on le crée à partir de ce dernier à l'aide d'un appel à la fonction createInstance. On obtient alors un tableau que l'on initialise avec la fonction initialize, prenant en paramètres le nombre de lignes et de colonnes.
Cela donne le code suivant, que j'ai déporté dans une fonction :
function CreateTable(document As Object, columns, rows) as Object
dim table As Object
table = document.createInstance("com.sun.star.text.TextTable")
table.initialize(rows, columns)
CreateTable = table
end function

C'est donc un code relativement simple...

b) Insertion de la table dans le document
La manipulation d'un bloc texte se fait à travers un concept similaire à ce que voit l'utilisateur : le curseur.
Ainsi, toute manipulation, qu'il s'agisse d'une insertion, suppression ou modification de texte, passe à travers le même objet. Nous allons donc, en toute logique, y trouver la méthode pour insérer un objet TextTable.
Sub InsertTable (document As Object, table As Object)
dim docText as Object
dim cursor as object
docText = document.Text
cursor = docText.createTextCursor
cursor.gotoNextParagraph(False)
docText.insertTextContent(cursor, table, False)
End Sub

Encore une fois, cette API est documentée, mais l'éditeur de macros ne vous sera d'aucun secours...

4) Continuer le code
Nous pouvons maintenant mélanger ces bouts de code et préparer la suite : copier les cellules et leur mise en forme.
Voici à quoi ressemble maintenant la nouvelle version du bloc principal de la méthode CopyToWriter :
if sel.supportsService("com.sun.star.table.CellRange") then
textDocument = StarDesktop.LoadComponentFromURL("private:factory/swriter", "_blank", 0, propertiesDoc)
textTable = CreateTable(textDocument, sel.RangeAddress.EndColumn - sel.RangeAddress.StartColumn + 1, sel.RangeAddress.EndRow - sel.RangeAddress.StartRow + 1)
textTable.HeaderRowCount = 0
InsertTable(textDocument, textTable)
for x = 0 to sel.RangeAddress.EndRow - sel.RangeAddress.StartRow
for y = 0 to sel.RangeAddress.EndColumn - sel.RangeAddress.StartColumn
textCell = textTable.getCellByPosition(y, x)
calcCell = sel.getCellByPosition(y, x)
copyCell(calcCell, textCell)
next y
next x
else
MsgBox "Select a range of cells"
end if

Nous créons et insérons le tableau dans le nouveau document texte, puis nous parcourons chaque ligne et chaque colonne : à chaque fois, on obtient deux objets correspondant aux cellules Calc et Writer, et nous appelons sur ces cellules une mystérieuse fonction copyCell, que je m'empresse de vous définir.

5) La vicieuse fonction copyCell
J'ai passé beaucoup de temps dans XRay pour trouver une méthode simple et efficace pour réaliser cette copie. J'ai découvert des horreurs dans l'API d'OpenOffice.org (un service qui me semblait être parfaitement adapté pour mon besoin, mais implémenté par personne à part par une classe obscure de 2008 documentée en allemand (!)). J'ai passé énormément de temps, mais j'ai trouvé une solution relativement maintenable, que voici...
- Itérer sur les paragraphes de la cellule de départ
- Itérer sur les portions dans chaque paragraphe
- Itérer sur les propriétés de cette portion
- Si cette propriété est valide sur la cellule de destination (si elle existe), alors l'appliquer.
Pour bien comprendre l'idée ici, il est nécessaire de savoir comment marche la représentation du texte dans la plupart des solutions que je connaisse (HTML, OpenDocument, OpenOffice.org, KOffice…)
Pour simplifier, je vais «parler» en HTML/CSS. Un paragraphe, balise

, est constitué d'un flot de texte uniforme, par défaut d'un seul style. Pour insérer un élément d'un style différent, on a recours à une balise . Au sein d'une même balise span, tout le texte aura le même style. Dans le cas du traitement de textes, les balises span ne peuvent être imbriquées. On a donc des «portions» de texte, mono-style, sur lesquelles toutes les propriétés sont à la même valeur.
Exemple : test est stocké en quatre portions de texte : t, puis e souligné, puis souligné en italique et enfin t en souligné.
À titre purement informatif, voici le code correspondant à ce mécanisme :
sourceCursor = source.createTextCursor
sourceText = sourceCursor.Text
listeParag = sourceText.createEnumeration
Do While listeParag.hasMoreElements
parag = listeParag.nextElement
listeParts = parag.createEnumeration
Do While listeParts.hasMoreElements
part = listeParts.nextElement
sourceCursor.gotoRange(part.End, False)
propsetsource() = sourceCursor.propertySetInfo.properties
propsetdest() = destCursor.propertySetInfo.properties
For i = 0 To UBound(propsetsource)
propertyName = propsetsource(i).Name
For j = 0 To UBound(propsetdest)
if propsetdest(j).Name = propertyName Then
destCursor.setPropertyValue(propertyName, sourceCursor.getPropertyValue(propertyName))
Goto NextProperty
End If
Next j
NextProperty:
Next i
destCursor.String = part.String
destCursor.gotoEnd(False)
Loop
Loop


6) Conclusion
J'avoue, je craque, et mon canal carpien n'aidant pas, j'ai un peu «bâclé» l'explication de l'API d'OpenOffice.org.
L'API en elle même n'est pas forcément compliquée, mais il y a souvent des problèmes pour trouver ce que l'on cherche. La documentation est assez complète, mais sans un bon outil pour écrire les macros, c'est une tâche réellement fastidieuse.
XRay (http://wiki.services.openoffice.org/wiki/Extensions_developm(...) est là pour vous aider un peu : on lance cet outil en lui donnant un objet, et il est capable d'en lister les propriétés et de les explorer de manière avancée... Mais cela serait tellement plus simple si c'était intégré dans l'IDE...
Je m'en vais terminer ma macro et en faire un addon OpenOffice.org complet, installable aisément. Je pense que cette manipulation justifiera un journal à elle seule, ne serait-ce que pour annoncer l'addon.

À venir dans de prochains épisodes :
- trackball ou souris verticale, comment lutter contre le syndrome du canal carpien ?
- réalisation d'addons OpenOffice.org en Java (avec des fonctionnalités inédites dans une suite bureautique, que je tenterai d'intégrer ensuite à KOffice)

À la prochaine fois pour d'autres aventures donc, et en attendant, n'oubliez pas : le libre, sans développeurs du dimanche (ok, on est mardi, et alors ?), ne peut exister...

  • # 11e

    Posté par  . Évalué à 1.

    Ceci est le 11e journal posté aujourd'hui. À quoi est due cette hausse soudaine d'activité ?
    • [^] # Re: 11e

      Posté par  . Évalué à 1.

      c'est la rentrée ?
    • [^] # Re: 11e

      Posté par  . Évalué à 9.

      C'est pour que ton journal qui dénonce grave passe en deuxième page.
  • # Comparaison

    Posté par  . Évalué à 4.

    Vu que tu sembles avoir l'experience des 2 suites office pour ce qui est des macros, si tu devais comparer ca donnerait quoi ? (J'en ai aucune idee personnellement, je sais meme pas comment faire une macro avec Word...)
    • [^] # Re: Comparaison

      Posté par  . Évalué à 7.

      Il faut bien délimiter la comparaison : que cherche-t-on à faire ?
      Automatiser une tâche simple, c'est-à-dire vraiment ce qu'est une macro, ou faire une vraie extension au logiciel, ce qui ressemble plus à un "plugin" ?
      Dans le cas vraiment de la macro, OpenOffice.org a une API mieux documentée, mais les outils de création de macro sont largement en dessous des outils de Microsoft Office (en tout cas de Word et d'Excel) : ils ne t'aident pas à rédiger ton code, ils ne t'aident pas à trouver à ta place les bons appels dans l'API...
      Par contre, pour réaliser une extension à OpenOffice.org, cela me semble plus simple et surtout bien moins limité. Je n'ai pas d'exemple à fournir pour l'instant, mais dans un futur journal je présenterai une extension OpenOffice.org en Java dont la réalisation sous Microsoft Office me paraît largement plus délicate.
  • # j'ai pas bien compris

    Posté par  . Évalué à 2.

    tu m'excuseras, mais j'ai pas bien compris l'interet de la macro, sinon la curiosité scientifique...

    perso, je copie/colle un tableau openoffice, dans writer, c'et effectivement loin d'etre parfait, mais ...

    à part pour avoir le resultat d'un tableau dans le document texte, pourquoi voudrais-je utiliser un tableau dans le traitement de texte ?

    pour presenter un tableau dans un rapport ? mouais, y a surement moins de faire des jolis rapports en utilisant correctement les outils /polices/mise en page ?

    et puis rien n'empeche d'imprimer les 2 tableaux à part, et de les inserer dans le "bouquins" papier que tu rends à la fin.

    Ou alors il se fait tard, et je ne vois pas l'interet dans mon cerveau embué
    • [^] # Re: j'ai pas bien compris

      Posté par  . Évalué à 3.

      Quand tu veux faire un tableau "simple" mais avec des formules, tu vas le faire dans le tableur. Puis lorsque tu voudras insérer ce petit tableau dans ton texte, tu n'auras pas le choix : il faudra soit le transformer en image, soit insérer un objet tableur qui ne s'intègre pas au flot de texte... Cela ne te permet plus de mettre en page convenablement les tableaux, comme le reste du texte.
      • [^] # Re: j'ai pas bien compris

        Posté par  . Évalué à 1.

        Faux ! Tu peux aussi te servir des metafichiers GDI, il te permettent de garder la mise en forme de l'origine vers la destination, et peuvent te permettre de redimensionner simplement. Il est vrai que pour les tableaux, ce n'est jamais évidents de les avoir propres depuis calc, donc c'est une bonne nouvelle d'avoir une macro pour
        • [^] # Re: j'ai pas bien compris

          Posté par  . Évalué à 3.

          Ce qui correspond à la transformation en image du tableau...
          • [^] # Re: j'ai pas bien compris

            Posté par  . Évalué à 1.

            Pas tout à fait vrai, une image certes mais vectorielle !

            Elle est de plus "explosible" pour en récupérer les infos, donc c'est moins binaire que tu m'entends ...
  • # Vous devez entrer un sujet dans la boîboîte.

    Posté par  (site web personnel) . Évalué à 4.

    >> - trackball ou souris verticale, comment lutter contre le syndrome du canal carpien ?

    "Pen tablet" (une petite intuos A5 fait parfaitement l'affaire)
  • # le basic c'est obligatoire?

    Posté par  . Évalué à 1.

    Il me semblait que OOo avait la possibilite de faire des macros/addons en python et bon

    http://wiki.services.openoffice.org/wiki/Python
    • [^] # Re: le basic c'est obligatoire?

      Posté par  . Évalué à 3.

      Et pour se faciliter la tâche, il y a http://www.multiracio.com/eoec/
    • [^] # Re: le basic c'est obligatoire?

      Posté par  . Évalué à 5.

      Je m'auto-cite : «OpenOffice.org est programmable en plusieurs langages de programmation, mais je ne me focaliserai ici que sur le StarBasic, simple et surtout ne dépendant pas d'un interpréteur externe...»
      • [^] # Re: le basic c'est obligatoire?

        Posté par  . Évalué à 2.

        Python ne nécessite pas non plus d'interpréteur externe : OOo embarque déjà un interpréteur Python.
        • [^] # Re: le basic c'est obligatoire?

          Posté par  . Évalué à 2.

          Pas forcément, tu peux choisir de ne pas l'installer il me semble... (paquet python-uno sous debian)
          • [^] # Re: le basic c'est obligatoire?

            Posté par  (site web personnel) . Évalué à 3.

            Salut,

            de ce que j'ai compris, sous nos bonnes distributions GNU-Linux, le paquet python-uno permet à OOo de manipuler le Python installé, mais aussi de manipuler OOo depuis un programme Python.
            Pas de problème puisque la distribution gère le fait que la version Python est la même.

            Le Python embarqué dans OOo, c'est pour Windows, car il y est impossible de savoir si l'éventuelle version de Python installée sera la même que celle utilisée par OOo.

            C'est le problème auquel je me suis trouvé confronté récemment.
            Je développe un programme Python+PyQt qui a besoin de manipuler des fichiers OOo.
            Sous Linux, aucun problème avec le paquet python-uno.
            Sous Windows, le python embarqué n'intègre évidemment pas PyQt, ce qui fait que je ne peux pas lancer mon logiciel en passant par icelui.
            Après pas mal d'essais-erreurs, j'ai trouvé comment me raccrocher à uno depuis le Python installé, et ça fonctionne.

            Reste à savoir ce qui se passe si un utilisateur a un OOo et un Python de versions différentes.

            Si ça intéresse, je peux indiquer la démarche que j'ai suivie.

            O-

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.