add all last func

This commit is contained in:
ksenia_mikhailova 2025-06-24 13:41:38 +03:00
parent 27ea6e903e
commit 9230dbced0
3 changed files with 452 additions and 16 deletions

View File

@ -35,7 +35,14 @@ class PythonJSBridge(QObject):
data = self.kompas.get_open_documents() data = self.kompas.get_open_documents()
if command == KompasCommand.GET_AVAILABLE_ACTIONS: if command == KompasCommand.GET_AVAILABLE_ACTIONS:
data = self.kompas.get_available_actions() data = self.kompas.get_available_actions()
if command == KompasCommand.IGES:
data = self.kompas.save_to_iges()
if command == KompasCommand.EXPORT_RASTER:
data = self.kompas.export_to_raster()
if command == KompasCommand.PROJECT_SUPPORT:
data = self.kompas.create_drawing_for_parts()
if command == KompasCommand.STATS:
data = self.kompas.collect_statistics()
return json.dumps(data, ensure_ascii=False) return json.dumps(data, ensure_ascii=False)
def send_event_to_js(self, event_type: str, data: dict): def send_event_to_js(self, event_type: str, data: dict):

View File

@ -4,6 +4,11 @@ class KompasCommand(str, Enum):
OPEN_KOMPAS = "open_kompas" OPEN_KOMPAS = "open_kompas"
GET_AVAILABLE_ACTIONS = "get_available_actions" GET_AVAILABLE_ACTIONS = "get_available_actions"
IGES = "iges"
STATS = "stats"
EXPORT_RASTER = "export_raster"
PROJECT_SUPPORT = "project_support"
@classmethod @classmethod
def has_value(cls, value): def has_value(cls, value):
return any(value == item.value for item in cls) return any(value == item.value for item in cls)

View File

@ -2,6 +2,9 @@ import os
import traceback import traceback
import pythoncom import pythoncom
from win32com.client import Dispatch, gencache from win32com.client import Dispatch, gencache
from PIL import Image, ImageDraw, ImageFont
from enums import KompasCommand
ids = { ids = {
"api_5": "{0422828C-F174-495E-AC5D-D31014DBBE87}", "api_5": "{0422828C-F174-495E-AC5D-D31014DBBE87}",
@ -80,28 +83,441 @@ class KompasDocumentParser:
return documents return documents
# --- Заглушки для действий --- def create_drawing_for_parts(self):
"""
Создание чертежей для всех уникальных деталей из открытых деталей и сборок.
Также создаёт спецификацию для сборок.
"""
print("Начинаем создание чертежей для деталей...")
saving_path = "../cdw/"
def get_all_sheets(self): # Получаем доступные типы из разрешённых действий
"""Заглушка: Извлечение данных о листовых деталях""" av_actions = self.get_available_actions()
pass 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_path = doc.Path
# Сохранение чертежа
output_dir = os.path.join(doc_path, saving_path.strip("/"))
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Создана папка: {output_dir}")
doc_type = doc.DocumentType
if doc_type not in allowed_types:
continue
doc.Active = True
doc_name = "-".join(doc.Name.split(".")[:-1])
print(f"Обрабатываем документ: {doc_name}")
# Сохранение чертежа
output_dir = os.path.join(doc_path, saving_path.strip("/"))
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Создана папка: {output_dir}")
doc_3d = self.api7_module.IKompasDocument3D(doc)
doc_components = []
def all_elements(part):
"""Рекурсивный обход всех компонентов сборки"""
parts = self.api7_module.IParts7(part.Parts)
for j in range(parts.Count):
element = parts.Part(j)
if element.Parts.Count == 0:
if not element.Standard and not any(
el.Name == element.Name for el in doc_components
):
doc_components.append(element)
else:
all_elements(element)
if doc_type == self.constants.ksDocumentAssembly:
all_elements(doc_3d.TopPart)
else:
doc_components.append(doc_3d.TopPart)
for component in doc_components:
component_ipart = self.api7_module.IPart7(component)
if component_ipart.Standard:
continue
print(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]
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))]
g = max(c_size)
# Масштабирование
c_scale = c_sheet.Format.FormatHeight / sum(c_size)
# Получаем интерфейс 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 = os.path.join(output_dir, f"{filename}.cdw")
c_doc.SaveAs(full_path)
print(f"[OK] Сохранён чертёж: {full_path}")
# Сохранение спецификации для сборок
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 = os.path.join(output_dir, f"{filename}.spw")
spec.SaveAs(full_path)
print(f"[OK] Сохранена спецификация: {full_path}")
except Exception as e:
print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
traceback.print_exc()
def save_to_iges(self): def save_to_iges(self):
"""Заглушка: Экспорт в IGES""" """Сохраняет открытые 3D-документы (детали/сборки) в формате IGES"""
pass print("Начинаем сохранение документов в формате IGES...")
av_actions = self.get_available_actions()
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 av_actions[KompasCommand.IGES]["allowed_types"]:
continue
doc.Active = True
doc_path = doc.Path
doc_name = "-".join(doc.Name.split(".")[:-1])
print(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
ext = "igs"
output_dir = os.path.join(doc_path, ext)
filename = f"{doc_name}.{ext}"
full_path = os.path.join(output_dir, filename)
# Создаём директорию, если её нет
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# Сохраняем файл
result = doc_api5.SaveAsToAdditionFormat(full_path, save_params)
if result:
print(f"[OK] Сохранено: {full_path}")
else:
print(f"[ERROR] Не удалось сохранить: {full_path}")
except Exception as e:
print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
traceback.print_exc()
def collect_statistics(self): def collect_statistics(self):
"""Заглушка: Сбор статистики по элементам, гибам и сваркам""" """Сбор статистики по элементам, гибам и сваркам в активном документе"""
pass print("Начинаем сбор статистики по элементам...")
# Получаем доступные типы из разрешённых действий
av_actions = self.get_available_actions()
allowed_types = av_actions[KompasCommand.STATS]["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_name = "-".join(doc.Name.split(".")[:-1])
print(f"Обрабатываем документ: {doc_name}")
doc_3d = self.api7_module.IKompasDocument3D(doc)
top_part = doc_3d.TopPart
elements = []
bends = []
welding = []
def look_features(element):
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)
def look_drawing(part):
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)
def find_el(part):
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:
print('Ошибка в DrawingContext:', e)
try:
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)
look_features(element)
find_el(element)
except Exception as e:
print('Ошибка в Parts:', e)
if doc_type == self.constants.ksDocumentAssembly:
find_el(top_part)
else:
elements.append(top_part)
look_drawing(top_part)
look_features(top_part)
print(f"Найдено:\n Элементов {len(elements)}\n Гибов {len(bends)}\n")
sorted_stats = {
"Name": {},
"Material": {},
"Area": {},
}
for e in elements:
for key in sorted_stats.keys():
if key == 'Name':
value = f"{getattr(e, key)}, масса {round(getattr(e, 'Mass'), 3)}"
sorted_stats[key][value] = sorted_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}, м²:"
sorted_stats[key][key_area] = round(sorted_stats[key].get(key_area, 0) + area, 6)
else:
value = getattr(e, key)
sorted_stats[key][value] = sorted_stats[key].get(value, 0) + 1
sorted_stats['Area']['Total'] = sum(sorted_stats['Area'].values())
welding_key = "Welding"
for w in welding:
if welding_key not in sorted_stats:
sorted_stats[welding_key] = {}
w_name_split = w.Name.split('-')
w_len = w_name_split[-1].split('@')[0]
sorted_stats[welding_key][w.Name] = w_len
if welding_key in sorted_stats:
def float_f(n):
try:
return float(n)
except:
return 0
total_length = sum(float_f(v) for v in sorted_stats[welding_key].values())
sorted_stats[welding_key]["Total"] = total_length
for section in sorted_stats:
print(section)
for name, value in sorted_stats[section].items():
print(f"{name} -- {value}")
print('-----')
except Exception as e:
print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
traceback.print_exc()
def export_to_raster(self): def export_to_raster(self):
"""Заглушка: Экспорт в растровый формат""" """Экспорт открытых 2D-документов (чертежи, фрагменты, спецификации) в JPG и DXF. Создание многостраничного PDF."""
pass print("Начинаем экспорт документов в растровый формат...")
images = []
first_doc_name = None
# Получаем доступные типы для экспорта из разрешенных действий
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] # Имя без расширения
print(f"Обрабатываем документ: {doc_name}")
# Сохраняем имя первого документа
if first_doc_name is None:
doc7 = self.api7_module.IKompasDocument(doc)
first_doc_name = doc7.LayoutSheets.ItemByNumber(1).Stamp.Text(2).Str
# Получаем интерфейс 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
# Сохранение в JPG и DXF
for ext in ["jpg", "dxf"]:
output_dir = os.path.join(doc_path, ext)
filename = f"{doc_name}.{ext}"
full_path = os.path.join(output_dir, filename)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Создана папка: {output_dir}")
if ext == "jpg":
doc_api5.SaveAsToRasterFormat(full_path, raster_params)
print(f"[OK] Сохранен JPG: {full_path}")
images.append(Image.open(full_path))
elif ext == "dxf":
doc_api5.ksSaveToDXF(full_path)
print(f"[OK] Сохранен DXF: {full_path}")
except Exception as e:
print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
traceback.print_exc()
# Создание 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,
)
print(f"[OK] PDF успешно сохранен: {pdf_output_path}")
except Exception as e:
print(f"[ERROR] Не удалось создать PDF: {e}")
def get_available_actions(self): def get_available_actions(self):
""" """
Возвращает список доступных действий и допустимые типы документов Возвращает список доступных действий и допустимые типы документов
Формат: { action_key: { 'label': str, 'allowed_types': list } } Формат: {
action_key: {
'label': str,
'allowed_types': list,
'method': str,
'command': str
}
}
""" """
# Допустимые типы документов # Допустимые типы документов
ALLOWED_TYPES_3D = [ ALLOWED_TYPES_3D = [
@ -115,20 +531,28 @@ class KompasDocumentParser:
ALLOWED_TYPES_SPEC = [self.constants.ksDocumentSpecification] ALLOWED_TYPES_SPEC = [self.constants.ksDocumentSpecification]
ALLOWED_TYPES_ALL = ALLOWED_TYPES_3D + ALLOWED_TYPES_2D + ALLOWED_TYPES_SPEC ALLOWED_TYPES_ALL = ALLOWED_TYPES_3D + ALLOWED_TYPES_2D + ALLOWED_TYPES_SPEC
return { return {
"iges": { KompasCommand.IGES: {
"label": "Сохранить как IGES", "label": "Сохранить как IGES",
"allowed_types": ALLOWED_TYPES_3D, "allowed_types": ALLOWED_TYPES_3D,
"method": "save_to_iges",
"command": KompasCommand.IGES,
}, },
"stats": { KompasCommand.STATS: {
"label": "Собрать статистику по элементам", "label": "Собрать статистику по элементам",
"allowed_types": ALLOWED_TYPES_3D, "allowed_types": ALLOWED_TYPES_3D,
"method": "collect_statistics",
"command": KompasCommand.STATS,
}, },
"export_pdf": { KompasCommand.EXPORT_RASTER: {
"label": "Экспортировать в PDF", "label": "Экспортировать в PDF",
"allowed_types": ALLOWED_TYPES_2D + ALLOWED_TYPES_SPEC, "allowed_types": ALLOWED_TYPES_2D + ALLOWED_TYPES_SPEC,
"method": "export_to_raster",
"command": KompasCommand.EXPORT_RASTER,
}, },
"project_support": { KompasCommand.PROJECT_SUPPORT: {
"label": "Создать чертежи деталей", "label": "Создать чертежи деталей",
"allowed_types": ALLOWED_TYPES_3D, "allowed_types": ALLOWED_TYPES_3D,
"method": "create_drawing_for_parts",
"command": KompasCommand.PROJECT_SUPPORT,
}, },
} }