File indexing completed on 2025-01-05 03:53:11
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2011-05-23 0007 * Description : a tool to create panorama by fusion of several images. 0008 * Acknowledge : based on the expoblending tool 0009 * 0010 * SPDX-FileCopyrightText: 2011-2016 by Benjamin Girault <benjamin dot girault at gmail dot com> 0011 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "panolastpage.h" 0018 0019 // Qt includes 0020 0021 #include <QUrl> 0022 #include <QFile> 0023 #include <QDir> 0024 #include <QLabel> 0025 #include <QPixmap> 0026 #include <QGroupBox> 0027 #include <QVBoxLayout> 0028 #include <QCheckBox> 0029 #include <QStandardPaths> 0030 #include <QLineEdit> 0031 0032 // KDE includes 0033 0034 #include <klocalizedstring.h> 0035 #include <ksharedconfig.h> 0036 #include <kconfiggroup.h> 0037 0038 // Local includes 0039 0040 #include "digikam_debug.h" 0041 #include "panomanager.h" 0042 #include "panoactionthread.h" 0043 #include "dlayoutbox.h" 0044 0045 namespace DigikamGenericPanoramaPlugin 0046 { 0047 0048 class Q_DECL_HIDDEN PanoLastPage::Private 0049 { 0050 public: 0051 0052 explicit Private() 0053 : copyDone (false), 0054 title (nullptr), 0055 saveSettingsGroupBox (nullptr), 0056 fileTemplateQLineEdit(nullptr), 0057 savePtoCheckBox (nullptr), 0058 warningLabel (nullptr), 0059 errorLabel (nullptr), 0060 mngr (nullptr) 0061 { 0062 } 0063 0064 bool copyDone; 0065 0066 QLabel* title; 0067 0068 QGroupBox* saveSettingsGroupBox; 0069 QLineEdit* fileTemplateQLineEdit; 0070 QCheckBox* savePtoCheckBox; 0071 QLabel* warningLabel; 0072 QLabel* errorLabel; 0073 0074 PanoManager* mngr; 0075 }; 0076 0077 PanoLastPage::PanoLastPage(PanoManager* const mngr, QWizard* const dlg) 0078 : DWizardPage(dlg, QString::fromLatin1("<b>%1</b>").arg(i18nc("@title:window", "Panorama Stitched"))), 0079 d (new Private) 0080 { 0081 KSharedConfigPtr config = KSharedConfig::openConfig(); 0082 KConfigGroup group = config->group(QLatin1String("Panorama Settings")); 0083 0084 d->mngr = mngr; 0085 0086 DVBox* const vbox = new DVBox(this); 0087 0088 d->title = new QLabel(vbox); 0089 d->title->setOpenExternalLinks(true); 0090 d->title->setWordWrap(true); 0091 0092 QVBoxLayout* const formatVBox = new QVBoxLayout(); 0093 0094 d->saveSettingsGroupBox = new QGroupBox(i18nc("@title:group", "Save Settings"), vbox); 0095 d->saveSettingsGroupBox->setLayout(formatVBox); 0096 formatVBox->addStretch(1); 0097 0098 QLabel* const fileTemplateLabel = new QLabel(i18nc("@label:textbox", "File name template:"), d->saveSettingsGroupBox); 0099 formatVBox->addWidget(fileTemplateLabel); 0100 0101 d->fileTemplateQLineEdit = new QLineEdit(QLatin1String("panorama"), d->saveSettingsGroupBox); 0102 d->fileTemplateQLineEdit->setToolTip(i18nc("@info:tooltip", "Name of the panorama file (without its extension).")); 0103 d->fileTemplateQLineEdit->setWhatsThis(i18nc("@info:whatsthis", "\"File name template\": Set here the base name of the files that " 0104 "will be saved. For example, if your template is \"panorama\" and if " 0105 "you chose a JPEG output, then your panorama will be saved with the " 0106 "name \"panorama.jpg\". If you choose to save also the project file, " 0107 "it will have the name \"panorama.pto\".")); 0108 formatVBox->addWidget(d->fileTemplateQLineEdit); 0109 0110 d->savePtoCheckBox = new QCheckBox(i18nc("@option:check", "Save project file"), d->saveSettingsGroupBox); 0111 d->savePtoCheckBox->setChecked(group.readEntry("Save PTO", false)); 0112 d->savePtoCheckBox->setToolTip(i18nc("@info:tooltip", "Save the project file for further processing within Hugin GUI.")); 0113 d->savePtoCheckBox->setWhatsThis(i18nc("@info:whatsthis", "\"Save project file\": You can keep the project file generated to stitch " 0114 "your panorama for further tweaking within %1 by checking this. " 0115 "This is useful if you want a different projection, modify the horizon or " 0116 "the center of the panorama, or modify the control points to get better results.", 0117 QLatin1String("<a href=\"http://hugin.sourceforge.net/\">Hugin</a>"))); // krazy:exclude=insecurenet 0118 formatVBox->addWidget(d->savePtoCheckBox); 0119 0120 d->warningLabel = new QLabel(d->saveSettingsGroupBox); 0121 d->warningLabel->hide(); 0122 formatVBox->addWidget(d->warningLabel); 0123 0124 d->errorLabel = new QLabel(d->saveSettingsGroupBox); 0125 d->errorLabel->hide(); 0126 formatVBox->addWidget(d->errorLabel); 0127 0128 vbox->setStretchFactor(new QWidget(vbox), 2); 0129 0130 setPageWidget(vbox); 0131 0132 QPixmap leftPix(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("digikam/data/assistant-hugin.png"))); 0133 setLeftBottomPix(leftPix.scaledToWidth(128, Qt::SmoothTransformation)); 0134 0135 connect(d->fileTemplateQLineEdit, SIGNAL(textChanged(QString)), 0136 this, SLOT(slotTemplateChanged(QString))); 0137 0138 connect(d->savePtoCheckBox, SIGNAL(stateChanged(int)), 0139 this, SLOT(slotPtoCheckBoxChanged(int))); 0140 } 0141 0142 PanoLastPage::~PanoLastPage() 0143 { 0144 KSharedConfigPtr config = KSharedConfig::openConfig(); 0145 KConfigGroup group = config->group(QLatin1String("Panorama Settings")); 0146 group.writeEntry("Save PTO", d->savePtoCheckBox->isChecked()); 0147 config->sync(); 0148 0149 delete d; 0150 } 0151 0152 void PanoLastPage::copyFiles() 0153 { 0154 connect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0155 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0156 0157 QUrl panoUrl = d->mngr->preProcessedMap().begin().key().adjusted(QUrl::RemoveFilename); 0158 panoUrl.setPath(panoUrl.path() + panoFileName(d->fileTemplateQLineEdit->text())); 0159 0160 d->mngr->thread()->copyFiles( 0161 d->mngr->panoPtoUrl(), 0162 d->mngr->panoUrl(), 0163 panoUrl, 0164 d->mngr->preProcessedMap(), 0165 d->savePtoCheckBox->isChecked(), 0166 d->mngr->gPano() 0167 ); 0168 } 0169 0170 QString PanoLastPage::panoFileName(const QString& fileTemplate) const 0171 { 0172 switch (d->mngr->format()) 0173 { 0174 default: 0175 case JPEG: 0176 return fileTemplate + QLatin1String(".jpg"); 0177 0178 case TIFF: 0179 return fileTemplate + QLatin1String(".tif"); 0180 } 0181 } 0182 0183 void PanoLastPage::checkFiles() 0184 { 0185 QString dir = d->mngr->preProcessedMap().begin().key().toString(QUrl::RemoveFilename); 0186 QUrl panoUrl(dir + panoFileName(d->fileTemplateQLineEdit->text())); 0187 QUrl ptoUrl(dir + d->fileTemplateQLineEdit->text() + QLatin1String(".pto")); 0188 QFile panoFile(panoUrl.toString(QUrl::PreferLocalFile)); 0189 QFile ptoFile(ptoUrl.toString(QUrl::PreferLocalFile)); 0190 0191 bool rawsOk = true; 0192 0193 if (d->savePtoCheckBox->isChecked()) 0194 { 0195 for (auto& input : d->mngr->preProcessedMap().keys()) 0196 { 0197 if (input != d->mngr->preProcessedMap()[input].preprocessedUrl) 0198 { 0199 QString dir2 = input.toString(QUrl::RemoveFilename); 0200 QUrl derawUrl(dir2 + d->mngr->preProcessedMap()[input].preprocessedUrl.fileName()); 0201 QFile derawFile(derawUrl.toString(QUrl::PreferLocalFile)); 0202 rawsOk &= !derawFile.exists(); 0203 } 0204 } 0205 } 0206 0207 if (panoFile.exists() || (d->savePtoCheckBox->isChecked() && ptoFile.exists())) 0208 { 0209 setComplete(false); 0210 Q_EMIT completeChanged(); 0211 d->warningLabel->setText(QString::fromUtf8("<qt><p><font color=\"red\"><b>%1:</b> %2.</font></p></qt>") 0212 .arg(i18nc("@title: dialog", "Warning")) 0213 .arg(i18nc("@label", "This file already exists"))); 0214 d->warningLabel->show(); 0215 } 0216 else if (!rawsOk) 0217 { 0218 setComplete(true); 0219 Q_EMIT completeChanged(); 0220 d->warningLabel->setText(QString::fromUtf8("<qt><p><font color=\"orange\"><b>:</b> %2.</font></p></qt>") 0221 .arg(i18nc("@title: dialog", "Warning")) 0222 .arg(i18nc("@label", "One or more converted raw files already exists (they will be skipped during the copying process)"))); 0223 d->warningLabel->show(); 0224 } 0225 else 0226 { 0227 setComplete(true); 0228 Q_EMIT completeChanged(); 0229 d->warningLabel->hide(); 0230 } 0231 } 0232 0233 void PanoLastPage::initializePage() 0234 { 0235 QString first = d->mngr->itemsList().first().fileName(); 0236 QString last = d->mngr->itemsList().last().fileName(); 0237 QString file = QString::fromLatin1("%1-%2") 0238 .arg(first.left(first.lastIndexOf(QLatin1Char('.')))) 0239 .arg(last.left(last.lastIndexOf(QLatin1Char('.')))); 0240 d->fileTemplateQLineEdit->setText(file); 0241 0242 checkFiles(); 0243 } 0244 0245 bool PanoLastPage::validatePage() 0246 { 0247 if (d->copyDone) 0248 { 0249 return true; 0250 } 0251 0252 setComplete(false); 0253 copyFiles(); 0254 0255 return false; 0256 } 0257 0258 void PanoLastPage::slotTemplateChanged(const QString&) 0259 { 0260 d->title->setText(QString::fromUtf8("<qt>" 0261 "<p><h1><b>%1</b></h1></p>" 0262 "<p>%2</p>" 0263 "<p>%3</p>" 0264 "<p>%4<br /><b>%5</b><br /></p>" 0265 "<p>%6</p>" 0266 "</qt>") 0267 .arg(i18nc("@info", "Panorama Stitching is Done")) 0268 .arg(i18nc("@info", "Congratulations. Your images are stitched into a panorama.")) 0269 .arg(i18nc("@info", "Your panorama will be created to the directory:")) 0270 .arg(QDir::toNativeSeparators(d->mngr->preProcessedMap().begin().key().toString(QUrl::RemoveFilename | QUrl::PreferLocalFile))) 0271 .arg(i18nc("@info", "once you press the \"Finish\" button, with the name set below.")) 0272 .arg(i18nc("@info", "If you choose to save the project file, and " 0273 "if your images were raw images then the converted images used during " 0274 "the stitching process will be copied at the same time (those are " 0275 "TIFF files that can be big)."))); 0276 checkFiles(); 0277 } 0278 0279 void PanoLastPage::slotPtoCheckBoxChanged(int) 0280 { 0281 checkFiles(); 0282 } 0283 0284 void PanoLastPage::slotPanoAction(const DigikamGenericPanoramaPlugin::PanoActionData& ad) 0285 { 0286 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "SlotPanoAction (lastPage)"; 0287 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "starting, success, action:" << ad.starting << ad.success << ad.action; 0288 0289 if (!ad.starting) // Something is complete... 0290 { 0291 if (!ad.success) // Something is failed... 0292 { 0293 switch (ad.action) 0294 { 0295 case PANO_COPY: 0296 { 0297 disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0298 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0299 0300 d->errorLabel->setText(QString::fromUtf8("<qt><p><font color=\"red\"><b>%1:</b> %2</font></p></qt>") 0301 .arg(i18nc("@label", "Error")) 0302 .arg(ad.message)); 0303 d->errorLabel->show(); 0304 break; 0305 } 0306 0307 default: 0308 { 0309 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown action (last) " << ad.action; 0310 break; 0311 } 0312 } 0313 } 0314 else // Something is done... 0315 { 0316 switch (ad.action) 0317 { 0318 case PANO_COPY: 0319 { 0320 disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0321 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0322 0323 d->copyDone = true; 0324 Q_EMIT signalCopyFinished(); 0325 break; 0326 } 0327 0328 default: 0329 { 0330 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown action (last) " << ad.action; 0331 break; 0332 } 0333 } 0334 } 0335 } 0336 } 0337 0338 } // namespace DigikamGenericPanoramaPlugin 0339 0340 #include "moc_panolastpage.cpp"