mirror of
https://github.com/x-tools-author/x-tools.git
synced 2025-09-15 15:28:40 +08:00
Merge pull request #36 from le-cheng/master
feat(page): 添加日志搜索功能并优化输出样式
This commit is contained in:
commit
d7fa2244c7
@ -403,6 +403,13 @@ void Page::initUiOutput()
|
||||
ui->toolButtonInputPreset->setMenu(ui->tabPreset->menu());
|
||||
ui->tabWidget->setCurrentIndex(0);
|
||||
ui->tabTransfers->setCurrentIndex(0);
|
||||
|
||||
// 连接搜索相关的信号和槽
|
||||
connect(ui->pushButtonSearch, &QPushButton::clicked, this, &Page::onSearchButtonClicked);
|
||||
connect(ui->lineEditSearch, &QLineEdit::textChanged, this, &Page::onSearchTextChanged);
|
||||
connect(ui->checkBoxRegex, &QCheckBox::checkStateChanged, this, [this](int) { performSearch(); });
|
||||
connect(ui->checkBoxMatchCase, &QCheckBox::checkStateChanged, this, [this](int) { performSearch(); });
|
||||
connect(ui->checkBoxWholeWord, &QCheckBox::checkStateChanged, this, [this](int) { performSearch(); });
|
||||
}
|
||||
|
||||
void Page::initUiInput()
|
||||
@ -744,20 +751,29 @@ void Page::outputText(const QByteArray &bytes, const QString &flag, bool isRx)
|
||||
} else {
|
||||
header = QString("%1 %2").arg(rxTx, dateTimeString);
|
||||
}
|
||||
|
||||
#if 0
|
||||
header = header.trimmed();
|
||||
header = QString("<font color=silver>[%1]</font>").arg(header);
|
||||
QString outputText = QString("%1 %2").arg(header, text);
|
||||
outputText = outputText.replace("\r", "\\r");
|
||||
outputText = outputText.replace("\n", "\\n");
|
||||
#else
|
||||
header = QString("<font color=silver style='font-family: \"Segoe UI\", Arial; font-size: 12px;'>[%1]</font>").arg(header.trimmed());
|
||||
QString outputText = QString("<pre style='margin:0; font-family: \"JetBrains Mono\", Consolas, Monaco, monospace; font-size: 13px; line-height: 1.1; padding: 2px 0;'>%1 %2</pre>").arg(header, text);
|
||||
#endif
|
||||
|
||||
// 在添加到输出前进行搜索匹配
|
||||
if (!ui->lineEditSearch->text().isEmpty()) {
|
||||
performSearch(outputText);
|
||||
}
|
||||
|
||||
if (m_outputSettings->isEnableFilter()) {
|
||||
QString filter = m_outputSettings->filterText();
|
||||
if (outputText.contains(filter)) {
|
||||
ui->textBrowserOutput->append(outputText);
|
||||
if (!outputText.contains(filter)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ui->textBrowserOutput->append(outputText);
|
||||
}
|
||||
ui->textBrowserOutput->append(outputText);
|
||||
}
|
||||
|
||||
void Page::saveControllerParameters()
|
||||
@ -784,6 +800,223 @@ void Page::updateChartUi()
|
||||
ui->widgetChartsController->setVisible(ui->toolButtonCharts->isChecked());
|
||||
}
|
||||
|
||||
void Page::onSearchButtonClicked()
|
||||
{
|
||||
performSearch();
|
||||
}
|
||||
|
||||
void Page::onSearchTextChanged()
|
||||
{
|
||||
// 当搜索文本变化时,如果文本不为空,则执行搜索
|
||||
if (!ui->lineEditSearch->text().isEmpty()) {
|
||||
performSearch();
|
||||
} else {
|
||||
// 如果搜索文本为空,则清空搜索结果
|
||||
ui->textBrowserSearchResult->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Page::performSearch()
|
||||
{
|
||||
QString searchText = ui->lineEditSearch->text();
|
||||
if (searchText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取搜索选项
|
||||
bool useRegex = ui->checkBoxRegex->isChecked();
|
||||
bool matchCase = ui->checkBoxMatchCase->isChecked();
|
||||
bool wholeWord = ui->checkBoxWholeWord->isChecked();
|
||||
|
||||
// 构建正则表达式
|
||||
QString pattern = searchText;
|
||||
if (!useRegex) {
|
||||
// 如果不使用正则表达式,则转义特殊字符
|
||||
pattern = QRegularExpression::escape(pattern);
|
||||
}
|
||||
|
||||
// 如果需要全词匹配,则添加单词边界
|
||||
if (wholeWord) {
|
||||
pattern = QString("\\b%1\\b").arg(pattern);
|
||||
}
|
||||
|
||||
// 创建正则表达式对象
|
||||
QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption;
|
||||
if (!matchCase) {
|
||||
options |= QRegularExpression::CaseInsensitiveOption;
|
||||
}
|
||||
QRegularExpression regex(pattern, options);
|
||||
|
||||
// 获取textBrowserOutput的文本内容
|
||||
QString content = ui->textBrowserOutput->toPlainText();
|
||||
|
||||
// 执行搜索并高亮结果
|
||||
highlightSearchResults(content, regex);
|
||||
}
|
||||
|
||||
void Page::performSearch(QString &line)
|
||||
{
|
||||
QString searchText = ui->lineEditSearch->text();
|
||||
if (searchText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取搜索选项
|
||||
bool useRegex = ui->checkBoxRegex->isChecked();
|
||||
bool matchCase = ui->checkBoxMatchCase->isChecked();
|
||||
bool wholeWord = ui->checkBoxWholeWord->isChecked();
|
||||
|
||||
// 构建正则表达式
|
||||
QString pattern = searchText;
|
||||
if (!useRegex) {
|
||||
// 如果不使用正则表达式,则转义特殊字符
|
||||
pattern = QRegularExpression::escape(pattern);
|
||||
}
|
||||
|
||||
// 如果需要全词匹配,则添加单词边界
|
||||
if (wholeWord) {
|
||||
pattern = QString("\\b%1\\b").arg(pattern);
|
||||
}
|
||||
|
||||
// 创建正则表达式对象
|
||||
QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption;
|
||||
if (!matchCase) {
|
||||
options |= QRegularExpression::CaseInsensitiveOption;
|
||||
}
|
||||
QRegularExpression regex(pattern, options);
|
||||
|
||||
// 只在当前行中搜索匹配项
|
||||
highlightSearchResultsForLine(line, regex);
|
||||
}
|
||||
|
||||
void Page::highlightSearchResults(const QString &text, const QRegularExpression ®ex)
|
||||
{
|
||||
ui->textBrowserSearchResult->clear();
|
||||
|
||||
// 查找所有匹配项
|
||||
QRegularExpressionMatchIterator matchIterator = regex.globalMatch(text);
|
||||
int matchCount = 0;
|
||||
|
||||
// 存储所有匹配结果
|
||||
QList<QPair<int, QRegularExpressionMatch>> matches;
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
matches.append(qMakePair(match.capturedStart(), match));
|
||||
matchCount++;
|
||||
}
|
||||
|
||||
// 如果没有匹配项,显示提示信息
|
||||
if (matchCount == 0) {
|
||||
ui->textBrowserSearchResult->setHtml(QString("<p style='color:gray'>未找到匹配项</p>"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示匹配数量
|
||||
ui->textBrowserSearchResult->setHtml(QString("<p style='color:gray'>找到 %1 个匹配项</p>").arg(matchCount));
|
||||
|
||||
// 获取文本的行
|
||||
QStringList lines = text.split('\n');
|
||||
|
||||
// 对于每个匹配项,显示上下文
|
||||
for (const auto &matchPair : matches) {
|
||||
int matchPos = matchPair.first;
|
||||
QRegularExpressionMatch match = matchPair.second;
|
||||
|
||||
// 计算匹配项所在的行号
|
||||
int lineNumber = 0;
|
||||
int posInLine = matchPos;
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
if (posInLine <= lines[i].length()) {
|
||||
lineNumber = i;
|
||||
break;
|
||||
}
|
||||
posInLine -= lines[i].length() + 1; // +1 是为了换行符
|
||||
}
|
||||
|
||||
// 获取匹配行及其上下文(前后各一行)
|
||||
QString contextBefore = lineNumber > 0 ? lines[lineNumber - 1] : QString();
|
||||
QString matchLine = lines[lineNumber];
|
||||
QString contextAfter = lineNumber < lines.size() - 1 ? lines[lineNumber + 1] : QString();
|
||||
|
||||
// 在匹配行中高亮匹配文本
|
||||
QString matchedText = match.captured();
|
||||
int matchStartInLine = posInLine;
|
||||
int matchEndInLine = matchStartInLine + matchedText.length();
|
||||
|
||||
QString highlightedLine = matchLine.left(matchStartInLine) +
|
||||
QString("<span style='background-color:yellow;'>%1</span>").arg(matchedText) +
|
||||
matchLine.mid(matchEndInLine);
|
||||
|
||||
// 构建HTML显示上下文
|
||||
QString html = "<div style='margin-bottom:10px;'>";
|
||||
html += QString("<p style='color:gray;margin:0;'>行 %1:</p>").arg(lineNumber + 1);
|
||||
|
||||
if (!contextBefore.isEmpty()) {
|
||||
html += QString("<pre style='margin:0;color:gray;'>%1</pre>").arg(contextBefore.toHtmlEscaped());
|
||||
}
|
||||
|
||||
html += QString("<pre style='margin:0;'>%1</pre>").arg(highlightedLine);
|
||||
|
||||
if (!contextAfter.isEmpty()) {
|
||||
html += QString("<pre style='margin:0;color:gray;'>%1</pre>").arg(contextAfter.toHtmlEscaped());
|
||||
}
|
||||
|
||||
html += "</div>";
|
||||
|
||||
// 添加到搜索结果
|
||||
ui->textBrowserSearchResult->append(html);
|
||||
}
|
||||
}
|
||||
|
||||
void Page::highlightSearchResultsForLine(const QString &line, const QRegularExpression ®ex)
|
||||
{
|
||||
// 查找所有匹配项
|
||||
QRegularExpressionMatchIterator matchIterator = regex.globalMatch(line);
|
||||
|
||||
// 如果没有匹配项,直接返回
|
||||
if (!matchIterator.hasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果这是第一个匹配项,初始化搜索结果区域
|
||||
if (ui->textBrowserSearchResult->document()->isEmpty()) {
|
||||
ui->textBrowserSearchResult->setHtml(QString("<p style='color:gray'>实时搜索结果:</p>"));
|
||||
}
|
||||
|
||||
// 使用原始文本进行匹配和高亮,与highlightSearchResults保持一致
|
||||
QString originalText = line;
|
||||
|
||||
// 对于每个匹配项,高亮并添加到结果中
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
QString matchedText = match.captured();
|
||||
int matchStart = match.capturedStart();
|
||||
int matchEnd = matchStart + matchedText.length();
|
||||
|
||||
// 在匹配行中高亮匹配文本,使用与highlightSearchResults相同的逻辑
|
||||
QString highlightedLine = originalText.left(matchStart) +
|
||||
QString("<span style='background-color:yellow;'>%1</span>").arg(matchedText) +
|
||||
originalText.mid(matchEnd);
|
||||
|
||||
// 构建HTML显示
|
||||
QString html = "<div style='margin-bottom:5px;'>";
|
||||
html += QString("<pre style='margin:0;'>%1</pre>").arg(highlightedLine);
|
||||
html += "</div>";
|
||||
|
||||
// 添加到搜索结果
|
||||
ui->textBrowserSearchResult->append(html);
|
||||
|
||||
// 更新匹配计数(可选)
|
||||
static int matchCount = 0;
|
||||
matchCount++;
|
||||
// 更新标题显示匹配数量
|
||||
QTextCursor cursor = ui->textBrowserSearchResult->textCursor();
|
||||
cursor.setPosition(0);
|
||||
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||
cursor.insertHtml(QString("<p style='color:gray'>实时搜索结果:找到 %1 个匹配项</p>").arg(matchCount));
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Page::payload() const
|
||||
{
|
||||
InputSettings::Parameters parameters = m_inputSettings->parameters();
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSettings>
|
||||
#include <QTabWidget>
|
||||
#include <QTimer>
|
||||
@ -83,6 +84,12 @@ private:
|
||||
void onBytesRead(const QByteArray &bytes, const QString &from);
|
||||
void onBytesWritten(const QByteArray &bytes, const QString &to);
|
||||
void onWrapModeChanged();
|
||||
void onSearchButtonClicked();
|
||||
void onSearchTextChanged();
|
||||
void performSearch();
|
||||
void highlightSearchResults(const QString &text, const QRegularExpression ®ex);
|
||||
void highlightSearchResultsForLine(const QString &line, const QRegularExpression ®ex);
|
||||
void performSearch(QString &line);
|
||||
|
||||
void openDevice();
|
||||
void closeDevice();
|
||||
|
||||
127
src/page/page.ui
127
src/page/page.ui
@ -397,19 +397,140 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="textBrowserOutput">
|
||||
<property name="minimumSize">
|
||||
<widget class="QSplitter" name="splitterOutput">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QTextBrowser" name="textBrowserOutput"/>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
</property>
|
||||
<widget class="QWidget" name="widgetSearch" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayoutSearch">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutSearch">
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditSearch">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>搜索日志内容</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonSearch">
|
||||
<property name="text">
|
||||
<string>搜索</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutSearchOptions">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxRegex">
|
||||
<property name="text">
|
||||
<string>正则表达式</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxMatchCase">
|
||||
<property name="text">
|
||||
<string>区分大小写</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxWholeWord">
|
||||
<property name="text">
|
||||
<string>全字匹配</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacerSearchOptions">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="textBrowserSearchResult"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user