#include "specificationpcbcontroller.h" #include "../model/specificationpcbtablemodel.h" #include "../model/designatormappingmodel.h" #include "../model/projectsettingsmodel.h" #include "../model/projectparamtablemodel.h" #include "../model/componenttablemodel.h" #include "../controller/maincontroller.h" #include #include SpecificationPCBController::SpecificationPCBController(QObject *parent) : QObject(parent) , m_tableModel(new SpecificationPCBTableModel(this)) , m_designatorMappingModel(nullptr) , m_projectSettingsModel(nullptr) , m_mainController(nullptr) , m_pcbDocFileName("") { // Устанавливаем маппинг по умолчанию для спецификации платы m_columnMappings << "Format" << "Zone" << "Position" << "Designator" << "Name" << "Quantity" << "Value"; } SpecificationPCBController::~SpecificationPCBController() { // m_tableModel удалится автоматически, так как он является дочерним объектом } void SpecificationPCBController::setDesignatorMappingModel(DesignatorMappingModel *mappingModel) { m_designatorMappingModel = mappingModel; } void SpecificationPCBController::setModel(SpecificationPCBTableModel *model) { if (m_tableModel != model) { qDebug() << "SpecificationPCBController::setModel: Заменяем модель таблицы"; m_tableModel = model; // Если модель не пуста, загружаем из неё настройки колонок if (m_tableModel) { QStringList columnMappings = m_tableModel->columnMappings(); if (!columnMappings.isEmpty()) { qDebug() << "SpecificationPCBController::setModel: Загружаем настройки колонок из модели:" << columnMappings; m_columnMappings = columnMappings; } } } } void SpecificationPCBController::setProjectSettingsModel(ProjectSettingsModel *model) { m_projectSettingsModel = model; qDebug() << "SpecificationPCBController::setProjectSettingsModel: Установлена модель настроек проекта"; } void SpecificationPCBController::setMainController(MainController *controller) { m_mainController = controller; qDebug() << "SpecificationPCBController::setMainController: Установлен главный контроллер"; } void SpecificationPCBController::setComponents(const QList &components) { qDebug() << "SpecificationPCBController::setComponents: Устанавливаем" << components.size() << "компонентов"; m_components = components; // Собираем доступные свойства из компонентов m_propertyNames = collectPropertyNames(); qDebug() << "SpecificationPCBController::setComponents: Собрано свойств:" << m_propertyNames.size(); qDebug() << "SpecificationPCBController::setComponents: Свойства:" << m_propertyNames; } void SpecificationPCBController::generateTableFromComponents() { if (!m_tableModel) { return; } qDebug() << "SpecificationPCBController::generateTableFromComponents: Начинаем генерацию таблицы"; qDebug() << "SpecificationPCBController::generateTableFromComponents: Текущие настройки колонок:" << m_columnMappings; qDebug() << "SpecificationPCBController::generateTableFromComponents: Количество компонентов:" << m_components.size(); // Очищаем таблицу m_tableModel->clear(); SpecificationPCBRowData emptyRow; emptyRow.isEmpty = true; m_tableModel->addRow(emptyRow); SpecificationPCBRowData headerRow; headerRow.isHeader = true; headerRow.format = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow.zone = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow.position = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow.designation = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow.name = SpecificationPCBCellData("Документация", 0, 1, true, false, false, 100, true); headerRow.quantity = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow.note = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); m_tableModel->addRow(headerRow); m_tableModel->addRow(emptyRow); SpecificationPCBRowData defaultRow1; defaultRow1.isHeader = false; defaultRow1.format = SpecificationPCBCellData("A1", 0, 1, false, false, false, 100, false); defaultRow1.zone = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow1.position = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow1.designation = SpecificationPCBCellData(getDecimalNumberBoard()+" СБ", 0, 1, false, false, false, 100, false); defaultRow1.name = SpecificationPCBCellData("Сборочный чертеж", 0, 1, false, false, false, 100, false); defaultRow1.quantity = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow1.note = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(defaultRow1); SpecificationPCBRowData defaultRow2; defaultRow2.isHeader = false; defaultRow2.format = SpecificationPCBCellData("A3", 0, 1, false, false, false, 100, false); defaultRow2.zone = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow2.position = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow2.designation = SpecificationPCBCellData(getDecimalNumberBoard()+" Э3", 0, 1, false, false, false, 100, false); defaultRow2.name = SpecificationPCBCellData("Схема электрическая принципиальная", 0, 1, false, false, false, 100, false); m_tableModel->addRow(defaultRow2); SpecificationPCBRowData defaultRow3; defaultRow3.isHeader = false; defaultRow3.format = SpecificationPCBCellData("A4", 0, 1, false, false, false, 100, false); defaultRow3.zone = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow3.position = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow3.designation = SpecificationPCBCellData(getDecimalNumberBoard()+" ПЭ3", 0, 1, false, false, false, 100, false); defaultRow3.name = SpecificationPCBCellData("Перечень элементов", 0, 1, false, false, false, 100, false); defaultRow3.quantity = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow3.note = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(defaultRow3); SpecificationPCBRowData defaultRow4; defaultRow4.isHeader = false; defaultRow4.format = SpecificationPCBCellData("A3", 0, 1, false, false, false, 100, false); defaultRow4.zone = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow4.position = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow4.designation = SpecificationPCBCellData(getDecimalNumberBoard()+" ВП", 0, 1, false, false, false, 100, false); defaultRow4.name = SpecificationPCBCellData("Ведомость покупных изделий", 0, 1, false, false, false, 100, false); defaultRow4.quantity = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); defaultRow4.note = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(defaultRow4); SpecificationPCBRowData defaultRow5; defaultRow5.isHeader = false; defaultRow5.format = SpecificationPCBCellData("*)", 0, 1, false); defaultRow5.zone = SpecificationPCBCellData("", 0, 1, false); defaultRow5.position = SpecificationPCBCellData("", 0, 1, false); defaultRow5.designation = SpecificationPCBCellData(getDecimalNumberBoard()+" Д33", 0, 1, false); defaultRow5.name = SpecificationPCBCellData("Данные результатов проектирования", 0, 1, false); defaultRow5.quantity = SpecificationPCBCellData("", 0, 1, false); defaultRow5.note = SpecificationPCBCellData("DVD диск", 0, 1, false); m_tableModel->addRow(defaultRow5); m_tableModel->addRow(emptyRow); SpecificationPCBRowData defaultRow5_1; defaultRow5_1.isHeader = false; defaultRow5_1.format = SpecificationPCBCellData("А4", 0, 1, false); defaultRow5_1.zone = SpecificationPCBCellData("", 0, 1, false); defaultRow5_1.position = SpecificationPCBCellData("", 0, 1, false); defaultRow5_1.designation = SpecificationPCBCellData(getDecimalNumberBoard()+" Д10-УЛ", 0, 1, false); defaultRow5_1.name = SpecificationPCBCellData("Удостоверяющий лист", 0, 1, false); defaultRow5_1.quantity = SpecificationPCBCellData("", 0, 1, false); defaultRow5_1.note = SpecificationPCBCellData("Размножать", 0, 1, false); m_tableModel->addRow(defaultRow5_1); SpecificationPCBRowData defaultRow5_2; defaultRow5_2.isHeader = false; defaultRow5_2.format = SpecificationPCBCellData("", 0, 1, false); defaultRow5_2.zone = SpecificationPCBCellData("", 0, 1, false); defaultRow5_2.position = SpecificationPCBCellData("", 0, 1, false); defaultRow5_2.designation = SpecificationPCBCellData("", 0, 1, false); defaultRow5_2.name = SpecificationPCBCellData("Данные результатов проектирования", 0, 1, false); defaultRow5_2.quantity = SpecificationPCBCellData("", 0, 1, false); defaultRow5_2.note = SpecificationPCBCellData("по указанию", 0, 1, false); m_tableModel->addRow(defaultRow5_2); m_tableModel->addRow(emptyRow); SpecificationPCBRowData headerRow2_4; headerRow2_4.isHeader = true; headerRow2_4.format = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_4.zone = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_4.position = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_4.designation = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_4.name = SpecificationPCBCellData("Сборочные единицы", 0, 1, true, false, false, 100, true); headerRow2_4.quantity = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_4.note = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); m_tableModel->addRow(headerRow2_4); m_tableModel->addRow(emptyRow); SpecificationPCBRowData pcbInfo; pcbInfo.isHeader = false; pcbInfo.format = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); pcbInfo.zone = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); pcbInfo.position = SpecificationPCBCellData("1", 0, 1, false, false, false, 100, false); pcbInfo.designation = SpecificationPCBCellData(getDecimalNumberBoard()+" платы", 0, 1, false, false, false, 100, false); pcbInfo.name = SpecificationPCBCellData(getBoardName(), 0, 1, false, false, false, 100, false); pcbInfo.quantity = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); pcbInfo.note = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(pcbInfo); m_tableModel->addRow(emptyRow); SpecificationPCBRowData headerRow2_1; headerRow2_1.isHeader = true; headerRow2_1.format = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_1.zone = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_1.position = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_1.designation = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_1.name = SpecificationPCBCellData("Стандартные изделия", 0, 1, true, false, false, 100, true); headerRow2_1.quantity = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2_1.note = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); m_tableModel->addRow(headerRow2_1); m_tableModel->addRow(emptyRow); SpecificationPCBRowData headerRow2; headerRow2.isHeader = true; headerRow2.format = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2.zone = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2.position = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2.designation = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2.name = SpecificationPCBCellData("Детали", 0, 1, true, false, false, 100, true); headerRow2.quantity = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow2.note = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); m_tableModel->addRow(headerRow2); SpecificationPCBRowData headerRow3; headerRow3.isHeader = true; headerRow3.format = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow3.zone = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow3.position = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow3.designation = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow3.name = SpecificationPCBCellData("Прочие изделия", 0, 1, true, false, false, 100, true); int pos1 = 2; m_tableModel->addRow(headerRow3); m_tableModel->addRow(emptyRow); if (m_components.isEmpty()) { return; } try { // Сортируем компоненты по десигнатору std::sort(m_components.begin(), m_components.end(), [this](ComponentModel* a, ComponentModel* b) { if (!a || !b) return false; QString designatorA = a->designator(); QString designatorB = b->designator(); // Разделяем десигнаторы на буквенную и числовую части QPair partsA = SpecificationPCBController::splitDesignator(designatorA); QPair partsB = SpecificationPCBController::splitDesignator(designatorB); QString letterA = partsA.first; QString letterB = partsB.first; QString numberA = partsA.second; QString numberB = partsB.second; // Сначала сравниваем буквенные части if (letterA != letterB) { return letterA < letterB; } // Если буквенные части одинаковые, сравниваем числовые части bool okA, okB; int numA = numberA.toInt(&okA); int numB = numberB.toInt(&okB); if (okA && okB) { return numA < numB; } // Fallback: если не удалось преобразовать в числа, используем строковое сравнение return numberA < numberB; }); // Группируем компоненты по буквенной части десигнатора QMap> groupedByLetter; for (ComponentModel *component : m_components) { QPair designatorParts = splitDesignator(component->designator()); QString desLetter = designatorParts.first; groupedByLetter[desLetter].append(component); } // Обрабатываем каждую группу for (auto it = groupedByLetter.begin(); it != groupedByLetter.end(); ++it) { QString desLetter = it.key(); QList componentsInGroup = it.value(); if (componentsInGroup.size() > 1) { // Если компонентов больше одного, добавляем заголовок типа SpecificationPCBRowData headerRow; headerRow.isHeader = true; headerRow.format = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow.zone = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow.position = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow.designation = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); // Заголовок с componentType не должен подчеркиваться headerRow.name = SpecificationPCBCellData(getComponentType(componentsInGroup.first()), 0, 1, true, false, false, 100, false); headerRow.quantity = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); headerRow.note = SpecificationPCBCellData("", 0, 1, true, false, false, 100, true); m_tableModel->addEmptyRow(); m_tableModel->addRow(headerRow); m_tableModel->addEmptyRow(); // Сначала группируем компоненты по наименованию QList>> groupedByName; for (ComponentModel *component : componentsInGroup) { QString componentName = getComponentValue(component, "ManufacturerPartNumber"); // Ищем существующую группу bool found = false; for (auto &group : groupedByName) { if (group.first == componentName) { group.second.append(component); found = true; break; } } // Если группа не найдена, создаем новую if (!found) { QList newGroup; newGroup.append(component); groupedByName.append(qMakePair(componentName, newGroup)); } } // Теперь создаем строки для каждой группы по наименованию for (const auto &nameGroup : groupedByName) { QString componentName = nameGroup.first; QList componentsWithSameName = nameGroup.second; // Подсчитываем общее количество компонентов int totalQuantity = componentsWithSameName.size(); // Создаем строку для этой группы компонентов ComponentModel *firstComponent = componentsWithSameName.first(); QMap props = firstComponent->properties(); QString originalString = props.value("ManufacturerPartNumber", ""); QList designationParts = splitLongDesignation(componentName, originalString, firstComponent); // Группируем десигнаторы только для компонентов с этим наименованием // Сначала сортируем компоненты по десигнатору (численно) std::sort(componentsWithSameName.begin(), componentsWithSameName.end(), [this](ComponentModel* a, ComponentModel* b) { if (!a || !b) return false; QString designatorA = a->designator(); QString designatorB = b->designator(); // Разделяем десигнаторы на буквенную и числовую части QPair partsA = splitDesignator(designatorA); QPair partsB = splitDesignator(designatorB); QString letterA = partsA.first; QString letterB = partsB.first; QString numberA = partsA.second; QString numberB = partsB.second; // Сначала сравниваем буквенные части if (letterA != letterB) { return letterA < letterB; } // Если буквенные части одинаковые, сравниваем числовые части bool okA, okB; int numA = numberA.toInt(&okA); int numB = numberB.toInt(&okB); if (okA && okB) { return numA < numB; } // Fallback: если не удалось преобразовать в числа, используем строковое сравнение return numberA < numberB; }); QList designatorGroupsForThisName; QString currentGroup = componentsWithSameName.first()->designator(); QString lastDesignator = currentGroup; int groupCount = 1; // Счетчик элементов в текущей группе for (int i = 1; i < componentsWithSameName.size(); ++i) { QString currentDesignator = componentsWithSameName[i]->designator(); QPair currentParts = splitDesignator(currentDesignator); QPair lastParts = splitDesignator(lastDesignator); // Проверяем, является ли текущий десигнатор следующим по порядку if (currentParts.first == lastParts.first) { bool ok1, ok2; int currentNum = currentParts.second.toInt(&ok1); int lastNum = lastParts.second.toInt(&ok2); if (ok1 && ok2 && currentNum == lastNum + 1) { // Продолжаем группу lastDesignator = currentDesignator; groupCount++; } else { // Завершаем текущую группу и начинаем новую if (currentGroup == lastDesignator) { designatorGroupsForThisName.append(currentGroup); } else { // Если в группе 2 элемента - через запятую, иначе через дефис if (groupCount == 2) { designatorGroupsForThisName.append(currentGroup + ", " + lastDesignator); } else { designatorGroupsForThisName.append(currentGroup + "-" + lastDesignator); } } currentGroup = currentDesignator; lastDesignator = currentDesignator; groupCount = 1; } } else { // Завершаем текущую группу и начинаем новую if (currentGroup == lastDesignator) { designatorGroupsForThisName.append(currentGroup); } else { // Если в группе 2 элемента - через запятую, иначе через дефис if (groupCount == 2) { designatorGroupsForThisName.append(currentGroup + ", " + lastDesignator); } else { designatorGroupsForThisName.append(currentGroup + "-" + lastDesignator); } } currentGroup = currentDesignator; lastDesignator = currentDesignator; groupCount = 1; } } // Добавляем последнюю группу if (currentGroup == lastDesignator) { designatorGroupsForThisName.append(currentGroup); } else { // Если в группе 2 элемента - через запятую, иначе через дефис if (groupCount == 2) { designatorGroupsForThisName.append(currentGroup + ", " + lastDesignator); } else { designatorGroupsForThisName.append(currentGroup + "-" + lastDesignator); } } // Разбиваем длинные группы десигнаторов (11 символов на строку) QStringList designatorsSplit = splitDesignatorGroups(designatorGroupsForThisName, 11); // Определяем максимальное количество строк для названия и десигнаторов int maxRows = qMax(designationParts.size(), designatorsSplit.size()); for (int rowIndex = 0; rowIndex < maxRows; ++rowIndex) { SpecificationPCBRowData row; // Только в первой строке показываем позицию и количество if (rowIndex == 0) { row.position = SpecificationPCBCellData(QString::number(pos1++), 0, 1, false, false, false, 100, false); row.quantity = SpecificationPCBCellData(QString::number(totalQuantity), 0, 1, false, false, false, 100, false); } else { // В последующих строках оставляем пустыми позицию и количество row.position = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); row.quantity = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); } // Заполняем название (если есть) if (rowIndex < designationParts.size()) { row.name = SpecificationPCBCellData(designationParts[rowIndex], 0, 1, false, false, false, 100, false); } else { row.name = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); } // Заполняем десигнаторы (если есть) if (rowIndex < designatorsSplit.size()) { row.note = SpecificationPCBCellData(designatorsSplit[rowIndex], 0, 1, false, false, false, 100, false); } else { row.note = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); } // Заполняем остальные поля согласно требованиям row.format = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); row.zone = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); row.designation = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(row); } } } else { // Если компонент один, добавляем его как отдельную строку ComponentModel *component = componentsInGroup.first(); QString currentDesignation = getComponentValue(component, "ManufacturerPartNumber"); // Разделяем длинное наименование на несколько строк QMap props = component->properties(); QString originalString = props.value("ManufacturerPartNumber", ""); QList designationParts = splitLongDesignation(currentDesignation, originalString, component); for (int partIndex = 0; partIndex < designationParts.size(); ++partIndex) { SpecificationPCBRowData row; // Только в первой строке показываем позицию и количество if (partIndex == 0) { row.position = SpecificationPCBCellData(QString::number(pos1++), 0, 1, false, false, false, 100, false); row.quantity = SpecificationPCBCellData("1", 0, 1, false, false, false, 100, false); row.note = SpecificationPCBCellData(component->designator(), 0, 1, false, false, false, 100, false); } else { // В последующих строках оставляем пустыми позицию и количество row.position = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); row.quantity = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); row.note = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); } // Заполняем остальные поля согласно требованиям row.format = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); row.zone = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); row.designation = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false); row.name = SpecificationPCBCellData(designationParts[partIndex], 0, 1, false, false, false, 100, false); m_tableModel->addRow(row); } } } qDebug() << "SpecificationPCBController::generateTableFromComponents: Таблица сгенерирована, строк:" << m_tableModel->rowCount(); qDebug() << "SpecificationPCBController::generateTableFromComponents: Модель доступна:" << (m_tableModel ? "да" : "нет"); qDebug() << "SpecificationPCBController::generateTableFromComponents: Количество строк в модели:" << m_tableModel->rowCount(); qDebug() << "SpecificationPCBController::generateTableFromComponents: Количество колонок в модели:" << m_tableModel->columnCount(); // Оптимизируем разрывы страниц после генерации таблицы m_tableModel->optimizePageBreaks(); } catch (const std::exception &e) { qDebug() << "SpecificationPCBController::generateTableFromComponents: Ошибка при генерации:" << e.what(); } catch (...) { qDebug() << "SpecificationPCBController::generateTableFromComponents: Неизвестная ошибка при генерации"; } m_tableModel->addRow(emptyRow); SpecificationPCBRowData defaultRow6; defaultRow6.isHeader = false; defaultRow6.format = SpecificationPCBCellData("", 0, 1, false); defaultRow6.zone = SpecificationPCBCellData("", 0, 1, false); defaultRow6.position = SpecificationPCBCellData("", 0, 1, false); defaultRow6.designation = SpecificationPCBCellData("", 0, 1, false); defaultRow6.name = SpecificationPCBCellData("Примечание", 0, 1, false); defaultRow6.quantity = SpecificationPCBCellData("", 0, 1, false); defaultRow6.note = SpecificationPCBCellData("", 0, 1, false); m_tableModel->addRow(defaultRow6); SpecificationPCBRowData defaultRow6_1; defaultRow6_1.isHeader = false; defaultRow6_1.format = SpecificationPCBCellData("", 0, 1, false); defaultRow6_1.zone = SpecificationPCBCellData("", 0, 1, false); defaultRow6_1.position = SpecificationPCBCellData("", 0, 1, false); defaultRow6_1.designation = SpecificationPCBCellData("", 0, 1, false); defaultRow6_1.name = SpecificationPCBCellData("Изготовить плату печатную", 0, 1, false); defaultRow6_1.quantity = SpecificationPCBCellData("", 0, 1, false); defaultRow6_1.note = SpecificationPCBCellData("", 0, 1, false); m_tableModel->addRow(defaultRow6_1); SpecificationPCBRowData defaultRow6_2; defaultRow6_2.isHeader = false; defaultRow6_2.format = SpecificationPCBCellData("", 0, 1, false); defaultRow6_2.zone = SpecificationPCBCellData("", 0, 1, false); defaultRow6_2.position = SpecificationPCBCellData("", 0, 1, false); defaultRow6_2.designation = SpecificationPCBCellData("", 0, 1, false); // Формируем строку "По файлу" с именем PcbDoc файла, если оно есть QString fileText = "По файлу"; if (!m_pcbDocFileName.isEmpty()) { QFileInfo fileInfo(m_pcbDocFileName); fileText = "По файлу " + fileInfo.fileName(); } defaultRow6_2.name = SpecificationPCBCellData(fileText, 0, 1, false); defaultRow6_2.quantity = SpecificationPCBCellData("", 0, 1, false); defaultRow6_2.note = SpecificationPCBCellData("", 0, 1, false); m_tableModel->addRow(defaultRow6_2); SpecificationPCBRowData defaultRow6_3; defaultRow6_3.isHeader = false; defaultRow6_3.format = SpecificationPCBCellData("", 0, 1, false); defaultRow6_3.zone = SpecificationPCBCellData("", 0, 1, false); defaultRow6_3.position = SpecificationPCBCellData("", 0, 1, false); defaultRow6_3.designation = SpecificationPCBCellData("", 0, 1, false); defaultRow6_3.name = SpecificationPCBCellData("из состава"+getDecimalNumberBoard()+" Д10", 0, 1, false); defaultRow6_3.quantity = SpecificationPCBCellData("", 0, 1, false); defaultRow6_3.note = SpecificationPCBCellData("", 0, 1, false); m_tableModel->addRow(defaultRow6_3); } void SpecificationPCBController::updateTableFromComponents() { if (!m_tableModel) { qDebug() << "SpecificationPCBController::updateTableFromComponents: m_tableModel is null"; return; } qDebug() << "SpecificationPCBController::updateTableFromComponents: Обновляем таблицу из" << m_components.size() << "компонентов"; if (m_components.isEmpty()) { qDebug() << "SpecificationPCBController::updateTableFromComponents: Нет компонентов для обновления"; return; } try { // Метод updateTableFromComponents больше не используется в новой логике qDebug() << "SpecificationPCBController::updateTableFromComponents: Метод устарел"; } catch (const std::exception &e) { qDebug() << "SpecificationPCBController::updateTableFromComponents: Ошибка при обновлении:" << e.what(); } catch (...) { qDebug() << "SpecificationPCBController::updateTableFromComponents: Неизвестная ошибка при обновлении"; } } QString SpecificationPCBController::getComponentValue(ComponentModel *component, const QString &propertyName) { if (!component) { return ""; } qDebug() << "SpecificationPCBController::getComponentValue: propertyName =" << propertyName; // Получаем значение свойства компонента QString value; if (propertyName == "Designator") { value = component->designator(); qDebug() << "SpecificationPCBController::getComponentValue: Designator =" << value; } else { QMap props = component->properties(); value = props.value(propertyName, ""); qDebug() << "SpecificationPCBController::getComponentValue: Свойство" << propertyName << "=" << value; } // Проверяем, является ли значение свойства выражением if (value.startsWith("=")) { qDebug() << "SpecificationPCBController::getComponentValue: Значение свойства является выражением:" << value; return parseExpression(component, value); } return value; } QString SpecificationPCBController::parseExpression(ComponentModel *component, const QString &expression) { if (!component) { return ""; } qDebug() << "SpecificationPCBController::parseExpression: Обрабатываем выражение:" << expression; QString result; QString expr; // Определяем, как обрабатывать выражение if (expression.startsWith("=")) { expr = expression.mid(1); // Убираем начальный "=" } else if (expression.startsWith("\"") && expression.endsWith("\"")) { // Если это просто значение в кавычках, возвращаем его без кавычек return expression.mid(1, expression.length() - 2); } else { // Если это не выражение, возвращаем как есть return expression; } // Разбиваем выражение по операторам "+" с учетом кавычек QStringList parts; QString currentPart; bool inDoubleQuotes = false; bool inSingleQuotes = false; for (int i = 0; i < expr.length(); ++i) { QChar ch = expr[i]; if (ch == '"' && !inSingleQuotes) { inDoubleQuotes = !inDoubleQuotes; currentPart += ch; } else if (ch == '\'' && !inDoubleQuotes) { inSingleQuotes = !inSingleQuotes; currentPart += ch; } else if (ch == '+' && !inDoubleQuotes && !inSingleQuotes) { // Это разделитель "+" вне кавычек if (!currentPart.trimmed().isEmpty()) { parts.append(currentPart.trimmed()); } currentPart.clear(); } else { currentPart += ch; } } // Добавляем последнюю часть if (!currentPart.trimmed().isEmpty()) { parts.append(currentPart.trimmed()); } qDebug() << "SpecificationPCBController::parseExpression: Части выражения:" << parts; for (const QString &part : parts) { QString trimmedPart = part.trimmed(); qDebug() << "SpecificationPCBController::parseExpression: Обрабатываем часть:" << trimmedPart; // Если это не пустая строка, добавляем к результату if (!trimmedPart.isEmpty()) { QString value; // Проверяем, является ли это литералом в одинарных кавычках if (trimmedPart.startsWith("'") && trimmedPart.endsWith("'")) { // Это литерал - убираем кавычки и используем как есть value = trimmedPart.mid(1, trimmedPart.length() - 2); qDebug() << "SpecificationPCBController::parseExpression: Найден литерал в одинарных кавычках:" << value; } // Проверяем, является ли это именем свойства компонента в двойных кавычках else if (trimmedPart.startsWith("\"") && trimmedPart.endsWith("\"")) { // Это свойство компонента - убираем кавычки и ищем в свойствах QString propertyName = trimmedPart.mid(1, trimmedPart.length() - 2); qDebug() << "SpecificationPCBController::parseExpression: Ищем свойство компонента:" << propertyName; if (propertyName == "Designator") { value = component->designator(); qDebug() << "SpecificationPCBController::parseExpression: Найдено свойство Designator =" << value; } else { // Ищем в свойствах компонента QMap props = component->properties(); value = props.value(propertyName, ""); qDebug() << "SpecificationPCBController::parseExpression: Ищем свойство" << propertyName << "в компоненте, результат:" << value; // Рекурсивно обрабатываем вложенные выражения if (value.startsWith("=")) { qDebug() << "SpecificationPCBController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем"; value = parseExpression(component, value); } } } // Проверяем, является ли это именем свойства компонента без кавычек else if (trimmedPart == "Designator") { value = component->designator(); qDebug() << "SpecificationPCBController::parseExpression: Найдено свойство Designator =" << value; } else { // Ищем в свойствах компонента QMap props = component->properties(); value = props.value(trimmedPart, ""); qDebug() << "SpecificationPCBController::parseExpression: Ищем свойство" << trimmedPart << "в компоненте, результат:" << value; // Рекурсивно обрабатываем вложенные выражения if (value.startsWith("=")) { qDebug() << "SpecificationPCBController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем"; value = parseExpression(component, value); } } // Добавляем значение к результату if (!value.isEmpty()) { if (!result.isEmpty()) { result += value; } else { result = value; } qDebug() << "SpecificationPCBController::parseExpression: Добавили к результату, текущий результат:" << result; } } } qDebug() << "SpecificationPCBController::parseExpression: Финальный результат:" << result; return result; } QStringList SpecificationPCBController::collectPropertyNames() { QStringList propertyNames; // Добавляем основные свойства компонента propertyNames << "Designator" << "Format" << "Zone" << "Position"; // Собираем дополнительные свойства из компонентов for (ComponentModel *component : m_components) { QMap props = component->properties(); for (auto it = props.begin(); it != props.end(); ++it) { if (!propertyNames.contains(it.key())) { propertyNames.append(it.key()); } } } // Если дополнительные свойства не найдены в компонентах, добавляем стандартные if (propertyNames.size() <= 4) { // Только базовые свойства propertyNames << "Name" << "Type" << "Value" << "Footprint" << "Description"; qDebug() << "SpecificationPCBController: Дополнительные свойства не найдены, используем стандартные:" << propertyNames; } // Добавляем специальное свойство для количества компонентов в группе propertyNames << "Quantity" << "Кол."; return propertyNames; } // Методы для работы с децимальными номерами QString SpecificationPCBController::getDecimalNumberBoard() const { QStringList mappings = getColumnMappings(); if (mappings.size() > 7) { QString fieldName = mappings[7]; if (!fieldName.isEmpty()) { // Получаем значение из projectparamtable if (m_mainController) { ProjectParamTableModel *paramModel = m_mainController->projectParamTableModel(); if (paramModel) { QMap params = paramModel->getDataAsMap(); if (params.contains(fieldName)) { return params[fieldName]; // Возвращаем значение, а не название поля } } } // Если не удалось получить значение из модели, возвращаем название поля return fieldName; } } return QString(); } void SpecificationPCBController::setDecimalNumberBoard(const QString &value) { QStringList mappings = getColumnMappings(); while (mappings.size() <= 7) { mappings.append(""); } mappings[7] = value; setColumnMappings(mappings); } QString SpecificationPCBController::getDecimalNumberDocument() const { QStringList mappings = getColumnMappings(); if (mappings.size() > 8) { QString fieldName = mappings[8]; if (!fieldName.isEmpty()) { // Получаем значение из projectparamtable if (m_mainController) { ProjectParamTableModel *paramModel = m_mainController->projectParamTableModel(); if (paramModel) { QMap params = paramModel->getDataAsMap(); if (params.contains(fieldName)) { return params[fieldName]; // Возвращаем значение, а не название поля } } } // Если не удалось получить значение из модели, возвращаем название поля return fieldName; } } return QString(); } void SpecificationPCBController::setDecimalNumberDocument(const QString &value) { QStringList mappings = getColumnMappings(); while (mappings.size() <= 8) { mappings.append(""); } mappings[8] = value; setColumnMappings(mappings); } // Методы для работы с названием платы QString SpecificationPCBController::getBoardName() const { QStringList mappings = getColumnMappings(); if (mappings.size() > 6) { QString fieldName = mappings[6]; if (!fieldName.isEmpty()) { // Получаем значение из projectparamtable if (m_mainController) { ProjectParamTableModel *paramModel = m_mainController->projectParamTableModel(); if (paramModel) { QMap params = paramModel->getDataAsMap(); if (params.contains(fieldName)) { return params[fieldName]; // Возвращаем значение, а не название поля } } } // Если не удалось получить значение из модели, возвращаем название поля return fieldName; } } return QString(); } void SpecificationPCBController::setBoardName(const QString &value) { QStringList mappings = getColumnMappings(); while (mappings.size() <= 6) { mappings.append(""); } mappings[6] = value; setColumnMappings(mappings); } void SpecificationPCBController::setPcbDocFileName(const QString &fileName) { m_pcbDocFileName = fileName; qDebug() << "SpecificationPCBController::setPcbDocFileName: Установлено имя файла:" << fileName; } QString SpecificationPCBController::getPcbDocFileName() const { return m_pcbDocFileName; } void SpecificationPCBController::setColumnMappings(const QStringList &mappings) { qDebug() << "SpecificationPCBController::setColumnMappings: Устанавливаем новые настройки колонок:" << mappings; m_columnMappings = mappings; // Сохраняем настройки в ProjectSettingsModel if (m_projectSettingsModel) { for (int i = 0; i < mappings.size(); ++i) { m_projectSettingsModel->setColumnMapping("SpecificationPCB", i, mappings[i]); } qDebug() << "SpecificationPCBController::setColumnMappings: Настройки сохранены в ProjectSettingsModel"; } // Обновляем настройки в модели if (m_tableModel) { m_tableModel->setColumnMappings(mappings); qDebug() << "SpecificationPCBController::setColumnMappings: Настройки обновлены в модели"; } qDebug() << "SpecificationPCBController::setColumnMappings: Настройки колонок обновлены:" << m_columnMappings; } QStringList SpecificationPCBController::getColumnMappings() const { // Если есть ProjectSettingsModel, читаем настройки из него if (m_projectSettingsModel) { QStringList settings = m_projectSettingsModel->getColumnMappings("SpecificationPCB"); if (!settings.isEmpty()) { qDebug() << "SpecificationPCBController::getColumnMappings: Получены настройки из ProjectSettingsModel:" << settings; return settings; } } // Fallback: возвращаем локальные настройки qDebug() << "SpecificationPCBController::getColumnMappings: Возвращаем локальные настройки:" << m_columnMappings; return m_columnMappings; } QStringList SpecificationPCBController::getAvailableProperties() const { QStringList properties = m_propertyNames; properties.prepend("<Пусто>"); properties.prepend("<Авто>"); return properties; } QString SpecificationPCBController::getComponentType(ComponentModel *component) { if (!component) return ""; // Получаем тип компонента из маппинга дезигнаторов if (m_designatorMappingModel) { QString componentType = m_designatorMappingModel->getComponentType(component->designator(), true); // true для множественного числа if (!componentType.isEmpty()) { return componentType; } } // Если маппинг не найден, используем тип из свойств компонента QMap props = component->properties(); QString type = props.value("Type", ""); if (!type.isEmpty()) { return type; } // Если и тип не найден, используем designator как fallback return component->designator(); } QString SpecificationPCBController::getComponentFormat(ComponentModel *component) { if (!component) return ""; // Получаем формат из свойств компонента QMap props = component->properties(); QString format = props.value("Format", ""); if (!format.isEmpty()) { return format; } // Если формат не найден, используем значение по умолчанию return "A4"; } QString SpecificationPCBController::getComponentZone(ComponentModel *component) { if (!component) return ""; // Получаем зону из свойств компонента QMap props = component->properties(); QString zone = props.value("Zone", ""); if (!zone.isEmpty()) { return zone; } // Если зона не найдена, используем значение по умолчанию return "1"; } QString SpecificationPCBController::getComponentPosition(ComponentModel *component) { if (!component) return ""; // Для спецификации платы позиция обычно совпадает с дезигнатором return component->designator(); } QPair SpecificationPCBController::splitDesignator(const QString &designator) { if (designator.isEmpty()) { return QPair("", ""); } // Используем регулярное выражение для разделения буквенной и цифровой части QRegExp regex("([A-Za-z]+)(\\d*)"); if (regex.indexIn(designator) != -1) { QString letterPart = regex.cap(1); QString numberPart = regex.cap(2); return QPair(letterPart, numberPart); } // Fallback: если регулярное выражение не сработало, // ищем первую цифру и разделяем по ней for (int i = 0; i < designator.length(); ++i) { if (designator[i].isDigit()) { QString letterPart = designator.left(i); QString numberPart = designator.mid(i); return QPair(letterPart, numberPart); } } // Если цифр нет, возвращаем весь десигнатор как буквенную часть return QPair(designator, ""); } // Новые методы для управления строками void SpecificationPCBController::addEmptyRow() { if (m_tableModel) { m_tableModel->addEmptyRow(); qDebug() << "SpecificationPCBController::addEmptyRow: Добавлена пустая строка"; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void SpecificationPCBController::addEmptyRowAt(int position) { if (m_tableModel) { m_tableModel->addEmptyRowAt(position); qDebug() << "SpecificationPCBController::addEmptyRowAt: Добавлена пустая строка в позицию" << position; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void SpecificationPCBController::insertEmptyRowAt(int position) { if (m_tableModel) { m_tableModel->addEmptyRowAt(position); qDebug() << "SpecificationPCBController::insertEmptyRowAt: Вставлена пустая строка в позицию" << position; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void SpecificationPCBController::removeRow(int row) { if (m_tableModel) { m_tableModel->removeRow(row); qDebug() << "SpecificationPCBController::removeRow: Удалена строка" << row; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void SpecificationPCBController::clearTable() { if (m_tableModel) { m_tableModel->clear(); qDebug() << "SpecificationPCBController::clearTable: Таблица очищена"; } } void SpecificationPCBController::optimizePageBreaks() { if (m_tableModel) { qDebug() << "SpecificationPCBController::optimizePageBreaks: Оптимизируем разрывы страниц"; m_tableModel->optimizePageBreaks(); // После оптимизации разрывов пересчитываем позиции recalculatePositions(); } } void SpecificationPCBController::recalculatePositions() { if (!m_tableModel) { qDebug() << "SpecificationPCBController::recalculatePositions: m_tableModel is null, выходим"; return; } qDebug() << "SpecificationPCBController::recalculatePositions: Начинаем перерасчет позиций, строк в таблице:" << m_tableModel->rowCount(); const int POSITION_COLUMN = 2; // Индекс колонки "Поз." int rowCount = m_tableModel->rowCount(); int currentPosition = 0; int updatedCount = 0; // Сначала собираем список строк, где позиция не пустая (пользователь ввел значение) QList rowsWithPosition; for (int row = 0; row < rowCount; ++row) { QModelIndex index = m_tableModel->index(row, POSITION_COLUMN); if (!index.isValid()) { qDebug() << "SpecificationPCBController::recalculatePositions: Строка" << row << "- невалидный индекс"; continue; } // Получаем значение позиции СНАЧАЛА, чтобы проверить, есть ли оно QString positionValueRaw = m_tableModel->data(index, Qt::DisplayRole).toString(); QString positionValue = positionValueRaw.trimmed(); // Детальное логирование для диагностики qDebug() << "SpecificationPCBController::recalculatePositions: Строка" << row << "- позиция (raw): '" << positionValueRaw << "'" << ", позиция (trimmed): '" << positionValue << "'" << ", isEmpty:" << positionValue.isEmpty(); // Если позиция пустая, пропускаем строку if (positionValue.isEmpty()) { qDebug() << "SpecificationPCBController::recalculatePositions: Строка" << row << "- позиция пустая, пропускаем"; continue; } // Проверяем, не является ли строка заголовком QModelIndex firstColumnIndex = m_tableModel->index(row, 0); QVariant headerVariant = m_tableModel->data(firstColumnIndex, Qt::UserRole); bool isHeader = false; if (headerVariant.isValid()) { SpecificationPCBCellData cellData = headerVariant.value(); isHeader = cellData.isHeader; } // Также проверяем через метод модели, если доступен if (m_tableModel->isRowHeader(row)) { isHeader = true; } // Пропускаем только заголовки (но не пустые строки, так как пользователь мог ввести позицию в пустую строку) if (isHeader) { qDebug() << "SpecificationPCBController::recalculatePositions: Строка" << row << "- заголовок, пропускаем (позиция:" << positionValue << ")"; continue; } // Если позиция не пустая и строка не заголовок, добавляем в список для пересчета rowsWithPosition.append(row); qDebug() << "SpecificationPCBController::recalculatePositions: Найдена строка" << row << "с позицией:" << positionValue << "(isHeader:" << isHeader << ")"; } qDebug() << "SpecificationPCBController::recalculatePositions: Найдено строк с позициями:" << rowsWithPosition.size(); // Теперь пересчитываем позиции сквозно от 1 до n только для строк, где позиция была не пустая // Всегда обновляем позиции, даже если они уже правильные, чтобы гарантировать последовательность for (int row : rowsWithPosition) { QModelIndex index = m_tableModel->index(row, POSITION_COLUMN); if (!index.isValid()) { continue; } // Получаем текущее значение позиции QString positionValue = m_tableModel->data(index, Qt::DisplayRole).toString().trimmed(); // Увеличиваем счетчик позиции и устанавливаем новое значение currentPosition++; QString newPosition = QString::number(currentPosition); // Всегда обновляем позицию, чтобы гарантировать последовательность qDebug() << "SpecificationPCBController::recalculatePositions: Обновляем позицию в строке" << row << "с '" << positionValue << "' на '" << newPosition << "'"; bool success = m_tableModel->setData(index, newPosition, Qt::EditRole); if (success) { updatedCount++; // Проверяем, что значение действительно обновилось QString updatedValue = m_tableModel->data(index, Qt::DisplayRole).toString().trimmed(); if (updatedValue != newPosition) { qDebug() << "SpecificationPCBController::recalculatePositions: ВНИМАНИЕ! Позиция в строке" << row << "не обновилась! Ожидалось:" << newPosition << ", получено:" << updatedValue; } else { qDebug() << "SpecificationPCBController::recalculatePositions: Позиция в строке" << row << "успешно обновлена на" << newPosition; } } else { qDebug() << "SpecificationPCBController::recalculatePositions: Ошибка при обновлении позиции в строке" << row << "- setData вернул false"; } } qDebug() << "SpecificationPCBController::recalculatePositions: Перерасчет позиций завершен, обновлено строк:" << updatedCount << "из" << currentPosition; } int SpecificationPCBController::getFontSize() const { if (m_projectSettingsModel) { return m_projectSettingsModel->getFontSize("SpecificationPCB"); } return 12; // По умолчанию } void SpecificationPCBController::setFontSize(int fontSize) { if (m_projectSettingsModel) { m_projectSettingsModel->setFontSize("SpecificationPCB", fontSize); } } int SpecificationPCBController::getFontStretch() const { if (m_projectSettingsModel) { return m_projectSettingsModel->getFontStretch("SpecificationPCB"); } return 100; // По умолчанию (нормальный) } void SpecificationPCBController::setFontStretch(int stretch) { if (m_projectSettingsModel) { m_projectSettingsModel->setFontStretch("SpecificationPCB", stretch); } } QList SpecificationPCBController::splitLongDesignation(const QString &designation, const QString originalString, ComponentModel *component, int maxLength) { QList result; if (designation.length() <= maxLength) { result.append(designation); return result; } qDebug() << "SpecificationPCBController::splitLongDesignation: Разделяем длинное наименование:" << designation; // Проверяем, является ли наименование выражением с частями if (originalString.startsWith("=") && component) { // Это выражение - разделяем по частям QString expr = originalString.mid(1); // Убираем начальный "=" QStringList parts; QString currentPart; bool inDoubleQuotes = false; bool inSingleQuotes = false; for (int i = 0; i < expr.length(); ++i) { QChar ch = expr[i]; if (ch == '"' && !inSingleQuotes) { inDoubleQuotes = !inDoubleQuotes; currentPart += ch; } else if (ch == '\'' && !inDoubleQuotes) { inSingleQuotes = !inSingleQuotes; currentPart += ch; } else if (ch == '+' && !inDoubleQuotes && !inSingleQuotes) { // Это разделитель "+" вне кавычек if (!currentPart.trimmed().isEmpty()) { parts.append(currentPart.trimmed()); } currentPart.clear(); } else { currentPart += ch; } } // Добавляем последнюю часть if (!currentPart.trimmed().isEmpty()) { parts.append(currentPart.trimmed()); } qDebug() << "SpecificationPCBController::splitLongDesignation: Части выражения:" << parts; // Сначала собираем все части в одну строку для комплексных строк QString fullString; for (int i = 0; i < parts.size(); ++i) { QString trimmedPart = parts[i].trimmed(); if (!trimmedPart.isEmpty()) { QString displayPart; // Проверяем, является ли это литералом в одинарных кавычках if (trimmedPart.startsWith("'") && trimmedPart.endsWith("'")) { // Это литерал (запятая, пробел и т.д.) displayPart = trimmedPart.mid(1, trimmedPart.length() - 2); } else { // Это имя свойства - получаем значение из компонента QMap props = component->properties(); qDebug() << "Обрабатываем свойство:" << trimmedPart; if (trimmedPart == "Designator") { displayPart = component->designator(); } else { QString propertyName = trimmedPart.startsWith("\"") && trimmedPart.endsWith("\"") ? trimmedPart.mid(1, trimmedPart.length() - 2) : trimmedPart; displayPart = props.value(propertyName, ""); // Рекурсивно обрабатываем вложенные выражения if (displayPart.startsWith("=")) { qDebug() << "SpecificationPCBController::splitLongDesignation: Значение свойства является вложенным выражением, рекурсивно обрабатываем"; displayPart = parseExpression(component, displayPart); } } } fullString += displayPart; } } qDebug() << "SpecificationPCBController::splitLongDesignation: Собранная полная строка:" << fullString << ", длина:" << fullString.length(); // Проверяем, содержит ли строка запятые (комплексная строка) bool isComplexString = fullString.contains(','); if (isComplexString && fullString.length() > maxLength) { qDebug() << "SpecificationPCBController::splitLongDesignation: Комплексная строка с запятыми, делим по середине"; // Для комплексной строки делим по середине, а не по началу result = splitComplexStringByMiddle(fullString, maxLength); } else if (fullString.length() > maxLength) { qDebug() << "SpecificationPCBController::splitLongDesignation: Строка длинная, разделяем по пробелам"; // Обычная длинная строка - разделяем по пробелам result = splitBySpaces(fullString, maxLength); } else { qDebug() << "SpecificationPCBController::splitLongDesignation: Строка короткая, возвращаем как есть"; result.append(fullString); } } else { // Обычный текст - разделяем по пробелам result = splitBySpaces(designation, maxLength); } qDebug() << "SpecificationPCBController::splitLongDesignation: Результат разделения:" << result; return result; } QList SpecificationPCBController::splitBySpaces(const QString &text, int maxLength) { QList result; if (text.length() <= maxLength) { result.append(text); return result; } // Ищем пробел, ближайший к середине строки int targetLength = maxLength; int bestSplitPos = -1; int minDifference = text.length(); // Минимальная разница с целевой длиной // Ищем пробелы в тексте for (int i = 0; i < text.length(); ++i) { if (text[i] == ' ') { int leftLength = i; int rightLength = text.length() - i - 1; // Вычисляем разницу с целевой длиной int leftDiff = abs(leftLength - targetLength); int rightDiff = abs(rightLength - targetLength); int totalDiff = leftDiff + rightDiff; // Если эта позиция лучше предыдущей, запоминаем её if (totalDiff < minDifference) { minDifference = totalDiff; bestSplitPos = i; } } } // Если нашли подходящий пробел, разделяем по нему if (bestSplitPos != -1) { QString leftPart = text.left(bestSplitPos).trimmed(); QString rightPart = text.mid(bestSplitPos + 1).trimmed(); // Рекурсивно обрабатываем обе части if (!leftPart.isEmpty()) { QList leftParts = splitBySpaces(leftPart, maxLength); result.append(leftParts); } if (!rightPart.isEmpty()) { QList rightParts = splitBySpaces(rightPart, maxLength); result.append(rightParts); } } else { // Если пробелов нет или не удалось найти подходящий, разбиваем посимвольно // for (int i = 0; i < text.length(); i += maxLength) { // result.append(text.mid(i, maxLength)); // } result.append(text); } return result; } QList SpecificationPCBController::splitComplexStringByMiddle(const QString &text, int maxLength) { QList result; if (text.length() <= maxLength) { result.append(text); return result; } qDebug() << "SpecificationPCBController::splitComplexStringByMiddle: Разделяем комплексную строку:" << text; // Ищем запятые в тексте QList commaPositions; for (int i = 0; i < text.length(); ++i) { if (text[i] == ',') { commaPositions.append(i); } } if (commaPositions.isEmpty()) { // Если запятых нет, используем обычное разделение по пробелам qDebug() << "SpecificationPCBController::splitComplexStringByMiddle: Запятых не найдено, используем splitBySpaces"; return splitBySpaces(text, maxLength); } // Ищем запятую, ближайшую к середине строки int targetLength = maxLength; int bestSplitPos = -1; int minDifference = text.length(); for (int pos : commaPositions) { int leftLength = pos + 1; // Включаем запятую в левую часть int rightLength = text.length() - pos - 1; // Вычисляем разницу с целевой длиной int leftDiff = abs(leftLength - targetLength); int rightDiff = abs(rightLength - targetLength); int totalDiff = leftDiff + rightDiff; // Если эта позиция лучше предыдущей, запоминаем её if (totalDiff < minDifference) { minDifference = totalDiff; bestSplitPos = pos; } } // Если нашли подходящую запятую, разделяем по ней if (bestSplitPos != -1) { QString leftPart = text.left(bestSplitPos + 1).trimmed(); // Включаем запятую QString rightPart = text.mid(bestSplitPos + 1).trimmed(); qDebug() << "SpecificationPCBController::splitComplexStringByMiddle: Найдена запятая на позиции" << bestSplitPos; qDebug() << "SpecificationPCBController::splitComplexStringByMiddle: Левая часть:" << leftPart; qDebug() << "SpecificationPCBController::splitComplexStringByMiddle: Правая часть:" << rightPart; // Рекурсивно обрабатываем обе части if (!leftPart.isEmpty()) { QList leftParts = splitComplexStringByMiddle(leftPart, maxLength); result.append(leftParts); } if (!rightPart.isEmpty()) { QList rightParts = splitComplexStringByMiddle(rightPart, maxLength); result.append(rightParts); } } else { // Если не удалось найти подходящую запятую, используем обычное разделение qDebug() << "SpecificationPCBController::splitComplexStringByMiddle: Не удалось найти подходящую запятую, используем splitBySpaces"; result = splitBySpaces(text, maxLength); } qDebug() << "SpecificationPCBController::splitComplexStringByMiddle: Результат:" << result; return result; } QStringList SpecificationPCBController::splitDesignatorGroups(const QStringList &designatorGroups, int maxLength) { QStringList result; QString currentLine; for (const QString &group : designatorGroups) { QString testLine = currentLine.isEmpty() ? group : currentLine + ", " + group; if (testLine.length() <= maxLength) { currentLine = testLine; } else { if (!currentLine.isEmpty()) { result.append(currentLine +","); } currentLine = group; } } if (!currentLine.isEmpty()) { result.append(currentLine); } // Теперь разбиваем каждую строку, если она длиннее maxLength QStringList finalResult; for (const QString &line : result) { if (line.length() <= maxLength) { finalResult.append(line); } else { // Разбиваем длинную строку посимвольно, но стараемся разбивать по запятым QStringList parts = line.split(", "); QString currentPart = ""; for (const QString &part : parts) { QString testPart = currentPart.isEmpty() ? part : currentPart + ", " + part; if (testPart.length() <= maxLength) { currentPart = testPart; } else { if (!currentPart.isEmpty()) { finalResult.append(currentPart); } currentPart = part; } } if (!currentPart.isEmpty()) { finalResult.append(currentPart); } } } return finalResult; }