#include "previewpdfwidget.h" #include "../controller/maincontroller.h" #include "../controller/pdfcontroller.h" #include "../model/perechentablemodel.h" #include "../model/specificationpcbtablemodel.h" #include "../model/specificationtablemodel.h" #include "../model/vedomosttablemodel.h" #include "../model/titleinscriptionsmodel.h" #include "../model/projectparamtablemodel.h" #include #include #include // Added for qDebug // Константы const int PreviewPdfWidget::ZOOM_LEVELS[] = {25, 50, 75, 100, 125, 150, 200}; const QStringList PreviewPdfWidget::DOCUMENT_TYPES = {"Перечень элементов", "Спецификация PCB", "Спецификация", "Ведомость покупных изделий"}; // Дополнительные константы для мелкого масштабирования const int PreviewPdfWidget::FINE_ZOOM_STEP = 5; // Шаг для Ctrl+колесико (5%) const int PreviewPdfWidget::MIN_ZOOM = 10; // Минимальный масштаб 10% const int PreviewPdfWidget::MAX_ZOOM = 400; // Максимальный масштаб 400% // Реализация ZoomableScrollArea ZoomableScrollArea::ZoomableScrollArea(QWidget *parent) : QScrollArea(parent) { } void ZoomableScrollArea::wheelEvent(QWheelEvent *event) { // Проверяем, нажат ли Ctrl if (event->modifiers() & Qt::ControlModifier) { // Отправляем сигнал для масштабирования emit zoomRequested(event->angleDelta().y()); event->accept(); } else { // Если Ctrl не нажат, используем стандартную прокрутку QScrollArea::wheelEvent(event); } } PreviewPdfWidget::PreviewPdfWidget(QWidget *parent) : QWidget{parent} , m_mainController(nullptr) , m_pdfController(nullptr) , m_currentPage(0) , m_currentZoom(100) , m_currentDocumentType("Перечень элементов") { setUp(); } void PreviewPdfWidget::setUp(){ setWidgets(); setUpLayout(); setUpConnections(); } void PreviewPdfWidget::setWidgets(){ // Создаем элементы управления m_documentTypeCombo = new QComboBox(this); m_documentTypeCombo->addItems(DOCUMENT_TYPES); m_zoomCombo = new QComboBox(this); for (int zoom : ZOOM_LEVELS) { m_zoomCombo->addItem(QString("%1%").arg(zoom), zoom); } m_zoomCombo->setCurrentText("100%"); m_pageCombo = new QComboBox(this); m_prevPageBtn = new QPushButton("←", this); m_nextPageBtn = new QPushButton("→", this); m_refreshBtn = new QPushButton("Обновить", this); m_pageInfoLabel = new QLabel("Страница 1 из 1", this); // Создаем область прокрутки для страниц m_scrollArea = new ZoomableScrollArea(this); m_scrollArea->setWidgetResizable(true); m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // Создаем контейнер для страниц m_pagesContainer = new QFrame(this); m_pagesLayout = new QVBoxLayout(m_pagesContainer); m_pagesLayout->setAlignment(Qt::AlignCenter); m_pagesLayout->setSpacing(20); m_pagesLayout->setContentsMargins(20, 20, 20, 20); m_scrollArea->setWidget(m_pagesContainer); } void PreviewPdfWidget::setUpLayout(){ m_mainLayout = new QVBoxLayout(this); m_controlsLayout = new QHBoxLayout(); // Настройка стилей this->setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc;"); this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // Добавляем элементы управления m_controlsLayout->addWidget(new QLabel("Тип документа:")); m_controlsLayout->addWidget(m_documentTypeCombo); m_controlsLayout->addSpacing(20); m_controlsLayout->addWidget(new QLabel("Масштаб:")); m_controlsLayout->addWidget(m_zoomCombo); m_controlsLayout->addSpacing(20); m_controlsLayout->addWidget(new QLabel("Страница:")); m_controlsLayout->addWidget(m_prevPageBtn); m_controlsLayout->addWidget(m_pageCombo); m_controlsLayout->addWidget(m_nextPageBtn); m_controlsLayout->addWidget(m_pageInfoLabel); m_controlsLayout->addSpacing(20); m_controlsLayout->addWidget(m_refreshBtn); m_controlsLayout->addStretch(); // Добавляем все в основной layout m_mainLayout->addLayout(m_controlsLayout); m_mainLayout->addWidget(m_scrollArea); m_mainLayout->setContentsMargins(10, 10, 10, 10); m_mainLayout->setSpacing(10); } void PreviewPdfWidget::setUpConnections(){ connect(m_documentTypeCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (index >= 0 && index < DOCUMENT_TYPES.size()) { onDocumentTypeChanged(DOCUMENT_TYPES[index]); } }); connect(m_zoomCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PreviewPdfWidget::onZoomChanged); connect(m_pageCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &PreviewPdfWidget::onPageChanged); connect(m_prevPageBtn, &QPushButton::clicked, [this]() { if (m_currentPage > 0) { m_currentPage = m_currentPage - 1; m_pageCombo->setCurrentIndex(m_currentPage); renderPage(m_currentPage); } }); connect(m_nextPageBtn, &QPushButton::clicked, [this]() { if (m_currentPage < m_pagesInfo.size() - 1) { m_currentPage = m_currentPage + 1; m_pageCombo->setCurrentIndex(m_currentPage); renderPage(m_currentPage); } }); connect(m_refreshBtn, &QPushButton::clicked, this, &PreviewPdfWidget::updatePreview); // Подключаем сигнал масштабирования от ZoomableScrollArea connect(m_scrollArea, &ZoomableScrollArea::zoomRequested, this, [this](int delta) { // Определяем направление масштабирования int zoomChange = (delta > 0) ? FINE_ZOOM_STEP : -FINE_ZOOM_STEP; // Вычисляем новый масштаб int newZoom = m_currentZoom + zoomChange; // Ограничиваем масштаб newZoom = qBound(MIN_ZOOM, newZoom, MAX_ZOOM); // Применяем новый масштаб setZoom(newZoom); }); } void PreviewPdfWidget::setMainController(MainController *mainController) { m_mainController = mainController; // Получаем PDFController из MainController if (m_mainController) { m_pdfController = m_mainController->pdfController(); qDebug() << "PreviewPdfWidget::setMainController: PDFController получен из MainController:" << m_pdfController; } else { m_pdfController = nullptr; } // Обновляем превью при первой установке контроллера updatePreview(); } void PreviewPdfWidget::setPdfController(PDFController *pdfController) { // Этот метод больше не нужен, так как PDFController получается из MainController qDebug() << "PreviewPdfWidget::setPdfController: Метод устарел, используйте setMainController"; } void PreviewPdfWidget::updatePreview() { qDebug() << "PreviewPdfWidget::updatePreview: Начинаем обновление предварительного просмотра"; qDebug() << "PreviewPdfWidget::updatePreview: m_mainController =" << m_mainController; qDebug() << "PreviewPdfWidget::updatePreview: m_pdfController =" << m_pdfController; if (!m_mainController || !m_pdfController) { qDebug() << "PreviewPdfWidget::updatePreview: Контроллеры не установлены"; return; } // Проверяем данные в моделях if (m_mainController->perechenTableModel()) { qDebug() << "PreviewPdfWidget::updatePreview: Количество строк в перечне:" << m_mainController->perechenTableModel()->rowCount(); } if (m_mainController->specificationPCBTableModel()) { qDebug() << "PreviewPdfWidget::updatePreview: Количество строк в спецификации PCB:" << m_mainController->specificationPCBTableModel()->rowCount(); } if (m_mainController->specificationTableModel()) { qDebug() << "PreviewPdfWidget::updatePreview: Количество строк в спецификации:" << m_mainController->specificationTableModel()->rowCount(); } if (m_mainController->vedomostTableModel()) { qDebug() << "PreviewPdfWidget::updatePreview: Количество строк в ведомости покупных изделий:" << m_mainController->vedomostTableModel()->rowCount(); } // Сохраняем текущую страницу перед обновлением int savedPage = m_currentPage; qDebug() << "PreviewPdfWidget::updatePreview: Сохраняем текущую страницу:" << savedPage; generatePages(); updatePageNavigation(); // Восстанавливаем сохраненную страницу, если она все еще валидна if (!m_pagesInfo.isEmpty()) { // Если сохраненная страница больше не существует, используем последнюю доступную int pageToShow = savedPage; if (savedPage >= m_pagesInfo.size()) { pageToShow = m_pagesInfo.size() - 1; qDebug() << "PreviewPdfWidget::updatePreview: Сохраненная страница больше не существует, показываем последнюю:" << pageToShow; } else if (savedPage < 0) { pageToShow = 0; qDebug() << "PreviewPdfWidget::updatePreview: Сохраненная страница невалидна, показываем первую:" << pageToShow; } else { qDebug() << "PreviewPdfWidget::updatePreview: Восстанавливаем сохраненную страницу:" << pageToShow; } m_currentPage = pageToShow; renderPage(pageToShow); qDebug() << "PreviewPdfWidget::updatePreview: Предварительный просмотр обновлен, страниц:" << m_pagesInfo.size() << ", текущая страница:" << pageToShow; } else { qDebug() << "PreviewPdfWidget::updatePreview: Нет страниц для отображения"; } } void PreviewPdfWidget::generatePages() { qDebug() << "PreviewPdfWidget::generatePages: Начинаем генерацию страниц"; m_pagesInfo.clear(); m_pagePixmaps.clear(); clearPages(); if (!m_mainController || !m_pdfController) { qDebug() << "PreviewPdfWidget::generatePages: Контроллеры не установлены"; return; } qDebug() << "PreviewPdfWidget::generatePages: Генерируем страницы для типа документа:" << m_currentDocumentType; // Получаем информацию о страницах в зависимости от типа документа if (m_currentDocumentType == "Перечень элементов") { qDebug() << "PreviewPdfWidget::generatePages: Вызываем getPerechenPagesInfo"; m_pagesInfo = m_pdfController->getPerechenPagesInfo(m_mainController); qDebug() << "PreviewPdfWidget::generatePages: Получено страниц перечня:" << m_pagesInfo.size(); } else if (m_currentDocumentType == "Спецификация PCB") { qDebug() << "PreviewPdfWidget::generatePages: Вызываем getSpecificationPcbPagesInfo"; m_pagesInfo = m_pdfController->getSpecificationPcbPagesInfo(m_mainController); qDebug() << "PreviewPdfWidget::generatePages: Получено страниц спецификации PCB:" << m_pagesInfo.size(); } else if (m_currentDocumentType == "Спецификация") { qDebug() << "PreviewPdfWidget::generatePages: Вызываем getSpecificationPagesInfo"; m_pagesInfo = m_pdfController->getSpecificationPagesInfo(m_mainController); qDebug() << "PreviewPdfWidget::generatePages: Получено страниц спецификации:" << m_pagesInfo.size(); } else if (m_currentDocumentType == "Ведомость покупных изделий") { qDebug() << "PreviewPdfWidget::generatePages: Вызываем getVedomostPagesInfo"; m_pagesInfo = m_pdfController->getVedomostPagesInfo(m_mainController); qDebug() << "PreviewPdfWidget::generatePages: Получено страниц ведомости:" << m_pagesInfo.size(); } else { qDebug() << "PreviewPdfWidget::generatePages: Неизвестный тип документа:" << m_currentDocumentType; } // Создаем pixmap'ы для каждой страницы for (int i = 0; i < m_pagesInfo.size(); ++i) { const PageInfo &pageInfo = m_pagesInfo[i]; qDebug() << "PreviewPdfWidget::generatePages: Создаем pixmap для страницы" << (i + 1) << "из" << m_pagesInfo.size(); QPixmap pagePixmap = createPagePixmap(pageInfo); m_pagePixmaps.append(pagePixmap); } qDebug() << "PreviewPdfWidget::generatePages: Создано pixmap'ов:" << m_pagePixmaps.size(); } QPixmap PreviewPdfWidget::createPagePixmap(const PageInfo &pageInfo) { qDebug() << "PreviewPdfWidget::createPagePixmap: Создаем pixmap для страницы" << pageInfo.pageNumber; // Определяем размер страницы в зависимости от типа документа int pageWidth, pageHeight; if (m_currentDocumentType == "Ведомость покупных изделий") { // Размер страницы А3 в альбомной ориентации в пикселях (фиксированный, не зависит от масштаба) // A3: 420mm x 297mm, в альбомной ориентации: 420mm x 297mm // При 96 DPI: 420 * 96 / 25.4 = 1587 пикселей, 297 * 96 / 25.4 = 1123 пикселей pageWidth = 1587; // A3 width in pixels at 96 DPI (альбомная ориентация) pageHeight = 1123; // A3 height in pixels at 96 DPI (альбомная ориентация) qDebug() << "PreviewPdfWidget::createPagePixmap: Используем формат A3 в альбомной ориентации для ведомости"; } else { // Размер страницы А4 в пикселях (фиксированный, не зависит от масштаба) pageWidth = 794; // A4 width in pixels at 96 DPI pageHeight = 1123; // A4 height in pixels at 96 DPI qDebug() << "PreviewPdfWidget::createPagePixmap: Используем формат A4 для других документов"; } qDebug() << "PreviewPdfWidget::createPagePixmap: Базовый размер страницы:" << pageWidth << "x" << pageHeight; // Создаем pixmap для страницы (фиксированный размер) QPixmap pixmap(pageWidth, pageHeight); pixmap.fill(Qt::white); QPainter painter(&pixmap); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); // Устанавливаем viewport для painter painter.setViewport(0, 0, pageWidth, pageHeight); // Рисуем страницу bool success = false; if (m_currentDocumentType == "Перечень элементов") { success = m_pdfController->drawPerechenPage(painter, pageInfo, m_mainController); qDebug() << "PreviewPdfWidget::createPagePixmap: Результат рисования перечня:" << success; } else if (m_currentDocumentType == "Спецификация PCB") { success = m_pdfController->drawSpecificationPcbPage(painter, pageInfo, m_mainController); qDebug() << "PreviewPdfWidget::createPagePixmap: Результат рисования спецификации PCB:" << success; } else if (m_currentDocumentType == "Спецификация") { success = m_pdfController->drawSpecificationPage(painter, pageInfo, m_mainController); qDebug() << "PreviewPdfWidget::createPagePixmap: Результат рисования спецификации:" << success; } else if (m_currentDocumentType == "Ведомость покупных изделий") { success = m_pdfController->drawVedomostPage(painter, pageInfo, m_mainController); qDebug() << "PreviewPdfWidget::createPagePixmap: Результат рисования ведомости:" << success; } painter.end(); qDebug() << "PreviewPdfWidget::createPagePixmap: Pixmap создан, размер:" << pixmap.size(); return pixmap; } void PreviewPdfWidget::renderPage(int pageIndex) { qDebug() << "PreviewPdfWidget::renderPage: Отображаем страницу" << pageIndex; qDebug() << "PreviewPdfWidget::renderPage: Всего pixmap'ов:" << m_pagePixmaps.size(); if (pageIndex < 0 || pageIndex >= m_pagePixmaps.size()) { qDebug() << "PreviewPdfWidget::renderPage: Неверный индекс страницы:" << pageIndex; return; } // Обновляем текущую страницу m_currentPage = pageIndex; // Очищаем контейнер страниц clearPages(); // Создаем label для отображения страницы QLabel *pageLabel = new QLabel(m_pagesContainer); QPixmap originalPixmap = m_pagePixmaps[pageIndex]; qDebug() << "PreviewPdfWidget::renderPage: Оригинальный размер pixmap:" << originalPixmap.size(); // Применяем масштабирование через QTransform qreal scale = m_currentZoom / 100.0; QTransform transform; transform.scale(scale, scale); QPixmap scaledPixmap = originalPixmap.transformed(transform, Qt::SmoothTransformation); qDebug() << "PreviewPdfWidget::renderPage: Масштабированный размер pixmap:" << scaledPixmap.size() << "масштаб:" << m_currentZoom << "%"; pageLabel->setPixmap(scaledPixmap); pageLabel->setAlignment(Qt::AlignCenter); pageLabel->setStyleSheet("border: 1px solid #ccc; background-color: white;"); // Добавляем тень для страницы QFrame *pageFrame = new QFrame(m_pagesContainer); QVBoxLayout *frameLayout = new QVBoxLayout(pageFrame); frameLayout->addWidget(pageLabel); frameLayout->setContentsMargins(10, 10, 10, 10); pageFrame->setStyleSheet("QFrame { background-color: #e0e0e0; border-radius: 5px; }"); m_pagesLayout->addWidget(pageFrame); // Обновляем информацию о странице if (pageIndex < m_pagesInfo.size()) { const PageInfo &pageInfo = m_pagesInfo[pageIndex]; m_pageInfoLabel->setText(QString("Страница %1 из %2").arg(pageInfo.pageNumber).arg(pageInfo.totalPages)); qDebug() << "PreviewPdfWidget::renderPage: Обновлена информация о странице:" << pageInfo.pageNumber << "из" << pageInfo.totalPages; } // Обновляем состояние кнопок навигации m_prevPageBtn->setEnabled(m_currentPage > 0); m_nextPageBtn->setEnabled(m_currentPage < m_pagesInfo.size() - 1); // Обновляем комбобокс страниц if (m_currentPage >= 0 && m_currentPage < m_pageCombo->count()) { m_pageCombo->blockSignals(true); m_pageCombo->setCurrentIndex(m_currentPage); m_pageCombo->blockSignals(false); } qDebug() << "PreviewPdfWidget::renderPage: Страница отображена успешно"; } void PreviewPdfWidget::updatePageNavigation() { m_pageCombo->clear(); for (int i = 0; i < m_pagesInfo.size(); ++i) { const PageInfo &pageInfo = m_pagesInfo[i]; m_pageCombo->addItem(QString("Страница %1").arg(pageInfo.pageNumber), i); } // Устанавливаем текущую страницу в комбобоксе if (m_currentPage >= 0 && m_currentPage < m_pageCombo->count()) { m_pageCombo->setCurrentIndex(m_currentPage); } } void PreviewPdfWidget::clearPages() { // Удаляем все виджеты из layout QLayoutItem *item; while ((item = m_pagesLayout->takeAt(0)) != nullptr) { if (item->widget()) { item->widget()->deleteLater(); } delete item; } } void PreviewPdfWidget::onDocumentTypeChanged(const QString &documentType) { qDebug() << "PreviewPdfWidget::onDocumentTypeChanged: Изменен тип документа на:" << documentType; // Проверяем, поддерживается ли этот тип документа if (DOCUMENT_TYPES.contains(documentType)) { m_currentDocumentType = documentType; // Обновляем выбранный элемент в комбобоксе int index = DOCUMENT_TYPES.indexOf(documentType); if (index >= 0) { m_documentTypeCombo->setCurrentIndex(index); } updatePreview(); emit documentTypeChanged(m_currentDocumentType); } else { qDebug() << "PreviewPdfWidget::onDocumentTypeChanged: Неподдерживаемый тип документа:" << documentType; } } void PreviewPdfWidget::onZoomChanged(int index) { if (index >= 0 && index < m_zoomCombo->count()) { // Получаем значение из комбобокса (может быть как из стандартного списка, так и произвольное) QVariant data = m_zoomCombo->itemData(index); if (data.isValid()) { int newZoom = data.toInt(); setZoom(newZoom); } else { // Если данных нет, пытаемся извлечь значение из текста QString text = m_zoomCombo->currentText(); text.remove('%'); // Убираем символ процента bool ok; int newZoom = text.toInt(&ok); if (ok) { setZoom(newZoom); } } } } void PreviewPdfWidget::onPageChanged(int page) { if (page >= 0 && page < m_pagesInfo.size()) { renderPage(page); emit pageChanged(page); } } void PreviewPdfWidget::setZoom(int zoom) { // Ограничиваем масштаб zoom = qBound(MIN_ZOOM, zoom, MAX_ZOOM); // Обновляем текущий масштаб m_currentZoom = zoom; qDebug() << "PreviewPdfWidget::setZoom: Установлен масштаб:" << m_currentZoom << "%"; // Временно блокируем сигналы комбобокса m_zoomCombo->blockSignals(true); // Очищаем произвольные значения из комбобокса (оставляем только стандартные) while (m_zoomCombo->count() > sizeof(ZOOM_LEVELS) / sizeof(ZOOM_LEVELS[0])) { m_zoomCombo->removeItem(0); } // Обновляем комбобокс масштаба // Сначала проверяем, есть ли точное значение в списке bool found = false; for (int i = 0; i < sizeof(ZOOM_LEVELS) / sizeof(ZOOM_LEVELS[0]); ++i) { if (ZOOM_LEVELS[i] == zoom) { m_zoomCombo->setCurrentIndex(i); found = true; break; } } // Если точного значения нет в списке, добавляем его как временный элемент if (!found) { // Добавляем текущее значение в начало списка m_zoomCombo->insertItem(0, QString("%1%").arg(zoom), zoom); m_zoomCombo->setCurrentIndex(0); } // Разблокируем сигналы m_zoomCombo->blockSignals(false); // Перерисовываем текущую страницу с новым масштабом if (m_currentPage >= 0 && m_currentPage < m_pagePixmaps.size()) { renderPage(m_currentPage); } emit zoomChanged(m_currentZoom); }