LogTableModel.cpp
Go to the documentation of this file.
1/*
2* This file is part of ArmarX.
3*
4* ArmarX is free software; you can redistribute it and/or modify
5* it under the terms of the GNU General Public License version 2 as
6* published by the Free Software Foundation.
7*
8* ArmarX is distributed in the hope that it will be useful, but
9* WITHOUT ANY WARRANTY; without even the implied warranty of
10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11* GNU General Public License for more details.
12*
13* You should have received a copy of the GNU General Public License
14* along with this program. If not, see <http://www.gnu.org/licenses/>.
15*
16* @package ArmarX::
17* @author Mirko Waechter ( mirko.waechter at kit dot edu)
18* @date 2012
19* @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20* GNU General Public License
21*/
22
23#include "LogTableModel.h"
24
26
27#include "LogTable.h"
28
29namespace armarx
30{
31 LogTableModel::LogTableModel(QObject* parent) : QAbstractTableModel(parent)
32 {
33 newEntryCount = 0;
34 maxNewLogLevelType = eUNDEFINED;
35 }
36
37 int
38 LogTableModel::rowCount(const QModelIndex& parent) const
39 {
40 return logEntries.size();
41 }
42
43 int
44 LogTableModel::columnCount(const QModelIndex& parent) const
45 {
46 return 8;
47 }
48
49 QVariant
50 LogTableModel::data(const QModelIndex& index, int role) const
51 {
52 const int& row = index.row();
53 const int& column = index.column();
54
55 switch (role)
56 {
57 case (int)UserRoles::FullMsgRole:
58 {
59 std::unique_lock lock(logEntriesMutex);
60 if (row >= (signed int)logEntries.size() || row < 0)
61 {
62 return QString("n/A");
63 }
64
65 const LogMessage& entry = logEntries[row];
66 switch (column)
67 {
68
69 case 4:
70 {
71 if (role == Qt::ToolTipRole)
72 {
73 return "";
74 }
75
76 QString whatStr;
77
78 if (!entry.backtrace.empty())
79 {
80 whatStr = QString::fromStdString(entry.what + "\nBacktrace:\n" +
81 entry.backtrace);
82 }
83 else
84 {
85 whatStr = QString::fromStdString(entry.what);
86 }
87
88 return whatStr;
89 }
90 default:
91 return "";
92 }
93 }
94 case Qt::DisplayRole:
95 case Qt::ToolTipRole:
96 {
97 std::unique_lock lock(logEntriesMutex);
98
99 //std::cout << "row " << row << " column:" << column << std::endl;
100 if (row >= (signed int)logEntries.size() || row < 0)
101 {
102 return QString("n/A");
103 }
104
105 const LogMessage& entry = logEntries[row];
106
107 switch (column)
108 {
109 case 0:
110 {
111 IceUtil::Time time = IceUtil::Time::microSeconds(entry.time);
112 std::string timeStr = time.toDateTime();
113 timeStr = timeStr.substr(timeStr.find(' '));
114 return QString::fromStdString(timeStr);
115 }
116
117 case 1:
118 {
119 return QString::fromStdString(entry.who);
120 }
121
122 case 2:
123 {
124 return QString::fromStdString(entry.tag);
125 }
126
127 case 3:
128 {
129 if (entry.type != eUNDEFINED)
130 {
131 return QString::fromStdString(
133 }
134 else
135 {
136 return QVariant();
137 }
138 }
139
140 case 4:
141 {
142 if (role == Qt::ToolTipRole)
143 {
144 return "";
145 }
146
147 QString whatStr;
148
149 if (!entry.backtrace.empty())
150 {
151 whatStr = QString::fromStdString(entry.what + "\nBacktrace:\n...");
152 }
153 else
154 {
155 whatStr = QString::fromStdString(entry.what);
156 }
157 int lines = 0;
158 int maxLinesToShow = 50;
159 int pos = 0;
160 for (int i = 0; i < whatStr.length(); i++)
161 {
162 auto c = whatStr.at(i);
163 if (c == '\n')
164 {
165 lines++;
166 }
167 if (lines == maxLinesToShow)
168 {
169 break;
170 }
171 pos++;
172 }
173 if (lines >= maxLinesToShow)
174 {
175 whatStr.truncate(pos);
176 return whatStr + "\n...";
177 }
178 return whatStr;
179 }
180
181 case 5:
182 {
183 if (role == Qt::ToolTipRole)
184 {
185 return QString::fromStdString(
186 "Double click to open file in editor: " + entry.file + ":" +
187 QString::number(entry.line).toStdString());
188 }
189 else if (!entry.file.empty())
190 {
191 return QString::fromStdString(
192 entry.file + ":" + QString::number(entry.line).toStdString());
193 }
194 else
195 {
196 return QVariant();
197 }
198 }
199
200 case 6:
201 {
202 return QString::fromStdString(entry.function);
203 }
204
205 case 7:
206 {
207 return QString::fromStdString(entry.group);
208 }
209
210 default:
211 return "";
212 }
213 }
214
215 case Qt::DecorationRole:
216 switch (column)
217 {
218 case 5:
219 return QIcon(":icons/document-open-4.ico");
220
221 default:
222 return QVariant();
223 }
224
225 case Qt::BackgroundColorRole:
226 {
227 if (column == 3) // Log Level color
228 {
229 if (row >= (signed int)logEntries.size() || row < 0)
230 {
231 return QVariant();
232 }
233
234 const LogMessage& entry = logEntries[row];
235
236 switch (entry.type)
237 {
238 case eVERBOSE:
239 return QColor(200, 200, 250);
240
241 case eIMPORTANT:
242 return QColor(50, 255, 50);
243
244 case eWARN:
245 return QColor(216, 88, 0);
246
247 case eERROR:
248 return QColor(255, 64, 64);
249
250 case eFATAL:
251 return QColor(176, 0, 0);
252
253 default:
254 break;
255 }
256 }
257
258
259 if (activeSearchStr.length() == 0)
260 {
261 return QVariant();
262 }
263
264
265 std::unique_lock lock(logEntriesMutex);
266
267 //std::cout << "row " << row << " column:" << column << std::endl;
268 if (row >= (signed int)logEntries.size() || row < 0)
269 {
270 return QVariant();
271 }
272
273 const LogMessage& entry = logEntries[row];
274
275 if (msgContainsString(entry, activeSearchStr))
276 {
277 return QColor(255, 244, 127);
278 }
279 else
280 {
281 return QVariant();
282 }
283 }
284
285 // case Qt::SizeHintRole:
286 // {
287 // if(column == 4)
288 // {
289 //// const armarx::LogMessage & entry = logEntries[row];
290 //// int lines = std::count(entry.what.begin(), entry.what.end(), '\n') +1 ;
291 //// if(lines > 5)
292 //// lines = 5;
293 //// std::cout << "SizeHintRole lines: " << lines << "-> " << lines*14 << std::endl;
294 // return QSize(350,logRowSize[row]);
295 // }
296
297 // }
298 }
299
300 return QVariant();
301 }
302
303 bool
304 LogTableModel::rowContainsString(int row, const QString& searchStr) const
305 {
306 if (searchStr.length() == 0)
307 {
308 return true;
309 }
310
311 for (int i = 0; i < columnCount(); i++)
312 {
313 if (data(createIndex(row, i), Qt::DisplayRole)
314 .toString()
315 .contains(searchStr, Qt::CaseInsensitive))
316 {
317 return true;
318 }
319 }
320
321 return false;
322 }
323
324 bool
325 LogTableModel::msgContainsString(const LogMessage& logMsg, QString searchStr) const
326 {
327 if (QString(logMsg.who.c_str()).contains(searchStr, Qt::CaseInsensitive))
328 {
329 return true;
330 }
331
332 if (QString(logMsg.tag.c_str()).contains(searchStr, Qt::CaseInsensitive))
333 {
334 return true;
335 }
336
337 if (QString(logMsg.what.c_str()).contains(searchStr, Qt::CaseInsensitive))
338 {
339 return true;
340 }
341
342 if (QString(logMsg.backtrace.c_str()).contains(searchStr, Qt::CaseInsensitive))
343 {
344 return true;
345 }
346
347 if (QString(logMsg.file.c_str()).contains(searchStr, Qt::CaseInsensitive))
348 {
349 return true;
350 }
351
352 if (QString(logMsg.function.c_str()).contains(searchStr, Qt::CaseInsensitive))
353 {
354 return true;
355 }
356
357 if (QString::number(logMsg.line).contains(searchStr, Qt::CaseInsensitive))
358 {
359 return true;
360 }
361
362 return false;
363 }
364
365 bool
366 LogTableModel::rowContainsSameContent(int row, const LogMessage& logMsg) const
367 {
368 if (row < 0 || row >= (signed int)logEntries.size())
369 {
370 return false;
371 }
372
373 const LogMessage& entry = logEntries[row];
374
375 if (logMsg.line != entry.line)
376 {
377 return false;
378 }
379
380 if (logMsg.who != entry.who)
381 {
382 return false;
383 }
384
385 if (logMsg.tag != entry.tag)
386 {
387 return false;
388 }
389
390 if (logMsg.what != entry.what)
391 {
392 return false;
393 }
394
395 if (logMsg.file != entry.file)
396 {
397 return false;
398 }
399
400 if (logMsg.function != entry.function)
401 {
402 return false;
403 }
404
405 return true;
406 }
407
408 void
409 LogTableModel::search(const QString& searchStr)
410 {
411 activeSearchStr = searchStr;
412 QModelIndex leftTop = index(0, 0);
413 QModelIndex rightBottom = index(logEntries.size() - 1, 6);
414 // std::cout << "updating " << leftTop.row() << " til " << rightBottom.row() << std::endl;
415 emit dataChanged(leftTop, rightBottom);
416
417 // searchResultTask = new RunningTask<LogTableModel>(this, &LogTableModel::fillSearchResults, "logTableSearch");
418 }
419
420 QVariant
421 LogTableModel::headerData(int section, Qt::Orientation orientation, int role) const
422 {
423 if (role == Qt::DisplayRole)
424 {
425 if (orientation == Qt::Horizontal)
426 {
427 switch (section)
428 {
429 case 0:
430 return QString(ARMARX_LOG_TIMESTR);
431
432 case 1:
433 return QString(ARMARX_LOG_COMPONENTSTR);
434
435 case 2:
436 return QString(ARMARX_LOG_TAGSTR);
437
438 case 3:
439 return QString(ARMARX_LOG_VERBOSITYSTR);
440
441 case 4:
442 return QString(ARMARX_LOG_MESSAGESTR);
443
444 case 5:
445 return QString(ARMARX_LOG_FILESTR);
446
447 case 6:
448 return QString(ARMARX_LOG_FUNCTIONSTR);
449
450 case 7:
451 return QString(ARMARX_LOG_LOGGINGGROUPSTR);
452
453 default:
454 return QString("");
455 }
456 }
457 }
458 else if (role == Qt::ToolTipRole)
459 {
460 if (orientation == Qt::Horizontal)
461 {
462 switch (section)
463 {
464 case 0:
465 return QString(ARMARX_LOG_TIMESTR);
466
467 case 1:
468 return QString(ARMARX_LOG_COMPONENTSTR);
469
470 case 2:
471 return QString(ARMARX_LOG_TAGSTR);
472
473 case 3:
474 return QString(ARMARX_LOG_VERBOSITYSTR);
475
476 case 4:
477 return QString(ARMARX_LOG_MESSAGESTR);
478
479 case 5:
480 return QString::fromStdString(
481 std::string(ARMARX_LOG_FILESTR) +
482 std::string(": Double click cell to open Qtcreator at that location"));
483
484 case 6:
485 return QString(ARMARX_LOG_FUNCTIONSTR);
486
487 default:
488 return QString("");
489 }
490 }
491 }
492
493 // else if(role == Qt::SizeHintRole)
494 // {
495 // QSize size(100,18);
496 // if (orientation == Qt::Horizontal) {
497 // std::cout << "size of header questioned" << std::endl;
498 // switch (section)
499 // {
500 // case 0:
501 // case 1:
502 // case 2:
503 // size.setWidth(90);
504 // return size;
505 // case 3:
506 // size.setWidth(60);
507 // return size;
508 // case 4:
509 // size.setWidth(350);
510 // return size;
511 // case 5:
512 // size.setWidth(150);
513 // return size;
514 // case 6:
515 // size.setWidth(200);
516 // return size;
517 // default:
518 // std::cout << "standard size" << std::endl;
519 // return size;
520 // }
521 // }
522 // }
523
524 return QVariant();
525 }
526
527 bool
528 LogTableModel::setData(const QModelIndex& index, const QVariant& value, int role)
529 {
530 return false;
531 }
532
533 Qt::ItemFlags
534 LogTableModel::flags(const QModelIndex& index) const
535 {
536 if (index.column() == getColumn(ARMARX_LOG_MESSAGESTR))
537 {
538 return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
539 }
540 else
541 {
542 return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
543 }
544 }
545
546 void
548 {
549 // QModelIndex topLeft = createIndex(0,0);
550 // emit dataChanged(topLeft, topLeft);
551 QModelIndex leftTop = index(logEntries.size() - newEntryCount, 0);
552 QModelIndex rightBottom = index(logEntries.size() - 1, 6);
553 // std::cout << "updating " << leftTop.row() << " til " << rightBottom.row() << std::endl;
554 emit dataChanged(leftTop, rightBottom);
555 newEntryCount = 0;
556 maxNewLogLevelType = eUNDEFINED;
557 }
558
559 bool
560 LogTableModel::insertRows(int row, int count, const QModelIndex& parent)
561 {
562 beginInsertRows(QModelIndex(), row, row + count - 1);
563 int newEntries = row + count - logEntries.size();
564
565 for (int i = 0; i < newEntries; i++)
566 {
567 logEntries.push_back(LogMessage());
568 }
569
570 endInsertRows();
571 return true;
572 }
573
574 void
575 LogTableModel::addFilter(const std::string& columnName, const std::string& filter)
576 {
577 activeFilters.push_back(std::make_pair(columnName, filter));
578 }
579
580 void
582 {
583
584 {
585 std::unique_lock lock(logEntriesMutex);
586 std::vector<LogMessage>::iterator it = logEntries.begin();
587 int row = 0;
588
589 for (; it != logEntries.end(); ++it)
590 {
591 const LogMessage& entry = *it;
592
593 if (!applyFilters(entry))
594 {
595 beginRemoveRows(QModelIndex(), row, row);
596 it = logEntries.erase(it);
597 it--;
598 endRemoveRows();
599 }
600 else
601 {
602 row++;
603 }
604 }
605 }
606 updateView();
607 }
608
609 void
611 {
612 activeFilters.clear();
613 }
614
615 bool
616 LogTableModel::addEntry(const LogMessage& entry, int* entriesAdded)
617 {
618 {
619 std::unique_lock lock(logEntriesMutex);
620
621 if (entriesAdded)
622 {
623 *entriesAdded = 0;
624 }
625
626 if (!applyFilters(entry))
627 {
628 return false;
629 }
630
631 if (rowContainsSameContent(logEntries.size() - 1, entry))
632 {
633 *logEntries.rbegin() = entry;
634 return false;
635 }
636 else
637 {
638 newEntryCount++;
639
640 if (entry.type > maxNewLogLevelType)
641 {
642 maxNewLogLevelType = entry.type;
643 }
644
645 logEntries.push_back(entry);
646
647 if (entriesAdded)
648 {
649 (*entriesAdded)++;
650 }
651 }
652 }
653 return true;
654 }
655
656 int
657 LogTableModel::addEntries(const std::vector<LogMessage>& entryList, const QString& filterStr)
658 {
659 if (entryList.size() == 0)
660 {
661 return 0;
662 }
663
664 //std::cout << "adding entries " << entryList.size() << std::endl;
665 unsigned int size = entryList.size();
666 int entriesAdded = 0;
667
668 for (unsigned int i = 0; i < size; i++)
669 {
670 if (addEntry(entryList[i]))
671 {
672 entriesAdded++;
673 }
674 }
675 if (entriesAdded > 0)
676 {
677 beginInsertRows(QModelIndex(), logEntries.size() - entriesAdded, logEntries.size() - 1);
678 endInsertRows();
679 updateView();
680 }
681 return entriesAdded;
682 }
683
684 int
685 LogTableModel::getColumn(const std::string& columnName) const
686 {
687 if (columnName.empty())
688 {
689 return -1;
690 }
691
692 QString qcolumnName = QString::fromStdString(columnName);
693
694 for (int i = 0; i < columnCount(QModelIndex()); i++)
695 {
696 if (headerData(i, Qt::Horizontal, Qt::DisplayRole).toString().compare(qcolumnName) == 0)
697 {
698 return i;
699 }
700 }
701
702 return -1;
703 }
704
705 int
707 {
708 int size;
709 {
710 beginRemoveRows(QModelIndex(), 0, logEntries.size() - 1);
711 std::unique_lock lock(logEntriesMutex);
712 size = (int)logEntries.size();
713 removeRows(0, logEntries.size() - 1);
714 logEntries.clear();
715 endRemoveRows();
716 }
717 updateView();
718 return size;
719 }
720
721 const LogMessage&
723 {
724 return logEntries.at(row);
725 }
726
727 bool
728 LogTableModel::applyFilter(std::pair<std::string, std::string> filter, const LogMessage& logMsg)
729 {
730 std::string columnName = filter.first;
731 QString filterStr = QString::fromStdString(filter.second);
732
733 // for(unsigned int i = 0; i < columns.size(); i++)
734 // {
735 // if(columnName == columns[i].columnName)
736 // {
737 // if(logMsg.who.find(filterStr) == std::string::npos)
738 // return false;
739 // else
740 // return true;
741 // }
742 // }
743 // TODO: identify columns by integer instead of string for better performance
744 if (columnName == ARMARX_LOG_LOGGINGGROUPSTR)
745 {
746 if (QString(logMsg.group.c_str()).contains(filterStr, Qt::CaseSensitive))
747 {
748 return true;
749 }
750 else
751 {
752 return false;
753 }
754 }
755 else if (columnName == ARMARX_LOG_COMPONENTSTR)
756 {
757 if (QString(logMsg.who.c_str()).contains(filterStr, Qt::CaseInsensitive))
758 {
759 return true;
760 }
761 else
762 {
763 return false;
764 }
765 }
766 else if (columnName == ARMARX_LOG_TAGSTR)
767 {
768 if (QString(logMsg.tag.c_str()).contains(filterStr, Qt::CaseInsensitive))
769 {
770 return true;
771 }
772 else
773 {
774 return false;
775 }
776 }
777 else if (columnName == ARMARX_LOG_VERBOSITYSTR)
778 {
779 if (logMsg.type < QString(filterStr).toInt())
780 {
781 return false;
782 }
783 else
784 {
785 return true;
786 }
787 }
788 else if (columnName == ARMARX_LOG_MESSAGESTR)
789 {
790 if (QString(logMsg.what.c_str()).contains(filterStr, Qt::CaseInsensitive))
791 {
792 return true;
793 }
794 else
795 {
796 return false;
797 }
798 }
799 else if (columnName == ARMARX_LOG_FILESTR)
800 {
801 if (!QString(logMsg.file.c_str()).contains(filterStr, Qt::CaseInsensitive) &&
802 filterStr.toInt() != logMsg.line)
803 {
804 return false;
805 }
806 else
807 {
808 return true;
809 }
810 }
811 else if (columnName == ARMARX_LOG_FUNCTIONSTR)
812 {
813 if (QString(logMsg.function.c_str()).contains(filterStr, Qt::CaseInsensitive))
814 {
815 return true;
816 }
817 else
818 {
819 return false;
820 }
821 }
822
823 return true;
824 }
825
826 bool
827 LogTableModel::applyFilter(std::string filter, int row, int column)
828 {
829 if (row >= rowCount(QModelIndex()) || column >= columnCount(QModelIndex()))
830 {
831 return true;
832 }
833
834 return data(createIndex(row, column), Qt::DisplayRole)
835 .toString()
836 .contains(QString(filter.c_str()), Qt::CaseInsensitive);
837 return false;
838 }
839
840 bool
842 {
843 for (unsigned int i = 0; i < activeFilters.size(); i++)
844 {
845 if (!applyFilter(activeFilters[i].second, row, getColumn(activeFilters[i].first)))
846 {
847 return false;
848 }
849 }
850
851 return true;
852 }
853
854 bool
855 LogTableModel::applyFilters(const LogMessage& logMsg)
856 {
857 for (unsigned int i = 0; i < activeFilters.size(); i++)
858 {
859 if (!applyFilter(activeFilters[i], logMsg))
860 {
861 return false;
862 }
863 }
864
865 return true;
866 }
867
868 //bool LogTableModel::applyFilter(std::pair filter, int row)
869 //{
870 // int selectedColumn = getColumn(filter.first);
871
872 // if(selectedColumn == -1)
873 // {
874 // ARMARX_WARNING << "Could not find column " << filter.first << flush;
875 // return true;
876 // }
877
878 // return true;
879 //}
880} // namespace armarx
uint8_t index
#define ARMARX_LOG_VERBOSITYSTR
Definition LogTable.h:43
#define ARMARX_LOG_COMPONENTSTR
Definition LogTable.h:41
#define ARMARX_LOG_FILESTR
Definition LogTable.h:45
#define ARMARX_LOG_TAGSTR
Definition LogTable.h:42
#define ARMARX_LOG_TIMESTR
Definition LogTable.h:40
#define ARMARX_LOG_MESSAGESTR
Definition LogTable.h:44
#define ARMARX_LOG_LOGGINGGROUPSTR
Definition LogTable.h:47
#define ARMARX_LOG_FUNCTIONSTR
Definition LogTable.h:46
constexpr T c
static std::string levelToString(MessageTypeT type)
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
LogTableModel(QObject *parent=0)
bool setData(const QModelIndex &index, const QVariant &value, int role) override
int addEntries(const std::vector< LogMessage > &entryList, const QString &filterStr)
bool insertRows(int row, int count, const QModelIndex &parent) override
void addFilter(const std::string &columnName, const std::string &filter)
bool rowContainsSameContent(int row, const LogMessage &logMsg) const
const LogMessage & getLogEntry(size_t row) const
int columnCount(const QModelIndex &parent=QModelIndex()) const override
int getColumn(const std::string &columnName) const
bool addEntry(const LogMessage &entry, int *entriesAdded=NULL)
bool msgContainsString(const LogMessage &logMsg, QString searchStr) const
QVariant data(const QModelIndex &index, int role) const override
bool rowContainsString(int row, const QString &searchStr) const
bool applyFilter(std::pair< std::string, std::string > filter, const LogMessage &logMsg)
void search(const QString &searchStr)
This file offers overloads of toIce() and fromIce() functions for STL container types.
int toInt(const std::string &input)
MessageTypeT
Definition LogSender.h:46