《一》Word文字编辑软件---架构设计分析

1,简单介绍

       今天,我们来模拟offic软件中的word文档,运行如图:

运行程序后会出现主界面,顶端的菜单栏包括“文件”“编辑”“格式”“窗口”和“帮助五个主菜单。
菜单栏下面是工具栏,包含了系统常用的功能按钮。工具栏有四个工具条,分别将一组相关功能按钮或控件组织在一起 。

工具栏的第一行有三个工具条: 第一个工具条包括新建、打开、保存、打印等文档管理功能,
第二个工具条包括撤销、重做、剪切、复制和粘贴这些最基本的文本编辑功能,
第三个工具条是各种较高级的文字字体格式设置按钮,包括加粗、倾斜、加下画线,还包括段落对齐及文本颜色设置。

在工具栏的第二行的工具条中有三个组合选择框控件,用于为文档添加段落标号和编号,以及选择特殊字体和更改字号。利用该工具条可以完成更复杂的文档排版和字体美化工作。
此外,在图中还给出了使用该软件制作出的二个文档示例。用Qt版MyWord字处理软件制作出的文档统一以HTML格式存盘,可使用Web浏览器打开观看效果。

开发这个软件主要分为如下三个阶段进行。

(1) 界面设计开发
界面设计开发内容包括菜单系统设计、工具栏设计、多窗体MDI程序框架的建立及多个文档子窗口的管理和控制等。
(2) 文本编辑功能实现
文本编辑功能实现主要包括文档的建立、打开和保存,文本的剪切、复制和粘贴,操作撤销与恢复等这些最基本的文档编辑功能。
(3) 排版美化功能实现
排版美化功能实现包括字体选择,字形、字号和文字颜色的设置,文档段落标号和编号的添加,段落对齐方式设置等高级功能实现。

但是今天,我们首先从架构设计分析,先完成他的文档建立,保存等功能,在一步步去实现其他:

2,创建文件

首先我们新建文件,选择:

名字按自己想法来取,后面这里我们先不要ui文件,选择mainwindow:

后面就创建好了

对于各种新建,保存,另存为等功能,我们需要新建一个C++类。

类的名字自己取,基类选择QTextEdit,因为:QTextEdit是Qt中提供的一个用于文本编辑的控件,支持对富文本进行编辑和格式化,可以用于各种应用程序中,如文本编辑器、笔记应用、电子邮件客户端等。

这样我们就创建好了一个mychild的源文件和头文件。

我们说过,我们建立这个类要完成的就是新建,保存等操作,所以我们要定义一些函数和槽函数来实现:

#ifndef MYCHILD_H
#define MYCHILD_H#include<QTextEdit>class MyChild : public QTextEdit
{Q_OBJECT
public:MyChild();void NewFile();//新建文件bool LoadFile(const QString &filename);//导入文件bool Save();//保存bool SaveAs();//另存为bool SaveFile(QString filename);//保存文件QString userFriendlyCurrentFile();//用户友好型当前文件QString currentFile(){return curFile;};//当前文件void MergeFormationOnWordOrSelection(const QTextCharFormat &format);//格式字体void SetAlign(int align);//对齐void SetStyle(int style);//段落标号,编号等风格protected:void closeEvent(QCloseEvent *event);// 可以通过参数event来控制是否让窗体关闭。private slots:void documentWasModified();//修改文件private:QString curFile;// 当前文件bool isUntitled;//判断是否命名bool MaybeSave();//是否保存void SetCurrentFile(const QString &fileName);//设置当前文件QString StrippedName(const QString &fullFilename);// 脱离文件名称
};

 然后我们就需要在源文件中去实现函数:

3,代码的实现

3.1MyChild()

首先,在构造函数中,我们需要设置如下:

MyChild::MyChild()
{setAttribute(Qt::WA_DeleteOnClose,true);//关闭窗口时销毁isUntitled=true;
}

setAttribute用来设置窗口属性,我们把它设置为:关闭窗口时销毁。

Qt::WA_DeleteOnClose

  • (1)调用close()方法,会向widget发送一个关闭事件(QCloseEvent),如果widget接受了关闭事件,窗口将会隐藏(实际上调用hide())。如果widget不接受关闭事件,那么窗口将什么也不做。也就是说close()方法只会隐藏窗口对象而已,并不会销毁该对象。
  • (2)倘若设置了WA_DeleteOnClose属性,它接收到QCloseEvent事件后,除了调用hide()方法将窗口隐藏外,同时会调用deleteLater()方法将窗口释放掉,不会再占用资源。

常用的setAttribute窗口属性 :

//设置为模态框。(如果再设置无边框窗口,那么模态会失效,不会阻塞其他窗口,须重新设置)
setAttribute(Qt::WA_ShowModal, true);//如果部件接收了关闭事件,则删除这个部件,相当于delete 
setAttribute(Qt::WA_DeleteOnClose, true);//意思是显示小部件而不使其处于活动状态,使它不能获得焦点
setAttribute(Qt::WA_ShowWithoutActivating,true);//使透明效果生效
setAttribute(Qt::WA_TranslucentBackground);//穿透属性,可以使部件不可点击,只显示外形,适合覆盖中的部件使用
setAttribute(Qt::WA_TransparentForMouseEvents);//输入法开关,如果一个编辑框不想让用户使用输入法输入字符打字,就可以将该属性设置为false。
setAttribute(Qt::WA_InputMethodEnabled, false);//使用操作系统原生的本地窗口,可以提高兼容性
//但是在linux下,使用该属性会有问题,因为linux下x11管理的默认原生窗口是白色矩形,就算添加了子部件,也会出现短暂的白色矩形闪烁。
setAttribute(Qt::WA_NativeWindow, true);

3.2 NewFile()

 在新建文件中,我们要保证每次新建文件名称都不能和上次一样,就像在电脑上,你多次点击新建文件 ,他会出现新建文件夹1,2,3.。。。等等。

所以我们要使用静态变量去实现:static局部变量只初始化一次,在函数退出时它不死亡,下次的运算依据是上一次的结果值。

void MyChild::NewFile()// 新建word文件
{static int sequenceNumber=1;isUntitled=true;curFile=tr("Word文档-%1").arg(sequenceNumber++);setWindowTitle(curFile);//把新建的名称设置到标题
}

3.3导入文件LoadFile()

在导入文件中,我们需要先进行判断,保证导入的不能为空,而且不能为只读文件。创建一个QByteArray数组来获取file读取到的所有文件,然后进行筛选,QTextCodecs 可以用于将一些本地编码的字符串转换为 Unicode。

bool MyChild::LoadFile(const QString &filename)// 导入文件
{if(!filename.isEmpty()){if(!QFile::exists(filename)){return false;}QFile file(filename);if (!file.open(QFile::ReadOnly))return false;// 提供一个字节数组,QByteArray可用于存储原始字节(包括“\ 0” )和传统的8位 “\ 0” 端接字符串 .QByteArray data=file.readAll();// Qt 使用 Unicode 来存储、绘制、操作字符串。 在许多情况下,可能希望处理使用不同编码的数据。QTextCodec *codec=Qt::codecForHtml(data);QString str=codec->toUnicode(data);if(Qt::mightBeRichText(str)){//如果是富文本就设置为HTMLthis->setHtml(str);}else{//否则不是富文本设置为简单字符串格式str=QString::fromLocal8Bit(data);this->setPlainText(str);}SetCurrentFile(filename);connect(document(),SIGNAL(contentsChanged()),this,SLOT(documentWasModified()));}
}

 对转化而来的字符串str进行判断,如果是富文本,就设置为HTML,如果不是,就设置为简单格式。

然后把打开的文件设置为当前文件。

再连接个槽函数,当问将发生改变时,调用修改文件函数。

3.4 保存文件

保存文件的时候需要判断是直接保存,还是需要另存为保存。

另存为:

  •  从文件对话框获取文件路径。
  •  如果路径不为空,则保存文件saveFile()
bool MyChild::Save()// 保存
{if(isUntitled){return SaveAs();}else{return SaveFile(curFile);}
}bool MyChild::SaveAs()// 另存为
{QString filename=QFileDialog::getSaveFileName(this,tr("另存为"),curFile,tr("HTML 文档(*.html *html);;所有文件(*.*)"));if(filename.isEmpty()){return false;}return SaveFile(filename);
}bool MyChild::SaveFile(QString filename)// 保存文件
{if(!(filename.endsWith(".htm",Qt::CaseInsensitive)||filename.endsWith(".html",Qt::CaseInsensitive))){//默认保存为 HTML 文档filename+=".html";}QTextDocumentWriter writer(filename);bool success=writer.write(this->document());if(success){SetCurrentFile(filename);}return success;
}

在保存文件这个SaveFile()函数里,我们首先就是判断,文件结尾是否带有.htm或者.html后缀,如果字符串的结尾引用.htm或者.html则返回true,否则返回false。忽略大小写

CaseInsensitive:不区分大小写,没有后缀的话,我们需要加上。

QTextDocumentWriter:用于将QTextDocument写入文件或其他设备的与格式无关的接口。

 保存文件对话框(对于某些格式QTextDocumentWriter可直接保存,其他不支持的格式就用QTextStream以流的形式保存 )

3.5 关闭文件closeEvent()

void MyChild::closeEvent(QCloseEvent *event)
{if(MaybeSave()){event->accept();}else{event->ignore();}
}

 MaybeSave()

bool MyChild::MaybeSave()//判断是否修改且保存文档
{if(!document()->isModified()){return true;}QMessageBox::StandardButton ret;ret=QMessageBox::warning(this,tr("Qt Word"),tr("文件'%1'已经被修改,是否保存?").arg(userFriendlyCurrentFile()),QMessageBox::Save|QMessageBox::Discard|QMessageBox::Cancel);if(ret=QMessageBox::Save){return Save();}else if(ret==QMessageBox::Discard){return false;}return true;
}

我们先进行判断文件是否被修改过, 是的话返回true; 

3.6 修改文件documentWasModified()

文件更改标签

 编辑器内容是否被更改,可以使用QTextDocument类的isModified()函数获知,这里使用了QTextEdit类,document()函数来获取

 它的QTextDocument类对象。然后使用setWindowModified()函数设置窗口的更改状态标志“*”,如果参数为true,则将在标题中设置了“[*]”号的地方显示“*”号,表示该文件已经被修改。

void MyChild::documentWasModified()
{//在设置改变的时候,设置窗口已修改setWindowModified(document()->isModified());
}
QString MyChild::userFriendlyCurrentFile()
{return StrippedName(curFile);
}
QString MyChild::StrippedName(const QString &fullFilename)
{return QFileInfo(fullFilename).fileName();
}

3.7设置当前文件:SetCurrentFile

 canonicalFilePath ()可以除去路径中符号链接,如“.”和“..”等符号。这个函数只是将加载文件的路径首先保存到curFile中,然后再进行一些状态的设置

void MyChild::SetCurrentFile(const QString &fileName)// 设置当前文件
{curFile=QFileInfo(fileName).canonicalFilePath();isUntitled=false; //文件已经被保存过document()->setModified(false);//文档没有被更改过setWindowModified(false);//窗口不显示被更改标志setWindowTitle(userFriendlyCurrentFile()+"[*]");//设置窗口标题,userFriendlyCurrentFile ()返回文件名
}

3.8 格式字体设置MergeFormationOnWordOrSelection

 先定位光标就是当前选中文本光标位置。
WordUnderCursor:选择光标下的单词。如果光标不在可选字符的字符串中,则不选择任何文本。

设置字体格式,调用QTextCursor的mergeCharFormat()函数,将参数format所表示的格式应用到光标所在处的字符上
void MyChild::MergeFormationOnWordOrSelection(const QTextCharFormat &format)
{QTextCursor cursor=this->textCursor();if(!(cursor.hasSelection())){cursor.select(QTextCursor::WordUnderCursor);}cursor.mergeCharFormat(format);this->mergeCurrentCharFormat(format);
}

 3.9设置段落对齐格式SetAlign

void MyChild::SetAlign(int align)
{if(align==1){this->setAlignment(Qt::AlignLeft|Qt::AlignAbsolute);//水平靠左}else if(align==2){this->setAlignment(Qt::AlignCenter);//水平方向居中}else if(align==3){this->setAlignment(Qt::AlignRight|Qt::AlignAbsolute);//}else if(align==4){this->setAlignment(Qt::AlignJustify);//水平方向两端对齐}
}

 设置文本光标,执行文本首部,QTextListFormat 主要用于描述文本符号,编号的格式。

//段落编号
void MyChild::SetStyle(int style)
{//多行文本框光标插入文本QTextCursor cursor=this->textCursor();if(style!=0){QTextListFormat::Style stylename=QTextListFormat::ListDisc;//样式为圆圈switch(style){default:case 1:stylename=QTextListFormat::ListDisc;break;case 2:stylename=QTextListFormat::ListCircle;//空心圆break;case 3:stylename=QTextListFormat::ListSquare;//方块break;case 4:stylename=QTextListFormat::ListDecimal;//阿拉伯数字break;case 5:stylename=QTextListFormat::ListLowerAlpha;//拉丁字符小写break;case 6:stylename=QTextListFormat::ListUpperAlpha;//拉丁字符大写break;case 7:stylename=QTextListFormat::ListLowerRoman;//小写罗马数字break;case 8:stylename=QTextListFormat::ListUpperRoman;//大写罗马break;}cursor.beginEditBlock();QTextBlockFormat blockFmt=cursor.blockFormat();QTextListFormat listFmt;if(cursor.currentList()){listFmt=cursor.currentList()->format();}else{listFmt.setIndent(blockFmt.indent()+1);blockFmt.setIndent(0);cursor.setBlockFormat(blockFmt);}listFmt.setStyle(stylename);cursor.createList(listFmt);cursor.endEditBlock();}else{QTextBlockFormat bfmt;bfmt.setObjectIndex(-1);cursor.mergeBlockFormat(bfmt);}
}

 

cursor.beginEditBlock()是Qt中的一个函数,用于开始编辑操作的分组,以便在多个编辑操作之间进行撤销和重做。调用此函数后,任何对文本的更改都将被视为一组操作。只有在调用了cursor.endEditBlock()函数之后,才会结束此组操作,之后才能进行下一个操作分组。

此代码段中,

  • 首先通过cursor.blockFormat()获取当前光标所在文本块的格式,并通过cursor.currentList()判断当前光标所在的文本块是否是列表。
  • 如果是,则获取列表格式并赋值给listFmt变量;
  • 如果不是,则设置listFmt的缩进为当前文本块缩进加1,并将文本块缩进设置为0。
  • 然后通过cursor.createList(listFmt)创建一个新的列表,并将样式设置为stylename
  • 最后调用cursor.endEditBlock()函数结束此次编辑分组。

 如果style=0:

创建一个QTextBlockFormat对象,bfmt的对象索引设置为-1,
用bfmt块格式修改当前块(或包含在选择中的所有块)的块格式。

感谢阅读!!!!!

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何判断海外住宅ip的好坏?

在海外IP代理中&#xff0c;住宅IP属于相对较好的资源&#xff0c;无论是用于工作、学习、还是娱乐&#xff0c;都能得到较好的使用效果。作为用户&#xff0c;该如何判断海外住宅IP的好坏呢&#xff1f; 稳定性与可靠性&#xff1a;海外住宅IP相比动态IP地址&#xff0c;通常具…

Java全局异常处理,@ControllerAdvice异常拦截原理解析【简单易懂】

https://www.bilibili.com/video/BV1sS411c7Mo 文章目录 一、全局异常处理器的类型1-1、实现方式一1-2、实现方式二 二、全局异常拦截点2-1、入口2-2、全局异常拦截器是如何注入到 DispatcherServlet 的 三、ControllerAdvice 如何解析、执行3-1、解析3-2、执行 四、其它4-1、设…

电脑提示找不到ffmpeg.dll无法继续执行代码怎么办?

电脑提示找不到找不到ffmpeg.dll无法继续执行代码怎么办&#xff0c;有什么好的解决办法&#xff0c;出现这样的弹出就会导致软件无法打开或者是异常关闭&#xff0c;找不到dll文件&#xff0c;是一个非常重要的电脑使用问题&#xff0c;会给使用者带来许多的麻烦。那么找不到d…

LeetCode746:使用最小花费爬楼梯

题目描述 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费。 代码 …

MongoDB和AI 赋能行业应用:制造业和汽车行业

欢迎阅读“MongoDB和AI 赋能行业应用”系列的第一篇。 本系列重点介绍AI应用于不同行业的关键用例&#xff0c;涵盖制造业和汽车行业、金融服务、零售、电信和媒体、保险以及医疗保健行业。 随着人工智能&#xff08;AI&#xff09;在制造业和汽车行业的集成&#xff0c;传统…

Tableau学习2.0版——复习

官网下载链接&#xff1a;https://www.tableau.com/zh-cn/support/releases 学生账户申请链接&#xff1a;https://www.tableau.com/zh-cn/academic/students。直接去学信网下载学籍在线验证作为申请证明。 目录 1、可视化原理 2、基础图表制作 2.1 对比分析&#xff08;比…

@游戏行业er!MongoDB广州线下沙龙邀您报名!

随着游戏和应用程序的发展&#xff0c;数据变得越来越重要。在为您的下一个游戏选择数据库时&#xff0c;数据库管理者常常会面对灵活性、可扩展性、可靠性、运营效率等问题或挑战。 MongoDB在游戏开发领域有着广泛的应用&#xff0c;灵活数据模型可以存储和处理各种类型的数据…

PHP 提取数组中的特定的值

需求&#xff1a; 前端展示&#xff1a; &#xff08;1&#xff09;之前的页面&#xff1a; &#xff08;2&#xff09;修改后的页面&#xff1a; 之前接口返回的数据 &#xff1a; 解决办法&#xff1a;提取tags 中的 ’约 的数组 添加到一个新的数组中去 1&#xff1a;一开…

2024年去除视频水印的5种方法

如果你从事电影剪辑或者视频编辑工作&#xff0c;你经常需要从优酷、抖音、TikTok下载各种视频片段……。 通常这些视频带有水印和字幕。一些免费软件如CapCut、canva、Filmora也会给你制作的视频打上水印&#xff0c;这些水印嵌入在视频内部。 2024年去除视频水印的5种方法 …

Mysql-用户变量的声明与使用

#声明变量 #1.标识符不能以数字开头 #2.只能使用_或$符号&#xff0c;不能使用其他符号 #3.不能使用系统关键字 setuserName刘德华; select userName:刘青云;#将赋值与查询结合 #查询变量、使用变量&#xff0c;匿名的时候建议加上as select userName as 读取到的userName变量…

Golang面向对象编程(二)

文章目录 封装基本介绍封装的实现工厂函数 继承基本介绍继承的实现字段和方法访问细节多继承 封装 基本介绍 基本介绍 封装&#xff08;Encapsulation&#xff09;是面向对象编程&#xff08;OOP&#xff09;中的一种重要概念&#xff0c;封装通过将数据和相关的方法组合在一起…

Java转Kotlin调用JNI方法异常

一、背景 Java调用JNI方法时没有任何问题&#xff0c;但是使用Java转Kotlin以后出现了崩溃异常&#xff1a;A java_vm_ext.cc:597] JNI DETECTED ERROR IN APPLICATION: jclass has wrong type: 校验参数后没有任何变化&#xff0c;经过分析验证找到解决方案 二、原因…

若依生成树表和下拉框选择树表结构(在其他页面使用该下拉框输入)

1.数据库表设计 生成树结构的主要列是id列和parent_id列&#xff0c;后者指向他的父级 2.来到前端代码生成器页面 导入你刚刚写出该格式的数据库表 3.点击编辑&#xff0c;来到字段 祖籍列表是为了好找到直接父类&#xff0c;不属于代码生成器方法&#xff0c;需要后台编…

【XSRP软件无线电】基于软件无线电平台的QPSK频带通信系统设计

目录&#xff1a; 目录&#xff1a; 一、绪论 1.1 设计背景 1.2 设计目的 二、系统总体方案 2.1 专题调研题目 2.2 调研背景 2.3 设计任务解读 2.4 设计原理 2.4.1 原理框图 2.4.2 功能验证 三、软件设计 3.1 程序解读 3.2 程序设计 3.3 仿真结果&#xff1a; 四、程序代码分析…

网络基础-SSH协议(思科、华为、华三)

SSH&#xff08;Secure Shell&#xff09;是一种用于安全远程访问和安全文件传输的协议。它提供了加密的通信通道&#xff0c;使得用户可以在不安全的网络上安全地远程登录到远程主机&#xff0c;并在远程主机上执行命令、访问文件以及传输文件&#xff0c;本篇主要讲解命令执行…

SpringAI集成本地AI大模型ollama(调用篇)非常简单!!

一&#xff0c;前提准备本地ai模型 1&#xff0c;首先需要去ollama官网下载开源ai到本地 网址&#xff1a;Ollama 直接下载到本地&#xff0c;然后启动ollama 启动完成后&#xff0c;我们可以在cmd中执行ollama可以看到相关命令行 2&#xff0c; 下载ai moudle 然后我们需要…

基于C#开发web网页模板流程-登录界面

前言&#xff0c;首先介绍一下本项目将要实现的功能 &#xff08;一&#xff09;登录界面 实现一个不算特别美观的登录窗口&#xff0c;当然这一步跟开发者本身的设计美学相关&#xff0c;像蒟蒻博主就没啥艺术细胞&#xff0c;勉强能用能看就行…… &#xff08;二&#xff09…

【vector】迭代器

Vector的基本数据结构 可以看到end指向的是数组的最后一个元素&#xff1b; 那么在使用函数遍历的时候就要注意这种清理&#xff1b; 比如计算一个数组前5个数字的最小值&#xff1b; vector<int> prices{2,1,4,2,0,52,12};auto iter_min min_element(prices.begin(),pr…

NSSCTF | [LitCTF 2023]我Flag呢?

这道题没啥好说的&#xff0c;题目标签为源码泄露&#xff0c;我们直接CtrlU查看网页源码就能在最后找到flag 本题完

深入学习指针2

前言 hello,我又来了&#xff0c;今天有我继续带领大家深入的学习指针&#xff0c;通过上次的学习&#xff0c;我们已经了解到了指针的基本概念&#xff0c;指针如何使用&#xff0c;指针使用的益处&#xff0c;以及一些相关的概念&#xff0c;那今天我们就继续深入的学习&am…