QT实现WPS功能

发布于:2025-03-24 ⋅ 阅读:(31) ⋅ 点赞:(0)

一 界面设计

在这里插入图片描述

1.1 新建项目

  • 建立mianwindow

在这里插入图片描述
在这里插入图片描述

1.2 添加action

  • 知识点:添加次级菜单的方式,直接拖拽到例如”字体“后面的到三角位置即可
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

1.3 界面部件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 添加语句 ui->mdiArea->setBackground(Qt::white);使得背景变成白色
    在这里插入图片描述
  • 添加语句RC_ICONS += images/wps.ico, 设置窗口图标
    在这里插入图片描述
    在这里插入图片描述

1.4 创建新的c++类,继承QTextEdit来实现多窗口的编辑界面显示

在这里插入图片描述

  • 新建成的c++类如下,需要做头文件声明#include QTextEdit,添加Q_OBJECT,并使其构造函数修改成TextEdit(QWidget *parent = nullptr);,为为什么要这么做,是因为当前类继承于QTextEdit,c++类在实现的时候,是先调用并构造父类才构造子类,而父类的构造函数里面需要传入参数QWidget *parent = nullptr,此处不需要使用到const string,所以使用第二个构造函数,所以需要对当前的类的构造函数进行修改
    在这里插入图片描述
    在这里插入图片描述

二 代码实现

2.0 几个重要的连接

  • 连接1:有些按键的功能需要选中文本之后才可以使用,如何判断文本是否被选中,on_initTextAction是自定义信号槽,将copyAvailable中的信号发送给信号槽,控制按键是否可以使用
    //如果打开的窗体中选中或取消选中文本时候,&TextEdit::copyAvailable 可以发送信号,
    connect(edit,&myTextEdit::copyAvailable, this, &MainWindow::on_initTextAction);

void MainWindow::on_initTextAction(bool checked)
{
    ui->actionCut->setEnabled(checked);
    ui->actionCopy->setEnabled(checked);
    ui->actionPaste->setEnabled(checked);
    ui->actionColor->setEnabled(checked);
    ui->actionBold->setEnabled(checked);
    ui->actionItaly->setEnabled(checked);
    ui->actionUnderline->setEnabled(checked);
    ui->actionLeftAlign->setEnabled(checked);
    ui->actionRightAlign->setEnabled(checked);
    ui->actionCenter->setEnabled(checked);
}
  • 连接2:判断子窗体是否被激活,如果被激活,启动某些按钮
    //窗体被激活的时候,发出信号&QMdiArea::subWindowActivated,当前窗体的initWindowAction会收到信号
    //窗体被关闭也会发出信号,信号参数为0
    connect(ui->mdiArea, &QMdiArea::subWindowActivated, this, &MainWindow::initAllWindowAction);

void MainWindow::initAllWindowAction()
{
    bool hasACtiveWindow = (getActiveWindow() !=nullptr);

    ui->actionSave->setEnabled(hasACtiveWindow);
    ui->actionPrint->setEnabled(hasACtiveWindow);
    ui->actionRedo->setEnabled(hasACtiveWindow);
    ui->actionUndo->setEnabled(hasACtiveWindow);
    ui->actionNext->setEnabled(hasACtiveWindow);
    ui->actionPre->setEnabled(hasACtiveWindow);
    ui->actionPrintView->setEnabled(hasACtiveWindow);

    ui->actionCut->setEnabled(hasACtiveWindow);
    ui->actionCopy->setEnabled(hasACtiveWindow);
    ui->actionPaste->setEnabled(hasACtiveWindow);
    ui->actionColor->setEnabled(hasACtiveWindow);
    ui->actionBold->setEnabled(hasACtiveWindow);
    ui->actionItaly->setEnabled(hasACtiveWindow);
    ui->actionUnderline->setEnabled(hasACtiveWindow);
    ui->actionLeftAlign->setEnabled(hasACtiveWindow);
    ui->actionRightAlign->setEnabled(hasACtiveWindow);
    ui->actionCenter->setEnabled(hasACtiveWindow);
}
  • 连接三:目的,在“窗口”菜单栏中显示所有打开的窗口
    在这里插入图片描述
    //将活动窗口添加到菜单栏
    actionGroup = new QActionGroup(this);  //创建一个action group来装action
    actionGroup->setExclusive(true);  //选中的action只能有一个
    signalMap = new QSignalMapper(this);
    connect(ui->menuWindow,&QMenu::aboutToShow, this, &MainWindow::on_addActionWindowToMenu);
    connect(signalMap,SIGNAL(mapped(QWidget*)),this,SLOT(on_connectActionAndMap(QWidget*)));

2.1 setWindowModified机制,作用是如果当窗显示的文档有未保存的更改,就会显示*

void TextEdit::initNewDoc()
{
    //修改子窗体的标题
    docName = QString ("文档 %1").arg(docNo++);
    //使用windowModified 机制,添加[*]占位符
    //当窗显示的文档有未保存的更改,就会显示*
    setWindowTitle(docName + "[*]");

    //document() 返回当前textEdit内的文档
    connect(document(),&QTextDocument::contentsChanged,this, &TextEdit::setWindowModify);
}

void TextEdit::setWindowModify()
{
    //返回文档是否被用户修改
    setWindowModified(document()->isModified());
}

在这里插入图片描述
在这里插入图片描述

2.2 打印和打印预览功能,需要修改.pro文件,包含头文件

#include <QPrinter>
#include <QPrintDialog>

#include <QPrintPreviewDialog>

在这里插入图片描述
在这里插入图片描述

2.3 映射器的作用,实现多个不同的信号,捆绑连接到同一个槽函数,简化信号连接过程,但是要求是同类型的信号,映射器在当前案例中的实现就是,每个打开的文档,为其创建了一个action,将他们添加到工具栏里面显示

  • action发送信号给映射器,映射器发送信号给主窗体来控制对应的子窗体,为啥不都在void MainWindow::addAcrtionsToMenu()里面connect,要去初始化函数里面连接? 是因为槽不是只调用一次
    在这里插入图片描述
void MainWindow::init()
{
   。。。。。。。。
    connect(ui->menu_window,&QMenu::aboutToShow, this,&MainWindow::addAcrtionsToMenu);
    connect(signalMap,SIGNAL(mapped(QWidget*)),this,SLOT(connectActionAndMap(QWidget*)));
}

void MainWindow::addAcrtionsToMenu()
{
    QList <QMdiSubWindow *> subWindowList = ui->mdiArea->subWindowList();  //获取子窗体列表
    if(!subWindowList.isEmpty()) ui->menu_window->addSeparator();  //在菜单上添加一个分隔符

    for(int i=0;i <subWindowList.count(); i++){
        QMdiSubWindow *subWindow = subWindowList[i];
        TextEdit *edit = qobject_cast<TextEdit*>(subWindow->widget());

        QString actionTitle = QString("%1 %2").arg(i+1).arg(edit->getDocName());
        QAction *action = ui->menu_window->addAction(actionTitle);  //向菜单添加actiOn
        actionGroup->addAction(action);

        if(edit == SubWindow()) action->setCheckable(true);  //对应的活动子窗体设置为被选中状态

        //将action 的triggered信号发送给信号映射器signalMap,由map来进行统一的转发
        connect(action,SIGNAL(triggered(bool)),signalMap,SLOT(map()));
        signalMap->setMapping(action,subWindow);  //添加映射,设置信号发送者和要发送的参数
    }
}

void MainWindow::connectActionAndMap(QWidget *widget)
{
    if(widget) ui->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow*>(widget));
}

2.4 窗体的关闭,需要重写父类函数 void closeEvent(QCloseEvent *event) override;,当前案例对应mainWindow有自己的关闭,以及打开的文档有自己的关闭需求,因此在各自的头文件里面都需要重写该函数,this->close(),本质也是调用该函数

2.5 MDI添加滚动条

  ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
  ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

2.6 修改MDI背景色

    //背景初始化
    ui->mdiArea->setBackground(Qt::white);

2.7 将文本内容写入本地文件,当前案例是继承QTextEdit ,如果是继承QPlainTextEdit,代码也不需要修改,因为 QPlainTextEdit 和 QTextEdit 都使用 QTextDocument 作为其文档模型,QTextEdit 可以处理纯文本或者富文本(除了纯文本就是富文本,包含图片或者其他HTML格式等),QPlainTextEdit只能处理纯文本

  //打开文件对话框
 QString filename = QFileDialog::getSaveFileName(this,"另存为","../","HTML文档(*.html);;文本文档(*.txt)");
 if(filename.isEmpty())
  {
        return false;
  }
 QTextDocumentWriter QdocWrite(filename);
 QdocWrite.write(this->document());

2.8 字号下拉框的初始化,需要手动初始化,才会产生下拉框,字体下拉框不需要手动初始化,标准格式初始化是直接在编辑组合框里面进行内容的添加

在这里插入图片描述

  • 初始化字体下选框,在自定义函数中实现
void MainWindow::initFontSize()
{
    ui->comboBoxSize->clear();

    for(int fontSzie:QFontDatabase::standardSizes()){
        ui->comboBoxSize->addItem(QString::number(fontSzie));
    }

    QFont appFont = QApplication::font(); //当前程序的默认字体
    int fontsize = appFont.pointSize(); // 当前应用程序的字号
    int sizeIndex = ui->comboBoxSize->findText(QString::number(fontsize));

    ui->comboBoxSize->setCurrentIndex(sizeIndex);
};

  • 将选中的内容设置为相应的字号,在槽函数中实现
void MainWindow::on_comboBoxSize_activated(const QString &arg1)
{
    myTextEdit *edit = getActiveWindow();  //获取活动子窗体
    if(edit){
        QTextCharFormat format;
        format.setFontPointSize(arg1.toInt());
        edit->mergeCurrentCharFormat(format);
    }
}

三 总代码

在这里插入图片描述

  • MainWindow .h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "mytextedit.h"
#include <QCloseEvent>
#include <QActionGroup>
#include <QSignalMapper>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_actionNew_triggered();
    void on_actionOpen_triggered();
    void on_actionSave_triggered();
    void on_actionUndo_triggered();
    void on_actionRedo_triggered();
    void on_actionCopy_triggered();
    void on_actionPaste_triggered();
    void on_actionCut_triggered();
    void on_actionColor_triggered();
    void on_actionBold_triggered(bool checked);
    void on_actionItaly_triggered(bool checked);
    void on_actionUnderline_triggered(bool checked);
    void on_actionLeftAlign_triggered(bool checked);
    void on_actionRightAlign_triggered(bool checked);
    void on_actionCenter_triggered(bool checked);
    void on_actionPingPu_triggered();
    void on_actionZanKai_triggered();
    void on_actionPre_triggered();
    void on_actionNext_triggered();

    void on_initTextAction(bool checked);
    void on_comboBoxStytle_activated(int index);
    void on_fontComboBox_activated(const QString &arg1);
    void on_comboBoxSize_activated(const QString &arg1);

    void on_addActionWindowToMenu();
    void on_connectActionAndMap(QWidget *widget);
protected:
    void closeEvent(QCloseEvent *event) override;

private:
    Ui::MainWindow *ui;
    myTextEdit *getActiveWindow();

    void init();
    void initAllWindowAction();
    void initTextAction(bool checked);   //初始化文字操作相关的按钮
    void initFontSize(); //初始化字号组合框

    QActionGroup *actionGroup;
    QSignalMapper *signalMap;
};
#endif // MAINWINDOW_H

  • myTextEdit.h
#ifndef MYTEXTEDIT_H
#define MYTEXTEDIT_H

#include <QObject>
#include <QTextEdit>
#include <QCloseEvent>
class myTextEdit : public QTextEdit
{
    Q_OBJECT
public:
    myTextEdit(QWidget *parent = nullptr);
    ~myTextEdit();
    void NewDoc();
    bool OpenDoc();
    bool SaveDoc();
    bool WriteToFile(QString docRoot);  //将写的内容存入指定文档
    bool DocSaveAs();  //已有文件做了修改,需要保存修改

    int get_docIndex();
    void loadFile(const QString filename);
    QString getDocName();
protected:
    void closeEvent(QCloseEvent *event) override;

private:
    static int docIndex;  //打开的文档的编号
    QString docName;
    QString docRoot;
private slots:
    void on_isDocModified();
};

#endif // MYTEXTEDIT_H

  • MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QColorDialog>
#include <QMdiSubWindow>
#include <QTextList>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    init();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::init()
{
    //背景初始化
    ui->mdiArea->setBackground(Qt::white);

    //添加两个滚动条
    ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

    //窗体被激活的时候,发出信号&QMdiArea::subWindowActivated,当前窗体的initWindowAction会收到信号
    //窗体被关闭也会发出信号,信号参数为0
    connect(ui->mdiArea, &QMdiArea::subWindowActivated, this, &MainWindow::initAllWindowAction);

    initAllWindowAction();
    initTextAction(false);  //文件界面打开的时为空,相关文字操作也应该是不能操作的

    initFontSize();


    //将活动窗口添加到菜单栏
    actionGroup = new QActionGroup(this);  //创建一个action group来装action
    actionGroup->setExclusive(true);  //选中的action只能有一个
    signalMap = new QSignalMapper(this);
    connect(ui->menuWindow,&QMenu::aboutToShow, this, &MainWindow::on_addActionWindowToMenu);
    connect(signalMap,SIGNAL(mapped(QWidget*)),this,SLOT(on_connectActionAndMap(QWidget*)));
}

void MainWindow::initAllWindowAction()
{
    bool hasACtiveWindow = (getActiveWindow() !=nullptr);

    ui->actionSave->setEnabled(hasACtiveWindow);
    ui->actionPrint->setEnabled(hasACtiveWindow);
    ui->actionRedo->setEnabled(hasACtiveWindow);
    ui->actionUndo->setEnabled(hasACtiveWindow);
    ui->actionNext->setEnabled(hasACtiveWindow);
    ui->actionPre->setEnabled(hasACtiveWindow);
    ui->actionPrintView->setEnabled(hasACtiveWindow);

    ui->actionCut->setEnabled(hasACtiveWindow);
    ui->actionCopy->setEnabled(hasACtiveWindow);
    ui->actionPaste->setEnabled(hasACtiveWindow);
    ui->actionColor->setEnabled(hasACtiveWindow);
    ui->actionBold->setEnabled(hasACtiveWindow);
    ui->actionItaly->setEnabled(hasACtiveWindow);
    ui->actionUnderline->setEnabled(hasACtiveWindow);
    ui->actionLeftAlign->setEnabled(hasACtiveWindow);
    ui->actionRightAlign->setEnabled(hasACtiveWindow);
    ui->actionCenter->setEnabled(hasACtiveWindow);
}

void MainWindow::initTextAction(bool checked)
{
    ui->actionCut->setEnabled(checked);
    ui->actionCopy->setEnabled(checked);
    ui->actionPaste->setEnabled(checked);
    ui->actionColor->setEnabled(checked);
    ui->actionBold->setEnabled(checked);
    ui->actionItaly->setEnabled(checked);
    ui->actionUnderline->setEnabled(checked);
    ui->actionLeftAlign->setEnabled(checked);
    ui->actionRightAlign->setEnabled(checked);
    ui->actionCenter->setEnabled(checked);
}

void MainWindow::initFontSize()
{
    ui->comboBoxSize->clear();

    for(int fontSzie:QFontDatabase::standardSizes()){
        ui->comboBoxSize->addItem(QString::number(fontSzie));
    }

    QFont appFont = QApplication::font(); //当前程序的默认字体
    int fontsize = appFont.pointSize(); // 当前应用程序的字号
    int sizeIndex = ui->comboBoxSize->findText(QString::number(fontsize));

    ui->comboBoxSize->setCurrentIndex(sizeIndex);
};


void MainWindow::on_actionNew_triggered()
{
    myTextEdit *edit = new myTextEdit(this);
    ui->mdiArea->addSubWindow(edit);

    edit->show();
    edit->NewDoc();

    initTextAction(false);  //文件界面打开的时为空,相关文字操作也应该是不能操作的
    //如果打开的窗体中选中或取消选中文本时候,&TextEdit::copyAvailable 可以发送信号,
    connect(edit,&myTextEdit::copyAvailable, this, &MainWindow::on_initTextAction);
}

void MainWindow::on_actionOpen_triggered()
{
    myTextEdit *edit = new myTextEdit(this);
    ui->mdiArea->addSubWindow(edit);
    edit->OpenDoc();
    initTextAction(false);
    //如果打开的窗体中选中或取消选中文本时候,&TextEdit::copyAvailable 可以发送信号,
    connect(edit,&myTextEdit::copyAvailable, this, &MainWindow::on_initTextAction);
}

void MainWindow::on_actionSave_triggered()
{
    myTextEdit *edit = new myTextEdit(this);
    ui->mdiArea->addSubWindow(edit);
    edit->SaveDoc();
}


void MainWindow::closeEvent(QCloseEvent *event){

}

myTextEdit *MainWindow::getActiveWindow()
{
    //返回当前活动窗口,如果没有就是空
    QMdiSubWindow *window = ui->mdiArea->activeSubWindow();
    if(window){
        return qobject_cast<myTextEdit*>(window->widget());
    }
    else {
        return nullptr;
    }
}


void MainWindow::on_actionUndo_triggered()
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        edit->undo();
    }
}

void MainWindow::on_actionRedo_triggered()
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        edit->redo();
    }
}

void MainWindow::on_actionCopy_triggered()
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        edit->copy();
    }
}

void MainWindow::on_actionPaste_triggered()
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        edit->paste();
    }
}

void MainWindow::on_actionCut_triggered()
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        edit->cut();
    }
}

void MainWindow::on_actionColor_triggered()
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        QColor color = QColorDialog::getColor();
        if (color.isValid()) {

            QTextCharFormat format; //为字体提供颜色选择
            format.setForeground(color);//设置前景色

            edit->mergeCurrentCharFormat(format); //设置选中文本的颜色

            QPixmap pixmap(16,16);
            pixmap.fill(color);
            ui->actionColor->setIcon(pixmap);
        }
    }
}



void MainWindow::on_actionBold_triggered(bool checked)
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        auto format = edit->currentCharFormat();
        format.setFontWeight(checked ?QFont::Bold : QFont::Normal);
        edit->mergeCurrentCharFormat(format);
    }
}

void MainWindow::on_actionItaly_triggered(bool checked)
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        auto format = edit->currentCharFormat();
        format.setFontItalic(checked);
        edit->mergeCurrentCharFormat(format);
    }
}

void MainWindow::on_actionUnderline_triggered(bool checked)
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        auto format = edit->currentCharFormat();
        format.setFontUnderline(checked);
        edit->mergeCurrentCharFormat(format);
    }
}

void MainWindow::on_actionLeftAlign_triggered(bool checked)
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        edit->setAlignment(Qt::AlignLeft);
    }
}

void MainWindow::on_actionRightAlign_triggered(bool checked)
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
        edit->setAlignment(Qt::AlignRight);
    }
}

void MainWindow::on_actionCenter_triggered(bool checked)
{
    myTextEdit *edit = this->getActiveWindow();  //获取活动子窗体
    if(edit){
         edit->setAlignment(Qt::AlignCenter);
    }
}

void MainWindow::on_actionPingPu_triggered()
{
    ui->mdiArea->tileSubWindows();
}

void MainWindow::on_actionZanKai_triggered()
{
     ui->mdiArea->cascadeSubWindows();
}

void MainWindow::on_actionPre_triggered()
{
    ui->mdiArea->activatePreviousSubWindow();
}

void MainWindow::on_actionNext_triggered()
{
    ui->mdiArea->activateNextSubWindow();
}

void MainWindow::on_initTextAction(bool checked)
{
    ui->actionCut->setEnabled(checked);
    ui->actionCopy->setEnabled(checked);
    ui->actionPaste->setEnabled(checked);
    ui->actionColor->setEnabled(checked);
    ui->actionBold->setEnabled(checked);
    ui->actionItaly->setEnabled(checked);
    ui->actionUnderline->setEnabled(checked);
    ui->actionLeftAlign->setEnabled(checked);
    ui->actionRightAlign->setEnabled(checked);
    ui->actionCenter->setEnabled(checked);
}

void MainWindow::on_comboBoxStytle_activated(int index)
{
    myTextEdit *edit = getActiveWindow();  //获取活动子窗体
    if(edit){
        if(index == 0){  //标准格式,如果之前文本有格式,要去除原本的项目符号
            QTextCursor curse = edit->textCursor();  //获取当前textEdit的可见游标的副本
            curse.beginEditBlock();  //编辑开始
            QTextList *list = curse.currentList();
            if(list){
                list->remove(curse.block());
                QTextBlockFormat blockFormat = curse.blockFormat();
                blockFormat.setIndent(0);   //重置段落缩进
                curse.setBlockFormat(blockFormat);
            }
        }

        QTextListFormat::Style style;
        switch(index){
        case 1:
            style = QTextListFormat::ListSquare;
            break;
        case 2:
            style = QTextListFormat::ListCircle;
            break;
        case 3:
            style = QTextListFormat::ListDisc;
            break;
        case 4:
            style = QTextListFormat::ListUpperAlpha;
            break;
        case 5:
            style = QTextListFormat::ListLowerAlpha;
            break;
        case 6:
            style = QTextListFormat::ListLowerRoman;
            break;
        default:
            style = QTextListFormat::ListStyleUndefined;
            break;
        }
        QTextCursor curse = edit->textCursor();  //获取当前textEdit的可见游标的副本

        curse.beginEditBlock();  //编辑开始
        QTextBlockFormat blockFormat = curse.blockFormat();
        QTextListFormat listFormat;
        QTextList *list = curse.currentList();
        if(list){
            listFormat = list->format();
            list->remove(curse.block());
            blockFormat.setIndent(0);
            curse.setBlockFormat(blockFormat);
        }
        listFormat.setStyle(style);
        curse.createList(listFormat);

        curse.endEditBlock();  //编辑结束
    }
}

void MainWindow::on_fontComboBox_activated(const QString &arg1)
{
    myTextEdit *edit = getActiveWindow();  //获取活动子窗体
    if(edit){
        QTextCharFormat format;
        format.setFontFamily(arg1);
        edit->mergeCurrentCharFormat(format);
    }
}



void MainWindow::on_comboBoxSize_activated(const QString &arg1)
{
    myTextEdit *edit = getActiveWindow();  //获取活动子窗体
    if(edit){
        QTextCharFormat format;
        format.setFontPointSize(arg1.toInt());
        edit->mergeCurrentCharFormat(format);
    }
}

void MainWindow::on_addActionWindowToMenu()
{
    QList<QAction*> actionList = actionGroup->actions();
    for(QAction* action : actionList)   //释放掉所有组内action
    {
        delete action;
    }

    QList <QMdiSubWindow*> subwindowList = ui->mdiArea->subWindowList();  //获取子窗体列表
    if(!subwindowList.isEmpty()){
        ui->menuWindow->addSeparator();  //在“窗口”菜单栏上添加一个分隔符
    }

    for(int i=0; i< subwindowList.count(); i++){
        QMdiSubWindow* subwindow = subwindowList[i];

        //获取当前的活动的action,添加到actionGroup
        myTextEdit *edit =qobject_cast<myTextEdit*> (subwindow->widget());
        QString actionTitle = QString("%1 %2").arg(i+1).arg(edit->getDocName());
        QAction *action = ui->menuWindow->addAction(actionTitle);
        actionGroup->addAction(action);

        if(edit == getActiveWindow()){
             action->setCheckable(true);
             action->setChecked(true);  //对应的活动子窗体设置为被选中状态
        }
        //将action 的triggered信号发送给信号映射器signalMap,由map来进行统一的转发
        connect(action,SIGNAL(triggered(bool)),signalMap,SLOT(map()));
        signalMap->setMapping(action,subwindow);  //添加映射,设置信号发送者和要发送的参数
    }
}

void MainWindow::on_connectActionAndMap(QWidget *widget)
{
     if(widget) ui->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow*>(widget));
}

  • myTextEdit.cpp
#include "mytextedit.h"
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QTextDocumentWriter>

int myTextEdit::docIndex = 1;

myTextEdit::myTextEdit(QWidget *parent):QTextEdit(parent)
{
    docName = "";
    docRoot = "";
}

myTextEdit::~myTextEdit()
{

}

void myTextEdit::NewDoc()
{
//    docIndex++;
//    this->setWindowTitle("文档" + QString::number(this->get_docIndex()));
//    this->show();

    docName = QString("文档%1").arg(docIndex++);
    //使用windowModified 机制,添加[*]占位符
    //当窗显示的文档有未保存的更改,就会显示*
    this->setWindowTitle(docName + "[*]");
    //document() 返回当前textEdit内的文档
    connect(this->document(),&QTextDocument::contentsChanged,this, &myTextEdit::on_isDocModified);
    this->show();
}


void myTextEdit::on_isDocModified()
{
    //返回文档是否被用户修改
    setWindowModified(document()->isModified());
}


bool myTextEdit::OpenDoc()
{
    //打开文件对话框
    QString fileroot = QFileDialog::getOpenFileName(this, "打开一个文件", QDir::currentPath(),
                                 "所有文件(*.*);;源文件(*.h *.cpp);;文本文件(*.txt)");
    if(fileroot.isEmpty())   return false;
    this->show();

    QFileInfo info(fileroot);
    docName = info.fileName();
    docRoot = fileroot;
    this->setWindowTitle(docName + "[*]");
    connect(this->document(),&QTextDocument::contentsChanged,this, &myTextEdit::on_isDocModified);


    this->loadFile(fileroot);
}


bool myTextEdit::SaveDoc()
{
    if(this->document()->isModified()){
        if(docRoot.isEmpty()){  //如果当前文档的路径是空,说明是新建的文档
            //打开文件对话框
            QString docRoot = QFileDialog::getSaveFileName(this,"选择保存路径","../","HTML文档(*.html);;文本文档(*.txt)");
            if(docRoot.isEmpty()) return false;
            //将内容写入txt
            WriteToFile(docRoot);
        }else{  //打开的是已有的文档,需要另存为
            DocSaveAs();
        }
    }
}

bool myTextEdit::WriteToFile(QString docRoot)
{

    //QTextDocumentWriter类用于将QTextDocument内容写入本地文件
    QTextDocumentWriter QdocWrite(docRoot);
    if(QdocWrite.write(this->document())){  //如果写入成功
        this->docRoot = QFileInfo(docRoot).canonicalFilePath(); //获取文件的绝对路径
        this->document()->setModified(false);
        setWindowModified(false); //窗口不显示*占位符
    }

}

bool myTextEdit::DocSaveAs()
{
//    this->docRoot = QFileInfo(docName).canonicalFilePath(); //获取文件的绝对路径
//    QTextDocumentWriter QdocWrite(docRoot);
//    QdocWrite.write(this->document());

    QString docRoot = QFileDialog::getSaveFileName(this,"另存为","../","HTML文档(*.html);;文本文档(*.txt)");
    if(docName.isEmpty()) return false;
    return WriteToFile(docRoot);
}

int myTextEdit::get_docIndex()
{
    return docIndex;
}

void myTextEdit::loadFile(const QString filename)
{
   QFile file(filename);
    if(file.open(QIODevice::ReadOnly )){
        //QTextStream stream(&file);
        //this->setPlainText(stream.readAll());
        QByteArray text = file.readAll();
        if(Qt::mightBeRichText(text)){  //判断是不是富文本,富文本可以存图片,样式等等,纯文本只存储文字
            setHtml(text);  //富文本显示
        }else{
            setPlainText(text);  //纯文本显示
        }
        file.close();
    }

}

QString myTextEdit::getDocName()
{
    return docName;
}

void myTextEdit::closeEvent(QCloseEvent *event)
{
     if(this->document()->isModified()){
         auto res = QMessageBox::information(this,"提示","文件未保存,是否需要保存?",QMessageBox::Save|QMessageBox::No);
         if(res == QMessageBox::Save){

             //将内容写入txt
             SaveDoc();
         }else if(res == QMessageBox::No){
             this->close();
         }
     }
}