LogViewer: Add Filter

Change-Id: I3df36ad7dcbe4a28fb5430397063d70bbc61c80b
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Marcus Tillmanns
2023-10-16 13:15:17 +02:00
parent 1b82616f35
commit 042aa1aa25

View File

@@ -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,