Files
GostGenerator/controller/specificationpcbcontroller.cpp
T

1633 lines
83 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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 <QDebug>
#include <QCollator>
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<ComponentModel*> &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;
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);
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 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<QString, QString> partsA = SpecificationPCBController::splitDesignator(designatorA);
QPair<QString, QString> 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<QString, QList<ComponentModel*>> groupedByLetter;
for (ComponentModel *component : m_components) {
QPair<QString, QString> 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<ComponentModel*> componentsInGroup = it.value();
if (componentsInGroup.size() > 1) {
// Сначала группируем компоненты по наименованию
QList<QPair<QString, QList<ComponentModel*>>> 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<ComponentModel*> newGroup;
newGroup.append(component);
groupedByName.append(qMakePair(componentName, newGroup));
}
}
// Теперь создаем строки для каждой группы по наименованию
for (const auto &nameGroup : groupedByName) {
QString componentName = nameGroup.first;
QList<ComponentModel*> componentsWithSameName = nameGroup.second;
// Подсчитываем общее количество компонентов
int totalQuantity = componentsWithSameName.size();
// Создаем строку для этой группы компонентов
ComponentModel *firstComponent = componentsWithSameName.first();
QMap<QString, QString> props = firstComponent->properties();
QString originalString = props.value("ManufacturerPartNumber", "");
QList<QString> 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<QString, QString> partsA = splitDesignator(designatorA);
QPair<QString, QString> 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<QString> 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<QString, QString> currentParts = splitDesignator(currentDesignator);
QPair<QString, QString> 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);
}
}
// Объединяем все группы дезигнаторов в одну строку через запятые
QString allDesignators = designatorGroupsForThisName.join(", ");
// Разбиваем длинные группы десигнаторов используя логику комплексных строк (11 символов на строку)
QList<QString> designatorsSplitList = splitComplexStringByMiddle(allDesignators, 11);
QStringList designatorsSplit;
for (const QString &str : designatorsSplitList) {
designatorsSplit.append(str);
}
// Получаем тип компонента: если количество > 1, используем множественное число, иначе единственное
QString componentType;
if (totalQuantity > 1) {
componentType = getComponentType(firstComponent);
} else {
componentType = getComponentTypeSingular(firstComponent);
}
// Формируем список строк названия с учетом типа компонента
QList<QString> nameParts;
if (!componentType.isEmpty() && !designationParts.isEmpty()) {
// Проверяем, помещается ли тип компонента + первая часть названия
QString firstPartWithType = componentType + " " + designationParts[0];
if (firstPartWithType.length() <= 34) {
// Помещается - добавляем тип в первую строку
nameParts.append(firstPartWithType);
// Добавляем остальные части
for (int i = 1; i < designationParts.size(); ++i) {
nameParts.append(designationParts[i]);
}
} else {
// Не помещается - выносим тип в отдельную строку
nameParts.append(componentType);
// Добавляем все части названия
for (const QString &part : designationParts) {
nameParts.append(part);
}
}
} else {
// Типа нет или нет частей названия - используем как есть
nameParts = designationParts;
}
// Определяем максимальное количество строк для названия и дезигнаторов
int maxRows = qMax(nameParts.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 < nameParts.size()) {
row.name = SpecificationPCBCellData(nameParts[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");
// Получаем тип компонента в единственном числе (количество = 1)
QString componentType = getComponentTypeSingular(component);
// Разделяем длинное наименование на несколько строк
QMap<QString, QString> props = component->properties();
QString originalString = props.value("ManufacturerPartNumber", "");
QList<QString> designationParts = splitLongDesignation(currentDesignation, originalString, component);
// Формируем список строк названия с учетом типа компонента
QList<QString> nameParts;
if (!componentType.isEmpty() && !designationParts.isEmpty()) {
// Проверяем, помещается ли тип компонента + первая часть названия
QString firstPartWithType = componentType + " " + designationParts[0];
if (firstPartWithType.length() <= 34) {
// Помещается - добавляем тип в первую строку
nameParts.append(firstPartWithType);
// Добавляем остальные части
for (int i = 1; i < designationParts.size(); ++i) {
nameParts.append(designationParts[i]);
}
} else {
// Не помещается - выносим тип в отдельную строку
nameParts.append(componentType);
// Добавляем все части названия
for (const QString &part : designationParts) {
nameParts.append(part);
}
}
} else {
// Типа нет или нет частей названия - используем как есть
nameParts = designationParts;
}
// Обрабатываем дезигнатор: если он длинный, разбиваем используя логику комплексных строк
QString designatorStr = component->designator();
QList<QString> designatorParts = splitComplexStringByMiddle(designatorStr, 11);
// Определяем максимальное количество строк для названия и дезигнатора
int maxParts = qMax(nameParts.size(), designatorParts.size());
for (int partIndex = 0; partIndex < maxParts; ++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);
} else {
// В последующих строках оставляем пустыми позицию и количество
row.position = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
row.quantity = SpecificationPCBCellData("", 0, 1, false, false, false, 100, false);
}
// Заполняем дезигнатор (если есть)
if (partIndex < designatorParts.size()) {
row.note = SpecificationPCBCellData(designatorParts[partIndex], 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);
// Заполняем название
if (partIndex < nameParts.size()) {
row.name = SpecificationPCBCellData(nameParts[partIndex], 0, 1, false, false, false, 100, false);
} else {
row.name = SpecificationPCBCellData("", 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<QString, QString> 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<QString, QString> 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<QString, QString> 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<QString, QString> 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<QString, QString> 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<QString, QString> 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<QString, QString> 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<QString, QString> props = component->properties();
QString type = props.value("Type", "");
if (!type.isEmpty()) {
return type;
}
// Если и тип не найден, используем designator как fallback
return component->designator();
}
QString SpecificationPCBController::getComponentTypeSingular(ComponentModel *component)
{
if (!component) return "";
// Получаем тип компонента из маппинга дезигнаторов в единственном числе
if (m_designatorMappingModel) {
QString componentType = m_designatorMappingModel->getComponentType(component->designator(), false); // false для единственного числа
if (!componentType.isEmpty()) {
return componentType;
}
}
// Если маппинг не найден, используем тип из свойств компонента
QMap<QString, QString> props = component->properties();
QString type = props.value("Type", "");
if (!type.isEmpty()) {
return type;
}
// Если и тип не найден, возвращаем пустую строку
return QString();
}
QString SpecificationPCBController::getComponentFormat(ComponentModel *component)
{
if (!component) return "";
// Получаем формат из свойств компонента
QMap<QString, QString> props = component->properties();
QString format = props.value("Format", "");
if (!format.isEmpty()) {
return format;
}
// Если формат не найден, используем значение по умолчанию
return "A4";
}
QString SpecificationPCBController::getComponentZone(ComponentModel *component)
{
if (!component) return "";
// Получаем зону из свойств компонента
QMap<QString, QString> 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<QString, QString> SpecificationPCBController::splitDesignator(const QString &designator)
{
if (designator.isEmpty()) {
return QPair<QString, QString>("", "");
}
// Используем регулярное выражение для разделения буквенной и цифровой части
QRegExp regex("([A-Za-z]+)(\\d*)");
if (regex.indexIn(designator) != -1) {
QString letterPart = regex.cap(1);
QString numberPart = regex.cap(2);
return QPair<QString, QString>(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<QString, QString>(letterPart, numberPart);
}
}
// Если цифр нет, возвращаем весь десигнатор как буквенную часть
return QPair<QString, QString>(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<int> 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<SpecificationPCBCellData>();
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<QString> SpecificationPCBController::splitLongDesignation(const QString &designation, const QString originalString, ComponentModel *component, int maxLength)
{
QList<QString> 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<QString, QString> 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<QString> SpecificationPCBController::splitBySpaces(const QString &text, int maxLength)
{
QList<QString> 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<QString> leftParts = splitBySpaces(leftPart, maxLength);
result.append(leftParts);
}
if (!rightPart.isEmpty()) {
QList<QString> 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<QString> SpecificationPCBController::splitComplexStringByMiddle(const QString &text, int maxLength)
{
QList<QString> result;
if (text.length() <= maxLength) {
result.append(text);
return result;
}
qDebug() << "SpecificationPCBController::splitComplexStringByMiddle: Разделяем комплексную строку:" << text;
// Ищем запятые в тексте
QList<int> 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<QString> leftParts = splitComplexStringByMiddle(leftPart, maxLength);
result.append(leftParts);
}
if (!rightPart.isEmpty()) {
QList<QString> 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;
}