#include "specificationcontroller.h" #include "../model/specificationtablemodel.h" #include "../model/designatormappingmodel.h" #include "../model/projectsettingsmodel.h" #include "../model/projectparamtablemodel.h" #include "../model/pcbmaterialmodel.h" #include "../controller/maincontroller.h" #include #include SpecificationController::SpecificationController(QObject *parent) : QObject(parent) , m_tableModel(new SpecificationTableModel(this)) , m_designatorMappingModel(nullptr) , m_projectSettingsModel(nullptr) , m_mainController(nullptr) , m_pcbMaterialModel(nullptr) { // Устанавливаем маппинг по умолчанию для спецификации m_columnMappings << "Format" << "Zone" << "Position" << "Designation" << "Name" << "Quantity" << "Note"; } SpecificationController::~SpecificationController() { // m_tableModel удалится автоматически, так как он является дочерним объектом } void SpecificationController::setDesignatorMappingModel(DesignatorMappingModel *mappingModel) { m_designatorMappingModel = mappingModel; } void SpecificationController::setModel(SpecificationTableModel *model) { if (m_tableModel != model) { qDebug() << "SpecificationController::setModel: Заменяем модель таблицы"; m_tableModel = model; // Если модель не пуста, загружаем из неё настройки колонок if (m_tableModel) { QStringList columnMappings = m_tableModel->columnMappings(); if (!columnMappings.isEmpty()) { qDebug() << "SpecificationController::setModel: Загружаем настройки колонок из модели:" << columnMappings; m_columnMappings = columnMappings; } } } } void SpecificationController::setProjectSettingsModel(ProjectSettingsModel *model) { m_projectSettingsModel = model; qDebug() << "SpecificationController::setProjectSettingsModel: Установлена модель настроек проекта"; } void SpecificationController::setMainController(MainController *controller) { m_mainController = controller; qDebug() << "SpecificationController::setMainController: Установлен главный контроллер"; } void SpecificationController::setMaterials(const QList &materials) { qDebug() << "SpecificationController::setMaterials: Устанавливаем" << materials.size() << "материалов"; m_materials = materials; // Собираем доступные свойства из материалов m_propertyNames = collectPropertyNames(); qDebug() << "SpecificationController::setMaterials: Собрано свойств:" << m_propertyNames.size(); qDebug() << "SpecificationController::setMaterials: Свойства:" << m_propertyNames; } void SpecificationController::setPCBMaterialModel(PCBMaterialModel* model) { qDebug() << "SpecificationController::setPCBMaterialModel: Устанавливаем PCB модель материалов"; m_pcbMaterialModel = model; if (m_pcbMaterialModel) { qDebug() << "SpecificationController::setPCBMaterialModel: Материалов в модели:" << m_pcbMaterialModel->getMaterialCount(); } } void SpecificationController::generateTableFromMaterials() { if (!m_tableModel) { return; } qDebug() << "SpecificationController::generateTableFromMaterials: Начинаем генерацию таблицы"; qDebug() << "SpecificationController::generateTableFromMaterials: Текущие настройки колонок:" << m_columnMappings; // Очищаем таблицу m_tableModel->clear(); // Добавляем заголовок таблицы SpecificationRowData emptyRow; emptyRow.isEmpty = true; m_tableModel->addRow(emptyRow); SpecificationRowData headerRow; headerRow.isHeader = true; headerRow.format = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow.zone = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow.position = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow.designation = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow.name = SpecificationCellData("Документаця", 0, 1, true, false, false, 100, true); headerRow.quantity = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow.note = SpecificationCellData("", 0, 1, true, false, false, 100, true); m_tableModel->addRow(headerRow); m_tableModel->addRow(emptyRow); SpecificationRowData defaultRow1; defaultRow1.isHeader = false; defaultRow1.format = SpecificationCellData("A1", 0, 1, false, false, false, 100, false); defaultRow1.zone = SpecificationCellData("", 0, 1, false, false, false, 100, false); defaultRow1.position = SpecificationCellData("", 0, 1, false, false, false, 100, false); defaultRow1.designation = SpecificationCellData(getDecimalNumberBoard() + "Э3 СБ", 0, 1, false, false, false, 100, false); defaultRow1.name = SpecificationCellData("Сборочный чертеж", 0, 1, false, false, false, 100, false); defaultRow1.quantity = SpecificationCellData("", 0, 1, false, false, false, 100, false); defaultRow1.note = SpecificationCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(defaultRow1); SpecificationRowData defaultRow2; defaultRow2.isHeader = false; defaultRow2.format = SpecificationCellData("*)", 0, 1, false, false, false, 100, false); defaultRow2.zone = SpecificationCellData("", 0, 1, false, false, false, 100, false); defaultRow2.position = SpecificationCellData("", 0, 1, false, false, false, 100, false); defaultRow2.designation = SpecificationCellData(getDecimalNumberBoard() + " Э3 Т5М", 0, 1, false, false, false, 100, false); defaultRow2.name = SpecificationCellData("Данные проектирования", 0, 1, false, false, false, 100, false); defaultRow2.quantity = SpecificationCellData("", 0, 1, false, false, false, 100, false); defaultRow2.note = SpecificationCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(defaultRow2); m_tableModel->addRow(emptyRow); SpecificationRowData headerRow2; headerRow2.isHeader = true; headerRow2.format = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow2.zone = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow2.position = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow2.designation = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow2.name = SpecificationCellData("Материалы", 0, 1, true, false, false, 100, true); headerRow2.quantity = SpecificationCellData("", 0, 1, true, false, false, 100, true); headerRow2.note = SpecificationCellData("", 0, 1, true, false, false, 100, true); m_tableModel->addRow(headerRow2); m_tableModel->addRow(emptyRow); if (!m_pcbMaterialModel || m_pcbMaterialModel->getMaterialCount() == 0) { qDebug() << "SpecificationController::generateTableFromMaterials: Нет материалов для генерации"; return; } qDebug() << "SpecificationController::generateTableFromMaterials: Материалов для обработки:" << m_pcbMaterialModel->getMaterialCount(); try { // Получаем список материалов из PCB модели QList materials = m_pcbMaterialModel->getMaterials(); // Сортируем материалы по номеру слоя std::sort(materials.begin(), materials.end(), [](const PCBMaterialData &a, const PCBMaterialData &b) { return a.layerNumber < b.layerNumber; }); // Группируем материалы по названию и типу QMap> groupedByName; for (const PCBMaterialData &material : materials) { QString materialName = material.name; groupedByName[materialName].append(material); } // Подсчитываем количество ядер (dielType = 1 в модели PCB) int coreCount = 0; for (const PCBMaterialData &material : materials) { if (material.dielType == 1) { coreCount++; } } // Подсчитываем общее количество слоев int totalLayers = m_pcbMaterialModel->getLayerCount(); // Рассчитываем количество фольги: (общее количество слоев - количество ядер) int foilCount = totalLayers - coreCount*2; // Обрабатываем каждую группу материалов int pos = 1; for (auto it = groupedByName.begin(); it != groupedByName.end(); ++it) { QString materialName = it.key(); QList materialsInGroup = it.value(); // Определяем тип материала и формируем название QString displayName; int quantity = 0; if (materialName == "DielMaterial") { // Для диэлектрических материалов группируем по значению QMap> groupedByValue; for (const PCBMaterialData &material : materialsInGroup) { QString value = material.value; groupedByValue[value].append(material); } // Обрабатываем каждое значение диэлектрика for (auto valueIt = groupedByValue.begin(); valueIt != groupedByValue.end(); ++valueIt) { QString value = valueIt.key(); QList valueMaterials = valueIt.value(); // Определяем тип диэлектрика и формируем название QString dielTypeName; if (value == "FR4 PR") { dielTypeName = "Препрег FR4 PR"; } else if (value == "FR4 Tg150") { dielTypeName = "Стеклотекстолит FR4 Tg150"; } else if (value == "Solder Resist") { dielTypeName = "Паяльная маска"; } else { dielTypeName = value; } // Добавляем толщину к названию if (!valueMaterials.isEmpty()) { double thickness = valueMaterials.first().height; displayName = QString("%1 %2 мм").arg(dielTypeName).arg(QString::number(thickness, 'f', 3)); } quantity = valueMaterials.size(); // Добавляем строку в таблицу SpecificationRowData row; row.format = SpecificationCellData("", 0, 1, false, false, false, 100, false); row.zone = SpecificationCellData("", 0, 1, false, false, false, 100, false); row.position = SpecificationCellData(QString::number(pos++), 0, 1, false, false, false, 100, false); row.designation = SpecificationCellData("", 0, 1, false, false, false, 100, false); row.name = SpecificationCellData(displayName, 0, 1, false, false, false, 100, false); row.quantity = SpecificationCellData(QString::number(quantity), 0, 1, false, false, false, 100, false); row.note = SpecificationCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(row); } } else { // Для других материалов (например, фольга) displayName = materialName; quantity = materialsInGroup.size(); // Добавляем строку в таблицу SpecificationRowData row; row.format = SpecificationCellData("", 0, 1, false, false, false, 100, false); row.zone = SpecificationCellData("", 0, 1, false, false, false, 100, false); row.position = SpecificationCellData(QString::number(pos++), 0, 1, false, false, false, 100, false); row.designation = SpecificationCellData("", 0, 1, false, false, false, 100, false); row.name = SpecificationCellData(displayName, 0, 1, false, false, false, 100, false); row.quantity = SpecificationCellData(QString::number(quantity), 0, 1, false, false, false, 100, false); row.note = SpecificationCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(row); } } // Добавляем фольгу в конец, если она есть if (foilCount > 0) { SpecificationRowData foilRow; foilRow.format = SpecificationCellData("", 0, 1, false, false, false, 100, false); foilRow.zone = SpecificationCellData("", 0, 1, false, false, false, 100, false); foilRow.position = SpecificationCellData(QString::number(pos++), 0, 1, false, false, false, 100, false); foilRow.designation = SpecificationCellData("", 0, 1, false, false, false, 100, false); foilRow.name = SpecificationCellData("Фольга медная толщиной 18 мкм", 0, 1, false, false, false, 100, false); foilRow.quantity = SpecificationCellData(QString::number(foilCount), 0, 1, false, false, false, 100, false); foilRow.note = SpecificationCellData("", 0, 1, false, false, false, 100, false); m_tableModel->addRow(foilRow); } qDebug() << "SpecificationController::generateTableFromMaterials: Таблица сгенерирована, строк:" << m_tableModel->rowCount(); // Оптимизируем разрывы страниц после генерации таблицы m_tableModel->optimizePageBreaks(); } catch (const std::exception &e) { qDebug() << "SpecificationController::generateTableFromMaterials: Ошибка при генерации:" << e.what(); } catch (...) { qDebug() << "SpecificationController::generateTableFromMaterials: Неизвестная ошибка при генерации"; } } void SpecificationController::updateTableFromMaterials() { if (!m_tableModel) { qDebug() << "SpecificationController::updateTableFromMaterials: m_tableModel is null"; return; } qDebug() << "SpecificationController::updateTableFromMaterials: Обновляем таблицу из" << m_materials.size() << "материалов"; if (m_materials.isEmpty()) { qDebug() << "SpecificationController::updateTableFromMaterials: Нет материалов для обновления"; return; } try { // Метод updateTableFromMaterials больше не используется в новой логике qDebug() << "SpecificationController::updateTableFromMaterials: Метод устарел"; } catch (const std::exception &e) { qDebug() << "SpecificationController::updateTableFromMaterials: Ошибка при обновлении:" << e.what(); } catch (...) { qDebug() << "SpecificationController::updateTableFromMaterials: Неизвестная ошибка при обновлении"; } } QString SpecificationController::getMaterialValue(PCBMaterialModel *material, const QString &propertyName) { if (!material) { return ""; } qDebug() << "SpecificationController::getMaterialValue: propertyName =" << propertyName; // Получаем значение свойства материала QString value; if (propertyName == "Name") { value = material->getMaterialName(0); // Получаем название первого материала qDebug() << "SpecificationController::getMaterialValue: Name =" << value; } else if (propertyName == "Value") { value = material->getMaterialValue(0); qDebug() << "SpecificationController::getMaterialValue: Value =" << value; } else if (propertyName == "Height") { value = material->formatHeight(material->getMaterialHeight(0)); qDebug() << "SpecificationController::getMaterialValue: Height =" << value; } else if (propertyName == "DielType") { value = material->formatDielType(material->getMaterialDielType(0)); qDebug() << "SpecificationController::getMaterialValue: DielType =" << value; } else if (propertyName == "LayerNumber") { value = QString::number(material->getMaterialLayerNumber(0)); qDebug() << "SpecificationController::getMaterialValue: LayerNumber =" << value; } else { qDebug() << "SpecificationController::getMaterialValue: Неизвестное свойство" << propertyName; return ""; } // Проверяем, является ли значение свойства выражением if (value.startsWith("=")) { qDebug() << "SpecificationController::getMaterialValue: Значение свойства является выражением:" << value; return parseExpression(material, value); } return value; } QString SpecificationController::formatDielType(int dielType) { switch (dielType) { case 0: return "Ядро"; case 1: return "Препрег"; default: return "Неизвестно"; } } QString SpecificationController::parseExpression(PCBMaterialModel *material, const QString &expression) { if (!material) { return ""; } qDebug() << "SpecificationController::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() << "SpecificationController::parseExpression: Части выражения:" << parts; for (const QString &part : parts) { QString trimmedPart = part.trimmed(); qDebug() << "SpecificationController::parseExpression: Обрабатываем часть:" << trimmedPart; // Если это не пустая строка, добавляем к результату if (!trimmedPart.isEmpty()) { QString value; // Проверяем, является ли это литералом в одинарных кавычках if (trimmedPart.startsWith("'") && trimmedPart.endsWith("'")) { // Это литерал - убираем кавычки и используем как есть value = trimmedPart.mid(1, trimmedPart.length() - 2); qDebug() << "SpecificationController::parseExpression: Найден литерал в одинарных кавычках:" << value; } // Проверяем, является ли это именем свойства материала в двойных кавычках else if (trimmedPart.startsWith("\"") && trimmedPart.endsWith("\"")) { // Это свойство материала - убираем кавычки и ищем в свойствах QString propertyName = trimmedPart.mid(1, trimmedPart.length() - 2); qDebug() << "SpecificationController::parseExpression: Ищем свойство материала:" << propertyName; value = getMaterialValue(material, propertyName); qDebug() << "SpecificationController::parseExpression: Найдено свойство" << propertyName << "=" << value; // Рекурсивно обрабатываем вложенные выражения (getMaterialValue уже обрабатывает первый уровень) // Но если значение само является выражением, нужно обработать его еще раз if (value.startsWith("=")) { qDebug() << "SpecificationController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем"; value = parseExpression(material, value); } } // Проверяем, является ли это именем свойства материала без кавычек else { // Ищем в свойствах материала value = getMaterialValue(material, trimmedPart); qDebug() << "SpecificationController::parseExpression: Ищем свойство" << trimmedPart << "в материале, результат:" << value; // Рекурсивно обрабатываем вложенные выражения (getMaterialValue уже обрабатывает первый уровень) // Но если значение само является выражением, нужно обработать его еще раз if (value.startsWith("=")) { qDebug() << "SpecificationController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем"; value = parseExpression(material, value); } } // Добавляем значение к результату if (!value.isEmpty()) { if (!result.isEmpty()) { result += value; } else { result = value; } qDebug() << "SpecificationController::parseExpression: Добавили к результату, текущий результат:" << result; } } } qDebug() << "SpecificationController::parseExpression: Финальный результат:" << result; return result; } QStringList SpecificationController::collectPropertyNames() { QStringList propertyNames; // Добавляем основные свойства материала propertyNames << "Name" << "Value" << "Height" << "DielType" << "LayerNumber"; // Если дополнительные свойства не найдены в материалах, добавляем стандартные if (propertyNames.size() <= 5) { // Только базовые свойства propertyNames << "Format" << "Zone" << "Position" << "Designation" << "Quantity" << "Note"; qDebug() << "SpecificationController: Дополнительные свойства не найдены, используем стандартные:" << propertyNames; } // Добавляем специальное свойство для количества материалов в группе propertyNames << "Quantity" << "Кол."; return propertyNames; } // Методы для работы с децимальными номерами QString SpecificationController::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 SpecificationController::setDecimalNumberBoard(const QString &value) { QStringList mappings = getColumnMappings(); while (mappings.size() <= 7) { mappings.append(""); } mappings[7] = value; setColumnMappings(mappings); } QString SpecificationController::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 SpecificationController::setDecimalNumberDocument(const QString &value) { QStringList mappings = getColumnMappings(); while (mappings.size() <= 8) { mappings.append(""); } mappings[8] = value; setColumnMappings(mappings); } void SpecificationController::setColumnMappings(const QStringList &mappings) { qDebug() << "SpecificationController::setColumnMappings: Устанавливаем новые настройки колонок:" << mappings; m_columnMappings = mappings; // Сохраняем настройки в ProjectSettingsModel if (m_projectSettingsModel) { for (int i = 0; i < mappings.size(); ++i) { m_projectSettingsModel->setColumnMapping("Specification", i, mappings[i]); } qDebug() << "SpecificationController::setColumnMappings: Настройки сохранены в ProjectSettingsModel"; } // Обновляем настройки в модели if (m_tableModel) { m_tableModel->setColumnMappings(mappings); qDebug() << "SpecificationController::setColumnMappings: Настройки обновлены в модели"; } qDebug() << "SpecificationController::setColumnMappings: Настройки колонок обновлены:" << m_columnMappings; } QStringList SpecificationController::getColumnMappings() const { // Если есть ProjectSettingsModel, читаем настройки из него if (m_projectSettingsModel) { QStringList settings = m_projectSettingsModel->getColumnMappings("Specification"); if (!settings.isEmpty()) { qDebug() << "SpecificationController::getColumnMappings: Получены настройки из ProjectSettingsModel:" << settings; return settings; } } // Fallback: возвращаем локальные настройки qDebug() << "SpecificationController::getColumnMappings: Возвращаем локальные настройки:" << m_columnMappings; return m_columnMappings; } QStringList SpecificationController::getAvailableProperties() const { QStringList properties = m_propertyNames; properties.prepend("<Пусто>"); properties.prepend("<Авто>"); return properties; } QString SpecificationController::getMaterialType(PCBMaterialModel *material) { if (!material) return ""; // Получаем тип материала из свойств QString type = material->formatDielType(material->getMaterialDielType(0)); if (!type.isEmpty()) { return type; } // Если тип не найден, используем fallback return "Материал"; } QPair SpecificationController::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 SpecificationController::addEmptyRow() { if (m_tableModel) { m_tableModel->addEmptyRow(); qDebug() << "SpecificationController::addEmptyRow: Добавлена пустая строка"; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void SpecificationController::addEmptyRowAt(int position) { if (m_tableModel) { m_tableModel->addEmptyRowAt(position); qDebug() << "SpecificationController::addEmptyRowAt: Добавлена пустая строка в позицию" << position; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void SpecificationController::insertEmptyRowAt(int position) { if (m_tableModel) { m_tableModel->addEmptyRowAt(position); qDebug() << "SpecificationController::insertEmptyRowAt: Вставлена пустая строка в позицию" << position; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void SpecificationController::removeRow(int row) { if (m_tableModel) { m_tableModel->removeRow(row); qDebug() << "SpecificationController::removeRow: Удалена строка" << row; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void SpecificationController::clearTable() { if (m_tableModel) { m_tableModel->clear(); qDebug() << "SpecificationController::clearTable: Таблица очищена"; } } void SpecificationController::optimizePageBreaks() { if (m_tableModel) { qDebug() << "SpecificationController::optimizePageBreaks: Оптимизируем разрывы страниц"; m_tableModel->optimizePageBreaks(); } } int SpecificationController::getFontSize() const { if (m_projectSettingsModel) { return m_projectSettingsModel->getFontSize("Specification"); } return 12; // По умолчанию } void SpecificationController::setFontSize(int fontSize) { if (m_projectSettingsModel) { m_projectSettingsModel->setFontSize("Specification", fontSize); } } int SpecificationController::getFontStretch() const { if (m_projectSettingsModel) { return m_projectSettingsModel->getFontStretch("Specification"); } return 100; // По умолчанию (нормальный) } void SpecificationController::setFontStretch(int stretch) { if (m_projectSettingsModel) { m_projectSettingsModel->setFontStretch("Specification", stretch); } } QList SpecificationController::splitLongDesignation(const QString &designation, const QString originalString, PCBMaterialModel *material, int maxLength) { QList result; if (designation.length() <= maxLength) { result.append(designation); return result; } qDebug() << "SpecificationController::splitLongDesignation: Разделяем длинное наименование:" << designation; // Обычный текст - разделяем по пробелам result = splitBySpaces(designation, maxLength); qDebug() << "SpecificationController::splitLongDesignation: Результат разделения:" << result; return result; } QList SpecificationController::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; } QString SpecificationController::getMaterialFormat(PCBMaterialModel *material) { if (!material) return ""; // Получаем формат из свойств материала // Для материалов платы обычно используется стандартный формат return "A4"; } QString SpecificationController::getMaterialZone(PCBMaterialModel *material) { if (!material) return ""; // Получаем зону из свойств материала // Для материалов платы зона обычно не указывается return ""; } QString SpecificationController::getMaterialPosition(PCBMaterialModel *material) { if (!material) return ""; // Для спецификации материалов позиция обычно совпадает с номером слоя return QString::number(material->getMaterialLayerNumber(0)); }