Compare commits

...

2 Commits

Author SHA1 Message Date
ksenia_mikhailova 9230dbced0 add all last func 2025-06-24 13:41:38 +03:00
ksenia_mikhailova 27ea6e903e actions list 2025-06-24 12:53:36 +03:00
5 changed files with 501 additions and 39 deletions

View File

@ -33,8 +33,17 @@ class PythonJSBridge(QObject):
data = None data = None
if command == KompasCommand.OPEN_KOMPAS: if command == KompasCommand.OPEN_KOMPAS:
data = self.kompas.get_open_documents() data = self.kompas.get_open_documents()
if command == KompasCommand.GET_AVAILABLE_ACTIONS:
return json.dumps(data) 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)
def send_event_to_js(self, event_type: str, data: dict): def send_event_to_js(self, event_type: str, data: dict):
self.emitter.onEvent.emit(event_type, data) self.emitter.onEvent.emit(event_type, data)

View File

@ -2,6 +2,12 @@ from enum import Enum
class KompasCommand(str, Enum): class KompasCommand(str, Enum):
OPEN_KOMPAS = "open_kompas" OPEN_KOMPAS = "open_kompas"
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):

View File

@ -7,23 +7,17 @@ from PyQt6.QtCore import QUrl, QThread
from config import MAIN_URL from config import MAIN_URL
from bridge import PythonJSBridge from bridge import PythonJSBridge
from parser.main import KompasDocumentParser
class KompasWorker(QThread): class KompasWorker(QThread):
def run(self): def run(self):
pythoncom.CoInitialize() pythoncom.CoInitialize()
self.parser = KompasDocumentParser()
class BrowserWindow(QMainWindow): class BrowserWindow(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setWindowTitle("QT6 Браузер") self.setWindowTitle("QT6 Браузер")
self.resize(1000, 800) self.resize(1366, 768)
self.kompas_thread = KompasWorker()
self.kompas_thread.start()
container = QWidget() container = QWidget()
layout = QVBoxLayout() layout = QVBoxLayout()

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}",
@ -22,6 +25,7 @@ class KompasDocumentParser:
"""Инициализация API КОМПАС версий 5 и 7""" """Инициализация API КОМПАС версий 5 и 7"""
try: try:
import pythoncom import pythoncom
pythoncom.CoInitialize() pythoncom.CoInitialize()
# Получаем API версии 5 # Получаем API версии 5
@ -62,7 +66,9 @@ class KompasDocumentParser:
for i in range(docs_collection.Count): for i in range(docs_collection.Count):
try: try:
doc = docs_collection.Item(i + 1) # Индексация с 1 doc = docs_collection.Item(i) # Индексация с 1
if doc is None:
continue
documents.append( documents.append(
{ {
@ -75,4 +81,478 @@ class KompasDocumentParser:
except Exception as e: except Exception as e:
print(f"Ошибка при обработке документа #{i + 1}: {e}") print(f"Ошибка при обработке документа #{i + 1}: {e}")
return documents return documents
def create_drawing_for_parts(self):
"""
Создание чертежей для всех уникальных деталей из открытых деталей и сборок.
Также создаёт спецификацию для сборок.
"""
print("Начинаем создание чертежей для деталей...")
saving_path = "../cdw/"
# Получаем доступные типы из разрешённых действий
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_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):
"""Сохраняет открытые 3D-документы (детали/сборки) в формате IGES"""
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):
"""Сбор статистики по элементам, гибам и сваркам в активном документе"""
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):
"""Экспорт открытых 2D-документов (чертежи, фрагменты, спецификации) в JPG и DXF. Создание многостраничного PDF."""
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):
"""
Возвращает список доступных действий и допустимые типы документов
Формат: {
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,
},
}

View File

@ -1,27 +0,0 @@
# kompas_worker.py
from PyQt6.QtCore import QObject, pyqtSignal
import pythoncom
from parser.main import KompasDocumentParser
class KompasWorker(QObject):
result_ready = pyqtSignal(object)
error_occurred = pyqtSignal(str)
def run_kompas(self):
try:
# Инициализируем COM правильно
pythoncom.CoInitializeEx(pythoncom.COINIT_APARTMENTTHREADED)
# Выполняем работу с КОМПАС
parser = KompasDocumentParser()
documents = parser.get_open_documents()
# Отправляем результат обратно
self.result_ready.emit(documents)
except Exception as e:
import traceback
error_msg = f"[Ошибка] {e}\n{traceback.format_exc()}"
self.error_occurred.emit(error_msg)
finally:
pythoncom.CoUninitialize()