文章目录
- 引言
- 项目功能介绍
-
-
- [1. **文件操作**](#1. 文件操作)
- [2. **文本编辑功能**](#2. 文本编辑功能)
- [3. **撤销与重做**](#3. 撤销与重做)
- [4. **剪切、复制与粘贴**](#4. 剪切、复制与粘贴)
- [5. **文本查找与替换**](#5. 文本查找与替换)
- [6. **打印功能**](#6. 打印功能)
- [7. **打印预览**](#7. 打印预览)
- [8. **设置字体颜色**](#8. 设置字体颜色)
- [9. **设置字号**](#9. 设置字号)
- [10. **设置字体**](#10. 设置字体)
- [11. **左对齐**](#11. 左对齐)
- [12. **右对齐**](#12. 右对齐)
- [13. **居中对齐**](#13. 居中对齐)
- [14. **两侧对齐**](#14. 两侧对齐)
- [15. **加粗**](#15. 加粗)
- [16. **斜体**](#16. 斜体)
- [17. **下划线**](#17. 下划线)
- [18. **设置项目符号**](#18. 设置项目符号)
-
- 代码解析(部分)
-
-
- [1. **剪切(`cut`)**](#1. 剪切(
cut
)) - [2. **复制(`copy`)**](#2. 复制(
copy
)) - [3. **粘贴(`paste`)**](#3. 粘贴(
paste
)) - [4. **撤销(`undo`)**](#4. 撤销(
undo
)) - [5. **重做(`redo`)**](#5. 重做(
redo
)) - [6. **加粗(`setBold`)**](#6. 加粗(
setBold
)) - [7. **斜体(`setItalic`)**](#7. 斜体(
setItalic
)) - [8. **下划线(`setUnderline`)**](#8. 下划线(
setUnderline
)) - [9. **设置字体(`setFont`)**](#9. 设置字体(
setFont
)) - [10. **设置字号(`setSize`)**](#10. 设置字号(
setSize
)) - [11. **设置文本颜色(`setTextColor`)**](#11. 设置文本颜色(
setTextColor
)) - [12. **设置背景颜色(`setBackgroundColor`)**](#12. 设置背景颜色(
setBackgroundColor
)) - [13. **设置左对齐(`setLeftAlignment`)**](#13. 设置左对齐(
setLeftAlignment
)) - [14. **设置居中对齐(`setCenterAlignment`)**](#14. 设置居中对齐(
setCenterAlignment
)) - [15. **设置右对齐(`setRightAlignment`)**](#15. 设置右对齐(
setRightAlignment
))
- [1. **剪切(`cut`)**](#1. 剪切(
-
- 源代码
- 总结
-
-
- [1. **初始化与UI设置**](#1. 初始化与UI设置)
- [2. **文本操作功能**](#2. 文本操作功能)
- [3. **文本格式化功能**](#3. 文本格式化功能)
- [4. **文本对齐功能**](#4. 文本对齐功能)
- [5. **功能实现的信号与槽机制**](#5. 功能实现的信号与槽机制)
-
引言
文本编辑器是每个程序员日常工作中不可或缺的工具,而开发一个属于自己的文本编辑器则是许多开发者的梦想。作为一名开发者,我也一直渴望能够打造一个既简单高效,又能满足个性化需求的文本编辑器。经过一段时间的探索与实践,我终于完成了这个项目------WPS,一个基于Qt框架开发的文本编辑器。
在这篇博客中,我将详细介绍如何利用Qt开发一个具备文件操作、文本编辑、撤销重做等常用功能的文本编辑器。我会从项目的设计思路、功能实现、代码分析等方面进行详细讲解,并分享开发过程中遇到的挑战及解决方案。无论你是刚接触Qt的初学者,还是想要深入理解Qt应用开发的开发者,都可以从中获得一些启发与技巧。
项目功能介绍
在这个项目中,WPS文本编辑器 实现了一些常见且实用的文本编辑功能,旨在提供一个简单高效的写作和编辑体验。以下是本项目所具备的主要功能:
1. 文件操作
- 新建文件:用户可以创建一个新的空白文本文件,开始书写或编辑。
- 打开文件 :用户可以从本地磁盘中选择一个现有文件并加载到编辑器中,支持标准的文本文件格式(如
.txt
)。 - 保存文件:支持将编辑器中的内容保存到指定的文件路径中。用户也可以选择"另存为"来保存为不同的文件名或路径。
- 文件另存为:提供了将当前编辑的文件另存为不同文件名或路径的功能,便于文件版本管理。
2. 文本编辑功能
- 文本输入与编辑:用户可以在编辑器中自由输入文本,支持文本的插入与删除。
- 文本格式化:提供基本的文本格式化功能,如字体、字号、颜色等调整,满足用户对文本样式的基本需求。
- 文本对齐与换行:支持文本的左对齐、右对齐、居中对齐等基本对齐操作,以及自动换行功能,保证文本编辑的灵活性与美观性。
3. 撤销与重做
- 撤销:允许用户在误操作或修改后,快速撤回到之前的状态,使得编辑过程更加安全和无忧。
- 重做:支持撤销操作的反向恢复,方便用户重新进行某些操作,提升编辑的灵活性。
4. 剪切、复制与粘贴
- 剪切:用户可以将选中的文本剪切,并保存在剪贴板中,方便移动文本内容。
- 复制:复制选中的文本,保留原内容不变,用户可以在其他地方粘贴。
- 粘贴:将剪贴板中的内容粘贴到当前文本光标位置。
5. 文本查找与替换
- 查找功能:允许用户在文本中进行关键字查找,定位到相应内容。
- 替换功能:提供查找到指定文本后,将其替换为新的文本内容,提升编辑效率。
6. 打印功能
打印功能允许用户将文本内容直接打印到纸张上。用户可以通过点击"打印"按钮来调用系统默认的打印对话框,选择打印机及其他打印设置,并将当前文档打印出来。
7. 打印预览
打印预览功能提供用户在实际打印前预览文档的打印效果。这可以帮助用户查看文本是否排版正确,是否需要调整格式,确保打印效果符合预期。用户通过点击"打印预览"按钮来进入此模式。
8. 设置字体颜色
用户可以通过此功能设置文本的字体颜色。点击"设置字体颜色"按钮后,会弹出颜色选择框,用户可以选择不同的颜色以改变文本的显示效果。这个功能帮助用户根据需要突出显示或装饰文本内容。
9. 设置字号
通过设置字号,用户可以调整文本的大小。点击"设置字号"按钮后,用户可以从下拉菜单中选择一个预设的字号,或者输入自定义的数字值,以改变文档中所有或选定部分文本的显示大小。
10. 设置字体
该功能允许用户选择文本的字体。通过点击"设置字体"按钮,用户可以从字体选择框中选择不同的字体样式,如"宋体","Arial",或"Times New Roman"等,从而调整文档的整体风格。
11. 左对齐
左对齐功能将文本与页面的左边缘对齐。用户可以点击"左对齐"按钮,确保段落中的文本从左边缘开始显示,常用于大多数书面内容。
12. 右对齐
右对齐功能将文本与页面的右边缘对齐。点击"右对齐"按钮后,用户可以将段落中的文本从右边缘开始显示,这对于一些语言(如阿拉伯语)或特殊排版是非常重要的。
13. 居中对齐
居中对齐功能将文本居中显示在页面上。点击"居中对齐"按钮后,文中的所有文本会被自动居中,确保文本在页面上的位置对称且整齐。
14. 两侧对齐
两侧对齐(即"对齐两端")功能使文本在左右两端同时对齐。这会通过调整单词之间的间距来实现文本两端对齐,通常用于报纸、杂志等印刷物的排版。
15. 加粗
加粗功能允许用户将文本设置为加粗格式。点击"加粗"按钮后,选中的文本将显示为加粗样式,常用于突出显示重要的内容或标题。
16. 斜体
斜体功能允许用户将文本设置为斜体样式。点击"斜体"按钮后,选中的文本将倾斜,通常用于强调、书名、外语词汇或引用内容。
17. 下划线
下划线功能为选定的文本添加下划线效果。点击"下划线"按钮后,文本下方会出现一条线,通常用于强调或标注链接文本。
18. 设置项目符号
设置项目符号功能允许用户在文本中插入项目符号(例如:圆点、方块等)并创建无序列表。用户可以通过点击"项目符号"按钮,快速将选中的段落转化为项目符号列表,帮助整理和清晰地展示信息。此功能适用于创建清单、步骤说明等。
代码解析(部分)
1. 剪切(cut
)
cpp
void TextEditor::cut()
{
QTextCursor cursor = textEdit->textCursor(); // 获取当前光标位置
if (cursor.hasSelection()) {
textEdit->cut(); // 如果有选中的文本,执行剪切操作
}
}
textEdit->textCursor()
获取当前QTextEdit
控件的文本光标 (QTextCursor
)。cursor.hasSelection()
检查当前光标位置是否有选中的文本。- 如果有选中的文本,调用
textEdit->cut()
执行剪切操作,将选中的文本从编辑器中删除,并复制到剪贴板。
2. 复制(copy
)
cpp
void TextEditor::copy()
{
QTextCursor cursor = textEdit->textCursor(); // 获取当前光标位置
if (cursor.hasSelection()) {
textEdit->copy(); // 如果有选中的文本,执行复制操作
}
}
textEdit->textCursor()
获取当前的文本光标。cursor.hasSelection()
检查是否选中了文本。- 如果选中了文本,调用
textEdit->copy()
将选中的文本复制到剪贴板。
3. 粘贴(paste
)
cpp
void TextEditor::paste()
{
textEdit->paste(); // 将剪贴板的内容粘贴到光标当前位置
}
textEdit->paste()
会将剪贴板中的内容粘贴到当前光标的位置。如果剪贴板没有内容,则不会有任何变化。
4. 撤销(undo
)
cpp
void TextEditor::undo()
{
textEdit->undo(); // 撤销上一步操作
}
textEdit->undo()
执行撤销操作,即撤回上一步在文本编辑器中做的更改。例如,撤回输入的文字或删除的内容。
5. 重做(redo
)
cpp
void TextEditor::redo()
{
textEdit->redo(); // 重做上一步撤销的操作
}
textEdit->redo()
执行重做操作,即将撤销的操作恢复。如果没有撤销操作,则此操作无效。
6. 加粗(setBold
)
cpp
void TextEditor::setBold()
{
QTextCharFormat format;
format.setFontWeight(QFont::Bold); // 设置字体为加粗
textEdit->mergeCurrentCharFormat(format); // 将格式应用到当前选中的文本
}
QTextCharFormat format
创建一个QTextCharFormat
对象,用来设置文本格式。format.setFontWeight(QFont::Bold)
设置字体加粗。textEdit->mergeCurrentCharFormat(format)
将新的字体格式应用到当前选中的文本。
7. 斜体(setItalic
)
cpp
void TextEditor::setItalic()
{
QTextCharFormat format;
format.setFontItalic(true); // 设置字体为斜体
textEdit->mergeCurrentCharFormat(format); // 将格式应用到当前选中的文本
}
format.setFontItalic(true)
设置字体为斜体。textEdit->mergeCurrentCharFormat(format)
将斜体格式应用到当前选中的文本。
8. 下划线(setUnderline
)
cpp
void TextEditor::setUnderline()
{
QTextCharFormat format;
format.setFontUnderline(true); // 设置字体为下划线
textEdit->mergeCurrentCharFormat(format); // 将格式应用到当前选中的文本
}
format.setFontUnderline(true)
设置字体加下划线。textEdit->mergeCurrentCharFormat(format)
将下划线格式应用到当前选中的文本。
9. 设置字体(setFont
)
cpp
void TextEditor::setFont()
{
bool ok;
QFont font = QFontDialog::getFont(&ok, textEdit->currentFont(), this); // 弹出字体选择对话框
if (ok) {
textEdit->setCurrentFont(font); // 设置当前文本编辑框的字体
}
}
QFontDialog::getFont(&ok, textEdit->currentFont(), this)
弹出一个字体选择对话框,允许用户选择字体。如果用户点击确认,返回选择的字体。textEdit->setCurrentFont(font)
将用户选择的字体设置到文本编辑器中。
10. 设置字号(setSize
)
cpp
void TextEditor::setSize()
{
bool ok;
int size = QInputDialog::getInt(this, tr("选择字号"), tr("字号:"), textEdit->currentFont().pointSize(), 1, 72, 1, &ok); // 弹出字号输入框
if (ok) {
QFont font = textEdit->currentFont();
font.setPointSize(size); // 设置字体大小
textEdit->setCurrentFont(font); // 更新文本编辑器字体
}
}
QInputDialog::getInt
弹出一个输入框,允许用户选择字号。font.setPointSize(size)
设置新的字体大小。textEdit->setCurrentFont(font)
更新文本编辑器中的字体。
11. 设置文本颜色(setTextColor
)
cpp
void TextEditor::setTextColor()
{
QColor color = QColorDialog::getColor(textEdit->textColor(), this); // 弹出颜色选择对话框
if (color.isValid()) {
QTextCharFormat format;
format.setForeground(color); // 设置前景色(文本颜色)
textEdit->mergeCurrentCharFormat(format); // 应用颜色
}
}
QColorDialog::getColor
弹出颜色选择对话框,允许用户选择文本颜色。format.setForeground(color)
设置选中的文本颜色。textEdit->mergeCurrentCharFormat(format)
将颜色应用到当前选中的文本。
12. 设置背景颜色(setBackgroundColor
)
cpp
void TextEditor::setBackgroundColor()
{
QColor color = QColorDialog::getColor(textEdit->textBackgroundColor(), this); // 弹出颜色选择对话框
if (color.isValid()) {
QTextCharFormat format;
format.setBackground(color); // 设置背景颜色
textEdit->mergeCurrentCharFormat(format); // 应用背景颜色
}
}
QColorDialog::getColor
弹出颜色选择对话框,允许用户选择背景颜色。format.setBackground(color)
设置选中文本的背景颜色。textEdit->mergeCurrentCharFormat(format)
将背景颜色应用到当前选中的文本。
13. 设置左对齐(setLeftAlignment
)
cpp
void TextEditor::setLeftAlignment()
{
QTextBlockFormat format;
format.setAlignment(Qt::AlignLeft); // 设置为左对齐
textEdit->mergeCurrentBlockFormat(format); // 将对齐方式应用到当前选中的文本
}
format.setAlignment(Qt::AlignLeft)
设置对齐方式为左对齐。textEdit->mergeCurrentBlockFormat(format)
将左对齐的格式应用到当前选中的文本块。
14. 设置居中对齐(setCenterAlignment
)
cpp
void TextEditor::setCenterAlignment()
{
QTextBlockFormat format;
format.setAlignment(Qt::AlignCenter); // 设置为居中对齐
textEdit->mergeCurrentBlockFormat(format); // 将对齐方式应用到当前选中的文本
}
format.setAlignment(Qt::AlignCenter)
设置对齐方式为居中对齐。textEdit->mergeCurrentBlockFormat(format)
将居中对齐的格式应用到当前选中的文本块。
15. 设置右对齐(setRightAlignment
)
cpp
void TextEditor::setRightAlignment()
{
QTextBlockFormat format;
format.setAlignment(Qt::AlignRight); // 设置为右对齐
textEdit->mergeCurrentBlockFormat(format); // 将对齐方式应用到当前选中的文本
}
format.setAlignment(Qt::AlignRight)
设置对齐方式为右对齐。textEdit->mergeCurrentBlockFormat(format)
将右对齐的格式应用到当前选中的文本块。
源代码
mainwindow.h
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextEdit>
#include <QMdiSubWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class TextEdit;
class QPrinter;
class QActionGroup;
class QSignalMapper;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void initWindowAction(); //初始化窗体action状态
void initTextAction(bool b); //初始化文本action状态
void on_New_action_triggered();
void on_Open_action_triggered();
void on_Save_action_triggered();
void on_Save_save_action_triggered();
void on_Print_action_triggered();
void on_Print_preview_action_triggered();
void on_Undo_action_triggered();
void on_Redo_action_triggered();
void on_Cut_action_triggered();
void on_Copy_action_triggered();
void on_paste_action_triggered();
void on_Bold_action_triggered();
void on_Italic_action_triggered();
void on_Under_action_triggered();
void on_Fontcombosize_activated(const QString &arg1);
void on_fontComboBox_activated(const QString &arg1);
void on_Left_action_triggered();
void on_Center_action_triggered();
void on_Right_action_triggered();
void on_Justfiy_action_triggered();
void on_Colse_action_triggered();
void on_Colse_all_action_triggered();
void on_Tittle_action_triggered();
void on_Cascade_action_triggered();
void on_Next_action_triggered();
void on_previous_action_triggered();
void on_About_action_triggered();
void on_Color_action_triggered();
void on_standardcomboBox_activated(int index);
void setActiveSubWindow(QWidget* widget);
private:
Ui::MainWindow *ui;
void init(); //初始化
void initMdiArea(); //初始化Mdi控件,初始化进度条
void initFontSize(); //初始化字号
TextEdit* activateWindow(); //获取Mdi中的活动窗体
QMdiSubWindow* findSubWindow(const QString &docname); //判断MDI中是否有相同文件名的文件
void printPreview(QPrinter *printer); //打印预览功能
void Start_Past(); //根据系统粘贴板状态控制past粘贴功能
void Select(); //判断文本格式,是否加粗,是否有下划线等
void closeEvent(QCloseEvent *event); //重写关闭事件
void addSubWindowAction(); //使用信号映射器实现标题栏窗口选择窗口功能
public:
QActionGroup* actionGroup;
QSignalMapper* signalMapper;
};
#endif // MAINWINDOW_H
textedit.h
cpp
#ifndef TEXTEDIT_H
#define TEXTEDIT_H
#include <QTextEdit>
class TextEdit : public QTextEdit
{
Q_OBJECT
public:
TextEdit(QWidget *parent=nullptr);
~TextEdit();
public:
void initNewDoc(); //初始化文档
void initOpenDoc(const QString &docName); //初始化标题
QString getDocFilePath() const; //返回绝对路径和文件名
bool loadDoc(const QString &docName); //读取文本显示出来
bool SaveDoc(); //保存文档
bool Save_save_Doc(); //另存为
bool writeToDoc(const QString &docName); //保存功能
void closeEvent(QCloseEvent *event); //重写关闭事件
bool promptSave();
QString getDocFileName() const; //获取文档名称
private slots:
void setWindowModify(); //设置windowModified属性
private:
static int docNo; //静态数据,文档编号,静态数据成员存储在静态数据区,所有类共享副本
//类存储在堆区,静态数据成员属于类,但是不在一块内存,所以要类内声明,类外定义
QString docWindowTitle; //标题
QString docFilePath_Name; //文档的绝对路径和名称
QString docFileName; //文档的名称
};
#endif // TEXTEDIT_H
mainwindow.cpp
cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "textedit.h"
#include <QFontDatabase>
#include <QFont>
#include <QMdiArea>
#include <QFileDialog>
#include <QPrintDialog>
#include <QPainter>
#include <QPrinter>
#include <QPrintPreviewDialog>
#include <QApplication>
#include <QClipboard>
#include <QtDebug>
#include <QColorDialog>
#include <QCloseEvent>
#include <QMessageBox>
#include <QTextCursor>
#include <QTextList>
#include <QTextBlockFormat>
#include <QSignalMapper>
#include <QActionGroup>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
init(); //初始化
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::init()
{
initMdiArea(); //初始化Mdi
initFontSize(); //初始化字体框
initWindowAction(); //初始化窗体action状态
//initTextAction(false); //初始化文本action状态
//当在mdi中添加活动窗体时会触发subWindowActivated信号,关闭窗口会返回0
connect(ui->mdiArea,&QMdiArea::subWindowActivated,this,&MainWindow::initWindowAction);
//获取系统剪贴板的指针,返回类型为,QClipboard *
QClipboard *clip=QApplication::clipboard();
//每当剪贴板的内容发生变化时都会触发这个信号
connect(clip,&QClipboard::dataChanged,this,&MainWindow::Start_Past);
signalMapper = new QSignalMapper(this);
actionGroup = new QActionGroup(this);
actionGroup->setExclusive(true); //排他性
connect(ui->menu_T,&QMenu::aboutToShow,this,&MainWindow::addSubWindowAction);
connect(signalMapper,SIGNAL(mapped(QWidget*)), this,SLOT(setActiveSubWindow(QWidget*)));
}
void MainWindow::initMdiArea()
{
//设置背景色
ui->mdiArea->setBackground(QBrush(Qt::white));
//设置滚动条
ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
void MainWindow::initFontSize()
{
ui->Fontcombosize->clear();
//使用QFontDatabase数据库设置字号
//standardSizes:QFontDatabase的静态函数,返回系统字号
for(int fontsize : QFontDatabase::standardSizes()){
ui->Fontcombosize->addItem(QString::number(fontsize));
}
QFont font=QApplication::font();
int appfontsize=font.pointSize();
//int sizeindex=ui->Fontcombosize->findText(QString::asprintf("%d",appfontsize));
int sizeindex=ui->Fontcombosize->findText(QString::number(appfontsize));
ui->Fontcombosize->setCurrentIndex(sizeindex);
}
void MainWindow::initWindowAction()
{
bool activate=(activateWindow()!=nullptr);
if(!activate){
initTextAction(false);
}
ui->paste_action->setEnabled(activate);
ui->Save_action->setEnabled(activate);
ui->Save_save_action->setEnabled(activate);
ui->Print_action->setEnabled(activate);
ui->Print_preview_action->setEnabled(activate);
ui->Undo_action->setEnabled(activate);
ui->Redo_action->setEnabled(activate);
ui->Colse_action->setEnabled(activate);
ui->Colse_all_action->setEnabled(activate);
ui->Tittle_action->setEnabled(activate);
ui->Cascade_action->setEnabled(activate);
ui->previous_action->setEnabled(activate);
ui->Next_action->setEnabled(activate);
ui->standardcomboBox->setEnabled(activate);
ui->Fontcombosize->setEnabled(activate);
ui->fontComboBox->setEnabled(activate);
}
TextEdit *MainWindow::activateWindow()
{
//activeSubWindow用于返回当前活动的窗体有窗体返回QMdiSubWindow指针,没有返回0
QMdiSubWindow* window=ui->mdiArea->activeSubWindow();
if(window){
//widget:返回 QMdiSubWindow 中实际显示的主控件
return qobject_cast<TextEdit*>(window->widget());
}
return nullptr;
}
void MainWindow::initTextAction(bool b)
{
ui->Cut_action->setEnabled(b);
ui->Copy_action->setEnabled(b);
ui->Color_action->setEnabled(b);
ui->Bold_action->setEnabled(b);
ui->Italic_action->setEnabled(b);
ui->Under_action->setEnabled(b);
ui->Left_action->setEnabled(b);
ui->Center_action->setEnabled(b);
ui->Right_action->setEnabled(b);
ui->Justfiy_action->setEnabled(b);
ui->Justfiy_action->setEnabled(b);
}
void MainWindow::on_New_action_triggered()
{
TextEdit*Edit=new TextEdit;
ui->mdiArea->addSubWindow(Edit);//会把Edit的父对象自动设置为mdi
//copyAvailable选中文本或者取消选择时会触发信号
connect(Edit,&TextEdit::copyAvailable,this,&MainWindow::initTextAction);
//每次选中文本的时候都会触发
connect(Edit,&TextEdit::copyAvailable,this,&MainWindow::Select);
Edit->initNewDoc();
Edit->show();
statusBar()->showMessage("新建成功",3000);
}
void MainWindow::on_Open_action_triggered()
{
QString FileName=QFileDialog::getOpenFileName(
this,"打开文件","../","所有文件(*.*);;文本文件(*.txt)",nullptr);
if(!FileName.isEmpty()){
QMdiSubWindow* sub=findSubWindow(FileName); //判断文件是否已经存在,如果存在就设置为活动窗口,不存在就新增
if(sub){
ui->mdiArea->setActiveSubWindow(sub);
return;
}
TextEdit* Edit=new TextEdit;
ui->mdiArea->addSubWindow(Edit);
//copyAvailable选中文本或者取消选择时会触发信号
connect(Edit,&TextEdit::copyAvailable,this,&MainWindow::initTextAction);
//每次选中文本的时候都会触发
connect(Edit,&TextEdit::copyAvailable,this,&MainWindow::Select);
if(Edit->loadDoc(FileName)){ //初始化Edit
statusBar()->showMessage("文档已打开",3000); //让状态栏显示三秒钟
Edit->show();
}else{
Edit->close();
}
}
return;
}
QMdiSubWindow *MainWindow::findSubWindow(const QString &docname)
{
QString filepath=QFileInfo(docname).canonicalFilePath(); //返回文件绝对路径
for(QMdiSubWindow* sub:ui->mdiArea->subWindowList()){ //遍历MDI中的活动窗口
TextEdit *edit=qobject_cast<TextEdit*>(sub->widget());//强转为TextEdit类型
if(edit->getDocFilePath()==filepath){ //如果找到了一样的文件(绝对路径)就直接返回真否则返回空
return sub;
}
}
return nullptr;
}
void MainWindow::on_Save_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->SaveDoc();
}
}
void MainWindow::on_Save_save_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->Save_save_Doc();
}
}
void MainWindow::on_Print_action_triggered()
{
QPrinter print;
print.setResolution(QPrinter::HighResolution);
//是 QPrinter 中的一个常量,它表示一个高分辨率的打印设置,通常是 600 DPI 或更高。
//使用这个常量时,打印机将尽可能以更高的质量进行打印,适用于需要高质量输出的情况
QPrintDialog printf_dialog(&print,this);
//在打印对话框中启用了"打印选定内容"选项,允许用户选择打印文档中的一部分内容。
printf_dialog.setOption(QAbstractPrintDialog::PrintSelection, true);
//用户可以选择"打印选定内容"或者"打印整个文档"。
printf_dialog.setOption(QAbstractPrintDialog::PrintSelection, true);
TextEdit *text=activateWindow();
if(printf_dialog.exec()==QDialog::Accepted){
text->print(&print);
}
}
void MainWindow::on_Print_preview_action_triggered()
{
// 获取当前活动的 TextEdit 控件(假设这是你编辑文档的地方)
TextEdit* Edit = activateWindow();
if (!Edit) return; // 如果没有活动的窗口,退出
// 创建一个 QPrinter 对象,它是用来控制打印机的
QPrinter print;
// 创建一个打印预览对话框,传入 QPrinter 对象
QPrintPreviewDialog preview(&print, this);
// 连接打印预览对话框的 paintRequested 信号到 printPreview 槽函数
connect(&preview, &QPrintPreviewDialog::paintRequested, this, &MainWindow::printPreview);
// 弹出打印预览对话框,并且马上回触发信号与槽,打印到屏幕上
// 如果用户点击了"打印"按钮(即接受了打印预览),则执行实际的打印
if (preview.exec() == QDialog::Accepted) { //实际打印到打印机上
// 如果打印预览对话框的返回值是"Accepted",表示用户点击了打印按钮
Edit->print(&print); // 调用 TextEdit 的 print 方法执行打印
}
}
void MainWindow::printPreview(QPrinter *printer) //打印到屏幕上
{
// 获取当前活动的 TextEdit 控件(文档编辑器)
TextEdit* Edit = activateWindow();
// 如果有活动的 TextEdit 控件(即有一个正在编辑的文档)
if (Edit) {
// 使用传入的 QPrinter 对象执行打印
Edit->print(printer);
}
}
void MainWindow::Start_Past()
{
//获取系统剪贴板的指针,返回类为QClipboard *
QClipboard *clip=QApplication::clipboard();
QString pastText=clip->text(); //返回粘贴板中的文本
if(!pastText.isEmpty()){ //判断是否存在文本
ui->paste_action->setEnabled(true);
}else{
ui->paste_action->setEnabled(false);
}
}
void MainWindow::Select()
{
TextEdit* Edit=activateWindow();
if(Edit){
QTextCharFormat format=Edit->currentCharFormat();
//判断字体是否加粗
bool isBold=(format.fontWeight()==QFont::Bold);
if(isBold){
ui->Bold_action->setChecked(true);
}else{
ui->Bold_action->setChecked(false);
}
//判断是否斜体
bool isitalic=format.fontItalic();
if(isitalic){
ui->Italic_action->setChecked(true);
}else{
ui->Italic_action->setChecked(false);
}
//判断是否下划线
bool isUnder=format.fontUnderline();
if(isUnder){
ui->Under_action->setChecked(true);
}else{
ui->Under_action->setChecked(false);
}
}
}
void MainWindow::closeEvent(QCloseEvent *event)
{
ui->mdiArea->closeAllSubWindows();
if(ui->mdiArea->currentSubWindow()){
event->ignore();
}else{
event->accept();
}
}
void MainWindow::addSubWindowAction()
{
QList<QAction*> actionList=actionGroup->actions();
if(!actionList.isEmpty()){
for(QAction* action:actionList){
delete action;
}
}
QList<QMdiSubWindow *> subWindowList=ui->mdiArea->subWindowList();
if(!subWindowList.isEmpty()) ui->menu_T->addSeparator(); //添加分割线
for(int i=0;i<subWindowList.count();i++){
//获取内嵌部件
QMdiSubWindow* subWindow=subWindowList.at(i);
TextEdit* Edit=qobject_cast<TextEdit*>(subWindow->widget());
if(Edit==nullptr){
qDebug()<<"转换失败!";
}
//设置需要添加的Action名称
QString ACtion_Name=QString("%1 %2").arg(i+1).arg(Edit->getDocFileName());
QAction *addAction=ui->menu_T->addAction(ACtion_Name);
actionGroup->addAction(addAction);
addAction->setCheckable(true);
if(Edit==activateWindow()) addAction->setChecked(true);
connect(addAction, SIGNAL(triggered(bool)), signalMapper, SLOT(map()));
connect(addAction, SIGNAL(triggered(bool)), this, SLOT(on_Center_action_triggered()));
signalMapper->setMapping(addAction,subWindow);
}
}
void MainWindow::setActiveSubWindow(QWidget *widget)
{
if(widget)ui->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow*>(widget));
}
void MainWindow::on_Undo_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->undo();
}
}
void MainWindow::on_Redo_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->redo();
}
}
void MainWindow::on_Cut_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->cut();
}
}
void MainWindow::on_Copy_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->copy();
}
}
void MainWindow::on_paste_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->paste();
}
}
void MainWindow::on_Bold_action_triggered()
{
//使用QFont不能局部设置加粗,一加粗所有文本都加粗了,不能根据光标位置或者选中的文本单独进行加粗
// TextEdit* Edit=activateWindow();
// if(Edit){
// QFont font=Edit->font();
// font.setBold(true);
// Edit->setFont(font);
// }
TextEdit* Edit=activateWindow();
if(Edit){
bool isChecked=ui->Bold_action->isChecked();
//设置文本字符格式的类。例如字体、颜色、加粗、斜体、下划线等。
QTextCharFormat format;
format.setFontWeight(isChecked ? QFont::Bold : QFont::Normal);
//使用mergeCurrentCharFormat可以局部进行单独的设置
Edit->mergeCurrentCharFormat(format);
}
}
void MainWindow::on_Italic_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
QTextCharFormat format;
bool isItalic=ui->Italic_action->isChecked();
format.setFontItalic(isItalic);
Edit->mergeCurrentCharFormat(format);
}
}
void MainWindow::on_Under_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
QTextCharFormat format;
bool isUnder=ui->Under_action->isChecked();
format.setFontUnderline(isUnder);
Edit->mergeCurrentCharFormat(format);
}
}
void MainWindow::on_Fontcombosize_activated(const QString &arg1)
{
TextEdit* Edit=activateWindow();
if(Edit){
QTextCharFormat format;
format.setFontPointSize(arg1.toInt());
Edit->mergeCurrentCharFormat(format);
}
}
void MainWindow::on_fontComboBox_activated(const QString &arg1)
{
TextEdit* Edit=activateWindow();
if(Edit){
QTextCharFormat format;
format.setFontFamily(arg1);
format.setFontPointSize(ui->Fontcombosize->currentText().toInt());
Edit->mergeCurrentCharFormat(format);
}
}
void MainWindow::on_Left_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->setAlignment(Qt::AlignLeft);
}
}
void MainWindow::on_Center_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->setAlignment(Qt::AlignCenter);
}
}
void MainWindow::on_Right_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->setAlignment(Qt::AlignRight);
}
}
void MainWindow::on_Justfiy_action_triggered()
{
TextEdit* Edit=activateWindow();
if(Edit){
Edit->setAlignment(Qt::AlignJustify);
}
}
void MainWindow::on_Colse_action_triggered()
{
// TextEdit* Edit=activateWindow();
// if(Edit){
// QMdiSubWindow* window=qobject_cast<QMdiSubWindow*>(Edit->parentWidget());
// if(window){
// window->close();
// }
// }
//Active 活动
ui->mdiArea->closeActiveSubWindow();
}
void MainWindow::on_Colse_all_action_triggered()
{
//All 所有
ui->mdiArea->closeAllSubWindows();
}
void MainWindow::on_Tittle_action_triggered()
{
//tile 平铺
ui->mdiArea->tileSubWindows();
}
void MainWindow::on_Cascade_action_triggered()
{
//cascade 层叠 SubWindows 子窗口
ui->mdiArea->cascadeSubWindows();
}
void MainWindow::on_Next_action_triggered()
{
//activ 活动 activate 激活
ui->mdiArea->activateNextSubWindow();
}
void MainWindow::on_previous_action_triggered()
{
//activ 活动 activate 激活 Previous 上一个
ui->mdiArea->activatePreviousSubWindow();
}
void MainWindow::on_Color_action_triggered()
{
TextEdit *Edit=activateWindow();
if(Edit){
QColor color=QColorDialog::getColor(Qt::black,this,"选中字体颜色");
if(color.isValid()){
QTextCharFormat format;
format.setForeground(color);
Edit->mergeCurrentCharFormat(format);
}
}
}
void MainWindow::on_standardcomboBox_activated(int index)
{
// 获取当前活动窗口,即 TextEdit 控件
TextEdit *Edit = activateWindow();
if(Edit){
// 如果下拉框选项为0(即取消项目符号)
if(index == 0){
QTextCursor cursor = Edit->textCursor(); // 获取当前光标对象
cursor.beginEditBlock(); // 开始一个编辑块,标记当前编辑操作的开始
QTextList* list = cursor.currentList(); // 获取当前光标所在的列表(如果有的话)
if(list){ // 如果当前光标所在的文本是列表的一部分
list->remove(cursor.block()); // 删除当前光标所在的文本块(即当前列表项)
QTextBlockFormat blockFormat = cursor.blockFormat(); // 获取当前块的格式
blockFormat.setIndent(0); // 将当前块的缩进设置为 0
cursor.setBlockFormat(blockFormat); // 应用修改后的格式
}
cursor.endEditBlock(); // 结束编辑块,标记当前编辑操作的结束
return; // 退出函数
}
// 根据下拉框选中的不同索引,设置不同的列表样式
QTextListFormat::Style style;
switch (index)
{
case 1:
style = QTextListFormat::ListDisc; // 圆点列表
break;
case 2:
style = QTextListFormat::ListCircle; // 圆圈列表
break;
case 3:
style = QTextListFormat::ListSquare; // 方形列表
break;
case 4:
style = QTextListFormat::ListDecimal; // 数字列表
break;
case 5:
style = QTextListFormat::ListLowerAlpha; // 小写字母列表
break;
case 6:
style = QTextListFormat::ListUpperAlpha; // 大写字母列表
break;
case 7:
style = QTextListFormat::ListLowerRoman; // 小写罗马数字列表
break;
case 8:
style = QTextListFormat::ListUpperRoman; // 大写罗马数字列表
break;
default:
style = QTextListFormat::ListStyleUndefined; // 未定义的列表样式
break;
}
// 获取当前光标对象
QTextCursor cursor = Edit->textCursor();
cursor.beginEditBlock(); // 开始编辑块
// 获取当前块的格式,并创建一个 QTextListFormat 对象来设置列表格式
QTextBlockFormat blockFormat = cursor.blockFormat();
QTextListFormat listFormat;
// 如果当前光标所在文本属于列表的一部分
QTextList* list = cursor.currentList();
if(list){
listFormat = list->format(); // 获取该列表的格式
list->remove(cursor.block()); // 删除当前光标所在的列表项
blockFormat.setIndent(0); // 设置块缩进为 0
cursor.setBlockFormat(blockFormat); // 应用新的块格式
}
// 设置列表样式为选中的样式
listFormat.setStyle(style);
// 使用新的列表格式创建一个列表
cursor.createList(listFormat);
cursor.endEditBlock(); // 结束编辑块
}
}
void MainWindow::on_About_action_triggered()
{
QMessageBox::about(this,tr("关于应用"),tr("这是一个WPS项目。\n版本1.0\n制作者:xiaohu"));
}
textedit.cpp
cpp
#include "textedit.h"
#include <QFileInfo>
#include <QFileDialog>
#include <QTextDocumentWriter>
#include <QCloseEvent>
#include <QMessageBox>
#include <QDebug>
int TextEdit::docNo=1; //类外初始化静态数据成员
TextEdit::TextEdit(QWidget *parent):QTextEdit(parent)
{
setAttribute(Qt::WA_DeleteOnClose); //关闭窗体时释放资源
//initNewDoc();
}
TextEdit::~TextEdit()
{
}
void TextEdit::initNewDoc()
{
docFileName=QString("文档 %1").arg(docNo++);
docWindowTitle=docFileName;
//使用windowmodified机制,添加[*]占位符
//当窗口显示的文档有未保存的更改,*号就会显示出来
setWindowTitle(docWindowTitle + "[*]");
/*
document() 是 QTextEdit 类的一个成员函数,返回当前编辑器中所使用的 QTextDocument 对象。
QTextDocument 类负责管理和操作文本内容。
contentsChanged 是 QTextDocument 类中的一个信号。它在文档的内容发生变化时发出。
文档内容变化的原因可能是用户输入文本、删除文本或其他任何修改内容的操作。
*/
connect(document(),&QTextDocument::contentsChanged,this,&TextEdit::setWindowModify);
}
void TextEdit::setWindowModify()
{
//isModified返回文档是否被用户修改
setWindowModified(document()->isModified());
}
void TextEdit::initOpenDoc(const QString &docName)
{
docFilePath_Name=QFileInfo(docName).canonicalFilePath(); //返回绝度路径,包含文件名
docFileName=QFileInfo(docName).fileName(); //返回文档名称
docWindowTitle=docFileName + " " + QString::number(docNo++); //设置标题
setWindowTitle(docWindowTitle + "[*]"); //设置标题并添加[*]占位符
/*之所以要调用两次信号与槽,是因为信号与槽的生命周期和类是绑定的,比如说新建文档会新建
一个类,那么这个时候就需要连接各种信号与槽,当关闭新建的窗口时,信号与槽就会解绑
新建文档和打开文档是两个独立的功能,所绑定的信号与槽是不通用的,虽然有两个相同的类,但是
内存都不同,所以每次New类的时候都需要重新绑定信号与槽
*/
connect(document(),&QTextDocument::contentsChanged,this,&TextEdit::setWindowModify);
}
QString TextEdit::getDocFilePath() const
{
return docFilePath_Name;
}
bool TextEdit::loadDoc(const QString &docName)
{
if(!docName.isEmpty()){
QFile file(docName);
if(!file.exists()) return false; //判断文件是否存在
if(!file.open(QFile::ReadOnly))return false; //打开文件
QByteArray text=file.readAll(); //读取所有数据,返回值是二进制
if(Qt::mightBeRichText(text)){ //判断文本内容是不是富文本
setHtml(text); //富文本显示
}else{
setText(text); //纯文本显示
}
initOpenDoc(docName); //初始化打开的文档
}
return true;
}
bool TextEdit::SaveDoc()
{
if(document()->isModified()){ //如果文档被修改
if(!docFilePath_Name.isEmpty()){ //如果路径不为空
writeToDoc(docFilePath_Name);
}else{
return Save_save_Doc();
}
}
return false;
}
bool TextEdit::Save_save_Doc()
{
QString docName=QFileDialog::getSaveFileName(this,"另存为","../","HTML文档(*.html);;文本文档(*.txt)");
if(!docName.isEmpty()){
return writeToDoc(docName);
}
return false;
}
bool TextEdit::writeToDoc(const QString &docName)
{
QTextDocumentWriter docWriter(docName); //用于将QTextDocument对象中的文本内容保存到文件中,参数就是文件名
if(docWriter.write(this->document())){ //写入文件,document是返回TexeEdit中的QTextDocument,这类是每个QTextEdit中都有的,负责存储和管理文本
docFilePath_Name=QFileInfo(docName).canonicalFilePath(); //更新路径,特别是另存为可能会改变路径
document()->setModified(false); //设置文档状态未改动,设置内部为未修改
setWindowModified(false); //取消窗口*号显示,设置ui界面*号消失
}else{
return false;
}
return true;
}
void TextEdit::closeEvent(QCloseEvent *event)
{
if(promptSave()){
event->accept(); //表示你处理了事件,事件结束,不再传递。
}else{
event->ignore(); //表示不处理该事件,让事件继续传递到其他地方或执行默认行为。
}
}
bool TextEdit::promptSave()
{
//document()QTextDocument 是 Qt 的文本编辑框架的核心部分
//isModified() 判断文档是否被修改过
if(!document()->isModified())return true;
QMessageBox::StandardButton res;
// res=QMessageBox::warning(this,"提示",QString("文档%1已修改,是否保存?").arg(getDocFileName()),
// QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
res=QMessageBox::warning(this,"提示",QString::asprintf("%s已修改,是否保存?",getDocFileName().toStdString().c_str()),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
//qDebug()<<getDocFileName();
if(res==QMessageBox::Yes){
return SaveDoc();
}else if(res==QMessageBox::No){
return true;
}else if(res==QMessageBox::Cancel){
return false;
}
return false;
}
QString TextEdit::getDocFileName() const
{
return docFileName;
}
main.cpp
cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
总结
这个项目实现了一个简单的文本编辑器,功能包括文本的基本编辑、格式化、撤销重做、剪切复制粘贴、以及文本的对齐和字体设置等。通过 Qt 提供的控件和方法,我们可以轻松地实现这些功能。下面是对整个代码实现的详细总结,帮助你更好地理解每一部分功能和实现原理。
1. 初始化与UI设置
程序的第一步是初始化应用程序及其主界面,创建一个 QMainWindow
窗口,并在其中放置一个 QTextEdit
控件,用于文本的显示和编辑。这是 Qt 中最常用的多行文本控件,支持文本的插入、编辑和格式化。通过设置 QTextEdit
,用户能够直接与文本进行交互。
在构造函数中,我们创建了菜单栏,菜单项包括"文件"和"编辑"菜单,"编辑"菜单下有撤销、重做、剪切、复制、粘贴等常见的文本操作;"文件"菜单下可以实现文本的打开、保存和新建等功能。
2. 文本操作功能
文本操作功能是这段代码的核心部分,主要包括以下几项常用的编辑功能:
-
剪切(cut):
剪切操作通过
textEdit->cut()
实现。当用户点击剪切按钮时,文本框中的选中文本会被剪切到系统剪贴板,并从QTextEdit
控件中删除。这是一个标准的文本编辑操作,允许用户删除选中的文本并将其保留在剪贴板中,以便后续粘贴使用。 -
复制(copy):
复制功能通过
textEdit->copy()
实现,它会将选中的文本复制到系统剪贴板中,而不会删除文本。这样用户可以将选中的内容复制并粘贴到其他地方。 -
粘贴(paste):
粘贴功能通过
textEdit->paste()
方法来实现,用户可以从剪贴板中粘贴内容到当前光标的位置。粘贴功能在剪切和复制之后常用。 -
撤销(undo)与重做(redo):
这两个操作通过
textEdit->undo()
和textEdit->redo()
实现。撤销功能允许用户回退到上一步的编辑状态,例如撤销输入的文字或删除的文本;而重做则是恢复上一步被撤销的操作。如果用户不小心撤销了操作,重做操作可以将其恢复。这两个功能是所有文本编辑器中必不可少的功能,确保用户能够灵活控制编辑历史。
3. 文本格式化功能
文本格式化功能是文本编辑器中的重要部分,可以通过这些功能让文本变得更加生动和富有表现力。代码实现了以下格式化功能:
-
加粗:
使用
QTextCharFormat
和setFontWeight()
来设置字体的粗细。通过mergeCurrentCharFormat
方法将加粗格式应用到选中的文本。 -
斜体:
通过
setFontItalic(true)
设置文本为斜体,并通过mergeCurrentCharFormat
应用到选中的文本区域。 -
下划线:
设置
fontUnderline
为true
来为文本添加下划线,应用到选中的文本。 -
设置字体:
使用
QFontDialog::getFont()
弹出一个字体选择对话框,允许用户选择字体及其样式。选定字体后,调用setCurrentFont()
更新文本框中的字体样式。 -
设置字号:
通过
QInputDialog::getInt()
弹出一个整数输入框,让用户输入所需的字号。然后通过setPointSize()
方法设置字体的大小,从而改变文本的显示效果。 -
设置文本颜色:
使用
QColorDialog::getColor()
获取用户选择的颜色,并通过setForeground()
方法将该颜色应用到选中的文本区域。 -
设置背景颜色:
类似文本颜色设置,使用
QColorDialog::getColor()
获取用户选择的颜色,并通过setBackground()
设置选中文本的背景色。
4. 文本对齐功能
文本对齐是文本编辑器中一个常见的功能,通常包括左对齐、居中对齐、右对齐和两端对齐。在这段代码中,我们使用了 QTextBlockFormat
来控制文本对齐方式:
-
左对齐(setAlignment(Qt::AlignLeft))
通过
QTextBlockFormat
设置文本的对齐方式为左对齐。 -
居中对齐(setAlignment(Qt::AlignCenter))
将对齐方式设置为居中。
-
右对齐(setAlignment(Qt::AlignRight))
将对齐方式设置为右对齐。
-
两端对齐(setAlignment(Qt::AlignJustify))
设置文本为两端对齐方式,使得文本在行内两边对齐,通常用于文档排版。
这些对齐功能通过 textEdit->mergeCurrentBlockFormat()
方法应用到选中的文本区域,允许用户根据需求选择合适的文本排版方式。
5. 功能实现的信号与槽机制
在 Qt 中,信号与槽是处理用户输入和界面交互的核心机制。例如,当用户点击菜单项时,会触发相应的信号。然后,这些信号会连接到相应的槽函数中,槽函数执行相应的操作。例如,点击"撤销"菜单项时,信号会触发槽函数 undo()
,进而撤销文本编辑器中的操作。
信号与槽的连接使得界面与逻辑操作分离,代码更为模块化、清晰。每个按钮或菜单项触发的操作都与相应的槽函数绑定,保证了应用的灵活性和可扩展性。