基于Qt的文字处理软件(一)

目录

mychild类的设计

mychild.h文件

主要功能概述

[1. 文件操作功能](#1. 文件操作功能)

[2. 文件管理功能](#2. 文件管理功能)

[3. 文本编辑功能](#3. 文本编辑功能)

[4. 窗口事件管理](#4. 窗口事件管理)

[5. 状态变量](#5. 状态变量)

[6. 保存确认](#6. 保存确认)

用途

mychild.cpp文件

析构函数

[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.cpp完整代码:


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. 设置 isUntitledtrue
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;
    }
}
功能分析:
  1. 文件存在性检查 :如果 fileName 为空或文件不存在,返回 false 表示加载失败。

  2. 打开文件 :尝试以只读模式 (QFile::ReadOnly) 打开文件。如果打开失败,则返回 false

  3. 读取文件内容 :通过 file.readAll() 读取文件的所有内容,并将其存储在 QByteArray 中。

  4. 处理编码 :使用 QTextCodec 类判断文件的编码,并将字节数据转换为 QString 字符串。Qt::codecForHtml(data) 会根据文件的内容猜测合适的编码类型。

  5. 处理富文本与普通文本

    • 如果文件是富文本(如 HTML),使用 setHtml() 方法将其内容设置到 QTextEdit 中。
    • 如果文件是纯文本,使用 setPlainText() 方法将其内容设置到 QTextEdit 中。
  6. 更新当前文件信息 :调用 setCurrentFile(fileName) 方法,更新当前文件的路径信息。

  7. 文件修改监听 :连接 document()contentsChanged 信号与当前对象的 documentWasModified 槽函数,监听文件内容的变化。当文档内容改变的时候,文件名称旁边会出现一个"*";

  8. 返回成功 :如果所有操作都成功完成,返回 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(),可以方便地管理文档的保存状态。如果用户未保存修改,窗口会自动显示为"已修改"状态。
示例场景:
  1. 假设用户打开一个文档并进行了修改。
  2. 由于 QTextEditcontentsChanged() 信号会被触发,因此 documentWasModified() 函数会被调用。
  3. documentWasModified() 中,setWindowModified(true) 会被调用,从而将窗口状态设置为"已修改"。
  4. 如果用户保存了文件,setWindowModified(false) 会被调用,表示文档已保存。

MyChild::documentWasModified() 主要是为了实现文档修改时的状态更新,提醒用户文档是否已修改,避免用户丢失未保存的修改。

提取文件名

cpp 复制代码
QString MyChild::strippedName(const QString &fullFileName) 
{
    return QFileInfo(fullFileName).fileName();
}

这段代码的功能是从给定的完整文件路径中提取并返回文件名部分,去除掉路径信息。

详细解释:

QFileInfo

  • QFileInfo 是 Qt 中的一个类,用于获取与文件或目录相关的详细信息。它可以解析文件的路径、文件名、文件大小、最后修改时间等。

fileName()

  • fileName()QFileInfo 类的一个方法,用来获取文件的基本名称 (即去掉路径信息后,文件的名称部分)。如果提供的路径是一个文件的完整路径,fileName() 仅返回文件名(包含扩展名),而不会返回文件的路径。
代码工作原理:
  1. QFileInfo(fullFileName):将传入的 fullFileName(一个完整的文件路径)传给 QFileInfo 对象。
  2. .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() 使得当前文件的名称更易于呈现给用户,它只返回文件的名称,而不包含路径信息。

代码解析:
  1. strippedName(curFile)

    • 这个函数调用了前面定义的 strippedName() 方法,并传入 curFile 作为参数。
    • curFile 是当前文件的完整路径(包括路径和文件名),通过 strippedName() 函数,返回文件的文件名部分(不包括路径)。
  2. 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() + "[*]");
}

这段代码 的作用是设置当前打开的文件,并更新相关的属性和界面显示。

代码解析:
  1. curFile = QFileInfo(fileName).canonicalFilePath();:

    • 这里使用 QFileInfo 来获取 fileName 的文件信息。
    • canonicalFilePath() 方法返回的是文件的标准路径(去掉符号链接、相对路径等),确保路径格式统一。
    • curFile 被设置为文件的完整路径,表示当前文件的位置。
  2. isUntitled = false;:

    • 这个成员变量 isUntitled 用于标识当前文件是否是新创建的文件。如果是新建的文件,isUntitled 应为 true;如果是加载的已有文件,isUntitledfalse
    • 这里将 isUntitled 设置为 false说明当前文件不再是新文件。
  3. document()->setModified(false);:

    • 这里 document() 返回的是 QTextEdit(或其子类 MyChild)的 QTextDocument,它是用于管理文档内容的核心类。
    • setModified(false) 表示当前文档的内容没有被修改。通常在打开文件后,设置文档为未修改状态。
  4. 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 格式。

代码解释:
  1. 文件名检查和扩展名处理

    • if (!(fileName.endsWith(".htm", Qt::CaseInsensitive) || fileName.endsWith(".html", Qt::CaseInsensitive))):
      • 这行代码检查传入的文件名是否以 .htm.html 结尾,且不区分大小写。如果文件名没有这两个扩展名,接下来的操作会自动为文件名添加 .html 扩展名
      • Qt::CaseInsensitive 使得文件名后缀检查不区分大小写
    • fileName += ".html";
      • 如果文件名没有 .htm.html 后缀,默认将其扩展名更改为 .html
  2. QTextDocumentWriter 用于保存文档

    • QTextDocumentWriter writer(fileName);
      • 这个类用于将 QTextDocument(当前文档)保存为指定的文件格式。在这里,使用 fileName 来指定保存的文件路径。
    • bool success = writer.write(this->document());
      • 调用 write() 方法将当前文档的内容保存到文件中。this->document() 获取当前编辑框(QTextEdit)的 QTextDocument
      • 如果保存成功,write() 方法返回 true,否则返回 false
  3. 更新当前文件

    • if (success) setCurrentFile(fileName);
      • 如果文档保存成功,更新当前文件路径为 fileName。这意味着文件已经保存并且 curFile 会被更新为新文件路径。
  4. 返回保存结果

    • 最后,函数返回 successtruefalse),表示保存是否成功。
总结:
  • 扩展名处理 :如果用户输入的文件名没有 .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);
}

该函数的作用是通过 "另存为" 文件对话框让用户选择一个保存路径和文件名,然后将当前文档保存为新文件。

代码解释:
  1. QFileDialog::getSaveFileName():

    • 这个函数弹出一个文件保存对话框,用户可以在对话框中选择文件保存的位置和名称。
    • this 表示当前窗口对象(MyChild 的实例),对话框将在这个窗口的父级下弹出。
    • tr("另存为") 设置对话框的标题。
    • curFile 是当前文件的路径,作为默认路径显示在文件对话框中。
    • tr("Html 文档(*.html);;所有文件(*.*)") 是文件类型过滤器,表示用户只能选择 .html 格式文件或所有文件。
  2. 检查文件名是否为空:

    • fileName.isEmpty() 检查用户是否取消了文件保存操作或没有选择文件。如果文件名为空,则函数返回 false,表示没有进行保存操作。
  3. 调用 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 函数弹出另存为对话框,允许用户选择文件名并保存;如果文件已经有名称(即已保存过),则直接保存到当前文件路径。

代码解释:
  1. if (isUntitled)

    • 这行检查文件是否为未命名状态。如果 isUntitledtrue,表示当前文件是一个新建的、没有保存过的文件(例如:未指定文件名的文档)。
  2. return saveAs();

    • 如果文件是未命名的,调用 saveAs() 函数来显示"另存为"对话框,允许用户为文件选择保存路径和文件名。
    • saveAs() 函数会返回 truefalse,表示保存是否成功。
  3. return saveFile(curFile);

    • 如果文件已经有名称(即 isUntitledfalse),则直接调用 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,允许关闭操作。

代码分析:
  1. 检查文档是否已修改:

    cpp 复制代码
    if (!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();
    }
}

这段代码用于处理窗口关闭时的逻辑。这个函数可以通过事件参数来控制是否允许关闭窗口。

代码解析:
  1. QCloseEvent *event

    • 这是传递给 closeEvent() 函数的事件对象。QCloseEvent 是一个用于描述关闭窗口事件的类,它包含了关于关闭窗口的相关信息。
    • 通过该事件对象,可以控制窗口是否关闭。
  2. if (maybeSave())

    • maybeSave() 是一个自定义函数,它用于检查文件是否有未保存的修改,并询问用户是否保存更改。它返回一个布尔值:
      • true:表示用户同意保存文件或者文件不需要保存。
      • false:表示用户拒绝保存文件或取消了保存操作。
    • 如果 maybeSave() 返回 true,表示用户已经同意保存(或者文件本身没有修改),那么程序就可以继续关闭窗口。
  3. event->accept();

    • accept() 方法表示接受关闭事件**,窗口将被关闭**。
    • 如果 maybeSave() 返回 true,那么调用 event->accept() 来允许关闭窗口
  4. 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 是一个格式设置函数,主要用于更改文本编辑器中选定文本或光标所在单词的字体格式。

代码分析:
  1. 获取当前光标

    cpp 复制代码
    QTextCursor cursor = this->textCursor();
    • this->textCursor() 获取当前的文本光标(QTextCursorQTextEdit 中用于表示文本的光标)。
    • 该光标可以用来对文本进行操作,如插入、删除或修改格式。
  2. 检查是否有选中的文本

    cpp 复制代码
    if (!cursor.hasSelection())
        cursor.select(QTextCursor::WordUnderCursor);
    • cursor.hasSelection() 用于检查当前是否有选中的文本。
    • 如果没有选中文本(hasSelection() 返回 false),则选择光标所在的单词(通过 cursor.select(QTextCursor::WordUnderCursor))。
    • 这意味着如果没有选择任何文本,程序会自动选择光标所在位置的单词来应用格式。
  3. 合并字符格式

    cpp 复制代码
    cursor.mergeCharFormat(format);
    • cursor.mergeCharFormat(format) 用于将传入的字符格式(format)合并到选中的文本中。
    • QTextCharFormat 是一个类,允许你设置文本的不同样式,如字体、大小、颜色、加粗、斜体等。
    • 合并格式会应用新的字符格式到文本,但不会完全覆盖原有的格式(例如,原来是斜体的字不会变成正常的)。
  4. 更新当前字符格式

    cpp 复制代码
    this->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),也就是无序列表中的圆形标号。
  1. 根据 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 值设置段落的标号或编号样式。它使用 QTextCursorQTextListFormat 类来操作文本格式,并允许通过不同的样式(如圆圈、方块、数字等)创建或修改列表。如果 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);
    }
}
相关推荐
wenxin-1 分钟前
NS3网络模拟器中如何利用Gnuplot工具像MATLAB一样绘制各类图形?
开发语言·matlab·画图·ns3·lr-wpan
数据小爬虫@2 小时前
深入解析:使用 Python 爬虫获取苏宁商品详情
开发语言·爬虫·python
健胃消食片片片片2 小时前
Python爬虫技术:高效数据收集与深度挖掘
开发语言·爬虫·python
王老师青少年编程3 小时前
gesp(C++五级)(14)洛谷:B4071:[GESP202412 五级] 武器强化
开发语言·c++·算法·gesp·csp·信奥赛
DogDaoDao3 小时前
leetcode 面试经典 150 题:有效的括号
c++·算法·leetcode·面试··stack·有效的括号
一只小bit4 小时前
C++之初识模版
开发语言·c++
王磊鑫5 小时前
C语言小项目——通讯录
c语言·开发语言
钢铁男儿5 小时前
C# 委托和事件(事件)
开发语言·c#
Ai 编码助手5 小时前
在 Go 语言中如何高效地处理集合
开发语言·后端·golang
喜-喜5 小时前
C# HTTP/HTTPS 请求测试小工具
开发语言·http·c#