目录
[1. 文件操作功能](#1. 文件操作功能)
[2. 文件管理功能](#2. 文件管理功能)
[3. 文本编辑功能](#3. 文本编辑功能)
[4. 窗口事件管理](#4. 窗口事件管理)
[5. 状态变量](#5. 状态变量)
[6. 保存确认](#6. 保存确认)
[1. 设置窗口关闭时销毁对象](#1. 设置窗口关闭时销毁对象)
[2. 初始化文件状态](#2. 初始化文件状态)
[1. 静态变量 sequenceNumber](#1. 静态变量 sequenceNumber)
[2. 设置 isUntitled 为 true](#2. 设置 isUntitled 为 true)
[3. 生成文件名](#3. 生成文件名)
[4. 设置窗口标题](#4. 设置窗口标题)
[saveAs() 的作用:](#saveAs() 的作用:)
[1. 设置左对齐 (绝对左对齐):](#1. 设置左对齐 (绝对左对齐):)
[2. 设置居中对齐:](#2. 设置居中对齐:)
[3. 设置右对齐 (绝对右对齐):](#3. 设置右对齐 (绝对右对齐):)
[4. 设置两端对齐:](#4. 设置两端对齐:)
[1. 获取文本光标:](#1. 获取文本光标:)
[2. 判断是否设置样式:](#2. 判断是否设置样式:)
[3. 设置标号/编号样式:](#3. 设置标号/编号样式:)
[5. 编辑文本块(段落):](#5. 编辑文本块(段落):)
[6. 设置当前段落的格式:](#6. 设置当前段落的格式:)
[7. 获取或创建列表格式:](#7. 获取或创建列表格式:)
[8. 设置列表样式并创建列表:](#8. 设置列表样式并创建列表:)
[9. 结束编辑块:](#9. 结束编辑块:)
[10. 取消标号/编号样式:](#10. 取消标号/编号样式:)
mychild类的设计
 MyChild 类是一个继承自 QTextEdit 的自定义类。它用来作为一个多文档界面(MDI)或类似功能的子窗口。
 QTextEdit 是 Qt 提供的一个文本编辑控件,支持富文本(HTML 格式)和普通文本编辑。MyChild 继承自 QTextEdit,并添加了一些自定义功能来增强文件管理和界面交互的功能。
mychild.h文件
            
            
              cpp
              
              
            
          
          #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 mergeFormatOnWordOrSelection(const QTextCharFormat &format);    
    void setAlign(int align);                                            
    void setStyle(int style);                                          
protected:
    void closeEvent(QCloseEvent *event);  
private slots:
    void documentWasModified();  
private:
    bool maybeSave(); 
    void setCurrentFile(const QString &fileName);  
    QString strippedName(const QString &fullFileName);  
    QString curFile;  
    bool isUntitled;  
};
#endif // MYCHILD_H主要功能概述
1. 文件操作功能
- 新建文件:
- void newFile():创建一个新的文本文件,并初始化状态。
 
- 加载文件:
- bool loadFile(const QString &fileName):从指定文件中加载文本内容到编辑器。
 
- 保存文件:
- bool save():保存当前内容到文件(覆盖保存)。
- bool saveAs():保存内容为一个新的文件(另存为)。
- bool saveFile(QString fileName):实际执行文件保存的函数。
 
2. 文件管理功能
- 当前文件状态:
- QString userFriendlyCurrentFile():获取简化的、友好的当前文件名(去掉路径)。
- QString currentFile():获取当前文件的完整路径。
 
- 辅助函数:
- QString strippedName(const QString &fullFileName):从文件路径中提取文件名。
- void setCurrentFile(const QString &fileName):更新当前文件路径并设置文件状态。
 
3. 文本编辑功能
- 文本格式设置:
- void mergeFormatOnWordOrSelection(const QTextCharFormat &format):将格式应用于当前文字或选中文字。
 
- 段落设置:
- void setAlign(int align):设置段落的对齐方式(如左对齐、居中等)。
- void setStyle(int style):设置段落样式(如编号、项目符号等)。
 
4. 窗口事件管理
- 关闭事件:
- void closeEvent(QCloseEvent *event):在窗口关闭时执行检查,决定是否允许关闭。
 
- 文档修改事件:
- void documentWasModified():监听文档内容修改,更新窗口标题以提示用户保存修改。
 
5. 状态变量
- 文件状态:
- QString curFile:存储当前文件的完整路径。
- bool isUntitled:标识当前文件是否是新建文件(未命名文件)。
 
6. 保存确认
- bool maybeSave():判断是否需要保存未保存的修改,并提示用户选择是否保存。
用途
- 文本编辑器:
- 这是一个基础的文本编辑器窗口,具有读取、保存、编辑文本文件的能力。
 
- 文件管理:
- 处理文件名、文件状态,以及是否需要保存未保存的更改。
 
- 增强功能:
- 添加了对段落格式(对齐、样式)的支持,可以用于实现一个简单的文字处理器。
 
mychild.cpp文件
析构函数
            
            
              cpp
              
              
            
          
          MyChild::MyChild()
{
    setAttribute(Qt::WA_DeleteOnClose);
    isUntitled = true;
}功能分析
1. 设置窗口关闭时销毁对象
setAttribute(Qt::WA_DeleteOnClose);- 作用:
- 设置了窗口关闭时自动销毁窗口对象(调用 delete删除)。
- Qt::WA_DeleteOnClose是一个属性,表示当窗口关闭时,- MyChild对象会自动从内存中清除(不需要手动管理内存)。
- 这是为了防止内存泄漏,尤其是在使用子窗口(如 MDI 子窗口)时,关闭后需要及时释放资源。
 
- 设置了窗口关闭时自动销毁窗口对象(调用 
2. 初始化文件状态
isUntitled = true;- 作用:
- 将 isUntitled设置为true,表示当前文档是一个未命名的新文件。
- 用途:
- 在新建文件时,标记文件未命名,后续需要用户保存或指定一个文件名。
- 与后续的文件保存功能(如 save()或saveAs())配合使用,用于决定是否需要弹出文件保存对话框。
 
 
- 将 
新建文件
            
            
              cpp
              
              
            
          
          void MyChild::newFile() // 新建word文件
{
    static int sequenceNumber = 1;
    isUntitled = true;
    curFile = tr("编辑文档%1").arg(sequenceNumber ++);
    setWindowTitle(curFile);
}功能分析
1. 静态变量 sequenceNumber
        static int sequenceNumber = 1;- 作用:
- static关键字使得- sequenceNumber在多次调用- newFile()时保持其值,而不是每次调用时都重置为- 1。
- 变量 sequenceNumber用于为每个新文件生成一个唯一的编号。每次创建新文件时,这个编号会递增,确保每个文件的名称是唯一的。
 
2. 设置 isUntitled 为 true
        isUntitled = true;- 作用:
- 将 isUntitled标记为true,表示当前文件是一个未命名的新文件。
- 这也意味着用户需要在保存文件时给文件命名。
 
- 将 
3. 生成文件名
curFile = tr("编辑文档%1").arg(sequenceNumber++);- 作用:
- 生成文件的名称,格式为 编辑文档X,其中X是一个递增的数字。
- tr()是 Qt 中的一个翻译函数,表示该字符串可以被翻译。这里的用法是为将来可能的多语言支持做准备。
- arg(sequenceNumber++)用来将- sequenceNumber的值插入到字符串中,生成类似- 编辑文档1、- 编辑文档2等名称。- sequenceNumber++先使用当前的数字,然后再递增。
 
- 生成文件的名称,格式为 
4. 设置窗口标题
setWindowTitle(curFile);- 作用:
- 设置窗口的标题为当前文件名,即 编辑文档X。
- 这可以帮助用户知道他们当前在编辑哪个文件,尤其是当有多个文件打开时。
 
- 设置窗口的标题为当前文件名,即 
导入文件
            
            
              cpp
              
              
            
          
          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 data = file.readAll();
        QTextCodec *codec = Qt::codecForHtml(data);
        QString str = codec->toUnicode(data);
        if (Qt::mightBeRichText(str)) {  
            this->setHtml(str);
        } else {     
            str = QString::fromLocal8Bit(data);
            this->setPlainText(str);
        }
        setCurrentFile(fileName);
        connect(document(), SIGNAL(contensChanged()), this, SLOT(documentWasModified()));
        return true;
    }
}功能分析:
- 
文件存在性检查 :如果 fileName为空或文件不存在,返回false表示加载失败。
- 
打开文件 :尝试以只读模式 ( QFile::ReadOnly) 打开文件。如果打开失败,则返回false。
- 
读取文件内容 :通过 file.readAll()读取文件的所有内容,并将其存储在QByteArray中。
- 
处理编码 :使用 QTextCodec类判断文件的编码,并将字节数据转换为QString字符串。Qt::codecForHtml(data)会根据文件的内容猜测合适的编码类型。
- 
处理富文本与普通文本: - 如果文件是富文本(如 HTML),使用 setHtml()方法将其内容设置到QTextEdit中。
- 如果文件是纯文本,使用 setPlainText()方法将其内容设置到QTextEdit中。
 
- 如果文件是富文本(如 HTML),使用 
- 
更新当前文件信息 :调用 setCurrentFile(fileName)方法,更新当前文件的路径信息。
- 
文件修改监听 :连接 document()的contentsChanged信号与当前对象的documentWasModified槽函数,监听文件内容的变化。当文档内容改变的时候,文件名称旁边会出现一个"*";
- 
返回成功 :如果所有操作都成功完成,返回 true。
这个方法可以用于打开并加载文本或 HTML 格式的文件,根据文件的格式来决定显示方式。
修改文件
            
            
              cpp
              
              
            
          
          void MyChild::documentWasModified() // 修改文件
{
    // 在设置改变的时,设置窗口已修改
    setWindowModified(document()->isModified());
}MyChild::documentWasModified() 是 MyChild 类中的一个槽函数, QTextDocument(在 QTextEdit 中使用)内容发生变化时被调用。它的作用是更新窗口的修改状态,标记当前文档是否已被修改。
- 
document():- document()返回当前- QTextEdit控件关联的- QTextDocument对象。- QTextDocument是 Qt 中用来表示文本内容的类,提供了对文档内容的处理能力。
 
- 
document()->isModified():- isModified()是- QTextDocument的一个函数,用来检查文档是否已经被修改。它返回一个布尔值:- true:文档已被修改。
- false:文档没有修改。
 
 
- 
setWindowModified():- **setWindowModified()是QWidget类的方法,用来设置窗口的修改状态。**如果一个窗口已被修改,通常会在窗口的标题中显示一个[*],以提醒用户该文档尚未保存。
- setWindowModified(true):表示窗口的内容已修改。
- setWindowModified(false):表示窗口内容未被修改。
 
- **
作用:
当 QTextDocument 中的内容发生变化时,contentsChanged() 信号会被触发,进而调用 documentWasModified() 函数。这个函数会检查文档是否被修改,并更新窗口的修改状态。
使用场景:
- 保存提示 :在用户修改了文档内容之后,通常需要提醒用户保存更改。在窗口标题中显示 [*]或使用其他视觉提示可以提醒用户文档已被修改。
- 状态管理 :通过 setWindowModified(),可以方便地管理文档的保存状态。如果用户未保存修改,窗口会自动显示为"已修改"状态。
示例场景:
- 假设用户打开一个文档并进行了修改。
- 由于 QTextEdit的contentsChanged()信号会被触发,因此documentWasModified()函数会被调用。
- 在 documentWasModified()中,setWindowModified(true)会被调用,从而将窗口状态设置为"已修改"。
- 如果用户保存了文件,setWindowModified(false)会被调用,表示文档已保存。
MyChild::documentWasModified() 主要是为了实现文档修改时的状态更新,提醒用户文档是否已修改,避免用户丢失未保存的修改。
提取文件名
            
            
              cpp
              
              
            
          
          QString MyChild::strippedName(const QString &fullFileName) 
{
    return QFileInfo(fullFileName).fileName();
}这段代码的功能是从给定的完整文件路径中提取并返回文件名部分,去除掉路径信息。
详细解释:
QFileInfo:
- QFileInfo是 Qt 中的一个类,用于获取与文件或目录相关的详细信息。它可以解析文件的路径、文件名、文件大小、最后修改时间等。
fileName():
- fileName()是- QFileInfo类的一个方法,用来获取文件的基本名称 (即去掉路径信息后,文件的名称部分)。如果提供的路径是一个文件的完整路径,- fileName()仅返回文件名(包含扩展名),而不会返回文件的路径。
代码工作原理:
- QFileInfo(fullFileName):将传入的- fullFileName(一个完整的文件路径)传给- QFileInfo对象。
- .fileName():通过- QFileInfo对象调用- fileName()方法,提取并返回该路径中的文件名部分。
举个例子:
假设 fullFileName 是 "/home/user/Documents/file.txt",那么:
- QFileInfo("/home/user/Documents/file.txt")会解析出文件路径信息。
- fileName()会返回- file.txt,即去掉路径后的文件名。
用户友好型文件
            
            
              cpp
              
              
            
          
          QString MyChild::userFriendlyCurrentFile() 
{
    return strippedName(curFile);
}这段代码的功能是返回当前文件的文件名,而不包括文件的完整路径。它通过调用先前定义的 strippedName() 函数来实现。
userFriendlyCurrentFile() 使得当前文件的名称更易于呈现给用户,它只返回文件的名称,而不包含路径信息。
代码解析:
- 
strippedName(curFile):- 这个函数调用了前面定义的 strippedName()方法,并传入curFile作为参数。
- curFile是当前文件的完整路径(包括路径和文件名),通过- strippedName()函数,返回文件的文件名部分(不包括路径)。
 
- 这个函数调用了前面定义的 
- 
strippedName():- strippedName()函数会从文件的完整路径中提取文件名部分,去除路径。
 
示例:
假设 curFile 是 /home/user/Documents/letter.txt,那么:
- strippedName("/home/user/Documents/letter.txt")会返回- letter.txt。
- userFriendlyCurrentFile()会返回- letter.txt,这是一个对用户友好的文件名。
用途:
- 这个函数主要用于显示或设置当前文件的名称,特别是在应用程序的标题栏或文件保存对话框中,通常用户只关心文件的名称,而不关心它的完整路径。
设置当前文件
            
            
              cpp
              
              
            
          
          void MyChild::setCurrentFile(const QString &fileName)  
    curFile = QFileInfo(fileName).canonicalFilePath();
    isUntitled = false;
    document()->setModified(false);
    setWindowTitle(userFriendlyCurrentFile() + "[*]");
}这段代码 的作用是设置当前打开的文件,并更新相关的属性和界面显示。
代码解析:
- 
curFile = QFileInfo(fileName).canonicalFilePath();:- 这里使用 QFileInfo来获取fileName的文件信息。
- canonicalFilePath()方法返回的是文件的标准路径(去掉符号链接、相对路径等),确保路径格式统一。
- curFile被设置为文件的完整路径,表示当前文件的位置。
 
- 这里使用 
- 
isUntitled = false;:- 这个成员变量 isUntitled用于标识当前文件是否是新创建的文件。如果是新建的文件,isUntitled应为true;如果是加载的已有文件,isUntitled为false。
- 这里将 isUntitled设置为false,说明当前文件不再是新文件。
 
- 这个成员变量 
- 
document()->setModified(false);:- 这里 document()返回的是QTextEdit(或其子类MyChild)的QTextDocument,它是用于管理文档内容的核心类。
- setModified(false)表示当前文档的内容没有被修改。通常在打开文件后,设置文档为未修改状态。
 
- 这里 
- 
setWindowTitle(userFriendlyCurrentFile() + "[*]");:- 这里调用了 userFriendlyCurrentFile()方法,获取当前文件的文件名(不带路径)。
- setWindowTitle()设置窗口标题。标题会显示文件名,并附加- [*],表示文件已经被修改。
- [ * ]通常是用来提示用户文档内容已被更改,但尚未保存。
 
- 这里调用了 
示例:
假设当前打开的文件路径是 /home/user/Documents/letter.txt,如果调用 setCurrentFile("/home/user/Documents/letter.txt"),则:
- curFile会被设置为- /home/user/Documents/letter.txt。
- isUntitled会被设置为- false。
- 文档的修改状态会被重置为未修改。
- 窗口标题会显示 letter.txt[*]。
总结:
setCurrentFile() 方法用于更新当前文件的信息,包括文件路径、修改状态以及窗口标题。它会确保:
- 文件路径被标准化。
- 文档的修改状态被重置。
- 窗口标题显示的是文件的名称,并指示文件是否有修改。
这段代码是用于文件打开、加载、设置当前文件状态等场景的关键部分。
保存文件
            
            
              cpp
              
              
            
          
          bool MyChild::saveFile(QString fileName)  
{
    if (!(fileName.endsWith(".htm", Qt::CaseInsensitive) || fileName.endsWith(".html", Qt::CaseInsensitive))) {
        fileName += ".html";     
    }
    QTextDocumentWriter writer(fileName);
    bool success = writer.write(this->document());
    if (success)
        setCurrentFile(fileName);
    return  success;
}这段代码实现了 MyChild 类中的 saveFile() 函数,用于将当前文档保存为指定的文件格式。该函数接受一个文件路径 fileName,并将文档保存为 HTML 格式。
代码解释:
- 
文件名检查和扩展名处理: - if (!(fileName.endsWith(".htm", Qt::CaseInsensitive) || fileName.endsWith(".html", Qt::CaseInsensitive))):- 这行代码检查传入的文件名是否以 .htm或.html结尾,且不区分大小写。如果文件名没有这两个扩展名,接下来的操作会自动为文件名添加.html扩展名。
- Qt::CaseInsensitive使得文件名后缀检查不区分大小写。
 
- 这行代码检查传入的文件名是否以 
- fileName += ".html";:- 如果文件名没有 .htm或.html后缀,默认将其扩展名更改为.html。
 
- 如果文件名没有 
 
- 
QTextDocumentWriter用于保存文档:- QTextDocumentWriter writer(fileName);:- 这个类用于将 QTextDocument(当前文档)保存为指定的文件格式。在这里,使用fileName来指定保存的文件路径。
 
- 这个类用于将 
- bool success = writer.write(this->document());:- 调用 write()方法将当前文档的内容保存到文件中。this->document()获取当前编辑框(QTextEdit)的QTextDocument。
- 如果保存成功,write()方法返回true,否则返回false。
 
- 调用 
 
- 
更新当前文件: - if (success) setCurrentFile(fileName);:- 如果文档保存成功,更新当前文件路径为 fileName。这意味着文件已经保存并且curFile会被更新为新文件路径。
 
- 如果文档保存成功,更新当前文件路径为 
 
- 
返回保存结果: - 最后,函数返回 success(true或false),表示保存是否成功。
 
- 最后,函数返回 
总结:
- 扩展名处理 :如果用户输入的文件名没有 .html或.htm扩展名,默认将其修改为.html。
- 保存操作 :使用 QTextDocumentWriter来将当前文档保存为 HTML 格式。
- 更新文件路径 :保存成功后,更新 curFile来表示当前文档的路径。
示例:
假设当前文档是一个 HTML 文件,用户选择保存为 myfile.html,如果文件名没有 .html 后缀,saveFile() 会自动添加后缀并保存文件为 myfile.html。保存成功后,curFile 将更新为 myfile.html,表示当前文件路径已更改。
另存为
            
            
              cpp
              
              
            
          
          bool MyChild::saveAs()  
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("另存为"), curFile, tr("Html 文档(*.html);;所有文件(*.*)"));
    if (fileName.isEmpty())
        return false;
    return saveFile(fileName);
}该函数的作用是通过 "另存为" 文件对话框让用户选择一个保存路径和文件名,然后将当前文档保存为新文件。
代码解释:
- 
QFileDialog::getSaveFileName():- 这个函数弹出一个文件保存对话框,用户可以在对话框中选择文件保存的位置和名称。
- this表示当前窗口对象(- MyChild的实例),对话框将在这个窗口的父级下弹出。
- tr("另存为")设置对话框的标题。
- curFile是当前文件的路径,作为默认路径显示在文件对话框中。
- tr("Html 文档(*.html);;所有文件(*.*)")是文件类型过滤器,表示用户只能选择- .html格式文件或所有文件。
 
- 
检查文件名是否为空: - fileName.isEmpty()检查用户是否取消了文件保存操作或没有选择文件。如果文件名为空,则函数返回- false,表示没有进行保存操作。
 
- 
调用 saveFile(fileName):- 如果用户选择了一个有效的文件路径,函数会调用 saveFile(fileName)来保存文件。这里的saveFile()函数可能是一个内部方法,负责将当前文档保存到指定路径。
 
- 如果用户选择了一个有效的文件路径,函数会调用 
saveAs() 的作用:
saveAs() 方法的主要目的是让用户将当前文件保存为一个新文件,并且能够指定新的文件名和文件路径。如果文件成功保存,它会调用 saveFile() 来处理文件保存的细节。
示例:
假设当前文件是一个 HTML 文档,用户选择 "另存为" 并且选择保存为 newfile.html,该函数会将当前文档内容保存到 newfile.html 文件中。
保存
            
            
              cpp
              
              
            
          
          bool MyChild::save() // 保存
{
    if (isUntitled) {
        return saveAs();
    } else {
        return saveFile(curFile);
    }
}MyChild::save 函数用于保存当前文件。如果文件是未命名的(即是"新建"文件),则调用 saveAs 函数弹出另存为对话框,允许用户选择文件名并保存;如果文件已经有名称(即已保存过),则直接保存到当前文件路径。
代码解释:
- 
if (isUntitled):- 这行检查文件是否为未命名状态。如果 isUntitled为true,表示当前文件是一个新建的、没有保存过的文件(例如:未指定文件名的文档)。
 
- 这行检查文件是否为未命名状态。如果 
- 
return saveAs();:- 如果文件是未命名的,调用 saveAs()函数来显示"另存为"对话框,允许用户为文件选择保存路径和文件名。
- saveAs()函数会返回- true或- false,表示保存是否成功。
 
- 如果文件是未命名的,调用 
- 
return saveFile(curFile);:- 如果文件已经有名称(即 isUntitled为false),则直接调用saveFile(curFile),将文件保存到当前文件路径curFile。
- saveFile(QString fileName)函数通过- QTextDocumentWriter将文档内容保存为指定文件(通常为- .html格式)。
 
- 如果文件已经有名称(即 
总结:
- 未命名文件 :调用 saveAs()让用户选择文件路径和名称来保存。
- 已命名文件 :直接保存到已有的路径,调用 saveFile()函数。
这段代码使得用户在保存文件时,有两种行为:
- 对于新建文件,用户需要指定文件路径和名称。
- 对于已保存过的文件,直接保存。
是否可以保存
            
            
              cpp
              
              
            
          
          bool MyChild::maybeSave()  
{
    if (!document()->isModified())
        return true;
    QMessageBox::StandardButton ret;
    ret = QMessageBox::warning(this, tr("Myself Qt Word"),tr("文档'%1'已被修改,保存吗?").arg(userFriendlyCurrentFile()),QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
    if (ret == QMessageBox::Save)
        return save();
    else if (ret == QMessageBox::Cancel)
        return false;
    return true;
}这段代码中的 maybeSave() 函数的作用是:在用户尝试关闭文件或退出程序时,检查文档是否有修改。如果文档被修改过,弹出一个提示框,询问用户是否保存修改。如果用户选择保存,调用 save() 函数保存文件;如果用户选择取消,返回 false,阻止关闭操作;如果选择不保存,直接返回 true,允许关闭操作。
代码分析:
- 
检查文档是否已修改: cppif (!document()->isModified()) return true;- 如果文档没有被修改,直接返回 true,表示文件可以关闭,不需要进行保存。
 
- 如果文档没有被修改,直接返回 
弹出确认保存框:
            
            
              cpp
              
              
            
          
          QMessageBox::StandardButton ret;
ret = QMessageBox::warning(this, tr("Myself Qt Word"),
                           tr("文档'%1'已被修改,保存吗?").arg(userFriendlyCurrentFile()),
                           QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);- 这里通过 QMessageBox::warning弹出一个提示框,询问用户是否保存文件。
- 提示框有三个按钮:Save(保存)、Discard(丢弃)、Cancel(取消)。
- tr()用于支持国际化 ,- userFriendlyCurrentFile()返回用户友好的当前文件名。
处理用户的选择:
            
            
              cpp
              
              
            
          
          if (ret == QMessageBox::Save)
    return save();
else if (ret == QMessageBox::Cancel)
    return false;- 如果用户点击了 保存 ,调用 save()函数保存文件并返回true,表示可以关闭。
- 如果用户点击了 取消 ,返回 false,阻止关闭操作。
默认行为(不保存):
            
            
              cpp
              
              
            
          
          return true;- 如果用户选择了 丢弃 ,直接返回 true,表示可以关闭,不保存文件。
修改总结:
- 用户点击 保存 时,会保存文件并返回 true。
- 用户点击 取消 时,会返回 false,阻止关闭操作。
- 用户点击 丢弃 时,会直接返回 true,表示可以关闭文件,不进行保存。
处理窗体关闭逻辑
            
            
              cpp
              
              
            
          
          void MyChild::closeEvent(QCloseEvent *event)  
{
    if (maybeSave()) {
        event->accept();
    } else {
        event->ignore();
    }
}这段代码用于处理窗口关闭时的逻辑。这个函数可以通过事件参数来控制是否允许关闭窗口。
代码解析:
- 
QCloseEvent *event:- 这是传递给 closeEvent()函数的事件对象。QCloseEvent是一个用于描述关闭窗口事件的类,它包含了关于关闭窗口的相关信息。
- 通过该事件对象,可以控制窗口是否关闭。
 
- 这是传递给 
- 
if (maybeSave()):- maybeSave()是一个自定义函数,它用于检查文件是否有未保存的修改,并询问用户是否保存更改。它返回一个布尔值:- true:表示用户同意保存文件或者文件不需要保存。
- false:表示用户拒绝保存文件或取消了保存操作。
 
- 如果 maybeSave()返回true,表示用户已经同意保存(或者文件本身没有修改),那么程序就可以继续关闭窗口。
 
- 
event->accept();:- accept()方法表示接受关闭事件**,窗口将被关闭**。
- 如果 maybeSave()返回true,那么调用event->accept()来允许关闭窗口。
 
- 
event->ignore();:- ignore()方法表示忽略关闭事件,窗口不会关闭。
- 如果 maybeSave()返回false,表示用户选择了不保存或取消了保存操作,那么调用event->ignore()来阻止窗口关闭。
 
总结:
closeEvent() 方法用于在窗口关闭时执行一些额外的逻辑,例如询问用户是否保存未保存的更改。它通过 maybeSave() 来决定是否允许窗口关闭:
- 如果文件没有修改或用户选择保存,窗口会关闭(event->accept())。
- 如果文件有修改且用户选择不保存,窗口不会关闭(event->ignore())。
这段代码的目的是确保用户在关闭窗口时不会丢失未保存的工作。
格式设置
            
            
              cpp
              
              
            
          
           void MyChild::mergeFormatOnWordOrSelection(const QTextCharFormat &format)    
{
    QTextCursor cursor = this->textCursor();
    if (!cursor.hasSelection())
        cursor.select(QTextCursor::WordUnderCursor);
    cursor.mergeCharFormat(format);
    this->mergeCurrentCharFormat(format);
}MyChild::mergeFormatOnWordOrSelection 是一个格式设置函数,主要用于更改文本编辑器中选定文本或光标所在单词的字体格式。
代码分析:
- 
获取当前光标: cppQTextCursor cursor = this->textCursor();- this->textCursor()获取当前的文本光标(- QTextCursor是- QTextEdit中用于表示文本的光标)。
- 该光标可以用来对文本进行操作,如插入、删除或修改格式。
 
- 
检查是否有选中的文本: cppif (!cursor.hasSelection()) cursor.select(QTextCursor::WordUnderCursor);- cursor.hasSelection()用于检查当前是否有选中的文本。
- 如果没有选中文本(hasSelection()返回false),则选择光标所在的单词(通过cursor.select(QTextCursor::WordUnderCursor))。
- 这意味着如果没有选择任何文本,程序会自动选择光标所在位置的单词来应用格式。
 
- 
合并字符格式: cppcursor.mergeCharFormat(format);- cursor.mergeCharFormat(format)用于将传入的字符格式(- format)合并到选中的文本中。
- QTextCharFormat是一个类,允许你设置文本的不同样式,如字体、大小、颜色、加粗、斜体等。
- 合并格式会应用新的字符格式到文本,但不会完全覆盖原有的格式(例如,原来是斜体的字不会变成正常的)。
 
- 
更新当前字符格式: cppthis->mergeCurrentCharFormat(format);- this->mergeCurrentCharFormat(format)用来将新的字符格式合并到当前光标所在的文本(即光标后续输入的文本)。
- 这行代码确保在更新当前选中区域的字符格式后,光标位置后续的字符输入也会应用相同的格式。
 
总结:
- 功能 :此函数用于在 QTextEdit中修改选中的文本或光标所在单词的字符格式。
- 流程:检查是否有选中文本,如果没有,自动选择光标所在的单词。然后将传入的格式应用于选中文本或单词。最终,当前光标后续的文本输入也会继承这个格式。
使用场景:
- 例如,在一个富文本编辑器中,当用户点击某个按钮(比如加粗、改变字体等)时,调用此函数来修改选中文本或光标所在单词的格式。
段落对齐设置
            
            
              cpp
              
              
            
          
          void MyChild::setAlign(int align)                                           
{
    if(align == 1)
        this->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute);   
    else if (align == 2)
        this->setAlignment(Qt::AlignHCenter);  
    else if (align == 3)
        this->setAlignment(Qt::AlignRight | Qt::AlignAbsolute);  
    else if (align == 4)
        this->setAlignment(Qt::AlignJustify);    
}这段代码用于设置文本在 QTextEdit 中的段落对齐方式。
1. 设置左对齐 (绝对左对齐):
            
            
              cpp
              
              
            
          
          if(align == 1)
    this->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute);- 这段代码设置文本对齐为左对齐,且使用 "绝对" 对齐方式。
- Qt::AlignLeft:表示文本在水平方向上对齐到左边缘。
- Qt::AlignAbsolute:指定绝对位置对齐(即不受任何方向或布局的影响,强制左对齐)。
- this->setAlignment():设置- QTextEdit中的文本对齐方式。
2. 设置居中对齐:
            
            
              cpp
              
              
            
          
          else if (align == 2)
    this->setAlignment(Qt::AlignHCenter);- 这段代码设置文本对齐为水平居中对齐。
- Qt::AlignHCenter:表示文本在水平方向上居中对齐。
- 该对齐方式不会改变文本的垂直方向对齐,默认垂直方向是顶部对齐。
3. 设置右对齐 (绝对右对齐):
            
            
              cpp
              
              
            
          
          else if (align == 3)
    this->setAlignment(Qt::AlignRight | Qt::AlignAbsolute);- 这段代码设置文本对齐为右对齐,且使用 "绝对" 对齐方式。
- Qt::AlignRight:表示文本在水平方向上对齐到右边缘。
- Qt::AlignAbsolute:指定绝对位置对齐,确保文本严格对齐到右边缘。
- 这种对齐方式与左对齐一样,不受布局管理器的影响。
4. 设置两端对齐:
            
            
              cpp
              
              
            
          
          else if (align == 4)
    this->setAlignment(Qt::AlignJustify);- 这段代码设置文本对齐为两端对齐。
- Qt::AlignJustify:表示文本在水平方向上进行两端对齐。文字将自动调整行间距,以确保每一行的文本两端都对齐。
- 这种对齐方式通常用于段落中的正文,避免文本在行尾出现不自然的空白。
总结:
- 该函数根据传入的 align参数值设置不同的段落对齐方式。- align == 1:左对齐(绝对左对齐)。
- align == 2:居中对齐。
- align == 3:右对齐(绝对右对齐)。
- align == 4:两端对齐。
 
段落标号功能
            
            
              cpp
              
              
            
          
          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; // 小写阿拉伯数字,最大4999
                break;
            case 5:
                stylename = QTextListFormat::ListLowerAlpha; // 小写拉丁字符,按字母顺序
                break;
            case 6:
                stylename = QTextListFormat::ListUpperAlpha; // 大写拉丁字符,按字母顺序
                break;
            case 7:
                stylename = QTextListFormat::ListLowerRoman; // 小写罗马数字,最大4999
                break;
            case 8:
                stylename = QTextListFormat::ListUpperRoman; // 大写罗马数字,最大4999
                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);
    }
}这段代码定义了一个 setStyle 函数,用于设置段落的标号或编号样式。具体来说,它根据传入的 style 参数来选择不同的列表样式(例如圆圈、方块、数字等)。
1. 获取文本光标:
            
            
              cpp
              
              
            
          
          QTextCursor cursor = this->textCursor();- QTextCursor用于控制文本光标的位置。通过- this->textCursor()获取当前文本光标。
2. 判断是否设置样式:
            
            
              cpp
              
              
            
          
          if (style != 0) {- 如果 style不为 0,则执行后续的标号/编号样式设置;如果为 0,则取消标号或编号。
3. 设置标号/编号样式:
            
            
              cpp
              
              
            
          
          QTextListFormat::Style stylename = QTextListFormat::ListDisc; // # 样式为圆圈- 默认的样式是圆圈(ListDisc),也就是无序列表中的圆形标号。
- 根据 style值选择不同的列表样式:
            
            
              cpp
              
              
            
          
          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; // 小写阿拉伯数字,最大4999
        break;
    case 5:
        stylename = QTextListFormat::ListLowerAlpha; // 小写拉丁字符,按字母顺序
        break;
    case 6:
        stylename = QTextListFormat::ListUpperAlpha; // 大写拉丁字符,按字母顺序
        break;
    case 7:
        stylename = QTextListFormat::ListLowerRoman; // 小写罗马数字,最大4999
        break;
    case 8:
        stylename = QTextListFormat::ListUpperRoman; // 大写罗马数字,最大4999
        break;
}- QTextListFormat::Style是列出不同列表样式的枚举类型。根据传入的- style值,选择不同的列表样式:- ListDisc:圆圈(默认样式)。
- ListCircle:空心圆圈。
- ListSquare:方块。
- ListDecimal:小写阿拉伯数字(最大4999)。
- ListLowerAlpha:小写字母(按字母顺序)。
- ListUpperAlpha:大写字母(按字母顺序)。
- ListLowerRoman:小写罗马数字(最大4999)。
- ListUpperRoman:大写罗马数字(最大4999)。
 
5. 编辑文本块(段落):
            
            
              cpp
              
              
            
          
          cursor.beginEditBlock();- beginEditBlock():开始一个文本块编辑,通常用于一系列修改,以便之后调用- endEditBlock()进行批量提交。
6. 设置当前段落的格式:
            
            
              cpp
              
              
            
          
          QTextBlockFormat blockFmt = cursor.blockFormat();- 获取当前文本光标所在位置的文本块格式(例如:缩进、对齐方式等)。
7. 获取或创建列表格式:
            
            
              cpp
              
              
            
          
          QTextListFormat listFmt;
if (cursor.currentList()) {
    listFmt = cursor.currentList()->format();
} else {
    listFmt.setIndent(blockFmt.indent() + 1);
    blockFmt.setIndent(0);
    cursor.setBlockFormat(blockFmt);
}- cursor.currentList():检查当前光标是否在一个已有的列表中。如果是,获取该列表的格式(- cursor.currentList()->format())。
- 如果光标不在列表中,则创建一个新的列表,并设置缩进。
- setIndent(blockFmt.indent() + 1):增加列表的缩进。
- blockFmt.setIndent(0):取消当前段落的缩进。
 
8. 设置列表样式并创建列表:
            
            
              cpp
              
              
            
          
          listFmt.setStyle(stylename);
cursor.createList(listFmt);- 设置列表的样式(如圆圈、方块等)。
- 使用 cursor.createList(listFmt)来应用这个列表样式,创建一个新的列表或修改当前列表的样式。
9. 结束编辑块:
            
            
              cpp
              
              
            
          
          cursor.endEditBlock();- endEditBlock():提交所有对文本块的修改,并结束批量编辑。
10. 取消标号/编号样式:
            
            
              cpp
              
              
            
          
          } else {
    QTextBlockFormat bfmt;
    bfmt.setObjectIndex(-1);
    cursor.mergeBlockFormat(bfmt);
}- 如果 style为 0,则表示取消列表标号或编号。通过mergeBlockFormat(bfmt)来清除列表样式,恢复正常的段落格式。
总结:
- setStyle(int style)函数根据传入的- style值设置段落的标号或编号样式。它使用- QTextCursor和- QTextListFormat类来操作文本格式,并允许通过不同的样式(如圆圈、方块、数字等)创建或修改列表。如果- style为 0,则清除列表样式。
mychild.cpp完整代码:
            
            
              cpp
              
              
            
          
          #include "mychild.h"
#include <QtWidgets>
MyChild::MyChild()
{
    setAttribute(Qt::WA_DeleteOnClose);
    isUntitled = true;
}
void MyChild::newFile() 
{
    static int sequenceNumber = 1;
    isUntitled = true;
    curFile = tr("编辑文档%1").arg(sequenceNumber ++);
    setWindowTitle(curFile);
}
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 data = file.readAll();
        QTextCodec *codec = Qt::codecForHtml(data);
        QString str = codec->toUnicode(data);
        if (Qt::mightBeRichText(str)) { 
            this->setHtml(str);
        } else {    
            str = QString::fromLocal8Bit(data);
            this->setPlainText(str);
        }
        setCurrentFile(fileName);
        connect(document(), SIGNAL(contensChanged()), this, SLOT(documentWasModified()));
        return true;
    }
}
void MyChild::documentWasModified()  
{
    setWindowModified(document()->isModified());
}
QString MyChild::strippedName(const QString &fullFileName)  
{
    return QFileInfo(fullFileName).fileName();
}
QString MyChild::userFriendlyCurrentFile() 
{
    return strippedName(curFile);
}
void MyChild::setCurrentFile(const QString &fileName)  
{
    curFile = QFileInfo(fileName).canonicalFilePath();
    isUntitled = false;
    document()->setModified(false);
    setWindowTitle(userFriendlyCurrentFile() + "[*]");
}
void MyChild::closeEvent(QCloseEvent *event) 
{
    if (maybeSave()) {
        event->accept();
    } else {
        event->ignore();
    }
}
bool MyChild::maybeSave()  
{
    if (!document()->isModified())
        return true;
    QMessageBox::StandardButton ret;
    ret = QMessageBox::warning(this, tr("Myself Qt Word"),tr("文档'%1'已被修改,保存吗?").arg(userFriendlyCurrentFile()),QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
    if (ret == QMessageBox::Save)
        return save();
    else if (ret == QMessageBox::Cancel)
        return false;
    return true;
}
bool MyChild::save()  
{
    if (isUntitled) {
        return saveAs();
    } else {
        return saveFile(curFile);
    }
}
bool MyChild::saveAs()  
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("另存为"), curFile, tr("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))) {
        fileName += ".html";    
    }
    QTextDocumentWriter writer(fileName);
    bool success = writer.write(this->document());
    if (success)
        setCurrentFile(fileName);
    return  success;
}
void MyChild::mergeFormatOnWordOrSelection(const QTextCharFormat &format)    
{
    QTextCursor cursor = this->textCursor();
    if (!cursor.hasSelection())
        cursor.select(QTextCursor::WordUnderCursor);
    cursor.mergeCharFormat(format);
    this->mergeCurrentCharFormat(format);
}
void MyChild::setAlign(int align)                                          
{
    if(align == 1)
        this->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute);   
    else if (align == 2)
        this->setAlignment(Qt::AlignHCenter);    
    else if (align == 3)
        this->setAlignment(Qt::AlignRight | Qt::AlignAbsolute);  
    else if (align == 4)
        this->setAlignment(Qt::AlignJustify);   
}
//段落标号、编号
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; // 小写阿拉伯数字,最大4999
                break;
            case 5:
                stylename = QTextListFormat::ListLowerAlpha; // 小写拉丁字符,按字母顺序
                break;
            case 6:
                stylename = QTextListFormat::ListUpperAlpha; // 大写拉丁字符,按字母顺序
                break;
            case 7:
                stylename = QTextListFormat::ListLowerRoman; // 小写罗马数字,最大4999
                break;
            case 8:
                stylename = QTextListFormat::ListUpperRoman; // 大写罗马数字,最大4999
                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);
    }
}