> > what is your vision howto proceed with those 'other calls'? >
I've found some errors in my code, but have also the impression QProcess in buggy on Linux (Kubuntu Qt 4.5.1) 1. QProcess::waitForFinished() sometimes crashes 2. QProcess::waitForFinished() sometimes does not return (splash does not disappear) 3. QProcess::started() is emitted twice (see disconnect in patch) Until nobody proves me wrong I assume QProcess is not usable synchronously. Then I try it again with Qt 7.0. Attached my changes. To reproduce the bugs just open some files an tex them. Peter -- GMX FreeDSL mit DSL 6.000 Flatrate und Telefonanschluss nur 17,95 Euro/mtl.! http://dslspecial.gmx.de/freedsl-aktionspreis/?ac=OM.AD.PD003K11308T4569a
Index: src/frontends/qt4/GuiProgress.cpp =================================================================== --- src/frontends/qt4/GuiProgress.cpp (Revision 0) +++ src/frontends/qt4/GuiProgress.cpp (Revision 0) @@ -0,0 +1,207 @@ +// -*- C++ -*- +/** + * \file GuiProgress.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Peter KÃŒmmel + * + * Full author contact details are available in file CREDITS. + */ + +#include <config.h> + +#include "GuiProgress.h" + +#include "qt_helpers.h" + +#include "support/Systemcall.h" + +#include <QApplication> +#include <QDebug> + + +namespace lyx { +namespace frontend { + + +static void processEvents() +{ + // QEventLoop::ExcludeUserInputEvents: + // don't allow user inputs while processing a document + // if we allow it, we open will Pandora's Box of multithreading + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); +} + + +GuiProgress::GuiProgress(GuiView & parent, Qt::DockWidgetArea area, + Qt::WindowFlags flags) : DockView(parent, "progress", "External tools output", area, flags) +{ + setWindowTitle(qt_("External process monitoring")); + setWidget(&text_edit); + text_edit.setReadOnly(true); +} + + +void GuiProgress::progessStarted() +{ + appendMessage("Process successfully started\n"); +} + + +void GuiProgress::progessFinished() +{ +} + + +void GuiProgress::appendMessage(QString const & msg) +{ + text_edit.insertPlainText(msg); + text_edit.ensureCursorVisible(); + processEvents(); +} + + +void GuiProgress::appendError(QString const & msg) +{ + appendMessage(msg); +} + + +void GuiProgress::clearMessages() +{ + text_edit.clear(); + processEvents(); +} + + +void GuiProgress::showEvent(QShowEvent*) +{ + // don't show splash when progress window is visible + support::ProgressInterface::setInstance(this); +} + + +void GuiProgress::hideEvent(QHideEvent*) +{ + // show splash when no progress window is visible + support::ProgressInterface::setInstance(GuiProgressSplash::instance()); +} + + +void GuiProgress::update() +{ + processEvents(); +} + + + +Dialog * createGuiProgress(GuiView & lv) +{ + GuiView & guiview = static_cast<GuiView &>(lv); +#ifdef Q_WS_MACX + // TODO where to show up on the Mac? + //return new GuiProgress(guiview, Qt::RightDockWidgetArea, Qt::Drawer); +#else + return new GuiProgress(guiview, Qt::BottomDockWidgetArea); +#endif +} + + + +GuiProgressSplash::GuiProgressSplash() : + QSplashScreen(QPixmap(":/images/banner.png"), Qt::WindowStaysOnTopHint ), + show_counter(0) +{ + wait.setSingleShot(true); + // wait before show + wait.setInterval(3000); + connect(&wait, SIGNAL(timeout()), this, SLOT(showSplash()), Qt::DirectConnection); +} + +void GuiProgressSplash::showSplash() +{ + show(); + processEvents(); +} + + +void GuiProgressSplash::progessStarted() +{ + show_counter++; + qDebug() << "++: " << show_counter; + if (!wait.isActive()) { + // does not work if we block the gui thread + // therefore call call update() + wait.start(); + } + processEvents(); +} + + +void GuiProgressSplash::progessFinished() +{ + if (show_counter > 0) + show_counter--; + qDebug() << "--: " << show_counter; + if (show_counter == 0) { + wait.stop(); + hide(); + } +} + + +void GuiProgressSplash::mousePressEvent(QMouseEvent *) +{ + //overwrite hide from QSplashScreen ? + hide(); +} + + + +void GuiProgressSplash::update() +{ + processEvents(); +} + + +void GuiProgressSplash::appendMessage(QString const & msg) +{ + QStringList lines = msg.split("\n"); + while (lines.last().isEmpty() && !lines.isEmpty()) { + lines.pop_back(); + } + showMessage(lines.last(), Qt::AlignLeft | Qt::AlignBottom); + processEvents(); +} + + +void GuiProgressSplash::appendError(QString const & msg) +{ + appendMessage(msg); +} + + +void GuiProgressSplash::clearMessages() +{ + showMessage(QString(), Qt::AlignLeft | Qt::AlignBottom); + processEvents(); +} + +GuiProgressSplash* GuiProgressSplash::splash_instance = 0; + +void GuiProgressSplash::setInstance(GuiProgressSplash* splash) +{ + splash_instance = splash; +} + +GuiProgressSplash* GuiProgressSplash::instance() +{ + return splash_instance; +} + + +} // namespace frontend +} // namespace lyx + +#include "moc_GuiProgress.cpp" \ No newline at end of file Index: src/frontends/qt4/GuiView.cpp =================================================================== --- src/frontends/qt4/GuiView.cpp (Revision 30004) +++ src/frontends/qt4/GuiView.cpp (Arbeitskopie) @@ -27,6 +27,7 @@ #include "GuiToolbar.h" #include "Menus.h" #include "TocModel.h" +#include "GuiProgress.h" #include "qt_helpers.h" @@ -69,6 +70,7 @@ #include "support/os.h" #include "support/Package.h" #include "support/Timeout.h" +#include "support/ProgressInterface.h" #include <QAction> #include <QApplication> @@ -173,6 +175,10 @@ stack_widget_->addWidget(bg_widget_); stack_widget_->addWidget(splitter_); setBackground(); + + splash_ = new GuiProgressSplash; + GuiProgressSplash::setInstance(splash_); + support::ProgressInterface::setInstance(splash_); } ~GuiViewPrivate() @@ -180,6 +186,8 @@ delete splitter_; delete bg_widget_; delete stack_widget_; + support::ProgressInterface::setInstance(0); + delete splash_; } QMenu * toolBarPopup(GuiView * parent) @@ -284,6 +292,8 @@ /// TocModels toc_models_; + + GuiProgressSplash* splash_; }; @@ -1267,7 +1277,9 @@ enable = name == "aboutlyx" || name == "file" //FIXME: should be removed. || name == "prefs" - || name == "texinfo"; + || name == "texinfo" + || name == "texinfo" + || name == "progress"; else if (name == "print") enable = buf->isExportable("dvi") && lyxrc.print_command != "none"; @@ -2423,7 +2435,7 @@ "mathmatrix", "mathspace", "nomenclature", "nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref", "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate", "thesaurus", "texinfo", -"toc", "view-source", "vspace", "wrap" }; +"toc", "view-source", "vspace", "wrap", "progress" }; char const * const * const end_dialognames = dialognames + (sizeof(dialognames) / sizeof(char *)); @@ -2630,6 +2642,7 @@ Dialog * createGuiVSpace(GuiView & lv); Dialog * createGuiViewSource(GuiView & lv); Dialog * createGuiWrap(GuiView & lv); +Dialog * createGuiProgress(GuiView & lv); Dialog * GuiView::build(string const & name) @@ -2732,6 +2745,8 @@ return createGuiVSpace(*this); if (name == "wrap") return createGuiWrap(*this); + if (name == "progress") + return createGuiProgress(*this); return 0; } Index: src/frontends/qt4/GuiProgress.h =================================================================== --- src/frontends/qt4/GuiProgress.h (Revision 0) +++ src/frontends/qt4/GuiProgress.h (Revision 0) @@ -0,0 +1,104 @@ +// -*- C++ -*- +/** + * \file GuiProgress.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Peter KÃŒmmel + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef GUIPROGRESS_H +#define GUIPROGRESS_H + +#include "support/ProgressInterface.h" + +#include "DockView.h" + +#include <QTextEdit> +#include <QSplashScreen> +#include <QTimer> + +#include <string> + + +namespace lyx { +namespace frontend { + + +class GuiProgress : + public DockView, + public lyx::support::ProgressInterface +{ + +public: + GuiProgress( + GuiView & parent, ///< the main window where to dock. + Qt::DockWidgetArea area, ///< Position of the dock (and also drawer) + Qt::WindowFlags flags = 0); + + void progessStarted(); + void progessFinished(); + void appendMessage(QString const &); + void appendError(QString const &); + void clearMessages(); + void update(); + + /// Controller inherited method. + ///@{ + bool initialiseParams(std::string const &) { return true; } + void clearParams() {} + void dispatchParams() {} + bool isBufferDependent() const { return false; } + bool canApply() const { return true; } + bool canApplyToReadOnly() const { return true; } + void updateView() {} + ///@} + + + void showEvent(QShowEvent*); + void hideEvent(QHideEvent*); + +private: + QTextEdit text_edit; +}; + + +class GuiProgressSplash : + public QSplashScreen, + public lyx::support::ProgressInterface +{ + + Q_OBJECT + +public: + GuiProgressSplash(); + + void progessStarted(); + void progessFinished(); + void appendMessage(QString const &); + void appendError(QString const &); + void clearMessages(); + void update(); + + static GuiProgressSplash* instance(); + static void setInstance(GuiProgressSplash*); + +private: + int show_counter; + QTimer wait; + + void mousePressEvent(QMouseEvent *); + + static GuiProgressSplash* splash_instance; + +private Q_SLOTS: + void showSplash(); +}; + +} // namespace frontend +} // namespace lyx + +#endif + Index: src/frontends/qt4/Makefile.am =================================================================== --- src/frontends/qt4/Makefile.am (Revision 30004) +++ src/frontends/qt4/Makefile.am (Arbeitskopie) @@ -107,6 +107,7 @@ GuiPrint.cpp \ GuiPrintindex.cpp \ GuiPrintNomencl.cpp \ + GuiProgress.cpp \ GuiRef.cpp \ GuiSearch.cpp \ GuiSelection.cpp \ @@ -205,6 +206,7 @@ GuiPrint.h \ GuiPrintindex.h \ GuiPrintNomencl.h \ + GuiProgress.h \ GuiRef.h \ GuiSearch.h \ GuiSelection.h \ Index: src/support/Systemcall.cpp =================================================================== --- src/support/Systemcall.cpp (Revision 30004) +++ src/support/Systemcall.cpp (Arbeitskopie) @@ -4,9 +4,9 @@ * Licence details can be found in the file COPYING. * * \author Asger Alstrup - * - * Interface cleaned up by * \author Angus Leeming + * \author Enrico Forestieri + * \author Peter KÃŒmmel * * Full author contact details are available in file CREDITS. */ @@ -19,11 +19,15 @@ #include "support/Systemcall.h" #include "support/SystemcallPrivate.h" #include "support/os.h" +#include "support/ProgressInterface.h" #include <cstdlib> #include <iostream> #include <QProcess> +#include <QEventLoop> +#include <QTimer> +#include <QDebug> #define USE_QPROCESS @@ -42,6 +46,75 @@ } + +class ProgressDummy : public ProgressInterface +{ +public: + ProgressDummy() {} + + void progessStarted() {} + void progessFinished() {} + void appendMessage(QString const &) {} + void appendError(QString const &) {} + void clearMessages() {} + void update() {} +}; + + +static ProgressInterface* progress_instance = 0; + +void ProgressInterface::setInstance(ProgressInterface* p) +{ + progress_instance = p; +} + +ProgressInterface* ProgressInterface::instance() +{ + if (!progress_instance) { + static ProgressDummy dummy; + return &dummy; + } + return progress_instance; +} + + + +ShowProgress::ShowProgress(std::string const & startmessage) +{ + ProgressInterface::instance()->progessStarted(); + ProgressInterface::instance()->appendMessage(startmessage.c_str()); //translate +} + + +ShowProgress::~ShowProgress() +{ + done(); +} + + +void ShowProgress::done() +{ + if (!finishedmessage.empty()) { + ProgressInterface::instance()->appendMessage(finishedmessage.c_str()); + } + ProgressInterface::instance()->progessFinished(); +} + + +void ShowProgress::setEndMessage(std::string const & msg) +{ + finishedmessage = msg; +} + +void ShowProgress::update() +{ + ProgressInterface::instance()->update(); +} + + + + + // Reuse of instance #ifndef USE_QPROCESS int Systemcall::startscript(Starttype how, string const & what) @@ -105,29 +178,40 @@ if (os::is_terminal(os::STDERR)) console.showerr(); + ProgressInterface* progress = ProgressInterface::instance(); + + progress->appendMessage("************************************\n"); + progress->appendMessage("Starting process:\n"); + progress->appendMessage(cmd + "\n"); + process->start(cmd); +qDebug() << _LINE__; if (!process->waitForStarted(3000)) { - LYXERR0("Qprocess " << cmd << " did not start!"); + LYXERR0("QProcess " << cmd << " did not start!"); LYXERR0("error " << process->error()); LYXERR0("state " << process->state()); LYXERR0("status " << process->exitStatus()); +qDebug() << _LINE__; return 10; } + if (how == DontWait) { // TODO delete process later return 0; } - - if (!process->waitForFinished(180000)) { - LYXERR0("Qprocess " << cmd << " did not finished!"); +qDebug() << _LINE__; + if (process->state() != QProcess::Running << !process->waitForFinished(180000)) { + LYXERR0("QProcess " << cmd << " did not finished!"); LYXERR0("error " << process->error()); LYXERR0("state " << process->state()); LYXERR0("status " << process->exitStatus()); +qDebug() << _LINE__; return 20; } + int const exit_code = process->exitCode(); if (exit_code) { - LYXERR0("Qprocess " << cmd << " finished!"); + LYXERR0("QProcess " << cmd << " finished!"); LYXERR0("exitCode " << process->exitCode()); LYXERR0("error " << process->error()); LYXERR0("state " << process->state()); @@ -144,7 +228,9 @@ cerr << fromqstr(QString::fromLocal8Bit( process->readAllStandardError().data())); +qDebug() << _LINE__; killProcess(process); +qDebug() << _LINE__; return exit_code; } @@ -154,6 +240,11 @@ { connect(proc, SIGNAL(readyReadStandardOutput()), SLOT(stdOut())); connect(proc, SIGNAL(readyReadStandardError()), SLOT(stdErr())); + connect(proc, SIGNAL(error(QProcess::ProcessError)), + SLOT(processError(QProcess::ProcessError))); + connect(proc, SIGNAL(started()), this, SLOT(processStarted())); + connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), + SLOT(processFinished(int, QProcess::ExitStatus))); } @@ -185,6 +276,7 @@ outdata_[outindex_] = '\0'; outindex_ = 0; cout << outdata_; + ProgressInterface::instance()->appendMessage(QString::fromLocal8Bit(outdata_)); } } } @@ -202,11 +294,73 @@ errdata_[errindex_] = '\0'; errindex_ = 0; cerr << errdata_; + ProgressInterface::instance()->appendError(QString::fromLocal8Bit(errdata_)); } } } } + +void ConOut::processError(QProcess::ProcessError err) +{ + QString message; + switch (err) { + case QProcess::FailedToStart: + message = "The process failed to start. Either the invoked program is missing, " + "or you may have insufficient permissions to invoke the program."; + break; + case QProcess::Crashed: + message = "The process crashed some time after starting successfully."; + break; + case QProcess::Timedout: + message = "The process timed out. It might be restarted automatically."; + break; + case QProcess::WriteError: + message = "An error occurred when attempting to write to the process-> For example, " + "the process may not be running, or it may have closed its input channel."; + break; + case QProcess::ReadError: + message = "An error occurred when attempting to read from the process-> For example, " + "the process may not be running."; + break; + case QProcess::UnknownError: + default: + message = "An unknown error occured."; + break; + } + ProgressInterface::instance()->appendMessage("\nThe process failed: " + message + '\n'); +} + + +void ConOut::processStarted() +{ + // why do we get two started signals? + disconnect(proc_, SIGNAL(started()), this, SLOT(processStarted())); + ProgressInterface::instance()->progessStarted(); +} + + +void ConOut::processFinished(int, QProcess::ExitStatus status) +{ + ProgressInterface* progress = ProgressInterface::instance(); + + QString message; + switch (status) { + case QProcess::NormalExit: + message = "The process exited normally."; + break; + case QProcess::CrashExit: + message = "The process crashed."; + break; + default: + message = "Unknown exit state."; + break; + } + progress->appendMessage("Process finished: " + message + '\n'); + progress->progessFinished(); +} + + #include "moc_SystemcallPrivate.cpp" #endif Index: src/support/Systemcall.h =================================================================== --- src/support/Systemcall.h (Revision 30004) +++ src/support/Systemcall.h (Arbeitskopie) @@ -20,6 +20,7 @@ namespace lyx { namespace support { + /** * An instance of Class Systemcall represents a single child process. * @@ -43,6 +44,8 @@ * by spaces. */ int startscript(Starttype how, std::string const & what); + +private: }; } // namespace support Index: src/support/ProgressInterface.h =================================================================== --- src/support/ProgressInterface.h (Revision 0) +++ src/support/ProgressInterface.h (Revision 0) @@ -0,0 +1,62 @@ +// -*- C++ -*- +/** + * \file ProgressInterface.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Peter KÃŒmmel + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef LYX_SUPPORT_PROGRESSINTERFACE_H +#define LYX_SUPPORT_PROGRESSINTERFACE_H + +#include <string> + +class QString; + +namespace lyx { +namespace support { + + +class ProgressInterface +{ +public: + virtual ~ProgressInterface() {} + + virtual void progessStarted() = 0; + virtual void progessFinished() = 0; + virtual void appendMessage(QString const &) = 0; + virtual void appendError(QString const &) = 0; + virtual void clearMessages() = 0; + virtual void update() = 0; + + static void setInstance(ProgressInterface*); + static ProgressInterface* instance(); + +protected: + ProgressInterface() {} +}; + + +class ShowProgress +{ +public: + ShowProgress(std::string const & startmessage); + ~ShowProgress(); + + void setEndMessage(std::string const & msg); + void done(); + void update(); + +private: + std::string finishedmessage; +}; + + +} // namespace support +} // namespace lyx + +#endif // LYX_SUPPORT_PROGRESSINTERFACE_H + Index: src/support/SystemcallPrivate.h =================================================================== --- src/support/SystemcallPrivate.h (Revision 30004) +++ src/support/SystemcallPrivate.h (Arbeitskopie) @@ -14,7 +14,7 @@ #include <QObject> -class QProcess; +#include <QProcess> namespace lyx { namespace support { @@ -58,6 +58,9 @@ public Q_SLOTS: void stdOut(); void stdErr(); + void processError(QProcess::ProcessError); + void processStarted(); + void processFinished(int, QProcess::ExitStatus status); }; } // namespace support Index: src/support/Makefile.am =================================================================== --- src/support/Makefile.am (Revision 30004) +++ src/support/Makefile.am (Arbeitskopie) @@ -77,6 +77,7 @@ Path.h \ Package.cpp \ Package.h \ + ProgressInterface.h \ qstring_helpers.cpp \ qstring_helpers.h \ socktools.cpp \ Index: src/Buffer.cpp =================================================================== --- src/Buffer.cpp (Revision 30004) +++ src/Buffer.cpp (Arbeitskopie) @@ -101,6 +101,7 @@ #include "support/Systemcall.h" #include "support/textutils.h" #include "support/types.h" +#include "support/ProgressInterface.h" #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> @@ -590,6 +591,8 @@ // Returns true if "\end_document" is not read (Asger) bool Buffer::readDocument(Lexer & lex) { + ShowProgress progress("Loading document"); + ErrorList & errorList = d->errorLists["Parse"]; errorList.clear(); @@ -603,6 +606,7 @@ LASSERT(paragraphs().empty(), /**/); readHeader(lex); + progress.update(); if (params().outputChanges) { bool dvipost = LaTeXFeatures::isAvailable("dvipost"); @@ -656,8 +660,11 @@ // read main text bool const res = text().read(*this, lex, errorList, d->inset); + progress.update(); updateMacros(); + progress.update(); + updateMacroInstances(); return res; } @@ -744,6 +751,8 @@ bool Buffer::readFile(FileName const & filename) { + ShowProgress progress("Exporting document"); + FileName fname(filename); params().compressed = fname.isZippedFile(); @@ -2759,6 +2768,8 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, string & result_file) const { + ShowProgress progress("Exporting document"); + string backend_format; OutputParams runparams(¶ms().encoding()); runparams.flavor = OutputParams::LATEX; Index: lib/ui/stdmenus.inc =================================================================== --- lib/ui/stdmenus.inc (Revision 30004) +++ lib/ui/stdmenus.inc (Arbeitskopie) @@ -310,6 +310,7 @@ Item "Split View Into Upper And Lower Half|e" "split-view vertical" Item "Close Tab Group|G" "close-tab-group" Item "Fullscreen|l" "ui-toggle fullscreen" + Item "Show process messages" "dialog-toggle progress" Submenu "Toolbars|b" "toolbars" Separator Documents