30 déc. 2023

[Dynamo += Python] Import d'Annotations Dwfx (Feuilles)

 








Importer des annotations Dwfx via l'API Revit

#DynamoBIM #Revit  #Dwfx#Python #DesignReview
if this article is not in your language, use the Google Translate widget ⬈ (bottom of page for Mobile version )





Qu'il soit utilisé comme support de synthèse, ou comme de contrôle ou d'autocontrôle avant publication, même si la tendance est le cloud (ACC), le dwfx a encore sa place dans la collaboration.

l'API Revit permet d'importer les annotations à partir d'un dwfx avec la méthode suivante :

Link Method (String, DWFImportOptions)

avec comme option une instance du constructeur DWFImportOptions 

DWFImportOptions Constructor (IList(ElementId))

à noter qu'il est seulement possible d'importer des annotations Dwfx de Feuilles (pas de Vues)


le seul problème, c'est qu'il faut passer en arguments la liste des Feuilles concerne par le Dwfx 😕


Alors comment faire ? 



La solution consiste à examiner le Dwfx et chercher les noms des Feuilles Revit dans celui-ci.

Le Dwfx est effectivement une archive ; en analysant son contenu, on peut extraire les noms des feuilles du Dwfx à partir d'un fichier XML manifest.xml que l'on peut parser.




Voici donc un exemple de code Python pour passer en arguments la bonne liste des vues

le code est compatible avec tous les moteurs Python
# Load the Python Standard and DesignScript Libraries
import clr
import re
import sys
import System
pyEngineName = sys.implementation.name 
from System.Collections.Generic import List

clr.AddReference('RevitAPI')
import Autodesk
import Autodesk.Revit.DB as DB

clr.AddReference('System.Windows.Forms')
import System.Windows.Forms
from System.Windows.Forms import OpenFileDialog, DialogResult

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import TaskDialog, TaskDialogIcon

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

import zipfile
import xml.etree.ElementTree as ET
		
def get_xml_namespace(xmlelement):
	m = re.match(r'\{.*\}', xmlelement.tag)
	return m.group(0) if m else ''

lst_annotids = []		

openFileDialog  = OpenFileDialog()	
openFileDialog.Filter = "Dwfx files (*.dwfx)|*.dwfx|All files (*.*)|*.*"
openFileDialog.RestoreDirectory = True
openFileDialog.Title = "Select dwfx file"
#
if openFileDialog.ShowDialog() == DialogResult.OK:
	list_ViewSheet = List[DB.ViewSheet](DB.FilteredElementCollector(doc).OfClass(DB.ViewSheet).ToElements())
	sheetViewIds = List[DB.ElementId]()
	filePath = openFileDialog.FileName
	import_dwfx = System.IO.Path.GetFileName(filePath)
	# Start Read dwfx file with zipfile
	lst_no_associate_views = []
	with zipfile.ZipFile(filePath, mode="r") as archive: 
		for info in archive.infolist():
			if info.filename.endswith("manifest.xml"):
				# Read and parse the Xml content
				xmlcontent = archive.read(info.filename)
				root = ET.fromstring(xmlcontent)
				xml_namespace = get_xml_namespace(root)
				allSection = root.findall(".//{}Section".format(xml_namespace))
				for sectionNode in allSection:
					#
					full_title =  sectionNode.get("title")
					if full_title is not None :
						#
						allressource = sectionNode.findall(".//{}Resource".format(xml_namespace))
						for ressourceNode in allressource:
							role =  ressourceNode.get("role")
							if "markup object" in role:
								# find the good sheet with Title property
								filter_title = System.Predicate[System.Object](lambda v : v.Title == full_title)
								viewRvt = list_ViewSheet.Find(filter_title)
								if viewRvt is not None:
									sheetViewIds.Add(viewRvt.Id)
									view_ids_founded = True
									break
								else:
									lst_no_associate_views.append(full_title)
                                    
	# sort by Title Name (same UI revit)
	sheetViewIds = sorted(sheetViewIds, key = lambda xId : doc.GetElement(xId).Title)
	sheetViewIds = List[ElementId](sheetViewIds)
	print(sheetViewIds)
	print(lst_no_associate_views)
	# create a warning if dwfx has no associated views
	if len(lst_no_associate_views) > 0:
		# reset sheetViewIds
		sheetViewIds = List[DB.ElementId]()
		dialog = TaskDialog("Erreur Views")
		dialog.MainInstruction = "Unassociated views, delete views (or rename correctly from Revit Sheets) below on the dwfx, save, then restart"
		dialog.MainContent = "View to Delete or Rename on dwfx :\n\n-{}".format("-\n".join(lst_no_associate_views))
		dialog.MainIcon  = TaskDialogIcon.TaskDialogIconWarning
		#
		if pyEngineName == "ironpython":
			dialog.Show()
		else:
			# unfortunately, the Show() method does not work with PythonNet2.5/CPython3 (overload issue).
			from System.Reflection import BindingFlags
			arguments = []
			clr.GetClrType(dialog.GetType()).InvokeMember("Show", BindingFlags.InvokeMethod , None, dialog, arguments)
	#
				
	if len(sheetViewIds) > 0:
		# Import dwfx and get annotation element IDs
		TransactionManager.Instance.EnsureInTransaction(doc)
		ops = DB.DWFImportOptions(sheetViewIds)
		lst_annotids = doc.Link(filePath, ops)
		TransactionManager.Instance.TransactionTaskDone()
		doc.Regenerate()
	# get some information
	lstAnnot = [doc.GetElement(xId) for xId in lst_annotids]
	lstAnnot = [[doc.GetElement(x.OwnerViewId),x.get_Parameter(DB.BuiltInParameter.MARKUPS_LABEL).AsString()] for x in lstAnnot]
	OUT = lstAnnot







Bonnes fêtes de fin d'années et Bonne Année à tous ceux qui liront l'article à partir du 1ᵉʳ janvier

Bon, j'ai des corrections à faire moi 😊


Edit 11/2024
grosse mise en à jour de Design Review HotFix 8 




0 commentaires:

Enregistrer un commentaire