目录

一、准备工作

1.1创建工程

1.2创建资源文件(myimages.qrc)

1.3创建界面文件(mainwindow.ui)

二、实现功能

1.1新建c++class类

1.2 MainWindow类代码如下

1.3主函数

三、程序发布

总结


使用qt5编写一个具有多文档编辑器功能(可以编写多个文档,有保存,打开,编辑,撤销,剪切,复制,粘贴等功能)的项目。

ps:本项目运用Qt5.6和基于《Qt及Qt Quick开发实战精解》第一章开发。(原书是运用Qt4.7和Qt Creator 2.1.0开发)。

下面是程序运行效果:

附上程序和源码地址:

链接:https://pan.baidu.com/s/1sNCOUqrQnX0Com6zxZhfKA 
提取码:idug

一、准备工作

准备需要的图标文件(最好是图标具有实际意义),保存为Qt资源文件,后面好调用,使用ui设计出窗口界面。

1.1创建工程

打开Qt Creator,创建新项目,新建Application ->Qt Widgets Applictation 项目名字改为myMid

设置基类为QMainWindow。创建成功如下:

1.2创建资源文件(myimages.qrc)

鼠标右击工程名称,添加新文件,选择Qt Resource File,文件名改为myimages.qrc,点开myimages.qrc,在下方添加菜单栏中添加一个前缀名/images。然后在工程目录新建一个文件夹命名为images,在里面放入图标文件(可以是任何格式如.**ico **.png **.jpg,但文件大小不易过大)。在回到myimages.qrc,通过添加菜单中的添加文件,将刚刚的图标文件添加进images。添加好后如下图

1.3创建界面文件(mainwindow.ui)

通过这些操作UI界面就设计好了。将四个运行菜单项名字改为menuF,menuE,menuW,menuH。

可以运行看看效果;

二、实现功能

让每个模块实现自己的功能,如打开文件,复制粘贴,撤销撤回等功能。

1.1新建c++class类

在在项目中右键选择新建c++class类基类选择QDialog类名为MdiChild。

MdiChild.h:

#ifndef MDICHILD_H
#define MDICHILD_H#include <QTextEdit>class MdiChild : public QTextEdit
{Q_OBJECT
public:explicit MdiChild(QWidget *parent = 0);void newFile(); // 新建操作bool loadFile(const QString &fileName); // 加载文件bool save();  // 保存操作bool saveAs();  // 另存为操作bool saveFile(const QString &fileName); // 保存文件QString userFriendlyCurrentFile(); // 提取文件名QString currentFile(){return curFile;} // 返回当前文件路径
protected:void closeEvent(QCloseEvent *event); // 关闭事件void contextMenuEvent(QContextMenuEvent *e); // 右键菜单事件
private slots:void documentWasModified();  // 文档被更改时,显示更改状态标志
private:bool maybeSave();  // 是否需要保存void setCurrentFile(const QString &fileName); // 设置当前文件QString curFile;  // 保存当前文件路径bool isUntitled;  // 作为当前文件是否被保存到硬盘上的标志
};#endif // MDICHILD_H

MdiChild.cpp

#include "mdichild.h"
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QFileInfo>
#include <QApplication>
#include <QFileDialog>
#include <QCloseEvent>
#include <QPushButton>
#include <QMenu>
#include <qtextcodec.h>
MdiChild::MdiChild(QWidget *parent) :QTextEdit(parent)
{setAttribute(Qt::WA_DeleteOnClose); // 这样可以在子窗口关闭时销毁这个类的对象isUntitled = true;  // 初始isUntitled为true
}QTextCodec *codec1 = QTextCodec::codecForName("GB2312");//显示为中文字符void MdiChild::newFile() // 新建文件操作
{static int sequenceNumber = 1;// 设置窗口编号,因为编号一直被保存,所以需要使用静态变量isUntitled = true; // 新建的文档没有被保存过// 将当前文件命名为未命名文档加编号,编号先使用再加1curFile = codec1->toUnicode("未命名文档%1.txt").arg(sequenceNumber++);// 设置窗口标题,使用[*]可以在文档被更改后在文件名称后显示”*“号setWindowTitle(curFile + "[*]" + codec1->toUnicode(" - 筱忆多文档编辑器"));// 当文档被更改时发射contentsChanged()信号,// 执行我们的documentWasModified()槽函数connect(document(), SIGNAL(contentsChanged()),this, SLOT(documentWasModified()));}void MdiChild::documentWasModified() //文档被更改时,窗口显示更改状态标志
{// 根据文档的isModified()函数的返回值,判断我们编辑器内容是否被更改了// 如果被更改了,就要在设置了[*]号的地方显示“*”号,这里我们会在窗口标题中显示setWindowModified(document()->isModified());
}bool MdiChild::loadFile(const QString &fileName) // 加载文件
{QFile file(fileName); // 新建QFile对象if (!file.open(QFile::ReadOnly | QFile::Text)) {QMessageBox::warning(this, codec1->toUnicode("筱忆多文档编辑器"),codec1->toUnicode("无法读取文件 %1:\n%2.").arg(fileName).arg(file.errorString()));return false; // 只读方式打开文件,出错则提示,并返回false}QTextStream in(&file); // 新建文本流对象QApplication::setOverrideCursor(Qt::WaitCursor); // 设置鼠标状态为等待状态setPlainText(in.readAll());  // 读取文件的全部文本内容,并添加到编辑器中QApplication::restoreOverrideCursor(); // 恢复鼠标状态setCurrentFile(fileName); // 设置当前文件connect(document(), SIGNAL(contentsChanged()),this, SLOT(documentWasModified()));return true;
}void MdiChild::setCurrentFile(const QString &fileName) // 设置当前文件
{// canonicalFilePath()可以除去路径中的符号链接,“.”和“..”等符号curFile = QFileInfo(fileName).canonicalFilePath();isUntitled = false; // 文件已经被保存过了document()->setModified(false); // 文档没有被更改过setWindowModified(false); // 窗口不显示被更改标志// 设置窗口标题,userFriendlyCurrentFile()返回文件名setWindowTitle(userFriendlyCurrentFile() + "[*]");}QString MdiChild::userFriendlyCurrentFile() // 提取文件名
{return QFileInfo(curFile).fileName(); // 从文件路径中提取文件名
}bool MdiChild::save() // 保存操作
{if (isUntitled) { // 如果文件未被保存过,则执行另存为操作return saveAs();} else {return saveFile(curFile); // 否则直接保存文件}
}bool MdiChild::saveAs() // 另存为操作
{// 获取文件路径,如果为空,则返回falseQString fileName = QFileDialog::getSaveFileName(this, codec1->toUnicode("另存为"),curFile);if (fileName.isEmpty())return false;return saveFile(fileName); // 否则保存文件
}bool MdiChild::saveFile(const QString &fileName) // 保存文件
{QFile file(fileName);if (!file.open(QFile::WriteOnly | QFile::Text)) {QMessageBox::warning(this, codec1->toUnicode("筱忆多文档编辑器"),codec1->toUnicode("无法写入文件 %1:\n%2.").arg(fileName).arg(file.errorString()));return false;}QTextStream out(&file);QApplication::setOverrideCursor(Qt::WaitCursor);out << toPlainText(); // 以纯文本文件写入QApplication::restoreOverrideCursor();setCurrentFile(fileName);return true;
}void MdiChild::closeEvent(QCloseEvent *event)
{if (maybeSave()) { // 如果maybeSave()函数返回true,则关闭窗口event->accept();} else {   // 否则忽略该事件event->ignore();}
}bool MdiChild::maybeSave()  // 是否需要保存
{if (document()->isModified()) { // 如果文档被更改过QMessageBox box;box.setWindowTitle(codec1->toUnicode("筱忆多文档编辑器"));box.setText(codec1->toUnicode("是否保存对“%1”的更改?").arg(userFriendlyCurrentFile()));box.setIcon(QMessageBox::Warning);// 添加按钮,QMessageBox::YesRole可以表明这个按钮的行为QPushButton *yesBtn = box.addButton(codec1->toUnicode("是(&Y)"),QMessageBox::YesRole);box.addButton(codec1->toUnicode("否(&N)"),QMessageBox::NoRole);QPushButton *cancelBtn = box.addButton(codec1->toUnicode("取消"),QMessageBox::RejectRole);box.exec(); // 弹出对话框,让用户选择是否保存修改,或者取消关闭操作if (box.clickedButton() == yesBtn)// 如果用户选择是,则返回保存操作的结果return save();else if (box.clickedButton() == cancelBtn) // 如果选择取消,则返回falsereturn false;}return true; // 如果文档没有更改过,则直接返回true
}void MdiChild::contextMenuEvent(QContextMenuEvent *e) // 右键菜单事件
{QMenu *menu = new QMenu; // 创建菜单,并向其中添加动作QAction *undo = menu->addAction(codec1->toUnicode("撤销(&U)"),this,SLOT(undo()),QKeySequence::Undo);undo->setEnabled(document()->isUndoAvailable());QAction *redo = menu->addAction(codec1->toUnicode("恢复(&R)"),this,SLOT(redo()),QKeySequence::Redo);redo->setEnabled(document()->isRedoAvailable());menu->addSeparator();QAction *cut = menu->addAction(codec1->toUnicode("剪切(&T)"),this,SLOT(cut()),QKeySequence::Cut);cut->setEnabled(textCursor().hasSelection());QAction *copy = menu->addAction(codec1->toUnicode("复制(&C)"),this,SLOT(copy()),QKeySequence::Copy);copy->setEnabled(textCursor().hasSelection());menu->addAction(codec1->toUnicode("粘贴(&P)"),this,SLOT(paste()),QKeySequence::Paste);QAction *clear = menu->addAction(codec1->toUnicode("清空"),this,SLOT(clear()));clear->setEnabled(!document()->isEmpty());menu->addSeparator();QAction *select = menu->addAction(codec1->toUnicode("全选"),this,SLOT(selectAll()),QKeySequence::SelectAll);select->setEnabled(!document()->isEmpty());menu->exec(e->globalPos()); // 获取鼠标的位置,然后在这个位置显示菜单delete menu; // 最后销毁这个菜单
}

1.2 MainWindow类代码如下

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
class MdiChild;
class QMdiSubWindow;
class QSignalMapper;
namespace Ui {class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();private slots:void on_actionNew_triggered(); // 新建文件菜单void updateMenus(); // 更新菜单MdiChild *createMdiChild(); // 创建子窗口void setActiveSubWindow(QWidget *window); // 设置活动子窗口void on_actionOpen_triggered(); // 打开文件菜单void updateWindowMenu(); // 更新窗口菜单void on_actionSave_triggered();void on_actionSaveAs_triggered();void on_actionUndo_triggered();void on_actionRedo_triggered();void on_actionCut_triggered();void on_actionCopy_triggered();void on_actionPaste_triggered();void on_actionClose_triggered();void on_actionCloseAll_triggered();void on_actionTile_triggered();void on_actionCascade_triggered();void on_actionNext_triggered();void on_actionPrevious_triggered();void on_actionAbout_triggered();void on_actionAboutQt_triggered();void on_actionExit_triggered();void showTextRowAndCol(); // 显示文本的行号和列号private:Ui::MainWindow *ui;QAction *actionSeparator; // 分隔符MdiChild *activeMdiChild(); // 活动窗口QMdiSubWindow *findMdiChild(const QString &fileName);// 查找子窗口QSignalMapper *windowMapper; // 信号映射器void readSettings();  // 读取窗口设置void writeSettings(); // 写入窗口设置void initWindow(); // 初始化窗口protected:void closeEvent(QCloseEvent *event);  // 关闭事件
};#endif // MAINWINDOW_H

MainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mdichild.h"
#include <QMdiSubWindow>
#include <QFileDialog>
#include <QSignalMapper>
#include <QMessageBox>
#include <QSettings>
#include <QCloseEvent>
#include <QLabel>
#include <qtextcodec.h>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);actionSeparator = new QAction(this); // 创建间隔器动作actionSeparator->setSeparator(true); // 在其中设置间隔器updateMenus();   // 更新菜单connect(ui->mdiArea,SIGNAL(subWindowActivated(QMdiSubWindow*)),this,SLOT(updateMenus()));  // 当有活动窗口时更新菜单windowMapper = new QSignalMapper(this); // 创建信号映射器connect(windowMapper, SIGNAL(mapped(QWidget*)), // 映射器重新发射信号this, SLOT(setActiveSubWindow(QWidget*))); // 设置活动窗口// 更新窗口菜单,并且设置当窗口菜单将要显示的时候更新窗口菜单updateWindowMenu();connect(ui->menuW,SIGNAL(aboutToShow()),this,SLOT(updateWindowMenu()));readSettings(); // 初始窗口时读取窗口设置信息initWindow(); // 初始化窗口
}QTextCodec *codec = QTextCodec::codecForName("GB2312");
MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_actionNew_triggered()  // 新建文件菜单
{MdiChild *child = createMdiChild(); // 创建MdiChildchild->newFile(); // 新建文件child->show(); // 显示子窗口
}MdiChild * MainWindow::createMdiChild() // 创建子窗口部件
{MdiChild *child = new MdiChild; // 创建MdiChild部件ui->mdiArea->addSubWindow(child); // 向多文档区域添加子窗口,child为中心部件// 根据QTextEdit类的是否可以复制信号设置剪切复制动作是否可用connect(child,SIGNAL(copyAvailable(bool)),ui->actionCut,SLOT(setEnabled(bool)));connect(child,SIGNAL(copyAvailable(bool)),ui->actionCopy,SLOT(setEnabled(bool)));// 根据QTextDocument类的是否可以撤销恢复信号设置撤销恢复动作是否可用connect(child->document(),SIGNAL(undoAvailable(bool)),ui->actionUndo,SLOT(setEnabled(bool)));connect(child->document(),SIGNAL(redoAvailable(bool)),ui->actionRedo,SLOT(setEnabled(bool)));// 每当编辑器中的光标位置改变,就重新显示行号和列号connect(child,SIGNAL(cursorPositionChanged()),this,SLOT(showTextRowAndCol()));return child;
}void MainWindow::updateMenus() // 更新菜单
{bool hasMdiChild = (activeMdiChild() != 0); // 是否有活动窗口ui->actionSave->setEnabled(hasMdiChild);   // 设置各个动作是否可用ui->actionSaveAs->setEnabled(hasMdiChild);ui->actionPaste->setEnabled(hasMdiChild);ui->actionClose->setEnabled(hasMdiChild);ui->actionCloseAll->setEnabled(hasMdiChild);ui->actionTile->setEnabled(hasMdiChild);ui->actionCascade->setEnabled(hasMdiChild);ui->actionNext->setEnabled(hasMdiChild);ui->actionPrevious->setEnabled(hasMdiChild);actionSeparator->setVisible(hasMdiChild); // 设置间隔器是否显示bool hasSelection = (activeMdiChild()&& activeMdiChild()->textCursor().hasSelection());// 有活动窗口且有被选择的文本,剪切复制才可用ui->actionCut->setEnabled(hasSelection);ui->actionCopy->setEnabled(hasSelection);ui->actionUndo->setEnabled(activeMdiChild() // 有活动窗口且文档有撤销操作&& activeMdiChild()->document()->isUndoAvailable());ui->actionRedo->setEnabled(activeMdiChild() // 有活动窗口且文档有恢复操作&& activeMdiChild()->document()->isRedoAvailable());
}MdiChild * MainWindow::activeMdiChild() // 活动窗口
{// 如果有活动窗口,则将其内的中心部件转换为MdiChild类型if (QMdiSubWindow *activeSubWindow = ui->mdiArea->activeSubWindow())return qobject_cast<MdiChild *>(activeSubWindow->widget());return 0; // 没有活动窗口,直接返回0
}void MainWindow::on_actionOpen_triggered() // 打开文件菜单
{QString fileName = QFileDialog::getOpenFileName(this); // 获取文件路径if (!fileName.isEmpty()) { // 如果路径不为空,则查看该文件是否已经打开QMdiSubWindow *existing = findMdiChild(fileName);if (existing) { // 如果已经存在,则将对应的子窗口设置为活动窗口ui->mdiArea->setActiveSubWindow(existing);return;}MdiChild *child = createMdiChild(); // 如果没有打开,则新建子窗口if (child->loadFile(fileName)) {ui->statusBar->showMessage(codec->toUnicode("打开文件成功"), 2000);child->show();} else {child->close();}}
}QMdiSubWindow * MainWindow::findMdiChild(const QString &fileName) // 查找子窗口
{QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();// 利用foreach语句遍历子窗口列表,如果其文件路径和要查找的路径相同,则返回该窗口foreach (QMdiSubWindow *window, ui->mdiArea->subWindowList()) {MdiChild *mdiChild = qobject_cast<MdiChild *>(window->widget());if (mdiChild->currentFile() == canonicalFilePath)return window;}return 0;
}void MainWindow::setActiveSubWindow(QWidget *window) // 设置活动子窗口
{if (!window) // 如果传递了窗口部件,则将其设置为活动窗口return;ui->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow *>(window));
}void MainWindow::updateWindowMenu() // 更新窗口菜单
{ui->menuW->clear(); // 先清空菜单,然后再添加各个菜单动作ui->menuW->addAction(ui->actionClose);ui->menuW->addAction(ui->actionCloseAll);ui->menuW->addSeparator();ui->menuW->addAction(ui->actionTile);ui->menuW->addAction(ui->actionCascade);ui->menuW->addSeparator();ui->menuW->addAction(ui->actionNext);ui->menuW->addAction(ui->actionPrevious);ui->menuW->addAction(actionSeparator);QList<QMdiSubWindow *> windows = ui->mdiArea->subWindowList();actionSeparator->setVisible(!windows.isEmpty());// 如果有活动窗口,则显示间隔器for (int i = 0; i < windows.size(); ++i) { // 遍历各个子窗口MdiChild *child = qobject_cast<MdiChild *>(windows.at(i)->widget());QString text;if (i < 9) { // 如果窗口数小于9,则设置编号为快捷键text = tr("&%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile());} else {text = tr("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile());}QAction *action  = ui->menuW->addAction(text); // 添加动作到菜单action->setCheckable(true); // 设置动作可以选择// 设置当前活动窗口动作为选中状态action ->setChecked(child == activeMdiChild());// 关联动作的触发信号到信号映射器的map()槽函数上,这个函数会发射mapped()信号connect(action, SIGNAL(triggered()), windowMapper, SLOT(map()));// 将动作与相应的窗口部件进行映射,在发射mapped()信号时就会以这个窗口部件为参数windowMapper->setMapping(action, windows.at(i));}
}/********************1.3.3小节对下面这些代码进行了部分省略****************************************/void MainWindow::on_actionSave_triggered() // 保存菜单
{if(activeMdiChild() && activeMdiChild()->save())ui->statusBar->showMessage(codec->toUnicode("文件保存成功"),2000);
}void MainWindow::on_actionSaveAs_triggered()  // 另存为菜单
{if(activeMdiChild() && activeMdiChild()->saveAs())ui->statusBar->showMessage(codec->toUnicode("文件保存成功"),2000);
}void MainWindow::on_actionUndo_triggered() // 撤销菜单
{if(activeMdiChild()) activeMdiChild()->undo();
}void MainWindow::on_actionRedo_triggered() // 恢复菜单
{if(activeMdiChild()) activeMdiChild()->redo();
}void MainWindow::on_actionCut_triggered() // 剪切菜单
{if(activeMdiChild()) activeMdiChild()->cut();
}void MainWindow::on_actionCopy_triggered() // 复制菜单
{if(activeMdiChild()) activeMdiChild()->copy();
}void MainWindow::on_actionPaste_triggered() // 粘贴菜单
{if(activeMdiChild()) activeMdiChild()->paste();
}void MainWindow::on_actionClose_triggered() // 关闭菜单
{ui->mdiArea->closeActiveSubWindow();
}void MainWindow::on_actionCloseAll_triggered() // 关闭所有窗口菜单
{ui->mdiArea->closeAllSubWindows();
}void MainWindow::on_actionTile_triggered() // 平铺菜单
{ui->mdiArea->tileSubWindows();
}void MainWindow::on_actionCascade_triggered() // 层叠菜单
{ui->mdiArea->cascadeSubWindows();
}void MainWindow::on_actionNext_triggered() // 下一个菜单
{ui->mdiArea->activateNextSubWindow();
}void MainWindow::on_actionPrevious_triggered() // 前一个菜单
{ui->mdiArea->activatePreviousSubWindow();
}void MainWindow::on_actionAbout_triggered() // 关于菜单
{QMessageBox::about(this,codec->toUnicode("关于本软件"),codec->toUnicode("欢迎访问我的csdn主页:https://blog.csdn.net/qq_46424406?spm=1003.2020.3001.5343"));
}void MainWindow::on_actionAboutQt_triggered() // 关于Qt菜单
{qApp->aboutQt(); // 这里的qApp是QApplication对象的全局指针,// 这行代码相当于QApplication::aboutQt();
}void MainWindow::on_actionExit_triggered() // 退出菜单
{qApp->closeAllWindows(); // 等价于QApplication::closeAllWindows();
}void MainWindow::closeEvent(QCloseEvent *event) // 关闭事件
{ui->mdiArea->closeAllSubWindows(); // 先执行多文档区域的关闭操作if (ui->mdiArea->currentSubWindow()) {event->ignore(); // 如果还有窗口没有关闭,则忽略该事件} else {writeSettings(); // 在关闭前写入窗口设置event->accept();}
}void MainWindow::writeSettings() // 写入窗口设置
{QSettings settings("yafeilinux", "myMdi");settings.setValue("pos", pos());   // 写入位置信息settings.setValue("size", size()); // 写入大小信息
}void MainWindow::readSettings() // 读取窗口设置
{QSettings settings("yafeilinux", "myMdi");QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();QSize size = settings.value("size", QSize(400, 400)).toSize();move(pos);resize(size);
}void MainWindow::showTextRowAndCol() // 显示文本的行号和列号
{// 如果有活动窗口,则显示其中光标所在的位置if(activeMdiChild()){// 因为获取的行号和列号都是从0开始的,所以我们这里进行了加1int rowNum = activeMdiChild()->textCursor().blockNumber()+1;int colNum = activeMdiChild()->textCursor().columnNumber()+1;ui->statusBar->showMessage(tr("%1行 %2列").arg(rowNum).arg(colNum),2000);}
}void MainWindow::initWindow() // 初始化窗口
{setWindowTitle(codec->toUnicode("筱忆多文档编辑器"));// 我们在工具栏上单击鼠标右键时,可以关闭工具栏ui->mainToolBar->setWindowTitle(codec->toUnicode("工具栏"));// 当多文档区域的内容超出可视区域后,出现滚动条ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);ui->statusBar->showMessage(codec->toUnicode("欢迎使用筱忆多文档编辑器"));QLabel *label = new QLabel(this);label->setFrameStyle(QFrame::Box | QFrame::Sunken);label->setText(tr("<a href=\"https://blog.csdn.net/qq_46424406?spm=1003.2020.3001.5343\">xiaoyi.com</a>"));label->setTextFormat(Qt::RichText); // 标签文本为富文本label->setOpenExternalLinks(true);  // 可以打开外部链接ui->statusBar->addPermanentWidget(label);ui->actionNew->setStatusTip(codec->toUnicode("创建一个文件"));ui->actionOpen->setStatusTip(codec->toUnicode("打开一个已经存在的文件"));ui->actionSave->setStatusTip(codec->toUnicode("保存文档到硬盘"));ui->actionSaveAs->setStatusTip(codec->toUnicode("以新的名称保存文档"));ui->actionExit->setStatusTip(codec->toUnicode("退出应用程序"));ui->actionUndo->setStatusTip(codec->toUnicode("撤销先前的操作"));ui->actionRedo->setStatusTip(codec->toUnicode("恢复先前的操作"));ui->actionCut->setStatusTip(codec->toUnicode("剪切选中的内容到剪贴板"));ui->actionCopy->setStatusTip(codec->toUnicode("复制选中的内容到剪贴板"));ui->actionPaste->setStatusTip(codec->toUnicode("粘贴剪贴板的内容到当前位置"));ui->actionClose->setStatusTip(codec->toUnicode("关闭活动窗口"));ui->actionCloseAll->setStatusTip(codec->toUnicode("关闭所有窗口"));ui->actionTile->setStatusTip(codec->toUnicode("平铺所有窗口"));ui->actionCascade->setStatusTip(codec->toUnicode("层叠所有窗口"));ui->actionNext->setStatusTip(codec->toUnicode("将焦点移动到下一个窗口"));ui->actionPrevious->setStatusTip(codec->toUnicode("将焦点移动到前一个窗口"));ui->actionAbout->setStatusTip(codec->toUnicode("显示本软件的介绍"));}

1.3主函数

main.cpp

#include <QtWidgets/QApplication>
#include "mainwindow.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}

以上就是全部代码块。

运行成功就已经实现了一个记事本基本功能了;

三、程序发布

发布QT程序需要对程序设置图标也就是我那个鲨鱼图标,只需要去设计一个.ico文件,文件名称设置为my.ico(方便代码调用),然后在.pro文件中添加

RC_ICONS = my.ico

然后编译,编译时使用Release编译,编译成功后可以在工程目录找到一个release文件,在此文件里面有一个.exe文件,这里我就是myMdi.exe

但是这个文件直接发给别人是不能直接运行的(原因是没有运行环境)所以说我们需要去进行打包发布。

可以通过Qt命令窗口来添加,在Qt命令输入

windeployqt 路径

我这里输入的是

windeployqt C:\Users\XiaoYi\Desktop\myapp

在原文件夹就有了添加的一些文件如下

现在压缩就可以发布了;

总结

这就是Qt写一个记事本的全部代码,在前文有发布文件和全部项目代码。本次代码大多数基于《Qt及Qt Quick开发实战精解》的源代码,只是使其在Qt5上实现,内容可能会有逻辑不太合理请指正。

Qt5编写一个记事本(多文档编辑器)相关推荐

  1. 如何编写一个Java帮助文档

    如何编写一个帮助文档 写一个工具类 对这个类加入文档注释 /** *@author 作者 *@version 版本 *... *@param 参数 参数说明 *@return 返回值说明 用javad ...

  2. JAVA编写的文档编辑器源代码

    //简单的类似记事本的文档编辑器 import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swin ...

  3. 06_多窗口文档编辑器(小钱版)[Qt开发][2012-03-07]

    1) 引言 -> 模块01:程序简要说明 1. 程序名称:多窗口文档编辑器(Multiple Text Editor) 2. 运行环境:Window NT / 2000 / XP / VISTA ...

  4. 代记事本的各种文档编辑器使用感受

    代记事本的各种文档编辑器使用感受(2009-12-28 17:59:32) 转自:http://blog.sina.com.cn/s/blog_55ccd3160100gm8n.html 标签: mi ...

  5. 专业的LaTeX: 在Linux下编写高质量的文档

    专业的LaTeX: 在Linux下编写高质量的文档 Linux下的OpenOffice.KWord等字处理软件虽然在功能上与Microsoft Word类似,但目前在易用性和可用性方面仍然存在许多不足 ...

  6. sscanf 连续相同编辑符_【第1995期】钉钉文档编辑器的前世今生

    前言 编辑器,一个重前端的领域方向.今日早读文章由钉钉@展新授权分享. @展新,来自钉钉文档团队.2011 年加入支付宝,一路成长于支付宝的前端团队,孵化了语雀,2018 年到钉钉,开启钉钉文档的旅程 ...

  7. 编辑器未包含main类型_利用 ONLYOFFICE 将在线文档编辑器集成到 Python Web 应用程序中...

    通过 API,开发人员可以将 ONLYOFFICE 编辑器集成到网站和利用程序设计语言编写的应用程序中,并能配置和管理编辑器. 来源:https://linux.cn/article-13037-1. ...

  8. 语雀文档编辑器将开源:始于但不止于Markdown

    文 | 大东 出品 | OSC开源社区(ID:oschina2013) 无论是写 API 文档还是输出技术博客,技术写作已经成为各位程序员写代码之余最多的工作,趁手的文档编辑器或许是仅次于 IDE 的 ...

  9. emeditor python_EmEditor Professional( 文档编辑器 )中文版分享

    EmEditor Professional20中文版是一款小巧强大的文档编辑器,它不仅运行的速度快还支持多种编程语言,是一款可以完全替代系统自带文档编辑的软件,它不仅仅可以胜任各位日常的文本编辑功能还 ...

最新文章

  1. 使用迭代查找一个list中最小和最大值,并返回一个tuple。
  2. 重要通知 | WanaCrypt0r 2.0及Onion等勒索软件安全建议
  3. “被遗忘的宇航员”逝世,他是阿波罗11号上唯一没登月的人
  4. IE6 CSS的一个bug
  5. Asp.net生成工作流、审批流的解决方案(asp.net workflow svg)
  6. 24帧动画走路分解图_人眼只能分辨24帧?我们来聊聊高刷新率的意义
  7. java coherence_Coherence配置默认覆盖路径
  8. ejb 属于哪一层,作用是什么,什么时候用
  9. 解决IDEA创建多模块项目找不到创建class类的问题
  10. .foreach()需要判断空吗_这次我们来聊聊 Stream#forEach 源码
  11. 在信号处理函数中调用longjmp
  12. txt简谱制作器上线(个人java程序)
  13. 绿联USB2.0 USB外接网卡驱动
  14. ASF文件格式详解(1)
  15. python与数据思维基础_7个python案例中的数据思维
  16. Java笔记 - 黑马程序员_03(继承,修饰符,多态,抽象类,接口,内部类)
  17. ts当中的infer关键字
  18. 网络扫描工具ncnmap
  19. Android2.0 Release 1 Eclair API变化预览
  20. MySQL 日期字符串转换

热门文章

  1. HandlerThread案例学习
  2. ios苹果企业签名操作方法及使用流程
  3. ES7在WIN环境开启闪退【完美解决】
  4. IDEA遇到项目包后面出现% classes,% lines covered该怎么解决
  5. 台式电脑(ubuntu系统)开机界面循环This product is covered by one or more of the following patents
  6. 异或性质——前缀异或
  7. R语言设置数值输出(保留至小数点后位数和保留有效数字)
  8. WebSocket实现简单的web聊天室
  9. 【小f的刷题笔记】(JS)链表 - 合并 k 个有序链表 LeetCode23 - 顺序合并暴力三种方法
  10. Java基础系列24-常用api之String类