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 :

if this article is not in your language, use the Google Translate widget (bottom of page for Mobile version) ⬈


  • le Dynamo Player 
    • permet de lancer des scripts
    • permet de modifier des données d'entrée 
    • permet de visualiser les données de sortie
  • le package MultipleInputForm++ (Data-Shape) pour créer facilement des UI Winform
  • le package Mandrill pour visualiser des données sous forme de graphiques
  • l'addin DynoBrowser (un launcher de script personnalisable)
Mais si vous avez quelques connaissances sur  le framework .Net (Winform ou Wpf) il est aussi possible de réaliser son propre UI.
Note:
Juste pour une question de débogage, bien que cela soit possible d'utiliser Wpf en IronPython il est préférable d'utiliser Winform.

Pour faciliter la création et soulager un peu vos neurones, le formulaire (Winform) sera créé dans un IDE comme SharpDevellop ou VisualStudio en Python, puis le code sera complété dans un nœud Dynamo (Python).

Version conseillée à ce jour

Prérequis: du fait que SharpDevellop est un peu dépassé, il est conseillé d'activer le Net Framework 3.5 dans les options de Windows pour éviter des erreurs de compilation IronPython (ipy)






Choisir  Python puis Application Windows pour créer un "Windows Form IronPython"


Note:
Dans le cas d'une simple réalisation d'une interface UI il n'est pas nécessaire d'importer les références telles que l'API Revit ou l'API DynamoDS.

Placement des Objets dans la fenêtre Design

Un double clique sur les objets pour créer les évènements puis copie du code vers Dynamo pour finalisation

Note :
Dans le nœud Python dans Dynamo on pensera à rajouter les "assembly" nécessaire
clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')

Aller, un Exemple ?
Contexte de l'exemple ci-après :
Au fil d'un projet, il n'est pas rare de devoir aller fouiller dans les Paramètres de Projet afin de retrouver les propriétés d'un paramètres (catégories associées ?,  groupe?, type de paramètre ?, etc...), et la liste peut être très longue....


Voici un script qui permet de visualiser les paramètres de Projet dans une fenêtre sous forme d'un TreeNode (WinForm) dynamique.

L'extraction des Paramètres de Projet se fait avec la propriété ParameterBindings ainsi que la
méthode ForwardIterator afin de retrouver le dictionnaire associé (IEnumerable).



# coding : utf-8
# Copyright POUPIN.C
import clr
import re

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

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp =  DocumentManager.Instance.CurrentUIApplication

import System
clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')
import System.Drawing
import System.Windows.Forms

from System.Drawing import *
from System.Windows.Forms import *
from System.Collections.Generic import List

class ParaProj():
 def __init__(self, definition, binding):
  self.defName = definition.Name
  self.binding = binding 
  self.thesecats = []
  self.group = LabelUtils.GetLabelFor(definition.ParameterGroup)
  for cat in binding.Categories:
   try:
    self.thesecats.append(cat.Name)
   except SystemError:
    pass      

class MainForm(Form):
    def __init__(self, lstObj_bind):
        self.lstObj_bind = lstObj_bind #lst object
        collSharP = FilteredElementCollector(doc).OfClass(SharedParameterElement)
        self._paraSharPara = [x.GetDefinition().Name for x in collSharP]
        self.InitializeComponent()
    
    def InitializeComponent(self):
        self._treeView1 = System.Windows.Forms.TreeView()
        self._buttonEdit = System.Windows.Forms.Button()
        self._buttonCancel = System.Windows.Forms.Button()
        self.SuspendLayout()   
        # 
        # buttonEdit
        self._buttonEdit.DialogResult = System.Windows.Forms.DialogResult.OK
        self._buttonEdit.Location = System.Drawing.Point(350, 500)
        self._buttonEdit.Name = "buttonEdit"
        self._buttonEdit.Size = System.Drawing.Size(150, 45)
        self._buttonEdit.TabIndex = 0
        self._buttonEdit.Text = "Editer Paramètres de Projet"
        self._buttonEdit.UseVisualStyleBackColor = True
        self._buttonEdit.Click += self.ButtonEditClick
        #
        # buttonCancel
        self._buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel
        self._buttonCancel.Location = System.Drawing.Point(50, 500)
        self._buttonCancel.Name = "buttonCancel"
        self._buttonCancel.Size = System.Drawing.Size(142, 45)
        self._buttonCancel.TabIndex = 0
        self._buttonCancel.Text = "Quitter"
        self._buttonCancel.UseVisualStyleBackColor = True
        self._buttonCancel.Click += self.ButtonCancelClick
        # 
        # textBox1
        self._textBox1 = System.Windows.Forms.TextBox()
        self._textBox1.Location = System.Drawing.Point(50, 46)
        self._textBox1.Name = "SearchBox"
        self._textBox1.Size = System.Drawing.Size(213, 22)
        self._textBox1.TabIndex = 0
        self._textBox1.TextChanged += self.TextBox1TextChanged
        # 
        # Label
        self._label1 = System.Windows.Forms.Label()
        self._label1.Location = System.Drawing.Point(50, 20)
        self._label1.Name = "Search"
        self._label1.Size = System.Drawing.Size(150, 23)
        self._label1.TabIndex = 1
        self._label1.Text = "Recherche (Regex)"        
        # 
        # treeView1
        self._treeView1.Location = System.Drawing.Point(50, 75)
        self._treeView1.Name = "treeView1"     
        dataBind = self.lstObj_bind   
        #call function to Populate TreeNode        
        self.setDataTreeNode(dataBind)
        self._treeView1.Size = System.Drawing.Size(450, 400)
        self._treeView1.TabIndex = 0
        # 
        # Form1
        self.ClientSize = System.Drawing.Size(540, 580)
        self.Controls.Add(self._treeView1)
        self.Controls.Add(self._label1)
        self.Controls.Add(self._textBox1)
        self.Controls.Add(self._buttonCancel)
        self.Controls.Add(self._buttonEdit)
        self.Name = "Projet Parameters"
        self.Text = "Liste des Parametres de Projet"
        self.ResumeLayout(False)
        
    def setDataTreeNode(self, dataBind):
        self.lsttreenode = List[System.Object]()
        for objbind in dataBind:
            paraproj = objbind.defName
            isShared = "Partagé" if paraproj in self._paraSharPara else  "Non Partagé"
            #check binding and apply Color
            if isinstance(objbind.binding, InstanceBinding):
                bindtype = "Occurrence"
                colorNod = System.Drawing.Color.Blue
            else:
                bindtype = "Type"
                colorNod = System.Drawing.Color.Red
            filtercats = objbind.thesecats
            sublist = List[System.Object]()
            #create data TreeNode
            for cat in filtercats:
                treeNodeChildCat = System.Windows.Forms.TreeNode(cat)
                treeNodeChildCat.Name = cat
                treeNodeChildCat.Text = cat
                sublist.Add(treeNodeChildCat)
            treeNodeParentCat = System.Windows.Forms.TreeNode("Catégories", System.Array[System.Windows.Forms.TreeNode](sublist))
            treeNodeParentCat.Name = "Catégories"
            treeNodeParentCat.Text = "Catégories"
            treeNodeTypePara = System.Windows.Forms.TreeNode("Propriété")
            treeNodeTypePara.Name = "Propriété"
            treeNodeTypePara.Text = "Propriété du Paramètre : " + bindtype 
            treeNodeShared = System.Windows.Forms.TreeNode("Type")
            treeNodeShared.Name = "Type"
            treeNodeShared.Text = "Type de Paramètre : " + isShared    
            treeNodeGroup = System.Windows.Forms.TreeNode("Groupe")
            treeNodeGroup.Name = "Groupe"
            treeNodeGroup.Text = "Groupe : " + objbind.group            
            treeNodeParentPara = System.Windows.Forms.TreeNode(paraproj, System.Array[System.Windows.Forms.TreeNode](
                [treeNodeTypePara, 
                treeNodeShared, 
                treeNodeGroup, 
                treeNodeParentCat]))
            #treeNodeParent 
            treeNodeParentPara.Name = paraproj
            treeNodeParentPara.Text = paraproj         
            treeNodeParentPara.ForeColor = colorNod          
            self.lsttreenode.Add(treeNodeParentPara)
        self._treeView1.Nodes.AddRange(System.Array[System.Windows.Forms.TreeNode](self.lsttreenode))
                    
    def TextBox1TextChanged(self, sender, e):
        try:
            dataBindFilter =  [x for x in self.lstObj_bind if re.search(sender.Text, x.defName) is not None]
            self._treeView1.BeginUpdate()
            self._treeView1.Nodes.Clear()
            self.setDataTreeNode(dataBindFilter)
            self._treeView1.EndUpdate()
        except:pass #if regex failed (typing not finish )
        
    def ButtonCancelClick(self, sender, e):
        self.Close()

    def ButtonEditClick(self, sender, e): 
        id_addin = RevitCommandId.LookupPostableCommandId(PostableCommand.ProjectParameters)
        uiapp.PostCommand(id_addin)  
        self.Close()
   
full_bind = []
iterator =  doc.ParameterBindings.ForwardIterator()
while iterator.MoveNext():
    objBind = ParaProj(iterator.Key, iterator.Current)
    full_bind.append(objBind)
full_bind.sort(key = lambda x : x.defName) 
objform = MainForm(full_bind)   
result = objform.ShowDialog()    

OUT = full_bind, result


lien Github

Aperçu en Vidéo



  
# coding : utf-8
import clr

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument


class ParaProj():
    def __init__(self, definition, binding):
        self.defName = definition.Name
        self.binding = binding 
        self.thesecats = []
        self.group = LabelUtils.GetLabelFor(definition.ParameterGroup)
        self.type = "Instance" if isinstance(binding, InstanceBinding) else "Type"
        
    @property   
    def categories(self):
        thesecats = []  
        for cat in self.binding.Categories:
            try:
                thesecats.append(cat.Name)
            except SystemError:
                pass  
        return thesecats
        
    @property                   
    def isShared(self):
        collSharP = FilteredElementCollector(doc).OfClass(SharedParameterElement)
        paraSharPara = [x.GetDefinition().Name for x in collSharP]  
        return  True if self.defName in paraSharPara else False 
                

paraName = []
paraGroup = []
paraCategories = [] 
paraType = []
paraIsShared = []
iterator =  doc.ParameterBindings.ForwardIterator()
while iterator.MoveNext():
    objBind = ParaProj(iterator.Key, iterator.Current)
    paraName.append(objBind.defName)
    paraGroup.append(objBind.group)
    paraType.append(objBind.type)
    paraIsShared.append(objBind.isShared)
    paraCategories.append(objBind.categories)
  

OUT = paraName, paraGroup, paraType, paraIsShared, paraCategories


Une astuce pour trouver si un paramètre partagé est présent dans le projet
  
import clr
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
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

lstDef = []
gpNames = []
spf = app.OpenSharedParameterFile()
if spf is not None:
	spfGroups = spf.Groups
	for group in spfGroups:
		for def_ in group.Definitions:
			guid = def_.GUID
			spInproj = SharedParameterElement.Lookup(doc, guid)
			if spInproj is not None:
				defInproj = spInproj.GetDefinition() 
				pGroup = defInproj.ParameterGroup
				lstDef.append(defInproj)
				gpNames.append(LabelUtils.GetLabelFor(pGroup))

OUT = lstDef, gpNames
  

5 commentaires:

  1. Thanks for this article. It help me a lot for my work.

    RépondreSupprimer
  2. Bonjour et merci pour les explications
    J'ai installé SharpDevelop
    dans la catégorie je ne vois pas le répertoire Python
    à l'occasion, merci

    RépondreSupprimer
    Réponses
    1. Salut, essaie la version suivante
      SharpDevelop_4.4.1.9729_Setup.msi
      j'ai rajouté le lien dans l'article

      Supprimer
    2. Bonjour, impeccable
      Merci @ vous
      cordialement

      Supprimer