Commit 62125c25 authored by Gilles Caulier's avatar Gilles Caulier 🗼
Browse files

Maintenance Manager : complete rewrite of maintenance tools handling.

Instead to use signal provided by tool to know if job is done, use progress manager signal.
Store instance of each tools in d private container instead stack to provide a better check of stage under progress.
Patch ScanController to provide a better feedback to NewItemsFinder, in all scan mode. Add a new method to only call core code to perform
scan WITHOUT to show a progress dialog. In fact progress manager as already all in place to give user feedback about progress...
Patch FaceManagement maintenance tool to indicate that face are detected and recognized.
Move status progress bar with ProgresManager code, as this one is fully relevant.

Marcel, I would to drop fully DProgressDialog code, which is only used now to ScanController, with all first run step to collect all information from image
managed by database. I remember that in 2.x serie, you have patched digiKam to only perform first run scan in background when albumgui is show. I'm right ?
At now, this feature sound fonctionnal some time, but it's not fully reproducible.
If you take a look how Google Picasa work, you will see that main DB referencing run in background when main GUI is displayed. A progress indication is show in statusbar.
User can start to work with collected items in real time and don't need to wait a long time that all referencing is done through a small progress dialog...
Look this entry for ex : 320359.
What do you think about?

BUGS: 295256
BUGS: 320121
BUGS: 295255
BUGS: 297614
BUGS: 306282

CCMAIL: marcel.wiesweg@gmx.de

FIXED-IN: 3.3.0
parent dd3dc1cd
......@@ -989,7 +989,6 @@ IF(DIGIKAM_CAN_BE_COMPILED)
${CMAKE_CURRENT_SOURCE_DIR}/libs/widgets/common/sidebar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libs/widgets/common/splashscreen.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libs/widgets/common/statesavingobject.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libs/widgets/common/statusprogressbar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libs/widgets/common/visibilitycontroller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libs/widgets/common/workingwidget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libs/widgets/common/ratingwidget.cpp
......@@ -1122,6 +1121,7 @@ IF(DIGIKAM_CAN_BE_COMPILED)
${CMAKE_CURRENT_SOURCE_DIR}/libs/progressmanager/progressview.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libs/progressmanager/progressmanager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libs/progressmanager/statusbarprogresswidget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/libs/progressmanager/statusprogressbar.cpp
)
SET(libthememanager_SRCS
......
......@@ -455,10 +455,26 @@ void ScanController::completeCollectionScan(SplashScreen* const splash, bool def
{
d->splash = splash;
createProgressDialog();
// we only need to count the files in advance
//if we show a progress percentage in progress dialog
d->needTotalFiles = (!d->splash || !CollectionScanner::databaseInitialScanDone());
// if we show a progress percentage in progress dialog
completeCollectionScanCore(!d->splash || !CollectionScanner::databaseInitialScanDone(), defer);
delete d->progressDialog;
d->progressDialog = 0;
// We do not delete Splashscreen here.
d->splash = 0;
}
void ScanController::completeCollectionScanInBackground(bool defer)
{
completeCollectionScanCore(true, defer);
}
void ScanController::completeCollectionScanCore(bool needTotalFiles, bool defer)
{
d->needTotalFiles = needTotalFiles;
{
QMutexLocker lock(&d->mutex);
d->needsCompleteScan = true;
......@@ -468,11 +484,7 @@ void ScanController::completeCollectionScan(SplashScreen* const splash, bool def
// loop is quit by signal
d->eventLoop->exec();
delete d->progressDialog;
d->progressDialog = 0;
// We do not delete Splashscreen here.
d->splash = 0;
d->needTotalFiles = false;
}
......
......@@ -89,6 +89,11 @@ public:
void completeCollectionScanDeferFiles(SplashScreen* const splash=0);
void allowToScanDeferredFiles();
/**
* Scan Whole collection without to display a progress dialog or to manage splashscreen, as for NewItemsFinder tool.
*/
void completeCollectionScanInBackground(bool defer);
/**
* Carries out a complete collection scan, at the same time updating
* the unique hash in the database and thumbnail database.
......@@ -249,6 +254,8 @@ private:
void createProgressDialog();
void setInitializationMessage();
void completeCollectionScanCore(bool needTotalFiles, bool defer);
virtual void moreSchemaUpdateSteps(int numberOfSteps);
virtual void schemaUpdateProgress(const QString& message, int numberOfSteps);
virtual void finishedSchemaUpdate(UpdateResult result);
......
......@@ -454,7 +454,7 @@ bool ProgressManager::addProgressItem(ProgressItem* const t, ProgressItem* const
else
{
KMessageBox::error(kapp->activeWindow(),
i18n("A tool named \"%1\" is already running....", t->label()));
i18n("A tool identified as \"%1\" is already running....", t->id()));
t->setComplete();
return false;
}
......
......@@ -6,6 +6,7 @@
* Date : 2007-01-24
* Description : a progress bar used to display action
* progress or a text in status bar.
* Progress events are dispatched to ProgressManager.
*
* Copyright (C) 2007-2013 by Gilles Caulier <caulier dot gilles at gmail dot com>
*
......
......@@ -6,6 +6,7 @@
* Date : 2007-01-24
* Description : a progress bar used to display action
* progress or a text in status bar.
* Progress events are dispatched to ProgressManager.
*
* Copyright (C) 2007-2013 by Gilles Caulier <caulier dot gilles at gmail dot com>
*
......
......@@ -92,6 +92,8 @@ DuplicatesFinder::~DuplicatesFinder()
void DuplicatesFinder::slotStart()
{
MaintenanceTool::slotStart();
setLabel(i18n("Find duplicates items"));
setThumbnail(KIcon("tools-wizard").pixmap(22));
ProgressManager::addProgressItem(this);
double thresh = d->similarity / 100.0;
......@@ -112,9 +114,6 @@ void DuplicatesFinder::slotStart()
connect(d->job, SIGNAL(processedAmount(KJob*,KJob::Unit,qulonglong)),
this, SLOT(slotDuplicatesSearchProcessedAmount(KJob*,KJob::Unit,qulonglong)));
setLabel(i18n("Find duplicates items"));
setThumbnail(KIcon("tools-wizard").pixmap(22));
}
void DuplicatesFinder::slotDuplicatesSearchTotalAmount(KJob*, KJob::Unit, qulonglong amount)
......
......@@ -114,6 +114,7 @@ FaceDetector::FaceDetector(const FaceScanSettings& settings, ProgressItem* const
: MaintenanceTool("FaceDetector", parent),
d(new Private)
{
setLabel(i18n("Updating faces database."));
ProgressManager::addProgressItem(this);
if (settings.task == FaceScanSettings::RetrainAll)
......@@ -281,7 +282,6 @@ void FaceDetector::slotStart()
d->total = qMax(1, d->total);
setUsesBusyIndicator(false);
setLabel(i18n("Updating faces database."));
setTotalItems(d->total);
slotContinueAlbumListing();
......
......@@ -76,6 +76,7 @@ FingerPrintsGenerator::FingerPrintsGenerator(const bool rebuildAll, ProgressItem
: MaintenanceTool("FingerPrintsGenerator", parent),
d(new Private)
{
setLabel(i18n("Finger-prints"));
ProgressManager::addProgressItem(this);
d->rebuildAll = rebuildAll;
......@@ -83,8 +84,6 @@ FingerPrintsGenerator::FingerPrintsGenerator(const bool rebuildAll, ProgressItem
connect(d->previewLoadThread, SIGNAL(signalImageLoaded(LoadingDescription,DImg)),
this, SLOT(slotGotImagePreview(LoadingDescription,DImg)));
setLabel(i18n("Finger-prints"));
}
FingerPrintsGenerator::~FingerPrintsGenerator()
......
......@@ -202,7 +202,7 @@ MaintenanceDlg::MaintenanceDlg(QWidget* const parent)
d->faceScannedHandling->addItem(i18n("Scan again and merge results"), FaceScanSettings::Merge);
d->faceScannedHandling->addItem(i18n("Clear unconfirmed results and rescan"), FaceScanSettings::Rescan);
d->expanderBox->insertItem(Private::FaceDetection, d->hbox3, SmallIcon("edit-image-face-detect"),
i18n("Face Detection"), "FaceDetection", false);
i18n("Detect and recognize Faces"), "FaceDetection", false);
d->expanderBox->setCheckBoxVisible(Private::FaceDetection, true);
d->expanderBox->insertStretch(Private::Stretch);
......
......@@ -45,6 +45,7 @@
#include "metadatasynchronizer.h"
#include "facedetector.h"
#include "knotificationwrapper.h"
#include "progressmanager.h"
namespace Digikam
{
......@@ -55,7 +56,13 @@ public:
Private()
{
running = false;
running = false;
newItemsFinder = 0;
thumbsGenerator = 0;
fingerPrintsGenerator = 0;
duplicatesFinder = 0;
metadataSynchronizer = 0;
faceDetector = 0;
}
bool running;
......@@ -63,11 +70,23 @@ public:
QTime duration;
MaintenanceSettings settings;
NewItemsFinder* newItemsFinder;
ThumbsGenerator* thumbsGenerator;
FingerPrintsGenerator* fingerPrintsGenerator;
DuplicatesFinder* duplicatesFinder;
MetadataSynchronizer* metadataSynchronizer;
FaceDetector* faceDetector;
};
MaintenanceMngr::MaintenanceMngr(QObject* const parent)
: QObject(parent), d(new Private)
{
connect(ProgressManager::instance(), SIGNAL(progressItemCompleted(ProgressItem*)),
this, SLOT(slotToolCompleted(ProgressItem*)));
connect(ProgressManager::instance(), SIGNAL(progressItemCanceled(ProgressItem*)),
this, SLOT(slotToolCanceled(ProgressItem*)));
}
MaintenanceMngr::~MaintenanceMngr()
......@@ -95,138 +114,159 @@ void MaintenanceMngr::setSettings(const MaintenanceSettings& settings)
kDebug() << "settings.faceScannedHandling : " << d->settings.faceSettings.alreadyScannedHandling;
d->duration.start();
slotStage1();
stage1();
}
void MaintenanceMngr::slotStage1()
void MaintenanceMngr::slotToolCompleted(ProgressItem* tool)
{
if (d->settings.newItems)
{
NewItemsFinder* const tool = new NewItemsFinder();
tool->setNotificationEnabled(false);
// At each stage, relevant tool instance is set to zero to prevent redondant call to this slot
// from ProgressManager. This will disable multiple triggering in this method.
// There is no memory leak. Each tool instance are delete later by ProgressManager.
connect(tool, SIGNAL(signalComplete()),
this, SLOT(slotStage2()));
if (tool == dynamic_cast<ProgressItem*>(d->newItemsFinder))
{
d->newItemsFinder = 0;
stage2();
}
else if (tool == dynamic_cast<ProgressItem*>(d->thumbsGenerator))
{
d->thumbsGenerator = 0;
stage3();
}
else if (tool == dynamic_cast<ProgressItem*>(d->fingerPrintsGenerator))
{
d->fingerPrintsGenerator = 0;
stage4();
}
else if (tool == dynamic_cast<ProgressItem*>(d->duplicatesFinder))
{
d->duplicatesFinder = 0;
stage5();
}
else if (tool == dynamic_cast<ProgressItem*>(d->metadataSynchronizer))
{
d->metadataSynchronizer = 0;
stage6();
}
else if (tool == dynamic_cast<ProgressItem*>(d->faceDetector))
{
d->faceDetector = 0;
done();
}
}
connect(tool, SIGNAL(progressItemCanceled(QString)),
this, SLOT(slotCancel()));
void MaintenanceMngr::slotToolCanceled(ProgressItem* tool)
{
if (tool == dynamic_cast<ProgressItem*>(d->newItemsFinder) ||
tool == dynamic_cast<ProgressItem*>(d->thumbsGenerator) ||
tool == dynamic_cast<ProgressItem*>(d->fingerPrintsGenerator) ||
tool == dynamic_cast<ProgressItem*>(d->duplicatesFinder) ||
tool == dynamic_cast<ProgressItem*>(d->metadataSynchronizer) ||
tool == dynamic_cast<ProgressItem*>(d->faceDetector))
{
cancel();
}
}
tool->start();
void MaintenanceMngr::stage1()
{
kDebug() << "stage1";
if (d->settings.newItems)
{
d->newItemsFinder = new NewItemsFinder();
d->newItemsFinder->setNotificationEnabled(false);
d->newItemsFinder->start();
}
else
{
slotStage2();
stage2();
}
}
void MaintenanceMngr::slotStage2()
void MaintenanceMngr::stage2()
{
kDebug() << "stage2";
if (d->settings.thumbnails)
{
bool rebuildAll = d->settings.scanThumbs == false;
ThumbsGenerator* const tool = new ThumbsGenerator(rebuildAll);
tool->setNotificationEnabled(false);
connect(tool, SIGNAL(signalComplete()),
this, SLOT(slotStage3()));
connect(tool, SIGNAL(progressItemCanceled(QString)),
this, SLOT(slotCancel()));
tool->start();
bool rebuildAll = (d->settings.scanThumbs == false);
d->thumbsGenerator = new ThumbsGenerator(rebuildAll);
d->thumbsGenerator->setNotificationEnabled(false);
d->thumbsGenerator->start();
}
else
{
slotStage3();
stage3();
}
}
void MaintenanceMngr::slotStage3()
void MaintenanceMngr::stage3()
{
kDebug() << "stage3";
if (d->settings.fingerPrints)
{
bool rebuildAll = d->settings.scanFingerPrints == false;
FingerPrintsGenerator* const tool = new FingerPrintsGenerator(rebuildAll);
tool->setNotificationEnabled(false);
connect(tool, SIGNAL(signalComplete()),
this, SLOT(slotStage4()));
connect(tool, SIGNAL(progressItemCanceled(QString)),
this, SLOT(slotCancel()));
tool->start();
bool rebuildAll = (d->settings.scanFingerPrints == false);
d->fingerPrintsGenerator = new FingerPrintsGenerator(rebuildAll);
d->fingerPrintsGenerator->setNotificationEnabled(false);
d->fingerPrintsGenerator->start();
}
else
{
slotStage4();
stage4();
}
}
void MaintenanceMngr::slotStage4()
void MaintenanceMngr::stage4()
{
kDebug() << "stage4";
if (d->settings.duplicates)
{
DuplicatesFinder* const tool = new DuplicatesFinder(d->settings.similarity);
tool->setNotificationEnabled(false);
connect(tool, SIGNAL(signalComplete()),
this, SLOT(slotStage5()));
connect(tool, SIGNAL(progressItemCanceled(QString)),
this, SLOT(slotCancel()));
tool->start();
d->duplicatesFinder = new DuplicatesFinder(d->settings.similarity);
d->duplicatesFinder->setNotificationEnabled(false);
d->duplicatesFinder->start();
}
else
{
slotStage5();
stage5();
}
}
void MaintenanceMngr::slotStage5()
void MaintenanceMngr::stage5()
{
kDebug() << "stage5";
if (d->settings.metadata)
{
MetadataSynchronizer* const tool = new MetadataSynchronizer(MetadataSynchronizer::WriteFromDatabaseToFile);
tool->setNotificationEnabled(false);
connect(tool, SIGNAL(signalComplete()),
this, SLOT(slotStage6()));
connect(tool, SIGNAL(progressItemCanceled(QString)),
this, SLOT(slotCancel()));
tool->start();
d->metadataSynchronizer = new MetadataSynchronizer(MetadataSynchronizer::WriteFromDatabaseToFile);
d->metadataSynchronizer->setNotificationEnabled(false);
d->metadataSynchronizer->start();
}
else
{
slotStage6();
stage6();
}
}
void MaintenanceMngr::slotStage6()
void MaintenanceMngr::stage6()
{
kDebug() << "stage6";
if (d->settings.faceDetection)
{
FaceDetector* const tool = new FaceDetector(d->settings.faceSettings);
tool->setNotificationEnabled(false);
connect(tool, SIGNAL(signalComplete()),
this, SLOT(slotDone()));
connect(tool, SIGNAL(progressItemCanceled(QString)),
this, SLOT(slotCancel()));
tool->start();
d->faceDetector = new FaceDetector(d->settings.faceSettings);
d->faceDetector->setNotificationEnabled(false);
d->faceDetector->start();
}
else
{
slotDone();
done();
}
}
void MaintenanceMngr::slotDone()
void MaintenanceMngr::done()
{
d->running = false;
QTime now, t = now.addMSecs(d->duration.elapsed());
......@@ -238,7 +278,7 @@ void MaintenanceMngr::slotDone()
emit signalComplete();
}
void MaintenanceMngr::slotCancel()
void MaintenanceMngr::cancel()
{
d->running = false;
emit signalComplete();
......
......@@ -32,6 +32,7 @@ namespace Digikam
{
class MaintenanceSettings;
class ProgressItem;
class MaintenanceMngr : public QObject
{
......@@ -53,16 +54,21 @@ Q_SIGNALS:
private Q_SLOTS:
void slotStage1(); // New items
void slotStage2(); // Thumbnails
void slotStage3(); // Finger-prints
void slotStage4(); // Duplicates
void slotStage5(); // Metadata
void slotStage6(); // Face detection
void slotToolCompleted(ProgressItem*);
void slotToolCanceled(ProgressItem*);
void slotDone();
void slotCancel();
private:
void stage1(); // New items
void stage2(); // Thumbnails
void stage3(); // Finger-prints
void stage4(); // Duplicates
void stage5(); // Metadata
void stage6(); // Face detection
void done(); // Called when all scheduled tools are done.
void cancel(); // Called when a tol is canceled.
private:
class Private;
......
......@@ -27,6 +27,7 @@
// Qt includes
#include <QTime>
#include <QTimer>
// KDE includes
......@@ -74,7 +75,8 @@ void MaintenanceTool::setNotificationEnabled(bool b)
void MaintenanceTool::start()
{
slotStart();
// We delay start to be sure that eventloop connect signals and slots in top level.
QTimer::singleShot(0, this, SLOT(slotStart()));
}
void MaintenanceTool::slotStart()
......
......@@ -59,22 +59,28 @@ public:
NewItemsFinder::NewItemsFinder(const FinderMode mode, const QStringList& foldersToScan, ProgressItem* const parent)
: MaintenanceTool("NewItemsFinder", parent), d(new Private)
{
setLabel(i18n("Find new items"));
setThumbnail(KIcon("view-refresh").pixmap(22));
ProgressManager::addProgressItem(this);
d->mode = mode;
// Common conections to ScanController
connect(ScanController::instance(), SIGNAL(collectionScanStarted(QString)),
this, SLOT(slotScanStarted(QString)));
connect(ScanController::instance(), SIGNAL(totalFilesToScan(int)),
this, SLOT(slotTotalFilesToScan(int)));
connect(ScanController::instance(), SIGNAL(filesScanned(int)),
this, SLOT(slotFilesScanned(int)));
connect(ScanController::instance(), SIGNAL(completeScanDone()),
this, SLOT(slotDone()));
// Connection and rules for ScheduleCollectionScan mode.
connect(ScanController::instance(), SIGNAL(partialScanDone(QString)),
this, SLOT(slotPartialScanDone(QString)));
d->mode = mode;
d->foldersToScan = foldersToScan;
d->foldersToScan.sort();
}
......@@ -93,20 +99,26 @@ void NewItemsFinder::slotStart()
case ScanDeferredFiles:
{
kDebug() << "scan mode: ScanDeferredFiles";
connect(ScanController::instance(), SIGNAL(completeScanDone()),
this, SLOT(slotDone()));
ScanController::instance()->allowToScanDeferredFiles();
ScanController::instance()->completeCollectionScanInBackground(true);
break;
}
case CompleteCollectionScan:
{
kDebug() << "scan mode: CompleteCollectionScan";
ScanController::instance()->completeCollectionScanInBackground(false);
QApplication::setOverrideCursor(Qt::WaitCursor);
ScanController::instance()->completeCollectionScanDeferFiles();
QApplication::restoreOverrideCursor();
connect(ScanController::instance(), SIGNAL(completeScanDone()),
this, SLOT(slotDone()));
ScanController::instance()->allowToScanDeferredFiles();
ScanController::instance()->completeCollectionScanInBackground(true);
break;
}
......@@ -114,7 +126,7 @@ void NewItemsFinder::slotStart()
{
kDebug() << "scan mode: ScheduleCollectionScan :: " << d->foldersToScan;
d->foldersScanned.clear();
foreach(const QString& folder, d->foldersToScan)
ScanController::instance()->scheduleCollectionScan(folder);
......@@ -125,15 +137,13 @@ void NewItemsFinder::slotStart()
void NewItemsFinder::slotScanStarted(const QString& info)
{
ProgressManager::addProgressItem(this);
setLabel(i18n("Find new items"));
kDebug() << info;
setStatus(info);
setThumbnail(KIcon("view-refresh").pixmap(22));
}
void NewItemsFinder::slotTotalFilesToScan(int t)
{
//kDebug() << "total scan value : " << t;
kDebug() << "total scan value : " << t;
setTotalItems(t);
}