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.
Aperçu en Vidéo
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.
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 en WPF et PythonNet avec :
- tracking de modification d'éléments
- se désabonner d'un Événement API Revit "DocumentChanged" dans le cas d'une fenêtre non Modale (unregister Revit API event handler in Modeless Window WPF or Form)
import clr
import sys
import System
from System import EventHandler, Uri
my_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments)
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
from Autodesk.Revit.UI.Events import ViewActivatedEventArgs, ViewActivatingEventArgs
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.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Xml")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
import System.Windows.Controls
from System.Windows.Controls import *
import System.Windows.Controls.Primitives
from System.Collections.Generic import List
from System.IO import StringReader
from System.Xml import XmlReader
from System.Windows import LogicalTreeHelper
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application
import time
import traceback
class MainForm(Window):
string_xaml = '''
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="UI_Event" Height="600" Width="480">
<Grid>
<!-- Label -->
<Label x:Name="label1" Content="Revit Events" HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="30,9,0,0" FontSize="16" />
<!-- RichTextBox -->
<RichTextBox x:Name="richTextBox1" HorizontalAlignment="Center" VerticalAlignment="Top"
Margin="5,45,5,5" Height="472">
<FlowDocument>
<Paragraph>Processing...</Paragraph>
</FlowDocument>
</RichTextBox>
<!-- Button -->
<Button x:Name="buttonCancel" Content="Stop Loop Event" HorizontalAlignment="Center"
VerticalAlignment="Bottom" Margin="5,5,5,5" Width="156" Height="37" />
</Grid>
</Window>'''
def __init__(self, uiapp, app):
super().__init__()
self._app = app
self.start_time = time.time()
xr = XmlReader.Create(StringReader(MainForm.string_xaml))
self.winLoad = XamlReader.Load(xr)
self.event_unregister = None
self.InitializeComponent()
def InitializeComponent(self):
#
self._richTextBox = LogicalTreeHelper.FindLogicalNode(self.winLoad, "richTextBox1")
#
self.buttonCancel = LogicalTreeHelper.FindLogicalNode(self.winLoad, "buttonCancel")
self.buttonCancel.Click += self.ButtonCancelClick
self.handler = EventHandler[DocumentChangedEventArgs](self.ModifEvent)
self._app.DocumentChanged += self.handler
#
self.winLoad.Loaded += self.OnLoad
self.winLoad.ContentRendered += self.OnContentRendered
#
def ConvertB64_to_BitmapImage(self, b64_value):
picInst = System.Convert.FromBase64String(b64_value)
bi = System.Windows.Media.Imaging.BitmapImage()
bi.BeginInit()
bi.StreamSource = System.IO.MemoryStream(picInst)
bi.EndInit()
return bi
def ModifEvent(self, sender, e):
#prevent if unregister event failed
try:
print("ModifEvent6")
source = System.Windows.PresentationSource.FromVisual(self.winLoad)
if source is not None and not source.IsDisposed:
lstElemNam = []
modified_element_ids = e.GetModifiedElementIds()
transaction_names = e.GetTransactionNames()
lst_elem_names = []
for elem_id in modified_element_ids:
element = doc.GetElement(elem_id)
if element:
lst_elem_names.append(f"{element.Name} (Id: {elem_id.IntegerValue})")
# Update the RichTextBox
if lst_elem_names:
paragraph_text = f"\n\nLIST ELEMENTS:\n" + "\n".join(lst_elem_names)
paragraph_text += f"\n\nTRANSACTION NAME:\n" + "\n".join(transaction_names)
paragraph_text += f"\n\nUSER NAME: {self._app.Username}"
paragraph_text += "\n###########################################"
# Create a new paragraph for the new content
new_paragraph = System.Windows.Documents.Paragraph()
new_paragraph.Inlines.Add(paragraph_text)
# Append the paragraph to the RichTextBox's FlowDocument
flow_document = self._richTextBox.Document
flow_document.Blocks.Add(new_paragraph)
except Exception as ex:
print(traceback.format_exc())
def OnContentRendered(self, sender, e):
print(f"UI show in {time.time() - self.start_time} seconds")
def OnLoad(self, sender, e):
try:
print("UI loaded")
except Exception as ex:
print(traceback.format_exc())
def ButtonCancelClick(self, sender, e):
try:
self.event_unregister.Raise()
self.winLoad.Close()
#self.event_unregister.Dispose()
except Exception as ex:
print(ex)
class EventUnRegisterHandler :
def __new__(cls, *args, **kwargs):
cls.args = args
# name of namespace : "CustomNameSpace" + "_" + shortUUID (8 characters)
# IMPORTANT NOTE Each time you modify this class, you must change the namesapce name.
cls.__namespace__ = "EventRegisterHandler_QTRGDE"
try:
# 1. Try to import the module and class. If it already exists, you've probably already created
module = __import__(cls.__namespace__)
# import class
return module._InnerClassInterface(*cls.args)
except Exception as ex:
print(ex)
class _InnerClassInterface(IExternalEventHandler):
__namespace__ = EventUnRegisterHandler.__namespace__
def __init__(self, wpf_form):
super().__init__()
self.wpf_form = wpf_form
def Execute(self, _uiap):
print("remove Event")
try:
_uidoc = _uiap.ActiveUIDocument
_doc = _uidoc.Document
_view = _doc.ActiveView
#processing
self.wpf_form._app.DocumentChanged -= self.wpf_form.handler
self.wpf_form.event_unregister.Dispose()
except Exception as ex:
print(ex)
def GetName(self):
return "EventRegisterHandler"
return _InnerClassInterface(*cls.args)
objForm = MainForm(uiapp, app)
obj_handlerUnRegister = EventUnRegisterHandler(objForm)
ext_eventUnRegister = ExternalEvent.Create(obj_handlerUnRegister)
objForm.event_unregister = ext_eventUnRegister
objForm.winLoad.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
Bonne Année, prenez soin de vous et de vos proches
0 commentaires:
Enregistrer un commentaire