13 janv. 2021

[Dynamo += Python] Revit et les Événements

 




Lorsque l'on parle de Dynamo et de la gestion des événements Revit (Évents) on pense souvent à une incompatibilité....

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


Par défaut Dynamo ne peut écouter les événements Revit que pendant l'exécution du graphique et Dynamo ne peut s'exécuter qu'à l'intérieur de l'événement inactif de Revit.

L'émetteur d'événements (Revit’s idle event) n'est déclenché que lorsqu'aucune autre interaction ne se produit et lorsqu'il est activé, le contexte de Revit est occupé jusqu'à ce que ce qui a déclenché l'événement s'achève.

Une solution est d'utiliser une boucle d’évènements dans un autre Thread (avec un Winform non bloquant par exemple) ce qui permet au Thread principale de Dynamo de se terminer.


À quoi peut bien servir les événements Revit ?...

Un exemple de cas d'usage courant est la recherche d'éléments dans le projet et de zoomer sur celui-ci. Pour pouvoir effectuer cette opération il faut s'assurer au préalable que la vue ait finit de s'afficher, c'est là qu'intervient l'utilisation d'un événement.

Voici une "reproduction" de la fonction native de Revit ou l'on effectue une recherche d'élément par leur ID (avec entre autres la méthode RequestViewChange(View) et une fois la vue affichée on applique la méthode  ShowElements(ElementId) )






Pour les opérations nécessitant une Transaction  cela devient un peu plus complexe, comme on est un autre Thread (non principal) on doit faire appel à un ExternalEvent 


Voici un exemple ci-dessous qui réalise les actions suivantes :
  • Liste les changements de vue (via les Évents).
  • Liste les éléments modifiés (via les Évents).
  • Colorise la sélection d'éléments en cours si des éléments viennent d'êtres modifiés. (on pourrait également colorisé tous les éléments modifiés mais j'ai simplifié ici) 

import sys
import clr
import System 
from System import EventHandler, Uri

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

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Events import ViewActivatedEventArgs, ViewActivatingEventArgs
from Autodesk.Revit.UI import IExternalEventHandler, ExternalEvent

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

instdoc = DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application

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 *

class MainForm(Form):
    def __init__(self, ext_event, uiapp, app):
        self._uiapp = uiapp
        self._app = app
        self._ext_event = ext_event
        self.InitializeComponent()
    
    def InitializeComponent(self):
        self._buttonCancel = System.Windows.Forms.Button()
        self._richTextBox1 = System.Windows.Forms.RichTextBox()
        self._label1 = System.Windows.Forms.Label()
        self.SuspendLayout()
        # self._uiapp.ViewActivating += EventHandler[ViewActivatingEventArgs](self.ViewEvent)
        # self._app.DocumentChanged += EventHandler[DocumentChangedEventArgs](self.ModifEvent)
        self._delegateViewEvent = EventHandler[ViewActivatingEventArgs](self.ViewEvent)
        self._delegateModifEvent = EventHandler[DocumentChangedEventArgs](self.ModifEvent)
        self._uiapp.ViewActivating += self._delegateViewEvent
        self._app.DocumentChanged += self._delegateModifEvent
        # 
        # buttonCancel
        self._buttonCancel.Location = System.Drawing.Point(150, 550)
        self._buttonCancel.Name = "buttonStop"
        self._buttonCancel.Size = System.Drawing.Size(156, 37)
        self._buttonCancel.TabIndex = 0
        self._buttonCancel.Text = "Stop Loop Event"
        self._buttonCancel.UseVisualStyleBackColor = True
        self._buttonCancel.Click += self.ButtonCancelClick
        # 
        # richTextBox1
        self._richTextBox1.Location = System.Drawing.Point(30, 37)
        self._richTextBox1.Name = "richTextBox1"
        self._richTextBox1.Size = System.Drawing.Size(400, 500)
        self._richTextBox1.TabIndex = 1
        self._richTextBox1.Text = "Processing..."
        # 
        # label1
        self._label1.Location = System.Drawing.Point(30, 9)
        self._label1.Name = "label1"
        self._label1.Size = System.Drawing.Size(152, 25)
        self._label1.TabIndex = 2
        self._label1.Text = "Revit Events"
        # 
        # MainForm
        self.ClientSize = System.Drawing.Size(480, 600)
        self.Controls.Add(self._label1)
        self.Controls.Add(self._richTextBox1)
        self.Controls.Add(self._buttonCancel)
        self.Name = "MainForm"
        self.Text = "UI_Event"
        self.ResumeLayout(False)
        
    def ViewEvent(self, sender, e): 
        #prevent if unregister  event failed
        if not self.Controls[0].IsDisposed:
            newViewName = e.NewActiveView.Name
            currentText = self._richTextBox1.Text
            self._richTextBox1.Text = currentText + "\nSwitch to View ->" + newViewName
        
    def ModifEvent(self, sender, e):
        #prevent if unregister  event failed
        if not self.Controls[0].IsDisposed:
            lstElemNam = []
            lstElemdIds = e.GetModifiedElementIds()
            for xId in lstElemdIds:
                elem = doc.GetElement(xId)
                if elem is not None or int(xId) != 1:
                    lstElemNam.append(elem.Name + "Id :"+ str(xId.IntegerValue))
            if len(lstElemNam) > 0: 
                currentText = self._richTextBox1.Text
                self._richTextBox1.Text = currentText + "\nModification Elements:\n" + "\n".join(lstElemNam)
                self._ext_event.Raise()
        
    def ButtonCancelClick(self, sender, e):
        self._ext_event.Dispose()
        self.Close()
        try:
            self._uiapp.ViewActivating -= self._delegateViewEvent
        except:
            self._uiapp.ViewActivating -= self.ViewEvent
        try:
            self._app.DocumentChanged -= self._delegateModifEvent
        except:
            self._app.DocumentChanged -= self.ModifEvent
        self.Dispose()
        
class ModExternalEvent(IExternalEventHandler):
    def Execute(self, _uiap):
        _uidoc = _uiap.ActiveUIDocument
        _doc = _uidoc.Document
        _view = _doc.ActiveView
        #processing
        tx = Transaction(_doc)
        tx.Start("MyEvent")
        rvtcolor = Autodesk.Revit.DB.Color(255, 0, 0)
        gSettings = OverrideGraphicSettings()
        gSettings.SetProjectionFillColor(rvtcolor)
        gSettings.SetProjectionLineColor(rvtcolor)
        gSettings.SetCutLineColor(rvtcolor)
        gSettings.SetCutFillColor(rvtcolor) 
        for elid in _uidoc.Selection.GetElementIds():
            _view.SetElementOverrides(elid, gSettings)
        tx.Commit()     
    
    def GetName(self):
        return "Test External Event"

obj_handler = ModExternalEvent()    
ext_event = ExternalEvent.Create(obj_handler)
objEvent =  MainForm(ext_event, uiapp, app)
objEvent.Show()
OUT = 0
  
un autre Exemple avec seulement un tracking de modification d'éléments:

import sys
import clr
import System 
from System import EventHandler, Uri

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

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

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

instdoc = DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application

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 *

class MainForm(Form):
    def __init__(self, uiapp, app):
        self._uiapp = uiapp
        self._app = app
        self.InitializeComponent()
    
    def InitializeComponent(self):
        self._buttonCancel = System.Windows.Forms.Button()
        self._richTextBox1 = System.Windows.Forms.RichTextBox()
        self._label1 = System.Windows.Forms.Label()
        self.SuspendLayout()
        self._app.DocumentChanged += EventHandler[DocumentChangedEventArgs](self.ModifEvent)
        # 
        # buttonCancel
        self._buttonCancel.Location = System.Drawing.Point(150, 550)
        self._buttonCancel.Name = "buttonStop"
        self._buttonCancel.Size = System.Drawing.Size(156, 37)
        self._buttonCancel.TabIndex = 0
        self._buttonCancel.Text = "Stop Loop Event"
        self._buttonCancel.UseVisualStyleBackColor = True
        self._buttonCancel.Click += self.ButtonCancelClick
        # 
        # richTextBox1
        self._richTextBox1.Location = System.Drawing.Point(30, 37)
        self._richTextBox1.Name = "richTextBox1"
        self._richTextBox1.Size = System.Drawing.Size(400, 500)
        self._richTextBox1.TabIndex = 1
        self._richTextBox1.Text = "Processing..."
        # 
        # label1
        self._label1.Location = System.Drawing.Point(30, 9)
        self._label1.Name = "label1"
        self._label1.Size = System.Drawing.Size(152, 25)
        self._label1.TabIndex = 2
        self._label1.Text = "Revit Events"
        # 
        # MainForm
        self.ClientSize = System.Drawing.Size(480, 600)
        self.Controls.Add(self._label1)
        self.Controls.Add(self._richTextBox1)
        self.Controls.Add(self._buttonCancel)
        self.Name = "MainForm"
        self.Text = "UI_Event"
        self.ResumeLayout(False)
        

        
    def ModifEvent(self, sender, e):
        #prevent if unregister  event failed
        if not self.Controls[0].IsDisposed:
            lstElemNam = []
            lstElemdIds = e.GetModifiedElementIds()
            listTransactionName = e.GetTransactionNames()
            for xId in lstElemdIds:
                elem = doc.GetElement(xId)
                if elem is not None or int(xId) != 1:
                    lstElemNam.append(elem.Name + "Id :"+ str(xId.IntegerValue))
            if len(lstElemNam) > 0: 
                currentText = self._richTextBox1.Text
                self._richTextBox1.Text = currentText + "\n\nLIST ELEMENTS:\n" + "\n".join(lstElemNam)
                self._richTextBox1.Text += "\n\nTRANSACTION NAME :\n" + "\n".join(listTransactionName)
                self._richTextBox1.Text += "\n\nUSER NAME : "+ self._app.Username
                self._richTextBox1.Text += "\n###########################################"
                self._ext_event.Raise()
        
    def ButtonCancelClick(self, sender, e):
        self.Close()
        self._app.DocumentChanged -= EventHandler[DocumentChangedEventArgs](self.ModifEvent)
        self.Dispose()

objEvent = MainForm(uiapp, app)
objEvent.Show()
OUT = 0
   
Note
L'exemple a été fait sous Revit 2019 et le code ne prends pas compte les divers changements d'API comme la classe DB.OverrideGraphicSettings 

Aperçu en Vidéo 



Bonne Année, prenez soin de vous et de vos proches

0 commentaires:

Enregistrer un commentaire