В этом материале Костя расскажет, как настроить заглушки — специальные подсказки при вводе кода, которые помогают проанализировать свойства и методы объектов и быстро их ввести, а не печатать вручную. С помощью свойств мы получаем информацию, а с помощью методов — создаём или меняем объекты. Все свойства и методы не запомнить, и тут помогают заглушки.
Автор материала
Статью написал Костя @say_hey_to_k — БИМ-специалист из Китая. Он работает на канадскую фирму и, кроме Ревита, занимается также и программированием.
Это вторая часть из материала о настройки VSCode, первая была про установку и настройку интерфейса VSCode.
Заглушки
Заглушки нам нужны для упрощения работы с объектами Revit API. С их помощью можно смотреть, какие есть свойства и методы у классов Ревита, а также что в них нужно подать. И не лезть каждый раз в RevitLookup или на revitapidocs.com.
Например, у некоторые трубопроводных элементов может быть свойство «PipingNetwork». Когда работаешь с кодом нечасто, то можно забыть, как это свойство пишется: pipenetwork или pipingNetwork. И заглушка поможет «увидеть» варианты в выпадающем списке и из них выбрать правильное написание.
Вот пример для создания линии. Я ввожу имя переменной «Line» и дальше начинаю писать название метода для создания новой линии, а заглушки мне подсказывают, какие варианты возможно с этим объектом:
Когда выберу метод и открою скобочку, то заглушки покажет подсказку, что нужно вводить в метод. Для создания линии нужно указать точку начала и конца в формате XYZ, о чём подсказка и сообщает. Теоретически там могли бы быть не XYZ, а Point или ещё что-нибудь, но подсказка нам говорит: «Давай вводи XYZ, вот так будет правильно». И не надо лезть на сайт с Revit API и читать, что там этот метод ждёт на ввод.
То есть заглушки работают как своего рода справочник возможных значений, а также помогают быстрее вводить команды, так как из списка можно выбрать подходящий вариант, нажать по нему и он напечатается. Для длинных команд это очень удобно, замучаешься печатать этот GetMEPConnectorInfo, например.
Скачивание и распаковка заглушек
Заходим на https://github.com/BIMOpenGroup/RevitAPIStubs, скачиваем архив с заглушками stubs.rar.
В целом, не важно, куда их распаковывать. Но имеет смысл положить в папку, которую в будущем можно будет переносить на другую Винду, компьютер и т. д.
К примеру, у меня они расположены в папке Stubs вместе со всем кодингом:
Настройка VSCode для работы с заглушками
Нам надо попасть в файл с пользовательскими настройками VSCode. Для этого нажимаем Ctrl+Shift+P и вбиваем settings. Находим меню «Параметры: Открыть пользовательские настройки (JSON)». Там видим знакомые нам из первой статьи настройки параметров flake8 и autopep8. Добавляем туда пути к заглушкам.
Можете скопировать нужные настройки отсюда, только не забудьте поменять пути к заглушкам на свои:
{
"python.autoComplete.extraPaths": [
"D:\\Coding\\Stubs\\BIMOpenGroup Stubs\\revit\\2023",
"D:\\Coding\\Stubs\\BIMOpenGroup Stubs\\common",
],
"python.analysis.extraPaths": [
"D:\\Coding\\Stubs\\BIMOpenGroup Stubs\\revit\\2023",
"D:\\Coding\\Stubs\\BIMOpenGroup Stubs\\common"
]
}
Важно: можно указывать только одну версию Ревита. Самая последняя версия заглушек для Ревита, что имеется на данный момент, — 2023. Хочется посвежее? Читайте заключение данной статьи.
Сохраняемся и перезапускаем VSCode.
Теперь, если вы импортируете в коде классы ревита, VSCode будет вам подсказывать:
import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit import DB
DB.Element().loo — начинаем вводить метод и получаем подсказку
<< Здесь заканчивается статья для нормальных людей и начинается дрючево для задротов. Я вас предупредил. >>
Подсказки типов
В предыдущем примере мы сразу начали с элемента. Но чаще скрипт начинается с переменной, в которую что-то прилетает извне. Допустим, мы предполагаем, что туда прилетает элемент и мы дальше хотим с ним взаимодействовать, как с ревитовским элементом. Сам VSCode не может догадаться об этом, но мы можем ему подсказать.
Для этого напишем в комментарии переменной её тип:
import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit import DB
element = UnwrapElement(IN[0]) # type: DB.Element
element.lo
Мы подсказали редактору кода, какого типа переменная, а он нам — какие атрибуты у неё есть. Как это работает? А вот так. Прогеры ещё во времена второго Питона договорились, что если для переменной в конце строки после двух пробелов написать # type:, а далее тип объекта, то редактор кода будет считать, что переменная этого типа. Причём, самому Питону на этот комментарий абсолютно наплевать. Это чисто между вами и редактором кода. Ну, и для mypy, если позанудничать. Но он всё равно не для нас. Даже ссылку на него давать не буду.
Для метода это работает так: после объявления метода пишете комментарий # type:, далее в скобочках какой тип в него прилетает, затем стрелочку -> и после неё, что метод возвращает. Если метод ничего не принимает, то оставляете скобки пустыми. Если метод ничего не возвращает, то после стрелочки пишете None.
В примере ниже мы подаём в метод элемент, а возвращаем строку.
import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit import DB
element = UnwrapElement(IN[0]) # type: DB.Element
def get_element_name(elem):
# type: (DB.Element) -> str
return elem.Name
name = get_element_name(element)
Что мы с этого получаем? Во-первых, VSCode уже знает, что внутри метода имеет дело с элементом, и подскажет его методы. Во-вторых, поскольку мы указали, что метод выдаст строку, то VSCode уже знает, что переменная name, полученная при помощи нашего метода, — это строка. И будет подсказывать, какие методы уже есть у неё.
В-третьих, больше не надо сочинять тупые описания, вроде: «Этот метод делает то, что и написано в его названии, ещё он забирает вот такие штуки и возвращает вот эти штуки. А, может, и ничего не возвращает… как-то я об этом ещё не подумал. В общем, такой вот он молодец!».
Ваш метод должен говорить сам за себя, а подсказки типа покажут, что в него входит и что из него выходит! Всё равно вы потом его логику поменяете, а описание поменять забудете и будет ещё хуже (читаем «Чистый код» дяди Боба, и всё такое)! А иногда и подсказки даже лишние! Минутка гнева закончилась, продолжаем.
Когда возможно несколько вариантов типов, то их можно указать через разделитель |. Например, не у всех элементов есть параметр Name, а даже если и есть, то он может вернуть None. Так и запишем:
def get_element_name(elem):
# type: (DB.Element) -> str | None
if hasattr(elem, 'Name'):
return elem.Name
name = get_element_name(element)
Если навести на метод в месте использования, то увидим в подсказке какой тип он принимает и какие типы возвращает:
Когда в метод подаётся несколько аргументов, то их типы пишутся через запятую
def get_str_value(elem, param_name):
# type: (DB.Element, str) -> str | None
param = elem.LookupParameter(param_name)
if param is not None:
return param.AsString()
Для итерируемых объектов (вроде списка и словаря) можно указать, что за объект внутри, написав его в квадратных скобочках:
import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit import DB
elems = UnwrapElement(IN[0]) # type: list[DB.Element]
for elem in elems:
elem.
Пример чуть посложней. Мы указали, что в переменную elems приходит список элементов, а в переменную elems_per_mark будем запихивать строки в качестве ключей и список элементов в качестве значений.
import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit import DB
elems = UnwrapElement(IN[0]) # type: list[DB.Element]
elems_per_mark = {} # type: dict[str, list[DB.Element]]
for elem in elems:
mark_param = elem.get_Parameter(DB.BuiltInParameter.ALL_MODEL_MARK)
if mark_param is not None:
mark = mark_param.AsString()
if mark in elems_per_mark:
elems_per_mark[mark].append(elem)
else:
elems_per_mark[mark] = [elem]
elems_per_mark
Я написал, что вышеуказанный способ подсказки типов придумали в Питоне 2. Следует добавить, что в Питоне 3.5 его поменяли (подробнее читайте здесь на сайте Питона). Поскольку большинство из нас, к сожалению, до сих пор работает со второй версией Питона, я не рекомендую использовать новый способ. Старый способ будет работать в новом питоне, а вот новый в старом не будет! VSCode всё равно понимает оба.
Допиливание заглушек
Мы знаем, что если у элемента вызвать метод LookupParameter, то должен выползти параметр. А значит, можно сразу через точку вызвать какой-нибудь метод параметра (хоть осторожные люди так и не делают). Но если попробуем это провернуть, то VSCode нам ничего нормального не подскажет:
import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit import DB
elem = UnwrapElement(IN[0]) # type: DB.Element
elem.LookupParameter('Yo')
Почему так? Как следует из описания заглушек, которые мы скачали, для полноценной работы их надо допиливать. Обратите внимание на то, что нам показывает подсказка типов у метода LookupParameter:
Если читать описание, то метод принимает self (игнорируем) и строку, а должен вернуть параметр. Но если смотреть на типы, а именно они нас сейчас и волнуют, то принимает он какой-то Any, а возвращает вообще None! Вот его-то методы нам и подсказали в предыдущем примере. Спасибо, блин!
Копнём чуть глубже, что же это за «заглушки» мы скачали. Мы скачали набор файлов, которые имитируют интересующие нас типы объектов и их методы, включая их описание. Их мы подсовываем редактору кода (через настройки) и как бы говорим, что если он увидит в нашем коде тип из заглушек, то вести этот тип будет себя так, как в них указано. Все методы внутри заглушек пустые. Выполняться файлы заглушек никогда не будут. Даже если попробовать, то ничего не получится.
Файлы заглушек были сгенерированы при помощи кода в исходниках репозитория, где мы их скачали. К сожалению, при генерации файлов не было учтено подсказки типов у атрибутов классов. Их нам и придётся допиливать своими ручками.
Итак, вспоминаем предыдущую статью и делаем Ctr + клик по методу LookupParameter (или F12). Он нас переносит в файл заглушки прямо в этот метод. Если с первого раза на нужный метод не перекинул, то, не закрывая файла заглушки, вернитесь в наш скрипт и нажмите F12 по этому методу ещё раз. Со второго раза должен перекинуть. Там видим его сгенерированное описание.
Добавляем нашу подсказку (не забываем, что может вернуться и None): # type: (str) -> Parameter | None
Сохраняем и закрываем файл заглушки. Перезапускаем окно VSCode. Можно закрыть и открыть VSCode, а можно нажать Ctrl+Shift+P, затем вбить «reload» и выбрать «Разработчик: Перезагрузить окно»:
После перезапуска пробуем ещё раз и радуемся или нет, если ничего не получилось.
Со свойствами класса — те, которые вызываются без скобочек, — поступаем немного по-другому. Они в заглушках записаны как переменные класса. Например, с Element.Name переходим к свойству через тот же F12, мотаем вправо и дописываем тип, как делали это для переменных:
Сохраняемся, перезапускаем окно. Теперь имя элемента возвращает строку, а не хрен пойми что.
Ещё можно добавлять, удалять и редактировать методы. Например, у элемента неверно сгенерировался метод get_BoundingBox. Так давайте его обновим!
def get_BoundingBox(self, view):
# type: (View | None) -> BoundingBoxXYZ | None
"""Retrieves a box that circumscribes all geometry of the element."""
pass
То же можно проделать и с get_Parameter, get_Geometry, и т. д.
А ещё можно запариться на отличненько и замутить вот такую штуку (это сами как-нибудь додумайте):
Заключение
Таким образом, раз за разом, постепенно можно подправить самые часто используемые атрибуты классов. Геморр? Да.
«Но ведь можно же было сразу сгенерировать заглушки нормально со всеми этими вашими типами?», — спросите вы. Конечно, можно! Изучайте Дотнет, Питон, Гитхаб и становитесь контрибьютором генератора заглушек! Автор репы с удовольствием рассмотрит ваш PR и, в случае нормальной работоспособной либы, замержит ваш труд.
Конец второй части.