Qt解析含颜色的QString字符串显示到控件

1、需求

开发接收含颜色字符串显示到窗口,可解析字符串颜色配置窗口属性,且分割字符串显示。
mprintf(“xxxxxx”);打印的xxxxxx含有颜色配置。

2、实现方法

2.1、条件

选用Qt的PlainTextEdit控件显示字符串,配置为只读模式

初始化串口成功后,用下列代码绑定串口信号,收到数据会及时显示

connect(this->serialPort,SIGNAL(readyRead()),this,SLOT(log_serialread()));

/* readyRead()触发,接收到的数据长度是不定长的 */
/* log_serialread()是自己实现的接收函数 */
2.2、颜色配置函数

颜色规则参考链接: ANSI控制码

void logThread::display_config(const QString &text)
{
    QTextCharFormat fmt;
    QMap<QString, int> map; //颜色列表
    map["black"] = 30;  //背景色40
    map["red"] = 31;    //背景色41
    map["green"] = 32;  //背景色42
    map["yellow"] = 33; //背景色43
    map["blue"] = 34;   //背景色44
    map["magenta"] = 35;//背景色45
    map["cyan"] = 36;   //背景色46
    map["white"] = 37;  //背景色47

    QString text_style = text;
    QRegularExpression regex("\x1b\\["); 
    QRegularExpression regex_else("m"); 
    text_style = text_style.remove(regex); //去掉\033[
    text_style = text_style.remove(regex_else); //去掉m

    QStringList list = text_style.split(";"); //用;分割属性成列表
    foreach (QString type, list) {
        if(type == "0") { //常规文本,清除属性
            fmt.setForeground(QBrush(QColor("black")));
            fmt.setFontWeight(50); //取消加粗
            fmt.setFontUnderline(false); //取消下划线
        }
        else if(type == "1") { //加粗文本
            fmt.setFontWeight(63); //63、75、87
        }
        else if(type == "4") { //含下划线文件
            fmt.setFontUnderline(true);
        }

        if((type.toInt() >= 30) && (type.toInt() <= 47))
        {
            QMap<QString, int>::iterator itor;
            for (itor = map.begin(); itor != map.end(); ++itor)
            {
                if(type.toInt() == itor.value()) //30 ~ 37
                {
                    fmt.setForeground(QBrush(QColor(itor.key()))); //字体色
                }
                else if((type.toInt() - 10) == itor.value()) //40 ~ 47
                {
                    fmt.setBackground(QBrush(QColor(itor.key()))); //背景色
                }
            }
        }
    }

    this->printlog_displayPlainTextEdit->mergeCurrentCharFormat(fmt);
}
2.3、log显示函数
void logThread::log_display(const QString &text)
{
    QString text_in = text;
    text_in = text_in.remove(QRegularExpression("\\r")); //去掉\r,会当做换行

    #if 0
    //将获取的数据追加在文本编辑的末尾,会导致插入的文本换行,显示会乱
    this->displayPlainTextEdit->appendPlainText(text_in);
    #else
    //虽然配置为只读,如果鼠标移动了光标,会导致当前这段数据跳到光标处显示
    this->displayPlainTextEdit->insertPlainText(text_in); //当前光标位置显示
    this->displayPlainTextEdit->moveCursor(QTextCursor::End,QTextCursor::MoveAnchor); //移动光标到最后
    #endif
}
2.4、log接收函数

3种情况的处理规则如图:

在这里插入图片描述

代码实现:

void logThread::log_serialread()
{
    static QString str;
    int config_flag = 0;
    
    /* 每一次readyRead()触发,都把数据读完,长度不定长*/
    QString buf = QString(this->serialPort->readAll());
    int string_length = buf.length();
    
    if(!save_str.isEmpty()) //上一次存储的字符串不为空
    {
        buf = save_str + buf; //拼接到当前字符串前
        string_length = buf.length();
        save_str = ""; //清除存储的字符串       
    }

    QRegularExpression re_esc("\x1b"); //匹配颜色标志\033
    QRegularExpressionMatchIterator j = re_esc.globalMatch(buf); //运用迭代器,可获取每个\033的位置
    int count_esc = 0;
    int last_esc_index = 0;
    while (j.hasNext()) //是否有下一个匹配结果
    {
        count_esc++; //统计\033的个数
        QRegularExpressionMatch match = j.next();; //next()指针往后移动1
        if(!(j.hasNext())) //无下一个匹配结果,记录最后一个\033的位置
        {
            last_esc_index = match.capturedStart();
        }
    }

    QRegularExpression re("\x1b\\[[0-9;]*[mGKF]"); //匹配完整颜色配置
    QRegularExpressionMatchIterator i = re.globalMatch(buf);
    QRegularExpressionMatch match_before;
    int count = 0; 
    while (i.hasNext()) {
        config_flag = 1; //标志按照颜色配置分割字符串显示
        count++; //统计完整颜色配置的个数
        QRegularExpressionMatch match = i.next(); //next()指针往后移动1
        
        if(count == 1) //取第一个完整颜色标签之前的文字显示
        {
            if(match.capturedStart() != 0)
            {
            		//buf.left()表示从下标0往后,分割match.capturedStart()个字符
                this->log_display(buf.left(match.capturedStart()));
            }
        }
        else //取上一个标签和当前标签之间字符串显示
        {   
        		 //先按上一个标签配置颜色
            this->display_config(match_before.captured());
            //buf.mid()从下标match_before.capturedEnd()往后,分割match.capturedStart() - match_before.capturedEnd()个字符
            this->log_display(buf.mid(match_before.capturedEnd(), match.capturedStart() - match_before.capturedEnd()));
        }
     
        if(!(i.hasNext())) //无下一个匹配结果
        {
            //按当前标签配置颜色
            this->display_config(match.captured());
            if(count_esc != count) //完整颜色标签和\033个数不一样
            {
                //取当前标签和最后的\033之间字符串显示
                this->log_display(buf.mid(match.capturedEnd(), last_esc_index - match.capturedEnd()));
                
                //存储不完整颜色标签(最后\033及之后的字符串)
                save_str = buf.right(string_length - last_esc_index);
            }
            else
            {
                //取末尾标签后面的所有内容显示
                this->log_display(buf.right(string_length - match.capturedEnd()));
            }
        }
        else //有下一个匹配,存储当前的
        {
            match_before = match; 
        }
    }

    if(!config_flag)
    {
        this->log_display(buf);
    }
}
2.5、显示结果

log内容:

mprintf("\033[1;31mhello\033[0;31m\n"); //1;31m表示红色加粗,0;31m表示红色和取消加粗
mprintf("world\n");
mprintf("\033[32mhello hello\033[0m\n"); //32m表示绿色,0m表示取消颜色
mprintf("world world\n");
mprintf("\033[1;33mhello hello hello\033[0m\n"); //1;33m表示黄色加粗
mprintf("world world world\n");
mprintf("\033[34mhello hello hello hello\033[0;34m\n"); //34m表示蓝色
mprintf("world world world world\n");
mprintf("\033[35mhello hello hello hello hello\033[0;35m\n"); //35m表示紫色
mprintf("world world world world world\n");
mprintf("\033[1;36mhello hello hello hello hello hello\033[0m\n"); //36m表示青色
mprintf("world world world world world world\n");
mprintf("\033[37mhello hello hello hello hello hello hello\033[0;37m\n"); //37m表示白色
mprintf("world world world world world world world\n");

显示结果:

在这里插入图片描述

3、注意事项

“ \t ” 即table,直接送到显示,可能是默认的10多个空格,显示不是很好看,验证配置为8个空格可以对齐。

//设置制表符\t为8个空格
QFontMetrics metrics(this->printlog_displayPlainTextEdit->font());
int tabStopWidth = 8 * metrics.width(' ');
this->printlog_displayPlainTextEdit->setTabStopDistance(tabStopWidth);

“ \r ” 即为enter,直接送到显示,会换行,不需要多余的换行,可以用下列方法去掉。

QString text_in = "\r\n你好\r\n";
text_in = text_in.remove(QRegularExpression("\\r")); //去掉\r,会当做换行

new一个新的QSerialPort()前,一定要检查指针是否为空,关闭串口delete时,一定要将指针置为空,不然程序闪退。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/344549.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++笔记(二)

函数的默认参数 如果我们自己传入数据&#xff0c;就用自己的数据&#xff0c;如果没有&#xff0c;就用默认值 语法&#xff1a; 返回值类型 函数名&#xff08;形参默认值&#xff09;{} int func&#xff08;int a&#xff0c;int b20&#xff0c;int c30&#xff09;{} …

[BUG] Authentication Error

前言 给服务器安装了一个todesk&#xff0c;但是远程一直就是&#xff0c;点击用户&#xff0c;进入输入密码界面&#xff0c;还没等输入就自动返回了 解决 服务器是无桌面版本&#xff0c;或者桌面程序死掉了&#xff0c;重新安装就好 sudo apt install xorg sudo apt inst…

C++入门语法———命名空间,缺省参数,重载函数

文章目录 一.命名空间1.存在意义2.语法使用1.定义命名空间2.使用命名空间的三种方式 二.缺省参数1.全缺省参数2.半缺省参数 三.重载函数1.定义2.重载原理———名字修饰 一.命名空间 1.存在意义 C命名空间的主要意义是为了避免命名冲突&#xff0c;尤其是在大型项目中可能存在…

ai伪原创生成器app,一键生成原创文章

近年来&#xff0c;随着人工智能技术的飞速发展&#xff0c;AI伪原创生成器App已经成为了许多写手和创作者们的新宠。这款AI伪原创生成器App以其一键生成原创文章的快速便捷性&#xff0c;正在引起广泛的关注和使用。下面跟随小编一起来了解下吧&#xff01; 随着互联网的普及&…

8 种不同类型的防火墙

一、什么是防火墙&#xff1f; 防火墙是一种监视网络流量并检测潜在威胁的安全设备或程序&#xff0c;作为一道保护屏障&#xff0c;它只允许非威胁性流量进入&#xff0c;阻止危险流量进入。 防火墙是client-server模型中网络安全的基础之一&#xff0c;但它们容易受到以下方…

内网环境pip使用代理服务器安装依赖库

目录 使用proxy参数配置pip代理 使用配置文件配置pip代理 其他 由于公司内部网络无法访问外网导致安装依赖库失败&#xff0c;现将安装方法如下记录。 使用proxy参数配置pip代理 如不使用离线安装方法&#xff0c;可利用pip的--proxy参数进行代理的配置&#xff0c;使用方法…

1.15火星人(全排列+变进制数),涂国旗(搜索)

P1088 [NOIP2004 普及组] 火星人 首先是要得到当前排序的排名&#xff0c;其次是要得到它的排名 n进制就是说满n就进&#xff0c;该位上不可能保持为n&#xff0c;最多保持为n-1&#xff1b; 变进制数 #include<iostream> #include<iomanip> #include<vector…

力扣518. 零钱兑换 II

动态规划 思路&#xff1a; 假设 dp[i] 为金额 i 使用零钱的组合数&#xff0c;其可以由其中的一种零钱 coin 和 i - coin 组合&#xff1b; 遍历零钱数组&#xff0c;对每一种零钱 coin 进行如下操作&#xff1a; 从 coin 到 amount 金额进行遍历&#xff0c;dp[j] dp[j] d…

【深度学习:Collaborative filtering 协同过滤】深入了解协同过滤:技术、应用与示例

此图显示了使用协作筛选预测用户评分的示例。起初&#xff0c;人们会对不同的项目&#xff08;如视频、图像、游戏&#xff09;进行评分。之后&#xff0c;系统将对用户对项目进行评分的预测&#xff0c;而用户尚未评分。这些预测基于其他用户的现有评级&#xff0c;这些用户与…

安全基础~通用漏洞1

文章目录 知识补充Acess数据库注入MySQL数据库PostgreSQL-高权限读写注入MSSQL-sa高权限读写执行注入Oracle 注入Mongodb 注入sqlmap基础命令 知识补充 order by的意义&#xff1a; union 操作符用于合并两个或多个 select语句的结果集。 union 内部的每个 select 语句必须拥有…

k8s详细教程(二)

5. Pod 详解 5.1 Pod 介绍 5.1.1 Pod 结构 每个 Pod 中都可以包含一个或者多个容器&#xff0c;这些容器可以分为两类&#xff1a; 用户程序所在的容器&#xff0c;数量可多可少Pause 容器&#xff0c;这是每个 Pod 都会有的一个根容器&#xff0c;它的作用有两个&#xff1…

感性负载对电路稳定性有什么影响?

感性负载是指带有电感元件的负载&#xff0c;如电动机、变压器等。在电路中&#xff0c;感性负载对电路稳定性有很大的影响。本文将从以下几个方面来分析感性负载对电路稳定性的影响&#xff1a; 当感性负载接通或断开时&#xff0c;会产生一个瞬时电流&#xff0c;这个瞬时电流…

GPT5?OpenAI 创始人:GPT5 已在训练中,需要更多数据

OpenAI 最近发出征集大规模数据集的呼吁&#xff0c;特别是“今天在互联网上尚未公开轻松获取”的数据集&#xff0c;尤其是长篇写作或任何格式的对话。 GPT-5丨AI浪潮席卷全球&#xff0c;OpenAI 推出GPT-4 后&#xff0c;又于上月26日宣布今年9月、10月将推出GPT-4.5&#xf…

C++PythonC# 三语言OpenCV从零开发(4):视频流读取

文章目录 相关链接视频流读取CCSharpPython 总结 相关链接 C&Python&Csharp in OpenCV 专栏 【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程&#xff08;附带课程课件资料课件笔记&#xff09; OpenCV 教程中文文档|OpenCV中文 OpenCV教程中文文档|W3Csc…

第九篇 华为云Iot SDK的简单应用

第九篇 华为云Iot SDK的简单应用 一、华为云Iot SDK API的简单使用 1.初始化SDK 2.绑定连接配置信息 3.连接服务器 4.上报属性 5.接收命令 二、实现智能家居灯光状态上报 &#x1f516;以下是上报数据到华为云Iot的代码片段&#xff0c;配合串口控制灯光&#xff0c;改变灯…

【论文阅读】Automated Runtime-Aware Scheduling for Multi-Tenant DNN Inference on GPU

该论文发布在 ICCAD’21 会议。该会议是EDA领域的顶级会议。 基本信息 AuthorHardwareProblemPerspectiveAlgorithm/StrategyImprovment/AchievementFuxun YuGPUResource under-utilization ContentionSW SchedulingOperator-level schedulingML-based scheduling auto-searc…

Ant Design Vue详解a-tree-select使用树形选择器,递归渲染数据,点击选项回显,一二级菜单是否可选等问题

后台给的树形数据&#xff1a; {"code": 200,"data": [{"code": "jsd","children": [{"code": "hx","children": [],"name": "航向","id": 8,"libTable…

Vue开始封装全局防抖和节流函数

封装文件 封装文件的实现思路如下&#xff1a; 首先&#xff0c;我们需要定义两个函数&#xff1a;防抖函数和节流函数。这两个函数的目的是为了减少频繁触发某个事件导致的性能问题&#xff1b;防抖函数的实现思路是创建一个计时器变量&#xff0c;用于延迟执行函数。当触发…

ubuntu下docker卸载和重新安装

卸载&#xff1a;步骤一&#xff1a;停止Docker服务 首先&#xff0c;我们需要停止正在运行的Docker服务。打开终端&#xff0c;执行以下命令&#xff1a; sudo systemctl stop docker 步骤二&#xff1a;删除Docker安装包 接下来&#xff0c;我们需要删除已经安装的Docker软件…

Android 渲染机制

1 Android 渲染流程 一般情况下&#xff0c;一个布局写好以后&#xff0c;使用 Activity#setContentView 调用该布局&#xff0c;这个 View tree 就创建好了。Activity#setContentView 其实是通过 LayoutInflate 来把布局文件转化为 View tree 的&#xff08;反射&#xff09;…