【Qt】QTextEdit/QPlainTextEdit 实现 Tab 键多行缩进与反缩进
【Qt】QTextEdit/QPlainTextEdit 实现 Tab 键多行缩进与反缩进
文章目录
- I - 主要原理
- II - 代码实现
- 2.1 - 自定义类
- 2.2 - 实现 Tab 缩进
- 2.3 - 实现反缩进
- III - 参考链接
I - 主要原理
由于 QTextEdit 和 QPlainTextEdit ,都无法实现多行选中缩进与反缩进,选中多行后,按下缩进或反缩进,选中文本都会清空,并替换为(反)缩进或空格。因此会造成使用很不方便和低效的问题。
需要实现一个类继承自 QTextEdit 或 QPlainTextEdit 类并重写其 keyPressEvent 接口,处理 Tab 按键
II - 代码实现
2.1 - 自定义类
自定义类 TextEdit,视使用目的继承 QTextEdit 或 QPlainTextEdit,
#include <QTextEdit>
// 或 #include <QPlainTextEdit>class TextEdit : public QTextEdit
// 或 class TextEdit : public QPlainTextEdit
{Q_OBJECT
public:explicit TextEdit(QWidget* parent = nullptr);protected:void keyPressEvent(QKeyEvent *e) override; // 键盘事件// 添加 override 编译时检查是否重写父类函数,防止敲错
private:// 根据是否保留制表符,设置为 \t 或空格QString m_indentation = "\t"; // 或 " "
}
2.2 - 实现 Tab 缩进
需要包含头文件
#include <QTextCursor> // 获取光标实例
#include <QTextBlock> // 文本块
void TextEdit::keyPressEvent(QKeyEvent* e)
{// 判断 Tab 按键 以及排除 Shift, Ctrl, Alt 等控制按键的情况,因为通常通过 Shift + Tab 实现反缩进if (Qt::Key_Tab && Qt::NoModifier == e->modifiers()){// 获取光标实例QTextCursor cursor = textCursor();// 在无选中的情况下仅插入一个缩进,并返回if (!cursor.hasSelection()){insertPlainText(m_indentation);return;} // 记录光标选中内容的开始和结束位置 spos(start position), epos(end position)int spos = cursor.anchor();int epos = cursor.position();// 在由下向上选中时,交换开始和结束位置if(spos > epos){int hold = spos;spos = epos;epos = hold;}// 获取开始文本块序号cursor.setPosition(spos, QTextCursor::MoveAnchor);int sblock = curs.block().blockNumber();// 获取结束文本块序号cursor.setPosition(epos, QTextCursor::MoveAnchor);int eblock = curs.block().blockNumber();// 开始处理文本缩进cursor.setPosition(spos, QTextCursor::MoveAnchor);// QTextCursor 在编辑文本块时需要调用此方法cursor.beginEditBlock();// 依次遍历所选中的文本块,并在起始处插入缩进for(int i = 0; i <= (eblock - sblock); ++i){cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);cursor.insertText(m_indentation);cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);}// 编辑文本块结束cursor.endEditBlock();// 将光标的选择设置为跨越所有涉及的行,也就是说保留之前内容的选中状态,并加入缩进为选中内容。// 将光标移动至开始位置cursor.setPosition(spos, QTextCursor::MoveAnchor);cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);while(cursor.block().blockNumber() < eblock){// 使用 QTextCursor::KeepAnchor 为选中文本cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);}// 移动至最后一个文本块结束位置cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);// 编辑器设置此光标,结束!setTextCursor(curs);} // end if
}
2.3 - 实现反缩进
反缩进的实现较缩进难实现,需要知道待反缩进的每一个文本行开头有多少个缩进位置,以及不足时如何处理。
int TextEditor::GetIndentationSpaces(const QString& blockText)
{int indentationSpaceCount = 0;// 遍历此文本块的每一个字符,检查是否包含空格或制表符for (int i = 0; i < blockText.size() && QString("\t ").contains(blockText[i]); ++i){// 若为空格if (' ' == blockText[i]){++indentationSpaceCount;}else // 若为制表符 \t{indentationSpaceCount += tabStopDistance() / fontMetrics().averageCharWidth();}}return indentationSpaceCount;
}
继续在 keyPressEvent 接口中实现 Shift + Tab 反缩进,或者也可以使用 Qt::Key_BackTab 枚举,这里使用与缩进处理文本不同的方式
void TextEdit::keyPressEvent(QKeyEvent* e)
{// ...else if (Qt::Key_BackTab == e->key()){// 获取光标实例QTextCursor cursor = textCursor();// 没有选中内容时,去除当前行的一个缩进并返回if (!cursor.hasSelection()){int spaceCount = std::min(GetIndentationSpaces(cursor.block().text(), m_indentation.size());cursor.movePosition(QTextCursor::StartOfLine); // movePosition 第二个参数缺省值为 QTextCursor::MoveMode::MoveAnchor// 从行起始处删除当前的一个缩进长度cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, spaceCount);cursor.removeSelectedText();return;}// 记录光标选中内容的开始和结束位置 int startPos = cursor.anchor();int endPos = cursor.position();// 在由下向上选中时,交换开始和结束位置if(startPos > endPos){std::swap(startPos, endPos);}// 若选中内容未选中首行的全部内容,更新开始位置cursor.setPosition(startPos, QTextCursor::MoveAnchor);cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);startPos = cursor.position()// 重新选中curosr.setPosition(endPos, QTextCursor::MoveAnchor); cursor.setPosition(startPos);cursor.setPosition(endPos, QTextCursor::KeepAnchor);QString content = cursor.selection().toPlainText();QStringList list = content.split("\n");// 减少开销int sz = list.size();for (int = 0; i < sz; ++i){int spaceCount = GetIndentationSpaces(list[i]);spaceCount = std::min(spaceCount, m_indentation.size());// 若该行无缩进则跳过if (0 == spaceCount){continue;}// 截断list[i] = list[i].mid(spaceCount); }// 文本还原content = list.join("\n");// 移除选中文本,替换为去除缩进的文本cursor.removeSelectedText();cursor.insertText(content);// 重新使用光标选中文本,保证可连续反缩进cursor.setPosition(startPos);cursor.setPosition(startPos() + content.size(), QTextCursor::KeepAnchor);setTextCursor(cursor);return;}
}
最后不要忘了, 调用父类的keyPressEvent ,以保证不影响其他的键盘事件
QTextEdit::keyPressEvent(e);
// 或
QPlainTextEdit::keyPressEvent(e);
III - 参考链接
-
https://www.qtcentre.org/threads/33582-indent-selection-in-QPlainTextEdit
-
https://codereview.stackexchange.com/questions/33899/qplaintextedit-subclass-function-to-indent-lines-in-selection
相关文章:
【Qt】QTextEdit/QPlainTextEdit 实现 Tab 键多行缩进与反缩进
【Qt】QTextEdit/QPlainTextEdit 实现 Tab 键多行缩进与反缩进 文章目录 I - 主要原理II - 代码实现2.1 - 自定义类2.2 - 实现 Tab 缩进2.3 - 实现反缩进 III - 参考链接 I - 主要原理 由于 QTextEdit 和 QPlainTextEdit ,都无法实现多行选中缩进与反缩进ÿ…...
C++缺陷与思考
数组隐式转换为指针 size_t func(int a[10]) {return sizeof(a); }int a[100]; func(a); // 指针大小 sizeof(a); // 数组大小函数的参数看似是一个数组形式,但事实上他已经退化为指针了,也就是等价于size_t func(int* a),而数组作为参数传…...
无公网ip环境使用DS file软件远程访问内网群晖NAS中储存的文件
文章目录 1. 群晖安装Cpolar2. 创建TCP公网地址3. 远程访问群晖文件4. 固定TCP公网地址5. 固定TCP地址连接 DS file 是一个由群晖公司开发的文件管理应用程序,主要用于浏览、访问和管理存储在群晖NAS(网络附加存储)中的文件。这个应用程序具有…...
软件工程基础
本博客地址:https://security.blog.csdn.net/article/details/136446772 一. 软件工程 1、软件危机。具体表现为:软件开发进度难以预测、软件开发成本难以控制、软件功能难以满足用户期望、软件质量无法保证、软件难以维护和软件缺少适当的文档资料。 …...
alzet供应商你值得拥有
在20世纪70年代,ALZE公司研发出来一款巧妙的药物输送装置——Alzet osmotic pump。这款产品如胶囊般精致小巧,它既有胶囊的外表,也具有胶囊的作用。在Alzet osmotic pump中藏有可以装配药物溶液的空间。此款胶囊泵如同一个小投递员࿰…...
x86中的TSS与任务切换
前言 今天在学习《深入理解Linux内核》的时候,发现出现了一个新的名词TSS(Task-State Segment),这还是我第一次了解到原来x86提供了硬件级别的任务切换功能,之前以为任务切换都是操作系统实现的来着,这里也…...
打造去中心化透明储蓄罐:Solidity智能合约的又一实践
一、案例背景 传统的储蓄罐通常是由个人或家庭使用,用于存放硬币或小额纸币。然而,这样的储蓄罐缺乏透明性,用户无法实时了解储蓄情况,也无法确保资金的安全性。 通过Solidity智能合约,我们可以构建一个去中心化…...
Java Mybatis数据库面试题
Java Mybatis数据库面试题 前言1、什么是 Mybatis?2、Mybaits 的优缺点:3、SQL 注入如何防止?4、MyBatis 框架适用场合:5、MyBatis 与 Hibernate 有哪些不同?6、#{}和${}的区别是什么?7、当表中的字段名和实…...
LeetCode-第14题-最长公共前缀
1.题目描述 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 ""。 2.样例描述 3.思路描述 按字符串数组每个数组的长度,将字符串数组从小到大排序;他们的公共前缀一定小于或等于最长元素长度…...
TCP/UDP模型:2024/2/29
作业1:TCP模型 服务器端: #include <myhead.h> #define SER_IP "192.168.199.129" #define SER_PORT 8899int main(int argc, const char *argv[]) {//1.创建用于连接的套接字文件int sfdsocket(AF_INET,SOCK_STREAM,0);if(sfd-1){per…...
微信如何设置自动回复消息,提升沟通效率的?
在日常微信聊天过程中,我们可能会频繁遇到相同问题的客户提问,特别是对于从事销售工作的朋友们而言,客户添加好友后的第一句话常常为“在吗”或“你好”。当我们拥有大量好友,手动逐一回复可能会耗费大量时间。因此,自…...
PCIE的BAR空间
1.PCIe 简介 PCIe(Peripheral Component Interconnect Express)是一种高速 串行计算机扩展总线标准,主要用于连接主板上的中央处理器(CPU)和 各种外部设备,如显卡、声卡、硬盘等。PCIe 总线取代了传统的 PC…...
11.互信息-机器学习模型性能的常用的评估指标
互信息(Mutual Information)是机器学习中常用的一种评估指标,特别是在无监督学习和聚类分析中。它用于衡量两个随机变量之间的相关性或相似性。 定义 给定两个随机变量X和Y,它们的互信息I(X;Y)定义如下: 其中&…...
SpringCloud(18)之Sleuth +Zipkin链路追踪
一、Zipkin介绍 Zipkin是一个开放源代码分布式的跟踪系统,它可以帮助收集服务的时间数据,以解决微服务架构中的延迟问 题,包括数据的收集、存储、查找和展现。每个服务向zipkin报告计时数据,zipkin会根据调用关系通 过Zipkin UI…...
GVA快速使用
1. clone 代码, 使用goland打开Server目录, 使用vsc打开前端web目录,运行后端,前端 gin-vue-admin后台管理系统 - 知乎 (zhihu.com) 2.了解端口配置 参考, 基于Go的后台管理框架Gin-vue-admin_go vue admin-CSDN博客…...
Linux文本处理三剑客:awk(内置函数详解笔记)
Linux系统中,AWK 是一个非常强大的文本处理工具,它的内置函数使得对文本数据进行处理更加高效和便捷。 本文将介绍 AWK 内置函数的几种主要类型: 算数函数字符串函数时间函数位操作函数其他常用函数 我们将使用一个示例文本文件来演示这些函…...
C++调用lua函数
C 调用Lua全局变量(普通) lua_getglobal(lua, "width");int width lua_tointeger(lua,-1);lua_pop(lua,1);std::cout << width << std::endl;lua_close(lua); 这几行代码要放到lua_pcall(lua, 0,0,0);之后才可以. C给lua传递变量 lua_pushstring(lua, …...
java找工作之Mybatis(入门及xml配置相关)
Mybatis 学习Mybatis就要学会查看官网,官网地址如下:<MyBatis中文网 > 1、简介 1.1什么是Mybatis MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取…...
如何保证 HTTPS 证书的有效性?
随着互联网的蓬勃发展,保障用户在网络上的隐私和安全成为至关重要的任务。HTTPS证书,作为一种安全套接字层协议,扮演着网站安全的守护者。 1. 什么是HTTPS 证书? HTTPS(HyperText Transfer Protocol Secureÿ…...
Qt 简约美观的动画 摆钟风格 第十季
😊 今天给大家分享一个摆钟风格的加载动画 😊 效果如下: 最近工作忙起来了 , 后续再分享其他有趣的加载动画吧. 一共三个文件 , 可以直接编译运行 //main.cpp #include "LoadingAnimWidget.h" #include <QApplication> #include <Q…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
