client/parser/main.py

871 lines
37 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import traceback
import time
import re
from collections import defaultdict
from win32com.client import Dispatch, gencache
from PIL import Image, ImageDraw, ImageFont
from enums import KompasCommand
from logger import logger
ids = {
"api_5": "{0422828C-F174-495E-AC5D-D31014DBBE87}",
"api_7": "{69AC2981-37C0-4379-84FD-5DD2F3C0A520}",
"const": "{75C9F5D0-B5B8-4526-8681-9903C567D2ED}",
}
class KompasDocumentParser:
def __init__(self):
self.api5 = None
self.api7 = None
self.constants = None
self.application = None
self._init_kompas()
def _init_kompas(self):
"""Инициализация API КОМПАС версий 5 и 7"""
try:
import pythoncom
pythoncom.CoInitialize()
# Получаем API версии 5
self.api5_module = gencache.EnsureModule(ids["api_5"], 0, 1, 0)
self.api5 = self.api5_module.KompasObject(
Dispatch("Kompas.Application.5")._oleobj_.QueryInterface(
self.api5_module.KompasObject.CLSID, pythoncom.IID_IDispatch
)
)
# Получаем API версии 7
self.api7_module = gencache.EnsureModule(ids["api_7"], 0, 1, 0)
self.api7 = self.api7_module.IKompasAPIObject(
Dispatch("Kompas.Application.7")._oleobj_.QueryInterface(
self.api7_module.IKompasAPIObject.CLSID, pythoncom.IID_IDispatch
)
)
# Получаем константы
constants_module = gencache.EnsureModule(ids["const"], 0, 1, 0)
self.constants = constants_module.constants
# Правильное получение IApplication
self.application = self.api7_module.IApplication(self.api7)
self.application.Visible = True
except Exception as e:
# Выводим полный traceback и сообщение об ошибке
print("[ERROR] Ошибка при подключении к КОМПАС:")
traceback.print_exc() # <-- Выводит номер строки, файл и стек
raise RuntimeError(f"Ошибка при подключении к КОМПАС: {e}")
def get_export_path(self, doc_path: str, doc_name: str, ext: str) -> str:
"""
Формирует путь для экспорта файла по шаблону:
- Создаёт подпапку `ext` внутри `doc_path`
- Возвращает полный путь к файлу `<doc_name>.<ext>`
"""
output_dir = os.path.join(doc_path, ext)
filename = f"{doc_name}.{ext}"
full_path = os.path.join(output_dir, filename)
return full_path
def prepare_export_path(self, doc_path: str, doc_name: str, ext: str) -> str:
full_path = self.get_export_path(doc_path, doc_name, ext)
os.makedirs(os.path.dirname(full_path), exist_ok=True)
return full_path
def get_open_documents(self):
"""Возвращает список информации о всех открытых документах"""
documents = []
docs_collection = self.application.Documents
for i in range(docs_collection.Count):
try:
doc = docs_collection.Item(i) # Индексация с 1
if doc is None:
continue
documents.append(
{
"type": doc.DocumentType,
"name": doc.Name,
"path": doc.Path,
"active": doc.Active,
}
)
except Exception as e:
print(f"Ошибка при обработке документа #{i + 1}: {e}")
return documents
def create_drawing_for_parts(self):
"""
Создание чертежей для всех уникальных деталей из открытых деталей и сборок.
Также создаёт спецификацию для сборок.
"""
logger.info("Начинаем создание чертежей для деталей...")
result = {"result": []}
# Получаем доступные типы из разрешённых действий
av_actions = self.get_available_actions()
allowed_types = av_actions[KompasCommand.PROJECT_SUPPORT]["allowed_types"]
docs_collection = self.application.Documents
for i in range(docs_collection.Count):
try:
doc = docs_collection.Item(i)
if doc is None:
continue
doc_type = doc.DocumentType
if doc_type not in allowed_types:
continue
doc.Active = True
doc_path = doc.Path
doc_name = "-".join(doc.Name.split(".")[:-1])
logger.info(f"Обрабатываем документ: {doc_name}")
doc_3d = self.api7_module.IKompasDocument3D(doc)
top_part = doc_3d.TopPart
# Используем общую рекурсивную функцию вместо all_elements
if doc_type == self.constants.ksDocumentAssembly:
result_recursive = self._traverse_parts_recursive(top_part)
elements = result_recursive["elements"]
welding = result_recursive["welding"]
bends = result_recursive["bends"]
else:
# Для отдельной детали просто собираем данные
elements = [top_part]
welding = self._collect_welds_from_drawing(top_part)
bends = self._collect_bends_from_element(top_part)
logger.info(f"Найдено элементов для создания чертежей: {len(elements)}")
# Сохранение чертежей
drawings = []
for component in elements:
component_ipart = self.api7_module.IPart7(component)
if component_ipart.Standard:
continue
logger.info(f"Создаём чертёж для: {component.Name}")
# Создаём новый чертёж
c_doc = self.application.Documents.Add(
self.constants.ksDocumentDrawing
)
c_layout = c_doc.LayoutSheets
c_sheet = c_layout.Item(0)
c_sheet.Format.Format = self.constants.ksFormatA3
c_sheet.Format.VerticalOrientation = False
c_sheet.Update()
# Расчёт габаритов
c_size = [1, 1, 1]
if component_ipart.Owner.ResultBodies:
gabarit = [0, 0, 0, 0, 0, 0]
if hasattr(component_ipart.Owner.ResultBodies, "GetGabarit"):
component_ipart.Owner.ResultBodies.GetGabarit(*gabarit)
g1 = gabarit[1:4]
g2 = gabarit[4:]
c_size = [abs(g1[i] - g2[i]) for i in range(len(g1))]
# Масштабирование
c_scale = (
c_sheet.Format.FormatHeight / sum(c_size)
if sum(c_size) > 0
else 1
)
# Получаем интерфейс 2D документа
c_doc_2d = self.api7_module.IKompasDocument2D(c_doc)
c_views = c_doc_2d.ViewsAndLayersManager.Views
# Добавляем стандартные виды
c_views.AddStandartViews(
component_ipart.FileName,
component_ipart.Name,
[1, 3, 5, 7],
c_size[1] * c_scale,
c_sheet.Format.FormatHeight - 25,
c_scale,
20,
20,
)
filename = "_".join(
filter(
None, [component_ipart.Marking, component_ipart.Name[:20]]
)
).replace(" ", "-")
full_path = self.prepare_export_path(doc_path, filename, "cdw")
c_doc.SaveAs(full_path)
logger.info(f"[OK] Сохранён чертёж: {full_path}")
drawings.append(
{
"name": f"{filename}.cdw",
"path": full_path,
"success": True,
"timestamp": time.time(),
}
)
# Сохранение спецификации для сборок
specifications = []
if doc_type == self.constants.ksDocumentAssembly:
spec = self.application.Documents.Add(
self.constants.ksDocumentSpecification
)
spec_doc = self.api7_module.ISpecificationDocument(spec)
spec_doc.AttachedDocuments.Add(doc.PathName, True)
filename = "Список_деталей"
full_path = self.prepare_export_path(doc_path, filename, "spw")
spec.SaveAs(full_path)
logger.info(f"[OK] Сохранена спецификация: {full_path}")
specifications.append(
{
"name": f"{filename}.spw",
"path": full_path,
"success": True,
"timestamp": time.time(),
}
)
# Добавляем результат
result["result"].append(
{
"document_name": doc_name,
"drawings": drawings,
"specifications": specifications,
"success": len(drawings) > 0 or len(specifications) > 0,
"timestamp": time.time(),
}
)
except Exception as e:
logger.error(
f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}",
exc_info=True,
)
result["result"].append(
{
"document_name": f"Документ #{i + 1}",
"drawings": [],
"specifications": [],
"success": False,
"error": str(e),
"timestamp": time.time(),
}
)
return result
def save_to_iges(self):
"""Сохраняет открытые 3D-документы (детали/сборки) в формате IGES"""
logger.info("Начинаем сохранение документов в формате IGES...")
result = {"result": []}
av_actions = self.get_available_actions()
docs_collection = self.application.Documents
allowed_types = av_actions[KompasCommand.IGES]["allowed_types"]
for i in range(docs_collection.Count):
try:
doc = docs_collection.Item(i)
if doc is None:
continue
doc_type = doc.DocumentType
if doc_type not in allowed_types:
continue
doc.Active = True
doc_path = doc.Path
doc_name = "-".join(doc.Name.split(".")[:-1])
logger.info(f"Попытка сохранить: {doc_name}")
# Получаем 3D-документ через API v5
doc_api5 = self.api5.ActiveDocument3D()
if not doc_api5:
raise RuntimeError(
"Не удалось получить активный 3D-документ через API v5"
)
# Подготавливаем параметры сохранения в IGES
save_params = doc_api5.AdditionFormatParam()
save_params.Init()
save_params.format = self.constants.ksConverterToIGES
full_path = self.prepare_export_path(doc_path, doc_name, "igs")
export_success = doc_api5.SaveAsToAdditionFormat(full_path, save_params)
# Добавляем информацию о результате в массив
logger.info(f"Успешно сохранено: {full_path}")
result["result"].append(
{
"file": full_path,
"success": bool(export_success),
"document_name": doc_name,
"document_type": doc_type,
"timestamp": time.time(),
}
)
if not export_success:
logger.error(f"Не удалось сохранить: {full_path}")
except Exception as e:
logger.error(
f"Ошибка при обработке документа #{i + 1}: {e}",
exc_info=True,
)
result["result"].append(
{
"file": None,
"success": False,
"error": str(e),
"document_index": i + 1,
"timestamp": time.time(),
}
)
return result
def _collect_bends_from_element(self, element):
bends = []
try:
feature = self.api7_module.IFeature7(element)
sub_features = feature.SubFeatures(1, True, False) or []
for item in sub_features:
if type(item) in (
self.api7_module.ISheetMetalBend,
self.api7_module.ISheetMetalLineBend,
self.api7_module.ISheetMetalBody,
):
sub_sheets = item.Owner.SubFeatures(1, True, False)
if sub_sheets:
for b in sub_sheets:
bend = self.api7_module.ISheetMetalBend(b)
bends.append(bend)
except Exception as e:
logger.error("Ошибка при сборе гибов из элемента", exc_info=True)
return bends
def _collect_welds_from_drawing(self, part):
welding = []
try:
drawing_context = self.api7_module.IDrawingContainer(part)
macro = self.api7_module.IMacroObject3D(drawing_context)
sub_features = macro.Owner.SubFeatures(1, True, False) or []
for item in sub_features:
if isinstance(item, self.api7_module.IUserDesignationCompObj):
welding.append(item)
except Exception as e:
logger.error(
"Ошибка при сборе сварок из чертежного контекста", exc_info=True
)
return welding
def _traverse_parts_recursive(self, part):
elements = []
welding = []
bends = []
try:
# Собираем сварки
welding += self._collect_welds_from_drawing(part)
# Обходим подчасти
doc_parts = self.api7_module.IParts7(part.Parts)
for j in range(doc_parts.Count):
element = doc_parts.Part(j)
# if element.Parts.Count == 0:
# elements.append(element)
elements.append(element)
bends += self._collect_bends_from_element(element)
result = self._traverse_parts_recursive(element)
elements += result["elements"]
welding += result["welding"]
bends += result["bends"]
except Exception as e:
logger.error("Ошибка при рекурсивном обходе частей", exc_info=True)
return {"elements": elements, "welding": welding, "bends": bends}
def _build_statistics_data(self, elements, welding):
stats = {"Name": {}, "Material": {}, "Area": {}}
item_template = lambda: {"quantity": 0, "area": 0, "mass":0, "material": ""}
detail_stats = {
"standard": defaultdict(item_template),
"custom": defaultdict(item_template),
}
# Сбор данных по элементам (кроме "Welding")
for e in elements:
type_key = "standard" if e.Standard else "custom"
name = getattr(e, "Name", "Неизвестно")
material = getattr(e, "Material", "Неизвестно")
element_key = (name, material)
mass_inertial_params = self.api7_module.IMassInertiaParam7(e)
area = mass_inertial_params.Area * 0.0001
mass = round(getattr(e, 'Mass'), 3)
quantity = 1
detail_stats[type_key][element_key]["material"] = material
detail_stats[type_key][element_key]["quantity"] += quantity
detail_stats[type_key][element_key]["area"] += area
detail_stats[type_key][element_key]["mass"] += mass
for key in stats.keys():
if key == "Name":
value = f"{getattr(e, key)}, масса {round(getattr(e, 'Mass'), 3)}"
stats[key][value] = stats[key].get(value, 0) + 1
elif key == "Area":
mass_inertial_params = self.api7_module.IMassInertiaParam7(e)
area = mass_inertial_params.Area * 0.0001 # м²
material = getattr(e, "Material", "Неизвестно")
key_area = f"Площадь {material}, м²:"
stats[key][key_area] = round(stats[key].get(key_area, 0) + area, 6)
else:
try:
value = getattr(e, key)
stats[key][value] = stats[key].get(value, 0) + 1
except AttributeError:
logger.warning(f"Элемент не имеет атрибута '{key}', пропускаем")
# logger.info(detail_stats)
# Общая площадь
stats["Area"]["Total"] = sum(stats["Area"].values())
# Добавляем сварки в статистику как отдельный раздел
stats["Welding"] = {}
for w in welding:
w_name_split = w.Name.split("-")
w_len = w_name_split[-1].split("@")[0]
stats["Welding"][w.Name] = w_len
if stats["Welding"]:
total_length = sum(
float(w_len)
for w_len in stats["Welding"].values()
if w_len.replace(".", "", 1).isdigit()
)
stats["Welding"]["Total"] = round(total_length, 2)
return stats
def extract_section(name):
"""Извлечение сечения из имени профиля"""
match = re.search(r'(\d+)х(\d+)(?:х(\d+))?', name)
if match:
w, h, t = match.groups()
return f"{w}x{h}" + (f", толщ. {t}" if t else "")
return "Без сечения"
def classify_item(self, name, obj):
"""Классификация элементов по типу и имени"""
name_lower = name.lower()
cls_name = str(type(obj))
# Гиб листовой детали
if isinstance(obj, (self.api7_module.ISheetMetalBend, self.api7_module.ISheetMetalLineBend)):
return "Гиб"
# Сварной шов
elif "IUserDesignationCompObj" in cls_name or ("IUserObject3D" in cls_name and "свар" in name_lower):
return "Сварной шов"
# Стандартное изделие
elif "IMacroObject3D" in cls_name or any(kw in name_lower for kw in ["болт", "гайка", "шайба"]):
return "Стандартное изделие"
# Профильная труба
elif "IUserObject3D" in cls_name and any(kw in name_lower for kw in ["труба", "профиль"]):
return "Профильная труба"
# Листовая оболочка
elif "ISheetMetalRuledShell" in cls_name:
return "Листовая деталь"
# Обычная деталь
elif "IPart7" in cls_name:
return "Кастомная деталь"
# Вспомогательные объекты
elif any(kw in cls_name for kw in ["IPlane3D", "IAxis3D", "IModelObject", "IMateConstraint3D", "ISketch"]):
return "Вспомогательный объект"
else:
return "Неизвестный тип"
def traverse_features(self, feature, level=0):
"""
Рекурсивный обход дерева построения модели.
Возвращает:
result_all: список всех элементов
classified_data: словарь с категориями
"""
try:
sub_features = self.api7_module.IFeature7(feature).SubFeatures(1, True, False)
if not sub_features:
return [], {
"bends": [],
"welding": [],
"standard_items": [],
"profile_tubes": [],
"sheet_metal_parts": [],
"custom_parts": [],
"support_objects": []
}
result_all = []
bends = []
welding = []
standard_items = []
profile_tubes = []
sheet_metal_parts = []
custom_parts = []
support_objects = []
for item in sub_features:
name = getattr(item, "Name", "---")
ftype = str(type(item))
indent = " " * level
classification = self.classify_item(name, item)
# print(f"{indent}- {name} | {ftype} → [{classification}]")
# Добавляем в общий список
result_all.append((name, ftype))
# Классифицируем элемент
if classification == "Гиб":
bends.append({"name": name, "type": ftype})
elif classification == "Сварной шов":
welding.append({"name": name, "type": ftype})
elif classification == "Стандартное изделие":
standard_items.append({"name": name, "type": ftype})
elif classification == "Профильная труба":
profile_tubes.append({"name": name, "type": ftype, "section": extract_section(name)})
elif classification == "Листовая деталь":
sheet_metal_parts.append({"name": name, "type": ftype})
elif classification == "Кастомная деталь":
custom_parts.append({"name": name, "type": ftype})
elif classification == "Вспомогательный объект":
support_objects.append({"name": name, "type": ftype})
# Рекурсивно обходим дочерние элементы
child_all, child_data = self.traverse_features(item, level + 1)
result_all.extend(child_all)
bends.extend(child_data.get("bends", []))
welding.extend(child_data.get("welding", []))
standard_items.extend(child_data.get("standard_items", []))
profile_tubes.extend(child_data.get("profile_tubes", []))
sheet_metal_parts.extend(child_data.get("sheet_metal_parts", []))
custom_parts.extend(child_data.get("custom_parts", []))
support_objects.extend(child_data.get("support_objects", []))
return result_all, {
"bends": bends,
"welding": welding,
"standard_items": standard_items,
"profile_tubes": profile_tubes,
"sheet_metal_parts": sheet_metal_parts,
"custom_parts": custom_parts,
"support_objects": support_objects
}
except Exception as ex:
logger.error(f"Ошибка при обходе SubFeatures: {ex}", exc_info=True)
return [], {
"bends": [],
"welding": [],
"standard_items": [],
"profile_tubes": [],
"sheet_metal_parts": [],
"custom_parts": [],
"support_objects": []
}
def collect_statistics(self):
"""Сбор статистики по элементам, гибам и сваркам в активном документе"""
logger.info("Начинаем сбор статистики по элементам...")
av_actions = self.get_available_actions()
allowed_types = av_actions[KompasCommand.STATS]["allowed_types"]
docs_collection = self.application.Documents
result = {"result": []}
for i in range(docs_collection.Count + 1):
try:
doc = docs_collection.Item(i)
if doc is None:
continue
doc_type = doc.DocumentType
if doc_type not in allowed_types:
continue
doc.Active = True
doc_name = "-".join(doc.Name.split(".")[:-1])
logger.info(f"Обрабатываем документ: {doc_name}")
elements = []
bends = []
welding = []
doc_3d = self.api7_module.IKompasDocument3D(doc)
top_part = doc_3d.TopPart
if doc_type == self.constants.ksDocumentAssembly:
# Рекурсивный обход для сборки
res = self._traverse_parts_recursive(top_part)
elements += res["elements"]
bends += res["bends"]
welding += res["welding"]
else:
# Для деталей просто добавляем верхнюю часть и собираем данные
elements += [top_part]
bends += self._collect_bends_from_element(top_part)
welding += self._collect_welds_from_drawing(top_part)
result_all, categorized = self.traverse_features(top_part)
# Пример вывода
print(f"Гибы: {len(categorized['bends'])}")
print(f"Сварные швы: {len(categorized['welding'])}")
print(f"Стандартные изделия: {len(categorized['standard_items'])}")
print(f"Профильные трубы: {len(categorized['profile_tubes'])}")
print(f"Листовые детали: {len(categorized['sheet_metal_parts'])}")
print(f"Кастомные детали: {len(categorized['custom_parts'])}")
logger.info(
f"Найдено:\n Элементов {len(elements)}\n Гибов {len(bends)}\n"
)
stats = self._build_statistics_data(elements, welding)
# Формируем объект документа для результата
result["result"].append(
{
"name": doc_name,
"elements_count": len(elements),
"bends_count": len(bends),
"statistics": stats,
}
)
except Exception as e:
logger.error(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
traceback.print_exc()
return result
def export_to_raster(self):
"""Экспорт открытых 2D-документов (чертежи, фрагменты, спецификации) в JPG и DXF. Создание многостраничного PDF."""
logger.info("Начинаем экспорт документов в растровый формат...")
images = []
first_doc_name = None
result = {"result": []}
# Получаем доступные типы для экспорта из разрешенных действий
av_actions = self.get_available_actions()
allowed_types = av_actions[KompasCommand.EXPORT_RASTER]["allowed_types"]
docs_collection = self.application.Documents
for i in range(docs_collection.Count):
try:
doc = docs_collection.Item(i)
if doc is None:
continue
doc_type = doc.DocumentType
if doc_type not in allowed_types:
continue
doc.Active = True
doc_path = doc.Path
doc_name = os.path.splitext(doc.Name)[0] # Имя без расширения
logger.info(f"Обрабатываем документ: {doc_name}")
# Сохраняем имя первого документа
if first_doc_name is None:
try:
doc7 = self.api7_module.IKompasDocument(doc)
first_doc_name = (
doc7.LayoutSheets.ItemByNumber(1).Stamp.Text(2).Str
)
except Exception as e:
first_doc_name = "Без имени"
logger.warning("Не удалось получить имя документа из штампа")
# Получаем интерфейс 2D-документа
if doc_type == self.constants.ksDocumentSpecification:
doc_api5 = self.api5.SpcActiveDocument()
else:
doc_api5 = self.api5.ActiveDocument2D()
if not doc_api5:
raise RuntimeError("Не удалось получить активный 2D-документ")
# Параметры экспорта в JPG
raster_params = doc_api5.RasterFormatParam()
raster_params.Init()
raster_params.colorBPP = 8
raster_params.colorType = 3
raster_params.extResolution = 96
raster_params.format = 0 # JPEG format
paths = {}
# Сохранение в JPG и DXF
for ext in ["jpg", "dxf"]:
full_path = self.prepare_export_path(doc_path, doc_name, ext)
if ext == "jpg":
doc_api5.SaveAsToRasterFormat(full_path, raster_params)
logger.info(f"[OK] Сохранен JPG: {full_path}")
try:
img = Image.open(full_path)
images.append(img.copy())
img.close()
except Exception as e:
logger.error(
f"Не удалось открыть изображение: {full_path}",
exc_info=True,
)
elif ext == "dxf":
doc_api5.ksSaveToDXF(full_path)
logger.info(f"Сохранен DXF: {full_path}")
paths[ext] = full_path
# Добавляем результат в список
result["result"].append(
{
"document_name": doc_name,
"document_type": doc_type,
"paths": paths,
"success": True,
"timestamp": time.time(),
}
)
except Exception as e:
logger.error(f"[ERROR] Не удалось создать PDF: {e}", exc_info=True)
result["result"].append(
{
"file": None,
"success": False,
"error": str(e),
"document_index": i + 1,
"timestamp": time.time(),
}
)
# Создание PDF
if images:
desktop_path = os.path.expanduser("~\\Desktop")
pdf_filename = f"{first_doc_name}_pages.pdf"
pdf_output_path = os.path.join(desktop_path, pdf_filename)
# Создаем титульную страницу с названием и количеством страниц
try:
font = ImageFont.truetype("arial.ttf", size=48)
except IOError:
print("Шрифт Arial не найден. Используется стандартный шрифт.")
font = ImageFont.load_default()
title_image = Image.new("RGB", (images[0].width, 200), color="white")
draw = ImageDraw.Draw(title_image)
title_text = f"{first_doc_name}\nКоличество страниц: {len(images)}"
draw.text((10, 50), title_text, fill="black", font=font, spacing=10)
images.insert(0, title_image)
# Сохраняем как PDF
try:
images[0].save(
pdf_output_path,
save_all=True,
append_images=images[1:],
format="PDF",
resolution=96.0,
)
logger.info(f"[OK] PDF успешно сохранен: {pdf_output_path}")
result["result"].append(
{
"document_name": pdf_output_path,
"document_type": "pdf",
"paths": pdf_output_path,
"success": True,
"timestamp": time.time(),
}
)
except Exception as e:
logger.info(f"[ERROR] Не удалось создать PDF: {e}")
return result
def get_available_actions(self):
"""
Возвращает список доступных действий и допустимые типы документов
Формат: {
action_key: {
'label': str,
'allowed_types': list,
'method': str,
'command': str
}
}
"""
# Допустимые типы документов
ALLOWED_TYPES_3D = [
self.constants.ksDocumentPart,
self.constants.ksDocumentAssembly,
]
ALLOWED_TYPES_2D = [
self.constants.ksDocumentDrawing,
self.constants.ksDocumentFragment,
]
ALLOWED_TYPES_SPEC = [self.constants.ksDocumentSpecification]
ALLOWED_TYPES_ALL = ALLOWED_TYPES_3D + ALLOWED_TYPES_2D + ALLOWED_TYPES_SPEC
return {
KompasCommand.IGES: {
"label": "Сохранить как IGES",
"allowed_types": ALLOWED_TYPES_3D,
"method": "save_to_iges",
"command": KompasCommand.IGES,
},
KompasCommand.STATS: {
"label": "Собрать статистику по элементам",
"allowed_types": ALLOWED_TYPES_3D,
"method": "collect_statistics",
"command": KompasCommand.STATS,
},
KompasCommand.EXPORT_RASTER: {
"label": "Экспортировать в PDF",
"allowed_types": ALLOWED_TYPES_2D + ALLOWED_TYPES_SPEC,
"method": "export_to_raster",
"command": KompasCommand.EXPORT_RASTER,
},
KompasCommand.PROJECT_SUPPORT: {
"label": "Создать чертежи деталей",
"allowed_types": ALLOWED_TYPES_3D,
"method": "create_drawing_for_parts",
"command": KompasCommand.PROJECT_SUPPORT,
},
}