时间是人类生活的空间,我们在时域上展开生活,构建自己的世界
零|说明
本文面向初学者介绍Qt5开发,不求全面但求过程完整每个步骤只说明【一种】操作方式代码和操作并重在给出代码的同时介绍开发环境的使用力图使读者能较容易地【复现】任务快速熟悉从【开发意图】到【打包发布】的一整套开发流程
一|任务导向
本文向Qt初学者介绍开发一款自定义的Notepad,也就是仿照Windows自带的记事本小应用的流程。
主界面如下:
界面说明
1:自定义的窗口图标,运行时在任务栏中显示的图标与之一致;
2:自定义窗口标题;
3:自定义工具栏,使用了自选的矢量图标;
4:将多个txt文档嵌入到同一个窗口中。
对初学Qt的朋友而言,首先要做的是熟悉Qt的开发环境,为了让读者把精力集中到开发技术的学习上,本文只介绍Qt Creator这种官方提供的集成开发环境(IDE)进行开发的过程。
二|认识Qt Creator开发环境
初见
不了解Qt Creator的朋友可以自行检索或到Qt官网了解。这里只需要知道,我们将使用这个工具完成接下来的开发就行了。
Qt Creator的下载和安装
1、官方通道下载速度比较着急,最好使用国内镜像源。找不到镜像源的额朋友可以参考我这篇文章:/qq_33904382/article/details/108632625
通过这篇文章介绍的告诉下载链接,可以很快下载完成,节省不必要的等待时间。
2、安装全程跟随安装引导点点点即可,略感困难的朋友可以参考我这篇文章:/qq_33904382/article/details/108643419中的图例,对照着完成Qt的安装。
新建工程
在Qt Creator中建立工程文件和在其他开发环境中类似,这里可以参考这篇文章:/qq_33904382/article/details/108743493建立一个名为Notepad
的工程。
注意我们在Kits
这个步骤和链接文章中不同,我们在这里选择的编译工具是MinGW 32bit
,这和编译以及打包发布有较大关联。为了避免不必要的麻烦,建议初学的朋友和本文保持一致。
查看工程内容
建立工程后,会自动打开,Qt Creator切换到Edit界面:
上图显示了工程中自动生成了一些文件,单击左下角的绿色三角形图标,这个工程将进行编译、链接(编译链接的进度将被实时显示在右下角的方条中),然后运行。在屏幕上出现一个空白的窗口:
我们将对工程中的这些文件进行编辑,从而把窗口内容打磨成我们想要的样子。
所以,首先要认识一下工程中的这些文件:
一个工程文件:Notepad.pro
这个文件和工程为一些配置有关,在本文的开发过程中不会用到。我们将在打包发布的阶段对它进行必要的编辑。
一个主文件:main.cpp
这个文件和进行纯c/c++的代码编写时有所不同。为了削弱朋友们的陌生感,本文将逐行地介绍一下这个文件。但仅供了解,不必深究。
主c文件内容:
#include "notepad.h"#include <QApplication>int main(int argc, char *argv[]){QApplication EditorApp(argc, argv);Notepad Editor;Editor.show();return EditorApp.exec();}
逐行解析:
最开始的两行用于引用notepad.h
和QApplication
这两个头文件。QApplication
是Qt库的一个头文件,所有的Qt类都有对应的头文件。
#include "notepad.h"#include <QApplication>
这行是定义了主函数。和纯c/c++开发一样,使用Qt开发环境编写的整个程序也是从这个函数开始执行。
int main(int argc, char *argv[])
这一行使用主函数的参数,创建了一个EditorApp对象。这是Qt程序的主函数中的固定操作,不必细究。
QApplication EditorApp(argc, argv);
下面这行声明了一个在mainwindow.h
和mainwindow.cpp
中定义的那个Notepad
类的对象Editor
,我们编辑的主要对象。
Notepad Editor;
下面这行代码让我们编写的这个窗口显示在屏幕上。
Editor.show();
最后一句代码让程序进入Qt的事件循环,也就是窗口一直运行着,等待我们的各种操作,并根据我们在代码中的设定做出响应。
return EditorApp.exec();
逐行解析结束
一个后缀为ui的文件:mainwindow.ui
除了使用代码编辑,我们还可以通过这个文件对窗口的界面进行设计和改动。
而且借助开发环境中集成的Qt Designer
,我们可以使用鼠标进行可视化编辑。双击这个ui
文件即可打开Qt Designer
界面,具体在第四部分熟悉Qt designer和窗体UI编辑方法
中介绍。
三|了解Qt的混合开发模式
有的初学者认为应该使用纯代码编辑窗体,诚然,这是可以的,但确实不推荐。
因为窗体的大小通常可以变化,所以想要保持一定风格的界面,窗体上的按钮等控件的位置也就应该随着窗体的变化。这样就需要许多与界面相关的代码。
所以实际上,我们通常用Qt designer这个工具对窗体的界面进行可视化的编辑,代码部分只用来完成槽函数和对事件的响应。
这也就是所谓的Qt的混合开发模式。
四|熟悉Qt designer和窗体UI编辑方法
鼠标双击工程目录中的mainwindow.ui文件,Qt Creator会自动转到Qt designer,如下图所示:
中间白色背景区域就是窗体显示区域,灰色的方框就是窗体mainwindow,因为我们还没有为它添加控件,此时上面时空白的。
比较一|任务导向中的效果图,我们先准备在顶部给它加上一排工具栏。
1、在窗体上单击右键2、在弹出的选项框中选择add toolbar(添加工具栏),单击左键
可以看到窗体顶部多了窄窄的一栏,右上角的元素框(上图右边那个红色圈中)中也显示出窗体上有一个名为toolbar
的控件。这个名字是可以修改的,我们现在不管这些细节。
当然,这和我们想要的工具栏还是不同,现在我们让它看上去好一点,具体来说有两个步骤:
1、为工具栏添加一些action2、为action设置图标,让它更好看一些
第二个步骤我们将在六|资源文件和自定义图标中说明,这一部分我们先考虑怎么让工具栏有一些像按键的东西,并且让这些“按键”被按下去后发生一些我们设想的事情。
为工具栏添加action
(1)新建action
首先就是新建acition吧,怎么做?
单击Qt designer底部的新建action按钮(上图中圈中的位置),弹出一个窗口:
在Text一栏中填写New,然后单击OK按钮,新建一个名为New的action:
可以看到,action栏中已经显示出这个actionNew
的属性。
(2)把action添加到工具栏中
这步很简单,直接用鼠标把action拖到工具栏中即可。
使用同样的步骤,再为工具栏添加Open、Save、Exit、Paste、Redo、Undo等action。完成后形如下图这样,这个图里的窗口有两个工具栏,你也可以只用一个:
上图白色框是一个textedit控件,添加方法就是在Qt designer左侧的控件列表中找到textedit,拖入窗体范围即可。
到这里我们就把单个窗体的界面做完了,接下来看mainwindow所谓代码部分:
mainwindow.h:
#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>namespace Ui {class Notepadx;}class Notepadx : public QMainWindow{Q_OBJECTpublic:explicit Notepadx(QWidget *parent = nullptr);~Notepadx();private slots:void newDocument();void open();void save();void saveAs();void print();void exit();void copy();void cut();void paste();void undo();void redo();void selectFont();void setFontBold(bool bold);void setFontUnderline(bool underline);void setFontItalic(bool italic);void about();void on_actionNew_triggered();void on_actionOpen_triggered();private:Ui::Notepadx *ui;QString currentFile;};#endif // mainwindow.h
mainwindow.cpp:
#include "notepadx.h"#include "ui_notepadx.h"#include <QFontDialog>#include <QFile>#include <QTextStream>#include <QMessageBox>#include <QFileDialog>Notepadx::Notepadx(QWidget *parent) :QMainWindow(parent),ui(new Ui::Notepadx){ui->setupUi(this);this->setCentralWidget(ui->textEdit);connect(ui->actionNew, &QAction::triggered, this, &Notepadx::newDocument);connect(ui->actionOpen, &QAction::triggered, this, &Notepadx::open);connect(ui->actionSave, &QAction::triggered, this, &Notepadx::save);connect(ui->actionSave_as, &QAction::triggered, this, &Notepadx::saveAs);connect(ui->actionPrint, &QAction::triggered, this, &Notepadx::print);connect(ui->actionExit, &QAction::triggered, this, &Notepadx::exit);connect(ui->actionCopy, &QAction::triggered, this, &Notepadx::copy);connect(ui->actionCut, &QAction::triggered, this, &Notepadx::cut);connect(ui->actionPaste, &QAction::triggered, this, &Notepadx::paste);connect(ui->actionUndo, &QAction::triggered, this, &Notepadx::undo);connect(ui->actionRedo, &QAction::triggered, this, &Notepadx::redo);connect(ui->actionFont, &QAction::triggered, this, &Notepadx::selectFont);connect(ui->actionBold, &QAction::triggered, this, &Notepadx::setFontBold);connect(ui->actionUnderline, &QAction::triggered, this, &Notepadx::setFontUnderline);connect(ui->actionItalic, &QAction::triggered, this, &Notepadx::setFontItalic);connect(ui->actionAbout, &QAction::triggered, this, &Notepadx::about);// Disable menu actions for unavailable features#if !defined(QT_PRINTSUPPORT_LIB) || !QT_CONFIG(printer)ui->actionPrint->setEnabled(false);#endif#if !QT_CONFIG(clipboard)ui->actionCut->setEnabled(false);ui->actionCopy->setEnabled(false);ui->actionPaste->setEnabled(false);#endif}Notepadx::~Notepadx(){delete ui;}void Notepadx::newDocument(){currentFile.clear();ui->textEdit->setText(QString());}void Notepadx::open(){QString fileName = QFileDialog::getOpenFileName(this, "Open the file");QFile file(fileName);currentFile = fileName;if (!file.open(QIODevice::ReadOnly | QFile::Text)) {QMessageBox::warning(this, "Warning", "Cannot open file: " + file.errorString());return;}setWindowTitle(fileName);QTextStream in(&file);QString text = in.readAll();ui->textEdit->setText(text);file.close();}void Notepadx::save(){QString fileName;// If we don't have a filename from before, get one.if (currentFile.isEmpty()) {fileName = QFileDialog::getSaveFileName(this, "Save");currentFile = fileName;} else {fileName = currentFile;}QFile file(fileName);if (!file.open(QIODevice::WriteOnly | QFile::Text)) {QMessageBox::warning(this, "Warning", "Cannot save file: " + file.errorString());return;}setWindowTitle(fileName);QTextStream out(&file);QString text = ui->textEdit->toPlainText();out << text;file.close();}void Notepadx::saveAs(){QString fileName = QFileDialog::getSaveFileName(this, "Save as");QFile file(fileName);if (!file.open(QFile::WriteOnly | QFile::Text)) {QMessageBox::warning(this, "Warning", "Cannot save file: " + file.errorString());return;}currentFile = fileName;setWindowTitle(fileName);QTextStream out(&file);QString text = ui->textEdit->toPlainText();out << text;file.close();}void Notepadx::print(){#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer)QPrinter printDev;#if QT_CONFIG(printdialog)QPrintDialog dialog(&printDev, this);if (dialog.exec() == QDialog::Rejected)return;#endif // QT_CONFIG(printdialog)ui->textEdit->print(&printDev);#endif // QT_CONFIG(printer)}void Notepadx::exit(){QCoreApplication::quit();}void Notepadx::copy(){#if QT_CONFIG(clipboard)ui->textEdit->copy();#endif}void Notepadx::cut(){#if QT_CONFIG(clipboard)ui->textEdit->cut();#endif}void Notepadx::paste(){#if QT_CONFIG(clipboard)ui->textEdit->paste();#endif}void Notepadx::undo(){ui->textEdit->undo();}void Notepadx::redo(){ui->textEdit->redo();}void Notepadx::selectFont(){bool fontSelected;QFont font = QFontDialog::getFont(&fontSelected, this);if (fontSelected)ui->textEdit->setFont(font);}void Notepadx::setFontBold(bool){}void Notepadx::setFontUnderline(bool){}void Notepadx::setFontItalic(bool){}void Notepadx::about(){}
代码说明:
上面的代码说用的是c++风格,并且我们主要关注的是Qt的特性,故而对于那些成员函数(member functions)就不说明了。
这些函数对应着各action,当我们在窗体上单击某个action,对应的函数就会被调用。譬如,我们运行程序,在窗体上的任务栏中New这个action的位置单击鼠标,程序调用Notepadx::newDocument()
这个函数。
读到这里,初学的朋友不禁要问,这些action和函数的关系是怎样确立的?
让我们把目光移到mainwindow.cpp中的这一部分:
connect(ui->actionNew, &QAction::triggered, this, &Notepadx::newDocument);connect(ui->actionOpen, &QAction::triggered, this, &Notepadx::open);connect(ui->actionSave, &QAction::triggered, this, &Notepadx::save);connect(ui->actionSave_as, &QAction::triggered, this, &Notepadx::saveAs);connect(ui->actionPrint, &QAction::triggered, this, &Notepadx::print);connect(ui->actionExit, &QAction::triggered, this, &Notepadx::exit);connect(ui->actionCopy, &QAction::triggered, this, &Notepadx::copy);connect(ui->actionCut, &QAction::triggered, this, &Notepadx::cut);connect(ui->actionPaste, &QAction::triggered, this, &Notepadx::paste);connect(ui->actionUndo, &QAction::triggered, this, &Notepadx::undo);connect(ui->actionRedo, &QAction::triggered, this, &Notepadx::redo);connect(ui->actionFont, &QAction::triggered, this, &Notepadx::selectFont);connect(ui->actionBold, &QAction::triggered, this, &Notepadx::setFontBold);connect(ui->actionUnderline, &QAction::triggered, this, &Notepadx::setFontUnderline);connect(ui->actionItalic, &QAction::triggered, this, &Notepadx::setFontItalic);connect(ui->actionAbout, &QAction::triggered, this, &Notepadx::about);
我们能够看到很多connect函数被调用。这就涉及到Qt的一个特质——信号(signal)和槽函数(slot)了,具体可参考我的这篇文章:
Qt的信号和槽函数/qq_33904382/article/details/108785463
这里只需要知道:
connect(ui->actionNew, &QAction::triggered, this, &Notepadx::newDocument);
这个这条语句的意思是:
把actionNew这个action的triggered("被单击"这个信号)和this(这个类(mainwindow))的函数newDocument()关联起来)
五|多窗体
初学的朋友先跳过这步,我以后再补充。
六|资源文件和自定义图标
我们在开发过程中可能会用到一些图片,或者程序运行过程中需要一些文件的支持,这些文件都可以放到工程目录下,方便打包的时候一并带上。
通常我们把这些文件叫做资源文件,Qt Creator提供了资源文件的管理工具。
要添加一个资源文件,在Qt Creator的工程目录中右键单击工程名,选择Add New
,然后Qt->Qt Resource File->choose…:
然后填写Name栏->Next->Finish:
可以看到工程目录中已经有了src1这个资源文件:
单击add Prefix
新建一个分支。
然后单击Add Files
,把资源文件添加到这个分支下面:
然后保存工程中所有文件(每次修改资源文件都要保存一次资源文件(快捷键ctrl+s)。
这样,我们就可以引用这些资源文件为action设置图标了。
自定义图标
双击ui文件打开Qt designer,双击action编辑栏中的action(随便选一个你想编辑的action),本文选择Cut这个action,双击,然后看到这个界面:
图中标注的2和3是我们接下来要操作的地方:
2:单击下拉箭头,把normal off(从不显示图标)换成normal on(总是显示图标)。
单击3指示的地方,弹出一个列表框:
找到对应的资源文件夹,选择你想要的图标
补充说明一下:这里的图标用png的图片格式即可。缺少矢量图标资源的可以在这个网站逛一逛:/collections/index?spm=a313x.7781069.1998910419.5&type=1
这里我选择了剪刀这个图片当作为Cut这个action的图标:
单击OK,确认:
可以看到工具栏里的actioncut已经显示为图标啦。用同样的方法,可以为其他action也配置上图标。
七|打包工具和自定义exe图标
exe图标
exe图标,两种选择,【不管不顾】/【自己选个奈斯的图】
这个其实很简单,只需要把图标文件xxx.ico
放到工程文件的目录(和主文件所在的那个文件夹)中:
然后再打开工程文件Notepad.pro
,在最后一行添加语句:RC_ICONS = xxx.ico
比如我这里想把一个名为lb256
的图标文件做exe的图标,就像下图这样在pro文件末尾补加一条:RC_ICONS = lb256.ico
需要注意的是,这里必须使用ico格式的文件做图标,如果你想把某张png/jpg格式的图片作为exe的图标,那就要借助一些转换工具。
幸运的是,Windows的应用商店里就有满足我们需求的应用,比如IconMaker
.
打包
1.前提
强调:使用Qt自带的打包工具!选择与编译工具对应的打包工具!
否则在打包时大概率会遇到如下问题:
a.使用cmd进行打包,遇到报错:Warning: Cannot find Visual Studio installation directory, VCINSTALLDIR is not set
.需要配置环境变量,但配置后也可能不行哈哈哈哈哈。
b.转用vs的开发者工具打包,遇到报错Warning: Cannot find GCC installation directory. g++.exe must be in the path.
这个可能是因为使用了x86工具打包64位exe。
c.好不容易打包完成了,一运行就报错:
丢失libstdc+±6.dll、libgcc_s_dw2-1.dll、libwinpthread-1.dll的系统错误
。
或者:应用程序无法正常启动(0xc000007b)
我为啥知道捏,因为我都遇到过哈哈哈哈
总而言之,使用这些方法打包不是遇到资源引用不畅,就是打包包含的dll不全。综上,建议各位读者朋友使用Qt自带的命令工具进行打包。
2.具体操作步骤
(1)准备工作
按release方式编译整个工程,把release目录下的可执行文件拷贝到一个空文件夹里面(这个文件夹用来为这个exe存放配套的动态库,以便exe能在其他机器上运行,也就是我们所说的打包里面的那个“包”)。
(2)选择打包工具
直接在全部程序/任务栏搜索框中搜索Qt,可以看到一系列的命令工具。
这里我们根据工程设置的编译工具,选择Qt5.9.9(MinGW 5.3.0 32-bit)
这个命令工具。
(3)开始打包
运行Qt5.9.9 MinGW 5.3.0 32-bit.exe
,切换到上一步把Notepad.exe放到的那个文件夹,命令形式是
cd 文件夹绝对路径
然后使用打包命令:
windepoyqt Notepad.exe
这条命令会把Notepad.exe运行时所需要的所有动态库拷贝到这个目录下,也就充实了我们的这个包。
上面两条命令及其效果都显示在下图中,供朋友们参考:
ok,打包完成了,把这个文件夹看成一个整体,拷贝到不同的机器上,我们的Notepad.exe都能运行,这就达到了打包的目的。
结语
至此,整个介绍就完结啦。
总的说来,初学Qt不必过分关注各种控件的操作方法,界面布局也是大致ok就行。重要的是这个工具库的特性,以及使用这个工具进行开发的并发布软件的流程。
本文以一个自定义Notepad开发为载体,介绍了使用Qt Creator这种IDE进行开发的流程。主要内容包含以下几方面:
1、Qt库几个特殊元素——信号、槽、事件及他们的设计和使用。
2、单窗体和多窗体设计
3、ui界面可视化编辑和窗体类代码编写的混合开发模式。
4、资源文件的添加和使用
6、在工程文件中编辑exe图标
7、使用Qt自带命令工具进行打包发布
祝各位后续的工作和学习顺利进行,对本文有意见和建议都可以在评论区提出。恭候各位的留言。
【Qt5】入门Qt开发教程 一篇文章就够了(Creator 混合开发 多窗体 资源文件 打包发布 exe图标)