目录
使用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; // 新建的文档没有被保存过
// 将当前文件命名为未命名文档加编号,编号先使用再加1
curFile = 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() // 另存为操作
{
// 获取文件路径,如果为空,则返回false
QString 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) // 如果选择取消,则返回false
return 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_OBJECT
public:
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(); // 创建MdiChild
child->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开始的,所以我们这里进行了加1
int 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上实现,内容可能会有逻辑不太合理请指正。