Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AnalyzeView: Fix GeoTagWorker Threading #11813

Merged
merged 1 commit into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/AnalyzeView/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ if(NOT exiv2_FOUND AND NOT LibExiv2_FOUND)
GIT_REPOSITORY https://github.com/Exiv2/exiv2.git
GIT_TAG v0.28.3
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
set(EXIV2_ENABLE_XMP OFF CACHE INTERNAL "" FORCE)
set(EXIV2_ENABLE_EXTERNAL_XMP OFF CACHE INTERNAL "" FORCE)
Expand Down
16 changes: 8 additions & 8 deletions src/AnalyzeView/ExifParser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ void init()
::atexit(Exiv2::XmpParser::terminate);
}

double readTime(const QByteArray &buf)
QDateTime readTime(const QByteArray &buf)
{
try {
// Convert QByteArray to std::string for Exiv2
Expand All @@ -36,7 +36,7 @@ double readTime(const QByteArray &buf)
const Exiv2::ExifData &exifData = image->exifData();
if (exifData.empty()) {
qCWarning(ExifParserLog) << "No EXIF data found in the image.";
return -1.0;
return QDateTime();
}

// Read DateTimeOriginal
Expand All @@ -45,7 +45,7 @@ double readTime(const QByteArray &buf)
const Exiv2::ExifData::const_iterator pos = exifData.findKey(key);
if (pos == exifData.end()) {
qCWarning(ExifParserLog) << "No DateTimeOriginal found.";
return -1.0;
return QDateTime();
}

const std::string dateTimeOriginal = pos->toString();
Expand All @@ -54,30 +54,30 @@ double readTime(const QByteArray &buf)

if (createDateList.size() < 2) {
qCWarning(ExifParserLog) << "Invalid date/time format: " << createDateList;
return -1.0;
return QDateTime();
}

const QStringList dateList = createDateList[0].split(':');
const QStringList timeList = createDateList[1].split(':');

if ((dateList.size() < 3) || (timeList.size() < 3)) {
qCWarning(ExifParserLog) << "Could not parse creation date/time: " << dateList << " " << timeList;
return -1.0;
return QDateTime();
}

const QDate date(dateList[0].toInt(), dateList[1].toInt(), dateList[2].toInt());
const QTime time(timeList[0].toInt(), timeList[1].toInt(), timeList[2].toInt());

const QDateTime tagTime(date, time);

return (tagTime.toMSecsSinceEpoch() / 1000.0);
return tagTime;
} catch (const Exiv2::Error &e) {
qCWarning(ExifParserLog) << "Error reading EXIF data:" << e.what();
return -1.0;
return QDateTime();
}
}

bool write(QByteArray &buf, const GeoTagWorker::cameraFeedbackPacket &geotag)
bool write(QByteArray &buf, const GeoTagWorker::CameraFeedbackPacket &geotag)
{
try {
// Convert QByteArray to std::string for Exiv2
Expand Down
7 changes: 4 additions & 3 deletions src/AnalyzeView/ExifParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ class QByteArray;

Q_DECLARE_LOGGING_CATEGORY(ExifParserLog)

namespace ExifParser {
namespace ExifParser
{
void init();
double readTime(const QByteArray &buf);
bool write(QByteArray &buf, const GeoTagWorker::cameraFeedbackPacket &geotag);
QDateTime readTime(const QByteArray &buf);
bool write(QByteArray &buf, const GeoTagWorker::CameraFeedbackPacket &geotag);
}
196 changes: 124 additions & 72 deletions src/AnalyzeView/GeoTagController.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,120 +8,172 @@
****************************************************************************/

#include "GeoTagController.h"
#include "GeoTagWorker.h"
#include "QGCLoggingCategory.h"

#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QThread>
#include <QtCore/QUrl>

QGC_LOGGING_CATEGORY(GeoTagControllerLog, "qgc.analyzeview.geotagcontroller")

GeoTagController::GeoTagController()
: _progress(0)
, _inProgress(false)
GeoTagController::GeoTagController(QObject *parent)
: QObject(parent)
, _worker(new GeoTagWorker())
, _workerThread(new QThread(this))
{
connect(&_worker, &GeoTagWorker::progressChanged, this, &GeoTagController::_workerProgressChanged);
connect(&_worker, &GeoTagWorker::error, this, &GeoTagController::_workerError);
connect(&_worker, &GeoTagWorker::started, this, &GeoTagController::inProgressChanged);
connect(&_worker, &GeoTagWorker::finished, this, &GeoTagController::inProgressChanged);
// qCDebug(GeoTagControllerLog) << Q_FUNC_INFO << this;

_worker->moveToThread(_workerThread);

(void) connect(_worker, &GeoTagWorker::progressChanged, this, &GeoTagController::_workerProgressChanged);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there something new going on with connect which required the (void) now?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a pattern carried over from my embedded work. I like to denote that I am intentionally ignoring return values rather than being unaware if it has one or not, helps with collaborating so others know that too

(void) connect(_worker, &GeoTagWorker::error, this, &GeoTagController::_workerError);
(void) connect(_workerThread, &QThread::started, _worker, &GeoTagWorker::process);
(void) connect(_workerThread, &QThread::started, this, &GeoTagController::inProgressChanged);
(void) connect(_workerThread, &QThread::finished, this, &GeoTagController::inProgressChanged);
}

GeoTagController::~GeoTagController()
{
cancelTagging();
delete _worker;

// qCDebug(GeoTagControllerLog) << Q_FUNC_INFO << this;
}

void GeoTagController::setLogFile(QString filename)
void GeoTagController::cancelTagging()
{
filename = QUrl(filename).toLocalFile();
if (!filename.isEmpty()) {
_worker.setLogFile(filename);
emit logFileChanged(filename);
}
(void) QMetaObject::invokeMethod(_worker, "cancelTagging", Qt::AutoConnection);
(void) QMetaObject::invokeMethod(_workerThread, "quit", Qt::AutoConnection);

_workerThread->wait();
}

void GeoTagController::setImageDirectory(QString dir)
QString GeoTagController::logFile() const
{
dir = QUrl(dir).toLocalFile();
if (!dir.isEmpty()) {
_worker.setImageDirectory(dir);
emit imageDirectoryChanged(dir);
if(_worker.saveDirectory() == "") {
QDir saveDirectory = QDir(_worker.imageDirectory() + kTagged);
if(saveDirectory.exists()) {
_setErrorMessage(tr("Images have alreay been tagged. Existing images will be removed."));
return;
}
}
return _worker->logFile();
}

QString GeoTagController::imageDirectory() const
{
return _worker->imageDirectory();
}

QString GeoTagController::saveDirectory() const
{
return _worker->saveDirectory();
}

bool GeoTagController::inProgress() const
{
return _workerThread->isRunning();
}

void GeoTagController::setLogFile(const QString &filename)
{
if (filename.isEmpty()) {
_setErrorMessage(tr("Empty Filename."));
return;
}

const QFileInfo logFileInfo = QFileInfo(filename);
if (!logFileInfo.exists() || !logFileInfo.isFile()) {
_setErrorMessage(tr("Invalid Filename."));
return;
}
_errorMessage.clear();
emit errorMessageChanged(_errorMessage);

_worker->setLogFile(filename);
emit logFileChanged(filename);

_setErrorMessage(QString());
}

void GeoTagController::setSaveDirectory(QString dir)
void GeoTagController::setImageDirectory(const QString &dir)
{
dir = QUrl(dir).toLocalFile();
if (!dir.isEmpty()) {
_worker.setSaveDirectory(dir);
emit saveDirectoryChanged(dir);
//-- Check and see if there are images already there
QDir saveDirectory = QDir(_worker.saveDirectory());
saveDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable);
QStringList nameFilters;
nameFilters << "*.jpg" << "*.JPG";
saveDirectory.setNameFilters(nameFilters);
QStringList imageList = saveDirectory.entryList();
if(!imageList.isEmpty()) {
_setErrorMessage(tr("The save folder already contains images."));
if (dir.isEmpty()) {
_setErrorMessage(tr("Invalid Directory."));
return;
}

const QFileInfo imageDirectoryInfo = QFileInfo(dir);
if (!imageDirectoryInfo.exists() || !imageDirectoryInfo.isDir()) {
_setErrorMessage(tr("Invalid Directory."));
return;
}

_worker->setImageDirectory(dir);
emit imageDirectoryChanged(dir);

if (_worker->saveDirectory().isEmpty()) {
const QDir saveDirectory = QDir(_worker->imageDirectory() + kTagged);
if (saveDirectory.exists()) {
_setErrorMessage(tr("Images have already been tagged. Existing images will be removed."));
return;
}
}
_errorMessage.clear();
emit errorMessageChanged(_errorMessage);

_setErrorMessage(QString());
}

void GeoTagController::setSaveDirectory(const QString &dir)
{
if (dir.isEmpty()) {
_setErrorMessage(tr("Invalid Directory."));
return;
}

const QFileInfo saveDirectoryInfo = QFileInfo(dir);
if (!saveDirectoryInfo.exists() || !saveDirectoryInfo.isDir()) {
_setErrorMessage(tr("Invalid Directory."));
return;
}

_worker->setSaveDirectory(dir);
emit saveDirectoryChanged(dir);

QDir saveDirectory = QDir(_worker->saveDirectory());
saveDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable);

QStringList nameFilters;
nameFilters << "*.jpg" << "*.JPG";
saveDirectory.setNameFilters(nameFilters);

const QStringList imageList = saveDirectory.entryList();
if (!imageList.isEmpty()) {
_setErrorMessage(tr("The save folder already contains images."));
return;
}

_setErrorMessage(QString());
}

void GeoTagController::startTagging()
{
_errorMessage.clear();
emit errorMessageChanged(_errorMessage);
QDir imageDirectory = QDir(_worker.imageDirectory());
if(!imageDirectory.exists()) {
_setErrorMessage(QString());

const QDir imageDirectory = QDir(_worker->imageDirectory());
if (!imageDirectory.exists()) {
_setErrorMessage(tr("Cannot find the image directory."));
return;
}
if(_worker.saveDirectory() == "") {
QDir oldTaggedFolder = QDir(_worker.imageDirectory() + kTagged);
if(oldTaggedFolder.exists()) {

if (_worker->saveDirectory().isEmpty()) {
QDir oldTaggedFolder = QDir(_worker->imageDirectory() + kTagged);
if (oldTaggedFolder.exists()) {
oldTaggedFolder.removeRecursively();
if(!imageDirectory.mkdir(_worker.imageDirectory() + kTagged)) {
if (!imageDirectory.mkdir(_worker->imageDirectory() + kTagged)) {
_setErrorMessage(tr("Couldn't replace the previously tagged images"));
return;
}
}
} else {
QDir saveDirectory = QDir(_worker.saveDirectory());
if(!saveDirectory.exists()) {
const QDir saveDirectory = QDir(_worker->saveDirectory());
if (!saveDirectory.exists()) {
_setErrorMessage(tr("Cannot find the save directory."));
return;
}
}
_worker.start();
}

void GeoTagController::_workerProgressChanged(double progress)
{
_progress = progress;
emit progressChanged(progress);
}

void GeoTagController::_workerError(QString errorMessage)
{
_errorMessage = errorMessage;
emit errorMessageChanged(errorMessage);
}


void GeoTagController::_setErrorMessage(const QString& error)
{
_errorMessage = error;
emit errorMessageChanged(error);
(void) QMetaObject::invokeMethod(_workerThread, "start", Qt::AutoConnection);
}
Loading
Loading