use another thread to parse files

This commit is contained in:
Martchus 2015-07-08 00:21:21 +02:00
parent e25b7cdfdf
commit 08eec94b8b
2 changed files with 133 additions and 87 deletions

View File

@ -64,6 +64,16 @@ namespace QtGui {
Necessary for lupdate. Necessary for lupdate.
*/ */
/*!
* \brief The LoadingResult enum specifies whether the file could be parsed.
*/
enum LoadingResult : char
{
ParsingSuccessful,
FatalParsingError,
IoError
};
/*! /*!
* \class QtGui::MainWindow * \class QtGui::MainWindow
* \brief The MainWindow class provides the main window of the Tag Editor's Qt gui. * \brief The MainWindow class provides the main window of the Tag Editor's Qt gui.
@ -220,7 +230,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event)
if(!data.isEmpty()) { if(!data.isEmpty()) {
event->accept(); event->accept();
if(event->type() == QEvent::Drop) { if(event->type() == QEvent::Drop) {
showFile(data, true); startParsing(data, true);
} }
} }
return true; return true;
@ -263,7 +273,7 @@ void MainWindow::fileSelected()
QString path(m_fileModel->filePath(m_fileFilterModel->mapToSource(selectedIndexes.at(0)))); QString path(m_fileModel->filePath(m_fileFilterModel->mapToSource(selectedIndexes.at(0))));
QFileInfo fileInfo(path); QFileInfo fileInfo(path);
if(fileInfo.isFile()) { if(fileInfo.isFile()) {
showFile(path); startParsing(path);
m_ui->pathLineEdit->setText(fileInfo.dir().path()); m_ui->pathLineEdit->setText(fileInfo.dir().path());
} else if(fileInfo.isDir()) { } else if(fileInfo.isDir()) {
m_ui->pathLineEdit->setText(path); m_ui->pathLineEdit->setText(path);
@ -612,21 +622,21 @@ void MainWindow::foreachTagEdit(const std::function<void (TagEdit *)> &function)
} }
/*! /*!
* \brief Opens a file and shows its tags and general information. * \brief Opens and parses a file using another thread.
* A possibly previously opened file will be closed if still open. * Shows its tags and general information using the showFile() method.
* \param path Specifies the \a path of the file. * \param path Specifies the \a path of the file.
* \param forceRefresh Specifies whether the file should be reparsed if it is already opened. * \param forceRefresh Specifies whether the file should be reparsed if it is already opened.
*/ */
void MainWindow::showFile(const QString &path, bool forceRefresh) bool MainWindow::startParsing(const QString &path, bool forceRefresh)
{ {
// check if file is current file // check if file is current file
bool sameFile = m_currentPath == path; bool sameFile = m_currentPath == path;
if(!forceRefresh && sameFile) { if(!forceRefresh && sameFile) {
return; return true;
} }
if(!m_fileOperationMutex.try_lock()) { if(!m_fileOperationMutex.try_lock()) {
m_ui->statusBar->showMessage(tr("Unable to load the selected file \"%1\" because the current process hasn't finished yet.").arg(path)); m_ui->statusBar->showMessage(tr("Unable to load the selected file \"%1\" because the current process hasn't finished yet.").arg(path));
return; return false;
} }
lock_guard<mutex> guard(m_fileOperationMutex, adopt_lock); lock_guard<mutex> guard(m_fileOperationMutex, adopt_lock);
// clear previous results and status // clear previous results and status
@ -651,82 +661,120 @@ void MainWindow::showFile(const QString &path, bool forceRefresh)
if(!m_makingResultsAvailable) { if(!m_makingResultsAvailable) {
m_originalNotifications.clear(); m_originalNotifications.clear();
} }
// load file info // show filename
bool sucess; m_ui->fileNameLabel->setText(QString::fromLocal8Bit(m_fileInfo.fileName().c_str()));
try { // define function to parse the file
m_fileInfo.setForceFullParse(Settings::forceFullParse()); auto startThread = [this] {
m_fileInfo.parseEverything(); m_fileOperationMutex.lock();
sucess = true; char result;
} catch(Failure &) { try {
// the file has been opened; parsing notifications will be shown in the info box m_fileInfo.setForceFullParse(Settings::forceFullParse());
sucess = false; m_fileInfo.parseEverything();
} catch(ios_base::failure &) { result = ParsingSuccessful;
// the file could not be opened because an IO error occured } catch(Failure &) {
m_fileInfo.close(); // ensure file is actually closed // the file has been opened; parsing notifications will be shown in the info box
result = FatalParsingError;
} catch(ios_base::failure &) {
// the file could not be opened because an IO error occured
m_fileInfo.close(); // ensure file is closed
result = IoError;
}
m_fileInfo.unregisterAllCallbacks();
QMetaObject::invokeMethod(this, "showFile", Qt::QueuedConnection, Q_ARG(char, result));
// showFile() will unlock the mutex!
};
m_fileInfo.unregisterAllCallbacks();
//m_fileInfo.registerCallback(showProgress); can't show progress yet
// use another thread to perform the operation
std::thread thr(startThread);
thr.detach();
// inform user
static const QString statusMsg(tr("The file is beeing parsed ..."));
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Progress);
m_ui->parsingNotificationWidget->setText(statusMsg);
m_ui->statusBar->showMessage(statusMsg);
return true;
}
/*!
* \brief Shows the current file info (technical info, tags, ...).
* This private slot is invoked from the thread which performed the
* parsing operation using Qt::QueuedConnection.
* \param result Specifies whether the file could be load sucessfully.
* \remarks Expects m_fileOperationMutex to be locked!
*/
void MainWindow::showFile(char result)
{
lock_guard<mutex> guard(m_fileOperationMutex, adopt_lock);
if(result == IoError) {
// update status // update status
updateUiStatus(); updateUiStatus();
static const QString statusMsg(tr("The file could not be opened because an IO error occurred.")); static const QString statusMsg(tr("The file could not be opened because an IO error occurred."));
QMessageBox::critical(this, windowTitle(), statusMsg); QMessageBox::critical(this, windowTitle(), statusMsg);
m_ui->statusBar->showMessage(statusMsg); m_ui->statusBar->showMessage(statusMsg);
return;
}
// show filename and update info model
QString fileName = QString::fromLocal8Bit(m_fileInfo.fileName().c_str());
m_ui->fileNameLabel->setText(fileName);
updateInfoWebView();
// show parsing status/result using parsing notification widget
Media::NotificationType worstNotificationType = m_fileInfo.worstNotificationTypeIncludingRelatedObjects();
sucess &= worstNotificationType < Media::NotificationType::Critical;
if(sucess) {
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::TaskComplete);
m_ui->parsingNotificationWidget->setText(tr("File could be parsed correctly."));
} else { } else {
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Critical); // update webview
m_ui->parsingNotificationWidget->setText(tr("File couldn't be parsed correctly.")); updateInfoWebView();
} // show parsing status/result using parsing notification widget
switch(worstNotificationType) { auto worstNotificationType = m_fileInfo.worstNotificationTypeIncludingRelatedObjects();
case Media::NotificationType::Critical: if(worstNotificationType >= Media::NotificationType::Critical) {
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Critical); // we catched no exception, but there are critical notifications
m_ui->parsingNotificationWidget->appendLine(tr("There are critical parsing notifications.")); // -> treat critical notifications as fatal parsing errors
break; result = LoadingResult::FatalParsingError;
case Media::NotificationType::Warning:
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Warning);
m_ui->parsingNotificationWidget->appendLine(tr("There are warnings."));
break;
default:
;
}
// load existing tags
m_tags.clear();
m_fileInfo.tags(m_tags);
// show notification if there is currently no existing tag(s) could be found
if(!m_tags.size()) {
m_ui->parsingNotificationWidget->appendLine(tr("There is no (supported) tag assigned."));
if(!m_fileInfo.areTagsSupported()) {
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Warning);
m_ui->parsingNotificationWidget->appendLine(tr("File format is not supported (an ID3 tag can be added anyways)."));
} }
} switch(result) {
// create appropriate tags according to file type and user preferences when automatic tag management is enabled case ParsingSuccessful:
if(Settings::autoTagManagement()) { m_ui->parsingNotificationWidget->setNotificationType(NotificationType::TaskComplete);
if(!m_fileInfo.createAppropriateTags(false, Settings::id3v1usage(), Settings::id3v2usage(), Settings::mergeMultipleSuccessiveId3v2Tags(), m_ui->parsingNotificationWidget->setText(tr("File could be parsed correctly."));
Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed())) { break;
if(confirmCreationOfId3TagForUnsupportedFile()) { default:
m_fileInfo.createAppropriateTags(true, Settings::id3v1usage(), Settings::id3v2usage(), Settings::mergeMultipleSuccessiveId3v2Tags(), m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Critical);
Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed()); m_ui->parsingNotificationWidget->setText(tr("File couldn't be parsed correctly."));
}
switch(worstNotificationType) {
case Media::NotificationType::Critical:
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Critical);
m_ui->parsingNotificationWidget->appendLine(tr("There are critical parsing notifications."));
break;
case Media::NotificationType::Warning:
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Warning);
m_ui->parsingNotificationWidget->appendLine(tr("There are warnings."));
break;
default:
;
}
// load existing tags
m_tags.clear();
m_fileInfo.tags(m_tags);
// show notification if there is currently no existing tag(s) could be found
if(!m_tags.size()) {
m_ui->parsingNotificationWidget->appendLine(tr("There is no (supported) tag assigned."));
if(!m_fileInfo.areTagsSupported()) {
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Warning);
m_ui->parsingNotificationWidget->appendLine(tr("File format is not supported (an ID3 tag can be added anyways)."));
} }
} }
// create appropriate tags according to file type and user preferences when automatic tag management is enabled
if(Settings::autoTagManagement()) {
if(!m_fileInfo.createAppropriateTags(false, Settings::id3v1usage(), Settings::id3v2usage(), Settings::mergeMultipleSuccessiveId3v2Tags(),
Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed())) {
if(confirmCreationOfId3TagForUnsupportedFile()) {
m_fileInfo.createAppropriateTags(true, Settings::id3v1usage(), Settings::id3v2usage(), Settings::mergeMultipleSuccessiveId3v2Tags(),
Settings::keepVersionOfExistingId3v2Tag(), Settings::id3v2versionToBeUsed());
}
}
}
m_tags.clear();
m_fileInfo.tags(m_tags); // reload tags
// update related widgets
updateTagEditsAndAttachmentEdits();
updateTagSelectionComboBox();
updateTagManagementMenu();
insertTitleFromFilename();
// update status
m_ui->statusBar->showMessage(tr("The file %1 has been opened.").arg(QString::fromLocal8Bit(m_fileInfo.fileName().c_str())));
updateUiStatus();
} }
m_tags.clear();
m_fileInfo.tags(m_tags); // reload tags
// update related widgets
updateTagEditsAndAttachmentEdits();
updateTagSelectionComboBox();
updateTagManagementMenu();
insertTitleFromFilename();
// update status
m_ui->statusBar->showMessage(tr("The file %1 has been opened.").arg(fileName));
updateUiStatus();
} }
/*! /*!
@ -859,21 +907,19 @@ bool MainWindow::startSaving()
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(sender.currentStatus()))); QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(sender.currentStatus())));
} }
}; };
auto startThread = [this] (void) -> void { auto startThread = [this] {
//lock_guard<mutex> guard(m_fileOperationMutex);
// Outcommented because the mutex needs to be locked when leaving the method.
m_fileOperationMutex.lock(); m_fileOperationMutex.lock();
bool sucess; bool success;
try { try {
m_fileInfo.applyChanges(); m_fileInfo.applyChanges();
sucess = true; success = true;
} catch(Failure &) { } catch(Failure &) {
sucess = false; success = false;
} catch(ios_base::failure &) { } catch(ios_base::failure &) {
sucess = false; success = false;
} }
m_fileInfo.unregisterAllCallbacks(); m_fileInfo.unregisterAllCallbacks();
QMetaObject::invokeMethod(this, "showSavingResult", Qt::QueuedConnection, Q_ARG(bool, sucess)); QMetaObject::invokeMethod(this, "showSavingResult", Qt::QueuedConnection, Q_ARG(bool, success));
// showSavingResult() will unlock the mutex! // showSavingResult() will unlock the mutex!
}; };
m_fileInfo.unregisterAllCallbacks(); m_fileInfo.unregisterAllCallbacks();
@ -886,14 +932,13 @@ bool MainWindow::startSaving()
/*! /*!
* \brief Shows the saving results. * \brief Shows the saving results.
* This slot is invoked from the thread which performed the * This private slot is invoked from the thread which performed the
* saving operation using Qt::QueuedConnection. * saving operation using Qt::QueuedConnection.
* \param sucess Specifies whether the file could be saved sucessfully. * \param sucess Specifies whether the file could be saved sucessfully.
* \remarks Expects m_fileOperationMutex to be locked! * \remarks Expects m_fileOperationMutex to be locked!
*/ */
void MainWindow::showSavingResult(bool sucess) void MainWindow::showSavingResult(bool sucess)
{ {
//m_fileOperationMutex.lock();
m_ui->abortButton->setHidden(true); m_ui->abortButton->setHidden(true);
m_ui->makingNotificationWidget->setNotificationType(NotificationType::TaskComplete); m_ui->makingNotificationWidget->setNotificationType(NotificationType::TaskComplete);
m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::Saving); m_ui->makingNotificationWidget->setNotificationSubject(NotificationSubject::Saving);
@ -955,7 +1000,7 @@ void MainWindow::showSavingResult(bool sucess)
} }
} }
if(!showNextFile) { if(!showNextFile) {
showFile(m_currentPath, true); startParsing(m_currentPath, true);
} }
} else { } else {
static const QString errormsg(tr("The tags couldn't be saved. See the file info box for detail.")); static const QString errormsg(tr("The tags couldn't be saved. See the file info box for detail."));
@ -964,7 +1009,7 @@ void MainWindow::showSavingResult(bool sucess)
m_ui->makingNotificationWidget->setText(errormsg); m_ui->makingNotificationWidget->setText(errormsg);
m_ui->makingNotificationWidget->setNotificationType(NotificationType::Critical); m_ui->makingNotificationWidget->setNotificationType(NotificationType::Critical);
m_fileOperationMutex.unlock(); m_fileOperationMutex.unlock();
showFile(m_currentPath, true); startParsing(m_currentPath, true);
} }
} }
@ -1080,7 +1125,7 @@ void MainWindow::showOpenFileDlg()
{ {
QString path = QFileDialog::getOpenFileName(this, QApplication::applicationName()); QString path = QFileDialog::getOpenFileName(this, QApplication::applicationName());
if(!path.isEmpty()) { if(!path.isEmpty()) {
showFile(path); startParsing(path);
} }
} }

View File

@ -65,7 +65,8 @@ private slots:
void showOpenFileDlg(); void showOpenFileDlg();
// opened file: load, save, delete, close // opened file: load, save, delete, close
void showFile(const QString &path, bool forceRefresh = false); bool startParsing(const QString &path, bool forceRefresh = false);
void showFile(char result);
bool applyEntriesAndSaveChangings(); bool applyEntriesAndSaveChangings();
bool deleteAllTagsAndSave(); bool deleteAllTagsAndSave();
void closeFile(); void closeFile();