ctkErrorLogModel.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QApplication>
  16. #include <QDateTime>
  17. #include <QDebug>
  18. #include <QMainWindow>
  19. #include <QMetaEnum>
  20. #include <QMetaType>
  21. #include <QPointer>
  22. #include <QStandardItem>
  23. #include <QStatusBar>
  24. // CTK includes
  25. #include "ctkErrorLogModel.h"
  26. #include <ctkPimpl.h>
  27. // --------------------------------------------------------------------------
  28. // ctkErrorLogAbstractMessageHandler methods
  29. // --------------------------------------------------------------------------
  30. ctkErrorLogAbstractMessageHandler::ctkErrorLogAbstractMessageHandler()
  31. : Enabled(false)
  32. {
  33. }
  34. // --------------------------------------------------------------------------
  35. ctkErrorLogAbstractMessageHandler::~ctkErrorLogAbstractMessageHandler()
  36. {
  37. }
  38. // --------------------------------------------------------------------------
  39. ctkErrorLogModel* ctkErrorLogAbstractMessageHandler::errorLogModel()const
  40. {
  41. return this->ErrorLogModel.data();
  42. }
  43. // --------------------------------------------------------------------------
  44. void ctkErrorLogAbstractMessageHandler::setErrorLogModel(ctkErrorLogModel * newErrorLogModel)
  45. {
  46. this->ErrorLogModel = QPointer<ctkErrorLogModel>(newErrorLogModel);
  47. }
  48. // --------------------------------------------------------------------------
  49. QString ctkErrorLogAbstractMessageHandler::handlerPrettyName()const
  50. {
  51. if (this->HandlerPrettyName.isEmpty())
  52. {
  53. return this->handlerName();
  54. }
  55. else
  56. {
  57. return this->HandlerPrettyName;
  58. }
  59. }
  60. // --------------------------------------------------------------------------
  61. void ctkErrorLogAbstractMessageHandler::setHandlerPrettyName(const QString& newHandlerPrettyName)
  62. {
  63. this->HandlerPrettyName = newHandlerPrettyName;
  64. }
  65. // --------------------------------------------------------------------------
  66. bool ctkErrorLogAbstractMessageHandler::enabled()const
  67. {
  68. return this->Enabled;
  69. }
  70. // --------------------------------------------------------------------------
  71. void ctkErrorLogAbstractMessageHandler::setEnabled(bool value)
  72. {
  73. if (value == this->Enabled)
  74. {
  75. return;
  76. }
  77. this->setEnabledInternal(value);
  78. this->Enabled = value;
  79. }
  80. // --------------------------------------------------------------------------
  81. // ctkErrorLogModelPrivate
  82. // --------------------------------------------------------------------------
  83. class ctkErrorLogModelPrivate
  84. {
  85. Q_DECLARE_PUBLIC(ctkErrorLogModel);
  86. protected:
  87. ctkErrorLogModel* const q_ptr;
  88. public:
  89. ctkErrorLogModelPrivate(ctkErrorLogModel& object);
  90. ~ctkErrorLogModelPrivate();
  91. void init();
  92. QStandardItemModel StandardItemModel;
  93. QHash<QString, ctkErrorLogAbstractMessageHandler*> RegisteredHandlers;
  94. ctkErrorLogModel::LogLevels CurrentLogLevelFilter;
  95. bool LogEntryGrouping;
  96. bool AddingEntry;
  97. int TerminalOutputEnabled;
  98. };
  99. // --------------------------------------------------------------------------
  100. // ctkErrorLogModelPrivate methods
  101. // --------------------------------------------------------------------------
  102. ctkErrorLogModelPrivate::ctkErrorLogModelPrivate(ctkErrorLogModel& object)
  103. : q_ptr(&object)
  104. {
  105. this->AddingEntry = false;
  106. this->TerminalOutputEnabled = false;
  107. }
  108. // --------------------------------------------------------------------------
  109. ctkErrorLogModelPrivate::~ctkErrorLogModelPrivate()
  110. {
  111. this->LogEntryGrouping = false;
  112. foreach(const QString& handlerName, this->RegisteredHandlers.keys())
  113. {
  114. ctkErrorLogAbstractMessageHandler * msgHandler =
  115. this->RegisteredHandlers.value(handlerName);
  116. Q_ASSERT(msgHandler);
  117. msgHandler->setEnabled(false);
  118. delete msgHandler;
  119. }
  120. }
  121. // --------------------------------------------------------------------------
  122. void ctkErrorLogModelPrivate::init()
  123. {
  124. Q_Q(ctkErrorLogModel);
  125. q->setDynamicSortFilter(true);
  126. //
  127. // WARNING - Using a QSortFilterProxyModel slows down the insertion of rows by a factor 10
  128. //
  129. q->setSourceModel(&this->StandardItemModel);
  130. q->setFilterKeyColumn(ctkErrorLogModel::LogLevelColumn);
  131. }
  132. // --------------------------------------------------------------------------
  133. // ctkErrorLogModel methods
  134. //------------------------------------------------------------------------------
  135. ctkErrorLogModel::ctkErrorLogModel(QObject * parentObject)
  136. : Superclass(parentObject)
  137. , d_ptr(new ctkErrorLogModelPrivate(*this))
  138. {
  139. Q_D(ctkErrorLogModel);
  140. d->init();
  141. }
  142. //------------------------------------------------------------------------------
  143. ctkErrorLogModel::~ctkErrorLogModel()
  144. {
  145. }
  146. //------------------------------------------------------------------------------
  147. bool ctkErrorLogModel::registerMsgHandler(ctkErrorLogAbstractMessageHandler * msgHandler)
  148. {
  149. Q_D(ctkErrorLogModel);
  150. if (!msgHandler)
  151. {
  152. return false;
  153. }
  154. if (d->RegisteredHandlers.keys().contains(msgHandler->handlerName()))
  155. {
  156. return false;
  157. }
  158. msgHandler->setErrorLogModel(this);
  159. d->RegisteredHandlers.insert(msgHandler->handlerName(), msgHandler);
  160. return true;
  161. }
  162. //------------------------------------------------------------------------------
  163. QStringList ctkErrorLogModel::msgHandlerNames()const
  164. {
  165. Q_D(const ctkErrorLogModel);
  166. return d->RegisteredHandlers.keys();
  167. }
  168. //------------------------------------------------------------------------------
  169. bool ctkErrorLogModel::msgHandlerEnabled(const QString& handlerName) const
  170. {
  171. Q_D(const ctkErrorLogModel);
  172. if (!d->RegisteredHandlers.keys().contains(handlerName))
  173. {
  174. return false;
  175. }
  176. return d->RegisteredHandlers.value(handlerName)->enabled();
  177. }
  178. //------------------------------------------------------------------------------
  179. void ctkErrorLogModel::setMsgHandlerEnabled(const QString& handlerName, bool enabled)
  180. {
  181. Q_D(ctkErrorLogModel);
  182. if (!d->RegisteredHandlers.keys().contains(handlerName))
  183. {
  184. // qCritical() << "Failed to enable/disable message handler " << handlerName
  185. // << "- Handler not registered !";
  186. return;
  187. }
  188. d->RegisteredHandlers.value(handlerName)->setEnabled(enabled);
  189. }
  190. //------------------------------------------------------------------------------
  191. QStringList ctkErrorLogModel::msgHandlerEnabled() const
  192. {
  193. Q_D(const ctkErrorLogModel);
  194. QStringList msgHandlers;
  195. foreach(const QString& handlerName, d->RegisteredHandlers.keys())
  196. {
  197. if (d->RegisteredHandlers.value(handlerName)->enabled())
  198. {
  199. msgHandlers << handlerName;
  200. }
  201. }
  202. return msgHandlers;
  203. }
  204. //------------------------------------------------------------------------------
  205. void ctkErrorLogModel::setMsgHandlerEnabled(const QStringList& handlerNames)
  206. {
  207. foreach(const QString& handlerName, handlerNames)
  208. {
  209. this->setMsgHandlerEnabled(handlerName, true);
  210. }
  211. }
  212. //------------------------------------------------------------------------------
  213. void ctkErrorLogModel::enableAllMsgHandler()
  214. {
  215. this->setAllMsgHandlerEnabled(true);
  216. }
  217. //------------------------------------------------------------------------------
  218. void ctkErrorLogModel::disableAllMsgHandler()
  219. {
  220. this->setAllMsgHandlerEnabled(false);
  221. }
  222. //------------------------------------------------------------------------------
  223. void ctkErrorLogModel::setAllMsgHandlerEnabled(bool enabled)
  224. {
  225. Q_D(ctkErrorLogModel);
  226. foreach(const QString& msgHandlerName, d->RegisteredHandlers.keys())
  227. {
  228. this->setMsgHandlerEnabled(msgHandlerName, enabled);
  229. }
  230. }
  231. //------------------------------------------------------------------------------
  232. bool ctkErrorLogModel::terminalOutputEnabled()const
  233. {
  234. Q_D(const ctkErrorLogModel);
  235. return d->TerminalOutputEnabled;
  236. }
  237. //------------------------------------------------------------------------------
  238. void ctkErrorLogModel::setTerminalOutputEnabled(bool enabled)
  239. {
  240. Q_D(ctkErrorLogModel);
  241. d->TerminalOutputEnabled = enabled;
  242. }
  243. //------------------------------------------------------------------------------
  244. QString ctkErrorLogModel::logLevelAsString(LogLevel logLevel)const
  245. {
  246. QMetaEnum logLevelEnum = this->metaObject()->enumerator(0);
  247. Q_ASSERT(QString("LogLevel").compare(logLevelEnum.name()) == 0);
  248. return QLatin1String(logLevelEnum.valueToKey(logLevel));
  249. }
  250. //------------------------------------------------------------------------------
  251. void ctkErrorLogModel::addEntry(ctkErrorLogModel::LogLevel logLevel,
  252. const QString& origin, const char* text)
  253. {
  254. Q_D(ctkErrorLogModel);
  255. if (d->AddingEntry)
  256. {
  257. // QFile f("/tmp/ctkErrorLogModel-AddingEntry-true.txt");
  258. // f.open(QFile::Append);
  259. // QTextStream s(&f);
  260. // s << "text=>" << text << "\n";
  261. // s << "\tlogLevel:" << qPrintable(this->logLevelAsString(logLevel)) << "\n";
  262. // s << "\torigin:" << qPrintable(origin) << "\n";
  263. // s << "\ttext:" << text << "\n";
  264. // f.close();
  265. return;
  266. }
  267. d->AddingEntry = true;
  268. QString timeFormat("dd.MM.yyyy hh:mm:ss");
  269. QDateTime currentDateTime = QDateTime::currentDateTime();
  270. bool groupEntry = false;
  271. if (d->LogEntryGrouping)
  272. {
  273. // Retrieve logLevel associated with last row
  274. QModelIndex lastRowLogLevelIndex =
  275. d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::LogLevelColumn);
  276. bool logLevelMatched = this->logLevelAsString(logLevel) == lastRowLogLevelIndex.data().toString();
  277. // Retrieve origin associated with last row
  278. QModelIndex lastRowOriginIndex =
  279. d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::OriginColumn);
  280. bool originMatched = origin == lastRowOriginIndex.data().toString();
  281. // Retrieve time associated with last row
  282. QModelIndex lastRowTimeIndex =
  283. d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::TimeColumn);
  284. QDateTime lastRowDateTime = QDateTime::fromString(lastRowTimeIndex.data().toString(), timeFormat);
  285. int groupingIntervalInMsecs = 1000;
  286. bool withinGroupingInterval = lastRowDateTime.time().msecsTo(currentDateTime.time()) <= groupingIntervalInMsecs;
  287. groupEntry = logLevelMatched && originMatched && withinGroupingInterval;
  288. }
  289. if (!groupEntry)
  290. {
  291. QList<QStandardItem*> itemList;
  292. // Time item
  293. QStandardItem * timeItem = new QStandardItem(currentDateTime.toString(timeFormat));
  294. timeItem->setEditable(false);
  295. itemList << timeItem;
  296. // LogLevel item
  297. QStandardItem * logLevelItem = new QStandardItem(this->logLevelAsString(logLevel));
  298. logLevelItem->setEditable(false);
  299. itemList << logLevelItem;
  300. // Origin item
  301. QStandardItem * originItem = new QStandardItem(origin);
  302. originItem->setEditable(false);
  303. itemList << originItem;
  304. // Description item
  305. QStandardItem * descriptionItem = new QStandardItem();
  306. QString descriptionText(text);
  307. descriptionItem->setData(descriptionText.left(160).append((descriptionText.size() > 160) ? "..." : ""), Qt::DisplayRole);
  308. descriptionItem->setData(descriptionText, ctkErrorLogModel::DescriptionTextRole);
  309. descriptionItem->setEditable(false);
  310. itemList << descriptionItem;
  311. d->StandardItemModel.invisibleRootItem()->appendRow(itemList);
  312. }
  313. else
  314. {
  315. // Retrieve description associated with last row
  316. QModelIndex lastRowDescriptionIndex =
  317. d->StandardItemModel.index(d->StandardItemModel.rowCount() - 1, ctkErrorLogModel::DescriptionColumn);
  318. QStringList updatedDescription;
  319. updatedDescription << lastRowDescriptionIndex.data(ctkErrorLogModel::DescriptionTextRole).toString();
  320. updatedDescription << QLatin1String(text);
  321. d->StandardItemModel.setData(lastRowDescriptionIndex, updatedDescription.join("\n"),
  322. ctkErrorLogModel::DescriptionTextRole);
  323. // Append '...' to displayText if needed
  324. QString displayText = lastRowDescriptionIndex.data().toString();
  325. if (!displayText.endsWith("..."))
  326. {
  327. d->StandardItemModel.setData(lastRowDescriptionIndex, displayText.append("..."), Qt::DisplayRole);
  328. }
  329. }
  330. if (d->TerminalOutputEnabled)
  331. {
  332. QStringList savedMsgHandlerEnabled = this->msgHandlerEnabled();
  333. this->disableAllMsgHandler();
  334. if (logLevel <= ctkErrorLogModel::Info)
  335. {
  336. std::cout << text << std::endl;
  337. }
  338. else
  339. {
  340. std::cerr << text << std::endl;
  341. }
  342. this->setMsgHandlerEnabled(savedMsgHandlerEnabled);
  343. }
  344. d->AddingEntry = false;
  345. }
  346. //------------------------------------------------------------------------------
  347. void ctkErrorLogModel::clear()
  348. {
  349. Q_D(ctkErrorLogModel);
  350. d->StandardItemModel.invisibleRootItem()->removeRows(0, d->StandardItemModel.rowCount());
  351. }
  352. //------------------------------------------------------------------------------
  353. void ctkErrorLogModel::filterEntry(const LogLevels& logLevel, bool disableFilter)
  354. {
  355. Q_D(ctkErrorLogModel);
  356. QStringList patterns;
  357. if (!this->filterRegExp().pattern().isEmpty())
  358. {
  359. patterns << this->filterRegExp().pattern().split("|");
  360. }
  361. patterns.removeAll(this->logLevelAsString(Self::None));
  362. // foreach(QString s, patterns)
  363. // {
  364. // std::cout << "pattern:" << qPrintable(s) << std::endl;
  365. // }
  366. QMetaEnum logLevelEnum = this->metaObject()->enumerator(0);
  367. Q_ASSERT(QString("LogLevel").compare(logLevelEnum.name()) == 0);
  368. // Loop over enum values and append associated name to 'patterns' if
  369. // it has been specified within 'logLevel'
  370. for (int i = 1; i < logLevelEnum.keyCount(); ++i)
  371. {
  372. int aLogLevel = logLevelEnum.value(i);
  373. if (logLevel & aLogLevel)
  374. {
  375. QString logLevelAsString = this->logLevelAsString(static_cast<Self::LogLevel>(aLogLevel));
  376. if (!disableFilter)
  377. {
  378. patterns << logLevelAsString;
  379. d->CurrentLogLevelFilter |= static_cast<Self::LogLevels>(aLogLevel);
  380. }
  381. else
  382. {
  383. patterns.removeAll(logLevelAsString);
  384. d->CurrentLogLevelFilter ^= static_cast<Self::LogLevels>(aLogLevel);
  385. }
  386. }
  387. }
  388. if (patterns.isEmpty())
  389. {
  390. // If there are no patterns, let's filter with the None level so that
  391. // all entries are filtered out.
  392. patterns << this->logLevelAsString(Self::None);
  393. }
  394. bool filterChanged = true;
  395. QStringList currentPatterns = this->filterRegExp().pattern().split("|");
  396. if (currentPatterns.count() == patterns.count())
  397. {
  398. foreach(const QString& p, patterns)
  399. {
  400. currentPatterns.removeAll(p);
  401. }
  402. filterChanged = currentPatterns.count() > 0;
  403. }
  404. this->setFilterRegExp(patterns.join("|"));
  405. if (filterChanged)
  406. {
  407. emit this->logLevelFilterChanged();
  408. }
  409. }
  410. //------------------------------------------------------------------------------
  411. ctkErrorLogModel::LogLevels ctkErrorLogModel::logLevelFilter()const
  412. {
  413. QMetaEnum logLevelEnum = this->metaObject()->enumerator(0);
  414. Q_ASSERT(QString("LogLevel").compare(logLevelEnum.name()) == 0);
  415. Self::LogLevels filter = Self::Unknown;
  416. foreach(const QString& filterAsString, this->filterRegExp().pattern().split("|"))
  417. {
  418. filter |= static_cast<Self::LogLevels>(logLevelEnum.keyToValue(filterAsString.toLatin1()));
  419. }
  420. return filter;
  421. }
  422. //------------------------------------------------------------------------------
  423. bool ctkErrorLogModel::logEntryGrouping()const
  424. {
  425. Q_D(const ctkErrorLogModel);
  426. return d->LogEntryGrouping;
  427. }
  428. //------------------------------------------------------------------------------
  429. void ctkErrorLogModel::setLogEntryGrouping(bool value)
  430. {
  431. Q_D(ctkErrorLogModel);
  432. d->LogEntryGrouping = value;
  433. }