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"