Community
Hi All, @jeremytammik
I've written a Python script using PyRevit that creates grids with a WPF interface, and it works perfectly. Now, I want to convert it to Dynamo while maintaining the WPF interface. However, I'm struggling to implement the _collect_input_data function, where I'm stuck handling events and exceptions from the UI to ensure the inputs are correct and collecting them in self.data.
Here my original code:
# -*- coding: UTF-8 -*-
from pyrevit import forms, revit
import wpf, clr
clr.AddReference("PresentationFramework")
clr.AddReference("System.Xml")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
from System.IO import StringReader
from System.Windows.Markup import XamlReader
from System.Windows import Window
from Autodesk.Revit.DB import *
from string import ascii_uppercase
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
app = __revit__.Application
class Grids(Window):
LAYOUT = '''
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Grids"
Height="Auto"
Width="300"
SizeToContent ="Height"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen">
<StackPanel Margin="10">
<!--Input LX-->
<StackPanel Margin="0,0,0,10">
<TextBlock Text ="Longueur Total suivant X (m):" Width="170" Height="25" HorizontalAlignment="Left" FontSize="12" FontWeight="Bold" />
<DockPanel Width="200" HorizontalAlignment="Left" >
<TextBlock Text =" Lx =" Width="50" Height="25" Margin="0,0,5,0" />
<TextBox x:Name="input_Lx" Width="75" Margin="0,0,5,0" />
<TextBlock Text="m" Width="25" HorizontalAlignment="Left" />
</DockPanel>
</StackPanel>
<!--Input LY-->
<StackPanel Margin="0,0,0,10">
<TextBlock Text ="Longueur Total suivant Y (m):" Width="170" Height="25" HorizontalAlignment="Left" FontSize="12" FontWeight="Bold" />
<DockPanel Width="200" HorizontalAlignment="Left" >
<TextBlock Text =" Ly =" Width="50" Height="25" Margin="0,0,5,0" />
<TextBox x:Name="input_Ly" Width="75" Margin="0,0,5,0" />
<TextBlock Text="m" Width="25" HorizontalAlignment="Left" />
</DockPanel>
</StackPanel>
<!--Grid Extension -->
<StackPanel Margin="0,0,0,10">
<TextBlock Text ="Extension des Axes X, Y (m):" Width="170" Height="25" HorizontalAlignment="Left" FontSize="12" FontWeight="Bold" />
<DockPanel Width="200" HorizontalAlignment="Left" >
<TextBlock Text =" e =" Width="50" Height="25" Margin="0,0,5,0" />
<TextBox x:Name="input_extension" Width="75" Margin="0,0,5,0" />
<TextBlock Text="m" Width="25" HorizontalAlignment="Left" />
</DockPanel>
</StackPanel>
<!--Spacing along X axis -->
<StackPanel Margin="0,0,0,10">
<TextBlock Text ="Espacement suivant X (m):" Width="170" Height="25" HorizontalAlignment="Left" FontSize="12" FontWeight="Bold" />
<DockPanel Width="200" HorizontalAlignment="Left" >
<TextBlock Text =" dx =" Width="50" Height="25" Margin="0,0,5,0" />
<TextBox x:Name="input_dx" Width="75" Margin="0,0,5,0" />
<TextBlock Text="m" Width="25" HorizontalAlignment="Left" />
</DockPanel>
</StackPanel>
<!--Spacing along Y axis -->
<StackPanel Margin="0,0,0,10">
<TextBlock Text ="Espacement suivant Y (m):" Width="170" Height="25" HorizontalAlignment="Left" FontSize="12" FontWeight="Bold" />
<DockPanel Width="200" HorizontalAlignment="Left" >
<TextBlock Text =" dy =" Width="50" Height="25" Margin="0,0,5,0" />
<TextBox x:Name="input_dy" Width="75" Margin="0,0,5,0" />
<TextBlock Text="m" Width="25" HorizontalAlignment="Left" />
</DockPanel>
</StackPanel>
<!--bubbles symbol -->
<StackPanel>
<TextBlock Text="Choix de symbols pour Lx, Ly:" FontWeight="Bold" Margin="0,0,0,10" />
<!-- Symbol Type for Y Axis -->
<DockPanel Margin="0,0,0,10" >
<TextBlock Text="Type de symbole Sx:" />
<ComboBox x:Name="Symbol_x" HorizontalAlignment="Right" SelectionChanged="Symbol_x_SelectionChanged" Width="110">
<ComboBoxItem Content="1 2 3 ..." />
<ComboBoxItem Content="A B C ..." />
</ComboBox>
</DockPanel>
<!-- Symbol Type for Y Axis -->
<DockPanel Margin="0,0,0,10">
<TextBlock Text="Type de symbole Sy:" />
<TextBox x:Name="Symbol_y" Width="110" HorizontalAlignment="Right"/>
</DockPanel>
</StackPanel>
<!--Separator, OK and Cancel Button-->
<Separator Margin="0,0,0,10"/>
<DockPanel HorizontalAlignment="Center">
<Button Content="OK" Width="75" Height="25" Margin="0,0,10,0" Click="OK_Clicked" />
<Button Content="Cancel" Width="75" Height="25" Click="Cancel_Clicked" />
</DockPanel>
</StackPanel>
</Window>'''
def __init__(self):
wpf.LoadComponent(self, StringReader(Grids.LAYOUT))
self.data = {}
self.ShowDialog()
def Symbol_x_SelectionChanged(self, sender, e):
selected_item = self.Symbol_x.SelectedItem
if selected_item.Content == "1 2 3 ...":
self.Symbol_y.Text = self.Symbol_x.Items[1].Content.ToString()
else:
self.Symbol_y.Text = self.Symbol_x.Items[0].Content.ToString()
def _collect_input_data(self):
"""Collect and convert input data from the UI"""
# Identify which key has missing or invalid input and alert the user
if not self.input_Lx.Text:
forms.alert("Please enter a valid value for : Lx")
elif not self.input_Ly.Text:
forms.alert("Please enter a valid value for : Ly")
elif not self.input_extension.Text:
forms.alert("Please enter a valid value for : e")
elif not self.input_dx.Text:
forms.alert("Please enter a valid value for : dx")
elif not self.input_dy.Text:
forms.alert("Please enter a valid value for : dy")
else:
try:
self.data = {
"Lx": _input_to_meters(self.input_Lx.Text),
"Ly": _input_to_meters(self.input_Ly.Text),
"e": _input_to_meters(self.input_extension.Text),
"dx": _input_to_meters(self.input_dx.Text),
"dy": _input_to_meters(self.input_dy.Text),
}
except (ValueError, AttributeError):
forms.alert("Invalid input or missing selections. Please correct and try again.")
def symbols_alpha(self, length_x, length_y):
"""Generate Sx and Sy symbols based on lengths of grids"""
Sx = []
Sy = []
if self.Symbol_x.SelectedItem == self.Symbol_x.Items[0]:
for i in range(length_y):
if i < len(ascii_uppercase):
Sy.append(ascii_uppercase[i])
Sx = []
else:
for i in range(length_x):
if i < len(ascii_uppercase):
Sx.append(ascii_uppercase[i])
Sy = []
return Sx, Sy
def OK_Clicked(self, sender, e):
self._collect_input_data()
if not self.data:
return
n1 = int(self.data["Lx"] // self.data["dx"])
if not isclose(self.data["Lx"], n1 * self.data["dx"]):
length_x = n1 + 2
else:
length_x = n1 + 1
print(length_x)
n2 = int(self.data["Ly"] // self.data["dy"])
if not isclose(self.data["Ly"], n2 * self.data["dy"]):
length_y = n2 + 2
else:
length_y = n2 + 1
# Populate Sx and Sy dynamically based on grid lengths
self.data["Sx"], self.data["Sy"] = self.symbols_alpha(length_x, length_y)
print(self.data["Sx"])
print(self.data["Sy"])
self.Close()
def Cancel_Clicked(self, sender, e):
self.data = {}
self.Close()
def _input_to_meters(value):
"""Convert a textual value to meters."""
return UnitUtils.ConvertToInternalUnits(float(value), UnitTypeId.Meters)
def isclose(a, b, rel_tol=1e-9, abs_tol=0.0):
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
def create_lines(lx, ly, d, e, y_axis=False):
"""Create lines along X, Y axis """
if y_axis:
lx, ly = ly, lx
n = lx // d
points_X = []
i = 0
while i <= int(n):
points_X.append(d * i - lx / 2)
i += 1
if not isclose(lx, n * d):
points_X.append(lx / 2)
points_Y = [-ly / 2 - e, ly / 2 + e]
if y_axis:
return [Line.CreateBound(*(XYZ(x, y, 0) for x in points_Y)) for y in points_X]
return [Line.CreateBound(*(XYZ(x, y, 0) for y in points_Y)) for x in points_X]
def create_grids(lines_x, lines_y, Sx, Sy, doc):
with Transaction(doc, "Create Grids") as t:
t.Start()
grids_X = [Grid.Create(doc, line) for line in lines_x]
print(len(grids_X))
grids_Y = [Grid.Create(doc, line) for line in lines_y]
print(len(grids_Y))
if Sx: # If Sx is populated, assign names to grids_X
print(len(Sx))
for grid, symbol in zip(grids_X, Sx):
grid.Name = symbol
for idx, grid in enumerate(grids_Y):
grid.Name = str(idx+1)
if Sy: # If Sy is populated, assign names to grids_Y
print(len(Sy))
for grid, symbol in zip(grids_Y, Sy):
grid.Name = symbol
for idx, grid in enumerate(grids_X):
grid.Name = str(idx+1)
t.Commit()
def main(doc=None):
doc = doc or revit.doc
form = Grids()
if not form.data:
return
data = form.data
lines_x = create_lines(data["Lx"], data["Ly"], data["dx"], data["e"])
lines_y = create_lines(data["Lx"], data["Ly"], data["dy"], data["e"], y_axis=True)
create_grids(lines_x, lines_y, data["Sx"], data["Sy"], doc)
if __name__ == "__main__":
main()
Any help would be appreciated
Can't find what you're looking for? Ask the community or share your knowledge.