Premier script en python, un exemple expliqué de A à Z

De Wiki-Cadlink
Sauter à la navigation Sauter à la recherche

Cet article fait suite à l'Introduction à l'utilisation de la connexion Archicad-Python.

Ce premier script est directement récupéré du tutoriel de Tibor Lorántfy, un ancien développeur de chez graphisoft :

https://archicadapi.graphisoft.com/getting-started-with-archicad-python-connection

Ce premier script va permettre d'afficher dans la console d'archicad le nombre de murs présents dans le projet.

Je vais vous présenter dans un premier temps une version "brute" de ce script, ensuite la version optimale telle que proposée par Tibor, et enfin proposer une interface graphique (très light) avec Tkinter.

Commencons!

La version "Brute"

Cette version est proposée pour mieux expliquer la méthodologie, mais elle n'est pas du tout recommandée. C'est le script écrit avec le minimum de ligne possible.

Ça se fait... en 3 lignes !

from archicad import ACConnection
walls = ACConnection.connect().commands.GetElementsByType('Wall')
print('Nombre de murs: ' + str(len(walls)))

Analyse de la 1ere Ligne

from archicad import ACConnection

De manière générale, un script python ne charge pas toutes les fonctionnalité permises par python, et il faut au début du script indiquer les modules supplémentaires que l'on souhaites intégrer.

Il peut s'agir d'un module permettant de travailler avec des fichiers excel (par exemple openpyxl) :

from openpyxl import Workbook

Il peut aussi s'agir d'un module permettant de créer une interface graphique (par exemple Tkinter) :

from tkinter import *

Il peut s'agir de modules permettant d'utiliser des images, créer de l'aléatoire, etc, etc.

Ces modules sont parfois déjà intégré à l'installation de base de python et ne nécessitent pas d'installation supplémentaires, mais doivent tout de même être appelés (c'est le cas de l'interface graphique Tkinter), et parfois, il faut avoir préalablement installé ce module supplémentaire.

Dans notre cas, cette première ligne est absolument nécessaire à tout les script python pour archicad: Il indique à python de charger le module python/archicad installé sur l'ordinateur ce qui permettra d'accéder à toutes les commandes dédiées à archicad.

Analyse de la 2eme Ligne

walls = ACConnection.connect().commands.GetElementsByType('Wall')

Dans cette deuxième ligne, on créé une variable nommée "walls" et on utilise la commande permettant de récupérer tout les éléments créé avec l'outil mur. J'ai dessiné dans mon fichier archicad 4 murs sur l'étage RDC.

La commande pour Récupérer les éléments selon leur type

Pour récupérer l'ensemble des murs du projet on va utiliser la commande GetElementsByType(), ce qui donne en français "Récupérer les éléments selon leur type".

Pour avoir des infos sur cette fonction on peut soit aller sur le site python pour archicad: https://archicadapi.graphisoft.com/archicadPythonPackage/archicad.releases.ac24.html#archicad.releases.ac24.b2310commands.Commands.GetElementsByType

soit aller dans Visual Studio code:

Python11.png


Dans Visual Studio Code, on a donc:

  1. le nom de la commande (1),
  2. ce qu'elle fait (2)
  3. le type de fonction (3) : une commande,
  4. le type d'argument qu'il faudra mettre entre les () et sous quelle forme (4): ici une chaîne indiquant le type de l’élément. Il faudra donc écrire en anglais le type entre deux guillemets " "
  5. le résultat qui nous sera renvoyé (5): une liste de type "ElementIdArrayItem"

Comment lancer la commande ?

Si on suit la partie précédente on doit donc écrire:

GetElementsByType("Wall")

Pour lancer cette fonction, il faut toutefois rajouter quelques informations:

Python12.png


On indique à python qu'on veut utiliser le module python pour archicad (1), puis qu'on veut se connecter à archicad (2), qu'on veut utiliser une commande (3) et enfin quelle commande on veut utiliser (4).

Le résultat obtenu avec la 2eme ligne

On a créé une variable walls qui récupère le résultat de la commande GetElementsByType()

SI on utilisait la fonction "print" pour voir ce que récupère la variable "walls", cela donnerait ça:

[ElementIdArrayItem {'elementId': {'guid': '86E4F938-8783-4E1C-881E-91C60E902526'}}, ElementIdArrayItem {'elementId': {'guid': 'E794D806-E94C-49D1-9854-A238F49D4AE3'}}, ElementIdArrayItem {'elementId': {'guid': 'B848DBB3-E432-4AEE-8AD1-A8FB0F559BBE'}}, ElementIdArrayItem {'elementId': {'guid': 'FF09403B-BA6D-43D3-B936-6C44AD229F85'}}]

C'est "presque" du JSON. SI on "nettoie" un peu cette réponse pour la transformer en JSON on obtient ça:

{
        "elements": [
            {
                "elementId": {
                    "guid": "86E4F938-8783-4E1C-881E-91C60E902526"
                }
            },
            {
                "elementId": {
                    "guid": "E794D806-E94C-49D1-9854-A238F49D4AE3"
                }
            },
            {
                "elementId": {
                    "guid": "B848DBB3-E432-4AEE-8AD1-A8FB0F559BBE"
                }
            },
            {
                "elementId": {
                    "guid": "FF09403B-BA6D-43D3-B936-6C44AD229F85"
                }
            }
        ]
    }

Schématiquement cela donne çà:

Python13.png


C'est à dire qu'on a une liste de 4 valeurs (nos 4 murs), allant de l'index 0 à l'index 3 (ce qui corresponds aux 4 murs: mur index 0, mur index 1, mur index 2, mur index 3 )

Cette notion d'index (en 1) sur le schéma n'est pas explicitement indiquée dans le format JSON, c'est une attribution automatique: la première valeur se voit attribuée l'index 0, la seconde l'index 1, etc... Comme une liste python.

A chacun de ces éléments est associé un GUID: c'est un identifiant unique généré par le logiciel qui permet de retrouver cet élément. Cet identifiant reste constant et ne changera pas.

On obtient donc une liste de 4 identifiants uniques (GUID) correspondant aux 4 murs du projet.

Analyse de la 3eme Ligne

print('Nombre de murs: ' + str(len(walls)))

Cette troisième ligne est la plus simple, il s'agit de pur python, et du très basique.

La fonction len() qui permet de compter le nombre d'éléments

Si on utilise len sur la variable walls, cela s'écrira:

len(walls)

et donnera comme résultat le nombre d'éléments dans la liste, soit:

4

La fonction print() et une concaténation

On utilise la fonction print() et on fait une concaténation entre le texte Nombre de Murs: et le nombre de mur (en utilisant la fonction len() donc)

Pour concaténer les valeurs, il faut qu'elles soient de même type. Le nombre obtenu par la fonction len() étant de type entier, nous utiliserons la fonction str() pour la transformer en chaine (string en anglais).

Cela donne:

str(len(walls))

On concatène en utilisant le symbole + entre les deux parties du même type, soit:

'Nombre de murs: ' + str(len(walls)))

Et on entre cette concaténation dans la fonction print().

Voila le résultat:

Python14.jpg


La "bonne méthode"

Le script tel que présenté par le développeur de Python pour archicad:

from archicad import ACConnection

conn = ACConnection.connect()
assert conn

acc = conn.commands
act = conn.types
acu = conn.utilities

walls = acc.GetElementsByType('Wall')

print(f'Number of Walls: {len(walls)}')

En comparaison de la méthode Brute que je vous ai présentée juste avant:

from archicad import ACConnection
walls = ACConnection.connect().commands.GetElementsByType('Wall')
print('Nombre de murs: ' + str(len(walls)))

C'est plus long dans la manière officielle, mais c'est pour le bien du script!

La partie à conserver pour tout script

De manière générale, tout vos script python pour archicad doivent comporter ces premières lignes:

from archicad import ACConnection

conn = ACConnection.connect()
assert conn

acc = conn.commands
act = conn.types
acu = conn.utilities

Pourquoi? Tout simplement pour éviter d'écrire des commandes à rallonge, comme vu précédemment. Cela permet d'utiliser acc plutôt que ACConnection.connect().commands

Python15.jpeg


Cela permet pour l'ensemble du script d'utiliser seulement acc.NomDeLaCommande, acu.NomDeLaFonctionUlitaire ou act.NomDuType.

L'instruction "assert" est une aide de code: si la connexion avec archicad ne se fait pas, le script donnera une erreur de type "AssertionError" ce qui permettra de comprendre d'où vient l'erreur plus facilement.

Pour la partie du texte, la concaténation n'est pas recommandée en python, il est recommandé d’utiliser la méthode dites du "f-Strings" Elle commence par un f suivi du texte entre deux guillemets ('). Le texte fixe est écrit tel quel et les variables sont écrites entre accolades ({}).

Ajouter des fonctions à notre script

Voici quelques exemples d'améliorations de script possibles.

Un message pop-up pour afficher le résultat

Python16.jpeg


Voici le code correspondant:

from archicad import ACConnection
from tkinter import *
from tkinter import messagebox

conn = ACConnection.connect()
assert conn

acc = conn.commands
act = conn.types
acu = conn.utilities

walls = acc.GetElementsByType('Wall')

messagebox.showinfo("Informations",f'Nombre de murs: {len(walls)}')

Quels sont les changements?

Importer un nouveau module

On importe le module tkinter au début, ainsi que message box. Cela s'ecrit:

from tkinter import *
from tkinter import messagebox
Utiliser messagebox

Pour afficher un message pop up, on va utiliser la fonction messagebox précédemment importée.et sa fonction showinfo (d'autres type de fonction existent: https://docs.python.org/3/library/tkinter.messagebox.html).

Cette fonction demande au minimum 1 information; le texte qui sera indiqué dans le titre du Pop-Up. Le second texte sera affiché dans le corps du texte. Ces deux textes doivent être séparés par une virgule.

Cela donne dans notre cas:

messagebox.showinfo("Informations",f'Nombre de murs: {len(walls)}')

Copier le résultat dans le presse-papier

Dans cette version, le résultat est affiché dans la console et copié dans le presse papier. Vous n'avez qu'à faire cmd+v ou ctrl+v pour coller le résultat où vous voulez.

Pour trouver cette partie de code, j'ai du faire quelques recherches google pour trouver une méthodologie ne nécessitant pas l'installation d'un module complémentaire (Pyperclip) sur mon ordi. Il semblerait que ce soit très lourd d'utiliser tkinter pour cela, mais ça marche!

from archicad import ACConnection
from tkinter import *

conn = ACConnection.connect()
assert conn

acc = conn.commands
act = conn.types
acu = conn.utilities

walls = acc.GetElementsByType('Wall')
text = f'Nombre de murs: {len(walls)}'

print(text)

r = Tk()
r.clipboard_clear()
r.clipboard_append(text)
r.after(300, r.destroy)
r.mainloop()