From 28a926b82015d432fadd9b08a817fed61087f9a2 Mon Sep 17 00:00:00 2001 From: lordmulder Date: Fri, 19 Nov 2010 21:11:54 +0100 Subject: [PATCH] Implement multi-theaded processing in progress dialog. --- gui/MainWindow.ui | 18 +------ src/Config.h | 2 +- src/Dialog_MainWindow.cpp | 94 +++++++++++++++++++++-------------- src/Dialog_MainWindow.h | 10 +++- src/Dialog_Processing.cpp | 92 ++++++++++++++++++++++------------ src/Dialog_Processing.h | 11 ++-- src/Main.cpp | 37 +++++++++++--- src/Thread_Initialization.cpp | 8 +-- src/Thread_Process.cpp | 17 ++++--- src/Thread_Process.h | 6 ++- 10 files changed, 185 insertions(+), 110 deletions(-) diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui index 936b37f7..33c9e5cd 100644 --- a/gui/MainWindow.ui +++ b/gui/MainWindow.ui @@ -1344,24 +1344,10 @@ + + - - buttonQuit - clicked() - MainWindow - close() - - - 483 - 422 - - - 276 - 222 - - - actionAbout triggered() diff --git a/src/Config.h b/src/Config.h index 6cb44b5b..314962cf 100644 --- a/src/Config.h +++ b/src/Config.h @@ -25,7 +25,7 @@ #define VER_LAMEXP_MAJOR 4 #define VER_LAMEXP_MINOR_HI 0 #define VER_LAMEXP_MINOR_LO 0 -#define VER_LAMEXP_BUILD 41 +#define VER_LAMEXP_BUILD 44 #define VER_LAMEXP_SUFFIX TechPreview /* diff --git a/src/Dialog_MainWindow.cpp b/src/Dialog_MainWindow.cpp index 8abc24a7..a036f10f 100644 --- a/src/Dialog_MainWindow.cpp +++ b/src/Dialog_MainWindow.cpp @@ -31,6 +31,7 @@ #include "Thread_MessageHandler.h" #include "Model_MetaInfo.h" #include "Model_Settings.h" +#include "Model_FileList.h" #include "Encoder_MP3.h" //Qt includes @@ -79,8 +80,13 @@ private: // Constructor //////////////////////////////////////////////////////////// -MainWindow::MainWindow(QWidget *parent) -: QMainWindow(parent) +MainWindow::MainWindow(FileListModel *fileListModel, AudioFileModel *metaInfo, SettingsModel *settingsModel, QWidget *parent) +: + QMainWindow(parent), + m_fileListModel(fileListModel), + m_metaData(metaInfo), + m_settings(settingsModel), + m_accepted(false) { //Init the dialog, from the .ui file setupUi(this); @@ -95,20 +101,16 @@ MainWindow::MainWindow(QWidget *parent) setWindowTitle(windowTitle().append(" [DEMO VERSION]")); } - //Load configuration - m_settings = new SettingsModel(); - m_settings->validate(); - //Enabled main buttons connect(buttonAbout, SIGNAL(clicked()), this, SLOT(aboutButtonClicked())); connect(buttonStart, SIGNAL(clicked()), this, SLOT(encodeButtonClicked())); + connect(buttonQuit, SIGNAL(clicked()), this, SLOT(closeButtonClicked())); //Setup tab widget tabWidget->setCurrentIndex(0); connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabPageChanged(int))); //Setup "Source" tab - m_fileListModel = new FileListModel(); sourceFileView->setModel(m_fileListModel); sourceFileView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); sourceFileView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); @@ -139,7 +141,6 @@ MainWindow::MainWindow(QWidget *parent) connect(buttonGotoMusic, SIGNAL(clicked()), this, SLOT(gotoMusicFolderButtonClicked())); //Setup "Meta Data" tab - m_metaData = new AudioFileModel(); m_metaInfoModel = new MetaInfoModel(m_metaData, 6); m_metaInfoModel->clearData(); metaDataView->setModel(m_metaInfoModel); @@ -261,15 +262,12 @@ MainWindow::~MainWindow(void) //Free memory LAMEXP_DELETE(m_tabActionGroup); LAMEXP_DELETE(m_styleActionGroup); - LAMEXP_DELETE(m_fileListModel); LAMEXP_DELETE(m_banner); LAMEXP_DELETE(m_fileSystemModel); LAMEXP_DELETE(m_messageHandler); LAMEXP_DELETE(m_delayedFileList); LAMEXP_DELETE(m_delayedFileTimer); - LAMEXP_DELETE(m_metaData); LAMEXP_DELETE(m_metaInfoModel); - LAMEXP_DELETE(m_settings); LAMEXP_DELETE(m_encoderButtonGroup); } @@ -313,6 +311,7 @@ void MainWindow::addFiles(const QStringList &files) void MainWindow::showEvent(QShowEvent *event) { QTimer::singleShot(0, this, SLOT(windowShown())); + m_accepted = false; } void MainWindow::dragEnterEvent(QDragEnterEvent *event) @@ -357,6 +356,15 @@ void MainWindow::dropEvent(QDropEvent *event) addFiles(droppedFiles); } +void MainWindow::closeEvent(QCloseEvent *event) +{ + if(m_banner->isVisible() || m_delayedFileTimer->isActive()) + { + MessageBeep(MB_ICONEXCLAMATION); + event->ignore(); + } +} + //////////////////////////////////////////////////////////// // Slots //////////////////////////////////////////////////////////// @@ -458,36 +466,48 @@ void MainWindow::encodeButtonClicked(void) return; } - m_banner->show("Encoding files, please wait..."); - QApplication::processEvents(); + m_accepted = true; + close(); + + //m_banner->show("Encoding files, please wait..."); + //QApplication::processEvents(); - MP3Encoder *mp3Encoder = new MP3Encoder(); - connect(mp3Encoder, SIGNAL(statusUpdated(QString)), m_banner, SLOT(setText(QString))); + //MP3Encoder *mp3Encoder = new MP3Encoder(); + //connect(mp3Encoder, SIGNAL(statusUpdated(QString)), m_banner, SLOT(setText(QString))); - for(int i = 0; i < m_fileListModel->rowCount(); i++) - { - AudioFileModel file = m_fileListModel->getFile(m_fileListModel->index(i,0)); - QString outFolder = m_fileSystemModel->filePath(this->outputFolderView->currentIndex()); - - QString baseName = QFileInfo(file.filePath()).fileName(); - int pos = baseName.lastIndexOf("."); - if(pos >= 1) baseName = baseName.left(pos); + //for(int i = 0; i < m_fileListModel->rowCount(); i++) + //{ + // AudioFileModel file = m_fileListModel->getFile(m_fileListModel->index(i,0)); + // QString outFolder = m_fileSystemModel->filePath(this->outputFolderView->currentIndex()); + // + // QString baseName = QFileInfo(file.filePath()).fileName(); + // int pos = baseName.lastIndexOf("."); + // if(pos >= 1) baseName = baseName.left(pos); - int n = 1; - QString outFileName = QString(outFolder).append("/").append(baseName).append(".mp3"); - - while(QFileInfo(outFileName).exists()) - { - outFileName = QString(outFolder).append("/").append(baseName).append(" (").append(QString::number(++n)).append(").mp3"); - } - - mp3Encoder->encode(file, outFileName); - } + // int n = 1; + // QString outFileName = QString(outFolder).append("/").append(baseName).append(".mp3"); + // + // while(QFileInfo(outFileName).exists()) + // { + // outFileName = QString(outFolder).append("/").append(baseName).append(" (").append(QString::number(++n)).append(").mp3"); + // } + // + // mp3Encoder->encode(file, outFileName); + //} - LAMEXP_DELETE(mp3Encoder); - m_banner->close(); - - QMessageBox::information(this, "Done", "Encoding process completed."); + //LAMEXP_DELETE(mp3Encoder); + //m_banner->close(); + // + //QMessageBox::information(this, "Done", "Encoding process completed."); +} + +/* + * Close button + */ +void MainWindow::closeButtonClicked(void) +{ + ABORT_IF_BUSY; + close(); } /* diff --git a/src/Dialog_MainWindow.h b/src/Dialog_MainWindow.h index 809a53e7..8037ecf0 100644 --- a/src/Dialog_MainWindow.h +++ b/src/Dialog_MainWindow.h @@ -23,7 +23,7 @@ #include "../tmp/UIC_MainWindow.h" -#include "Model_FileList.h" +//#include "Model_FileList.h" //Class declarations class QFileSystemModel; @@ -33,19 +33,23 @@ class AudioFileModel; class MetaInfoModel; class SettingsModel; class QButtonGroup; +class FileListModel; class MainWindow: public QMainWindow, private Ui::MainWindow { Q_OBJECT public: - MainWindow(QWidget *parent = 0); + MainWindow(FileListModel *fileListModel, AudioFileModel *metaInfo, SettingsModel *settingsModel, QWidget *parent = 0); ~MainWindow(void); + bool isAccepted() { return m_accepted; } + private slots: void windowShown(void); void aboutButtonClicked(void); void encodeButtonClicked(void); + void closeButtonClicked(void); void addFilesButtonClicked(void); void clearFilesButtonClicked(void); void removeFileButtonClicked(void); @@ -76,10 +80,12 @@ protected: void showEvent(QShowEvent *event); void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); + void closeEvent(QCloseEvent *event); private: void addFiles(const QStringList &files); + bool m_accepted; FileListModel *m_fileListModel; QFileSystemModel *m_fileSystemModel; QActionGroup *m_tabActionGroup; diff --git a/src/Dialog_Processing.cpp b/src/Dialog_Processing.cpp index f1cabe66..77257b30 100644 --- a/src/Dialog_Processing.cpp +++ b/src/Dialog_Processing.cpp @@ -22,6 +22,7 @@ #include "Dialog_Processing.h" #include "Global.h" +#include "Model_FileList.h" #include "Model_Progress.h" #include "Thread_Process.h" @@ -40,7 +41,7 @@ // Constructor //////////////////////////////////////////////////////////// -ProcessingDialog::ProcessingDialog(void) +ProcessingDialog::ProcessingDialog(FileListModel *fileListModel) { //Init the dialog, from the .ui file setupUi(this); @@ -65,7 +66,6 @@ ProcessingDialog::ProcessingDialog(void) //Init progress indicator m_progressIndicator = new QMovie(":/images/Working.gif"); label_headerWorking->setMovie(m_progressIndicator); - progressBar->setRange(0, 4); progressBar->setValue(0); //Init progress model @@ -76,8 +76,18 @@ ProcessingDialog::ProcessingDialog(void) view_log->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); view_log->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); - //Init member vars - for(int i = 0; i < 4; i++) m_thread[i] = NULL; + //Enque jobs + if(fileListModel) + { + for(int i = 0; i < fileListModel->rowCount(); i++) + { + m_pendingJobs.append(fileListModel->getFile(fileListModel->index(i,0))); + } + } + + //Init other vars + m_runningThreads = 0; + m_userAborted = false; } //////////////////////////////////////////////////////////// @@ -90,6 +100,11 @@ ProcessingDialog::~ProcessingDialog(void) if(m_progressIndicator) m_progressIndicator->stop(); LAMEXP_DELETE(m_progressIndicator); LAMEXP_DELETE(m_progressModel); + + while(!m_threadList.isEmpty()) + { + delete m_threadList.takeFirst(); + } } //////////////////////////////////////////////////////////// @@ -145,63 +160,60 @@ bool ProcessingDialog::eventFilter(QObject *obj, QEvent *event) void ProcessingDialog::initEncoding(void) { + m_runningThreads = 0; + m_userAborted = false; + label_progress->setText("Encoding files, please wait..."); m_progressIndicator->start(); - m_pendingJobs = 4; - - for(int i = 0; i < 4; i++) - { - m_thread[i] = new ProcessThread(); - connect(m_thread[i], SIGNAL(finished()), this, SLOT(doneEncoding()), Qt::QueuedConnection); - connect(m_thread[i], SIGNAL(processStateInitialized(QUuid,QString,QString,int)), m_progressModel, SLOT(addJob(QUuid,QString,QString,int)), Qt::QueuedConnection); - connect(m_thread[i], SIGNAL(processStateChanged(QUuid,QString,int)), m_progressModel, SLOT(updateJob(QUuid,QString,int)), Qt::QueuedConnection); - m_thread[i]->start(); - } - button_closeDialog->setEnabled(false); button_AbortProcess->setEnabled(true); + progressBar->setRange(0, m_pendingJobs.count()); + + startNextJob(); + startNextJob(); } void ProcessingDialog::abortEncoding(void) { + m_userAborted = true; button_AbortProcess->setEnabled(false); - for(int i = 0; i < 4; i++) + for(int i = 0; i < m_threadList.count(); i++) { - if(m_thread[i]) - { - m_thread[i]->abort(); - } + m_threadList.at(i)->abort(); } } void ProcessingDialog::doneEncoding(void) { + m_runningThreads--; + progressBar->setValue(progressBar->value() + 1); + label_progress->setText(QString("%1 files out of %2 completed, please wait...").arg(QString::number(progressBar->value()), QString::number(progressBar->maximum()))); - if(--m_pendingJobs > 0) + if(!m_pendingJobs.isEmpty() && !m_userAborted) { + startNextJob(); + qDebug("Running jobs: %u", m_runningThreads); return; } - label_progress->setText("Completed."); + if(m_runningThreads > 0) + { + qDebug("Running jobs: %u", m_runningThreads); + return; + } + + qDebug("Running jobs: %u", m_runningThreads); + + label_progress->setText(m_userAborted ? "Process aborted by user." : "Alle files completed."); m_progressIndicator->stop(); setCloseButtonEnabled(true); button_closeDialog->setEnabled(true); button_AbortProcess->setEnabled(false); - for(int i = 0; i < 4; i++) - { - if(m_thread[i]) - { - m_thread[i]->terminate(); - m_thread[i]->wait(); - LAMEXP_DELETE(m_thread[i]); - } - } - progressBar->setValue(100); } @@ -209,6 +221,22 @@ void ProcessingDialog::doneEncoding(void) // Private Functions //////////////////////////////////////////////////////////// +void ProcessingDialog::startNextJob(void) +{ + if(m_pendingJobs.isEmpty()) + { + return; + } + + ProcessThread *thread = new ProcessThread(m_pendingJobs.takeFirst()); + m_threadList.append(thread); + connect(thread, SIGNAL(finished()), this, SLOT(doneEncoding()), Qt::QueuedConnection); + connect(thread, SIGNAL(processStateInitialized(QUuid,QString,QString,int)), m_progressModel, SLOT(addJob(QUuid,QString,QString,int)), Qt::QueuedConnection); + connect(thread, SIGNAL(processStateChanged(QUuid,QString,int)), m_progressModel, SLOT(updateJob(QUuid,QString,int)), Qt::QueuedConnection); + m_runningThreads++; + thread->start(); +} + void ProcessingDialog::setCloseButtonEnabled(bool enabled) { HMENU hMenu = GetSystemMenu((HWND) winId(), FALSE); diff --git a/src/Dialog_Processing.h b/src/Dialog_Processing.h index 7129d773..4a2baf16 100644 --- a/src/Dialog_Processing.h +++ b/src/Dialog_Processing.h @@ -26,13 +26,15 @@ class QMovie; class ProgressModel; class ProcessThread; +class FileListModel; +class AudioFileModel; class ProcessingDialog : public QDialog, private Ui::ProcessingDialog { Q_OBJECT public: - ProcessingDialog(void); + ProcessingDialog(FileListModel *fileListModel = NULL); ~ProcessingDialog(void); private slots: @@ -47,9 +49,12 @@ protected: private: void setCloseButtonEnabled(bool enabled); + void ProcessingDialog::startNextJob(void); - int m_pendingJobs; + QList m_pendingJobs; + QList m_threadList; QMovie *m_progressIndicator; ProgressModel *m_progressModel; - ProcessThread *m_thread[4]; + unsigned int m_runningThreads; + bool m_userAborted; }; diff --git a/src/Main.cpp b/src/Main.cpp index 9cff87e9..9abc5f5d 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -27,6 +27,8 @@ #include "Thread_Initialization.h" #include "Thread_MessageProducer.h" #include "Model_Settings.h" +#include "Model_FileList.h" +#include "Model_AudioFile.h" //Qt includes #include @@ -41,6 +43,7 @@ int lamexp_main(int argc, char* argv[]) { int iResult = -1; + bool bAccepted = true; //Init console lamexp_init_console(argc, argv); @@ -112,21 +115,39 @@ int lamexp_main(int argc, char* argv[]) } } + //Create models + FileListModel *fileListModel = new FileListModel(); + AudioFileModel *metaInfo = new AudioFileModel(); + SettingsModel *settingsModel = new SettingsModel(); + settingsModel->validate(); + //Show splash screen InitializationThread *poInitializationThread = new InitializationThread(); SplashScreen::showSplash(poInitializationThread); LAMEXP_DELETE(poInitializationThread); //Show main window - MainWindow *poMainWindow = new MainWindow(); - poMainWindow->show(); - iResult = QApplication::instance()->exec(); - LAMEXP_DELETE(poMainWindow); + while(bAccepted) + { + MainWindow *poMainWindow = new MainWindow(fileListModel, metaInfo, settingsModel); + poMainWindow->show(); + iResult = QApplication::instance()->exec(); + bAccepted = poMainWindow->isAccepted(); + LAMEXP_DELETE(poMainWindow); + + //Show processing dialog + if(bAccepted && fileListModel->rowCount() > 0) + { + ProcessingDialog *processingDialog = new ProcessingDialog(fileListModel); + processingDialog->exec(); + LAMEXP_DELETE(processingDialog); + } + } - //Show processing dialog - ProcessingDialog *processingDialog = new ProcessingDialog(); - processingDialog->exec(); - LAMEXP_DELETE(processingDialog); + //Free models + LAMEXP_DELETE(fileListModel); + LAMEXP_DELETE(metaInfo); + LAMEXP_DELETE(settingsModel); //Final clean-up qDebug("Shutting down, please wait...\n"); diff --git a/src/Thread_Initialization.cpp b/src/Thread_Initialization.cpp index 3e2dcf93..73b7e5b1 100644 --- a/src/Thread_Initialization.cpp +++ b/src/Thread_Initialization.cpp @@ -213,10 +213,10 @@ void InitializationThread::initNeroAac(void) if(versionTokens.count() == 4) { neroVersion = 0; - neroVersion += versionTokens.at(3).toInt(); - neroVersion += versionTokens.at(2).toInt() * 10; - neroVersion += versionTokens.at(1).toInt() * 100; - neroVersion += versionTokens.at(0).toInt() * 1000; + neroVersion += min(9, max(0, versionTokens.at(3).toInt())); + neroVersion += min(9, max(0, versionTokens.at(2).toInt())) * 10; + neroVersion += min(9, max(0, versionTokens.at(1).toInt())) * 100; + neroVersion += min(9, max(0, versionTokens.at(0).toInt())) * 1000; } } } diff --git a/src/Thread_Process.cpp b/src/Thread_Process.cpp index f7e4d197..ea27a84a 100644 --- a/src/Thread_Process.cpp +++ b/src/Thread_Process.cpp @@ -22,9 +22,12 @@ #include "Thread_Process.h" #include "Global.h" +#include "Model_AudioFile.h" #include "Model_Progress.h" -#include +#include +#include + #include #include @@ -32,8 +35,11 @@ // Constructor //////////////////////////////////////////////////////////// -ProcessThread::ProcessThread(void) - : m_jobId(QUuid::createUuid()), m_aborted(false) +ProcessThread::ProcessThread(AudioFileModel audioFile) +: + m_audioFile(audioFile), + m_jobId(QUuid::createUuid()), + m_aborted(false) { } @@ -46,12 +52,11 @@ void ProcessThread::run() m_aborted = false; qDebug("Process thread %s has started.", m_jobId.toString().toLatin1().constData()); - emit processStateInitialized(m_jobId, "Slime - Der Tod Ist Ein Meister Aus Deutschland.mp3", "Starting...", ProgressModel::JobRunning); + emit processStateInitialized(m_jobId, QFileInfo(m_audioFile.filePath()).fileName(), "Starting...", ProgressModel::JobRunning); QUuid uuid = QUuid::createUuid(); qsrand(uuid.data1 * uuid.data2 * uuid.data3 * uuid.data4[0] * uuid.data4[1] * uuid.data4[2] * uuid.data4[3] * uuid.data4[4] * uuid.data4[5] * uuid.data4[6] * uuid.data4[7]); - unsigned long delay = 250 + (qrand() % 500); - + unsigned long delay = 100 + (qrand() % 150); for(int i = 1; i <= 100; i++) { diff --git a/src/Thread_Process.h b/src/Thread_Process.h index 158fc005..141001fa 100644 --- a/src/Thread_Process.h +++ b/src/Thread_Process.h @@ -24,15 +24,18 @@ #include #include +#include "Model_AudioFile.h" + class ProcessThread: public QThread { Q_OBJECT public: - ProcessThread(void); + ProcessThread(AudioFileModel audioFile); ~ProcessThread(void); void run(); void abort() { m_aborted = true; } + QUuid getId() { return m_jobId; } signals: void processStateInitialized(const QUuid &jobId, const QString &jobName, const QString &jobInitialStatus, int jobInitialState); @@ -40,5 +43,6 @@ signals: private: const QUuid m_jobId; + AudioFileModel m_audioFile; volatile bool m_aborted; };