From 80e407e6be3d73c98822f501dcb459d384497d2a Mon Sep 17 00:00:00 2001 From: grigo Date: Tue, 23 Dec 2025 14:12:50 +0300 Subject: [PATCH] Refactor specification handling in DocumentSettingsDialog and PDFController. Introduced primary application field for document types, updated export messages for consistency, and improved page number handling in specifications. Cleaned up unnecessary code in preview widgets. --- app_icon.rc | 6 + controller/databasecontroller.cpp | 4 +- controller/pdfcontroller.cpp | 18 +- controller/specificationpcbcontroller.cpp | 23 ++- model/specificationpcbtablemodel.cpp | 34 +++- view/documentprocessor.cpp | 23 +++ view/documentsettingsdialog.cpp | 233 +++++++++++++++++++++- view/documentsettingsdialog.h | 10 + view/editpdfwidget.cpp | 2 +- view/mainwindow.cpp | 22 +- view/previewpdfwidget.cpp | 74 ++----- view/previewpdfwidget.h | 5 +- 12 files changed, 349 insertions(+), 105 deletions(-) diff --git a/app_icon.rc b/app_icon.rc index f2cdf59..c133b1c 100644 --- a/app_icon.rc +++ b/app_icon.rc @@ -5,3 +5,9 @@ IDI_ICON1 ICON DISCARDABLE "assets/icon2.ico" + + + + + + diff --git a/controller/databasecontroller.cpp b/controller/databasecontroller.cpp index 721d6b7..50c3b72 100644 --- a/controller/databasecontroller.cpp +++ b/controller/databasecontroller.cpp @@ -2585,7 +2585,7 @@ QString DataBaseController::getSpecificationTableInfo(const QString &projectName query.addBindValue(projectName); if (!query.exec() || !query.next()) { - setLastError("Ошибка получения информации о спецификации материалов: " + query.lastError().text()); + setLastError("Ошибка получения информации о спецификации: " + query.lastError().text()); return QString(); } @@ -2595,7 +2595,7 @@ QString DataBaseController::getSpecificationTableInfo(const QString &projectName int minOrder = query.value(3).toInt(); int maxOrder = query.value(4).toInt(); - QString info = QString("Спецификация материалов для проекта '%1':\n" + QString info = QString("Спецификация для проекта '%1':\n" "Всего строк: %2\n" "Заголовков: %3\n" "Пустых строк: %4\n" diff --git a/controller/pdfcontroller.cpp b/controller/pdfcontroller.cpp index 556981b..eee483e 100644 --- a/controller/pdfcontroller.cpp +++ b/controller/pdfcontroller.cpp @@ -113,7 +113,7 @@ bool PDFController::exportPerechenToPdf(const QString &filePath, MainController int pageNumber = 1; // Вычисляем общее количество страниц заранее - int totalPages = 2; + int totalPages = 1; int tempRow = 0; while (tempRow < totalRows) { int rowsOnThisPage = (totalPages == 1) ? rowsOnFirstPage : rowsOnOtherPages; @@ -988,7 +988,18 @@ painter.drawText(mainTableRect.left()+165*mmToPixels, mainTableRect.top()+20*mmT painter.drawText(60*mmToPixels,0, 60*mmToPixels, 5*mmToPixels, Qt::AlignCenter,"Перв. примен."); painter.drawText(0,5*mmToPixels, 60*mmToPixels, 7*mmToPixels, Qt::AlignCenter, getInscriptionValueWithProjectParams(model, projectParamModel, 24)); - painter.drawText(60*mmToPixels,5*mmToPixels, 60*mmToPixels, 7*mmToPixels, Qt::AlignCenter, getInscriptionValueWithProjectParams(model, projectParamModel, 25)); + // Определяем номер поля "Первичное применение" в зависимости от типа документа + int primaryApplicationField = 25; // По умолчанию + if (docType == 1) { + primaryApplicationField = 251; // Перечень + } else if (docType == 2) { + primaryApplicationField = 252; // Спецификация ПП + } else if (docType == 3) { + primaryApplicationField = 253; // Спецификация материалов + } else if (docType == 4) { + primaryApplicationField = 254; // Ведомость + } + painter.drawText(60*mmToPixels,5*mmToPixels, 60*mmToPixels, 7*mmToPixels, Qt::AlignCenter, getInscriptionValueWithProjectParams(model, projectParamModel, primaryApplicationField)); painter.restore(); @@ -1351,7 +1362,8 @@ bool PDFController::addGostFrameA3FirstPage(QPainter &painter, const QRectF &pag painter.drawText(60*mmToPixels,0, 60*mmToPixels, 5*mmToPixels, Qt::AlignCenter,"Перв. примен."); painter.drawText(0,5*mmToPixels, 60*mmToPixels, 7*mmToPixels, Qt::AlignCenter, getInscriptionValueWithProjectParams(model, projectParamModel, 24)); - painter.drawText(60*mmToPixels,5*mmToPixels, 60*mmToPixels, 7*mmToPixels, Qt::AlignCenter, getInscriptionValueWithProjectParams(model, projectParamModel, 25)); + // A3 формат используется только для ведомости, поэтому используем поле 254 + painter.drawText(60*mmToPixels,5*mmToPixels, 60*mmToPixels, 7*mmToPixels, Qt::AlignCenter, getInscriptionValueWithProjectParams(model, projectParamModel, 254)); painter.restore(); diff --git a/controller/specificationpcbcontroller.cpp b/controller/specificationpcbcontroller.cpp index 387c401..9421fcb 100644 --- a/controller/specificationpcbcontroller.cpp +++ b/controller/specificationpcbcontroller.cpp @@ -203,7 +203,17 @@ void SpecificationPCBController::generateTableFromComponents() 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); @@ -215,16 +225,7 @@ void SpecificationPCBController::generateTableFromComponents() 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; diff --git a/model/specificationpcbtablemodel.cpp b/model/specificationpcbtablemodel.cpp index f7f29a1..617a53c 100644 --- a/model/specificationpcbtablemodel.cpp +++ b/model/specificationpcbtablemodel.cpp @@ -1018,10 +1018,14 @@ void SpecificationPCBTableModel::updatePageNumbers() int currentRow = 0; int currentPage = 1; int rowsOnCurrentPage = 0; + bool pageNumbersChanged = false; for (int i = 0; i < m_rows.size(); ++i) { SpecificationPCBRowData &row = m_rows[i]; + // Сохраняем старый номер страницы для проверки изменений + int oldPageNumber = row.pageNumber; + // Определяем, сколько строк помещается на текущей странице int maxRowsOnPage = (currentPage == 1) ? ROWS_PER_FIRST_PAGE : ROWS_PER_OTHER_PAGE; @@ -1032,6 +1036,11 @@ void SpecificationPCBTableModel::updatePageNumbers() maxRowsOnPage = ROWS_PER_OTHER_PAGE; // Начиная со второй страницы } + // Проверяем, изменился ли номер страницы + if (oldPageNumber != currentPage) { + pageNumbersChanged = true; + } + // Устанавливаем номер страницы для строки row.pageNumber = currentPage; row.format.pageNumber = currentPage; @@ -1052,8 +1061,9 @@ void SpecificationPCBTableModel::updatePageNumbers() qDebug() << "SpecificationPCBTableModel::updatePageNumbers: Обновление завершено, всего страниц:" << currentPage; - // Уведомляем UI об изменении данных для обновления отображения страниц - if (!m_rows.isEmpty()) { + // Уведомляем UI об изменении данных ТОЛЬКО если номера страниц действительно изменились + // Это критично для производительности при редактировании больших таблиц + if (pageNumbersChanged && !m_rows.isEmpty()) { emit dataChanged(index(0, 0), index(m_rows.size() - 1, COLUMN_COUNT - 1), QVector() << Qt::BackgroundRole); emit headerDataChanged(Qt::Vertical, 0, m_rows.size() - 1); @@ -1264,11 +1274,27 @@ void SpecificationPCBTableModel::updatePageDisplay() { qDebug() << "SpecificationPCBTableModel::updatePageDisplay: Обновляем отображение страниц в UI"; + // Сохраняем старые номера страниц для проверки изменений + QList oldPageNumbers; + for (const SpecificationPCBRowData &row : m_rows) { + oldPageNumbers.append(row.pageNumber); + } + // Обновляем номера страниц updatePageNumbers(); - // Уведомляем UI об изменении всех данных для обновления отображения - if (!m_rows.isEmpty()) { + // Проверяем, изменились ли номера страниц + bool pageNumbersChanged = false; + for (int i = 0; i < m_rows.size() && i < oldPageNumbers.size(); ++i) { + if (m_rows[i].pageNumber != oldPageNumbers[i]) { + pageNumbersChanged = true; + break; + } + } + + // Уведомляем UI об изменении данных ТОЛЬКО если номера страниц действительно изменились + // Это критично для производительности при редактировании больших таблиц + if (pageNumbersChanged && !m_rows.isEmpty()) { emit dataChanged(index(0, 0), index(m_rows.size() - 1, COLUMN_COUNT - 1), QVector() << Qt::BackgroundRole); diff --git a/view/documentprocessor.cpp b/view/documentprocessor.cpp index d0ff474..d162d11 100644 --- a/view/documentprocessor.cpp +++ b/view/documentprocessor.cpp @@ -1339,6 +1339,29 @@ void DocumentEditWidget::onRemoveRow() return; } + // Проверяем, является ли процессор SpecificationProcessor + SpecificationProcessor *specProcessor = qobject_cast(m_processor); + if (specProcessor) { + // Проверяем, можно ли удалить эту строку + if (m_controller && m_controller->specificationTableModel()) { + if (!m_controller->specificationTableModel()->canEditRow(row)) { + qDebug() << "DocumentEditWidget::onRemoveRow: Строка" << row << "не может быть удалена (заголовок или автоматически сгенерированная)"; + return; + } + } + + qDebug() << "DocumentEditWidget::onRemoveRow: Удаляем строку" << row; + + // Получаем контроллер и удаляем строку + if (m_controller && m_controller->specificationController()) { + m_controller->specificationController()->removeRow(row); + qDebug() << "DocumentEditWidget::onRemoveRow: Строка удалена через SpecificationController"; + } else { + qDebug() << "DocumentEditWidget::onRemoveRow: SpecificationController недоступен"; + } + return; + } + // Проверяем, является ли процессор VedomostProcessor VedomostProcessor *vedomostProcessor = qobject_cast(m_processor); if (vedomostProcessor) { diff --git a/view/documentsettingsdialog.cpp b/view/documentsettingsdialog.cpp index df14c10..7cb2697 100644 --- a/view/documentsettingsdialog.cpp +++ b/view/documentsettingsdialog.cpp @@ -45,6 +45,10 @@ void DocumentSettingsDialog::initializeFieldData() s_fieldNames[22] = "Инв. номер дубликата"; s_fieldNames[24] = "Обозначение документа (взамен) / Справ. №"; s_fieldNames[25] = "Обозначение соответствующего документа / Перв. примен."; + s_fieldNames[251] = "Первичное применение (Перечень)"; + s_fieldNames[252] = "Первичное применение (Спецификация ПП)"; + s_fieldNames[253] = "Первичное применение (Спецификация)"; + s_fieldNames[254] = "Первичное применение (Ведомость)"; s_fieldNames[301]= "Наименование документа"; s_fieldNames[1002] = "Название документа"; @@ -77,6 +81,8 @@ DocumentSettingsDialog::DocumentSettingsDialog(DocumentType docType, MainControl , m_controller(controller) , m_projectParamModel(nullptr) , m_titleInscriptionsModel(nullptr) + , m_primaryApplicationComboBox(nullptr) + , m_primaryApplicationFieldNumber(0) { qDebug() << "DocumentSettingsDialog: Конструктор для типа документа:" << static_cast(docType); @@ -118,6 +124,9 @@ void DocumentSettingsDialog::setUp() loadDecimalNumberSettings(); } + // Загружаем настройки "Первичное применение" для всех типов документов + loadPrimaryApplicationSettings(); + qDebug() << "DocumentSettingsDialog::setUp: Настройка диалога завершена"; } @@ -166,6 +175,9 @@ void DocumentSettingsDialog::createTableSettingsTab() createDecimalNumberFields(); } + // Создаем поле "Первичное применение" для всех типов документов + createPrimaryApplicationField(); + // Создаем поле "Куда входит" для ведомости if (m_documentType == DocumentType::Vedomost) { createWhereUsedField(); @@ -551,6 +563,95 @@ void DocumentSettingsDialog::createWhereUsedField() qDebug() << "DocumentSettingsDialog::createWhereUsedField: Поле 'Куда входит' создано"; } +void DocumentSettingsDialog::createPrimaryApplicationField() +{ + qDebug() << "DocumentSettingsDialog::createPrimaryApplicationField: Создаем поле 'Первичное применение'"; + + // Определяем номер поля в зависимости от типа документа + int fieldNumber = 0; + QString fieldLabelText; + switch (m_documentType) { + case DocumentType::Perechen: + fieldNumber = 251; + fieldLabelText = "Первичное применение (Перечень):"; + break; + case DocumentType::SpecificationPCB: + fieldNumber = 252; + fieldLabelText = "Первичное применение (Спецификация ПП):"; + break; + case DocumentType::Specification: + fieldNumber = 253; + fieldLabelText = "Первичное применение (Спецификация):"; + break; + case DocumentType::Vedomost: + fieldNumber = 254; + fieldLabelText = "Первичное применение (Ведомость):"; + break; + default: + return; + } + + // Создаем группу для поля "Первичное применение" + QGroupBox *primaryAppGroup = new QGroupBox("Первичное применение", m_tableScrollContent); + QGridLayout *primaryAppLayout = new QGridLayout(primaryAppGroup); + + // Поле "Первичное применение" + QLabel *primaryAppLabel = new QLabel(fieldLabelText, primaryAppGroup); + primaryAppLayout->addWidget(primaryAppLabel, 0, 0); + + QComboBox *primaryAppComboBox = new QComboBox(primaryAppGroup); + primaryAppComboBox->setEditable(true); + primaryAppComboBox->addItem("-- Не выбрано --", ""); + + // Добавляем параметры проекта в комбобокс + if (m_projectParamModel) { + QMap projectParams = m_projectParamModel->getDataAsMap(); + for (auto it = projectParams.begin(); it != projectParams.end(); ++it) { + QString paramName = it.key(); + QString paramValue = it.value(); + + // Формируем отображаемый текст: "Название (значение)" если значение не пустое + QString displayText = paramName; + if (!paramValue.isEmpty()) { + displayText += QString(" (%1)").arg(paramValue); + } + + primaryAppComboBox->addItem(displayText, paramName); + } + } + + // Соединяем сигнал изменения + connect(primaryAppComboBox, QOverload::of(&QComboBox::currentTextChanged), + [this, fieldNumber](const QString &value) { + onPrimaryApplicationComboBoxChanged(fieldNumber, value); + }); + + // Добавляем комбобокс в компоновку + primaryAppLayout->addWidget(primaryAppComboBox, 0, 1); + + // Сохраняем ссылку на комбобокс + m_primaryApplicationComboBox = primaryAppComboBox; + m_primaryApplicationFieldNumber = fieldNumber; + + // Добавляем группу в основную компоновку таблицы + QGridLayout *mainGridLayout = qobject_cast(m_tableScrollContent->layout()); + if (mainGridLayout) { + int row = mainGridLayout->rowCount(); + mainGridLayout->addWidget(primaryAppGroup, row, 0, 1, 2); + } else { + // Если основная компоновка не является QGridLayout, создаем новую + QVBoxLayout *mainLayout = qobject_cast(m_tableScrollContent->layout()); + if (mainLayout) { + mainLayout->addWidget(primaryAppGroup); + } else { + QVBoxLayout *newLayout = new QVBoxLayout(m_tableScrollContent); + newLayout->addWidget(primaryAppGroup); + } + } + + qDebug() << "DocumentSettingsDialog::createPrimaryApplicationField: Поле 'Первичное применение' создано для поля" << fieldNumber; +} + void DocumentSettingsDialog::createFontSettingsFields() { qDebug() << "DocumentSettingsDialog::createFontSettingsFields: Создаем поля настроек шрифта"; @@ -816,7 +917,8 @@ QStringList DocumentSettingsDialog::getDefaultTableMappings() const QList DocumentSettingsDialog::getRequiredInscriptionFields() const { // Общие обязательные поля для всех документов - return {1, 2, 4, 9, 10, 11, 111, 112, 113, 114, 25}; + // Поле 25 (Первичное применение) теперь находится в настройках таблицы как 251-254 + return {1, 2, 4, 9, 10, 11, 111, 112, 113, 114}; } QList DocumentSettingsDialog::getOptionalInscriptionFields() const @@ -1146,22 +1248,24 @@ void DocumentSettingsDialog::saveInscriptionSettings() qDebug() << "DocumentSettingsDialog::saveInscriptionSettings: Сохраняем настройки надписей"; if (m_titleInscriptionsModel) { - QMap values; - + // Обновляем только поля из таба основных надписей, не заменяя все поля + // Это важно, чтобы не потерять поля 251-254 (Первичное применение) for (auto it = m_inscriptionFields.begin(); it != m_inscriptionFields.end(); ++it) { int fieldNumber = it.key(); const TitleInscriptionFieldExtended &field = it.value(); if (!field.currentValue.isEmpty()) { - values[fieldNumber] = field.currentValue; + m_titleInscriptionsModel->setInscriptionValue(fieldNumber, field.currentValue); + } else { + m_titleInscriptionsModel->setInscriptionValue(fieldNumber, ""); } } - m_titleInscriptionsModel->setAllInscriptions(values); - // Сохраняем в базу данных через контроллер + // Сохраняем все надписи из модели, как это делают остальные поля if (m_controller) { - m_controller->saveTitleInscriptionsToDatabase(values); + QMap allInscriptions = m_titleInscriptionsModel->getAllInscriptions(); + m_controller->saveTitleInscriptionsToDatabase(allInscriptions); } } @@ -1264,6 +1368,9 @@ void DocumentSettingsDialog::onAccept() saveDecimalNumberSettings(); } + // Сохраняем настройки "Первичное применение" для всех типов документов + savePrimaryApplicationSettings(); + accept(); } @@ -1617,6 +1724,118 @@ void DocumentSettingsDialog::saveDecimalNumberSettings() } } +void DocumentSettingsDialog::loadPrimaryApplicationSettings() +{ + qDebug() << "DocumentSettingsDialog::loadPrimaryApplicationSettings: Загружаем настройки 'Первичное применение'"; + + if (!m_primaryApplicationComboBox || m_primaryApplicationFieldNumber == 0) { + qDebug() << "DocumentSettingsDialog::loadPrimaryApplicationSettings: Поле 'Первичное применение' не создано"; + return; + } + + // Загружаем значение из titleInscriptionsModel + if (m_titleInscriptionsModel) { + QString value = m_titleInscriptionsModel->getInscriptionValue(m_primaryApplicationFieldNumber); + if (!value.isEmpty()) { + // Пытаемся найти значение в списке по itemData (название поля) + int index = m_primaryApplicationComboBox->findData(value); + if (index >= 0) { + m_primaryApplicationComboBox->setCurrentIndex(index); + } else { + // Если не найдено в списке, устанавливаем как кастомный текст + m_primaryApplicationComboBox->setCurrentText(value); + } + qDebug() << "DocumentSettingsDialog::loadPrimaryApplicationSettings: Загружено значение из поля" << m_primaryApplicationFieldNumber << ":" << value; + } else { + // Если значение пустое, устанавливаем "-- Не выбрано --" + m_primaryApplicationComboBox->setCurrentIndex(0); + } + } +} + +void DocumentSettingsDialog::savePrimaryApplicationSettings() +{ + qDebug() << "DocumentSettingsDialog::savePrimaryApplicationSettings: Сохраняем настройки 'Первичное применение'"; + + if (!m_primaryApplicationComboBox || m_primaryApplicationFieldNumber == 0) { + qDebug() << "DocumentSettingsDialog::savePrimaryApplicationSettings: Поле 'Первичное применение' не создано"; + return; + } + + QString value = m_primaryApplicationComboBox->currentText(); + + // Если выбрано "-- Не выбрано --", очищаем поле + if (value == "-- Не выбрано --" || value.isEmpty()) { + if (m_titleInscriptionsModel) { + m_titleInscriptionsModel->setInscriptionValue(m_primaryApplicationFieldNumber, ""); + } + qDebug() << "DocumentSettingsDialog::savePrimaryApplicationSettings: Очищено поле" << m_primaryApplicationFieldNumber; + } else { + // Получаем значение: если это предустановленное значение, берем itemData, иначе текст + int index = m_primaryApplicationComboBox->findText(value); + if (index >= 0) { + QString fieldName = m_primaryApplicationComboBox->itemData(index).toString(); + if (!fieldName.isEmpty()) { + // Это предустановленное значение - сохраняем название поля + if (m_titleInscriptionsModel) { + m_titleInscriptionsModel->setInscriptionValue(m_primaryApplicationFieldNumber, fieldName); + } + qDebug() << "DocumentSettingsDialog::savePrimaryApplicationSettings: Сохранено предустановленное значение в поле" << m_primaryApplicationFieldNumber << ":" << fieldName; + } else { + // Это кастомный ввод - сохраняем текст + if (m_titleInscriptionsModel) { + m_titleInscriptionsModel->setInscriptionValue(m_primaryApplicationFieldNumber, value); + } + qDebug() << "DocumentSettingsDialog::savePrimaryApplicationSettings: Сохранено кастомное значение в поле" << m_primaryApplicationFieldNumber << ":" << value; + } + } else { + // Кастомный ввод - сохраняем текст + if (m_titleInscriptionsModel) { + m_titleInscriptionsModel->setInscriptionValue(m_primaryApplicationFieldNumber, value); + } + qDebug() << "DocumentSettingsDialog::savePrimaryApplicationSettings: Сохранено кастомное значение в поле" << m_primaryApplicationFieldNumber << ":" << value; + } + + // Сохраняем в базу данных через контроллер + // Сохраняем все надписи из модели, как это делают остальные поля + if (m_controller && m_titleInscriptionsModel) { + QMap allInscriptions = m_titleInscriptionsModel->getAllInscriptions(); + m_controller->saveTitleInscriptionsToDatabase(allInscriptions); + qDebug() << "DocumentSettingsDialog::savePrimaryApplicationSettings: Сохранены все надписи из модели, включая поле" << m_primaryApplicationFieldNumber; + } + } +} + +void DocumentSettingsDialog::onPrimaryApplicationComboBoxChanged(int fieldNumber, const QString &value) +{ + qDebug() << "DocumentSettingsDialog::onPrimaryApplicationComboBoxChanged: Изменено значение поля" << fieldNumber << "на" << value; + + // Обновляем значение в модели (но не сохраняем в базу данных до нажатия ОК) + if (m_titleInscriptionsModel) { + if (value == "-- Не выбрано --" || value.isEmpty()) { + m_titleInscriptionsModel->setInscriptionValue(fieldNumber, ""); + } else { + // Получаем значение: если это предустановленное значение, берем itemData, иначе текст + if (m_primaryApplicationComboBox) { + int index = m_primaryApplicationComboBox->findText(value); + if (index >= 0) { + QString fieldName = m_primaryApplicationComboBox->itemData(index).toString(); + if (!fieldName.isEmpty()) { + // Это предустановленное значение - сохраняем название поля + m_titleInscriptionsModel->setInscriptionValue(fieldNumber, fieldName); + } else { + // Это кастомный ввод - сохраняем текст + m_titleInscriptionsModel->setInscriptionValue(fieldNumber, value); + } + } else { + // Кастомный ввод - сохраняем текст + m_titleInscriptionsModel->setInscriptionValue(fieldNumber, value); + } + } + } + } +} + // Публичные методы QStringList DocumentSettingsDialog::getTableColumnMappings() const { diff --git a/view/documentsettingsdialog.h b/view/documentsettingsdialog.h index d11dc8a..8ed353c 100644 --- a/view/documentsettingsdialog.h +++ b/view/documentsettingsdialog.h @@ -133,6 +133,10 @@ private: QMap m_decimalNumberComboBoxes; QMap m_decimalNumberValues; + // Поля для "Первичное применение" + QComboBox *m_primaryApplicationComboBox; + int m_primaryApplicationFieldNumber; + // Статические данные о графах ГОСТ static QMap s_fieldNames; static QMap s_fieldDescriptions; @@ -146,6 +150,12 @@ private: void onDecimalNumberComboBoxChanged(const QString &fieldName, const QString &value); void updateDecimalNumberFieldValue(const QString &type, const QString &value); + // Методы для работы с полем "Первичное применение" + void createPrimaryApplicationField(); + void loadPrimaryApplicationSettings(); + void savePrimaryApplicationSettings(); + void onPrimaryApplicationComboBoxChanged(int fieldNumber, const QString &value); + // Методы для работы с настройками шрифта void createFontSettingsFields(); void loadFontSettings(); diff --git a/view/editpdfwidget.cpp b/view/editpdfwidget.cpp index 2ef78cc..e68b767 100644 --- a/view/editpdfwidget.cpp +++ b/view/editpdfwidget.cpp @@ -140,7 +140,7 @@ void EditPDFWidget::setUpConnections(){ QString documentType; switch (index) { case 0: // Спецификация - documentType = "Спецификация материалов"; + documentType = "Спецификация"; break; case 1: // Спецификация ПП documentType = "Спецификация PCB"; diff --git a/view/mainwindow.cpp b/view/mainwindow.cpp index 573f803..290a6c9 100644 --- a/view/mainwindow.cpp +++ b/view/mainwindow.cpp @@ -752,25 +752,25 @@ void MainWindow::onExportSpecification(){ QString defaultPath = QCoreApplication::applicationDirPath() + "/" + fileNameBase + ".pdf"; QString fileName = QFileDialog::getSaveFileName( this, - tr("Экспорт спецификации материалов"), + tr("Экспорт спецификации"), defaultPath, tr("PDF файлы (*.pdf)") ); if (!fileName.isEmpty()) { - m_statusBar->showMessage(tr("Экспорт спецификации материалов: %1").arg(fileName)); + m_statusBar->showMessage(tr("Экспорт спецификации: %1").arg(fileName)); // Экспортируем в PDF через MainController if (m_controller->exportSpecificationToPdf(fileName)) { - m_statusBar->showMessage(tr("Спецификация материалов успешно экспортирована: %1").arg(fileName)); + m_statusBar->showMessage(tr("Спецификация успешно экспортирована: %1").arg(fileName)); // Автоматически открываем созданный PDF файл QUrl fileUrl = QUrl::fromLocalFile(fileName); QDesktopServices::openUrl(fileUrl); } else { - m_statusBar->showMessage(tr("Ошибка экспорта спецификации материалов")); + m_statusBar->showMessage(tr("Ошибка экспорта спецификации")); QMessageBox::critical(this, tr("Ошибка"), - tr("Не удалось экспортировать спецификацию материалов")); + tr("Не удалось экспортировать спецификацию")); } } } @@ -982,23 +982,23 @@ void MainWindow::onExportSpecificationToCsv() QString defaultPath = QCoreApplication::applicationDirPath() + "/Спецификация.csv"; QString fileName = QFileDialog::getSaveFileName( this, - tr("Экспорт спецификации материалов в CSV"), + tr("Экспорт спецификации в CSV"), defaultPath, tr("CSV файлы (*.csv);;Все файлы (*)") ); if (!fileName.isEmpty()) { - m_statusBar->showMessage(tr("Экспорт спецификации материалов в CSV: %1").arg(fileName)); + m_statusBar->showMessage(tr("Экспорт спецификации в CSV: %1").arg(fileName)); // Экспортируем в CSV через MainController if (m_controller->exportSpecificationToCsv(fileName)) { - m_statusBar->showMessage(tr("Спецификация материалов успешно экспортирована в CSV: %1").arg(fileName)); + m_statusBar->showMessage(tr("Спецификация успешно экспортирована в CSV: %1").arg(fileName)); QMessageBox::information(this, tr("Успех"), - tr("Спецификация материалов успешно экспортирована в CSV:\n%1").arg(fileName)); + tr("Спецификация успешно экспортирована в CSV:\n%1").arg(fileName)); } else { - m_statusBar->showMessage(tr("Ошибка экспорта спецификации материалов в CSV")); + m_statusBar->showMessage(tr("Ошибка экспорта спецификации в CSV")); QMessageBox::critical(this, tr("Ошибка"), - tr("Не удалось экспортировать спецификацию материалов в CSV")); + tr("Не удалось экспортировать спецификацию в CSV")); } } } diff --git a/view/previewpdfwidget.cpp b/view/previewpdfwidget.cpp index a9b238c..b63cdb0 100644 --- a/view/previewpdfwidget.cpp +++ b/view/previewpdfwidget.cpp @@ -10,11 +10,10 @@ #include #include #include // Added for qDebug -#include // Константы const int PreviewPdfWidget::ZOOM_LEVELS[] = {25, 50, 75, 100, 125, 150, 200}; -const QStringList PreviewPdfWidget::DOCUMENT_TYPES = {"Перечень элементов", "Спецификация PCB", "Спецификация материалов", "Ведомость покупных изделий"}; +const QStringList PreviewPdfWidget::DOCUMENT_TYPES = {"Перечень элементов", "Спецификация PCB", "Спецификация", "Ведомость покупных изделий"}; // Дополнительные константы для мелкого масштабирования const int PreviewPdfWidget::FINE_ZOOM_STEP = 5; // Шаг для Ctrl+колесико (5%) @@ -44,16 +43,10 @@ PreviewPdfWidget::PreviewPdfWidget(QWidget *parent) : QWidget{parent} , m_mainController(nullptr) , m_pdfController(nullptr) - , m_updateTimer(new QTimer(this)) , m_currentPage(0) , m_currentZoom(100) , m_currentDocumentType("Перечень элементов") { - // Настраиваем таймер для отложенного обновления (500 мс задержка) - m_updateTimer->setSingleShot(true); - m_updateTimer->setInterval(500); - connect(m_updateTimer, &QTimer::timeout, this, &PreviewPdfWidget::updatePreview); - setUp(); } @@ -78,6 +71,7 @@ void PreviewPdfWidget::setWidgets(){ m_prevPageBtn = new QPushButton("←", this); m_nextPageBtn = new QPushButton("→", this); + m_refreshBtn = new QPushButton("Обновить", this); m_pageInfoLabel = new QLabel("Страница 1 из 1", this); // Создаем область прокрутки для страниц @@ -119,6 +113,9 @@ void PreviewPdfWidget::setUpLayout(){ m_controlsLayout->addWidget(m_nextPageBtn); m_controlsLayout->addWidget(m_pageInfoLabel); + m_controlsLayout->addSpacing(20); + m_controlsLayout->addWidget(m_refreshBtn); + m_controlsLayout->addStretch(); // Добавляем все в основной layout @@ -154,6 +151,7 @@ void PreviewPdfWidget::setUpConnections(){ renderPage(m_currentPage); } }); + connect(m_refreshBtn, &QPushButton::clicked, this, &PreviewPdfWidget::updatePreview); // Подключаем сигнал масштабирования от ZoomableScrollArea connect(m_scrollArea, &ZoomableScrollArea::zoomRequested, @@ -180,59 +178,11 @@ void PreviewPdfWidget::setMainController(MainController *mainController) if (m_mainController) { m_pdfController = m_mainController->pdfController(); qDebug() << "PreviewPdfWidget::setMainController: PDFController получен из MainController:" << m_pdfController; - - // Подключаемся к сигналам изменения данных (используем отложенное обновление) - connect(reinterpret_cast(m_mainController->perechenTableModel()), &QAbstractItemModel::dataChanged, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->perechenTableModel()), &QAbstractItemModel::modelReset, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->perechenTableModel()), &QAbstractItemModel::rowsInserted, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->perechenTableModel()), &QAbstractItemModel::rowsRemoved, - this, [this]() { m_updateTimer->start(); }); - - connect(reinterpret_cast(m_mainController->specificationPCBTableModel()), &QAbstractItemModel::dataChanged, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->specificationPCBTableModel()), &QAbstractItemModel::modelReset, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->specificationPCBTableModel()), &QAbstractItemModel::rowsInserted, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->specificationPCBTableModel()), &QAbstractItemModel::rowsRemoved, - this, [this]() { m_updateTimer->start(); }); - - connect(reinterpret_cast(m_mainController->specificationTableModel()), &QAbstractItemModel::dataChanged, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->specificationTableModel()), &QAbstractItemModel::modelReset, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->specificationTableModel()), &QAbstractItemModel::rowsInserted, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->specificationTableModel()), &QAbstractItemModel::rowsRemoved, - this, [this]() { m_updateTimer->start(); }); - - connect(reinterpret_cast(m_mainController->vedomostTableModel()), &QAbstractItemModel::dataChanged, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->vedomostTableModel()), &QAbstractItemModel::modelReset, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->vedomostTableModel()), &QAbstractItemModel::rowsInserted, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->vedomostTableModel()), &QAbstractItemModel::rowsRemoved, - this, [this]() { m_updateTimer->start(); }); - - // Подключаемся к сигналам изменения данных TitleInscriptionsModel - connect(m_mainController->titleInscriptionsModel(), &TitleInscriptionsModel::dataChanged, - this, [this]() { - qDebug() << "PreviewPdfWidget: Получен сигнал dataChanged от TitleInscriptionsModel"; - m_updateTimer->start(); - }); - - connect(reinterpret_cast(m_mainController->projectParamTableModel()), &QAbstractItemModel::dataChanged, - this, [this]() { m_updateTimer->start(); }); - connect(reinterpret_cast(m_mainController->projectParamTableModel()), &QAbstractItemModel::modelReset, - this, [this]() { m_updateTimer->start(); }); } else { m_pdfController = nullptr; } + // Обновляем превью при первой установке контроллера updatePreview(); } @@ -261,7 +211,7 @@ void PreviewPdfWidget::updatePreview() qDebug() << "PreviewPdfWidget::updatePreview: Количество строк в спецификации PCB:" << m_mainController->specificationPCBTableModel()->rowCount(); } if (m_mainController->specificationTableModel()) { - qDebug() << "PreviewPdfWidget::updatePreview: Количество строк в спецификации материалов:" << m_mainController->specificationTableModel()->rowCount(); + qDebug() << "PreviewPdfWidget::updatePreview: Количество строк в спецификации:" << m_mainController->specificationTableModel()->rowCount(); } if (m_mainController->vedomostTableModel()) { qDebug() << "PreviewPdfWidget::updatePreview: Количество строк в ведомости покупных изделий:" << m_mainController->vedomostTableModel()->rowCount(); @@ -320,10 +270,10 @@ void PreviewPdfWidget::generatePages() qDebug() << "PreviewPdfWidget::generatePages: Вызываем getSpecificationPcbPagesInfo"; m_pagesInfo = m_pdfController->getSpecificationPcbPagesInfo(m_mainController); qDebug() << "PreviewPdfWidget::generatePages: Получено страниц спецификации PCB:" << m_pagesInfo.size(); - } else if (m_currentDocumentType == "Спецификация материалов") { + } else if (m_currentDocumentType == "Спецификация") { qDebug() << "PreviewPdfWidget::generatePages: Вызываем getSpecificationPagesInfo"; m_pagesInfo = m_pdfController->getSpecificationPagesInfo(m_mainController); - qDebug() << "PreviewPdfWidget::generatePages: Получено страниц спецификации материалов:" << m_pagesInfo.size(); + qDebug() << "PreviewPdfWidget::generatePages: Получено страниц спецификации:" << m_pagesInfo.size(); } else if (m_currentDocumentType == "Ведомость покупных изделий") { qDebug() << "PreviewPdfWidget::generatePages: Вызываем getVedomostPagesInfo"; m_pagesInfo = m_pdfController->getVedomostPagesInfo(m_mainController); @@ -384,9 +334,9 @@ QPixmap PreviewPdfWidget::createPagePixmap(const PageInfo &pageInfo) } else if (m_currentDocumentType == "Спецификация PCB") { success = m_pdfController->drawSpecificationPcbPage(painter, pageInfo, m_mainController); qDebug() << "PreviewPdfWidget::createPagePixmap: Результат рисования спецификации PCB:" << success; - } else if (m_currentDocumentType == "Спецификация материалов") { + } else if (m_currentDocumentType == "Спецификация") { success = m_pdfController->drawSpecificationPage(painter, pageInfo, m_mainController); - qDebug() << "PreviewPdfWidget::createPagePixmap: Результат рисования спецификации материалов:" << success; + qDebug() << "PreviewPdfWidget::createPagePixmap: Результат рисования спецификации:" << success; } else if (m_currentDocumentType == "Ведомость покупных изделий") { success = m_pdfController->drawVedomostPage(painter, pageInfo, m_mainController); qDebug() << "PreviewPdfWidget::createPagePixmap: Результат рисования ведомости:" << success; diff --git a/view/previewpdfwidget.h b/view/previewpdfwidget.h index 5a355d2..fa2eeef 100644 --- a/view/previewpdfwidget.h +++ b/view/previewpdfwidget.h @@ -16,7 +16,6 @@ #include #include #include -#include // Включаем структуру PageInfo #include "../model/pageinfo.h" @@ -88,15 +87,13 @@ private: QComboBox* m_pageCombo; QPushButton* m_prevPageBtn; QPushButton* m_nextPageBtn; + QPushButton* m_refreshBtn; QLabel* m_pageInfoLabel; // Контроллеры MainController* m_mainController; PDFController* m_pdfController; - // Таймер для отложенного обновления (чтобы избежать множественных обновлений при массовых изменениях) - QTimer* m_updateTimer; - // Данные QList m_pagesInfo; QList m_pagePixmaps;