A few things kept getting messed up.
Interesting observation: In the English Version (I work on the german one), it didnt work quiet right alot of times.
I needed to add:
import sys
I also needed to hard code the IFC bundle location
pf_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
pf_data_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.CommonApplicationData)
sdk = doc.Application.VersionNumber # z.B. "2024"
ifc_bundle_path = "{0}\\Autodesk\\ApplicationPlugins\\IFC {1}.bundle\\Contents\\{1}".format(pf_data_path, sdk)
ifc_addin_path = "{}\\Autodesk\\Revit {}\\AddIns\\IFCExporterUI".format(pf_path, sdk)
and modify the two methods.
the whole script.py is underneath. It somehow catches the correct Setup and Property Set List. Be aware, that it could have issues in other Languages
# -*- coding: utf-8 -*-
__title__ = "PROJECT Export Skript - 1.1.3"
__doc__ = """Version: 1.1.3 - Force-Init IFC Service"""
import os
import sys
import json
import re
import codecs
import clr
import System
# Standard Referenzen
clr.AddReference("System")
clr.AddReference("PresentationFramework")
clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIIFC")
# 1. VERSUCH: DLL PFAD ERZWINGEN
from System.Diagnostics import Process
from System.Reflection import Assembly
revit_path = os.path.dirname(Process.GetCurrentProcess().MainModule.FileName)
ifc_ui_dll = os.path.join(revit_path, "AddIns", "IFCExporterUI", "BIM.IFC.Export.UI.dll")
if os.path.exists(ifc_ui_dll):
try:
Assembly.LoadFrom(ifc_ui_dll)
clr.AddReference(ifc_ui_dll)
except:
pass
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.IFC import *
from System.Windows import MessageBox, MessageBoxButton, MessageBoxImage
from System.Windows.Markup import XamlReader
from System.IO import FileStream, FileMode
from System.ComponentModel import INotifyPropertyChanged, PropertyChangedEventArgs
from System.Collections.ObjectModel import ObservableCollection
from System.Collections.Generic import List
SKRIPT_VERSION = "1.1.3"
PATH_SCRIPT = os.path.dirname(__file__)
CONFIG_PATH = os.path.join(PATH_SCRIPT, "settings.json")
doc = __revit__.ActiveUIDocument.Document
# --- IFC UI PFAD LOGIK (neu) ---
pf_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
pf_data_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.CommonApplicationData)
sdk = doc.Application.VersionNumber # z.B. "2024"
ifc_bundle_path = "{0}\\Autodesk\\ApplicationPlugins\\IFC {1}.bundle\\Contents\\{1}".format(pf_data_path, sdk)
ifc_addin_path = "{}\\Autodesk\\Revit {}\\AddIns\\IFCExporterUI".format(pf_path, sdk)
class ExportWindow(object):
def __init__(self, xaml_path):
self.vm = MainViewModel()
with FileStream(xaml_path, FileMode.Open) as stream: self.ui = XamlReader.Load(stream)
self.ui.DataContext = self.vm
self.btn_Start = self.ui.FindName("btn_Start")
self.listBox_Progress = self.ui.FindName("listBox_Progress")
self.btn_Start.Click += self.btn_Start_Click
self.config_data = self._load_config_with_check()
self._setup_items()
def _load_config_with_check(self):
if not os.path.exists(CONFIG_PATH): return {}
try:
with codecs.open(CONFIG_PATH, "r", encoding="utf-8") as f:
data = json.load(f)
if data.get("version") != SKRIPT_VERSION:
res = MessageBox.Show("Skriptversion geändert. Einstellungen übernehmen?", "Version Check", MessageBoxButton.YesNo)
if res == MessageBoxButton.No: return {}
return data
except: return {}
def _get_advanced_ifc_config(self):
# Initialisierung der Variablen
configs_map_class = None
target_name = "RW Export: Nur sichtbares | Pset BT-Liste | Mittel"
try:
if System.IO.Directory.Exists(ifc_bundle_path):
sys.path.append(ifc_bundle_path)
clr.AddReference("IFCExporterUIOverride")
self._log("ifc_bundle_path geladen")
elif System.IO.Directory.Exists(ifc_addin_path):
sys.path.append(ifc_addin_path)
clr.AddReference("Autodesk.IFC.Export.UI")
self._log("ifc_addin_path geladen")
# Import der Klassen aus der geladenen DLL
from BIM.IFC.Export.UI import IFCExportConfigurationsMap, IFCCommandOverrideApplication
configs_map_class = IFCExportConfigurationsMap
except Exception as e:
self._log("Could not load IFC UI Assemblies: {}".format(e))
return None
try:
# Dokument-Kontext für IFC setzen
if IFCCommandOverrideApplication.TheDocument is None:
propinfo = clr.GetClrType(IFCCommandOverrideApplication).GetProperty('TheDocument')
propinfo.SetValue(None, doc)
# Map instanzieren & Konfigurationen laden
configs_map = configs_map_class()
configs_map.AddBuiltInConfigurations()
configs_map.AddSavedConfigurations()
# Ziel-Konfiguration suchen
for config in configs_map.Values:
if config.Name == target_name:
self._log("Konfiguration gefunden: {}".format(target_name))
return config
self._log("Konfiguration '{}' NICHT gefunden. Nutze Standard.".format(target_name))
return None
except Exception as e:
self._log("Error accessing IFC configurations: {}".format(e))
return None
def _get_dwg_options(self):
try:
settings = FilteredElementCollector(doc).OfClass(ExportDWGSettings).FirstElement()
if settings: return settings.GetDWGExportOptions()
except: pass
ops = DWGExportOptions()
ops.MergedViews = True
ops.ExportLayerOptions = ExportLayerOptions.ExportOnDifferentLayers
return ops
def btn_Start_Click(self, sender, e):
from datetime import datetime
self.listBox_Progress.Items.Clear()
self._log(">>> Starte Export (v{})...".format(SKRIPT_VERSION))
self._save_config()
selected = [i for i in self.vm.Items if i.EbeneChkd]
if not selected: return
adv_ifc_config = self._get_advanced_ifc_config()
dwg_options = self._get_dwg_options()
# --- ORDNER-LOGIK ---
has_dwg = any(i.DWGChkd for i in selected)
has_3d_dwg = any(i.ThreeDDWGChkd for i in selected)
formate = []
if has_dwg or has_3d_dwg: formate.append("DWG")
if any(i.IFCChkd for i in selected): formate.append("IFC")
if self.vm.PdfManuellChkd: formate.append("PDF")
folder_name = "{} {} {}".format(datetime.now().strftime("%Y-%m-%d"), self._get_geschose_string(selected), ",".join(formate)).strip()
root_path = os.path.join("Z:\Projekte\PROJECT\Daten\Daten out", folder_name)
dwg_2d_target_path = os.path.join(root_path, self.vm.SubfolderName) if (self.vm.IsSubfolderActive and has_dwg) else root_path
if not os.path.exists(root_path): os.makedirs(root_path)
if self.vm.IsSubfolderActive and has_dwg and not os.path.exists(dwg_2d_target_path): os.makedirs(dwg_2d_target_path)
all_views = [v for v in FilteredElementCollector(doc).OfClass(View).ToElements() if not v.IsTemplate]
for item in selected:
s_num, s_type = "{:02d}".format(item.num), {"UG":"Untergeschoss","EG":"Erdgeschoss","OG":"Obergeschoss"}.get(item.l_type)
v_ifc = next((v for v in all_views if s_num in v.Name and s_type in v.Name and "- für IFC Export" in v.Name), None)
v_dwg = next((v for v in all_views if s_num in v.Name and s_type in v.Name and "- für DWG Export" in v.Name), None)
v_3d_dwg = next((v for v in all_views if s_num in v.Name and s_type in v.Name and "- für 3D DWG Export" in v.Name), None)
if item.DWGChkd and v_dwg:
doc.Export(dwg_2d_target_path, item.sDWGName, List[ElementId]([v_dwg.Id]), dwg_options)
if item.ThreeDDWGChkd:
if v_3d_dwg: doc.Export(root_path, item.s3DDWGName, List[ElementId]([v_3d_dwg.Id]), dwg_options)
else: self._log(" [ERR] 3D View für {} nicht gefunden!".format(item.sID))
if item.IFCChkd and v_ifc:
self._export_ifc_logic(item, v_ifc, root_path, adv_ifc_config)
self._cleanup_pcp(root_path)
self._log(">>> FERTIG.")
try:
if os.path.exists(root_path): os.startfile(root_path)
except: pass
def _export_ifc_logic(self, item, view, path, config):
t = Transaction(doc, "IFC Prep " + item.sID)
try:
t.Start()
# --- Ebene & Mapping Logik ---
levels = FilteredElementCollector(doc).OfClass(Level).ToElements()
current_lvl = None
for lvl in levels:
p_story = lvl.get_Parameter(BuiltInParameter.LEVEL_IS_BUILDING_STORY)
if p_story:
if lvl.Name == item.sRevitName:
p_story.Set(1)
current_lvl = lvl
lvl.Name = item.sID
else:
p_story.Set(0)
# Fixpunkt-Verschiebung
if current_lvl:
view_els = FilteredElementCollector(doc, view.Id).WhereElementIsNotElementType()
for el in view_els:
if any(x in el.Name.lower() for x in ["pyramide", "nullpunkt", "referenz", "fixpunkt"]):
for pid in [BuiltInParameter.FAMILY_LEVEL_PARAM, BuiltInParameter.SCHEDULE_LEVEL_PARAM]:
p = el.get_Parameter(pid)
if p and not p.IsReadOnly:
p.Set(current_lvl.Id)
# --- IFC EXPORT OPTIONEN ---
ops = IFCExportOptions()
if config:
config.UpdateOptions(ops, view.Id)
ops.FilterViewId = view.Id
else:
ops.FilterViewId = view.Id
ops.AddOption("VisibleElementsInView", "true")
# Export
success = doc.Export(path, item.sIFCName, ops)
if success:
self._log(" [OK] IFC Export: {}".format(item.sIFCName))
else:
self._log(" [!] IFC Export fehlgeschlagen: {}".format(item.sIFCName))
# Rollback (sehr wichtig!)
t.RollBack()
except Exception as ex:
if t.HasStarted():
t.RollBack()
self._log(" [ERR] IFC {}: {}".format(item.sID, str(ex)))
def _cleanup_pcp(self, start_path):
for root, dirs, files in os.walk(start_path):
for file in files:
if file.lower().endswith(".pcp"):
try: os.remove(os.path.join(root, file))
except: pass
def _log(self, t): self.listBox_Progress.Items.Add(str(t)); self.listBox_Progress.ScrollIntoView(str(t))
def _get_geschose_string(self, selected_items):
if not selected_items: return ""
all_sorted_items = list(self.vm.Items)
indices = sorted([all_sorted_items.index(i) for i in selected_items])
def get_name(idx): return all_sorted_items[idx].sRevitName.replace("#", "").strip()
groups, cur = [], [indices[0]]
for i in range(1, len(indices)):
if indices[i] == indices[i-1] + 1: cur.append(indices[i])
else: groups.append(cur); cur = [indices[i]]
groups.append(cur)
parts = []
for g in groups:
if len(g) == 1: parts.append(get_name(g[0]))
elif len(g) == 2: parts.append("{} und {}".format(get_name(g[0]), get_name(g[-1])))
else: parts.append("{} bis {}".format(get_name(g[0]), get_name(g[-1])))
return ", ".join(parts)
def _setup_items(self):
levels = FilteredElementCollector(doc).OfClass(Level).ToElements()
temp_list, processed = [], set()
for lvl in levels:
fn = lvl.Name.split(" - ")[0].strip()
n = fn.upper().replace("#", "").strip()
digits = re.findall(r'\d+', n)
num = int(digits[0]) if digits else 0
lt = "UG" if "UG" in n else "OG" if "OG" in n else "EG" if "EG" in n else "?"
if lt == "?" or (lt == "UG" and 3 <= num <= 5) or (lt == "OG" and num > 13): continue
if (lt, num) not in processed:
processed.add((lt, num)); temp_list.append((fn, lt, num))
temp_list.sort(key=lambda x: ({"UG":1,"EG":2,"OG":3}.get(x[1],4), -x[2] if x[1]=="UG" else x[2]))
for fn, lt, nm in temp_list:
sid = lt + "{:02d}".format(nm)
item = EbeneItem(sid, fn, lt, nm)
item.sDWGName = self.config_data.get(sid+"_DWG", "GR_{}_100_100".format(fn.replace("#","")))
item.s3DDWGName = self.config_data.get(sid+"_3D", "D_{}_100_100".format(fn.replace("#","")))
item.sIFCName = self.config_data.get(sid+"_IFC", "3D_{}_100_YYY".format(fn.replace("#","")))
item.add_PropertyChanged(self._on_item_property_changed)
self.vm.Items.Add(item)
def _save_config(self):
data = {"version": SKRIPT_VERSION}
for i in self.vm.Items:
data[i.sID+"_DWG"], data[i.sID+"_3D"], data[i.sID+"_IFC"] = i.sDWGName, i.s3DDWGName, i.sIFCName
with codecs.open(CONFIG_PATH, "w", encoding="utf-8") as f: json.dump(data, f, indent=4, ensure_ascii=False)
def _on_item_property_changed(self, s, a):
self.btn_Start.IsEnabled = any(i.EbeneChkd for i in self.vm.Items)
self.vm.IsSubfolderActive = any(i.DWGChkd for i in self.vm.Items)
def show(self): self.ui.ShowDialog()
class NotifyObject(INotifyPropertyChanged):
def __init__(self): self._property_changed_handlers = []
def add_PropertyChanged(self, h): self._property_changed_handlers.append(h)
def OnPropertyChanged(self, name):
args = PropertyChangedEventArgs(name); [h(self, args) for h in list(self._property_changed_handlers)]
class EbeneItem(NotifyObject):
def __init__(self, sID, sRevitName, l_type, num):
super(EbeneItem, self).__init__()
self.sID, self.sRevitName, self.l_type, self.num = sID, sRevitName, l_type, num
self._dwg = self._3d = self._ifc = False
self.sDWGName = self.s3DDWGName = self.sIFCName = ""
@property
def EbeneChkd(self): return self._dwg or self._3d or self._ifc
@property
def DWGChkd(self): return self._dwg
@DWGChkd.setter
def DWGChkd(self, v): self._dwg = v; self.OnPropertyChanged("DWGChkd"); self.OnPropertyChanged("EbeneChkd")
@property
def ThreeDDWGChkd(self): return self._3d
@ThreeDDWGChkd.setter
def ThreeDDWGChkd(self, v): self._3d = v; self.OnPropertyChanged("ThreeDDWGChkd"); self.OnPropertyChanged("EbeneChkd")
@property
def IFCChkd(self): return self._ifc
@IFCChkd.setter
def IFCChkd(self, v): self._ifc = v; self.OnPropertyChanged("IFCChkd"); self.OnPropertyChanged("EbeneChkd")
class MainViewModel(NotifyObject):
def __init__(self):
super(MainViewModel, self).__init__()
self.Items = ObservableCollection[object]()
self._subName, self._subActive, self._pdfMan = "DWG für Nutzerabstimmung", True, False
@property
def SubfolderName(self): return self._subName
@SubfolderName.setter
def SubfolderName(self, v): self._subName = v; self.OnPropertyChanged("SubfolderName")
@property
def IsSubfolderActive(self): return self._subActive
@IsSubfolderActive.setter
def IsSubfolderActive(self, v): self._subActive = v; self.OnPropertyChanged("IsSubfolderActive")
@property
def PdfManuellChkd(self): return self._pdfMan
@PdfManuellChkd.setter
def PdfManuellChkd(self, v): self._pdfMan = v; self.OnPropertyChanged("PdfManuellChkd")
window = ExportWindow(os.path.join(PATH_SCRIPT, 'MainWindow.xaml'))
window.show()