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 * 0009 * SPDX-FileCopyrightText: 2011-2016 by Benjamin Girault <benjamin dot girault at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "panooptimizepage.h" 0016 0017 // Qt includes 0018 0019 #include <QDir> 0020 #include <QLabel> 0021 #include <QPixmap> 0022 #include <QPushButton> 0023 #include <QTimer> 0024 #include <QCheckBox> 0025 #include <QMutex> 0026 #include <QMutexLocker> 0027 #include <QStandardPaths> 0028 #include <QApplication> 0029 #include <QVBoxLayout> 0030 #include <QHBoxLayout> 0031 #include <QTextBrowser> 0032 0033 // KDE includes 0034 0035 #include <klocalizedstring.h> 0036 #include <ksharedconfig.h> 0037 #include <kconfiggroup.h> 0038 0039 // Local includes 0040 0041 #include "digikam_debug.h" 0042 #include "autooptimiserbinary.h" 0043 #include "panomodifybinary.h" 0044 #include "panomanager.h" 0045 #include "panoactionthread.h" 0046 #include "dlayoutbox.h" 0047 #include "dworkingpixmap.h" 0048 0049 namespace DigikamGenericPanoramaPlugin 0050 { 0051 0052 class Q_DECL_HIDDEN PanoOptimizePage::Private 0053 { 0054 public: 0055 0056 explicit Private() 0057 : progressCount (0), 0058 progressLabel (nullptr), 0059 progressTimer (nullptr), 0060 optimisationDone (false), 0061 canceled (false), 0062 title (nullptr), 0063 /* 0064 preprocessResults (0), 0065 */ 0066 horizonCheckbox (nullptr), 0067 /* 0068 projectionAndSizeCheckbox (0), 0069 */ 0070 detailsText (nullptr), 0071 progressPix (nullptr), 0072 mngr (nullptr) 0073 { 0074 } 0075 0076 int progressCount; 0077 QLabel* progressLabel; 0078 QTimer* progressTimer; 0079 QMutex progressMutex; ///< This is a precaution in case the user does a back / next action at the wrong moment 0080 bool optimisationDone; 0081 bool canceled; 0082 0083 QLabel* title; 0084 /* 0085 QLabel* preprocessResults; 0086 */ 0087 QCheckBox* horizonCheckbox; 0088 /* 0089 QCheckBox* projectionAndSizeCheckboxs; 0090 */ 0091 QTextBrowser* detailsText; 0092 0093 DWorkingPixmap* progressPix; 0094 0095 PanoManager* mngr; 0096 }; 0097 0098 PanoOptimizePage::PanoOptimizePage(PanoManager* const mngr, QWizard* const dlg) 0099 : DWizardPage(dlg, QString::fromLatin1("<b>%1</b>").arg(i18nc("@title: window", "Optimization"))), 0100 d (new Private) 0101 { 0102 d->mngr = mngr; 0103 d->progressTimer = new QTimer(this); 0104 d->progressPix = new DWorkingPixmap(this); 0105 DVBox* const vbox = new DVBox(this); 0106 d->title = new QLabel(vbox); 0107 d->title->setOpenExternalLinks(true); 0108 d->title->setWordWrap(true); 0109 0110 KSharedConfigPtr config = KSharedConfig::openConfig(); 0111 KConfigGroup group = config->group(QLatin1String("Panorama Settings")); 0112 0113 d->horizonCheckbox = new QCheckBox(i18nc("@option: check", "Level horizon"), vbox); 0114 d->horizonCheckbox->setChecked(group.readEntry("Horizon", true)); 0115 d->horizonCheckbox->setToolTip(i18nc("@info: tooltip", "Detect the horizon and adapt the project to make it horizontal.")); 0116 d->horizonCheckbox->setWhatsThis(i18nc("@info: whatsthis", "\"Level horizon\": Detect the horizon and adapt the projection so that " 0117 "the detected horizon is an horizontal line in the final panorama")); 0118 /* 0119 if (!d->mngr->gPano()) 0120 { 0121 d->projectionAndSizeCheckbox = new QCheckBox(i18nc("@option: check", "Automatic projection and output aspect"), vbox); 0122 d->projectionAndSizeCheckbox->setChecked(group.readEntry("Output Projection And Size", true)); 0123 d->projectionAndSizeCheckbox->setToolTip(i18nc("@info: tooltip", "Adapt the projection of the panorama and the area rendered on the " 0124 "resulting projection so that every photo fits in the resulting " 0125 "panorama.")); 0126 d->projectionAndSizeCheckbox->setWhatsThis(i18nc("@info: whatsthis", "\"Automatic projection and output aspect\": Automatically " 0127 "adapt the projection and the area rendered of the panorama to " 0128 "get every photos into the panorama.")); 0129 } 0130 else 0131 { 0132 d->projectionAndSizeCheckbox = new QCheckBox(i18nc("@option: check", "Automatic output aspect"), vbox); 0133 d->projectionAndSizeCheckbox->setChecked(group.readEntry("Output Projection And Size", true)); 0134 d->projectionAndSizeCheckbox->setToolTip(i18nc("@info: tooltip", "Adapt the area rendered on the resulting projection so that " 0135 "every photo fits in the resulting panorama.")); 0136 d->projectionAndSizeCheckbox->setWhatsThis(i18nc("@info: whatsthis", "\"Automatic output aspect\": Automatically adapt the area " 0137 "rendered of the panorama to get every photos into the panorama.")); 0138 } 0139 */ 0140 0141 /* 0142 d->preprocessResults = new QLabel(vbox); 0143 */ 0144 0145 vbox->setStretchFactor(new QWidget(vbox), 2); 0146 0147 d->detailsText = new QTextBrowser(vbox); 0148 d->detailsText->hide(); 0149 0150 vbox->setStretchFactor(new QWidget(vbox), 2); 0151 0152 d->progressLabel = new QLabel(vbox); 0153 d->progressLabel->setAlignment(Qt::AlignCenter); 0154 0155 vbox->setStretchFactor(new QWidget(vbox), 10); 0156 0157 setPageWidget(vbox); 0158 0159 QPixmap leftPix(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("digikam/data/assistant-hugin.png"))); 0160 setLeftBottomPix(leftPix.scaledToWidth(128, Qt::SmoothTransformation)); 0161 0162 connect(d->progressTimer, SIGNAL(timeout()), 0163 this, SLOT(slotProgressTimerDone())); 0164 } 0165 0166 PanoOptimizePage::~PanoOptimizePage() 0167 { 0168 KSharedConfigPtr config = KSharedConfig::openConfig(); 0169 KConfigGroup group = config->group(QLatin1String("Panorama Settings")); 0170 group.writeEntry("Horizon", d->horizonCheckbox->isChecked()); 0171 /* 0172 group.writeEntry("Output Projection And Size", d->projectionAndSizeCheckbox->isChecked()); 0173 */ 0174 config->sync(); 0175 0176 delete d; 0177 } 0178 0179 void PanoOptimizePage::process() 0180 { 0181 QMutexLocker lock(&d->progressMutex); 0182 0183 d->title->setText(QString::fromUtf8("<qt>" 0184 "<p>%1</p>" 0185 "<p>%2</p>" 0186 "</qt>") 0187 .arg(i18nc("@info", "Optimization is in progress, please wait.")) 0188 .arg(i18nc("@info", "This can take a while..."))); 0189 0190 d->horizonCheckbox->hide(); 0191 /* 0192 d->projectionAndSizeCheckbox->hide(); 0193 */ 0194 d->progressTimer->start(300); 0195 0196 connect(d->mngr->thread(), SIGNAL(stepFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0197 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0198 0199 connect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0200 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0201 0202 d->mngr->resetAutoOptimisePto(); 0203 d->mngr->resetViewAndCropOptimisePto(); 0204 d->mngr->thread()->optimizeProject(d->mngr->cpCleanPtoUrl(), 0205 d->mngr->autoOptimisePtoUrl(), 0206 d->mngr->viewAndCropOptimisePtoUrl(), 0207 d->horizonCheckbox->isChecked(), 0208 d->mngr->gPano(), 0209 d->mngr->autoOptimiserBinary().path(), 0210 d->mngr->panoModifyBinary().path()); 0211 } 0212 0213 void PanoOptimizePage::initializePage() 0214 { 0215 d->title->setText(QString::fromUtf8("<qt>" 0216 "<p>%1</p>" 0217 "<p>%2</p>" 0218 "<p>%3</p>" 0219 "<p>%4</p>" 0220 "</qt>") 0221 .arg(i18nc("@info", "The optimization step according to your settings is ready to be performed.")) 0222 .arg(i18nc("@info", "This step can include an automatic leveling of the horizon, and also " 0223 "an automatic projection selection and size.")) 0224 .arg(i18nc("@info", "To perform this operation, the \"%1\" program will be used.", 0225 QDir::toNativeSeparators(d->mngr->autoOptimiserBinary().path()))) 0226 .arg(i18nc("@info", "Press the \"Next\" button to run the optimization."))); 0227 0228 /* 0229 QPair<double, int> result = d->mngr->cpFindUrlData().standardDeviation(); 0230 d->preprocessResults->setText(i18n("Alignment error: %1px", result.first / ((double) result.second))); 0231 */ 0232 d->detailsText->hide(); 0233 d->horizonCheckbox->show(); 0234 /* 0235 d->projectionAndSizeCheckbox->show(); 0236 */ 0237 d->canceled = false; 0238 d->optimisationDone = false; 0239 0240 setComplete(true); 0241 Q_EMIT completeChanged(); 0242 } 0243 0244 bool PanoOptimizePage::validatePage() 0245 { 0246 if (d->optimisationDone) 0247 { 0248 return true; 0249 } 0250 0251 setComplete(false); 0252 process(); 0253 0254 return false; 0255 } 0256 0257 void PanoOptimizePage::cleanupPage() 0258 { 0259 d->canceled = true; 0260 0261 disconnect(d->mngr->thread(), SIGNAL(stepFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0262 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0263 0264 disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0265 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0266 0267 d->mngr->thread()->cancel(); 0268 0269 QMutexLocker lock(&d->progressMutex); 0270 0271 if (d->progressTimer->isActive()) 0272 { 0273 d->progressTimer->stop(); 0274 d->progressLabel->clear(); 0275 } 0276 } 0277 0278 void PanoOptimizePage::slotProgressTimerDone() 0279 { 0280 d->progressLabel->setPixmap(d->progressPix->frameAt(d->progressCount)); 0281 0282 if (d->progressPix->frameCount()) 0283 { 0284 d->progressCount = (d->progressCount + 1) % d->progressPix->frameCount(); 0285 } 0286 0287 d->progressTimer->start(300); 0288 } 0289 0290 void PanoOptimizePage::slotPanoAction(const DigikamGenericPanoramaPlugin::PanoActionData& ad) 0291 { 0292 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "SlotPanoAction (optimize)"; 0293 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "starting, success, canceled, action: " << ad.starting << ad.success << d->canceled << ad.action; 0294 0295 QString text; 0296 0297 QMutexLocker lock(&d->progressMutex); 0298 0299 if (!ad.starting) // Something is complete... 0300 { 0301 if (!ad.success) // Something is failed... 0302 { 0303 if (d->canceled) // In that case, the error is expected 0304 { 0305 return; 0306 } 0307 0308 switch (ad.action) 0309 { 0310 case PANO_OPTIMIZE: 0311 case PANO_AUTOCROP: 0312 { 0313 disconnect(d->mngr->thread(), SIGNAL(stepFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0314 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0315 0316 disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0317 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0318 0319 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Job failed (optimize): " << ad.action; 0320 0321 if (d->detailsText->isHidden()) 0322 { 0323 d->title->setText(QString::fromUtf8("<qt>" 0324 "<p>%1</p>" 0325 "<p>%2</p>" 0326 "</qt>") 0327 .arg(i18nc("@info", "Optimization has failed.")) 0328 .arg(i18nc("@info", "See processing messages below."))); 0329 d->progressTimer->stop(); 0330 d->horizonCheckbox->hide(); 0331 /* 0332 d->projectionAndSizeCheckbox->hide(); 0333 */ 0334 d->detailsText->show(); 0335 d->progressLabel->clear(); 0336 d->detailsText->setText(ad.message); 0337 0338 setComplete(false); 0339 Q_EMIT completeChanged(); 0340 } 0341 0342 break; 0343 } 0344 0345 default: 0346 { 0347 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown action (optimize) " << ad.action; 0348 break; 0349 } 0350 } 0351 } 0352 else // Something is done... 0353 { 0354 switch (ad.action) 0355 { 0356 case PANO_OPTIMIZE: 0357 { 0358 return; 0359 } 0360 0361 case PANO_AUTOCROP: 0362 { 0363 disconnect(d->mngr->thread(), SIGNAL(stepFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0364 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0365 0366 disconnect(d->mngr->thread(), SIGNAL(jobCollectionFinished(DigikamGenericPanoramaPlugin::PanoActionData)), 0367 this, SLOT(slotPanoAction(DigikamGenericPanoramaPlugin::PanoActionData))); 0368 0369 d->progressTimer->stop(); 0370 d->progressLabel->clear(); 0371 d->optimisationDone = true; 0372 0373 Q_EMIT signalOptimized(); 0374 initializePage(); 0375 0376 break; 0377 } 0378 0379 default: 0380 { 0381 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Unknown action (optimize) " << ad.action; 0382 break; 0383 } 0384 } 0385 } 0386 } 0387 } 0388 0389 } // namespace DigikamGenericPanoramaPlugin 0390 0391 #include "moc_panooptimizepage.cpp"