QT实现WPS功能

一 界面设计

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中的信号发送给信号槽,控制按键是否可以使用
cpp 复制代码
    //如果打开的窗体中选中或取消选中文本时候,&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:判断子窗体是否被激活,如果被激活,启动某些按钮
cpp 复制代码
    //窗体被激活的时候,发出信号&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);
}
  • 连接三:目的,在"窗口"菜单栏中显示所有打开的窗口
cpp 复制代码
    //将活动窗口添加到菜单栏
    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机制,作用是如果当窗显示的文档有未保存的更改,就会显示*

cpp 复制代码
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文件,包含头文件

cpp 复制代码
#include <QPrinter>
#include <QPrintDialog>

#include <QPrintPreviewDialog>


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

  • action发送信号给映射器,映射器发送信号给主窗体来控制对应的子窗体,为啥不都在void MainWindow::addAcrtionsToMenu()里面connect,要去初始化函数里面连接? 是因为槽不是只调用一次
cpp 复制代码
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添加滚动条

cpp 复制代码
  ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
  ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

2.6 修改MDI背景色

cpp 复制代码
    //背景初始化
    ui->mdiArea->setBackground(Qt::white);

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

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

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

  • 初始化字体下选框,在自定义函数中实现
cpp 复制代码
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);
};
  • 将选中的内容设置为相应的字号,在槽函数中实现
cpp 复制代码
void MainWindow::on_comboBoxSize_activated(const QString &arg1)
{
    myTextEdit *edit = getActiveWindow();  //获取活动子窗体
    if(edit){
        QTextCharFormat format;
        format.setFontPointSize(arg1.toInt());
        edit->mergeCurrentCharFormat(format);
    }
}

三 总代码

  • MainWindow .h
cpp 复制代码
#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
cpp 复制代码
#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
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
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();
         }
     }
}
相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript