From dd604e8fc559277a2fc42a161feb0de91d3515a5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Tue, 20 Dec 2011 14:52:15 +0100 Subject: [PATCH] Improve feedback when a job fails (needs KDE >= 4.7) using messagewidget that shows the job log --- src/cliptranscode.cpp | 4 +- src/projectlist.cpp | 114 +++++++++++++++++++++------- src/projectlist.h | 22 +++++- src/projecttree/abstractclipjob.cpp | 5 ++ src/projecttree/abstractclipjob.h | 7 ++ src/projecttree/cutclipjob.cpp | 5 +- src/projecttree/proxyclipjob.cpp | 3 + 7 files changed, 128 insertions(+), 32 deletions(-) diff --git a/src/cliptranscode.cpp b/src/cliptranscode.cpp index 8f64c75e..863db131 100644 --- a/src/cliptranscode.cpp +++ b/src/cliptranscode.cpp @@ -221,10 +221,10 @@ void ClipTranscode::slotTranscodeFinished(int exitCode, QProcess::ExitStatus exi } else { #if KDE_IS_VERSION(4,7,0) m_infoMessage->setMessageType(KMessageWidget::Warning); - m_infoMessage->setText(i18n("Transcoding FAILED!")); + m_infoMessage->setText(i18n("Transcoding failed!")); m_infoMessage->animatedShow(); #else - log_text->setHtml(log_text->toPlainText() + "
" + i18n("Transcoding FAILED!")); + log_text->setHtml(log_text->toPlainText() + "
" + i18n("Transcoding failed!")); #endif log_text->setVisible(true); } diff --git a/src/projectlist.cpp b/src/projectlist.cpp index 174e5ff2..ba755238 100644 --- a/src/projectlist.cpp +++ b/src/projectlist.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #ifdef NEPOMUK @@ -236,7 +237,7 @@ ProjectList::ProjectList(QWidget *parent) : m_jobsMenu->addAction(cancelJobs); m_infoLabel->setMenu(m_jobsMenu); box->addWidget(m_infoLabel); - + int size = style()->pixelMetric(QStyle::PM_SmallIconSize); QSize iconSize(size, size); @@ -260,6 +261,18 @@ ProjectList::ProjectList(QWidget *parent) : m_listView = new ProjectListView; layout->addWidget(m_listView); + +#if KDE_IS_VERSION(4,7,0) + m_infoMessage = new KMessageWidget; + layout->addWidget(m_infoMessage); + m_infoMessage->setCloseButtonVisible(true); + //m_infoMessage->setWordWrap(true); + m_infoMessage->hide(); + m_logAction = new QAction(i18n("Show Log"), this); + m_logAction->setCheckable(false); + connect(m_logAction, SIGNAL(triggered()), this, SLOT(slotShowJobLog())); +#endif + setLayout(layout); searchView->setTreeWidget(m_listView); @@ -277,6 +290,9 @@ ProjectList::ProjectList(QWidget *parent) : connect(m_listView, SIGNAL(showProperties(DocClipBase *)), this, SIGNAL(showClipProperties(DocClipBase *))); connect(this, SIGNAL(cancelRunningJob(const QString, stringMap )), this, SLOT(slotCancelRunningJob(const QString, stringMap))); + connect(this, SIGNAL(processLog(ProjectItem *, int , int)), this, SLOT(slotProcessLog(ProjectItem *, int , int))); + + connect(this, SIGNAL(jobCrashed(ProjectItem *, const QString &, const QString &, const QString)), this, SLOT(slotJobCrashed(ProjectItem *, const QString &, const QString &, const QString))); m_listViewDelegate = new ItemDelegate(m_listView); m_listView->setItemDelegate(m_listViewDelegate); @@ -302,6 +318,9 @@ ProjectList::~ProjectList() m_listView->blockSignals(true); m_listView->clear(); delete m_listViewDelegate; +#if KDE_IS_VERSION(4,7,0) + delete m_infoMessage; +#endif } void ProjectList::focusTree() const @@ -1664,7 +1683,7 @@ void ProjectList::slotRemoveInvalidProxy(const QString &id, bool durationError) kDebug() << "Proxy duration is wrong, try changing transcoding parameters."; emit displayMessage(i18n("Proxy clip unusable (duration is different from original)."), -2); } - item->setJobStatus(JOBCRASHED); + slotJobCrashed(item, i18n("Failed to create proxy for %1. check parameters", item->text(0)), "project_settings"); QString path = item->referencedClip()->getProperty("proxy"); KUrl proxyFolder(m_doc->projectFolder().path( KUrl::AddTrailingSlash) + "proxy/"); @@ -2490,7 +2509,7 @@ void ProjectList::slotCreateProxy(const QString id) if (!item || hasPendingProxy(item) || item->referencedClip()->isPlaceHolder()) return; QString path = item->referencedClip()->getProperty("proxy"); if (path.isEmpty()) { - setJobStatus(item, JOBCRASHED); + jobCrashed(item, i18n("Failed to create proxy, empty path.")); return; } @@ -2530,7 +2549,7 @@ void ProjectList::slotCutClipJob(const QString &id, QPoint zone) in = GenTime(in, m_timecode.fps()).frames(clipFps); out = GenTime(out, m_timecode.fps()).frames(clipFps); int max = GenTime(item->clipMaxDuration(), m_timecode.fps()).frames(clipFps); - int duration = out - in; + int duration = out - in + 1; QString timeIn = Timecode::getStringTimecode(in, clipFps, true); QString timeOut = Timecode::getStringTimecode(duration, clipFps, true); @@ -2622,10 +2641,11 @@ void ProjectList::slotProcessJobs() } // Get the list of clips that will need to get progress info - QTreeWidgetItemIterator it(m_listView); - QList processingItems; - ProjectItem *item = getItemById(job->clipId()); - processingItems.append(item); + ProjectItem *processingItem = getItemById(job->clipId()); + if (processingItem == NULL) { + delete job; + continue; + } /*while (*it && !m_abortAllJobs) { if ((*it)->type() == PROJECTCLIPTYPE) { @@ -2637,11 +2657,10 @@ void ProjectList::slotProcessJobs() ++it; }*/ - // Make sure proxy path is writable + // Make sure destination path is writable QFile file(job->destination()); if (!file.open(QIODevice::WriteOnly)) { - for (int i = 0; i < processingItems.count(); i++) - setJobStatus(processingItems.at(i), JOBCRASHED); + emit jobCrashed(processingItem, i18n("Cannot write to path: %1", job->destination())); m_processingProxy.removeAll(job->destination()); delete job; continue; @@ -2649,8 +2668,7 @@ void ProjectList::slotProcessJobs() file.close(); QFile::remove(job->destination()); - for (int i = 0; i < processingItems.count(); i++) - setJobStatus(processingItems.at(i), CREATINGJOB, 0, job->jobType); + setJobStatus(processingItem, CREATINGJOB, 0, job->jobType); bool success; QProcess *jobProcess = job->startJob(&success); @@ -2658,15 +2676,15 @@ void ProjectList::slotProcessJobs() int result = -1; if (jobProcess == NULL) { // job is finished - for (int i = 0; i < processingItems.count(); i++) - setJobStatus(processingItems.at(i), success ? JOBDONE : JOBCRASHED); if (success) { + setJobStatus(processingItem, JOBDONE); if (job->jobType == PROXYJOB) slotGotProxy(job->destination()); //TODO: set folder for transcoded clips else if (job->jobType == CUTJOB) emit addClip(job->destination(), QString(), QString()); } else { QFile::remove(job->destination()); + emit jobCrashed(processingItem, job->errorMessage()); } m_abortProxy.removeAll(job->destination()); m_processingProxy.removeAll(job->destination()); @@ -2691,7 +2709,7 @@ void ProjectList::slotProcessJobs() } else { int progress = job->processLogInfo(); - if (progress > -1) processLogInfo(processingItems, progress, job->jobType); + if (progress > -1) emit processLog(processingItem, progress, job->jobType); } jobProcess->waitForFinished(500); } @@ -2700,13 +2718,14 @@ void ProjectList::slotProcessJobs() if (result == -1) result = jobProcess->exitStatus(); if (result != -2 && QFileInfo(job->destination()).size() == 0) { - result = QProcess::CrashExit; + job->processLogInfo(); + emit jobCrashed(processingItem, i18n("Failed to create file"), QString(), job->errorMessage()); + result = -2; } if (result == QProcess::NormalExit) { // proxy successfully created - for (int i = 0; i < processingItems.count(); i++) - setJobStatus(processingItems.at(i), JOBDONE); + setJobStatus(processingItem, JOBDONE); if (job->jobType == PROXYJOB) slotGotProxy(job->destination()); //TODO: set folder for transcoded clips else if (job->jobType == CUTJOB) emit addClip(job->destination(), QString(), QString()); @@ -2714,8 +2733,7 @@ void ProjectList::slotProcessJobs() else if (result == QProcess::CrashExit) { // Proxy process crashed QFile::remove(job->destination()); - for (int i = 0; i < processingItems.count(); i++) - setJobStatus(processingItems.at(i), JOBCRASHED); + emit jobCrashed(processingItem, i18n("Job crashed"), QString(), job->errorMessage()); } delete job; continue; @@ -2731,12 +2749,6 @@ void ProjectList::slotProcessJobs() } -void ProjectList::processLogInfo(QList items, int progress, JOBTYPE jobType) -{ - for (int i = 0; i < items.count(); i++) - setJobStatus(items.at(i), CREATINGJOB, progress, jobType); -} - void ProjectList::updateProxyConfig() { ProjectItem *item; @@ -2808,6 +2820,11 @@ void ProjectList::updateProxyConfig() else delete command; } +void ProjectList::slotProcessLog(ProjectItem *item, int progress, int type) +{ + setJobStatus(item, CREATINGJOB, progress, (JOBTYPE) type); +} + void ProjectList::slotProxyCurrentItem(bool doProxy, ProjectItem *itemToProxy) { QList list; @@ -3042,6 +3059,49 @@ void ProjectList::deleteJobsForClip(const QString &clipId) } } +void ProjectList::slotJobCrashed(ProjectItem *item, const QString &label, const QString &actionName, const QString details) +{ +#if KDE_IS_VERSION(4,7,0) + m_infoMessage->animatedHide(); + item->setJobStatus(NOJOB); + m_errorLog.clear(); + m_infoMessage->setText(label); + m_infoMessage->setWordWrap(label.length() > 35); + m_infoMessage->setMessageType(KMessageWidget::Warning); + QList actions = m_infoMessage->actions(); + for (int i = 0; i < actions.count(); i++) { + m_infoMessage->removeAction(actions.at(i)); + } + + if (!actionName.isEmpty()) { + QAction *action; + QList< KActionCollection * > collections = KActionCollection::allCollections(); + for (int i = 0; i < collections.count(); i++) { + KActionCollection *coll = collections.at(i); + action = coll->action(actionName); + if (action) break; + } + if (action) m_infoMessage->addAction(action); + } + if (!details.isEmpty()) { + m_errorLog = details; + m_infoMessage->addAction(m_logAction); + } + m_infoMessage->animatedShow(); +#else + item->setJobStatus(JOBCRASHED); +#endif +} +void ProjectList::slotShowJobLog() +{ + KDialog d(this); + d.setButtons(KDialog::Close); + QTextEdit t(&d); + t.setPlainText(m_errorLog); + t.setReadOnly(true); + d.setMainWidget(&t); + d.exec(); +} #include "projectlist.moc" diff --git a/src/projectlist.h b/src/projectlist.h index a2ee232a..a0f87f3b 100644 --- a/src/projectlist.h +++ b/src/projectlist.h @@ -40,6 +40,11 @@ #include #include #include +#include + +#if KDE_IS_VERSION(4,7,0) +#include +#endif #ifdef NEPOMUK #include @@ -335,6 +340,13 @@ private: InvalidDialog *m_invalidClipDialog; QMenu *m_jobsMenu; SmallInfoLabel *m_infoLabel; +#if KDE_IS_VERSION(4,7,0) + KMessageWidget *m_infoMessage; + /** @brief A string containing the last error log for a clip job. */ + QString m_errorLog; + /** @brief The action that will trigger the log dialog. */ + QAction *m_logAction; +#endif void requestClipThumbnail(const QString id); @@ -360,8 +372,6 @@ private: * @param item The clip item to set status * @param status The job status (see definitions.h) */ void setJobStatus(ProjectItem *item, CLIPJOBSTATUS status, int progress = 0, JOBTYPE jobType = NOJOBTYPE); - /** @brief Process ffmpeg output to find out process progress. */ - void processLogInfo(QList items, int progress, JOBTYPE jobType); void monitorItemEditing(bool enable); /** @brief Get cached thumbnail for a project's clip or create it if no cache. */ void getCachedThumbnail(ProjectItem *item); @@ -418,6 +428,12 @@ private slots: void slotCancelJobs(); /** @brief Discard a running clip jobs. */ void slotCancelRunningJob(const QString id, stringMap); + /** @brief Update a clip's job status. */ + void slotProcessLog(ProjectItem *item, int progress, int); + /** @brief A clip fob crashed, inform user. */ + void slotJobCrashed(ProjectItem *item, const QString &label, const QString &actionName = QString(), const QString details = QString()); + /** @brief Display error log for last failed job. */ + void slotShowJobLog(); signals: void clipSelected(DocClipBase *, QPoint zone = QPoint(), bool forceUpdate = false); @@ -444,7 +460,9 @@ signals: /** @brief Set number of running jobs. */ void jobCount(int); void cancelRunningJob(const QString, stringMap); + void processLog(ProjectItem *, int , int); void addClip(const QString, const QString &, const QString &); + void jobCrashed(ProjectItem *item, const QString &label, const QString &actionName = QString(), const QString details = QString()); }; #endif diff --git a/src/projecttree/abstractclipjob.cpp b/src/projecttree/abstractclipjob.cpp index 64b6905a..ff630f72 100644 --- a/src/projecttree/abstractclipjob.cpp +++ b/src/projecttree/abstractclipjob.cpp @@ -44,6 +44,11 @@ const QString AbstractClipJob::clipId() const return m_clipId; } +const QString AbstractClipJob::errorMessage() const +{ + return m_errorMessage; +} + QProcess *AbstractClipJob::startJob(bool */*ok*/) { return NULL; diff --git a/src/projecttree/abstractclipjob.h b/src/projecttree/abstractclipjob.h index 6975b476..1a3955e2 100644 --- a/src/projecttree/abstractclipjob.h +++ b/src/projecttree/abstractclipjob.h @@ -40,12 +40,19 @@ public: QString m_clipId; QString description; const QString clipId() const; + const QString errorMessage() const; virtual const QString destination() const; virtual QProcess *startJob(bool */*ok*/); virtual stringMap cancelProperties(); virtual int processLogInfo(); + +protected: + QString m_errorMessage; QProcess *m_jobProcess; +private: + + signals: void jobProgress(int progress); diff --git a/src/projecttree/cutclipjob.cpp b/src/projecttree/cutclipjob.cpp index 9397e2b8..bc483b52 100644 --- a/src/projecttree/cutclipjob.cpp +++ b/src/projecttree/cutclipjob.cpp @@ -57,8 +57,11 @@ QProcess *CutClipJob::startJob(bool *ok) kDebug()<<"// STARTING CIUT JOS: "<start("ffmpeg", parameters); m_jobProcess->waitForStarted(); + QString log = m_jobProcess->readAll(); + if (!log.isEmpty()) m_errorMessage.append(log + '\n'); return m_jobProcess; } + else m_errorMessage = i18n("Cannot process this clip type."); *ok = false; return NULL; } @@ -67,7 +70,7 @@ int CutClipJob::processLogInfo() { if (!m_jobProcess || m_jobDuration == 0) return -1; QString log = m_jobProcess->readAll(); - kDebug()<waitForStarted(); + QString log = m_jobProcess->readAll(); + if (!log.isEmpty()) m_errorMessage.append(log + '\n'); return m_jobProcess; } } @@ -148,6 +150,7 @@ int ProxyJob::processLogInfo() { if (!m_jobProcess) return -1; QString log = m_jobProcess->readAll(); + if (!log.isEmpty()) m_errorMessage.append(log + '\n'); int progress; if (m_isFfmpegJob) { // Parse FFmpeg output -- 2.39.2