Comment gérer les états des sous projets en Python avec l'API Revit ?
MAJ 21/05/2025 (version ACC + WPF) voir ici 🔗
Suite à une idée d'amélioration, voici un exemple de script permettant de gérer l'ouverture et la fermeture des sous-projets d'une maquette liée (avec une interface Winform)
Le seul moyen à ce jour pour changer les états des sous-projets d'une maquette liée via l'API Revit, c'est de recharger le lien avec une configuration via la méthode LoadFrom().
Dans l'exemple ci-après, on crée une configuration par défaut où les sous projets "utilisateur" sont fermés. Puis, on ouvre ceux que l'on souhaite avec la méthode Open()
WorksetConfiguration(WorksetConfigurationOption.CloseAllWorksets)
Pour ceux qu'ils souhaitent que ce script peut être encore amélioré avec l'intégration d'un TreeNode regroupant l'ensemble des maquettes liées (mode collaboratif activé), ou chaque nœud principal serait une maquette liée.
J'en profite pour y introduire un bref exemple de gestion multi-langage sur une Interface User grâce à l'API Revit
if app.Language == Autodesk.Revit.ApplicationServices.LanguageType.French
import clr
import sys
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
#import net library
clr.AddReference('System')
from System.Collections.Generic import List
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
#Get Important vars
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
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 WksetLnkUtils(Form):
def __init__(self):
rvtLinkInst = FilteredElementCollector(doc).OfClass(RevitLinkInstance)
self._dictRvtInstance = {}
for x in rvtLinkInst:
_lkdoc = x.GetLinkDocument()
if _lkdoc.IsWorkshared:
self._dictRvtInstance[x.Name] = x
self._dictWkset = {}
self.rvtRvtlinkType = None
self.out = None
self.InitializeComponent()
def InitializeComponent(self):
self._groupBox1 = System.Windows.Forms.GroupBox()
self._checkedListBox1 = System.Windows.Forms.CheckedListBox()
self._buttonOpen = System.Windows.Forms.Button()
self._buttonClose = System.Windows.Forms.Button()
self._comboBoxRvtLink = System.Windows.Forms.ComboBox()
self._groupBox2 = System.Windows.Forms.GroupBox()
self._groupBox1.SuspendLayout()
self._groupBox2.SuspendLayout()
self.SuspendLayout()
#
# groupBox1
self._groupBox1.Controls.Add(self._checkedListBox1)
self._groupBox1.Location = System.Drawing.Point(30, 123)
self._groupBox1.Name = "groupBox1"
self._groupBox1.Size = System.Drawing.Size(361, 423)
self._groupBox1.TabIndex = 0
self._groupBox1.TabStop = False
self._groupBox1.Text = "Select Workset"
#
# checkedListBox1
self._checkedListBox1.FormattingEnabled = True
self._checkedListBox1.Items.Clear()
self._checkedListBox1.Location = System.Drawing.Point(16, 42)
self._checkedListBox1.Name = "checkedListBox1"
self._checkedListBox1.Size = System.Drawing.Size(326, 361)
self._checkedListBox1.TabIndex = 0
#
# buttonOpen
self._buttonOpen.Location = System.Drawing.Point(60, 560)
self._buttonOpen.Name = "buttonOpen"
self._buttonOpen.Size = System.Drawing.Size(300, 40)
self._buttonOpen.TabIndex = 1
if app.Language == Autodesk.Revit.ApplicationServices.LanguageType.French:
self._buttonOpen.Text = "Ouvrir les sous-projets selectionnés \n(les autres seront fermés)"
else:
self._buttonOpen.Text = "Open Selected Worksets (others will be close)"
self._buttonOpen.UseVisualStyleBackColor = True
self._buttonOpen.Click += self.ButtonOpenClick
#
# buttonClose
self._buttonClose.Location = System.Drawing.Point(60, 620)
self._buttonClose.Name = "buttonClose"
self._buttonClose.Size = System.Drawing.Size(300, 40)
self._buttonClose.TabIndex = 1
if app.Language == Autodesk.Revit.ApplicationServices.LanguageType.French:
self._buttonClose.Text = "Fermer les sous-projets selectionnés \n(les autres seront ouverts)"
else:
self._buttonClose.Text = "Close Selected Worksets (others will be open)"
self._buttonClose.UseVisualStyleBackColor = True
self._buttonClose.Click += self.ButtonCloseClick
#
# comboBoxRvtLink
self._comboBoxRvtLink.Items.AddRange(System.Array[System.Object](self._dictRvtInstance.keys()))
self._comboBoxRvtLink.FormattingEnabled = True
self._comboBoxRvtLink.Location = System.Drawing.Point(19, 41)
self._comboBoxRvtLink.Name = "comboBoxRvtLink"
self._comboBoxRvtLink.Size = System.Drawing.Size(323, 24)
self._comboBoxRvtLink.TabIndex = 2
self._comboBoxRvtLink.SelectedIndexChanged += self.ComboBoxRvtLinkSelectedIndexChanged
#
# groupBox2
self._groupBox2.Controls.Add(self._comboBoxRvtLink)
self._groupBox2.Location = System.Drawing.Point(30, 17)
self._groupBox2.Name = "groupBox2"
self._groupBox2.Size = System.Drawing.Size(361, 89)
self._groupBox2.TabIndex = 3
self._groupBox2.TabStop = False
self._groupBox2.Text = "Select Revit Link"
#
# MainForm
self.ClientSize = System.Drawing.Size(430, 700)
self.Controls.Add(self._groupBox2)
self.Controls.Add(self._buttonClose)
self.Controls.Add(self._buttonOpen)
self.Controls.Add(self._groupBox1)
self.Name = "WksetLnkUtils"
self.Text = "Manage linked model Worksets"
self._groupBox1.ResumeLayout(False)
self._groupBox2.ResumeLayout(False)
self.ResumeLayout(False)
def setLinkstatut(self, wksetsId):
if self.rvtRvtlinkType is not None:
#create WorksetConfiguration with CloseAllWorksets by default
wkstConfig = WorksetConfiguration(WorksetConfigurationOption.CloseAllWorksets)
lstWksetId = List[WorksetId](wksetsId)
wkstConfig.Open(lstWksetId)
loada = self.rvtRvtlinkType.LoadFrom(self.mpath, wkstConfig)
self.out = loada.LoadResult
wkstConfig.Dispose()
def ButtonOpenClick(self, sender, e):
wksetsId = [ self._dictWkset.get(wksetnam) for wksetnam in self._checkedListBox1.CheckedItems]
self.setLinkstatut(wksetsId)
def ButtonCloseClick(self, sender, e):
self.wksetsId = []
#get items are not checked for open others Worksets
setDiff = set(self._checkedListBox1.Items) - set(self._checkedListBox1.CheckedItems)
wksetsId = [ self._dictWkset.get(wksetnam) for wksetnam in setDiff]
self.setLinkstatut(wksetsId)
def ComboBoxRvtLinkSelectedIndexChanged(self, sender, e):
rvtLinkInstance = self._dictRvtInstance.get(sender.Text)
self.rvtRvtlinkType = doc.GetElement(rvtLinkInstance.GetTypeId())
_linkdoc = rvtLinkInstance.GetLinkDocument()
_worksetTable = _linkdoc.GetWorksetTable()
self.mpath = _linkdoc.GetWorksharingCentralModelPath()
lstPreview = WorksharingUtils.GetUserWorksetInfo(self.mpath)
self._dictWkset = {item.Name : _worksetTable.GetWorkset(item.Id).Id for item in lstPreview }
self._checkedListBox1.Items.Clear()
self._checkedListBox1.Items.AddRange(System.Array[System.Object](self._dictWkset.keys()))
objForm = WksetLnkUtils()
objForm.Show()
OUT = 0
- conversion en WPF
- compatibles liens ACC
- ajout de style (visualisation)
- compatible seulement avec IronPython3 et PythonNet3
# Python Script | Main
import clr
import sys
import System
from System.Collections.Generic import List
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
clr.AddReference('RevitAPI')
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB import *
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
#Get Important vars
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
clr.AddReference('System.Data')
from System.Data import *
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.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
from System.Windows.Data import IValueConverter
import time
import traceback
class MainWindow(Window):
__slot__ = "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"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
x:Name="MainWindow"
Title="Manage linked model Worksets"
MinHeight="700" MinWidth="430"
Width="430" Height="720"
ResizeMode="CanResizeWithGrip">
<Window.Resources>
<TextBlock x:Key="tooltipTextBlock" TextWrapping="Wrap">
<Run>Green → Open Workset</Run>
<LineBreak/>
<Run>Red → Closed Workset</Run>
<LineBreak/>
<Run>use 'Shift + click' for multiple selection</Run>
</TextBlock>
</Window.Resources>
<Grid>
<GroupBox
Header="Select Revit Link"
Grid.Column="0" Grid.Row="0"
HorizontalAlignment="Stretch" VerticalAlignment="Top"
Margin="30,17,30,0" Height="70">
<ComboBox
Name="comboBoxRvtLink" Background="LightBlue"
DisplayMemberPath="Name"
VerticalAlignment="Top"
Margin="5,10,5,5"
HorizontalAlignment="Stretch" />
</GroupBox>
<GroupBox
Header="Select Workset"
Grid.Column="0" Grid.Row="0"
HorizontalAlignment="Stretch"
ToolTip="{StaticResource tooltipTextBlock}"
Margin="30,102,30,116">
<ListView
Name="checkedListBox1"
Grid.Column="0" Grid.Row="0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
SelectionMode="Extended"
ToolTip="{StaticResource tooltipTextBlock}"
Margin="5,10,5,5">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected">
<Setter.Value>
<Binding Path="IsChecked"
Mode="OneWayToSource"
UpdateSourceTrigger="PropertyChanged"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsOpen}" Value="True">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsOpen}" Value="False">
<Setter Property="Background" Value="LightCoral" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<CheckBox VerticalAlignment="Center" Margin="0,0,0,0" IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}">
<CheckBox.Content>
<TextBlock Text = "{Binding Name}"/>
</CheckBox.Content>
</CheckBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</GroupBox>
<Button
Name="buttonOpen"
Content="Open the selected worksets\n(the others will be closed)"
Height="40" VerticalAlignment="Bottom"
Margin="30,0,30,68" />
<Button
Name="buttonClose"
Content="Close the selected worksets\n(the others will be opened)"
Height="40" VerticalAlignment="Bottom"
Margin="30,0,30,15" />
</Grid>
</Window>'''
def __init__(self):
super().__init__()
xr = XmlReader.Create(StringReader(MainWindow.string_xaml))
self.winLoad = XamlReader.Load(xr)
#
self._rvtLinkInst = FilteredElementCollector(doc).OfClass(RevitLinkInstance)\
.Where(System.Func[DB.Element, System.Boolean](lambda x : doc.GetElement(x.GetTypeId()).GetLinkedFileStatus() == LinkedFileStatus.Loaded))\
.ToList()
self.dt = DataTable("CustomData")
self.link_doc = None
self.InitializeComponent()
def InitializeComponent(self):
#
self.checkedListBox1 = LogicalTreeHelper.FindLogicalNode(self.winLoad, "checkedListBox1")
self.comboBoxRvtLink = LogicalTreeHelper.FindLogicalNode(self.winLoad, "comboBoxRvtLink")
self.comboBoxRvtLink.ItemsSource = self._rvtLinkInst
self.comboBoxRvtLink.SelectionChanged += self.ComboBoxRvtLinkSelectedIndexChanged
#
self.buttonClose = LogicalTreeHelper.FindLogicalNode(self.winLoad, "buttonClose")
self.buttonClose.Click += self.ButtonCloseClick
#
self.buttonOpen = LogicalTreeHelper.FindLogicalNode(self.winLoad, "buttonOpen")
self.buttonOpen.Click += self.ButtonOpenClick
#
def ButtonCloseClick(self, sender, e):
try:
wkset_not_selected = [row["Workset"] for row in self.dt.Rows if not row["IsChecked"]]
wksetsId = [wkset.Id for wkset in wkset_not_selected]
self.SetLinkStatus(wksetsId)
self.UpdateWorksetsList()
except Exception as ex:
print(traceback.format_exc())
def ButtonOpenClick(self, sender, e):
try:
selected_worksets = [row["Workset"] for row in self.dt.Rows if row["IsChecked"]]
wksetsId = [wkset.Id for wkset in selected_worksets]
self.SetLinkStatus(wksetsId)
self.UpdateWorksetsList()
except Exception as ex:
print(traceback.format_exc())
def GetDataFromLink(self):
try:
rvtLinkInstance = self.comboBoxRvtLink.SelectedItem
self.rvtRvtlinkType = doc.GetElement(rvtLinkInstance.GetTypeId())
self.link_doc = rvtLinkInstance.GetLinkDocument()
return self.link_doc
except Exception as ex:
print(traceback.format_exc())
def ComboBoxRvtLinkSelectedIndexChanged(self, sender, e):
try:
self.UpdateWorksetsList()
except Exception as ex:
print(traceback.format_exc())
def UpdateWorksetsList(self):
try:
self.GetDataFromLink() # populate self.mpath
self.dt = DataTable("CustomData")
# Create columns
self.dt.Columns.Add("Workset", Workset)
self.dt.Columns.Add("Name", System.String)
self.dt.Columns.Add("IsChecked", System.Boolean)
self.dt.Columns.Add("IsOpen", System.Boolean)
if self.link_doc is not None:
userWorksets = FilteredWorksetCollector(self.link_doc).OfKind(WorksetKind.UserWorkset).ToWorksets()
lst_workset_infos = sorted(userWorksets, key=lambda x : x.Name)
# add rows
for wkset in lst_workset_infos:
if wkset is not None:
self.dt.Rows.Add(wkset, wkset.Name, False, wkset.IsOpen)
#
print("change checkedListBox1")
self.checkedListBox1.ClearValue(ItemsControl.ItemsSourceProperty)
self.checkedListBox1.ItemsSource = self.dt.DefaultView
except Exception as ex:
print(traceback.format_exc())
def SetLinkStatus(self, workset_ids):
if self.rvtRvtlinkType is not None:
dict_extRef = self.rvtRvtlinkType.GetExternalResourceReferences()
externalResourceReference = None
for item in dict_extRef:
externalResourceType = item.Key
externalResourceReference = item.Value
print(externalResourceReference.InSessionPath )
break
if externalResourceReference is not None:
wkstConfig = WorksetConfiguration(WorksetConfigurationOption.CloseAllWorksets)
wkstConfig.Open(List[WorksetId](workset_ids))
load_result = self.rvtRvtlinkType.LoadFrom(externalResourceReference, wkstConfig)
self.out = load_result.LoadResult
wkstConfig.Dispose()
objWindow = MainWindow()
objWindow.winLoad.Show()
OUT = 0

Excellent. Je vais l'essayer de suite. Merci
RépondreSupprimerAdding
RépondreSupprimer# -*- coding: latin-1 -*-
and it works well in a pyrevit toolbar
Bonjour Cyril, excellent script! Quelles sont les modifications à faire pour que ce script fonctionne avec des maquettes hébergées sur le BIM360 (cloud)? De mon côté le script fonctionne seulement sur les maquettes sur un réseau "local". Merci!
RépondreSupprimerBonjour,
SupprimerActuellement je n'ai pas accès à BIM360, donc je ne sais
(je suppose qu'il faut utiliser la méthode 'LoadFrom Method (ExternalResourceReference, WorksetConfiguration)')
Je mettrai l'article à jour si le cas se présente