Browse Source

Add ctkErrorLogModel ans associated Qt, Stream and FD message handlers

Qt: Allows to catch qDebug, qWarning, ...
Stream: Allows to catch message printed using std::cout, std::cerr
FD: Allows to catch messages printed using frpintf(stdout, ...) or fprintf(stderr,...)
Jean-Christophe Fillion-Robin 14 years ago
parent
commit
68f1aba6ed

+ 12 - 1
Libs/Core/CMakeLists.txt

@@ -30,6 +30,15 @@ SET(KIT_SRCS
   ctkCommandLineParser.h
   ctkDependencyGraph.cpp
   ctkDependencyGraph.h
+  ctkErrorLogModel.cpp
+  ctkErrorLogModel.h
+  ctkErrorLogFDMessageHandler.cpp
+  ctkErrorLogFDMessageHandler.h
+  ctkErrorLogFDMessageHandler_p.h
+  ctkErrorLogQtMessageHandler.cpp
+  ctkErrorLogQtMessageHandler.h
+  ctkErrorLogStreamMessageHandler.cpp
+  ctkErrorLogStreamMessageHandler.h
   ctkLogger.cpp
   ctkLogger.h
   ctkHistogram.cpp
@@ -74,7 +83,9 @@ ENDIF()
 # Headers that should run through moc
 SET(KIT_MOC_SRCS
   ctkCommandLineParser.h
-  ctkLogger.h 
+  ctkErrorLogFDMessageHandler_p.h
+  ctkErrorLogModel.h
+  ctkLogger.h
   ctkHistogram.h
   ctkModelTester.h
   ctkTransferFunction.h

+ 217 - 0
Libs/Core/ctkErrorLogFDMessageHandler.cpp

@@ -0,0 +1,217 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+
+// CTK includes
+#include "ctkErrorLogFDMessageHandler.h"
+#include "ctkErrorLogFDMessageHandler_p.h"
+
+// STD includes
+#include <cstdio>
+#ifdef Q_OS_WIN32
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+// --------------------------------------------------------------------------
+// ctkFDHandler methods
+
+// --------------------------------------------------------------------------
+ctkFDHandler::ctkFDHandler(ctkErrorLogFDMessageHandler* messageHandler,
+                           ctkErrorLogModel::LogLevel logLevel,
+                           int fileDescriptorNumber)
+{
+  this->MessageHandler = messageHandler;
+  this->LogLevel = logLevel;
+  this->FDNumber = fileDescriptorNumber;
+  this->SavedFDNumber = 0;
+  this->FD = 0;
+  this->Enabled = false;
+
+  const QString outputFileTemplateName =  QDir::tempPath()
+    + QDir::separator () + "ctkFDHandler-%1.XXXXXX.txt";
+  this->OutputFile.setFileTemplate(outputFileTemplateName.arg(this->FDNumber));
+
+  connect(&this->OutputFileWatcher, SIGNAL(fileChanged(const QString&)),
+          SLOT(outputFileChanged(const QString&)));
+}
+
+// --------------------------------------------------------------------------
+FILE* ctkFDHandler::fileDescriptorFromNumber(int fdNumber)
+{
+  Q_ASSERT(fdNumber == 1 /* stdout*/ || fdNumber == 2 /*stderr*/);
+  if (fdNumber == 1)
+    {
+    return stdout;
+    }
+  else if (fdNumber == 2)
+    {
+    return stderr;
+    }
+  return 0;
+}
+
+// --------------------------------------------------------------------------
+void ctkFDHandler::setEnabled(bool value)
+{
+  if (this->Enabled == value)
+    {
+    return;
+    }
+
+  if (value)
+    {
+    // Flush (stdout|stderr) so that any buffered messages are delivered
+    fflush(Self::fileDescriptorFromNumber(this->FDNumber));
+
+    // Save position of current standard output
+    fgetpos(Self::fileDescriptorFromNumber(this->FDNumber), &this->SavedFDPos);
+#ifdef Q_OS_WIN32
+    this->SavedFDNumber = _dup(_fileno(Self::fileDescriptorFromNumber(this->FDNumber)));
+#else
+    this->SavedFDNumber = dup(fileno(Self::fileDescriptorFromNumber(this->FDNumber)));
+#endif
+
+    // Open and close the OutputFile so that the unique filename is created
+    if (!this->OutputFile.exists())
+      {
+      this->OutputFile.open();
+      this->OutputFile.close();
+      }
+
+    //qDebug() << "ctkFDHandler - OutputFile" << this->OutputFile.fileName();
+
+    if ((this->FD = freopen(this->OutputFile.fileName().toLatin1(),
+                            "w",
+                            Self::fileDescriptorFromNumber(this->FDNumber))) == 0)
+      {
+      // this->SavedFDNumber = 0;
+      }
+
+    // Observe the OutputFile for changes
+    this->OutputFileWatcher.addPath(this->OutputFile.fileName());
+    }
+  else
+    {
+    // Flush stdout or stderr so that any buffered messages are delivered
+    fflush(Self::fileDescriptorFromNumber(this->FDNumber));
+
+    // Flush current stream so that any buffered messages are delivered
+    fflush(this->FD);
+
+    // Un-observe OutputFile
+    this->OutputFileWatcher.removePath(this->OutputFile.fileName());
+
+    // Close file and restore standard output to stdout or stderr - which should be the terminal
+#if Q_OS_WIN32
+    _dup2(this->SavedFDNumber, _fileno(Self::fileDescriptorFromNumber(this->FDNumber)));
+    _close(this->SavedFDNumber);
+#else
+    dup2(this->SavedFDNumber, fileno(Self::fileDescriptorFromNumber(this->FDNumber)));
+    close(this->SavedFDNumber);
+#endif
+    clearerr(Self::fileDescriptorFromNumber(this->FDNumber));
+    fsetpos(Self::fileDescriptorFromNumber(this->FDNumber), &this->SavedFDPos);
+    }
+
+  this->Enabled = value;
+}
+
+// --------------------------------------------------------------------------
+void ctkFDHandler::outputFileChanged(const QString & path)
+{
+  QFile file(path);
+  if (!file.open(QFile::ReadOnly))
+    {
+    qCritical() << "ctkFDHandler - Failed to open file" << path;
+    return;
+    }
+
+  QTextStream stream(&file);
+  while (!stream.atEnd())
+    {
+    Q_ASSERT(this->MessageHandler->errorLogModel());
+    this->MessageHandler->errorLogModel()->addEntry(
+          this->LogLevel, this->MessageHandler->handlerPrettyName(), qPrintable(stream.readLine()));
+    }
+}
+
+// --------------------------------------------------------------------------
+// ctkErrorLogFDMessageHandlerPrivate
+
+// --------------------------------------------------------------------------
+class ctkErrorLogFDMessageHandlerPrivate
+{
+public:
+  ctkErrorLogFDMessageHandlerPrivate();
+  ~ctkErrorLogFDMessageHandlerPrivate();
+
+  ctkFDHandler * StdOutFDHandler;
+  ctkFDHandler * StdErrFDHandler;
+};
+
+// --------------------------------------------------------------------------
+// ctkErrorLogFDMessageHandlerPrivate methods
+
+//------------------------------------------------------------------------------
+ctkErrorLogFDMessageHandlerPrivate::ctkErrorLogFDMessageHandlerPrivate()
+{
+}
+
+//------------------------------------------------------------------------------
+ctkErrorLogFDMessageHandlerPrivate::~ctkErrorLogFDMessageHandlerPrivate()
+{
+  delete this->StdOutFDHandler;
+  delete this->StdErrFDHandler;
+}
+
+//------------------------------------------------------------------------------
+// ctkErrorLogFDMessageHandler methods
+
+//------------------------------------------------------------------------------
+QString ctkErrorLogFDMessageHandler::HandlerName = QLatin1String("FD");
+
+// --------------------------------------------------------------------------
+ctkErrorLogFDMessageHandler::ctkErrorLogFDMessageHandler() :
+  Superclass(), d_ptr(new ctkErrorLogFDMessageHandlerPrivate())
+{
+  Q_D(ctkErrorLogFDMessageHandler);
+  d->StdOutFDHandler = new ctkFDHandler(this, ctkErrorLogModel::Info, 1 /* stdout */);
+  d->StdErrFDHandler = new ctkFDHandler(this, ctkErrorLogModel::Critical, 2 /* stderr */);
+}
+
+// --------------------------------------------------------------------------
+QString ctkErrorLogFDMessageHandler::handlerName()const
+{
+  return ctkErrorLogFDMessageHandler::HandlerName;
+}
+
+// --------------------------------------------------------------------------
+void ctkErrorLogFDMessageHandler::setEnabledInternal(bool value)
+{
+  Q_D(ctkErrorLogFDMessageHandler);
+  d->StdOutFDHandler->setEnabled(value);
+  d->StdErrFDHandler->setEnabled(value);
+}

+ 52 - 0
Libs/Core/ctkErrorLogFDMessageHandler.h

@@ -0,0 +1,52 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkErrorLogFDMessageHandler_h
+#define __ctkErrorLogFDMessageHandler_h
+
+// CTK includes
+#include <ctkErrorLogModel.h>
+#include "ctkCoreExport.h"
+
+class ctkErrorLogFDMessageHandlerPrivate;
+
+//------------------------------------------------------------------------------
+class CTK_CORE_EXPORT ctkErrorLogFDMessageHandler : public ctkErrorLogAbstractMessageHandler
+{
+public:
+  typedef ctkErrorLogAbstractMessageHandler Superclass;
+
+  ctkErrorLogFDMessageHandler();
+
+  static QString HandlerName;
+
+  virtual QString handlerName()const;
+  virtual void setEnabledInternal(bool value);
+
+protected:
+  QScopedPointer<ctkErrorLogFDMessageHandlerPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkErrorLogFDMessageHandler);
+  Q_DISABLE_COPY(ctkErrorLogFDMessageHandler);
+};
+
+#endif
+

+ 74 - 0
Libs/Core/ctkErrorLogFDMessageHandler_p.h

@@ -0,0 +1,74 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkErrorLogFDMessageHandler_p_h
+#define __ctkErrorLogFDMessageHandler_p_h
+
+// Qt includes
+#include <QObject>
+#include <QFileSystemWatcher>
+#include <QTemporaryFile>
+
+// CTK includes
+#include "ctkErrorLogModel.h"
+
+// STD includes
+#include <cstdio>
+
+class ctkErrorLogFDMessageHandler;
+
+// --------------------------------------------------------------------------
+class ctkFDHandler : public QObject
+{
+  Q_OBJECT
+public:
+  typedef ctkFDHandler Self;
+
+  ctkFDHandler(ctkErrorLogFDMessageHandler* messageHandler,
+               ctkErrorLogModel::LogLevel logLevel,
+               int fileDescriptorNumber);
+
+  void setEnabled(bool value);
+
+  static FILE* fileDescriptorFromNumber(int fdNumber);
+
+public slots:
+  void outputFileChanged(const QString & path);
+
+private:
+  ctkErrorLogFDMessageHandler * MessageHandler;
+  ctkErrorLogModel::LogLevel LogLevel;
+
+  /// OutputFile where either stdout or stderr is redirected
+  QTemporaryFile     OutputFile;
+
+  /// The fileWatcher will emit the signal 'fileChanged()' each time the outputFile changed.
+  QFileSystemWatcher OutputFileWatcher;
+
+  int    FDNumber;
+  int    SavedFDNumber;
+  fpos_t SavedFDPos;
+  FILE*  FD;
+
+  bool Enabled;
+};
+
+
+#endif

+ 418 - 0
Libs/Core/ctkErrorLogModel.cpp

@@ -0,0 +1,418 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+#include <QDateTime>
+#include <QDebug>
+#include <QMainWindow>
+#include <QMetaEnum>
+#include <QMetaType>
+#include <QPointer>
+#include <QStandardItem>
+#include <QStatusBar>
+
+// CTK includes
+#include "ctkErrorLogModel.h"
+#include <ctkPimpl.h>
+
+// --------------------------------------------------------------------------
+// ctkErrorLogAbstractMessageHandler methods
+
+// --------------------------------------------------------------------------
+ctkErrorLogModel* ctkErrorLogAbstractMessageHandler::errorLogModel()const
+{
+  return this->ErrorLogModel.data();
+}
+
+// --------------------------------------------------------------------------
+void ctkErrorLogAbstractMessageHandler::setErrorLogModel(ctkErrorLogModel * newErrorLogModel)
+{
+  this->ErrorLogModel = QPointer<ctkErrorLogModel>(newErrorLogModel);
+}
+
+// --------------------------------------------------------------------------
+QString ctkErrorLogAbstractMessageHandler::handlerPrettyName()const
+{
+  if (this->HandlerPrettyName.isEmpty())
+    {
+    return this->handlerName();
+    }
+  else
+    {
+    return this->HandlerPrettyName;
+    }
+}
+
+// --------------------------------------------------------------------------
+void ctkErrorLogAbstractMessageHandler::setHandlerPrettyName(const QString& newHandlerPrettyName)
+{
+  this->HandlerPrettyName = newHandlerPrettyName;
+}
+
+// --------------------------------------------------------------------------
+bool ctkErrorLogAbstractMessageHandler::enabled()const
+{
+  return this->Enabled;
+}
+
+// --------------------------------------------------------------------------
+void ctkErrorLogAbstractMessageHandler::setEnabled(bool value)
+{
+  if (value == this->Enabled)
+    {
+    return;
+    }
+  this->setEnabledInternal(value);
+  this->Enabled = value;
+}
+
+
+// --------------------------------------------------------------------------
+// ctkErrorLogModelPrivate
+
+// --------------------------------------------------------------------------
+class ctkErrorLogModelPrivate
+{
+  Q_DECLARE_PUBLIC(ctkErrorLogModel);
+protected:
+  ctkErrorLogModel* const q_ptr;
+public:
+  ctkErrorLogModelPrivate(ctkErrorLogModel& object);
+  ~ctkErrorLogModelPrivate();
+
+  void init();
+
+  QStandardItemModel StandardItemModel;
+
+  QHash<QString, ctkErrorLogAbstractMessageHandler*> RegisteredHandlers;
+
+  ctkErrorLogModel::LogLevels CurrentLogLevelFilter;
+
+  bool LogEntryGrouping;
+
+};
+
+// --------------------------------------------------------------------------
+// ctkErrorLogModelPrivate methods
+
+// --------------------------------------------------------------------------
+ctkErrorLogModelPrivate::ctkErrorLogModelPrivate(ctkErrorLogModel& object)
+  : q_ptr(&object)
+{
+}
+
+// --------------------------------------------------------------------------
+ctkErrorLogModelPrivate::~ctkErrorLogModelPrivate()
+{
+  this->LogEntryGrouping = false;
+  foreach(const QString& handlerName, this->RegisteredHandlers.keys())
+    {
+    ctkErrorLogAbstractMessageHandler * msgHandler =
+        this->RegisteredHandlers.value(handlerName);
+    Q_ASSERT(msgHandler);
+    msgHandler->setEnabled(false);
+    delete msgHandler;
+    }
+}
+
+// --------------------------------------------------------------------------
+void ctkErrorLogModelPrivate::init()
+{
+  Q_Q(ctkErrorLogModel);
+  q->setDynamicSortFilter(true);
+  //
+  // WARNING - Using a QSortFilterProxyModel slows down the insertion of rows by a factor 10
+  //
+  q->setSourceModel(&this->StandardItemModel);
+  q->setFilterKeyColumn(ctkErrorLogModel::LogLevelColumn);
+}
+
+// --------------------------------------------------------------------------
+// ctkErrorLogModel methods
+
+//------------------------------------------------------------------------------
+ctkErrorLogModel::ctkErrorLogModel(QObject * parentObject)
+  : Superclass(parentObject)
+  , d_ptr(new ctkErrorLogModelPrivate(*this))
+{
+  Q_D(ctkErrorLogModel);
+
+  d->init();
+}
+
+//------------------------------------------------------------------------------
+ctkErrorLogModel::~ctkErrorLogModel()
+{
+}
+
+//------------------------------------------------------------------------------
+bool ctkErrorLogModel::registerMsgHandler(ctkErrorLogAbstractMessageHandler * msgHandler)
+{
+  Q_D(ctkErrorLogModel);
+  if (!msgHandler)
+    {
+    return false;
+    }
+  if (d->RegisteredHandlers.keys().contains(msgHandler->handlerName()))
+    {
+    return false;
+    }
+  msgHandler->setErrorLogModel(this);
+  d->RegisteredHandlers.insert(msgHandler->handlerName(), msgHandler);
+  return true;
+}
+
+//------------------------------------------------------------------------------
+QStringList ctkErrorLogModel::msgHandlerNames()const
+{
+  Q_D(const ctkErrorLogModel);
+  return d->RegisteredHandlers.keys();
+}
+
+//------------------------------------------------------------------------------
+bool ctkErrorLogModel::msgHandlerEnabled(const QString& handlerName) const
+{
+  Q_D(const ctkErrorLogModel);
+  if (!d->RegisteredHandlers.keys().contains(handlerName))
+    {
+    return false;
+    }
+  return d->RegisteredHandlers.value(handlerName)->enabled();
+}
+
+//------------------------------------------------------------------------------
+void ctkErrorLogModel::setMsgHandlerEnabled(const QString& handlerName, bool enabled)
+{
+  Q_D(ctkErrorLogModel);
+  if (!d->RegisteredHandlers.keys().contains(handlerName))
+    {
+//    qCritical() << "Failed to enable/disable message handler " << handlerName
+//                << "-  Handler not registered !";
+    return;
+    }
+  d->RegisteredHandlers.value(handlerName)->setEnabled(enabled);
+}
+
+//------------------------------------------------------------------------------
+void ctkErrorLogModel::disableAllMsgHandler()
+{
+  Q_D(ctkErrorLogModel);
+  foreach(const QString& msgHandlerName, d->RegisteredHandlers.keys())
+    {
+    this->setMsgHandlerEnabled(msgHandlerName, false);
+    }
+}
+
+//------------------------------------------------------------------------------
+QString ctkErrorLogModel::logLevelAsString(LogLevel logLevel)const
+{
+  QMetaEnum logLevelEnum = this->metaObject()->enumerator(0);
+  Q_ASSERT(QString("LogLevel").compare(logLevelEnum.name()) == 0);
+  return QLatin1String(logLevelEnum.valueToKey(logLevel));
+}
+
+//------------------------------------------------------------------------------
+void ctkErrorLogModel::addEntry(ctkErrorLogModel::LogLevel logLevel,
+                                const QString& origin, const char* text)
+{
+  Q_D(ctkErrorLogModel);
+
+  QString timeFormat("dd.MM.yyyy hh:mm:ss");
+  QDateTime currentDateTime = QDateTime::currentDateTime();
+
+  bool groupEntry = false;
+  if (d->LogEntryGrouping)
+    {
+    // Retrieve logLevel associated with last row
+    QModelIndex lastRowLogLevelIndex =
+        d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::LogLevelColumn);
+
+    bool logLevelMatched = this->logLevelAsString(logLevel) == lastRowLogLevelIndex.data().toString();
+
+    // Retrieve origin associated with last row
+    QModelIndex lastRowOriginIndex =
+        d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::OriginColumn);
+
+    bool originMatched = origin == lastRowOriginIndex.data().toString();
+
+    // Retrieve time associated with last row
+    QModelIndex lastRowTimeIndex =
+            d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::TimeColumn);
+    QDateTime lastRowDateTime = QDateTime::fromString(lastRowTimeIndex.data().toString(), timeFormat);
+
+    int groupingIntervalInMsecs = 1000;
+    bool withinGroupingInterval = lastRowDateTime.msecsTo(currentDateTime) <= groupingIntervalInMsecs;
+
+    groupEntry = logLevelMatched && originMatched && withinGroupingInterval;
+    }
+
+  if (!groupEntry)
+    {
+    QList<QStandardItem*> itemList;
+
+    // Time item
+    QStandardItem * timeItem = new QStandardItem(currentDateTime.toString(timeFormat));
+    timeItem->setEditable(false);
+    itemList << timeItem;
+
+    // LogLevel item
+    QStandardItem * logLevelItem = new QStandardItem(this->logLevelAsString(logLevel));
+    logLevelItem->setEditable(false);
+    itemList << logLevelItem;
+
+    // Origin item
+    QStandardItem * originItem = new QStandardItem(origin);
+    originItem->setEditable(false);
+    itemList << originItem;
+
+    // Description item
+    QStandardItem * descriptionItem = new QStandardItem();
+    QString descriptionText(text);
+    descriptionItem->setData(descriptionText.left(160).append((descriptionText.size() > 160) ? "..." : ""), Qt::DisplayRole);
+    descriptionItem->setData(descriptionText, ctkErrorLogModel::DescriptionTextRole);
+    descriptionItem->setEditable(false);
+    itemList << descriptionItem;
+
+    d->StandardItemModel.invisibleRootItem()->appendRow(itemList);
+    }
+  else
+    {
+    // Retrieve description associated with last row
+    QModelIndex lastRowDescriptionIndex =
+        d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::DescriptionColumn);
+
+    QStringList updatedDescription;
+    updatedDescription << lastRowDescriptionIndex.data(ctkErrorLogModel::DescriptionTextRole).toString();
+    updatedDescription << QLatin1String(text);
+
+    d->StandardItemModel.setData(lastRowDescriptionIndex, updatedDescription.join("\n"),
+                                 ctkErrorLogModel::DescriptionTextRole);
+
+    // Append '...' to displayText if needed
+    QString displayText = lastRowDescriptionIndex.data().toString();
+    if (!displayText.endsWith("..."))
+      {
+      d->StandardItemModel.setData(lastRowDescriptionIndex, displayText.append("..."), Qt::DisplayRole);
+      }
+    }
+}
+
+//------------------------------------------------------------------------------
+void ctkErrorLogModel::clear()
+{
+  Q_D(ctkErrorLogModel);
+  d->StandardItemModel.invisibleRootItem()->removeRows(0, d->StandardItemModel.rowCount());
+}
+
+//------------------------------------------------------------------------------
+void ctkErrorLogModel::filterEntry(const LogLevels& logLevel, bool disableFilter)
+{
+  Q_D(ctkErrorLogModel);
+
+  QStringList patterns;
+  if (!this->filterRegExp().pattern().isEmpty())
+    {
+    patterns << this->filterRegExp().pattern().split("|");
+    }
+  patterns.removeAll(this->logLevelAsString(Self::None));
+
+//  foreach(QString s, patterns)
+//    {
+//    std::cout << "pattern:" << qPrintable(s) << std::endl;
+//    }
+
+  QMetaEnum logLevelEnum = this->metaObject()->enumerator(0);
+  Q_ASSERT(QString("LogLevel").compare(logLevelEnum.name()) == 0);
+
+  // Loop over enum values and append associated name to 'patterns' if
+  // it has been specified within 'logLevel'
+  for (int i = 1; i < logLevelEnum.keyCount(); ++i)
+    {
+    int aLogLevel = logLevelEnum.value(i);
+    if (logLevel & aLogLevel)
+      {
+      QString logLevelAsString = this->logLevelAsString(static_cast<Self::LogLevel>(aLogLevel));
+      if (!disableFilter)
+        {
+        patterns << logLevelAsString;
+        d->CurrentLogLevelFilter |= static_cast<Self::LogLevels>(aLogLevel);
+        }
+      else
+        {
+        patterns.removeAll(logLevelAsString);
+        d->CurrentLogLevelFilter ^= static_cast<Self::LogLevels>(aLogLevel);
+        }
+      }
+    }
+
+  if (patterns.isEmpty())
+    {
+    // If there are no patterns, let's filter with the None level so that
+    // all entries are filtered out.
+    patterns << this->logLevelAsString(Self::None);
+    }
+
+  bool filterChanged = true;
+  QStringList currentPatterns = this->filterRegExp().pattern().split("|");
+  if (currentPatterns.count() == patterns.count())
+    {
+    foreach(const QString& p, patterns)
+      {
+      currentPatterns.removeAll(p);
+      }
+    filterChanged = currentPatterns.count() > 0;
+    }
+
+  this->setFilterRegExp(patterns.join("|"));
+
+  if (filterChanged)
+    {
+    emit this->logLevelFilterChanged();
+    }
+}
+
+//------------------------------------------------------------------------------
+ctkErrorLogModel::LogLevels ctkErrorLogModel::logLevelFilter()const
+{
+  QMetaEnum logLevelEnum = this->metaObject()->enumerator(0);
+  Q_ASSERT(QString("LogLevel").compare(logLevelEnum.name()) == 0);
+
+  Self::LogLevels filter = Self::Unknown;
+  foreach(const QString& filterAsString, this->filterRegExp().pattern().split("|"))
+    {
+    filter |= static_cast<Self::LogLevels>(logLevelEnum.keyToValue(filterAsString.toLatin1()));
+    }
+  return filter;
+}
+
+//------------------------------------------------------------------------------
+bool ctkErrorLogModel::logEntryGrouping()const
+{
+  Q_D(const ctkErrorLogModel);
+  return d->LogEntryGrouping;
+}
+
+//------------------------------------------------------------------------------
+void ctkErrorLogModel::setLogEntryGrouping(bool value)
+{
+  Q_D(ctkErrorLogModel);
+  d->LogEntryGrouping = value;
+}

+ 139 - 0
Libs/Core/ctkErrorLogModel.h

@@ -0,0 +1,139 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkErrorLogModel_h
+#define __ctkErrorLogModel_h
+
+// Qt includes
+#include <QPointer>
+#include <QSortFilterProxyModel>
+
+// CTK includes
+#include "ctkCoreExport.h"
+
+class ctkErrorLogModel;
+class ctkErrorLogModelPrivate;
+class QStandardItemModel;
+
+#include <iostream>
+
+//------------------------------------------------------------------------------
+class CTK_CORE_EXPORT ctkErrorLogAbstractMessageHandler
+{
+public:
+  ctkErrorLogAbstractMessageHandler():Enabled(false){}
+  virtual ~ctkErrorLogAbstractMessageHandler(){}
+
+  ctkErrorLogModel * errorLogModel()const;
+  void setErrorLogModel(ctkErrorLogModel * newErrorLogModel);
+
+  virtual QString handlerName()const = 0;
+
+  QString handlerPrettyName()const;
+
+  bool enabled()const;
+  void setEnabled(bool value);
+
+protected:
+  void setHandlerPrettyName(const QString& newHandlerPrettyName);
+
+  virtual void setEnabledInternal(bool value) = 0;
+
+private:
+  QPointer<ctkErrorLogModel> ErrorLogModel;
+  bool Enabled;
+  QString HandlerPrettyName;
+};
+
+//------------------------------------------------------------------------------
+class CTK_CORE_EXPORT ctkErrorLogModel : public QSortFilterProxyModel
+{
+  Q_OBJECT
+  Q_FLAGS(LogLevel)
+  Q_PROPERTY(bool logEntryGrouping READ logEntryGrouping WRITE setLogEntryGrouping)
+public:
+  typedef QSortFilterProxyModel Superclass;
+  typedef ctkErrorLogModel Self;
+  explicit ctkErrorLogModel(QObject* parentObject = 0);
+  virtual ~ctkErrorLogModel();
+
+  enum LogLevel
+    {
+    None     = 0x0,
+    Unknown  = 0x1,
+    Status   = 0x2,
+    Trace    = 0x4,
+    Debug    = 0x8,
+    Info     = 0x10,
+    Warning  = 0x20,
+    Error    = 0x40,
+    Critical = 0x80,
+    Fatal    = 0x100
+    };
+  Q_DECLARE_FLAGS(LogLevels, LogLevel)
+
+  enum ColumnsIds
+    {
+    TimeColumn = 0,
+    LogLevelColumn,
+    OriginColumn,
+    DescriptionColumn
+    };
+
+  enum ItemDataRole{
+    DescriptionTextRole = Qt::UserRole + 1
+    };
+
+  bool registerMsgHandler(ctkErrorLogAbstractMessageHandler * msgHandler);
+
+  QStringList msgHandlerNames()const;
+
+  bool msgHandlerEnabled(const QString& handlerName) const;
+
+  void setMsgHandlerEnabled(const QString& handlerName, bool enabled);
+
+  void disableAllMsgHandler();
+
+  QString logLevelAsString(LogLevel logLevel)const;
+
+  void addEntry(LogLevel logLevel, const QString& origin, const char* text);
+
+  void clear();
+
+  void filterEntry(const LogLevels& logLevel = Unknown, bool disableFilter = false);
+
+  LogLevels logLevelFilter()const;
+
+  bool logEntryGrouping()const;
+  void setLogEntryGrouping(bool value);
+
+signals:
+  void logLevelFilterChanged();
+
+protected:
+  QScopedPointer<ctkErrorLogModelPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkErrorLogModel);
+  Q_DISABLE_COPY(ctkErrorLogModel);
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(ctkErrorLogModel::LogLevels)
+
+#endif

+ 117 - 0
Libs/Core/ctkErrorLogQtMessageHandler.cpp

@@ -0,0 +1,117 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QCoreApplication>
+
+// CTK includes
+#include "ctkErrorLogQtMessageHandler.h"
+
+// STD includes
+#include <iostream>
+
+//------------------------------------------------------------------------------
+QString ctkErrorLogQtMessageHandler::HandlerName = QLatin1String("Qt");
+
+//------------------------------------------------------------------------------
+//Q_DECLARE_METATYPE(QPointer<ctkErrorLogQtMessageHandler>)
+Q_DECLARE_METATYPE(ctkErrorLogQtMessageHandler*)
+
+// --------------------------------------------------------------------------
+ctkErrorLogQtMessageHandler::ctkErrorLogQtMessageHandler() : Superclass()
+{
+  this->SavedQtMessageHandler = 0;
+
+  QCoreApplication * coreApp = QCoreApplication::instance();
+
+  // Keep track of all instantiated ctkErrorLogModel
+  QList<QVariant> handlers = coreApp->property("ctkErrorLogQtMessageHandlers").toList();
+  handlers << QVariant::fromValue(this);
+  //handlers << QVariant::fromValue(QPointer<ctkErrorLogQtMessageHandler>(this));
+  coreApp->setProperty("ctkErrorLogQtMessageHandlers", handlers);
+}
+
+namespace
+{
+//------------------------------------------------------------------------------
+void ctkErrorLogModelQtMessageOutput(QtMsgType type, const char *msg)
+{
+  // Warning: To avoid inifinite loop, do not use Q_ASSERT in this function.
+  if (QString(msg).isEmpty())
+    {
+    return;
+    }
+  ctkErrorLogModel::LogLevel level = ctkErrorLogModel::Unknown;
+  if (type == QtDebugMsg)
+    {
+    level = ctkErrorLogModel::Debug;
+    }
+  else if (type == QtWarningMsg)
+    {
+    level = ctkErrorLogModel::Warning;
+    }
+  else if (type == QtCriticalMsg)
+    {
+    level = ctkErrorLogModel::Critical;
+    }
+  else if (type == QtFatalMsg)
+    {
+    level = ctkErrorLogModel::Fatal;
+    }
+
+  QCoreApplication * coreApp = QCoreApplication::instance();
+  QList<QVariant> handlers = coreApp->property("ctkErrorLogQtMessageHandlers").toList();
+  foreach(QVariant v, handlers)
+    {
+    ctkErrorLogQtMessageHandler* handler = v.value<ctkErrorLogQtMessageHandler*>();
+    Q_ASSERT(handler);
+//    //QPointer<ctkErrorLogQtMessageHandler> handler = v.value<QPointer<ctkErrorLogQtMessageHandler> >();
+//    //if(handler.isNull())
+//    //  {
+//    //  continue;
+//    //  }
+    if (!handler->errorLogModel())
+      {
+      std::cout << "ErrorLogModel is Null !" << std::endl;
+      return;
+      }
+    handler->errorLogModel()->addEntry(level, handler->handlerPrettyName(), msg);
+    }
+}
+}
+
+// --------------------------------------------------------------------------
+QString ctkErrorLogQtMessageHandler::handlerName()const
+{
+  return ctkErrorLogQtMessageHandler::HandlerName;
+}
+
+// --------------------------------------------------------------------------
+void ctkErrorLogQtMessageHandler::setEnabledInternal(bool value)
+{
+  if (value)
+    {
+    this->SavedQtMessageHandler = qInstallMsgHandler(ctkErrorLogModelQtMessageOutput);
+    }
+  else
+    {
+    qInstallMsgHandler(this->SavedQtMessageHandler);
+    }
+}

+ 45 - 0
Libs/Core/ctkErrorLogQtMessageHandler.h

@@ -0,0 +1,45 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkErrorLogQtMessageHandler_h
+#define __ctkErrorLogQtMessageHandler_h
+
+// CTK includes
+#include <ctkErrorLogModel.h>
+#include "ctkCoreExport.h"
+
+//------------------------------------------------------------------------------
+class CTK_CORE_EXPORT ctkErrorLogQtMessageHandler : public ctkErrorLogAbstractMessageHandler
+{
+public:
+  typedef ctkErrorLogAbstractMessageHandler Superclass;
+
+  ctkErrorLogQtMessageHandler();
+
+  static QString HandlerName;
+
+  virtual QString handlerName()const;
+  virtual void setEnabledInternal(bool value);
+
+  QtMsgHandler SavedQtMessageHandler;
+};
+
+#endif
+

+ 200 - 0
Libs/Core/ctkErrorLogStreamMessageHandler.cpp

@@ -0,0 +1,200 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// CTK includes
+#include "ctkErrorLogStreamMessageHandler.h"
+
+// STD includes
+#include <iostream>
+#include <streambuf>
+#include <string>
+
+namespace
+{
+
+// --------------------------------------------------------------------------
+// ctkStreamHandler
+
+//
+// See http://lists.trolltech.com/qt-interest/2005-06/thread00166-0.html
+//
+
+// --------------------------------------------------------------------------
+class ctkStreamHandler : public std::streambuf
+{
+public:
+  ctkStreamHandler(ctkErrorLogStreamMessageHandler* messageHandler,
+                   ctkErrorLogModel::LogLevel logLevel,
+                   std::ostream& stream);
+
+  void setEnabled(bool value);
+
+protected:
+  virtual int_type overflow(int_type v);
+  virtual std::streamsize xsputn(const char *p, std::streamsize n);
+
+private:
+  ctkErrorLogStreamMessageHandler * MessageHandler;
+  ctkErrorLogModel::LogLevel LogLevel;
+
+  bool Enabled;
+
+  std::ostream&   Stream;
+  std::streambuf* SavedBuffer;
+  std::string     StringBuffer;
+};
+
+// --------------------------------------------------------------------------
+// ctkStreamHandler methods
+
+// --------------------------------------------------------------------------
+ctkStreamHandler::ctkStreamHandler(ctkErrorLogStreamMessageHandler* messageHandler,
+                                   ctkErrorLogModel::LogLevel logLevel,
+                                   std::ostream& stream) :
+  MessageHandler(messageHandler), LogLevel(logLevel), Stream(stream)
+{
+  this->Enabled = false;
+}
+
+// --------------------------------------------------------------------------
+void ctkStreamHandler::setEnabled(bool value)
+{
+  if (this->Enabled == value)
+    {
+    return;
+    }
+
+  if (value)
+    {
+    this->SavedBuffer = this->Stream.rdbuf();
+    this->Stream.rdbuf(this);
+    }
+  else
+    {
+    // Output anything that is left
+    if (!this->StringBuffer.empty())
+      {
+      Q_ASSERT(this->MessageHandler->errorLogModel());
+      this->MessageHandler->errorLogModel()->addEntry(
+            this->LogLevel, this->MessageHandler->handlerPrettyName(), this->StringBuffer.c_str());
+      }
+    this->Stream.rdbuf(this->SavedBuffer);
+    }
+
+  this->Enabled = value;
+}
+
+// --------------------------------------------------------------------------
+std::streambuf::int_type ctkStreamHandler::overflow(std::streambuf::int_type v)
+{
+  if (v == '\n')
+    {
+    Q_ASSERT(this->MessageHandler->errorLogModel());
+    this->MessageHandler->errorLogModel()->addEntry(
+          this->LogLevel, this->MessageHandler->handlerPrettyName(), this->StringBuffer.c_str());
+    this->StringBuffer.erase(this->StringBuffer.begin(), this->StringBuffer.end());
+    }
+  else
+    {
+    this->StringBuffer += v;
+    }
+  return v;
+}
+
+// --------------------------------------------------------------------------
+std::streamsize ctkStreamHandler::xsputn(const char *p, std::streamsize n)
+{
+  this->StringBuffer.append(p, p + n);
+
+  std::string::size_type pos = 0;
+  while (pos != std::string::npos)
+    {
+    pos = this->StringBuffer.find('\n');
+    if (pos != std::string::npos)
+      {
+      std::string tmp(this->StringBuffer.begin(), this->StringBuffer.begin() + pos);
+      Q_ASSERT(this->MessageHandler->errorLogModel());
+      this->MessageHandler->errorLogModel()->addEntry(
+            this->LogLevel, this->MessageHandler->handlerPrettyName(), tmp.c_str());
+      this->StringBuffer.erase(this->StringBuffer.begin(), this->StringBuffer.begin() + pos + 1);
+      }
+    }
+  return n;
+}
+
+}
+
+// --------------------------------------------------------------------------
+// ctkErrorLogStreamMessageHandlerPrivate
+
+// --------------------------------------------------------------------------
+class ctkErrorLogStreamMessageHandlerPrivate
+{
+public:
+  ctkErrorLogStreamMessageHandlerPrivate();
+  ~ctkErrorLogStreamMessageHandlerPrivate();
+
+  ctkStreamHandler * CoutStreamHandler;
+  ctkStreamHandler * CerrStreamHandler;
+};
+
+// --------------------------------------------------------------------------
+// ctkErrorLogStreamMessageHandlerPrivate methods
+
+//------------------------------------------------------------------------------
+ctkErrorLogStreamMessageHandlerPrivate::ctkErrorLogStreamMessageHandlerPrivate()
+{
+}
+
+//------------------------------------------------------------------------------
+ctkErrorLogStreamMessageHandlerPrivate::~ctkErrorLogStreamMessageHandlerPrivate()
+{
+  delete this->CoutStreamHandler;
+  delete this->CerrStreamHandler;
+}
+
+//------------------------------------------------------------------------------
+// ctkErrorLogStreamMessageHandler methods
+
+//------------------------------------------------------------------------------
+QString ctkErrorLogStreamMessageHandler::HandlerName = QLatin1String("Stream");
+
+// --------------------------------------------------------------------------
+ctkErrorLogStreamMessageHandler::ctkErrorLogStreamMessageHandler() :
+  Superclass(), d_ptr(new ctkErrorLogStreamMessageHandlerPrivate())
+{
+  Q_D(ctkErrorLogStreamMessageHandler);
+  d->CoutStreamHandler = new ctkStreamHandler(this, ctkErrorLogModel::Info, std::cout);
+  d->CerrStreamHandler = new ctkStreamHandler(this, ctkErrorLogModel::Critical, std::cerr);
+}
+
+// --------------------------------------------------------------------------
+QString ctkErrorLogStreamMessageHandler::handlerName()const
+{
+  return ctkErrorLogStreamMessageHandler::HandlerName;
+}
+
+// --------------------------------------------------------------------------
+void ctkErrorLogStreamMessageHandler::setEnabledInternal(bool value)
+{
+  Q_D(ctkErrorLogStreamMessageHandler);
+  d->CoutStreamHandler->setEnabled(value);
+  d->CerrStreamHandler->setEnabled(value);
+}

+ 52 - 0
Libs/Core/ctkErrorLogStreamMessageHandler.h

@@ -0,0 +1,52 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkErrorLogStreamMessageHandler_h
+#define __ctkErrorLogStreamMessageHandler_h
+
+// CTK includes
+#include <ctkErrorLogModel.h>
+#include "ctkCoreExport.h"
+
+class ctkErrorLogStreamMessageHandlerPrivate;
+
+//------------------------------------------------------------------------------
+class CTK_CORE_EXPORT ctkErrorLogStreamMessageHandler : public ctkErrorLogAbstractMessageHandler
+{
+public:
+  typedef ctkErrorLogAbstractMessageHandler Superclass;
+
+  ctkErrorLogStreamMessageHandler();
+
+  static QString HandlerName;
+
+  virtual QString handlerName()const;
+  virtual void setEnabledInternal(bool value);
+
+protected:
+  QScopedPointer<ctkErrorLogStreamMessageHandlerPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkErrorLogStreamMessageHandler);
+  Q_DISABLE_COPY(ctkErrorLogStreamMessageHandler);
+};
+
+#endif
+