14 déc. 2020

[Dynamo += Python] le Lacing...en Python

 




Il peut être intéressant de retrouver le système de lacing des nœuds Dynamo dans un script Python.

4 déc. 2020

[Dynamo += Python] Un peu de décoration

Credit RevitCity pour le Sapin
Une fonction du moment décorée 




À l'approche des fêtes de fin d'année les décorations sont de sorties, un moment opportun pour voir ou revoir les décorateurs Python.

8 nov. 2020

[Dynamo += Python] Ouvrir plusieurs Vues





Une petite astuce pour ouvrir plusieurs vues avec Dynamo.

19 oct. 2020

[Dynamo += Python] Et si on parlait de Revit? (MAJ/Update)

 


Oups !, il y a une petite erreur dans le Titre....

29 sept. 2020

[Dynamo += Python] Conversion de Coordonnées

 




Vous avez des coordonnées, vous voulez les convertir vers un autre système de coordonnées, voici quelques exemples...

30 août 2020

[Dynamo+=Python]Rechercher et Filtrer des Éléments (FilteredElementCollector)







La Classe FilteredElementCollector permet de rechercher une collection d'éléments à l'aide de méthodes et/ou de filtres. 
Voici un récapitulatif et quelques exemples sur son utilisation.

9 août 2020

[Dynamo += Python ] Gestion des sous-projets des maquettes liées

 



Comment gérer les états des sous projets en Python avec l'API Revit ?

28 juil. 2020

[Dynamo += Python] Ajouter ou Modifier un filtre dans une nomenclature




Travailler avec les filtres de nomenclature avec l'API Revit peut s'apparenter à première vue un peu à jeu de piste.

15 juil. 2020

[Dynamo += Python] Moduler vos noeuds Python






La composition d'un design Script est-elle forcément constituée de nœuds consécutifs et de façon séquentielle ? Voici une alternative...

27 juin 2020

[Revit & Sélections] Les fonctions natives





Les Sélections dans Revit.

9 juin 2020

[Dynamo+=Python] Parser vos rapports d'avertissement (HTML)




Retrouver les éléments d'un rapport d'avertissement avec l'assembly mshtml

29 mai 2020

[Revit] L'éclairement dans Revit


PxHere


Un tour d'horizon sur Revit et sa gestion de l'éclairement 

16 mai 2020

[Dynamo+=Python] Géométries...Groupir !!!




 Comment regrouper des éléments linéaires ?

8 mai 2020

[Dynamo+=Python] Changer de Type une Occurrence de Famille



Un petit aperçu et comparatif entre 2 méthodes pour changer de Type une Occurrence de Famille.

6 mai 2020

Dialux Evo 9 (MAJ)




Dialux Evo 9.0 vient de récemment de sortir (avril 2020) voici les nouveautés :


1 mai 2020

[Dynamo+=Python] Exemple UI (SharpDevellop): Check Paramètres (ParameterBindings)




Pour utiliser/créer une interface utilisateur  depuis Dynamo il y a quelques solutions :

19 avr. 2020

[Dynamo+=Python] Debogage - Printez vos variables




Lorsqu'on développe en Python et qu'on arrive la première fois sous Dynamo, on peut être surpris par l'absence de console quand un on écrit dans un nœud Python.
À ce jour, seule la variable OUT permet d'afficher des données

16 avr. 2020

[Revit] Est tu un Revitien ?


Un peu d'humour par les temps qui courent 😀


11 avr. 2020

[Revit] De l'Elec dans Revit 2021 !





Revit 2021 vient de sortir, voici quelques nouveautés intéressantes autour du domaine de l'électricité et plus.

De l'ingénierie électrique au menu dans cette version !

5 avr. 2020

[Revit+Plugin SGP] Export DWG et paramétrage des calques






Lors d'un export DWG,  Revit affecte un calque pour chaque Catégorie.
Il est possible de d'ajouter/modifier les noms des calques en passant le modificateur de calque, par exemple en ajoutant le nom du sous projet à une Catégorie, mais...

26 mars 2020

[Dynamo+=Python] Retrouver l'élément hôte (Maquette liée)

Par défaut lorsqu'une instance de famille est hébergée sur une face d'une maquette liée, Revit n'affiche que la maquette hôte dans les propriétés.

21 mars 2020

[Revit] AC Consuel Verte + Revit = Quantitatif Facile


Parmi les travaux fastidieux le quantitatif le nombre de circuits 1.5² et 2.5² sur les Attestations Consuel Vertes.
Un ptit filtre sous Revit et c'est terminé en moins de 2 min...

14 mars 2020

[Python] des Infos sur les fichiers Revit sans Revit...




Connaître certaines informations comme la version d'un fichier Revit avant de l'ouvrir peut s'avérer particulièrement utile (Rétrocompatibilité)

#DynamoWin - Création de murs à partir de DWG

Et si un projet réalisé au format DWG n'était plus un obstacle pour commencer l'étude d'exécution sous Revit?
Et si la modélisation du bâtiment sous DialuxEvo (ou de pièces dans Dialux) était généré plus rapidement ? via import de fichier IFC ou STF

5 mars 2020

[Dynamo+=Python] Les Itérables et le Dynamo Wrappeur


  • Unwrapping vs Wrapping

Un petit rappel sur la fonction UnwrapElement()

Les éléments dans Dynamo sont exposés par défaut à l'API de Dynamo qui enveloppe l'API de Revit (d'où la notion de wrapp et de wrapped Elements)

15 févr. 2020

[Dynamo+=Python] Transfert de Légendes



L'outil d'insertion  de vue à partir de fichier de Revit ne permet pas à ce jour de copier des Légendes, mais...

11 févr. 2020

[Dynamo] Les modes d’exécution, la spécificité du mode Périodique




Sous Dynamo il existe 3 modes d'exécution :

Automatique : par défaut, les nouvelles définitions Dynamo sont définies pour s'exécuter automatiquement. Cela signifie que le graphique est exécuté par l'ordinateur, chaque fois qu'il y a un changement dans le modèle.


Manuel : le graphique Dynamo ne s'exécutera que lorsque vous appuyez sur ce bouton, ce qui signifie que vous pouvez apporter des modifications sans demander à l'ordinateur de calculer le résultat jusqu'à ce que vous soyez prêt.
C'est celui-ci qui est activé par défaut avec Dynamo Player
Remarque :
Lorsque vous recevez un script que vous (ou votre société) n'avez pas développé il est préférable de l'exécuter dans ce mode à l'ouverture.





Périodique : Dynamo a la possibilité d'exécuter le graphique de manière rythmique, toutes les X millisecondes, avec une fréquence que l'on peut définir. Cette option n'est activée que lorsqu'un nœud du canevas appelle spécifiquement ce type de comportement d'exécution.
Il s'agit du nœud standard (OOTB) DateTime.Now




Les nœuds activés périodiquement sont ceux qui pourraient injecter des informations en constante évolution dans le graphique Dynamo, telles que les données d'un capteur ou d'un microcontrôleur (comme un LeapMotion ou un Raspberry Pi couplé à un capteur) ou encore une GTB.

Sans données d'entrée dynamique, ce mode d'exécution peut également servir pour animer/déplacer des objets dans la maquette ou encore acquérir des données de la maquette toutes les X minutes afin d'auditer cette dernière (par exemple en BIM niveau 3).

Voici un exemple de déplacement périodique d'un objet le long d'une Ligne de Modèle.
Note :
Ici les localisations des instances à T-1 sont sauvegardés (via un index) localement dans un fichier txt (peut être également fait via le module Python Pickle)





import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

import sys
sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')
import os

def readidxfile(file):
    with open(file, "r") as f:
        return int(f.read())

def setidxfile(file, idx):
    with open(file, "w") as f:
        return f.write(str(idx))
        
        
dataEnteringNode = IN
time = IN[0] #input DateTime.Now() node
modelcurve = UnwrapElement(IN[1])
instance = UnwrapElement(IN[2])

pathuser  = os.path.expanduser('~')
fullpath = pathuser + "\\Documents\\objb.txt"

try:
    oldidx = readidxfile(fullpath)
    indx = oldidx + 1
    setidxfile(fullpath, indx)
except: 
    indx = 0
    setidxfile(fullpath, str(indx))
#create range
rangeint = range(1,11,1)
#create simple float range
floatrange = [x * 0.1 for x in rangeint]
curveproto  = modelcurve.GeometryCurve.ToProtoType()
try:
    ptx = curveproto.PointAtParameter(floatrange[indx])
except:
    indx = 0
    setidxfile(fullpath, indx)
    ptx = curveproto.PointAtParameter(floatrange[indx])
TransactionManager.Instance.EnsureInTransaction(doc)
instance.Location.Point = ptx.ToXyz()
TransactionManager.Instance.TransactionTaskDone()
    

OUT = ptx

github


6 févr. 2020

[Revit] Familles et formules paramétrique 2/2




  • Les formules de recherche dans les tables de consultation 

Les tables de consultation contiennent des données sur les types,  Revit propose une fonction size_lookup qui lit les valeurs nécessaires dans un fichier CSV qui est importé dans la famille.

29 janv. 2020

[Revit] Familles et formules paramétrique 1/2




    1. Échapper un caractère spécial contenu dans un nom de paramètre

      
Il se peut qu'un caractère spécial soit inclut dans le nom d'un paramètre dans ce cas il faut utiliser les crochets [ ]
 Exemple : 

    • Paramètre oui-non : Alim Saillie/Encastrée
    • exemple formule associée : if([Alim Saillie/Encastrée], 0 m, 0.035 m)
Note :
Si vous avez pour habitude de "piloter" vos paramètres par programmation,  dans la mesure du possible éviter les caractères spéciaux, idem pour les caractères accentués (penser à l'encoding)


    2. Les Maths

Concernant l'utilisation de fonctions mathématiques elles sont similaires à  ceux que nous pouvons rencontrer en programmation




Syntaxe de fonctionDescriptionExemples
+Addition, additionnez des valeursLongueur totale = Hauteur + Largeur
-Soustraction, recherchez la différence entre des valeursVolume supprimé = Volume A - Volume B
*MultiplicationAire = Hauteur * Largeur
/DivisionMi-longueur = Longueur / 2
^Exponentiation, X élevé à la puissance YHauteur ^ 2
logLogarithme, l'exposant de la puissance à laquelle un nombre de base doit être élevé pour qu'il soit égal à un nombre donné.2 = log10 100
In(x)Logarithme népérien, le logarithme d'un nombre sur la base de la constante mathématique e.ln(x*y) = ln*x + ln*y
sqrt(x)Racine carrée4 = sqrt(16)
sin(x)SinusAvec des valeurs c et A connues, a = c * sin(A)
cos(x)CosinusAvec des valeurs c et A connues, b = c * cos(A)
tan(x)TangenteAvec des valeurs a et B connues, b = a * tan(B)
asin(x)Arc sinusAvec des valeurs a et c connues, A = asin(a/c)
acos(x)Arc cosinusAvec des valeurs a et c connues, B = acos(a/c)
atan(x)Arc tangenteAvec des valeurs a et b connues, A = atan(a/b)
exp(x)Constante mathématique e élevée à la puissance x.exp(3)
abs(x)Valeur absolue2 = abs(-2)
piRapport de la circonférence d'un cercle à son diamètreAire du cercle = pi * r^2
round(x)La fonction d'arrondi renvoie une valeur arrondie à l'entier le plus proche. Elle ne prend pas en compte la direction de l'arrondi.
round(3.1) = 3
round(3.5) = 4
round(-3.7) = -4
roundup(x)La fonction d'arrondi par excès renvoie une valeur arrondie à la valeur entière supérieure ou égale à x.
roundup(3) = 3
roundup(3.1) = 4
roundup(-3.7) = -3
rounddown(x)La fonction d'arrondi par défaut renvoie une valeur arrondie à la valeur entière inférieure ou égale à x.
rounddown(3) = 3
rounddown(3.7) = 3
rounddown(-3.7) = -4


     3. Les Opérateurs et les formules conditionnelles

La structure est similaire à celle que nous pouvons utiliser sous Excel.
  • Règle n°1
Le type de paramètre conditionne la formule. Inutile d'essayer de retourner un booléen à un paramètre qui attend une longueur 
  • Règle n°2
Les unités doivent être compatibles entre elles.
Cependant même si elles sont compatibles, attention tout de même à ne pas additionner n'importe quoi ...
Le scandale du théorème de Boucherot 
  • Règle n°3
À l'instar d'un appel de fonction suivi d'arguments, les opérateurs se mettent en préfixe suivis de parenthèses englobant la condition et/ou les valeurs.
  • Règle n°4
À ce jour Revit ne permet pas d’évaluer les valeurs des paramètres de type Texte, si vous y tenez, passez par une table de consultation 

Quand Revit nous envoi booler...



Pour les tests logiques les opérateurs supportés sont:
Opérateur Logique   Description
<  strictement inférieur
>strictement supérieur
=  égal
andretourne Vrai si toutes les valeurs sont Vrai 
orretourne Vrai si une des valeurs est Vrai
notretourne l'inverse de la valeur booléenne (ou du résultat de l'évaluation booléenne)
not(a > b) équivalent à : a <= b
not(a < b)équivalent à : a >= b


Utilisation de l'Opérateur AND
Exemple:
x = 5
y = 4
and(x = 5, y = 5) retourne Faux (False)


Utilisation de l'Opérateur OR
Exemple :
x = 5
y = 4
or(x = 5, y = 5) retourne Vrai (True)


Utilisation de l'Opérateur NOT
Exemple :
x = 5
not(x = 6) retourne Vrai (True), x = 6 étant Faux


      4. Les Syntaxes des Formules 

  • La syntaxe de base est la suivante :
 if (test logique, valeurA si vrai, valeurB si fausse)retourne valeurA ou valeurB
   
  • Test logique avec AND
 if (and(LongA = 10, LongB = 10), valeurA, valeurB)- retourne valeurA ou valeurB
 - Paramètre de Type Longueur

L'équivalent Pseudo-Code à titre de comparaison

   
SI LongA = 10 ET LongB = 10:
    retourne valeurA
SINON:
    retourne valeurB

  • Test logique avec OR
 if (or(LongA = 10, LongB = 10), valeurA , valeurB )- retourne valeurA ou valeurB
 - Paramètre de Type Longueur

Pseudo-Code
   
SI LongA = 10 OU LongB = 10:
    retourne valeurA
SINON:
    retourne valeurB

  • Test logique avec multiple conditions
La syntaxe est la suivante : 
 if (test logique1, valeurA si vrai, (test logique2 si faux, valeurB si vrai, valeurC si vrai))retourne valeurA ou valeurB ou valeurC

Prenons pour l'exemple 3 paramètres de longueur, nous cherchons à retourner la valeur max dans un nouveau paramètre "MaxLong:
LongA = 50
LongB = 40
LongC = 30

Pour simplifier la compréhension commençons par l’équivalent avec un Pseudo-Code

Pseudo-Code
  
SI LongA > LongB and LongA > LongC:
     MaxLong = LongA  
SINON SI LongB > LongA and LongB > LongC: 
    MaxLong = LongB
SINON: 
    MaxLong = LongC   


 l'expression sous Revit devient la suivante :

 if (and(LongA > LongB, LongA > LongC), LongA , if(and(LongB > LongA, LongB > LongC), LongB, LongC ))- retourne LongA ou LongB ou
LongC
 - Paramètre de Type Longueur

On peut comme cela imbriquer autant de conditions que nécessaire...dans la limite du raisonnable

Moralité (ou conseil): si vous n'êtes pas à l'aise avec les formules Revit commencer par écrire la formule sous forme Pythonesque ou Pseudo-Code ou autre



À suivre les formules de recherche dans les tables de consultation...


10 janv. 2020

[Revit] Interopérabilité Excel







Analyses, modifications,  import de données, transfert vers une application tierce, transmissions de quantitatifs, etc..., dans l'écosystème du BIM, l'utilisation d'Excel devient incontournable.

Le plus souvent la nécessité d'exporter vers Excel provient des Nomenclatures, plusieurs méthodes pour y parvenir.

  • Avec l'export natif de Revit 

Après export de rapport de nomenclatures au format texte avec choix du délimiteur, il suffit de d'importer le fichier txt en tant que données depuis Excel ou de changer directement l'extension "txt" en "csv" puis de l'ouvrir avec Excel.


  • Avec des Add-Ins
Ce n'est pas le choix qui manque, exemple sur l'AutodeskAppStore

voici mes préférés (gratuit) :
    • Import/Export Excel de BIM One (export - import de nomenclatures, modification possible des paramètres d’Occurrence)
    •  SheetLink de Diroots  (export - import de nomenclatures et bien plus, modification possible des paramètres d’Occurrence et de Type avec précautions)

  • Avec Dynamo 

    • avec ses 2 nœuds natifs 



  • Par programmation avec les API(s) 

    • Pour des besoins très spécifiques, on peut également personnaliser ses propres exports avec les API de Revit et de Microsoft, par exemple pour exporter des données spécifiques ou pour personnaliser la mise en page des données sur Excel.
Les données des Nomenclatures peuvent être récupérées au travers de diverses Classes comme TableData ou TableView
Pour rappel les nomenclatures sont des vues... on peut donc parcourir les éléments via la méthode 
FilteredElementCollector

    Voici un exemple d'utilisation de l'API de Microsoft pour exporter les paramètres de fichiers Familles Revit contenu dans un dossier.
    Ici les données sont stockées dans un Array puis transférées dans un Range  via la variable Value2 (sauf pour les couleurs de cellules 😕...y'en a qui ont essayé ils ont eu des problèmes 😅... bref, obligé de passer une boucle)

    Plus d'informations ici 
      gist_export_Excel
      
      # Copyright (c) 20119- POUPIN.C
      import clr
      import re
      clr.AddReference('ProtoGeometry')
      from Autodesk.DesignScript.Geometry import *
      clr.AddReference('RevitAPI')
      import Autodesk
      from Autodesk.Revit.DB import *
      clr.AddReference('RevitServices')
      import RevitServices
      from RevitServices.Persistence import DocumentManager
      from RevitServices.Transactions import TransactionManager
      app = DocumentManager.Instance.CurrentUIApplication.Application
      
      clr.AddReference('System.Drawing')
      from System import Array
      from System.Drawing import *
      
      clr.AddReferenceByName('Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c')
      from Microsoft.Office.Interop import Excel
      from System.Runtime.InteropServices import Marshal
      import sys
      sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')
      import os
              
      class ParameterProp():
          def __init__(self, para):
              self._para = para
              self._pname = para.Definition.Name
              self._pisinstance = para.IsInstance
          
      class FamiliesCheck():
          def __init__(self, dirpath):
              self.dirpath = dirpath
              self.full_lstP = [] 
              for dir, subdir, files in os.walk(dirpath):
                  for file in files:
                      if file.endswith(".rfa") and re.search(r'\.\d+.rfa$', file) is None and re.search(r'[aA]ncien', dir) is None:
                          famDoc = app.OpenDocumentFile(dir + '\\' + file)
                          if famDoc.IsFamilyDocument:
                              famManager = famDoc.FamilyManager
                              lst_para = [ ParameterProp(x) for x in famManager.Parameters]
                              lst_para.sort(key = lambda x : x._pname)
                              lst_para[0:0] = [file]
                              self.full_lstP.append(lst_para)             
                          famDoc.Close(False)     
          
          def XlsWriter(self):
              ex = Excel.ApplicationClass()
              ex.Visible = True
              ex.DisplayAlerts = False
              workbook = ex.Workbooks.Add()
              workbook.SaveAs(self.dirpath + "\\reportFamilies.xlsx")
              ws = workbook.Worksheets[1]     
              nbr_row = 1
              for sub_lst in self.full_lstP:
                  nbr_col = len(sub_lst)
                  xlrange  = ws.Range[ws.Cells(nbr_row, 1), ws.Cells(nbr_row, len(sub_lst))]
                  a = Array.CreateInstance(object, 1, len(sub_lst))
                  b = Array.CreateInstance(object, 1, len(sub_lst))
                  for index, i in enumerate(sub_lst):
                      # if i is string
                      if isinstance(i, str):
                          a[0,index] = i
                      #else i is a ParameterProp objet    
                      else:
                          a[0,index] = i._pname
                          if i._pisinstance == False:
                              b[0,index] = 6
                          elif i._pisinstance == True:
                              b[0,index] = 8      
                          else:
                              b[0,index] = None
                  #copy Array in range            
                  xlrange.Value2 = a  
                  for cell, color_index in zip(xlrange, b):
                      cell.Interior.ColorIndex = color_index
                  nbr_row += 1
              used_range = ws.UsedRange   
              for column in used_range.Columns:
                  column.AutoFit()
              workbook.Save() 
                  
      dirpath = IN[0]
      obj_check = FamiliesCheck(dirpath)
      obj_check.XlsWriter()
      OUT = obj_check.full_lstP
      
      Aperçu en Vidéo avec quelques lignes supplémentaire


      un 2ᵉ exemple ou l'on exporte des preview des familles vers Excel
      (avec les méthodes Clipboard  et Paste )
      
      # coding: utf-8 
      import clr
      clr.AddReference('ProtoGeometry')
      from Autodesk.DesignScript.Geometry import *
      
      clr.AddReference('RevitAPI')
      import Autodesk
      from Autodesk.Revit.DB import *
      
      clr.AddReference('RevitServices')
      import RevitServices
      from RevitServices.Persistence import DocumentManager
      
      doc = DocumentManager.Instance.CurrentDBDocument
      uiapp = DocumentManager.Instance.CurrentUIApplication
      app = uiapp.Application
      uidoc = uiapp.ActiveUIDocument
      
      
      import System
      from System import Environment
      from System import Array
      from System.Collections.Generic import *
      
      clr.AddReference('System.Drawing')
      import System.Drawing
      from System.Drawing import *
      clr.AddReference('System.Windows.Forms')
      from System.Windows.Forms import Clipboard
      
      
      clr.AddReferenceByName('Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' )
      from Microsoft.Office.Interop import Excel
      from System.Runtime.InteropServices import Marshal
      
      
      class ExcelUtils():
          def __init__(self, expSettings, filepath, sizebitmap):
              self.expSettings = expSettings
              self.filepath = filepath
              self.sizebitmap = sizebitmap
              print self.filepath
              
              
          def exportXls(self):
              
              ex = Excel.ApplicationClass()
              ex.Visible = True
              ex.DisplayAlerts = False
              workbook = ex.Workbooks.Add()
              workbook.SaveAs(self.filepath)
              ws = workbook.Worksheets[1] 
              nbr_row = len(self.expSettings)
              nbr_colum = len(self.expSettings[0])
              #
              xlrange  = ws.Range[ws.Cells(1, 2), ws.Cells(nbr_row, nbr_colum )]
              xlrange.EntireRow.RowHeight  = self.sizebitmap.Height
              ws.Range("a1").EntireColumn.ColumnWidth = self.sizebitmap.Width / 4
              a = Array.CreateInstance(object, nbr_row, nbr_colum - 1)
              #
              for indexR, row in enumerate(self.expSettings):
                  for indexC , value in  enumerate(row):
                      if indexC == 0 and value is not None:
                          Clipboard.SetDataObject(value)
                          rng  = ws.Range[ws.Cells(indexR + 1, 1), ws.Cells(indexR + 1 , 1)]
                          ws.Paste(rng, False)
                          
                      else:
                          a[indexR, indexC - 1] = value
                      
              #copy Array in range            
              xlrange.Value2 = a      
              used_range = ws.UsedRange   
              for column in used_range.Columns:
                  column.AutoFit()
                  
      toList = lambda x : x if hasattr(x, '__iter__') else [x]        
      listcat = toList(UnwrapElement(IN[0]))          
      #make filter            
      lstbipCat = [System.Enum.ToObject(BuiltInCategory, x.Id.IntegerValue) for x in listcat]         
      filtercat = ElementMulticategoryFilter(List[BuiltInCategory](lstbipCat))
      #collector
      fecSymb = FilteredElementCollector(doc).WherePasses(filtercat).WhereElementIsElementType().ToElements()
      outdata = []
      imgSize = Size( 100, 100 )
      for symb in fecSymb:
          bitm = symb.GetPreviewImage(imgSize) 
          famName = symb.Family.Name
          symbName = Element.Name.GetValue(symb)
          outdata.append([bitm, famName, symbName])
          
      #define folder to export        
      directory = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
      
      objXls = ExcelUtils(outdata, directory + '\\test.xls', imgSize)
      objXls.exportXls()
      
      OUT = directory  
      

       Bonne Année et Meilleurs vœux