Покажу, как с помощью Динамо и Питона поменять имена семействам и их типоразмерам. Получим семейства из модели, сгенерируем имена для семейств и типов, а потом запишем их с помощью несложного кода в Питоне.

Алгоритм обработки
Это реальный пример работы над одним из заказов. Мне нужно актуализировать семейства под шаблоны АДСК. Для этого удаляю левые общие параметры, нужные общие преобразовываю в параметры семейства, потому что как общие мне эти параметры не нужны. Все эти операции делаю МодПлюсом, плагином «Параметры».
Но теперь задача, которую в МодПлюсе мне решать неудобно, мне проще накидать скрипт в Динамо. Для этого загружаю семейства в пустой проект. Работать будет только с типоразмерами семейств.
Итак, алгоритм следующий:
- Получаю все типоразмеры семейств из проекта, оставляю только нужные.
- Получаю имена семейств и типоразмеров.
- Преобразовываю их в нужные мне списки имён.
- Записываю Питоном новые имена.
Звучит просто, на деле тоже относительно несложно. Погнали.
Получаем все типоразмеры
Как получать элементы в Динамо, можете почитать в отдельной обзорной статье:
Нам нужны Фэмили Симболы, если по-простому, то это типоразмеры, которые загружены в проект, но необязательно размещены в модели. Получить их можно нодом Element Types, в более новых версиях Ревита этот нод называется Element Classes.
Это выпадающий список, в нём выбираем FamilySymbol. Далее подаем на нод All Elements of Type, в новых версиях —


Я специально загрузил семейства в пустой проект, у меня для этого есть специальный шаблон проекта, в нём всё удалено. Поэтому в списке типов есть только один лишний элемент — какая-то стена. Поэтому можно либо делать стандартную фильтрацию по имени типа, либо просто отбросить лишний элемент нодом List.Deconstruct. Я знаю структуру своего списка, поэтому пойду вторым путём. В более сложных случаях пошёл бы по первому.
Нод Deconstruct берёт список и выдаёт первый элемент списка и всё остальное. Вот с этим всем остальным и буду работать дальше. Нужно получить семейство с типоразмера, а также имена семейств и типоразмеров. У меня так получилось, что имена семейств и типов совпадают.
Беру нод FamilyType.Family, им получу семейство с типоразмера. В Код Блоке напишу a.Name — это быстрый способ получить имя элемента. Получаю список типов, семейств и имён.

Теперь давайте менять имена. Везде есть одинаковый префикс «Plumbing_Manifolds_IVAR_Brass-Rod-Manifolds_CI596MN» — его заменю на нужный мне префикс. Сделаю это нодом String.Replace. На него подаю исходный список, что меняю и на что меняю. В итоге получаю измененный список.

Дальше нужно взять артикулы и заменить на правильные. В оригинальном тексте есть дефисы, по ним можно разделить строку, а дальше заменить артикулы. Сначала разделить строки нодом String.Split. На него подаём исходный список строк и разделитель, в моём случае — дефис.

Каждая строка разбивается на несколько подстрок в виде подсписка. Здесь первое значение в каждом списке — префикс наименования семейства, второе — артикул исходный, третье — количество выходов коллектора. Слово Ways не нужно, его выкинем.
У меня есть список исходных артикулов и артикулов на замену от заказчика. Я создам словарь, а потом «подменю» значения. Для этого беру нод Dictionary.ByKeysValues. На вход keys подам список исходных артикулов, на вход values — артикулы на замену. Подробнее про словари читайте в отдельном материале:
Данные я специально подготовлю в Экселе так, чтобы минимально обрабатывать их в Динамо. Так как артикулы — это текст, то надо превратить их в текст для Код блока. Делаю это формулой и сразу добавлю в конец запятую. Теперь скопирую в буфер, а потом вставлю всё в Код блок в Динамо. Добавлю квадратные скобки, удалю последнюю запятую — всё, список готов.


Теперь мне нужно создать список ключей для запроса из моих коллекторов, а потом получить артикулы на замену из словаря. Для этого создам список ключей из нашей разделенной строки. Для этого мне нужно вытащить из каждого подсписка только второе значение. Делаю это нодом List.GetItemAtIndex. Тут есть два нюанса.
Первый — мне нужно получить элементы из подсписков, второе — нумерация индексов идёт с нуля, а не единицы. Поэтому, чтобы получить второй элемент, надо указать индекс 1, а обработку вести на уровне списка L2.

Чтобы получить артикул на замену, беру нод Dictionary.ValueAtKey, подаю в него словарь, который создал ранее, и ключи, которые сгенерировал выше. На выходе получаю список артикулов. Их буду записывать в параметры семейства, а также использую в имени семейства.

Для генерации имен мне удобнее взять артикулы и добавить к ним префиксы и суффиксы. Делаю это в Код блоке, тут нет особого смысла использовать ноды для суммирования, так как формула очень простая.

Всё, у меня готовы артикулы и имена семейств. Теперь нужно переходить в Питон. В Динамо нет нодов для изменения имён семейств и типоразмеров, поэтому тут только Питон.
В Питон нужно подать: семейства, типоразмеры и имена для них. Это четыре списка, четыре входа.

Открываем Питон. Так как мы будем преобразовывать элементы, то нам нужно Ревит АПИ, обращение к документу и транзакции. Вникать в детали не будем, в начале кода нам нужен такой текст:
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager as DM
from RevitServices.Transactions import TransactionManager as TM
doc = DM.Instance.CurrentDBDocument
Теперь надо получить в Питон-скрипт наши списки для обработки. Пишем вот так:
family_list = UnwrapElement(IN[0])
family_type_list = UnwrapElement(IN[1])
type_name_list = IN[2]
family_name_list = IN[3]
Здесь функция UnwrapElement() преобразовывает элементы Динамо в элементы для работы в Питоне. Зачем и почему — без понятия, но без этого ничего не зафурычит. Нам нужно, чтобы фурычило, поэтому улыбаемся и повторяем.
Для текстовых данных такие преобразования не нужны, поэтому тут мы пишем просто имя входа в нод, чтобы получить списки.
Итак, у нас есть четыре списка одинаковой структуры и длины. В каждом списке по 19 элементов, все списки плоские, то есть нет вложенных списков. И что ещё важнее, в каждом списке каждый элемент соответствует элементам из других списков.
Сперва разберёмся с именами семейств. Нужно сделать цикл, который переберёт каждое семейство и каждое имя, а потом запишет имя в семейство.
Делаем это так:
TM.Instance.EnsureInTransaction(doc) # Открытие транзакции
for family, family_name in zip(family_list, family_name_list):
family.Name = family_name
TM.Instance.TransactionTaskDone() # Закрытие транзакции
Здесь мы сначала открываем транзакцию — это режим, в котором мы можем менять элементы внутри проекта. Без этого не сработает, в начале гифки ниже нод Питона у меня жёлтый, то есть выдал ошибку, именно потому, что я записал код вне транзакции.
Далее пишу цикл с функцией zip(). Подробнее о ней читайте в специальной статье про работу со циклами. И вся магия записывается простым кодом family.Name = family_name
. То есть я обращаюсь к имени элемента, а потом приравниваю к нему своё новое имя. Всё, весь процесс в реальном времени на гифке ниже.

Лучше на запускайте код на данном этапе, лучше допишите его целиком и запускайте разом всё.
Для типа семейства всё то же самое, только внутри цикла будем работать со списком типоразмеров. Кроме того, нужно будет записать в параметры типа артикул коллектора. Сделаем всё внутри той же транзакции, что меняла нам имя семейства.
for type, name in zip(family_type_list, type_name_list):
type.Name = name
type.LookupParameter("ADSK_Марка").Set(name)

Метод LookupParameter() наход параметр в семействе по указанному в скобках имени, имя нужно вводить в кавычках. Далее метод Set() записывает значение в параметр, в скобки подаём значение для записи.
Собственно, вот и всё. Полный код привожу ниже:
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager as DM
from RevitServices.Transactions import TransactionManager as TM
doc = DM.Instance.CurrentDBDocument
family_list = UnwrapElement(IN[0])
family_type_list = UnwrapElement(IN[1])
type_name_list = IN[2]
family_name_list = IN[3]
TM.Instance.EnsureInTransaction(doc) # Открытие транзакции
for family, family_name in zip(family_list, family_name_list):
family.Name = family_name
for type, name in zip(family_type_list, type_name_list):
type.Name = name
type.LookupParameter("ADSK_Марка").Set(name)
TM.Instance.TransactionTaskDone() # Закрытие транзакции
Можно было бы сделать всё в одном цикле, так как наши списки равны по длине и структуре. Тогда код был бы чуть короче, но цикл читался бы посложнее:
for family, family_name, type, name in zip(family_list, family_name_list, family_type_list, type_name_list):
family.Name = family_name
type.Name = name
type.LookupParameter("ADSK_Марка").Set(name)
Курсы по Ревиту тут, магазин тут, а ссылки на Телеграм-канал с новостями и Телеграм-группа для вопросов по Ревиту вот тут на картинках, подписывайтесь, следите за обновлениями в блоге.



