C++集成Qt实现简易记事本-qnotepad

1. 前言

最近学习了C++语法,心血来潮就想着搞一个项目出来,C++是之前学的,Qt才刚出炉,于是就整合了Qt框架编写了一个WIndows版的简易记事本,现在是个很小的文本编辑器,还有很大的扩展空间和改进。

本项目用到的语法不是很多但也比较全面了,比如Qt的信号与槽机制,ui界面布局等等。

实现的记事本项目运行后效果是这样的:

作者希望大家作为学习Qt框架的一个参考,共同努力,共同进步,不足之处,评论区请留言。

2. 部署Qt开发环境

2.1 下载Qt安装包

首先进入Qt官方网站:http://download.qt.io/archive/qt/,点击DownLoad下载Windows版本的Qt最新安装包,如果想下载以前的版本,也可以选择历史版本,本项目使用的是Qt5.12.10版本。

等待下载完成后,双击exe文件进入Qt安装程序,如果连网的话默认是在线安装,需要输入基本信息,如果断网的话可以进行离线安装,安装步骤请自行搜索,本文不予详解。

2.2 Qt Creator介绍

Qt Creator是一款跨平台的、官方推荐的集成开发环境(IDE),专为Qt开发者设计。从官网下载的Qt安装包中就包含了Qt Creator,不用再单独下载。

Qt Creator可用于开发Qt桌面应用程序和Qt控制台程序,还集成了跨平台构建工具:qmakeCMake

Qt Creator还自带了一个界面设计器:Qt Designer,用于设计和构建图形用户界面(GUI)。

2.3 创建qmake项目

Qt框架和Qt Creator安装好了后,打开Qt Creator就可以进行创建项目了。

首先,选择顶部导航栏中的文件–新建文件或项目,进入如下界面:

左侧选择Application(Qt)模板,右侧选择Qt Widgets Application创建界面程序,输入项目的基本信息,名称自己定义,创建路径选择本地的存放目录::

接下来是选择构建工具为qmake,当然也可以选择CMake:

这里是创建基本窗口类,这里有三种基本窗口类:QMainWindow、QWidget、QDialog,我们这里是选择QMainWindow类,再自定义一个类来继承QMainWindow类:

这里的设置用于国际化信息,选择中文模板就可以了:

接下来是选择编译套件,建议MinGW和MSVC两个都选择上:

最后一步是选择版本控制系统为Git,用于将代码同步到Github。

3. 开始设计界面

3.1 主界面设计

主窗口界面设计在Forms/mainwindow.ui文件中,左边一栏是界面组件,中间的设计效果,右侧是具体的设计对象和类,使用时拖动左边的组件到中间视图中就可以看到效果了。

本记事本项目的文本输入部分使用了QPlainTextEdit类而没有使用QTextEdit,是因为QPlainTextEdit类更高效、性能更优,可以理解为它是简化版的QTextEdit,但QPlainTextEdit的功能比QTextEdit少,不过对于一个简单的记事本来说已经足够了。

3.2 查找与替换界面

查找和替换界面使用Forms/findAndReplaceDialog.ui文件来表示:

4. 实现记事本功能

4.1 文件操作功能

文件操作功能主要包括新建、打开、保存、另存为、打印、退出功能:

/**
 * @brief MainWindow::openFileAction 打开文件槽
 */
void MainWindow::openFileAction(){

    if(!isSaved()) return;
    QString recentPath = settings.value("recent/filePath").toString();
   QString filePath = QFileDialog::getOpenFileName(this,QString::fromLocal8Bit("打开"),recentPath,QString::fromLocal8Bit("文本文档(*.txt);;所有文件(*.*)"));
   openFile(filePath);
}
/**打开文件API*/
void MainWindow::openFile(QString filePath){

    filePath_ = filePath;

    if(filePath.isEmpty()){
        savedFileData = "";//新建文件数据为空
        fileName_ = QString::fromLocal8Bit("无标题");
    }else{
        QFile file(filePath);
        if(!file.exists()) return;

        fileName_ = QFileInfo(filePath).baseName();

        if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){
            QMessageBox::critical(this, "错误", "打开文件失败");
            return;
        }
        savedFileData = QString::fromLocal8Bit(file.readAll());//获取打开的文件数据
        file.close();
    }
    ui->plainTextEdit->setPlainText(savedFileData);
    resetWindowTitle();//重置窗口标题
}
/**
 * @brief MainWindow::newFileAction 新建
 */
void MainWindow::newFileAction(){

    if(!isSaved()) return;
    openFile("");
}
/**
 * @brief MainWindow::saveFileAction 保存文件槽
 */
bool MainWindow::saveFileAction(){

    if(ui->plainTextEdit->toPlainText().isEmpty())
        return false;

    //如果路径名为空,就另存为
    if(filePath_.isEmpty()){
        QString recentPath = settings.value("recent/filePath").toString();//返回最近打开的路径
        QString filePath = QFileDialog::getSaveFileName(this,QString::fromLocal8Bit("另存为"),recentPath,QString::fromLocal8Bit("文本文档(*.txt);;所有文件(*.*)"));
        settings.setValue("recent/filePath", filePath);
        filePath_ = filePath;
        fileName_ = QFileInfo(filePath).baseName();
    }

    QFile file(filePath_);
    if(!file.open(QIODevice::WriteOnly)) return false;

    QTextStream stream(&file);
    stream.setCodec("GBK");
    savedFileData = ui->plainTextEdit->toPlainText();//保存的文件数据
    stream << savedFileData;
    stream.flush();
    file.close();

    resetWindowTitle();
    return true;
}
/**
 * @brief MainWindow::saveAsAction 另存为
 */
void MainWindow::saveAsAction(){

    QString temp = filePath_;
    filePath_ = "";
    if (!saveFileAction()) // 直接调用保存的
         filePath_ = temp;
}
/**
 * @brief MainWindow::exitAction 退出
 */
void MainWindow::exitAction(){

    if(!isSaved()) return;
    this->close();
}

4.2 文本编辑功能

文本编辑功能主要包含撤销、剪切、复制、粘贴、删除等操作。

/**
 * @brief MainWindow::undoAction 撤销
 */
void MainWindow::undoAble(bool flag){

    ui->actionUndo->setEnabled(flag);
}
/**
 * @brief MainWindow::selectedUpdate 选择的内容更新
 */
void MainWindow::selectedUpdate(){

    bool selected = ui->plainTextEdit->textCursor().hasSelection();
    ui->actionCut->setEnabled(selected);
    ui->actionCopy->setEnabled(selected);
    ui->actionDelete->setEnabled(selected);
}
/**
 * @brief MainWindow::undoAction 撤销
 */
void MainWindow::undoAction(){
    ui->plainTextEdit->undo();
}
/**
 * @brief MainWindow::cutAction 剪切
 */
void MainWindow::cutAction(){
    ui->plainTextEdit->cut();
}
/**
 * @brief MainWindow::copyAction 复制
 */
void MainWindow::copyAction(){
    ui->plainTextEdit->copy();
}
/**
 * @brief MainWindow::pasteAction 粘贴
 */
void MainWindow::pasteAction(){
    ui->plainTextEdit->paste();
}
/**
 * @brief MainWindow::deleteAction 删除
 */
void MainWindow::deleteAction(){
    QTextCursor textCursor = ui->plainTextEdit->textCursor();
        int pos = textCursor.position();
        if (pos >= ui->plainTextEdit->toPlainText().length())
            return ;
        textCursor.setPosition(pos + 1, QTextCursor::MoveMode::KeepAnchor);
        textCursor.removeSelectedText();
}

4.3 查找与替换功能

查找与替换文本有单独的界面,即查找与替换对话框,用于查找想要搜索的文本或替换:

/**
 * @brief FindAndReplaceDialog::openFind 打开查找对话框
 * @param isReplace
 */
void FindAndReplaceDialog::openFindOrReplaceDialog(bool isReplace){

    //控制选项的显示,如果是查找则不显示下列该项
    ui->label_2->setVisible(isReplace);
    ui->replaceEdit->setVisible(isReplace);
    ui->replaceBtn->setVisible(isReplace);
    ui->replaceAllBtn->setVisible(isReplace);
    ui->groupBox->setVisible(!isReplace);
    QDialog::show();
    ui->findEdit->setFocus();
    ui->findEdit->selectAll();
    this->adjustSize();

    if(!isReplace)
        setWindowTitle("查找");
    else
        setWindowTitle("替换");
}
/**
 * @brief MainWindow::newFindDialog 新建查找对话框
 */
void MainWindow::newFindDialog(){

    findAndReplaceDialog  =  new FindAndReplaceDialog(settings,this);

    connect(findAndReplaceDialog, &FindAndReplaceDialog::signalShow, this, [=]{
            ui->actionFindNext->setEnabled(true);
            ui->actionFindPrev->setEnabled(true);
        });
    connect(findAndReplaceDialog, &FindAndReplaceDialog::signalClose, this, [=]{
            ui->actionFindNext->setEnabled(false);
            ui->actionFindPrev->setEnabled(false);
        });
    connect(findAndReplaceDialog, &FindAndReplaceDialog::signalFindNext, this, &MainWindow::findNextAction);
    connect(findAndReplaceDialog, &FindAndReplaceDialog::signalFindPrev, this, &MainWindow::findPrevAction);
    connect(findAndReplaceDialog, &FindAndReplaceDialog::signalReplceNext, this, [=]{
            const QString& findText = findAndReplaceDialog->getFindTextData();
            const QString& replaceText = findAndReplaceDialog->getReplaceTextData();
            if (findText.isEmpty()) return ;

    //设置选中的文本
    const QString& selectedText = ui->plainTextEdit->textCursor().selectedText();

    if((findAndReplaceDialog->isCaseSensitive() && selectedText != findText)  || selectedText.toLower() != findText.toLower()){
        //如果选中的词不是findText,查找下一个
        findNextAction();
    }else {
        //已选中则替换选中的
        QTextCursor tc = ui->plainTextEdit->textCursor();
        tc.insertText(replaceText);
        ui->plainTextEdit->setTextCursor(tc);

        findNextAction();
    }
});
    connect(findAndReplaceDialog, &FindAndReplaceDialog::signalReplaceAll, this, [=]{
            const QString& findText = findAndReplaceDialog->getFindTextData();
            const QString& replaceText = findAndReplaceDialog->getReplaceTextData();
            if (findText.isEmpty()) return;

            QString content = ui->plainTextEdit->toPlainText();
            QTextCursor textCursor = ui->plainTextEdit->textCursor();
            textCursor.setPosition(0);
            textCursor.setPosition(content.length(), QTextCursor::KeepAnchor);
            content.replace(findText, replaceText);
            textCursor.insertText(content);
        });
}
/**
 * @brief MainWindow::findNextAction 查找下一个槽
 */
void MainWindow::findNextAction(){

    const QString& text = findAndReplaceDialog->getFindTextData();
    if(text.isEmpty()) return;

    QTextDocument::FindFlags findFlags;

    if(findAndReplaceDialog->isCaseSensitive()) findFlags |= QTextDocument::FindCaseSensitively;

    bool result = ui->plainTextEdit->find(text,findFlags);


    if(!result && findAndReplaceDialog->isLoop() && ui->plainTextEdit->toPlainText().contains(text)){
        QTextCursor tc = ui->plainTextEdit->textCursor();//从头开始查找
        tc.setPosition(0);
        ui->plainTextEdit->setTextCursor(tc);
        findNextAction();
    }
}
/**
 * @brief MainWindow::findPrevAction 查找上一个
 */
void MainWindow::findPrevAction(){

    const QString& text = findAndReplaceDialog->getFindTextData();
    if(text.isEmpty()) return;

    QTextDocument::FindFlags findFlags = QTextDocument::FindBackward;

    if(findAndReplaceDialog->isCaseSensitive()) findFlags |= QTextDocument::FindCaseSensitively;

    bool result = ui->plainTextEdit->find(text,findFlags);

    if(!result && findAndReplaceDialog->isLoop() && ui->plainTextEdit->toPlainText().contains(text)){
        QTextCursor tc = ui->plainTextEdit->textCursor();//从末尾开始查找
        tc.setPosition(ui->plainTextEdit->toPlainText().length());//设置成末尾位置
        ui->plainTextEdit->setTextCursor(tc);
        findPrevAction();
    }
}
/**
 * @brief MainWindow::findAction 查找
 */
void MainWindow::findAction(){

    if(!findAndReplaceDialog) newFindDialog();

    findAndReplaceDialog->openFindOrReplaceDialog(false);
}

/**
 * @brief MainWindow::findAction 替换
 */
void MainWindow::replaceAction(){

    if(!findAndReplaceDialog) newFindDialog();

    findAndReplaceDialog->openFindOrReplaceDialog(true);
}

除了基本的查找与替换外,还可以选择区分大小写、是否循环等条件

/**
 * @brief FindAndReplaceDialog::caseSensitiveSlots 区分大小写
 */
void FindAndReplaceDialog::caseSensitiveSlots(){
    settings.setValue("find/caseSensitive", ui->caseSensitiveCheck->isChecked());
}
/**
 * @brief FindAndReplaceDialog::loopSlots 是否循环
 */
void FindAndReplaceDialog::loopSlots(){
    settings.setValue("find/loop", ui->loopCheck->isChecked());
}
/**
 * @brief FindAndReplaceDialog::upSlots 向上
 */
void FindAndReplaceDialog::upSlots(){
    settings.setValue("find/down", false);
}
/**
 * @brief FindAndReplaceDialog::downSlots 向下
 */
void FindAndReplaceDialog::downSlots(){

    settings.setValue("find/down", true);
}

4.4 字体设置功能

Qt框架中有专门的字体设置类,导入QFontDialog类就可以实现该功能了。

/**
 * @brief MainWindow::fontAction 字体格式
 */
void MainWindow::fontAction(){
    bool flag;
    QFont font = QFontDialog::getFont(&flag,ui->plainTextEdit->font(),this,QString::fromLocal8Bit("字体"));

    if(!flag) return;
    ui->plainTextEdit->setFont(font);
    settings.setValue("font",font.toString());
}

4.5 打印功能

Qt框架中提供了QPrintDialog类和QPrinter类用于实现打印功能。

/**
 * @brief MainWindow::printAction 打印,编译报错VS中缺少相应的库文件,在pro中添加QT+= printsupport选项即可
 */
void MainWindow::printAction(){

    QPrinter printer;
    QString printer_name = printer.printerName();

    if(printer_name.size() == 0) return;

    QPrintDialog printDialog(&printer,this);

    if(ui->plainTextEdit->textCursor().hasSelection()){
        printDialog.addEnabledOption(QAbstractPrintDialog::PrintSelection);
    }
    if(printDialog.exec() == QDialog::Accepted){
        ui->plainTextEdit->print(&printer);
    }
}

4.6 关于-帮助

打开帮助即可显示帮助文档和版本、作者等信息。

/**
 * @brief MainWindow::helpAction 帮助
 */
void MainWindow::helpAction(){
    QDesktopServices::openUrl(QUrl("https://www.peiqiblog.com/article/4800/"));
}
/**
 * @brief MainWindow::aboutAction 关于
 */

void MainWindow::aboutAction(){
    QMessageBox::about(this, QString::fromLocal8Bit("关于"), tr("qnotepad\n" "version1.0.0\n""simple notepad"));
}

5. 常见问题解决

5.1 编码问题

默认情况下,创建了新的Qt项目后,如果使用中文则界面中会显示乱码,这种情况下可以使用QString类的fromLocal8Bit方法实现编码配置:

/**
 * @brief MainWindow::aboutAction 关于
 */

void MainWindow::aboutAction(){
    QMessageBox::about(this, QString::fromLocal8Bit("关于"), tr("qnotepad\n" "version1.0.0\n""simple notepad"));
}

5.2 打印窗口不显示

调用QPrintDialog类实现打印窗口时不显示,只需要在pro配置文件中加入QT+= printsupport即可。

6. 测试运行

编写完代码后,右击项目运行就可以运行本记事本了。

主窗口:

查找窗口:

替换窗口:

打印窗口:

字体窗口:

关于窗口:

C++结合Qt框架实现的简易记事本已开源至Github

记事本项目代码链接:https://github.com/peiqi0818/qnotepad

若有不足之处,请评论区的朋友们指正。

觉得有帮助可以赞赏本文哦~万分感谢!
文章:C++集成Qt实现简易记事本-qnotepad
作者:沛旗
链接:https://www.peiqiblog.com/article/4800/
版权声明::本博客站点所有文章除特别声明外,均采用 CC BY-NC-SA 4.0协议
转载请注明文章地址及作者哦~
暂无评论

发送评论(禁止发表一切违反法律法规的敏感言论) 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇