效果图如下所示:
全部代码如下:
main.h
#include "notepad.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.showMaximized();
return a.exec();
}
notepad.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <subtext.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void closeEvent(QCloseEvent *) override;
private:
Ui::MainWindow *ui;
void init();
private slots:
// 文件
void slot_newFile(bool);
void slot_openFile(bool);
void slot_saveFile(bool);
void slot_saveAsFile(bool);
// 编辑
void slot_copy(bool);
void slot_paste(bool);
void slot_cut(bool);
void slot_undo(bool);
void slot_redo(bool);
// 窗口
void slot_closeFile(bool);
void slot_closeAllFile(bool);
void slot_tile(bool);
void slot_cascade(bool);
void slot_next(bool);
void slot_former(bool);
// 帮助
void slot_about(bool);
void slot_aboutQt(bool);
subtext *getActiveChildForm();
private:
QString codeName;
};
#endif // MAINWINDOW_H
notepad.cpp
#include "notepad.h"
#include "ui_notepad.h"
#include <QDebug>
#include <QMdiSubWindow>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QFont font;
font.setBold(true);
ui->actionnew->setFont(font);
setWindowIcon(QIcon(":/pic/booknote.jpg"));
// 初始化各种功能
init();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *)
{
ui->mdiArea->closeAllSubWindows();
this->close();
}
void MainWindow::init()
{
// 文件
codeName = "UTF-8";
connect(ui->actionnew,SIGNAL(triggered(bool)),this,SLOT(slot_newFile(bool)));
connect(ui->actionOpen,SIGNAL(triggered(bool)),this,SLOT(slot_openFile(bool)));
connect(ui->actionS2,&QAction::triggered,this,[=](){this->codeName = "UTF-8";});
connect(ui->actionS1,&QAction::triggered,this,[=](){this->codeName = "GB18030";});
connect(ui->actionSave,SIGNAL(triggered(bool)),this,SLOT(slot_saveFile(bool)));
connect(ui->actionResave,SIGNAL(triggered(bool)),this,SLOT(slot_saveAsFile(bool)));
connect(ui->actionExit,&QAction::triggered,[=](){
ui->mdiArea->closeAllSubWindows();
this->close();});
// 编辑
connect(ui->actionCopy,SIGNAL(triggered(bool)),this,SLOT(slot_copy(bool)));
connect(ui->actionPaste,SIGNAL(triggered(bool)),this,SLOT(slot_paste(bool)));
connect(ui->actionCut,SIGNAL(triggered(bool)),this,SLOT(slot_cut(bool)));
connect(ui->actionRedo,SIGNAL(triggered(bool)),this,SLOT(slot_redo(bool)));
connect(ui->actionUndo,SIGNAL(triggered(bool)),this,SLOT(slot_undo(bool)));
// 窗口
connect(ui->actionClose,SIGNAL(triggered(bool)),this,SLOT(slot_closeFile(bool)));
connect(ui->actionCloseAll,SIGNAL(triggered(bool)),this,SLOT(slot_closeAllFile(bool)));
connect(ui->actionTile,SIGNAL(triggered(bool)),this,SLOT(slot_tile(bool)));
connect(ui->actionCascade,SIGNAL(triggered(bool)),this,SLOT(slot_cascade(bool)));
connect(ui->actionNext,SIGNAL(triggered(bool)),this,SLOT(slot_next(bool)));
connect(ui->actionFomer,SIGNAL(triggered(bool)),this,SLOT(slot_former(bool)));
// 帮助
connect(ui->actionAbout,SIGNAL(triggered(bool)),this,SLOT(slot_about(bool)));
connect(ui->actionAboutQt,SIGNAL(triggered(bool)),this,SLOT(slot_aboutQt(bool)));
}
void MainWindow::slot_newFile(bool)
{
// qDebug() << "新建文件";
// 创建窗体
// 如果subtext()里不传参则意味着子窗体没有父对象,是离开主窗体独立显示的,
// 如果要在主窗口中显示多个窗体,则要使用MDI Area控件
subtext * sub = new subtext();
ui->mdiArea->addSubWindow(sub);
sub->setCodeName(codeName);
sub->newFile();
sub->show();
}
void MainWindow::slot_openFile(bool)
{
subtext * sub = new subtext();
ui->mdiArea->addSubWindow(sub);
sub->setCodeName(codeName);
sub->openFile();
sub->show();
}
void MainWindow::slot_saveFile(bool)
{
QMdiSubWindow * subWin = ui->mdiArea->activeSubWindow();
if(subWin == NULL)return;
QWidget *win = subWin->widget();
if(win == NULL)return;
subtext *sub = (subtext *)win;
if(sub == NULL)return;
sub->saveFile();
}
void MainWindow::slot_saveAsFile(bool)
{
QMdiSubWindow * subWin = ui->mdiArea->activeSubWindow();
if(subWin == NULL)return;
QWidget *win = subWin->widget();
if(win == NULL)return;
subtext *sub = (subtext *)win;
if(sub == NULL)return;
sub->saveAsFile();
}
void MainWindow::slot_copy(bool)
{
subtext * sub = getActiveChildForm();
if(sub != NULL){
sub->copy();
}
}
void MainWindow::slot_paste(bool)
{
subtext * sub = getActiveChildForm();
if(sub != NULL){
sub->paste();
}
}
void MainWindow::slot_cut(bool)
{
subtext * sub = getActiveChildForm();
if(sub != NULL){
sub->cut();
}
}
void MainWindow::slot_undo(bool)
{
subtext * sub = getActiveChildForm();
if(sub != NULL){
sub->undo();
}
}
void MainWindow::slot_redo(bool)
{
subtext * sub = getActiveChildForm();
if(sub != NULL){
sub->redo();
}
}
void MainWindow::slot_closeFile(bool)
{
ui->mdiArea->closeActiveSubWindow();
}
void MainWindow::slot_closeAllFile(bool)
{
ui->mdiArea->closeAllSubWindows();
}
void MainWindow::slot_tile(bool)
{
ui->mdiArea->tileSubWindows();
}
void MainWindow::slot_cascade(bool)
{
ui->mdiArea->cascadeSubWindows();
}
void MainWindow::slot_next(bool)
{
ui->mdiArea->activateNextSubWindow();
}
void MainWindow::slot_former(bool)
{
ui->mdiArea->activatePreviousSubWindow();
}
void MainWindow::slot_about(bool)
{
QMessageBox::about(this,"关于作者","版权所有:***,2021年2月25日");
}
void MainWindow::slot_aboutQt(bool)
{
QMessageBox::aboutQt(this,"关于Qt");
}
subtext *MainWindow::getActiveChildForm()
{
subtext * sub = NULL;
QMdiSubWindow *subwin = ui->mdiArea->activeSubWindow();
if(subwin == NULL)return sub;
QWidget *wid = subwin->widget();
if(wid == NULL)return sub;
sub = (subtext *)wid;
return sub;
}
subtext.h
#ifndef SUBTEXT_H
#define SUBTEXT_H
#include <QWidget>
#include <QTextEdit>
#include <QFile>
#include <QCloseEvent>
#include <QContextMenuEvent>
class subtext : public QTextEdit
{
Q_OBJECT
public:
explicit subtext(QWidget *parent = nullptr); // 注意将QObject 转换成了QWidget
void newFile(); // 窗体新建
void openFile(); // 打开文件
void setCodeName(const QString codeName); // 设置编码格式
void saveFile(); // 保存文件
void saveAsFile(); // 另存为文件
protected:
void closeEvent(QCloseEvent *) override; // QCloseEvent 可以接受点击叉号的事件
void contextMenuEvent(QContextMenuEvent *) override; // 重写右键菜单事件
private slots:
void slot_contentChanged();
private:
QString filename; // 文件名地址
QFile *myFile;
QString codeName; // 编码格式
bool isEdit = false;
signals:
};
#endif // SUBTEXT_H
subtext.cpp
#include "subtext.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QTextStream> // 文本流 目的是便于修改文件内容
#include <QDebug>
#include <QMenu>
subtext::subtext(QWidget *parent) : QTextEdit(parent)
{
// newFile();
this->filename.clear(); // 清除字符串的内容并使其为空。
myFile = new QFile(this);
}
void subtext::newFile()
{
static int index = 1;
QString str = QString("未命名文档[%1][*]").arg(index); // 使用中括号将*包括,隐藏*
this->setWindowTitle(str);
index++;
// 处于编辑后启用“*”
// document函数返回一个指向QTextDocument*的指针,该对象的信号含有contentschanged信号
connect(this->document(),SIGNAL(contentsChanged()),this,SLOT(slot_contentChanged()));
}
void subtext::openFile()
{
// 获取要打开的文本文件
QString filename = QFileDialog::getOpenFileName(this,"获取文本文件",".",
"Text(*.cpp *.h *.txt)");
// "."表示当前目录
if(filename.isEmpty()){
return;
}
// 把文件路径保存,文件设置到窗体
this->filename = filename;
// 第一种获取文件名方式
// QStringList list = filename.split("/");
// QString title = list.at(list.length()-1);
// 第二种获取文件名方式
QFileInfo info(filename);
QString title = QString("%1[*]").arg(info.fileName());
this->setWindowTitle(title);
connect(this->document(),SIGNAL(contentsChanged()),this,SLOT(slot_contentChanged()));
// 对文本进行操作(用流形式打开文件)
myFile->setFileName(info.fileName());
bool ret = myFile->open(QIODevice::ReadWrite|QIODevice::Text);
if(!ret){
QMessageBox::warning(this,"警告","打开文件失败");
return;
}
QTextStream stream(myFile);
stream.setCodec(this->codeName.toLocal8Bit().data());
while(!stream.atEnd()){
QString str = stream.readLine();
this->append(str);
}
myFile->close();
}
void subtext::setCodeName(const QString codeName)
{
this->codeName = codeName;
}
void subtext::saveFile()
{
// 如果没有编辑则不尽行保存
if(!isEdit){
qDebug() << "未保存";
return;
}
if(this->filename.isEmpty()){
QString filename = QFileDialog::getSaveFileName(this,"保存文件",".","Text(*.cpp *.h *.txt)");
if(filename.isEmpty()){
return;
}
this->filename = filename;
}
// 对文件进行操作
myFile->setFileName(this->filename);
bool ret = myFile->open(QIODevice::WriteOnly|QIODevice::Text);
if(!ret){
QMessageBox::warning(this,"警告","文件打开失败");
return;
}
QTextStream stream(myFile);
stream.setCodec(this->codeName.toLocal8Bit().data());
stream << this->toPlainText();
stream.flush(); // 强制刷新
this->setWindowModified(false);
isEdit = false;
myFile->close();
}
void subtext::saveAsFile()
{
isEdit = true;
filename.clear();
saveFile();
}
void subtext::closeEvent(QCloseEvent *event)
{
if(!isEdit){
return;
}
QMessageBox::StandardButton ret = QMessageBox::information(this,"保存提示","是否保存",
QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,QMessageBox::Yes);
if(ret == QMessageBox::Yes){
saveFile();
event->accept();
}
else if(ret == QMessageBox::No){
event->accept();
}
else if(ret == QMessageBox::Cancel){
event->ignore();
}
}
void subtext::contextMenuEvent(QContextMenuEvent *e)
{
QMenu *myMenu = new QMenu(this);
QAction *redo = myMenu->addAction(QIcon(":/pic/recall2.jpg"),"撤销",this,SLOT(redo()),QKeySequence::Redo);
redo->setEnabled(this->document()->isRedoAvailable());
QAction *undo = myMenu->addAction(QIcon(":/pic/undo.jpg"),"恢复",this,SLOT(undo()),QKeySequence::Undo);
undo->setEnabled(this->document()->isUndoAvailable());
myMenu->addSeparator();
QAction * cut = myMenu->addAction(QIcon(":/pic/cut2.jpg"),"剪贴",this,SLOT(cut()),QKeySequence::Cut);
cut->setEnabled(this->textCursor().hasSelection());
QAction *copy = myMenu->addAction(QIcon(":/pic/copy.jpg"),"复制",this,SLOT(copy()),QKeySequence::Copy);
copy->setEnabled(this->textCursor().hasSelection());
myMenu->addAction(QIcon(":/pic/v.jpg"),"粘贴",this,SLOT(paste()),QKeySequence::Paste);
myMenu->addSeparator();
QAction * clear = myMenu->addAction(QIcon(":/pic/v.jpg"),"清除",this,SLOT(clear()),QKeySequence(tr("Ctrl+D")));
clear->setEnabled(!this->document()->isEmpty());
QAction * selectAll = myMenu->addAction(QIcon(":/pic/v.jpg"),"全选",this,SLOT(selectAll()),QKeySequence::SelectAll);
selectAll->setEnabled(!this->document()->isEmpty());
myMenu->exec(e->globalPos());
}
void subtext::slot_contentChanged()
{
isEdit = true;
this->setWindowModified(true);
}