logic
This commit is contained in:
parent
875103603c
commit
25ee7e317d
47
bridge.py
47
bridge.py
|
@ -6,12 +6,21 @@ from PyQt6.QtWebChannel import QWebChannel
|
|||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
|
||||
from enums import KompasCommand
|
||||
from logger import logger
|
||||
from parser.main import KompasDocumentParser
|
||||
|
||||
|
||||
class PythonEventEmitter(QObject):
|
||||
onEvent = pyqtSignal(str, "QVariantMap") # событие: тип + данные
|
||||
|
||||
|
||||
def format_response(success: bool, data=None, error: str = None):
|
||||
return json.dumps(
|
||||
{"status": "success" if success else "error", "data": data, "error": error},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
|
||||
class PythonJSBridge(QObject):
|
||||
def __init__(self, browser: QWebEngineView):
|
||||
super().__init__()
|
||||
|
@ -29,21 +38,29 @@ class PythonJSBridge(QObject):
|
|||
|
||||
@pyqtSlot(str, str, result=str)
|
||||
def callFromJS(self, command: str, data_json: str):
|
||||
print(f"[Python] Получена команда: {command}")
|
||||
data = None
|
||||
if command == KompasCommand.OPEN_KOMPAS:
|
||||
data = self.kompas.get_open_documents()
|
||||
if command == KompasCommand.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)
|
||||
logger.info(f"[Python] Получена команда: {command}")
|
||||
|
||||
try:
|
||||
if command == KompasCommand.OPEN_KOMPAS:
|
||||
data = self.kompas.get_open_documents()
|
||||
elif command == KompasCommand.GET_AVAILABLE_ACTIONS:
|
||||
data = self.kompas.get_available_actions()
|
||||
elif command == KompasCommand.IGES:
|
||||
data = self.kompas.save_to_iges() # <-- твой метод
|
||||
elif command == KompasCommand.EXPORT_RASTER:
|
||||
data = self.kompas.export_to_raster()
|
||||
elif command == KompasCommand.PROJECT_SUPPORT:
|
||||
data = self.kompas.create_drawing_for_parts()
|
||||
elif command == KompasCommand.STATS:
|
||||
data = self.kompas.collect_statistics()
|
||||
else:
|
||||
return format_response(False, error=f"Неизвестная команда: {command}")
|
||||
|
||||
return format_response(True, data=data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при выполнении команды {command}: {e}", exc_info=True)
|
||||
return format_response(False, error=str(e))
|
||||
|
||||
def send_event_to_js(self, event_type: str, data: dict):
|
||||
self.emitter.onEvent.emit(event_type, data)
|
|
@ -0,0 +1,29 @@
|
|||
# logger.py
|
||||
import logging
|
||||
import os
|
||||
|
||||
# Создаем корневой логгер
|
||||
logger = logging.getLogger('parser')
|
||||
logger.setLevel(logging.DEBUG) # Уровень по умолчанию: DEBUG и выше
|
||||
|
||||
# Проверяем, существует ли папка logs
|
||||
logs_dir = "logs"
|
||||
if not os.path.exists(logs_dir):
|
||||
os.makedirs(logs_dir)
|
||||
|
||||
# Форматтер для записей в логе
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(funcName)s - %(lineno)d - %(message)s'
|
||||
)
|
||||
|
||||
# Обработчик для вывода в консоль
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
console_handler.setFormatter(formatter)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
# Обработчик для записи в файл
|
||||
file_handler = logging.FileHandler(os.path.join(logs_dir, 'parser.log'), encoding='utf-8')
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
file_handler.setFormatter(formatter)
|
||||
logger.addHandler(file_handler)
|
|
@ -0,0 +1,24 @@
|
|||
2025-06-24 16:48:58,547 - parser - INFO - main - save_to_iges - 244 - Начинаем сохранение документов в формате IGES...
|
||||
2025-06-24 16:48:59,394 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Проф труба
|
||||
2025-06-24 16:49:00,892 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Сборка изделий
|
||||
2025-06-24 16:49:02,333 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Список деталей
|
||||
2025-06-24 16:49:03,686 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Nema 17
|
||||
2025-06-24 16:49:04,310 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Квадрат
|
||||
2025-06-24 16:49:04,842 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Цилиндр
|
||||
2025-06-24 16:49:48,697 - parser - INFO - main - save_to_iges - 244 - Начинаем сохранение документов в формате IGES...
|
||||
2025-06-24 16:49:49,345 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Проф труба
|
||||
2025-06-24 16:49:49,600 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Проф труба.igs
|
||||
2025-06-24 16:49:50,167 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Сборка изделий
|
||||
2025-06-24 16:49:50,554 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Сборка изделий.igs
|
||||
2025-06-24 16:49:50,947 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Список деталей
|
||||
2025-06-24 16:49:51,482 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Список деталей.igs
|
||||
2025-06-24 16:49:52,346 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Nema 17
|
||||
2025-06-24 16:49:52,485 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Nema 17.igs
|
||||
2025-06-24 16:49:52,986 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Квадрат
|
||||
2025-06-24 16:49:53,124 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Квадрат.igs
|
||||
2025-06-24 16:49:53,781 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Цилиндр
|
||||
2025-06-24 16:49:53,923 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Цилиндр.igs
|
||||
2025-06-24 17:00:05,175 - parser - INFO - bridge - callFromJS - 41 - [Python] Получена команда: open_kompas
|
||||
2025-06-24 17:00:09,976 - parser - INFO - bridge - callFromJS - 41 - [Python] Получена команда: open_kompas
|
||||
2025-06-24 17:01:37,671 - parser - INFO - bridge - callFromJS - 41 - [Python] Получена команда: open_kompas
|
||||
2025-06-24 17:01:40,023 - parser - INFO - bridge - callFromJS - 41 - [Python] Получена команда: open_kompas
|
124
parser/main.py
124
parser/main.py
|
@ -1,10 +1,11 @@
|
|||
import os
|
||||
import traceback
|
||||
import pythoncom
|
||||
import time
|
||||
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}",
|
||||
|
@ -58,6 +59,23 @@ class KompasDocumentParser:
|
|||
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):
|
||||
"""Возвращает список информации о всех открытых документах"""
|
||||
|
||||
|
@ -154,7 +172,9 @@ class KompasDocumentParser:
|
|||
print(f"Создаём чертёж для: {component.Name}")
|
||||
|
||||
# Создаём новый чертёж
|
||||
c_doc = self.application.Documents.Add(self.constants.ksDocumentDrawing)
|
||||
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
|
||||
|
@ -190,7 +210,6 @@ class KompasDocumentParser:
|
|||
20,
|
||||
)
|
||||
|
||||
|
||||
filename = "_".join(
|
||||
filter(
|
||||
None,
|
||||
|
@ -221,9 +240,12 @@ class KompasDocumentParser:
|
|||
|
||||
def save_to_iges(self):
|
||||
"""Сохраняет открытые 3D-документы (детали/сборки) в формате IGES"""
|
||||
print("Начинаем сохранение документов в формате 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:
|
||||
|
@ -232,13 +254,13 @@ class KompasDocumentParser:
|
|||
continue
|
||||
|
||||
doc_type = doc.DocumentType
|
||||
if doc_type not in av_actions[KompasCommand.IGES]["allowed_types"]:
|
||||
if doc_type not in allowed_types:
|
||||
continue
|
||||
|
||||
doc.Active = True
|
||||
doc_path = doc.Path
|
||||
doc_name = "-".join(doc.Name.split(".")[:-1])
|
||||
print(f"Попытка сохранить: {doc_name}")
|
||||
logger.info(f"Попытка сохранить: {doc_name}")
|
||||
|
||||
# Получаем 3D-документ через API v5
|
||||
doc_api5 = self.api5.ActiveDocument3D()
|
||||
|
@ -252,25 +274,39 @@ class KompasDocumentParser:
|
|||
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)
|
||||
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 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}")
|
||||
if not export_success:
|
||||
logger.error(f"Не удалось сохранить: {full_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
|
||||
traceback.print_exc()
|
||||
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_statistics(self):
|
||||
"""Сбор статистики по элементам, гибам и сваркам в активном документе"""
|
||||
|
@ -329,10 +365,12 @@ class KompasDocumentParser:
|
|||
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):
|
||||
if isinstance(
|
||||
item, self.api7_module.IUserDesignationCompObj
|
||||
):
|
||||
welding.append(item)
|
||||
except Exception as e:
|
||||
print('Ошибка в DrawingContext:', e)
|
||||
print("Ошибка в DrawingContext:", e)
|
||||
|
||||
try:
|
||||
doc_parts = self.api7_module.IParts7(part.Parts)
|
||||
|
@ -343,7 +381,7 @@ class KompasDocumentParser:
|
|||
look_features(element)
|
||||
find_el(element)
|
||||
except Exception as e:
|
||||
print('Ошибка в Parts:', e)
|
||||
print("Ошибка в Parts:", e)
|
||||
|
||||
if doc_type == self.constants.ksDocumentAssembly:
|
||||
find_el(top_part)
|
||||
|
@ -362,43 +400,55 @@ class KompasDocumentParser:
|
|||
|
||||
for e in elements:
|
||||
for key in sorted_stats.keys():
|
||||
if key == 'Name':
|
||||
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)
|
||||
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', 'Неизвестно')
|
||||
material = getattr(e, "Material", "Неизвестно")
|
||||
key_area = f"Площадь {material}, м²:"
|
||||
sorted_stats[key][key_area] = round(sorted_stats[key].get(key_area, 0) + area, 6)
|
||||
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[key][value] = (
|
||||
sorted_stats[key].get(value, 0) + 1
|
||||
)
|
||||
|
||||
sorted_stats['Area']['Total'] = sum(sorted_stats['Area'].values())
|
||||
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]
|
||||
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())
|
||||
|
||||
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('-----')
|
||||
print("-----")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
|
||||
|
|
Loading…
Reference in New Issue