Qt 解析并显示rt-thread输出的彩色日志源码分享
- 一、ANSI Color Code(ANSI SGR 颜色码)介绍
-
- 1、📌 基础格式
- 2、 🎨 常用 SGR 参数组合
-
- 1️⃣ 文本字体效果
- 2️⃣ 基础文本颜色(8色)
- 3️⃣ 背景颜色(8色)
- ✳️ 256色扩展模式
- ✴️ 真彩模式
- 3、💡 典型应用场景
- 4、🛠️ 注意事项
- 5、编程支持示例代码
- 二、rt-thread ulog组件介绍
-
- 1、什么是ulog组件?
- 2、主要功能和特点
- 3、如何在RT-Thread中使用ulog?
- 4、 示例代码
- 三、源码分享
-
- 1、效果展示
- 2、源码分享
- 2.1、rt-thread串口接收格式
-
- 2.2、mainwindow.h
- 2.3、mainwindow.h

一、ANSI Color Code(ANSI SGR 颜色码)介绍
ANSI SGR(Select Graphic Rendition)颜色码,它是文本终端中广泛使用的转义序列标准中的一部分,主要用于控制终端显示文本的样式(如颜色、背景、字体效果等)。该标准由 ANSI X3.64 定义。
1、📌 基础格式
每条样式控制命令以 ESC [ 开头(ESC 为 ASCII 缩写,十进制为 27,十六进制 \\x1b),后接数字参数(参数间用 ; 分隔),最后以 m 结尾。大多数编程语言中以 \\033[…] 或 \\u001B[…] 表示。
通用模板: \\033[<参数1>;<参数2>;…;<参数N>m
例如:红色文本 + 加粗: \\033[1;31m
2、 🎨 常用 SGR 参数组合
1️⃣ 文本字体效果
| 0 | 重置 | 清除所有样式 |
| 1 | 加粗 / 高亮 | ✅ 建议使用 |
| 2 | 淡色 | ✅ 效果有限 |
| 3 | 斜体 | ✅ 常用 |
| 4 | 下划线 | ✅ 常用 |
| 5 | 闪烁 | ✘ 老旧使用 |
| 7 | 反色 | ✅ 常见 |
| 9 | 删除线 | 🟡 支持有限 |
2️⃣ 基础文本颜色(8色)
| 30 | 黑色 | 🖤🖤 |
| 31 | 红色 | ❤️❤️ |
| 32 | 绿色 | 💚💚 |
| 33 | 黄色 | 💛💛 |
| 34 | 蓝色 | 💙💙 |
| 35 | 粉色 | 💜💜 |
| 36 | 青色 | 🟩🟩 |
| 37 | 白色 | ◯◯ |
示例代码:
print("\\033[31m红色文本示例: Hello \\033[m") # \\033[m 用于重置
3️⃣ 背景颜色(8色)
| 40 | 黑色背景 | ❤️🖤 |
| 41 | 红色背景 | ❤️❤️ |
| 42 | 绿色背景 | 🟢🟢 |
| 43 | 黄色背景 | 🔶🔶 |
| 44 | 蓝色背景 | 💙💙 |
| 45 | 粉背景 | 🟣🟣 |
| 46 | 青背景 | 🟦🟦 |
| 47 | 白背景 | ▫️▫️ |
示例:红字绿底且加粗: \\033[1;32;41m重磅消息\\033[0m
✳️ 256色扩展模式
现代终端大多支持256色模式,使用如下格式:
# 文本颜色
\\033[38;5;<颜色索引>m
# 背景颜色
\\033[48;5;<颜色索引>m
其中 <颜色索引> 取值范围:
- 0-15:传统16色
- 16-231:6×6×6 RGB 颜色(大大扩充)
- 232-255:24阶灰度(黑→白)
示例:显示RGB色表索引为201的文本颜色:
# 终端中使用
echo -e "\\033[38;5;201m流光紫 \\033[0m"
✴️ 真彩模式
支持RGB真彩色调,格式如下:
\\033[38;2;<R>;<G>;<B>m # 文字颜色调整
\\033[48;2;<R>;<G>;<B>m # 背景颜色设置
其中 <R>, <G>, <B> 均指0-255之间的值。
例如涂蓝文本并蓝色背景:
print("\\033[38;2;0;120;255m蓝宝库\\033[0m")
3、💡 典型应用场景
现代Shell工具(如PowerShell, zsh)均支持ANSI转义序列显示美观界面。
4、🛠️ 注意事项
- 兼容性差异:并非每个终端都能对所有参数(尤其5xxx级高阶代码)正确处理
- 嵌套复位:建议在使用后立刻加入 \\033[0m 以防后续文字渲染混乱
- 性能开销:文本级渲染转义代码每次输出都会造成本地重复渲染开销
5、编程支持示例代码
class Color:
reset = '\\033[0m'
bold = '\\033[1m'
underline = '\\033[4m'
red = '\\033[31m'
green = '\\033[32m'
blue = '\\033[94m'
print(Color.red + "重要警告" + Color.reset + Color.bold + "请注意" + Color.reset)
✅ 欲深入了解拓展参数表和颜色空间转换函数可参考ANSI官方手册。如有疑问请随时咨询!
二、rt-thread ulog组件介绍
1、什么是ulog组件?
ulog是RT-Thread中的一个标准化日志管理模块,用于记录系统运行时的调试信息、警告和错误。它提供了一种高效的方式来输出日志数据,方便开发者排查问题和监控系统状态。ulog的核心目标是减少资源占用,同时保持灵活性和扩展性,适合嵌入式设备开发。
2、主要功能和特点
ulog组件具有以下关键特性:
- 线程安全:在多线程环境中安全输出日志数据,避免竞争条件。
- 多级日志机制:通过设置不同的日志级别来控制输出信息,有效管理日志内容:
- 日志级别包括:DEBUG(调试)、INFO(信息)、WARN(警告)、ERROR(错误)等。
- 可插拔的后端输出:ulog支持多种输出模式和格式,包括:
- 串口控制台:这是最常见的输出方式,适用于开发阶段。
- 外部文件系统:可以将日志保存到SD卡或Flash中。
- 用户定义的自定义输出:开发者可以扩展ulog接口,将日志转到自定义设备如USB或网络。
- 高效的内存使用:ulog设计紧凑,避免了不必要的内存开销,适合资源有限的MCU(微控制器单元)。
3、如何在RT-Thread中使用ulog?
为了在RT-Thread系统中启用ulog组件,开发者需要配置和调用相关API。具体步骤如下:
- 启用ulog组件:在RT-Thread配置文件中(如Kconfig或SCons),开启ulog支持。
- 设置日志级别:通过API设置全局日志级别,例如设置为INFO级别,则只会输出INFO及以上级别的日志。
- 输出日志信息:在代码中使用ulog提供的模块接口来输出日志。
- 配置日志过滤:开发者可以为特定模块或线程设置日志过滤规则,突出关键信息。
4、 示例代码
以下是一个使用RT-Thread ulog组件的C代码示例。假设已启用ulog,代码展示了如何在应用中记录日志:
#include <rtthread.h>
#include <ulog.h> // 引入ulog库
void sample_task(void)
{
LOG_D("这是一条DEBUG级别的日志"); // DEBUG级别日志
LOG_I("这是一条INFO级别的日志"); // INFO级别日志
LOG_W("这是一条WARN级别的日志"); // WARN级别日志
LOG_E("这是一条ERROR级别的日志"); // ERROR级别日志
}
int main(void)
{
// 创建任务并导出到线程
rt_thread_t tid = rt_thread_create("sample", sample_task, RT_NULL, 512, 5, 10);
rt_thread_startup(tid);
return 0;
}
在这个示例中:
- LOG_D, LOG_I, LOG_W, LOG_E 是ulog的宏定义,分别输出DEBUG、INFO、WARN、ERROR级别的日志。
- 开发者可以在线程任务中直接调用这些接口,日志会根据设置输出到后端(如串口)。
三、源码分享
1、效果展示

2、源码分享
2.1、rt-thread串口接收格式
串口接收的数据格式为:
1. 绿色 32
字符串:
\\x1B[0m\\x1B[32m01–01 10:51:53 I/mavlink_comm: mavlink active!\\r\\r\\n\\x1B[0m
HEX:
1b 5b 30 6d 1b 5b 33 32 6d 30 31 2d 30 31 20 31 30 3a 35 31 3a 35 33 20 49 2f 6d 61 76 6c 69 6e 6b 5f 63 6f 6d 6d 3a 20 6d 61 76 6c 69 6e 6b 20 61 63 74 69 76 65 21 0d 0d 0a 1b 5b 30 6d
2. 红色 31
字符串:
\\x1B[0m\\x1B[31m01–01 10:51:54 I/mavlink_comm: mavlink active!\\r\\r\\n\\x1B[0m
HEX:
1b 5b 30 6d 1b 5b 33 31 6d 30 31 2d 30 31 20 31 30 3a 35 31 3a 35 34 20 49 2f 6d 61 76 6c 69 6e 6b 5f 63 6f 6d 6d 3a 20 6d 61 76 6c 69 6e 6b 20 61 63 74 69 76 65 21 0d 0d 0a 1b 5b 30 6d
3. 黄色 33
字符串:
\\x1B[0m\\x1B[33m01–01 10:51:55 I/mavlink_comm: mavlink active!\\r\\r\\n\\x1B[0m
HEX:
1b 5b 30 6d 1b 5b 33 33 6d 30 31 2d 30 31 20 31 30 3a 35 31 3a 35 35 20 49 2f 6d 61 76 6c 69 6e 6b 5f 63 6f 6d 6d 3a 20 6d 61 76 6c 69 6e 6b 20 61 63 74 69 76 65 21 0d 0d 0a 1b 5b 30 6d
4. 蓝色 34
字符串:
\\x1B[0m\\x1B[34m01–01 10:51:56 I/mavlink_comm: mavlink active!\\r\\r\\n\\x1B[0m
HEX:
1b 5b 30 6d 1b 5b 33 34 6d 30 31 2d 30 31 20 31 30 3a 35 31 3a 35 36 20 49 2f 6d 61 76 6c 69 6e 6b 5f 63 6f 6d 6d 3a 20 6d 61 76 6c 69 6e 6b 20 61 63 74 69 76 65 21 0d 0d 0a 1b 5b 30 6d
5. 紫色 35
字符串:
\\x1B[0m\\x1B[35m01–01 10:51:57 I/mavlink_comm: mavlink active!\\r\\r\\n\\x1B[0m
HEX:
1b 5b 30 6d 1b 5b 33 35 6d 30 31 2d 30 31 20 31 30 3a 35 31 3a 35 37 20 49 2f 6d 61 76 6c 69 6e 6b 5f 63 6f 6d 6d 3a 20 6d 61 76 6c 69 6e 6b 20 61 63 74 69 76 65 21 0d 0d 0a 1b 5b 30 6d
6. 青色 36
字符串:
\\x1B[0m\\x1B[36m01–01 10:51:58 I/mavlink_comm: mavlink active!\\r\\r\\n\\x1B[0m
HEX:
1b 5b 30 6d 1b 5b 33 36 6d 30 31 2d 30 31 20 31 30 3a 35 31 3a 35 38 20 49 2f 6d 61 76 6c 69 6e 6b 5f 63 6f 6d 6d 3a 20 6d 61 76 6c 69 6e 6b 20 61 63 74 69 76 65 21 0d 0d 0a 1b 5b 30 6d
7. 亮红 91
字符串:
\\x1B[0m\\x1B[91m01–01 10:51:59 I/mavlink_comm: mavlink active!\\r\\r\\n\\x1B[0m
HEX:
1b 5b 30 6d 1b 5b 39 31 6d 30 31 2d 30 31 20 31 30 3a 35 31 3a 35 39 20 49 2f 6d 61 76 6c 69 6e 6b 5f 63 6f 6d 6d 3a 20 6d 61 76 6c 69 6e 6b 20 61 63 74 69 76 65 21 0d 0d 0a 1b 5b 30 6d
8. 亮绿 92
字符串:
\\x1B[0m\\x1B[92m01–01 10:52:00 I/mavlink_comm: mavlink active!\\r\\r\\n\\x1B[0m
HEX:
1b 5b 30 6d 1b 5b 39 32 6d 30 31 2d 30 31 20 31 30 3a 35 32 3a 30 30 20 49 2f 6d 61 76 6c 69 6e 6b 5f 63 6f 6d 6d 3a 20 6d 61 76 6c 69 6e 6b 20 61 63 74 69 76 65 21 0d 0d 0a 1b 5b 30 6d
2.2、mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
private:
void splitLines();
int parseAnsi(const QByteArray &rawLine, QString &outText);
private slots:
void btnClickedSlot();
void onSerialRecvData();
QColor ansiCodeToColor(int code);
private:
Ui::MainWindow *ui;
QSerialPort m_serial;
QByteArray m_rxBuffer; // 接收环形缓存
};
#endif // MAINWINDOW_H
2.3、mainwindow.h
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this->ui->btnConnect,&QPushButton::clicked,this,&MainWindow::btnClickedSlot);
connect(&m_serial, &QSerialPort::readyRead, this, &MainWindow::onSerialRecvData);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::splitLines()
{
const QByteArray sep = "\\r\\r\\n";
int sepLen = sep.size();
// 循环查找所有完整行
while (true)
{
int pos = m_rxBuffer.indexOf(sep);
if (pos < 0)
break; // 无完整行,退出
// 截取一行原始数据(不含分隔符)
QByteArray rawLine = m_rxBuffer.left(pos);
// 移除已处理行 + 分隔符
m_rxBuffer = m_rxBuffer.mid(pos + sepLen);
// 解析ANSI颜色并发送信号
QString text;
int code = parseAnsi(rawLine, text);
qDebug()<<"code:"<<code<<" text:"<<text;
QColor textColor = ansiCodeToColor(code);
// 获取光标
QTextCursor cursor = this->ui->textBrowser->textCursor();
QTextCharFormat fmt;
// 设置文字颜色
fmt.setForeground(textColor);
// 可选:加粗(ansi 1)可扩展判断
// if(ansiCode == 1) fmt.setFontWeight(QFont::Bold);
// 应用格式到光标
cursor.setCharFormat(fmt);
this->ui->textBrowser->setTextCursor(cursor);
// 追加一行文本,自动换行
this->ui->textBrowser->append(text);
}
}
int MainWindow::parseAnsi(const QByteArray &rawLine, QString &outText)
{
outText.clear();
int colorCode = 0; // 0 = 重置样式
int i = 0;
int len = rawLine.size();
while (i < len)
{
// 判断 ESC[ 序列 0x1B 0x5B
if (rawLine.at(i) == 0x1B && (i+1) < len && rawLine.at(i+1) == 0x5B)
{
i += 2; // 跳过 \\033[
QString numStr;
// 读取数字直到 'm'
while (i < len && rawLine.at(i) != 'm')
{
numStr += rawLine.at(i);
i++;
}
// 读到m,更新颜色码
if (i < len && rawLine.at(i) == 'm')
{
colorCode = numStr.toInt();
i++;
}
}
else
{
// 普通可见字符,存入文本
outText += rawLine.at(i);
i++;
}
}
return colorCode;
}
void MainWindow::btnClickedSlot()
{
// 1. 配置串口参数
m_serial.setPortName("COM1"); // 串口号
m_serial.setBaudRate(QSerialPort::Baud115200); // 波特率
m_serial.setDataBits(QSerialPort::Data8); // 数据位8
m_serial.setParity(QSerialPort::NoParity); // 无校验
m_serial.setStopBits(QSerialPort::OneStop); // 停止位1
m_serial.setFlowControl(QSerialPort::NoFlowControl);
// 打开串口
if(m_serial.open(QIODevice::ReadWrite))
{
qDebug() << "串口打开成功";
}
else
{
qDebug() << "串口打开失败:" << m_serial.errorString();
}
}
void MainWindow::onSerialRecvData()
{
m_rxBuffer.append(m_serial.readAll());
// 循环切分完整行
splitLines();
}
QColor MainWindow::ansiCodeToColor(int code)
{
switch (code)
{
case 30: return Qt::gray;
case 31: return QColor(255, 80, 80); // 红
case 32: return QColor(80, 255, 80); // 绿
case 33: return QColor(255, 255, 80); // 黄
case 34: return QColor(80, 180, 255); // 蓝
case 35: return QColor(255, 80, 255); // 紫
case 36: return QColor(80, 255, 255); // 青
case 37: return Qt::white; // 白
case 0: // \\033[0m 重置为默认白色
default: return Qt::white;
}
}



