目录
[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);
}
}