找回密码
 立即注册
收起左侧

第6篇 基础(六)实现Qt文本编辑功能

154
回复
110002
查看
  [复制链接]
累计签到:1564 天
连续签到:1 天
来源: 2013-3-26 19:49:17 显示全部楼层 |阅读模式
实现Qt文本编辑功能


版权声明

该文章原创于Qter开源社区(www.qter.org),作者yafeilinux,转载请注明出处!


导语

       前面已经在主窗口中添加了菜单和工具栏,这一篇中我们将实现基本的文本编辑功能。在开始正式写程序之前,我们先要考虑一下整个流程。因为这里要写一个记事本一样的程序,所以最好先打开Windows中的记事本,进行一些简单的操作,然后考虑怎样去实现这些功能。再者,再强大的软件,它的功能也是一个一个加上去的,不要设想一下子写出所有的功能。我们这里先实现新建文件,保存文件,和文件另存为三个功能,是因为它们联系很紧,而且这三个功能总的代码量也不是很大。


环境是:Windows 7 + Qt 4.8.1+ Qt Creator 2.4.1


目录

一、实现新建文件、文件保存和另存为功能
二、实现打开、关闭、退出、撤销、复制、剪切、粘贴等功能


正文

一、实现新建文件、文件保存和另存为功能

1.首先来分析下整个流程,当新建文件时,要考虑是否保存正在编辑的文件,如果需要保存,还要根据该文件以前是否保存过来进行保存或者另存为操作。下面我们根据这里的分析来添加需要的函数和对象。

2.打开上一篇完成的项目,然后先在main.cpp文件中添加代码来保证代码中可以使用中文字符。
首先添加#include <QTextCodec>头文件包含,然后在主函数中添加如下代码:
QTextCodec::setCodecForTr(QTextCodec::codecForLocale());

3.mainwindow.h文件中添加public函数声明:
void newFile();   // 新建操作
bool maybeSave(); // 判断是否需要保存
bool save();      // 保存操作
bool saveAs();    // 另存为操作
bool saveFile(const QString &fileName); // 保存文件

       这里的几个函数就是用来完成功能逻辑的,下面我们会添加它们的定义来实现相应的功能。因为这几个功能联系紧密,所以这几个函数会相互调用。

4.然后添加private变量定义:
// 为真表示文件没有保存过,为假表示文件已经被保存过了
bool isUntitled;
// 保存当前文件的路径
QString curFile;

       这里的isUntitled是一个标志,用来判断文档是否被保存过。而curFile用来保存当前打开的文件的路径。

5.下面到mainwindow.cpp文件,先添加头文件:
#include <QMessageBox>
#include <QPushButton>
#include <QFileDialog>
#include <QTextStream>
然后在构造函数中添加如下代码来进行一些初始化操作:
// 初始化文件为未保存状态
isUntitled = true;
// 初始化文件名为"未命名.txt"
curFile = tr("未命名.txt");
// 初始化窗口标题为文件名
setWindowTitle(curFile);
    这里设置了在启动程序时窗口标题显示文件的名字,效果如下图所示。

6.下面添加那几个函数的定义。
首先是新建文件操作的函数:
void MainWindow::newFile()
{
   if (maybeSave()) {
       isUntitled = true;
       curFile = tr("未命名.txt");
       setWindowTitle(curFile);
       ui->textEdit->clear();
       ui->textEdit->setVisible(true);
   }
}
       这里先使用maybeSave()来判断文档是否需要保存,如果已经保存完了,则新建文档,并进行初始化。下面是maybeSave()函数的定义:
bool MainWindow::maybeSave()
{
   // 如果文档被更改了
if (ui->textEdit->document()->isModified()) {
// 自定义一个警告对话框
       QMessageBox box;
       box.setWindowTitle(tr("警告"));
       box.setIcon(QMessageBox::Warning);
       box.setText(curFile + tr(" 尚未保存,是否保存?"));
       QPushButton *yesBtn = box.addButton(tr("是(&Y)"),
                        QMessageBox::YesRole);
       box.addButton(tr("否(&N)"), QMessageBox::NoRole);
       QPushButton *cancelBut = box.addButton(tr("取消"),
                        QMessageBox::RejectRole);
       box.exec();
       if (box.clickedButton() == yesBtn)
            return save();
       else if (box.clickedButton() == cancelBut)
            return false;
   }
   // 如果文档没有被更改,则直接返回true
   return true;
}
    这里先使用了isModified()来判断文档是否被更改了,如果被更改了,则弹出对话框让用户选择是否进行保存,或者取消操作。如果取消操作,那么就返回false,什么都不执行。下面是save()函数的定义:
bool MainWindow::save()
{
   if (isUntitled) {
       return saveAs();
   } else {
       return saveFile(curFile);
   }
}
    这里如果文档以前没有保存过,那么执行另存为操作saveAs(),如果已经保存过,那么调用saveFile()执行文件保存操作。下面是saveAs()函数的定义:
bool MainWindow::saveAs()
{
   QString fileName = QFileDialog::getSaveFileName(this,
                                         tr("另存为"),curFile);
   if (fileName.isEmpty()) return false;
   return saveFile(fileName);
}
       这里使用QFileDialog来实现了一个另存为对话框,并且获取了文件的路径,然后使用文件路径来保存文件。下面是saveFile()函数的定义:
bool MainWindow::saveFile(const QString &fileName)
{
   QFile file(fileName);
   
   if (!file.open(QFile::WriteOnly | QFile::Text)) {
      
       // %1和%2分别对应后面arg两个参数,/n起换行的作用
       QMessageBox::warning(this, tr("多文档编辑器"),
                   tr("无法写入文件 %1:/n %2")
                  .arg(fileName).arg(file.errorString()));
       return false;
   }
   QTextStream out(&file);
   // 鼠标指针变为等待状态
   QApplication::setOverrideCursor(Qt::WaitCursor);
   out << ui->textEdit->toPlainText();
   // 鼠标指针恢复原来的状态
   QApplication::restoreOverrideCursor();
   isUntitled = false;
   // 获得文件的标准路径
   curFile = QFileInfo(fileName).canonicalFilePath();
   setWindowTitle(curFile);
   return true;
}
       该函数执行真正的文件保存操作。先是使用一个QFile类对象来指向要保存的文件,然后将其使用写入方式打开。打开后再使用QTextStream文本流将编辑器中的内容写入到文件中。
       这里使用了很多新的类,以后我们对自己不明白的类都可以去帮助里进行查找,这也许是我们以后要做的最多的一件事了。对于其中的英文解释,我们最好想办法弄明白它的大意,其实网上也有一些中文的翻译,但最好还是从一开始就尝试着看英文原版的帮助,这样以后才不会对中文翻译产生依赖。


7.设置菜单功能。双击mainwindow.ui文件,在图形界面窗口下面的Action编辑器里,我们右击新建菜单一条,选择“转到槽”,然后选择triggered(),进入其触发事件槽。如下图所示。


同理,进入其他两个菜单的槽,将相应的操作的函数写入槽中。最终代码如下:
void MainWindow::on_action_New_triggered()
{
   newFile();
}
void MainWindow::on_action_Save_triggered()
{
   save();
}
void MainWindow::on_action_SaveAs_triggered()
{
   saveAs();
}

现在运行程序,已经能够实现新建文件,保存文件,文件另存为的功能了。


二、实现打开、关闭、退出、撤销、复制、剪切、粘贴等功能

       先到mainwindow.h文件中添加public函数声明:
bool loadFile(const QString &fileName); // 加载文件

然后到mainwindow.cpp文件中添加该函数的定义:
bool MainWindow::loadFile(const QString &fileName)
{
   QFile file(fileName); // 新建QFile对象
   if (!file.open(QFile::ReadOnly | QFile::Text)) {
       QMessageBox::warning(this, tr("多文档编辑器"),
                             tr("无法读取文件 %1:\n%2.")
                             .arg(fileName).arg(file.errorString()));
       return false; // 只读方式打开文件,出错则提示,并返回false
   }
   QTextStream in(&file); // 新建文本流对象
QApplication::setOverrideCursor(Qt::WaitCursor);
// 读取文件的全部文本内容,并添加到编辑器中
   ui->textEdit->setPlainText(in.readAll());      QApplication::restoreOverrideCursor();

   // 设置当前文件
   curFile = QFileInfo(fileName).canonicalFilePath();
   setWindowTitle(curFile);
   return true;
}     
这里的操作和saveFile()函数是相似的。下面到设计模式,分别进入其他几个动作的触发信号的槽,更改如下:
// 打开动作
void MainWindow::on_action_Open_triggered()
{
   if (maybeSave()) {
      
       QString fileName = QFileDialog::getOpenFileName(this);
      
       // 如果文件名不为空,则加载文件
       if (!fileName.isEmpty()) {
            loadFile(fileName);
            ui->textEdit->setVisible(true);
       }
   }
}
// 关闭动作
void MainWindow::on_action_Close_triggered()
{
   if (maybeSave()) {
       ui->textEdit->setVisible(false);
   }
}
// 退出动作
void MainWindow::on_action_Exit_triggered()
{
   // 先执行关闭操作,再退出程序
   // qApp是指向应用程序的全局指针
   on_action_Close_triggered();
   qApp->quit();
}
// 撤销动作
void MainWindow::on_action_Undo_triggered()
{
   ui->textEdit->undo();
}
// 剪切动作
void MainWindow::on_action_Cut_triggered()
{
   ui->textEdit->cut();
}
// 复制动作
void MainWindow::on_action_Copy_triggered()
{
   ui->textEdit->copy();
}
// 粘贴动作
void MainWindow::on_action_Paste_triggered()
{
   ui->textEdit->paste();
}
       这里可以看到,复制、粘贴等常用功能是QTextEdit已经实现的,我们只需要调用相应的函数。虽然实现了退出功能,但是,有时候会使用窗口标题栏的关闭按钮来关闭程序,这里我们需要使用关闭事件处理函数来实现相应的功能。
       下面到mainwindow.h文件中,先添加头文件包含#include <QCloseEvent>,然后添加函数声明:
protected:
    void closeEvent(QCloseEvent *event); // 关闭事件

然后到mainwindow.cpp文件中添加该函数的定义:
void MainWindow::closeEvent(QCloseEvent *event)
{
   // 如果maybeSave()函数返回true,则关闭程序
   if (maybeSave()) {
       event->accept();
   } else {   // 否则忽略该事件
       event->ignore();
   }
}
       关于事件的概念,会在后面的教程中讲解。


结语

      这一篇中实现了最基本的编辑功能,现在还剩下查找和帮助菜单没有实现,这个会在下一篇进行介绍。如果大家想学习一个更完整的文本编辑器的实现,可以参考QtQtQuick开发实战精解》一书的第一章。




涉及到的源码下载:



上一篇:第5篇 Qt布局管理器

下一篇:第7篇 实现Qt文本查找功能

返回:系列教程目录  




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
参与人数 9人气 +16 收起 理由
onereal + 2
buling + 1 必须支持!
qt萌新 + 2 对我帮助很大!
alex.hegang + 2 对我帮助很大!
一代菜鸟 + 2 对我帮助很大!
yu19970928 + 2 对我帮助很大!
凡夫俗子lb + 1 很实用!
苍穹 + 2 对我帮助很大!
仰望bym + 2 很详细!

查看全部评分总评分 : 人气 +16

回复

使用道具 举报

累计签到:1 天
连续签到:1 天
2013-4-24 23:54:27 显示全部楼层
我在设置窗口标题时,在代码里使用setWindowTitle()没效果,必须先在ui文件里将窗口标题清掉才有效,印象中好像没有必要这么做的,不知道是什么原因?
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-4-25 22:08:39 显示全部楼层
夏叫兽 发表于 2013-4-24 23:54
我在设置窗口标题时,在代码里使用setWindowTitle()没效果,必须先在ui文件里将窗口标题清掉才有效,印象中 ...

不应该啊,代码可以的,看是不是函数调用错误,或者使用了中文字符串,而没有设置字符集
回复 支持 反对

使用道具 举报

累计签到:84 天
连续签到:1 天
2013-6-1 14:22:22 显示全部楼层
OK,没问题。就是,ui->textEdit........这些没有提示。包括 textEdit 都提示不了,不知道为什么。智能提示那个功能。
回复 支持 反对

使用道具 举报

累计签到:84 天
连续签到:1 天
2013-6-1 17:22:22 显示全部楼层
caikeyter 发表于 2013-6-1 14:22
OK,没问题。就是,ui->textEdit........这些没有提示。包括 textEdit 都提示不了,不知道为什么。智能提示 ...

我把QT CREATER 关了,重新打开,就可以提示了,不知道这个是什么情况。
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-6-2 22:24:00 显示全部楼层
caikeyter 发表于 2013-6-1 17:22
我把QT CREATER 关了,重新打开,就可以提示了,不知道这个是什么情况。

嗯,可能是哪里关联不好吧,Qt Creator有时候会这样的。
回复 支持 反对

使用道具 举报

尚未签到

2013-6-10 13:32:17 显示全部楼层
您好我想问一下这些png图标去哪下载
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-6-10 22:10:27 显示全部楼层
进碗里没 发表于 2013-6-10 13:32
您好我想问一下这些png图标去哪下载

www.yafeilinux.com有,或者下载这里的源码,里面也有的
回复 支持 反对

使用道具 举报

尚未签到

2013-6-20 11:22:57 显示全部楼层
box.clickedButton() == yesBtn.此处出现了“无法从QPushButton转换为QAbstractButton”问题,
请问box.clickedButton()返回类型是QAbstractButton*,而yesBtn是QPushButton*类型,应该如何解决?
回复 支持 反对

使用道具 举报

尚未签到

2013-6-20 16:48:53 显示全部楼层
double_fish 发表于 2013-6-20 11:22
box.clickedButton() == yesBtn.此处出现了“无法从QPushButton转换为QAbstractButton”问题,
请问box.cli ...

问题已解决,不过还是疑惑两种不同类的指针如何相等?

点评

教程下下载的文件,运行调试时出现这种错误,请指教是啥问题??  发表于 2016-10-11 00:09
Cannot find file: d:\sofe\原程序代码\myMainWindow\myMainWindow\myMainWindow.pro. 00:06:34: 进程"d:\qt\4.8.1\bin\qmake.exe"退出,退出代码 2 。 构建项目myMainWindow 时发生错误...   发表于 2016-10-11 00:08
00:06:34: 为项目myMainWindow执行构建步骤 ... 00:06:34: 正在启动 "d:\qt\4.8.1\bin\qmake.exe" D:\sofe\原程序代码\myMainWindow\myMainWindow\myMainWindow.pro -r -spec win32-g++ "CONFI...   发表于 2016-10-11 00:07
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-6-23 17:10:44 显示全部楼层
double_fish 发表于 2013-6-20 16:48
问题已解决,不过还是疑惑两种不同类的指针如何相等?

因为QPushButton是QAbstractButton的子类!
回复 支持 反对

使用道具 举报

尚未签到

2013-6-27 22:40:28 显示全部楼层
相当顺利 依旧是Qt的感觉 本就认识Qt那么久,那么喜欢,为什么以前从没想过把Qt掌握吃透呢 加油
回复 支持 反对

使用道具 举报

累计签到:29 天
连续签到:1 天
2013-7-23 20:40:16 显示全部楼层
能使用ui->textEdit->setWindowTitle(tr("未命名.txt"));  是可以的。
但是编辑框好像貌似不可以输入啊。
回复 支持 反对

使用道具 举报

累计签到:29 天
连续签到:1 天
2013-7-23 20:41:08 显示全部楼层
夏叫兽 发表于 2013-4-24 23:54
我在设置窗口标题时,在代码里使用setWindowTitle()没效果,必须先在ui文件里将窗口标题清掉才有效,印象中 ...


能使用ui->textEdit->setWindowTitle(tr("未命名.txt"));  是可以的。
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2013-8-10 08:51:45 显示全部楼层
很顺利,按着教程完成了,以后继续加深了解!
回复 支持 反对

使用道具 举报

尚未签到

2013-8-13 20:15:57 显示全部楼层
楼主您好 我用QTextCodec::setCodecForTr(QTextCodec::codecForLocale())的时候还是乱码,然后我改成了QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));但是显示的时候“未命名.txt”的点号和它前一个汉字变成了乱码,如果把点号改成中文标点就没有乱码
我使用的是qt4.8.4+creator2.6.1 编译器是msvc(用mingw构建套件会显示一个感叹号 信息是mingw无法编译 我试了好多版本)
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-8-14 17:13:05 显示全部楼层
轩辕专属 发表于 2013-8-13 20:15
楼主您好 我用QTextCodec::setCodecForTr(QTextCodec::codecForLocale())的时候还是乱码,然后我改成了QTex ...

嗯,现在是需要使用utf-8;

不过mingw版本的Qt应该可以的啊,可以看一下前面的详细安装教程,这个还没有发现不能用的情况。

有问题可以截图看一下。
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2013-8-22 21:14:45 显示全部楼层
有个bug,saveFile中,需要将设置ui->textEdit->document()->setModified(false);
不然,保存完后,maybeSave()返回的状态是错误的,导致无法直接打开其它文档。
回复 支持 反对

使用道具 举报

累计签到:13 天
连续签到:1 天
2013-9-11 00:12:27 显示全部楼层
// 获得文件的标准路径
   curFile = QFileInfo(fileName).canonicalFilePath();
这句对吗?
我在Qtcreator里编译不过啊。说明没有这个函数、
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-9-11 11:32:53 显示全部楼层
QString QFileInfo::canonicalFilePath () const
Returns the canonical path including the file name, i.e. an absolute path without symbolic links or redundant "." or ".." elements.

If the file does not exist, canonicalFilePath() returns an empty string.


有的。帮助里面查看canonicalFilePath关键字
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了