forked from qt-creator/qt-creator
LogViewer: Add Filter
Change-Id: I3df36ad7dcbe4a28fb5430397063d70bbc61c80b Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -7,7 +7,9 @@
|
|||||||
#include "coreplugintr.h"
|
#include "coreplugintr.h"
|
||||||
#include "icore.h"
|
#include "icore.h"
|
||||||
|
|
||||||
|
#include <utils/async.h>
|
||||||
#include <utils/basetreeview.h>
|
#include <utils/basetreeview.h>
|
||||||
|
#include <utils/fancylineedit.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/listmodel.h>
|
#include <utils/listmodel.h>
|
||||||
@@ -31,7 +33,6 @@
|
|||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
namespace Core::Internal {
|
namespace Core::Internal {
|
||||||
|
|
||||||
static QColor colorForCategory(const QString &category);
|
static QColor colorForCategory(const QString &category);
|
||||||
@@ -209,6 +210,13 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isEnabledOriginally(QtMsgType msgType) const
|
||||||
|
{
|
||||||
|
if (m_originalSettings)
|
||||||
|
return (*m_originalSettings)[msgType];
|
||||||
|
return isEnabled(msgType);
|
||||||
|
}
|
||||||
|
|
||||||
void setEnabled(QtMsgType msgType, bool isEnabled)
|
void setEnabled(QtMsgType msgType, bool isEnabled)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(!m_useOriginal, return);
|
QTC_ASSERT(!m_useOriginal, return);
|
||||||
@@ -308,6 +316,8 @@ public:
|
|||||||
~LoggingCategoryModel() override;
|
~LoggingCategoryModel() override;
|
||||||
enum Column { Color, Name, Debug, Warning, Critical, Fatal, Info };
|
enum Column { Color, Name, Debug, Warning, Critical, Fatal, Info };
|
||||||
|
|
||||||
|
enum Role { OriginalStateRole = Qt::UserRole + 1 };
|
||||||
|
|
||||||
void append(const LoggingCategoryEntry &entry);
|
void append(const LoggingCategoryEntry &entry);
|
||||||
int columnCount(const QModelIndex &) const final { return 7; }
|
int columnCount(const QModelIndex &) const final { return 7; }
|
||||||
int rowCount(const QModelIndex & = QModelIndex()) const final { return m_categories.size(); }
|
int rowCount(const QModelIndex & = QModelIndex()) const final { return m_categories.size(); }
|
||||||
@@ -363,13 +373,19 @@ QVariant LoggingCategoryModel::data(const QModelIndex &index, int role) const
|
|||||||
|
|
||||||
static const QColor defaultColor = Utils::creatorTheme()->palette().text().color();
|
static const QColor defaultColor = Utils::creatorTheme()->palette().text().color();
|
||||||
return defaultColor;
|
return defaultColor;
|
||||||
} else if (role == Qt::CheckStateRole && index.column() >= Column::Debug
|
} else if (index.column() >= Column::Debug && index.column() <= Column::Info) {
|
||||||
&& index.column() <= Column::Info) {
|
if (role == Qt::CheckStateRole) {
|
||||||
const LoggingCategoryEntry &entry = m_categories.at(index.row());
|
const LoggingCategoryEntry &entry = m_categories.at(index.row());
|
||||||
return entry.isEnabled(static_cast<QtMsgType>(index.column() - Column::Debug))
|
const bool isEnabled = entry.isEnabled(
|
||||||
|
static_cast<QtMsgType>(index.column() - Column::Debug));
|
||||||
|
return isEnabled ? Qt::Checked : Qt::Unchecked;
|
||||||
|
} else if (role == OriginalStateRole) {
|
||||||
|
const LoggingCategoryEntry &entry = m_categories.at(index.row());
|
||||||
|
return entry.isEnabledOriginally(static_cast<QtMsgType>(index.column() - Column::Debug))
|
||||||
? Qt::Checked
|
? Qt::Checked
|
||||||
: Qt::Unchecked;
|
: Qt::Unchecked;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,9 +625,8 @@ LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent)
|
|||||||
|
|
||||||
auto qtInternal = new QToolButton;
|
auto qtInternal = new QToolButton;
|
||||||
qtInternal->setIcon(Core::Icons::QTLOGO.icon());
|
qtInternal->setIcon(Core::Icons::QTLOGO.icon());
|
||||||
qtInternal->setToolTip(Tr::tr("Toggle Qt Internal Logging"));
|
qtInternal->setToolTip(Tr::tr("Filter Qt Internal Log Categories"));
|
||||||
qtInternal->setCheckable(true);
|
qtInternal->setCheckable(false);
|
||||||
qtInternal->setChecked(false);
|
|
||||||
|
|
||||||
auto autoScroll = new QToolButton;
|
auto autoScroll = new QToolButton;
|
||||||
autoScroll->setIcon(Utils::Icons::ARROW_DOWN.icon());
|
autoScroll->setIcon(Utils::Icons::ARROW_DOWN.icon());
|
||||||
@@ -663,27 +678,53 @@ LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent)
|
|||||||
for (int i = LoggingCategoryModel::Column::Color; i < LoggingCategoryModel::Column::Info; i++)
|
for (int i = LoggingCategoryModel::Column::Color; i < LoggingCategoryModel::Column::Info; i++)
|
||||||
m_categoryView->resizeColumnToContents(i);
|
m_categoryView->resizeColumnToContents(i);
|
||||||
|
|
||||||
|
auto filterEdit = new Utils::FancyLineEdit;
|
||||||
|
filterEdit->setHistoryCompleter("LogFilterCompletionHistory");
|
||||||
|
filterEdit->setFiltering(true);
|
||||||
|
filterEdit->setPlaceholderText(Tr::tr("Filter categories by regular expression"));
|
||||||
|
filterEdit->setText("^(?!qt\\.).+");
|
||||||
|
filterEdit->setValidationFunction(
|
||||||
|
[](const QString &input) {
|
||||||
|
return Utils::asyncRun([input]() -> Utils::expected_str<QString> {
|
||||||
|
QRegularExpression re(input);
|
||||||
|
if (re.isValid())
|
||||||
|
return input;
|
||||||
|
|
||||||
|
return Utils::make_unexpected(
|
||||||
|
Tr::tr("Invalid regular expression: %1").arg(re.errorString()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
QSplitter *splitter{nullptr};
|
QSplitter *splitter{nullptr};
|
||||||
|
|
||||||
using namespace Layouting;
|
using namespace Layouting;
|
||||||
// clang-format off
|
// clang-format off
|
||||||
Column {
|
Column {
|
||||||
|
Splitter {
|
||||||
|
bindTo(&splitter),
|
||||||
|
Column {
|
||||||
|
noMargin(),
|
||||||
Row {
|
Row {
|
||||||
spacing(0),
|
spacing(0),
|
||||||
save,
|
save,
|
||||||
clean,
|
clean,
|
||||||
m_stopLog,
|
m_stopLog,
|
||||||
qtInternal,
|
|
||||||
autoScroll,
|
autoScroll,
|
||||||
m_timestamps,
|
m_timestamps,
|
||||||
m_messageTypes,
|
m_messageTypes,
|
||||||
st,
|
st,
|
||||||
},
|
},
|
||||||
Splitter {
|
m_logView
|
||||||
bindTo(&splitter),
|
},
|
||||||
m_logView,
|
Column {
|
||||||
|
noMargin(),
|
||||||
|
Row {
|
||||||
|
qtInternal,
|
||||||
|
filterEdit,
|
||||||
|
},
|
||||||
m_categoryView,
|
m_categoryView,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}.attachTo(this);
|
}.attachTo(this);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@@ -746,12 +787,17 @@ LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent)
|
|||||||
|
|
||||||
m_sortFilterModel->setFilterRegularExpression("^(?!qt\\.).+");
|
m_sortFilterModel->setFilterRegularExpression("^(?!qt\\.).+");
|
||||||
|
|
||||||
connect(qtInternal, &QToolButton::toggled, m_sortFilterModel, [this](bool checked) {
|
connect(qtInternal, &QToolButton::clicked, filterEdit, [filterEdit] {
|
||||||
if (checked) {
|
filterEdit->setText("^(?!qt\\.).+");
|
||||||
m_sortFilterModel->setFilterRegularExpression("");
|
});
|
||||||
} else {
|
|
||||||
m_sortFilterModel->setFilterRegularExpression("^(?!qt\\.).+");
|
connect(filterEdit,
|
||||||
}
|
&Utils::FancyLineEdit::textChanged,
|
||||||
|
m_sortFilterModel,
|
||||||
|
[this](const QString &f) {
|
||||||
|
QRegularExpression re(f);
|
||||||
|
if (re.isValid())
|
||||||
|
m_sortFilterModel->setFilterRegularExpression(f);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_timestamps, &QToolButton::toggled, this, [this](bool checked) {
|
connect(m_timestamps, &QToolButton::toggled, this, [this](bool checked) {
|
||||||
@@ -802,40 +848,70 @@ void LoggingViewManagerWidget::showLogCategoryContextMenu(const QPoint &pos) con
|
|||||||
|
|
||||||
QMenu m;
|
QMenu m;
|
||||||
auto uncheckAll = new QAction(Tr::tr("Uncheck All"), &m);
|
auto uncheckAll = new QAction(Tr::tr("Uncheck All"), &m);
|
||||||
|
auto resetAll = new QAction(Tr::tr("Reset All"), &m);
|
||||||
|
|
||||||
int column = 0;
|
auto isTypeColumn = [](int column) {
|
||||||
|
return column >= LoggingCategoryModel::Column::Debug
|
||||||
|
&& column <= LoggingCategoryModel::Column::Info;
|
||||||
|
};
|
||||||
|
|
||||||
if (idx.isValid()) {
|
auto setChecked = [this](std::initializer_list<LoggingCategoryModel::Column> columns,
|
||||||
column = idx.column();
|
Qt::CheckState checked) {
|
||||||
if (column >= LoggingCategoryModel::Column::Debug
|
for (int row = 0, count = m_sortFilterModel->rowCount(); row < count; ++row) {
|
||||||
&& column <= LoggingCategoryModel::Column::Info) {
|
for (int column : columns) {
|
||||||
bool isChecked = idx.data(Qt::CheckStateRole).toBool();
|
m_sortFilterModel->setData(m_sortFilterModel->index(row, column),
|
||||||
|
checked,
|
||||||
|
Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto resetToOriginal = [this](std::initializer_list<LoggingCategoryModel::Column> columns) {
|
||||||
|
for (int row = 0, count = m_sortFilterModel->rowCount(); row < count; ++row) {
|
||||||
|
for (int column : columns) {
|
||||||
|
const QModelIndex id = m_sortFilterModel->index(row, column);
|
||||||
|
m_sortFilterModel->setData(id,
|
||||||
|
id.data(LoggingCategoryModel::OriginalStateRole),
|
||||||
|
Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
QString text = isChecked ? Tr::tr("Uncheck All %1") : Tr::tr("Check All %1");
|
if (idx.isValid() && isTypeColumn(idx.column())) {
|
||||||
|
const LoggingCategoryModel::Column column = static_cast<LoggingCategoryModel::Column>(
|
||||||
|
idx.column());
|
||||||
|
bool isChecked = idx.data(Qt::CheckStateRole).toInt() == Qt::Checked;
|
||||||
|
const QString uncheckText = isChecked ? Tr::tr("Uncheck All %1") : Tr::tr("Check All %1");
|
||||||
|
|
||||||
uncheckAll->setText(text.arg(messageTypeToString(
|
uncheckAll->setText(uncheckText.arg(messageTypeToString(
|
||||||
static_cast<QtMsgType>(column - LoggingCategoryModel::Column::Debug))));
|
static_cast<QtMsgType>(column - LoggingCategoryModel::Column::Debug))));
|
||||||
|
resetAll->setText(Tr::tr("Reset All %1")
|
||||||
|
.arg(messageTypeToString(static_cast<QtMsgType>(
|
||||||
|
column - LoggingCategoryModel::Column::Debug))));
|
||||||
|
|
||||||
connect(uncheckAll, &QAction::triggered, m_sortFilterModel, [this, idx, isChecked] {
|
Qt::CheckState newState = isChecked ? Qt::Unchecked : Qt::Checked;
|
||||||
for (int i = 0; i < m_sortFilterModel->rowCount(); ++i) {
|
|
||||||
m_sortFilterModel->setData(m_sortFilterModel->index(i, idx.column()),
|
connect(uncheckAll,
|
||||||
!isChecked,
|
&QAction::triggered,
|
||||||
Qt::CheckStateRole);
|
m_sortFilterModel,
|
||||||
}
|
[setChecked, column, newState]() { setChecked({column}, newState); });
|
||||||
|
|
||||||
|
connect(resetAll, &QAction::triggered, m_sortFilterModel, [resetToOriginal, column]() {
|
||||||
|
resetToOriginal({column});
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
connect(uncheckAll, &QAction::triggered, m_sortFilterModel, [this] {
|
// No need to add Fatal here, as it is read-only
|
||||||
for (int i = 0; i < m_sortFilterModel->rowCount(); ++i) {
|
static auto allColumns = {LoggingCategoryModel::Column::Debug,
|
||||||
for (int c = LoggingCategoryModel::Column::Debug;
|
LoggingCategoryModel::Column::Warning,
|
||||||
c <= LoggingCategoryModel::Column::Info;
|
LoggingCategoryModel::Column::Critical,
|
||||||
++c) {
|
LoggingCategoryModel::Column::Info};
|
||||||
m_sortFilterModel->setData(m_sortFilterModel->index(i, c),
|
|
||||||
false,
|
connect(uncheckAll, &QAction::triggered, m_sortFilterModel, [setChecked]() {
|
||||||
Qt::CheckStateRole);
|
setChecked(allColumns, Qt::Unchecked);
|
||||||
}
|
});
|
||||||
}
|
connect(resetAll, &QAction::triggered, m_sortFilterModel, [resetToOriginal]() {
|
||||||
|
resetToOriginal(allColumns);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// minimal load/save - plugins could later provide presets on their own?
|
// minimal load/save - plugins could later provide presets on their own?
|
||||||
@@ -844,6 +920,7 @@ void LoggingViewManagerWidget::showLogCategoryContextMenu(const QPoint &pos) con
|
|||||||
auto loadPreset = new QAction(Tr::tr("Update from Preset..."), &m);
|
auto loadPreset = new QAction(Tr::tr("Update from Preset..."), &m);
|
||||||
m.addAction(loadPreset);
|
m.addAction(loadPreset);
|
||||||
m.addAction(uncheckAll);
|
m.addAction(uncheckAll);
|
||||||
|
m.addAction(resetAll);
|
||||||
connect(savePreset,
|
connect(savePreset,
|
||||||
&QAction::triggered,
|
&QAction::triggered,
|
||||||
m_categoryModel,
|
m_categoryModel,
|
||||||
|
|||||||
Reference in New Issue
Block a user