В субботу 20 июля я сделал короткий стрим о том, как сделать то, что написано в заголовке статьи. В итоге сделать до конца не получилось. Я думал, что там всё будет просто, но в программировании я слабоват, так что потерпел неудачу. Но потом добил этот алгоритм и теперь выложу код сюда.

Я планировал эфир на полчасика. Тык-тык, быстро накидаю ноды и код в Питоне, буду красавчик.

Я обосрался. У меня получилось обработать элементы с первого уровня вложенности, но ведь вложенность может быть очень разной.

В итоге три часа сидел и разбирался, как решить проблему. Вроде получилось, я решил выложить код сюда, чтобы вы могли тоже применять его в своей работе, заодно дадите обратную связь по тому, как скрипт обрабатывает элементы с большой вложенностью.

Ограничения

Скрипт работает только с параметрами экземпляра с типом данных текст. То есть копировать значения можно только из текстового параметра в текстовый и только из параметра экземпляра в параметр экземпляра.

Если не знаете, что такое экземпляр и тип данных, читайте статью про виды параметров и статью про понятия типа и экземпляра.

Скрипт написал для работы с инженерными система и в первую очередь для копирования параметра «Имя системы».

Алгоритм

Скрипт состоит из двух частей: нодов и кода в Питоне.

Нодами я получаю элементы для обработки и выбираю имена параметров, из которых копирую и в которые записываю значения. Эти имена параметров можно вывести в Проигрыватель Динамо.

Если вам нужен иной принцип отбора элементов, то поменяйте эту нодовую часть. Вот так выглядит у меня:

В данном случае получаю все элементы с текущего вида. Ноды наверху для получения кода в Питоне, вам они вряд ли нужны будут

Вторая часть скрипта — это код в Питоне. Я приведу его ниже, скопируйте его и вставлять в нод Python Script. Если вы программист, дальше не читайте или читайте с закрытыми глазами, иначе из них пойдёт кровь. Я предупредил.

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

TM.Instance.EnsureInTransaction(doc) # Открытие транзакции
### Действия внутри транзакции ###
TM.Instance.TransactionTaskDone() # Закрытие транзакции

element_list = UnwrapElement(IN[0])

param_get_value = IN[1]
param_set_value = IN[2]

parent_elements = [] # получаем фэмили инстансы, которые не являются общими вложенными

TM.Instance.EnsureInTransaction(doc) # Открытие транзакции

for element in element_list:
    if isinstance(element, FamilyInstance):
        if element.SuperComponent:
            pass
        else:        
            parent_elements.append(element)
    elif isinstance(element, MEPSystem):
        pass
    else:
        param_value = element.LookupParameter(param_get_value).AsString() # имя системы
        element.LookupParameter(param_set_value).Set(param_value)

def flat_list(leveled_list):
    flatten = []
    if isinstance(leveled_list, list):
        for item in leveled_list:
            if isinstance(item, list):
                for subitem in item:
                    flatten.extend(flat_list(subitem))
            else: flatten.append(item)
    else:flatten.append(leveled_list)
    
    return flatten

def subcomponents(element):
    all_subs = []
    if element.GetSubComponentIds():
        sub_Id_list = element.GetSubComponentIds()
        
        for sub_Id in sub_Id_list:
            sub = doc.GetElement(sub_Id)
            if sub.GetSubComponentIds():
                all_subs.append(sub)
                all_subs.append(subcomponents(sub))
            else:
                all_subs.append(sub)

    return all_subs
    
all_subcomponents = []
for el in parent_elements:
    param_value = el.LookupParameter(param_get_value).AsString() # имя системы
    el.LookupParameter(param_set_value).Set(param_value)

    if el.GetSubComponentIds(): 
        shared_nested = flat_list(subcomponents(el))
    
        for sn in shared_nested:
            sn.LookupParameter(param_set_value).Set(param_value)
            all_subcomponents.append(sn)

TM.Instance.TransactionTaskDone() # Закрытие транзакции

OUT = parent_elements, all_subcomponents

Вот такой код. На выходе первый список — это родительские семейства, то есть те, которые не являются вложенными. Второе список — это все вложенные общие со всех уровней, которые обработал скрипт.