ReportView.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00024 #include "PreCompiled.h"
00025 #ifndef _PreComp_
00026 # include <QGridLayout>
00027 # include <QApplication>
00028 # include <QMenu>
00029 # include <QContextMenuEvent>
00030 # include <QTextCursor>
00031 # include <QTextStream>
00032 #endif
00033 
00034 #include <Base/Interpreter.h>
00035 #include "ReportView.h"
00036 #include "FileDialog.h"
00037 #include "PythonConsole.h"
00038 #include "PythonConsolePy.h"
00039 #include "BitmapFactory.h"
00040 
00041 using namespace Gui;
00042 using namespace Gui::DockWnd;
00043 
00044 /* TRANSLATOR Gui::DockWnd::ReportView */
00045 
00050 ReportView::ReportView( QWidget* parent )
00051   : QWidget(parent)
00052 {
00053     setObjectName(QLatin1String("ReportOutput"));
00054 
00055     resize( 529, 162 );
00056     QGridLayout* tabLayout = new QGridLayout( this );
00057     tabLayout->setSpacing( 0 );
00058     tabLayout->setMargin( 0 );
00059 
00060     tabWidget = new QTabWidget( this );
00061     tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
00062     tabWidget->setTabPosition(QTabWidget::South);
00063     tabWidget->setTabShape(QTabWidget::Rounded);
00064     tabLayout->addWidget( tabWidget, 0, 0 );
00065 
00066 
00067     // create the output window
00068     tabOutput = new ReportOutput();
00069     tabOutput->setWindowTitle(trUtf8("Output"));
00070     tabOutput->setWindowIcon(BitmapFactory().pixmap("MacroEditor"));
00071     int output = tabWidget->addTab(tabOutput, tabOutput->windowTitle());
00072     tabWidget->setTabIcon(output, tabOutput->windowIcon());
00073 
00074     // create the python console
00075     tabPython = new PythonConsole();
00076     tabPython->setWordWrapMode(QTextOption::NoWrap);
00077     tabPython->setWindowTitle(trUtf8("Python console"));
00078     tabPython->setWindowIcon(BitmapFactory().pixmap("python_small"));
00079     int python = tabWidget->addTab(tabPython, tabPython->windowTitle());
00080     tabWidget->setTabIcon(python, tabPython->windowIcon());
00081     tabWidget->setCurrentIndex(0);
00082 
00083     // raise the tab page set in the preferences
00084     ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
00085     int index = hGrp->GetInt("AutoloadTab", 0);
00086     tabWidget->setCurrentIndex(index);
00087 }
00088 
00092 ReportView::~ReportView()
00093 {
00094     // no need to delete child widgets, Qt does it all for us
00095 }
00096 
00097 void ReportView::changeEvent(QEvent *e)
00098 {
00099     QWidget::changeEvent(e);
00100     if (e->type() == QEvent::LanguageChange) {
00101         tabOutput->setWindowTitle(trUtf8("Output"));
00102         tabPython->setWindowTitle(trUtf8("Python console"));
00103         for (int i=0; i<tabWidget->count();i++)
00104             tabWidget->setTabText(i, tabWidget->widget(i)->windowTitle());
00105     }
00106 }
00107 
00108 // ----------------------------------------------------------
00109 
00110 namespace Gui {
00111 struct TextBlockData : public QTextBlockUserData
00112 {
00113     struct State {
00114         int length;
00115         ReportHighlighter::Paragraph type;
00116     };
00117     QVector<State> block;
00118 };
00119 }
00120 
00121 ReportHighlighter::ReportHighlighter(QTextEdit* edit)
00122   : QSyntaxHighlighter(edit), type(Message)
00123 {
00124     txtCol = Qt::black;
00125     logCol = Qt::blue;
00126     warnCol = QColor(255, 170, 0);
00127     errCol = Qt::red;
00128 }
00129 
00130 ReportHighlighter::~ReportHighlighter()
00131 {
00132 }
00133 
00134 void ReportHighlighter::highlightBlock (const QString & text)
00135 {
00136     if (text.isEmpty())
00137         return;
00138     TextBlockData* ud = static_cast<TextBlockData*>(this->currentBlockUserData());
00139     if (!ud) {
00140         ud = new TextBlockData;
00141         this->setCurrentBlockUserData(ud);
00142     }
00143 
00144     TextBlockData::State b;
00145     b.length = text.length();
00146     b.type = this->type;
00147     ud->block.append(b);
00148 
00149     QVector<TextBlockData::State> block = ud->block;
00150     int start = 0;
00151     for (QVector<TextBlockData::State>::Iterator it = block.begin(); it != block.end(); ++it) {
00152         switch (it->type)
00153         {
00154         case Message:
00155             setFormat(start, it->length-start, txtCol);
00156             break;
00157         case Warning:
00158             setFormat(start, it->length-start, warnCol);
00159             break;
00160         case Error:
00161             setFormat(start, it->length-start, errCol);
00162             break;
00163         case LogText:
00164             setFormat(start, it->length-start, logCol);
00165             break;
00166         default:
00167             break;
00168         }
00169 
00170         start = it->length;
00171     }
00172 }
00173 
00174 void ReportHighlighter::setParagraphType(ReportHighlighter::Paragraph t)
00175 {
00176     type = t;
00177 }
00178 
00179 void ReportHighlighter::setTextColor( const QColor& col )
00180 {
00181     txtCol = col;
00182 }
00183 
00184 void ReportHighlighter::setLogColor( const QColor& col )
00185 {
00186     logCol = col;
00187 }
00188 
00189 void ReportHighlighter::setWarningColor( const QColor& col )
00190 {
00191     warnCol = col;
00192 }
00193 
00194 void ReportHighlighter::setErrorColor( const QColor& col )
00195 {
00196     errCol = col;
00197 }
00198 
00199 // ----------------------------------------------------------
00200 
00209 class CustomReportEvent : public QEvent
00210 {
00211 public:
00212     CustomReportEvent(ReportHighlighter::Paragraph p, const QString& s)
00213     : QEvent(QEvent::Type(QEvent::User)) 
00214     { par = p; msg = s;}
00215     ~CustomReportEvent()
00216     { }
00217     const QString& message() const
00218     { return msg; }
00219     ReportHighlighter::Paragraph messageType() const
00220     { return par; }
00221 private:
00222     ReportHighlighter::Paragraph par;
00223     QString msg;
00224 };
00225 
00226 // ----------------------------------------------------------
00227 
00228 class ReportOutput::Data
00229 {
00230 public:
00231     Data()
00232     {
00233         if (!default_stdout) {
00234             Base::PyGILStateLocker lock;
00235             default_stdout = PySys_GetObject(const_cast<char*>("stdout"));
00236             replace_stdout = new OutputStdout();
00237             redirected_stdout = false;
00238         }
00239 
00240         if (!default_stderr) {
00241             Base::PyGILStateLocker lock;
00242             default_stderr = PySys_GetObject(const_cast<char*>("stderr"));
00243             replace_stderr = new OutputStderr();
00244             redirected_stderr = false;
00245         }
00246     }
00247     ~Data()
00248     {
00249         if (replace_stdout) {
00250             Py_DECREF(replace_stdout);
00251             replace_stdout = 0;
00252         }
00253 
00254         if (replace_stderr) {
00255             Py_DECREF(replace_stderr);
00256             replace_stderr = 0;
00257         }
00258     }
00259 
00260     // make them static because redirection should done only once
00261     static bool redirected_stdout;
00262     static PyObject* default_stdout;
00263     static PyObject* replace_stdout;
00264 
00265     static bool redirected_stderr;
00266     static PyObject* default_stderr;
00267     static PyObject* replace_stderr;
00268 };
00269 
00270 bool ReportOutput::Data::redirected_stdout = false;
00271 PyObject* ReportOutput::Data::default_stdout = 0;
00272 PyObject* ReportOutput::Data::replace_stdout = 0;
00273 
00274 bool ReportOutput::Data::redirected_stderr = false;
00275 PyObject* ReportOutput::Data::default_stderr = 0;
00276 PyObject* ReportOutput::Data::replace_stderr = 0;
00277 
00278 /* TRANSLATOR Gui::DockWnd::ReportOutput */
00279 
00284 ReportOutput::ReportOutput(QWidget* parent)
00285   : QTextEdit(parent), WindowParameter("OutputWindow"), d(new Data), gotoEnd(false)
00286 {
00287     bLog = false;
00288     reportHl = new ReportHighlighter(this);
00289 
00290     restoreFont();
00291     setReadOnly(true);
00292     clear();
00293     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00294 
00295     Base::Console().AttachObserver(this);
00296     getWindowParameter()->Attach(this);
00297 
00298     getWindowParameter()->NotifyAll();
00299     _prefs = WindowParameter::getDefaultParameter()->GetGroup("Editor");
00300     _prefs->Attach(this);
00301     _prefs->Notify("FontSize");
00302 
00303     // scroll to bottom at startup to make sure that last appended text is visible
00304     ensureCursorVisible();
00305 }
00306 
00310 ReportOutput::~ReportOutput()
00311 {
00312     getWindowParameter()->Detach(this);
00313     _prefs->Detach(this);
00314     Base::Console().DetachObserver(this);
00315     delete reportHl;
00316     delete d;
00317 }
00318 
00319 void ReportOutput::restoreFont()
00320 {
00321     QFont serifFont(QLatin1String("Courier"), 10, QFont::Normal);
00322     setFont(serifFont);
00323 }
00324 
00325 void ReportOutput::Warning(const char * s)
00326 {
00327     // Send the event to itself to allow thread-safety. Qt will delete it when done.
00328     CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::Warning, QString::fromUtf8(s));
00329     QApplication::postEvent(this, ev);
00330 }
00331 
00332 void ReportOutput::Message(const char * s)
00333 {
00334     // Send the event to itself to allow thread-safety. Qt will delete it when done.
00335     CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::Message, QString::fromUtf8(s));
00336     QApplication::postEvent(this, ev);
00337 }
00338 
00339 void ReportOutput::Error  (const char * s)
00340 {
00341     // Send the event to itself to allow thread-safety. Qt will delete it when done.
00342     CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::Error, QString::fromUtf8(s));
00343     QApplication::postEvent(this, ev);
00344 }
00345 
00346 void ReportOutput::Log (const char * s)
00347 {
00348     QString msg = QString::fromUtf8(s);
00349     if (msg.length() < 1000){
00350         // Send the event to itself to allow thread-safety. Qt will delete it when done.
00351         CustomReportEvent* ev = new CustomReportEvent(ReportHighlighter::LogText, msg);
00352         QApplication::postEvent(this, ev);
00353     }
00354 }
00355 
00356 void ReportOutput::customEvent ( QEvent* ev )
00357 {
00358     // Appends the text stored in the event to the text view
00359     if ( ev->type() ==  QEvent::User ) {
00360         CustomReportEvent* ce = (CustomReportEvent*)ev;
00361         reportHl->setParagraphType(ce->messageType());
00362 
00363         QTextCursor cursor(this->document());
00364         cursor.beginEditBlock();
00365         cursor.movePosition(QTextCursor::End);
00366         cursor.insertText(ce->message());
00367         cursor.endEditBlock();
00368         if (gotoEnd) {
00369             setTextCursor(cursor);
00370         }
00371         ensureCursorVisible();
00372     }
00373 }
00374 
00375 void ReportOutput::contextMenuEvent ( QContextMenuEvent * e )
00376 {
00377     QMenu* menu = createStandardContextMenu();
00378     QAction* first = menu->actions().front();
00379 
00380     QMenu* submenu = new QMenu( menu );
00381     QAction* logAct = submenu->addAction(tr("Logging"), this, SLOT(onToggleLogging()));
00382     logAct->setCheckable(true);
00383     logAct->setChecked(bLog);
00384 
00385     QAction* wrnAct = submenu->addAction(tr("Warning"), this, SLOT(onToggleWarning()));
00386     wrnAct->setCheckable(true);
00387     wrnAct->setChecked(bWrn);
00388 
00389     QAction* errAct = submenu->addAction(tr("Error"), this, SLOT(onToggleError()));
00390     errAct->setCheckable(true);
00391     errAct->setChecked(bErr);
00392 
00393     submenu->addSeparator();
00394 
00395     QAction* stdoutAct = submenu->addAction(tr("Redirect Python output"), this, SLOT(onToggleRedirectPythonStdout()));
00396     stdoutAct->setCheckable(true);
00397     stdoutAct->setChecked(d->redirected_stdout);
00398 
00399     QAction* stderrAct = submenu->addAction(tr("Redirect Python errors"), this, SLOT(onToggleRedirectPythonStderr()));
00400     stderrAct->setCheckable(true);
00401     stderrAct->setChecked(d->redirected_stderr);
00402 
00403     submenu->addSeparator();
00404     QAction* botAct = submenu->addAction(tr("Go to end"), this, SLOT(onToggleGoToEnd()));
00405     botAct->setCheckable(true);
00406     botAct->setChecked(gotoEnd);
00407 
00408     submenu->setTitle(tr("Options"));
00409     menu->insertMenu(first, submenu);
00410     menu->insertSeparator(first);
00411 
00412     menu->addAction(tr("Clear"), this, SLOT(clear()));
00413     menu->addSeparator();
00414     menu->addAction(tr("Save As..."), this, SLOT(onSaveAs()));
00415 
00416     menu->exec(e->globalPos());
00417     delete menu;
00418 }
00419 
00420 void ReportOutput::onSaveAs()
00421 {
00422     QString fn = QFileDialog::getSaveFileName(this, tr("Save Report Output"), QString(), tr("Plain Text Files (*.txt *.log)"));
00423     if (!fn.isEmpty()) {
00424         QFileInfo fi(fn);
00425         if (fi.completeSuffix().isEmpty())
00426             fn += QLatin1String(".log");
00427         QFile f(fn);
00428         if (f.open(QIODevice::WriteOnly)) {
00429             QTextStream t (&f);
00430             t << toPlainText();
00431             f.close();
00432         }
00433     }
00434 }
00435 
00436 bool ReportOutput::isError() const
00437 {
00438     return bErr;
00439 }
00440 
00441 bool ReportOutput::isWarning() const
00442 {
00443     return bWrn;
00444 }
00445 
00446 bool ReportOutput::isLogging() const
00447 {
00448     return bLog;
00449 }
00450 
00451 void ReportOutput::onToggleError()
00452 {
00453     bErr = bErr ? false : true;
00454     getWindowParameter()->SetBool( "checkError", bErr );
00455 }
00456 
00457 void ReportOutput::onToggleWarning()
00458 {
00459     bWrn = bWrn ? false : true;
00460     getWindowParameter()->SetBool( "checkWarning", bWrn );
00461 }
00462 
00463 void ReportOutput::onToggleLogging()
00464 {
00465     bLog = bLog ? false : true;
00466     getWindowParameter()->SetBool( "checkLogging", bLog );
00467 }
00468 
00469 void ReportOutput::onToggleRedirectPythonStdout()
00470 {
00471     if (d->redirected_stdout) {
00472         d->redirected_stdout = false;
00473         Base::PyGILStateLocker lock;
00474         PySys_SetObject(const_cast<char*>("stdout"), d->default_stdout);
00475     }
00476     else {
00477         d->redirected_stdout = true;
00478         Base::PyGILStateLocker lock;
00479         PySys_SetObject(const_cast<char*>("stdout"), d->replace_stdout);
00480     }
00481 
00482     getWindowParameter()->SetBool("RedirectPythonOutput", d->redirected_stdout);
00483 }
00484 
00485 void ReportOutput::onToggleRedirectPythonStderr()
00486 {
00487     if (d->redirected_stderr) {
00488         d->redirected_stderr = false;
00489         Base::PyGILStateLocker lock;
00490         PySys_SetObject(const_cast<char*>("stderr"), d->default_stderr);
00491     }
00492     else {
00493         d->redirected_stderr = true;
00494         Base::PyGILStateLocker lock;
00495         PySys_SetObject(const_cast<char*>("stderr"), d->replace_stderr);
00496     }
00497 
00498     getWindowParameter()->SetBool("RedirectPythonErrors", d->redirected_stderr);
00499 }
00500 
00501 void ReportOutput::onToggleGoToEnd()
00502 {
00503     gotoEnd = gotoEnd ? false : true;
00504     getWindowParameter()->SetBool( "checkGoToEnd", gotoEnd );
00505 }
00506 
00507 void ReportOutput::OnChange(Base::Subject<const char*> &rCaller, const char * sReason)
00508 {
00509     ParameterGrp& rclGrp = ((ParameterGrp&)rCaller);
00510     if (strcmp(sReason, "checkLogging") == 0) {
00511         bLog = rclGrp.GetBool( sReason, bLog );
00512     }
00513     else if (strcmp(sReason, "checkWarning") == 0) {
00514         bWrn = rclGrp.GetBool( sReason, bWrn );
00515     }
00516     else if (strcmp(sReason, "checkError") == 0) {
00517         bErr = rclGrp.GetBool( sReason, bErr );
00518     }
00519     else if (strcmp(sReason, "colorText") == 0) {
00520         unsigned long col = rclGrp.GetUnsigned( sReason );
00521         reportHl->setTextColor( QColor( (col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff) );
00522     }
00523     else if (strcmp(sReason, "colorLogging") == 0) {
00524         unsigned long col = rclGrp.GetUnsigned( sReason );
00525         reportHl->setLogColor( QColor( (col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff) );
00526     }
00527     else if (strcmp(sReason, "colorWarning") == 0) {
00528         unsigned long col = rclGrp.GetUnsigned( sReason );
00529         reportHl->setWarningColor( QColor( (col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff) );
00530     }
00531     else if (strcmp(sReason, "colorError") == 0) {
00532         unsigned long col = rclGrp.GetUnsigned( sReason );
00533         reportHl->setErrorColor( QColor( (col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff) );
00534     }
00535     else if (strcmp(sReason, "checkGoToEnd") == 0) {
00536         gotoEnd = rclGrp.GetBool(sReason, gotoEnd);
00537     }
00538     else if (strcmp(sReason, "FontSize") == 0 || strcmp(sReason, "Font") == 0) {
00539         int fontSize = rclGrp.GetInt("FontSize", 10);
00540         QString fontFamily = QString::fromAscii(rclGrp.GetASCII("Font", "Courier").c_str());
00541         
00542         QFont font(fontFamily, fontSize);
00543         setFont(font);
00544         QFontMetrics metric(font);
00545         int width = metric.width(QLatin1String("0000"));
00546         setTabStopWidth(width);
00547     }
00548     else if (strcmp(sReason, "RedirectPythonOutput") == 0) {
00549         bool checked = rclGrp.GetBool(sReason);
00550         if (checked != d->redirected_stdout)
00551             onToggleRedirectPythonStdout();
00552     }
00553     else if (strcmp(sReason, "RedirectPythonErrors") == 0) {
00554         bool checked = rclGrp.GetBool(sReason);
00555         if (checked != d->redirected_stderr)
00556             onToggleRedirectPythonStderr();
00557     }
00558 }
00559 
00560 #include "moc_ReportView.cpp"

Generated on Wed Nov 23 19:00:35 2011 for FreeCAD by  doxygen 1.6.1