After a few days’ break, I was finally able to implement the del_Click event, which allows me to delete the selected FootingType instance from the combobox dropdown list. However, for now, it’s still difficult for me to implement delAll_Click, which would allow deleting all FootingType instances.
here the final code:
Note: It is necessary to add the path for the lib subfolder within the attached Footings folder for the code to work as expected.
Footings.7z (1.5 KB)
Footings
import clr
import sys
sys.path.append(r"C:\Users\nono\OneDrive\Desktop\Footings\lib")
import System
import math
from System import Array
from System.Collections.Generic import List, KeyValuePair
from System.Collections.ObjectModel import ObservableCollection
# Import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB.Structure import StructuralType
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
clr.AddReference("IronPython.Wpf")
clr.AddReference('System.Core')
clr.AddReference('System.Xml')
clr.AddReference('PresentationCore')
clr.AddReference('PresentationFramework')
clr.AddReference('System.Windows.Forms') # For MessageBox
from System.IO import StringReader
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application
from System.ComponentModel import INotifyPropertyChanged
from System.ComponentModel import PropertyChangedEventArgs
from System.Windows import Window
from System.Windows.Forms import MessageBox
import wpf
import itertools
import traceback
from _mySymbols import FootingType
from _myMaterials import ConcreteMaterial, get_or_create_concrete_material
class ViewModelBase(INotifyPropertyChanged):
def __init__(self):
self.propertyChangedHandlers = []
def RaisePropertyChanged(self, propertyName):
args = PropertyChangedEventArgs(propertyName)
for handler in self.propertyChangedHandlers:
handler(self, args)
def add_PropertyChanged(self, handler):
self.propertyChangedHandlers.append(handler)
def remove_PropertyChanged(self, handler):
self.propertyChangedHandlers.remove(handler)
class Footings_VM(ViewModelBase):
def __init__(self, Position):
ViewModelBase.__init__(self)
self._position = Position
self._types = ObservableCollection[FootingType]()
self._types.Add(FootingType(0, 0, 0))
self._selected_type = self._types[0]
@property
def Position(self):
return self._position
@Position.setter
def Position(self, value):
self._position = value
self.RaisePropertyChanged('Position')
@property
def LstTypes(self):
return self._types
@LstTypes.setter
def LstTypes(self, lst_value):
self._types = ObservableCollection[object](lst_value)
self.RaisePropertyChanged('LstTypes')
@property
def SelectedType(self):
return self._selected_type
@SelectedType.setter
def SelectedType(self, value):
self._selected_type = value
self.RaisePropertyChanged('SelectedType')
def AddType(self, footing_type):
self._types.Add(footing_type)
self.RaisePropertyChanged('LstTypes')
def _grids_symbols():
symbols = []
grids = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Grids) \
.WhereElementIsNotElementType().ToElements()
for gd_a, gd_b in itertools.combinations(grids, 2):
symA = gd_a.Name
symB = gd_b.Name
if gd_a.Curve.Intersect(gd_b.Curve) == SetComparisonResult.Overlap:
if symA.isalpha() and symB.isdigit():
symbol = "{}-{}".format(symA, symB)
symbols.append(symbol)
else:
symbol = "{}-{}".format(symB, symA)
symbols.append(symbol)
return sorted(symbols)
class MyWindow(Window):
xaml_str = '''
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Footings"
Height="Auto" Width="460"
SizeToContent="Height"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="85*"/>
<RowDefinition Height="5*"/>
<RowDefinition Height="5*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="30*"/>
<ColumnDefinition Width="20*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Grid.ColumnSpan="2" Text="Caracteristiques des semelles" FontWeight="Bold" FontSize="11" />
<DataGrid x:Name="Symbols" Grid.Row="1" Grid.RowSpan="2" AutoGenerateColumns="False" ItemsSource="{Binding}"
Background="Transparent" GridLinesVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Header="Position" Binding="{Binding Position}" Width="*" />
<DataGridTemplateColumn Header="Type de Semelle" Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="Types"
ItemsSource="{Binding LstTypes}"
SelectedItem="{Binding SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="Types_SelectionChanged"
DisplayMemberPath="Name">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid >
<StackPanel Grid.Row="1" Grid.Column="1">
<Label Content="Longueur :" VerticalAlignment="Center" Width="75" HorizontalAlignment="Left"/>
<DockPanel>
<TextBox x:Name="long_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
<Label Content="cm"/>
</DockPanel>
<Label Content="Largeur :" VerticalAlignment="Center" Width="75" HorizontalAlignment="Left"/>
<DockPanel>
<TextBox x:Name="larg_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
<Label Content="cm"/>
</DockPanel>
<Label Content="Epaisseur :" VerticalAlignment="Center" Width="75" HorizontalAlignment="Left"/>
<DockPanel>
<TextBox x:Name="ep_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
<Label Content="cm"/>
</DockPanel>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2" Orientation="Vertical">
<Button Content="Ajouter" Width="85" Click="Add_Click" VerticalAlignment="Top" Margin="0,30,0,10"/>
<Button Content="Supprimer" Width="85" Click="del_Click" VerticalAlignment="Top" Margin="0,20,0,10"/>
<Button Content="Supprimer tous" Width="85" VerticalAlignment="Top" Margin="0,20,0,10"/>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2">
<Label Content="Contrainte de Béton :" VerticalAlignment="Center"/>
<DockPanel>
<TextBox x:Name="contrainte_value" Height="20" Width="75" VerticalContentAlignment="Center" Margin="5,0,0,0"/>
<Label Content="MPA" />
</DockPanel>
</StackPanel>
<StackPanel Grid.Row="3" Grid.ColumnSpan="3" Orientation="Horizontal" VerticalAlignment="Center" Width="210" Margin="0,5,0,0">
<Button x:Name="Appliquer" Click="Appliquer_Click" Content="Appliquer" Width="100"/>
<Button x:Name="Fermer" Click="Fermer_Click" Content="Fermer" Width="100" Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</Window>
'''
def __init__(self):
wpf.LoadComponent(self, StringReader(MyWindow.xaml_str))
self.Data = {}
self.DataTypes = ObservableCollection[Footings_VM]()
self.symbols = _grids_symbols()
for s in self.symbols:
foot = Footings_VM(s)
self.DataTypes.Add(foot)
self.DataContext = self.DataTypes
self.selected_position = None
def Types_SelectionChanged(self, sender, e):
if sender.SelectedItem is not None:
self.selected_position = sender.SelectedItem
def Add_Click(self, sender, e):
try:
long_value = float(self.long_value.Text)
larg_value = float(self.larg_value.Text)
ep_value = float(self.ep_value.Text)
foot = FootingType(long_value, larg_value, ep_value)
for vm in self.DataTypes:
vm.AddType(foot)
self.long_value.Clear()
self.larg_value.Clear()
self.ep_value.Clear()
except ValueError:
MessageBox.Show("Please enter valid numeric values for dimensions.", "Error")
def del_Click(self, sender, e):
if self.selected_position is not None:
index_to_remove = None
for item in self.DataTypes:
if self.selected_position in item.LstTypes:
index_to_remove = item.LstTypes.IndexOf(self.selected_position)
break
if index_to_remove is not None:
for item in self.DataTypes:
if index_to_remove < len(item.LstTypes):
item.LstTypes.RemoveAt(index_to_remove)
if len(item.LstTypes) == 0:
default_type = FootingType(0, 0, 0)
item.LstTypes.Add(default_type)
item.SelectedType = default_type
elif item.SelectedType is None or item.SelectedType == self.selected_position:
item.SelectedType = item.LstTypes[0]
self.selected_position = None
def _collect_input_data(self):
for item in self.DataTypes:
if len(item.LstTypes) < 2:
MessageBox.Show("Please add at least one footing type", "Error")
return False
elif item.SelectedType is None or item.SelectedType.H == 0:
MessageBox.Show("Please select a footing type for each Position", "Error")
return False
if not self.contrainte_value.Text:
MessageBox.Show("Please enter a valid value for : contrainte", "Error")
return False
try:
self.Data["foot"] = [vm.SelectedType for vm in self.DataTypes if vm.SelectedType.Name != "??"]
self.Data["contrainte"] = self.contrainte_value.Text
return True
except ValueError:
MessageBox.Show("Invalid input detected. Please ensure all inputs are numeric where required.", "Error")
return False
def Appliquer_Click(self, sender, e):
if not self._collect_input_data():
return
if not self.Data:
return
self.Data["lvl"] = get_foundation_level()
self.Close()
def Fermer_Click(self, sender, e):
self.Close()
def get_foundation_level():
levels = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels) \
.WhereElementIsNotElementType().ToElements()
return min(levels, key=lambda l: l.Elevation)
def get_grid_intersection_points(grids):
lstPts = []
results = clr.Reference[IntersectionResultArray]()
for gd_a, gd_b in itertools.combinations(grids, 2):
curveA = gd_a.Curve
curveB = gd_b.Curve
vectA = curveA.ComputeDerivatives(0.5, True).BasisX
vectB = curveB.ComputeDerivatives(0.5, True).BasisX
if abs(vectA.CrossProduct(vectB).Z) < 0.01:
continue
result = curveA.Intersect(curveB, results)
if result == SetComparisonResult.Overlap:
interResult = results.Value
lstPts.append(interResult[0].XYZPoint)
lstPts.sort(key=lambda p: (p.Y, p.X))
return lstPts
def CreateFootingTypes(LstTypes, doc):
all = list(LstTypes)
created_types = []
symbols = FilteredElementCollector(doc) \
.WhereElementIsElementType() \
.OfCategory(BuiltInCategory.OST_StructuralFoundation) \
.ToElements()
footing_names = {"Base rectangulaire", "M_Base-Rectangulaire"}
existing_types_dict = {Element.Name.GetValue(fs): fs for fs in symbols if fs.FamilyName in footing_names}
created_types_dict = {}
with Transaction(doc, "Create Footing Types") as t:
t.Start()
for ft in all:
if ft.Name in existing_types_dict:
created_types.append(existing_types_dict[ft.Name])
elif ft.Name in created_types_dict:
created_types.append(created_types_dict[ft.Name])
else:
first_type = next(iter(existing_types_dict.values()), None)
if not first_type:
MessageBox.Show("No existing footing family found.", "Error")
return None
new_type = first_type.Duplicate(ft.Name)
new_type.LookupParameter("Largeur").Set(ft.H / 30.48)
new_type.LookupParameter("Longueur").Set(ft.W / 30.48)
new_type.LookupParameter("Epaisseur de fondation").Set(ft.T / 30.48)
created_types.append(new_type)
created_types_dict[ft.Name] = new_type
t.Commit()
return created_types
def Create_footings(points, family_symbol, level, material_id, doc):
with Transaction(doc, "Create Footings") as t:
t.Start()
for p, s in zip(points, family_symbol):
foot = doc.Create.NewFamilyInstance(p, s, level, StructuralType.Footing)
material_param = foot.get_Parameter(BuiltInParameter.STRUCTURAL_MATERIAL_PARAM)
if material_param:
material_param.Set(material_id)
t.Commit()
# Main execution
grids = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Grids) \
.WhereElementIsNotElementType().ToElements()
foundation = MyWindow()
foundation.ShowDialog()
Data = foundation.Data
Data = foundation.Data
family_symbol = CreateFootingTypes(Data["foot"], doc)
concrete_material = ConcreteMaterial(Data["contrainte"])
material_id = get_or_create_concrete_material(concrete_material, doc)
if family_symbol:
points = get_grid_intersection_points(grids)
Create_footings(points, family_symbol, Data["lvl"], material_id, doc)
OUT = 0
Thanks.