From a79d319dbd30b9114481643639dd3adbecabd64b Mon Sep 17 00:00:00 2001 From: birkeh Date: Mon, 4 Mar 2019 00:07:36 +0100 Subject: [PATCH] update copy function --- ccopier.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++++ ccopier.h | 49 +++++++++++++++++ cexif.cpp | 5 ++ cexif.h | 7 +++ cimportdialog.cpp | 13 +++-- cimportdialog.ui | 11 +++- cmainwindow.cpp | 2 +- common.cpp | 22 +++++++- common.h | 6 ++- cpicture.cpp | 34 ++++++++++-- cpicture.h | 16 ++++++ cpicturelibrary.cpp | 1 + pictureLibrary.pro | 8 +-- 13 files changed, 285 insertions(+), 18 deletions(-) create mode 100644 ccopier.cpp create mode 100644 ccopier.h diff --git a/ccopier.cpp b/ccopier.cpp new file mode 100644 index 0000000..9eb6bd5 --- /dev/null +++ b/ccopier.cpp @@ -0,0 +1,129 @@ +#include "ccopier.h" + + +static QString toString(HRESULT hr) +{ + _com_error err{hr}; + return QStringLiteral("Error 0x%1: %2").arg((quint32)hr, 8, 16, QLatin1Char('0')).arg(err.ErrorMessage()); +} + +static QString getLastErrorMsg() +{ + return toString(HRESULT_FROM_WIN32(GetLastError())); +} + +cCopier::cCopier(const QString& src, const QString& dst, QObject* parent) : + QObject(parent), + m_src(src), + m_dst(dst) +{ +} + +cCopier::~cCopier() +{ + stop(); +} + +void cCopier::stop() +{ + resume(); + m_stop = TRUE; +} + +void cCopier::pause() +{ + m_pause = true; +} + +void cCopier::resume() +{ + if(m_pause) + m_pauseWait.notify_one(); + m_pause = false; +} + +void cCopier::newStatus(ULONGLONG part, ULONGLONG whole) +{ + if(part != m_lastPart || whole != m_lastWhole) + { + m_lastPart = part; + m_lastWhole = whole; + emit newStatus(static_cast(part*100/whole)); + } +} + +#if _WIN32_WINNT >= _WIN32_WINNT_WIN8 +void cCopier::copy() +{ + m_lastPart = m_lastWhole = {}; + m_stop = FALSE; + m_pause = false; + QtConcurrent::run([this] + { + COPYFILE2_EXTENDED_PARAMETERS params + { + sizeof(COPYFILE2_EXTENDED_PARAMETERS), 0, &m_stop, + Copier::copyProgress2, this + }; + auto rc = CopyFile2((PCWSTR)m_src.utf16(), (PCWSTR)m_dst.utf16(), ¶ms); + if(!SUCCEEDED(rc)) + emit newStatus(toString(rc)); + emit finished(); + }); +} + +COPYFILE2_MESSAGE_ACTION CALLBACK cCopier::copyProgress2(const COPYFILE2_MESSAGE *message, PVOID context) +{ + COPYFILE2_MESSAGE_ACTION action = COPYFILE2_PROGRESS_CONTINUE; + auto self = static_cast(context); + + if(message->Type == COPYFILE2_CALLBACK_CHUNK_FINISHED) + { + auto& info = message->Info.ChunkFinished; + self->newStatus(info.uliTotalBytesTransferred.QuadPart, info.uliTotalFileSize.QuadPart); + } + else if (message->Type == COPYFILE2_CALLBACK_ERROR) + { + auto& info = message->Info.Error; + self->newStatus(info.uliTotalBytesTransferred.QuadPart, info.uliTotalFileSize.QuadPart); + emit self->newStatus(toString(info.hrFailure)); + action = COPYFILE2_PROGRESS_CANCEL; + } + + if(self->m_pause) + { + QMutexLocker lock{&self->m_pauseMutex}; + self->m_pauseWait.wait(&self->m_pauseMutex); + } + return action; +} +#else +void cCopier::copy() +{ + m_lastPart = m_lastWhole = {}; + m_stop = FALSE; + m_pause = false; + QtConcurrent::run([this] + { + auto rc = CopyFileExW((LPCWSTR)m_src.utf16(), (LPCWSTR)m_dst.utf16(), ©Progress, this, &m_stop, 0); + if(!rc) + { + qDebug() << getLastErrorMsg(); + emit newStatus(-1); + } + emit finished(); + }); +} + +DWORD CALLBACK cCopier::copyProgress(const LARGE_INTEGER totalSize, const LARGE_INTEGER totalTransferred, LARGE_INTEGER, LARGE_INTEGER, DWORD, DWORD, HANDLE, HANDLE, LPVOID data) +{ + auto self = static_cast(data); + self->newStatus(totalTransferred.QuadPart, totalSize.QuadPart); + if(self->m_pause) + { + QMutexLocker lock{&self->m_pauseMutex}; + self->m_pauseWait.wait(&self->m_pauseMutex); + } + return PROGRESS_CONTINUE; +} +#endif diff --git a/ccopier.h b/ccopier.h new file mode 100644 index 0000000..bf91ead --- /dev/null +++ b/ccopier.h @@ -0,0 +1,49 @@ +#ifndef CCOPIER_H +#define CCOPIER_H + + +#include + +#include +#include +#include + + +class cCopier : public QObject +{ + Q_OBJECT + + BOOL m_stop; + QMutex m_pauseMutex; + QAtomicInt m_pause; + QWaitCondition m_pauseWait; + + QString m_src; + QString m_dst; + ULONGLONG m_lastPart; + ULONGLONG m_lastWhole; + + void newStatus(ULONGLONG part, ULONGLONG whole); + +#if _WIN32_WINNT >= _WIN32_WINNT_WIN8 + static COPYFILE2_MESSAGE_ACTION CALLBACK copyProgress2(const COPYFILE2_MESSAGE *message, PVOID context); +#else + static DWORD CALLBACK copyProgress(LARGE_INTEGER totalSize, LARGE_INTEGER totalTransferred, LARGE_INTEGER streamSize, LARGE_INTEGER streamTransferred, DWORD streamNo, DWORD callbackReason, HANDLE src, HANDLE dst, LPVOID data); +#endif + +public: + explicit cCopier(const QString& src, const QString& dst, QObject* parent = nullptr); + ~cCopier() override; + +signals: + void newStatus(int percent); + void finished(); + +public slots: + void copy(); + void stop(); + void pause(); + void resume(); +}; + +#endif // CCOPIER_H diff --git a/cexif.cpp b/cexif.cpp index 7ad1c5d..07323e9 100644 --- a/cexif.cpp +++ b/cexif.cpp @@ -446,6 +446,11 @@ QString cEXIF::gps() return(szGPS); } +QString cEXIF::duration() +{ + return(getXMPTag("Xmp.video.Duration").toString()); +} + QString cEXIF::fileName() { return(m_szFileName); diff --git a/cexif.h b/cexif.h index 8af46d5..99ede9d 100644 --- a/cexif.h +++ b/cexif.h @@ -757,6 +757,13 @@ public: /*! \brief + \fn duration + \return QString + */ + QString duration(); + /*! + \brief + \fn fileName \return QString */ diff --git a/cimportdialog.cpp b/cimportdialog.cpp index 27d9f5b..64944d4 100644 --- a/cimportdialog.cpp +++ b/cimportdialog.cpp @@ -55,6 +55,7 @@ void cImportDialog::initUI() { ui->setupUi(this); ui->m_lpProgress->setVisible(false); + ui->m_lpTotalProgress->setVisible(false); ui->m_lpImport->setEnabled(false); m_lpFolderViewModel = new QStandardItemModel; @@ -228,7 +229,9 @@ void cImportDialog::onImport() QFile file; ui->m_lpProgress->setVisible(true); - ui->m_lpProgress->setRange(0, ui->m_lpThumbnailView->selectionModel()->selectedIndexes().count()); + ui->m_lpProgress->setRange(0, 100); + ui->m_lpTotalProgress->setVisible(true); + ui->m_lpTotalProgress->setRange(0, ui->m_lpThumbnailView->selectionModel()->selectedIndexes().count()); for(int x = 0;x < ui->m_lpThumbnailView->selectionModel()->selectedIndexes().count();x++) { @@ -255,11 +258,10 @@ void cImportDialog::onImport() szDest.append(szDestPath); szDest.append(lpPicture->fileName()); -// if(copyFile(szSource, szDest, ui->m_lpMove->isChecked())) - if(copyFile(szSource, szDest, false)) +// if(copyFile(ui->m_lpProgress, szSource, szDest, ui->m_lpMove->isChecked())) + if(copyFile(ui->m_lpProgress, szSource, szDest, false)) { - - ui->m_lpProgress->setValue(x); + ui->m_lpTotalProgress->setValue(x+1); qApp->processEvents(); lpPicture->setFilePath(szDestPath.left(szDestPath.length()-1).replace("\\", "/")); @@ -271,6 +273,7 @@ void cImportDialog::onImport() } ui->m_lpStatusText->setText(""); ui->m_lpProgress->setVisible(false); + ui->m_lpTotalProgress->setVisible(false); m_bHasImported = true; } diff --git a/cimportdialog.ui b/cimportdialog.ui index 4ee1723..0bee34b 100644 --- a/cimportdialog.ui +++ b/cimportdialog.ui @@ -15,7 +15,7 @@ - + @@ -222,7 +222,7 @@ 0 0 69 - 342 + 315 @@ -263,6 +263,13 @@ + + + + 0 + + + diff --git a/cmainwindow.cpp b/cmainwindow.cpp index f04e3e8..4890338 100644 --- a/cmainwindow.cpp +++ b/cmainwindow.cpp @@ -463,7 +463,7 @@ void cMainWindow::onChangeDate() QString szPath = QString::number(lpPicture->dateTime().date().year()) + "/" + lpPicture->dateTime().date().toString("yyyy-MM-dd"); m_lpThumbnailSortFilterProxyModel->setData(index, QVariant::fromValue(szPath), Qt::UserRole+2); - if(copyFile(m_pictureLibrary.rootPath() + QDir::separator() + lpPicture->filePath() + QDir::separator() + lpPicture->fileName(), + if(copyFile(0, m_pictureLibrary.rootPath() + QDir::separator() + lpPicture->filePath() + QDir::separator() + lpPicture->fileName(), m_pictureLibrary.rootPath() + QDir::separator() + szPath + QDir::separator() + lpPicture->fileName(), true)) { lpPicture->setFilePath(szPath); diff --git a/common.cpp b/common.cpp index 4f035c6..8a3065a 100644 --- a/common.cpp +++ b/common.cpp @@ -10,8 +10,11 @@ #include "common.h" +#include "ccopier.h" + #include #include +#include QImage blob2Image(const QByteArray& ba) @@ -86,7 +89,8 @@ QStandardItem* insertPath(QString szPath, QStandardItem* lpRootItem) return(lpCurRoot); } -bool copyFile(const QString& szSource, const QString& szDest, bool bDelete) +//bool copyFile(cImportDialog* lpImportDialog, const QString& szSource, const QString& szDest, bool bDelete) +bool copyFile(QProgressBar* lpProgressBar, const QString& szSource, const QString& szDest, bool bDelete) { QString szDestPath; QString szDestFilePath = szDest; @@ -102,7 +106,21 @@ bool copyFile(const QString& szSource, const QString& szDest, bool bDelete) if(file.exists(szDestFilePath)) file.remove(szDestFilePath); - file.copy(szSource, szDestFilePath); + cCopier* lpCopier = new cCopier(szSource, szDest); + + if(lpProgressBar) + QObject::connect(lpCopier, SIGNAL(newStatus(int)), lpProgressBar, SLOT(setValue(int))); +// connect(copier, SIGNAL(newStatus(QString)), this, SLOT(newStatus(QString))); +// connect(copier, SIGNAL(finished()), SIGNAL(copyFinished())); +// connect(copier, SIGNAL(finished()), copier, SLOT(deleteLater())); +// connect(this, SIGNAL(stopCopy()), copier, SLOT(stop())); + + QEventLoop loop; + + lpCopier->copy(); + + QObject::connect(lpCopier, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); if(bDelete) file.remove(szSource); diff --git a/common.h b/common.h index 916bad9..cc7b565 100644 --- a/common.h +++ b/common.h @@ -7,11 +7,15 @@ #define COMMON_H +#include "cimportdialog.h" + #include #include #include #include +#include + #include @@ -63,6 +67,6 @@ QStandardItem* insertPath(QString szPath, QStandardItem* lpRootItem); \param bDelete \return bool */ -bool copyFile(const QString& szSource, const QString& szDest, bool bDelete = false); +bool copyFile(QProgressBar* lpProgressBar, const QString& szSource, const QString& szDest, bool bDelete = false); #endif // COMMON_H diff --git a/cpicture.cpp b/cpicture.cpp index 989fc05..0ec2b39 100644 --- a/cpicture.cpp +++ b/cpicture.cpp @@ -38,7 +38,8 @@ cPicture::cPicture(qint32 iID, QObject *parent) : m_exifVersion(""), m_whiteBalance(0), m_focalLength35(0.0), - m_gps("") + m_gps(""), + m_duration(0) { } @@ -78,6 +79,7 @@ bool cPicture::fromFile(const QString& szFileName) m_whiteBalance = exif.whiteBalance(); m_focalLength35 = exif.focalLength35(); m_gps = exif.gps(); + m_duration = exif.duration().toLongLong(); return(true); } @@ -97,12 +99,12 @@ bool cPicture::toDB() } if(!query.next()) - query.prepare("INSERT INTO picture (fileName, filePath, fileSize, mimeType, imageWidth, imageHeight, imageOrientation, cameraMake, cameraModel, dateTime, fNumber, iso, flashID, focalLength, lensMake, lensModel, exposureTime, exposureBias, exifVersion, dateTimeOriginal, dateTimeDigitized, whiteBalance, focalLength35, gps, thumbnail) VALUES (:fileName, :filePath, :fileSize, :mimeType, :imageWidth, :imageHeight, :imageOrientation, :cameraMake, :cameraModel, :dateTime, :fNumber, :iso, :flashID, :focalLength, :lensMake, :lensModel, :exposureTime, :exposureBias, :exifVersion, :dateTimeOriginal, :dateTimeDigitized, :whiteBalance, :focalLength35, :gps, :thumbnail);"); + query.prepare("INSERT INTO picture (fileName, filePath, fileSize, mimeType, imageWidth, imageHeight, imageOrientation, cameraMake, cameraModel, dateTime, fNumber, iso, flashID, focalLength, lensMake, lensModel, exposureTime, exposureBias, exifVersion, dateTimeOriginal, dateTimeDigitized, whiteBalance, focalLength35, gps, duration, thumbnail) VALUES (:fileName, :filePath, :fileSize, :mimeType, :imageWidth, :imageHeight, :imageOrientation, :cameraMake, :cameraModel, :dateTime, :fNumber, :iso, :flashID, :focalLength, :lensMake, :lensModel, :exposureTime, :exposureBias, :exifVersion, :dateTimeOriginal, :dateTimeDigitized, :whiteBalance, :focalLength35, :gps, :duration, :thumbnail);"); else - query.prepare("UPDATE picture SET fileName=:fileName, filePath=:filePath, fileSize=:fileSize, mimeType=:mimeType, imageWidth=:imageWidth, imageHeight=:imageHeight, imageOrientation=:imageOrientation, cameraMake=:cameraMake, cameraModel=:cameraModel, dateTime=:dateTime, fNumber=:fNumber, iso=:iso, flashID=:flashID, focalLength=:focalLength, lensMake=:lensMake, lensModel=:lensModel, exposureTime=:exposureTime, exposureBias=:exposureBias, exifVersion=:exifVersion, dateTimeOriginal=:dateTimeOriginal, dateTimeDigitized=:dateTimeDigitized, whiteBalance=:whiteBalance, focalLength35=:focalLength35, gps=:gps, thumbnail=:thumbnail WHERE id=:id;"); + query.prepare("UPDATE picture SET fileName=:fileName, filePath=:filePath, fileSize=:fileSize, mimeType=:mimeType, imageWidth=:imageWidth, imageHeight=:imageHeight, imageOrientation=:imageOrientation, cameraMake=:cameraMake, cameraModel=:cameraModel, dateTime=:dateTime, fNumber=:fNumber, iso=:iso, flashID=:flashID, focalLength=:focalLength, lensMake=:lensMake, lensModel=:lensModel, exposureTime=:exposureTime, exposureBias=:exposureBias, exifVersion=:exifVersion, dateTimeOriginal=:dateTimeOriginal, dateTimeDigitized=:dateTimeDigitized, whiteBalance=:whiteBalance, focalLength35=:focalLength35, gps=:gps, duration=:duration, thumbnail=:thumbnail WHERE id=:id;"); } else - query.prepare("INSERT INTO picture (fileName, filePath, fileSize, mimeType, imageWidth, imageHeight, imageOrientation, cameraMake, cameraModel, dateTime, fNumber, iso, flashID, focalLength, lensMake, lensModel, exposureTime, exposureBias, exifVersion, dateTimeOriginal, dateTimeDigitized, whiteBalance, focalLength35, gps, thumbnail) VALUES (:fileName, :filePath, :fileSize, :mimeType, :imageWidth, :imageHeight, :imageOrientation, :cameraMake, :cameraModel, :dateTime, :fNumber, :iso, :flashID, :focalLength, :lensMake, :lensModel, :exposureTime, :exposureBias, :exifVersion, :dateTimeOriginal, :dateTimeDigitized, :whiteBalance, :focalLength35, :gps, :thumbnail);"); + query.prepare("INSERT INTO picture (fileName, filePath, fileSize, mimeType, imageWidth, imageHeight, imageOrientation, cameraMake, cameraModel, dateTime, fNumber, iso, flashID, focalLength, lensMake, lensModel, exposureTime, exposureBias, exifVersion, dateTimeOriginal, dateTimeDigitized, whiteBalance, focalLength35, gps, duration, thumbnail) VALUES (:fileName, :filePath, :fileSize, :mimeType, :imageWidth, :imageHeight, :imageOrientation, :cameraMake, :cameraModel, :dateTime, :fNumber, :iso, :flashID, :focalLength, :lensMake, :lensModel, :exposureTime, :exposureBias, :exifVersion, :dateTimeOriginal, :dateTimeDigitized, :whiteBalance, :focalLength35, :gps, :duration, :thumbnail);"); query.bindValue(":id", m_iID); @@ -130,6 +132,7 @@ bool cPicture::toDB() query.bindValue(":whiteBalance", m_whiteBalance); query.bindValue(":focalLength35", m_focalLength35); query.bindValue(":gps", m_gps); + query.bindValue(":duration", m_duration); if(!m_thumbnail.isNull()) query.bindValue(":thumbnail", image2Blob(m_thumbnail)); else @@ -387,6 +390,16 @@ QString cPicture::gps() return(m_gps); } +void cPicture::setDuration(const qint64& duration) +{ + m_duration = duration; +} + +qint64 cPicture::duration() +{ + return(m_duration); +} + void cPicture::setFileName(const QString& fileName) { m_szFileName = fileName; @@ -475,6 +488,8 @@ bool cPicture::operator==(const cPicture& other) const return(false); if(this->m_gps != other.m_gps) return(false); + if(this->m_duration != other.m_duration) + return(false); return(true); } @@ -527,6 +542,8 @@ bool cPicture::operator==(const cPicture*& other) const return(false); if(this->m_gps != other->m_gps) return(false); + if(this->m_duration != other->m_duration) + return(false); return(true); } @@ -579,6 +596,8 @@ bool cPicture::operator==(const cPicture* other) const return(false); if(this->m_gps != other->m_gps) return(false); + if(this->m_duration != other->m_duration) + return(false); return(true); } @@ -631,6 +650,8 @@ bool cPicture::operator==(cPicture* other) return(false); if(this->m_gps != other->m_gps) return(false); + if(this->m_duration != other->m_duration) + return(false); return(true); } @@ -686,6 +707,7 @@ bool cPictureList::load(cSplashScreen *lpSplashScreen, QProgressBar *lpProgressB " whiteBalance, " " focalLength35, " " gps, " + " duration, " " thumbnail " "FROM picture " "ORDER BY filePath, " @@ -700,6 +722,9 @@ bool cPictureList::load(cSplashScreen *lpSplashScreen, QProgressBar *lpProgressB qint32 count = 0; qint32 step = max/200; + if(!step) + step = 1; + while(query.next()) { cPicture* lpPicture = add(query.value("id").toInt(), true); @@ -728,6 +753,7 @@ bool cPictureList::load(cSplashScreen *lpSplashScreen, QProgressBar *lpProgressB lpPicture->setWhiteBalance(query.value("whiteBalance").toInt()); lpPicture->setFocalLength35(query.value("focalLength35").toDouble()); lpPicture->setGPS(query.value("gps").toString()); + lpPicture->setDuration(query.value("duration").toLongLong()); lpPicture->setThumbnail(blob2Image(query.value("thumbnail").toByteArray())); count++; diff --git a/cpicture.h b/cpicture.h index 6388ff7..81236d5 100644 --- a/cpicture.h +++ b/cpicture.h @@ -398,6 +398,21 @@ public: */ QString gps(); + /*! + \brief + + \fn setDuration + \param duration + */ + void setDuration(const qint64& duration); + /*! + \brief + + \fn gps + \return qint64 + */ + qint64 duration(); + /*! \brief @@ -526,6 +541,7 @@ private: qint32 m_whiteBalance; /*!< TODO: describe */ qreal m_focalLength35; /*!< TODO: describe */ QString m_gps; /*!< TODO: describe */ + qint64 m_duration; /*!< TODO: describe */ }; Q_DECLARE_METATYPE(cPicture*) diff --git a/cpicturelibrary.cpp b/cpicturelibrary.cpp index a76c4a9..1f832ae 100644 --- a/cpicturelibrary.cpp +++ b/cpicturelibrary.cpp @@ -211,6 +211,7 @@ bool cPictureLibrary::updateDatabase3(qint32 version) " whiteBalance INTEGER, " " focalLength35 DOUBLE, " " gps TEXT, " + " duration BIGINT, " " thumbnail BLOB" "); ")) return(false); diff --git a/pictureLibrary.pro b/pictureLibrary.pro index ab60ec4..b29216b 100644 --- a/pictureLibrary.pro +++ b/pictureLibrary.pro @@ -10,7 +10,7 @@ QMAKE_TARGET_PRODUCT = pictureLibrary QMAKE_TARGET_DESCRIPTION = pictureLibrary QMAKE_TARGET_COPYRIGHT = (c) 2019 WIN-DESIGN -QT += core gui sql multimedia +QT += core gui sql multimedia concurrent greaterThan(QT_MAJOR_VERSION, 4): QT += widgets @@ -65,7 +65,8 @@ SOURCES += \ cimportdialog.cpp \ cdatetimepicker.cpp \ cthumbnailsortfilterproxymodel.cpp \ - cfoldersortfilterproxymodel.cpp + cfoldersortfilterproxymodel.cpp \ + ccopier.cpp HEADERS += \ cmainwindow.h \ @@ -79,7 +80,8 @@ HEADERS += \ cimportdialog.h \ cdatetimepicker.h \ cthumbnailsortfilterproxymodel.h \ - cfoldersortfilterproxymodel.h + cfoldersortfilterproxymodel.h \ + ccopier.h FORMS += \ cmainwindow.ui \