时间过的ZTMK
,距离上一篇文章已经小半年过去了。为了安家、装修和结婚,搞得本身焦头烂额,这不是也正好遇上过年,一直没有时间写篇文章,最近终于慢慢回归正轨,因此决定写下这一篇文章,记录工做中的一些经验和内容。对于写文章这件事,我是这么认为的:一个是回顾本身的工做内容;另外一方面也是为了能让有一样需求的同窗用于借鉴。同时这也是我对本身的一个要求,每一个阶段都应该有所输出,并有所记录,假若若干年后,有机会再次看到这些东西的时候,能有一丝感动。。。函数
废话很少说了,那咱们直接进入今天要分享的内容,怎么去自动生成Qt的信号声明工具
用过Qt的人应该都知道,Qt中的每一个控件都有不少信号,基于这些信号,咱们能够实现咱们本身的响应函数,也就是槽函数,一般槽函数和信号是经过connect链接起来的。除此以外呢,还有一种书写槽的方式,咱们能够不用connect来链接,那就是咱们的槽函数名称须要知足必定的规律,好比咱们要实现一个名称为pushButton_ok的按钮点击事件,那么咱们的槽函数声明可能会像下面这样:ui
private slots: void on_pushButton_ok_clicked();
用过QtCreator
写代码的人可能都知道,上述的代码能够经过QtCreator
内嵌的界面设计工具直接生成,可是当咱们直接用QtDesigner
工具编辑时,没有了转到槽这个菜单,以下图左侧的截图所示
.net
上图中的右侧截图是咱们修改事后的截图,当咱们直接编辑UI文件时,也能够转到槽,不一样的是咱们须要本身去相应的.h和.cpp文件中去把声明和实现添加上,本篇文章咱们先分析怎么添加函数声明,下篇文章在分析怎么添加函数实现定义,函数声明和定义是怎么构造的,这里不会讲解,Qt源码中都有,有兴趣的同窗能够本身去了解下。设计
既然咱们的函数声明已经有了,咱们只须要打开ui文件对应的头文件,而后把代码插入到合适的位置上便可,这里有2种方式实现。code
以上两种方式,各有利弊,第一种方式简单粗暴,比较容易实现功能,但扩展性差,好比说要插入到指定域的全部函数以后,就比较难;第二种方式实现起来比较复杂,解析头文件是一个比较大的活,可是一旦文件解析成功后,插入工做就变得很简单。这里我选择了第二种方式来实现这个功能。blog
首先,解析头文件,我画了一个大体的流程图,主要是为了理解起来方便,并非特别专业,凑合着看下
头文件解析时,主要的规则仍是按行读取代码,而后去检测是否知足某一个类型条件,好比说已\\
开头的咱们认为是注释。索引
当知足条件时,咱们去更新相应的内存结构,而后继续往下读,有时候咱们可能须要连续读取好几行才能知道当前的内容是什么,事件
class A ;
如上述代码所示,是一个不标准的C++类预声明,咱们只有读到;
时,才知道这是一个类预声明,而不是一个类声明,有点儿绕口,可是这个很重要。
图中对于解析一个类模块,只是简单的用了一个块来表示,实际上解析一个类也是比较费劲的。当咱们解析完类文件以后,就是简单的插入操做了,插入流程以下图所示
类图中总共有3个模块:对外暴露的QtGrammaAnalysis
类,提供给用户操做;QtFileCache
是文件缓存类,供QtGrammaAnalysis
类调用,文件缓存类能够有多个,主要是为了分析不一样类型的文件;QtHeaderDescription
是真正的文件描述类,全部的实际操做都是经过这个类来进行的。
哈哈哈,好的代码自带注释,下面的结构体是为了咱们解析头文件而声明的,每一个重要的字段都有注释,这里不在作解释
struct OffsetItem { int start;//偏移起始行 int number;//偏移大小 }; struct BaseItem { BaseItem() :start(0), end(0) {} QString name;//项目名称 列如注释内容、块名称等 int start;//标记代码所在起始行 int end;//标记代码所在结束行 BaseItem & BaseItem::operator += (const OffsetItem &); }; struct ScopePiece : public BaseItem { ScopePiece() :g_end(-1) {} int g_end;//做用域下全部代码结束 QList<BaseItem> funcations;//函数(变量)列表 ScopePiece & ScopePiece::operator += (const OffsetItem & offset); }; struct ClassDescription : public BaseItem { ClassDescription() :g_end(-1) {} int g_end;//做用域下全部代码结束 QList<QString> parents;//父类 QMap<int, ScopePiece> pieces;//做用域列表 行号:域 QMap<int, ScopePiece> pieceIndexs;//快速访问索引 域类型:域 void RowNumber(const OffsetItem &); }; struct HeaderFile { QString name;//文件名 QList<BaseItem> note;//注释 QList<BaseItem> macros;//宏定义 QList<BaseItem> includeHeader;//包含头文件 QList<BaseItem> predeclaration;//类预声明 QMap<QString, ClassDescription> classDeclare; //类列表 QMap<int, QString> classOrder; //类顺序 QList<BaseItem> cStyleFuncations;//C函数(全局变量) void CleanUp() { note.clear(); macros.clear(); includeHeader.clear(); predeclaration.clear(); classDeclare.clear(); cStyleFuncations.clear(); } void RowNumber(int, int); };
当咱们把程序运行起来后,解析一个类文件时,他的内存描述可能会像这样
QtHeaderDescription
是解析头文件的真正实现类,代码比较多,这了我讲下每一个函数声明的做用
void SetFile(const QString &); 设置头文件 void Refrush(); 刷新内存结构 void CleanUp(bool = true); 清空内存结构 void GenerateFuncationCode(FuncType, const QString &, const QString & = ""); 插入指定代码在某个做用域 int GetClassStart(const QString & = "") const; 获取类的开始行 int GetClassEnd(const QString & = "") const; 获取类的结束行 int GetScopePieceStart(FuncType, const QString & = "") const; 获取做用域的开始行 int GetScopePieceEnd(FuncType, const QString & = "") const; void DeleteRow(int); 获取做用域的结束行 QString GetDefaultClass() const { return m_strDefaultClass; }获取默认的插入类名称 void Save(); 保存新的文件
StatementType GuessType(int); 预测当前行类型,多是注释、类或者函数等 void ReadFile(); 读取一个文件到内存 void AnalysisFile(); 分析内存中的文件到指定结构中 void AnalysisOne(int &); 分析一行代码 void ReadSingleRow(int); 插入到内存中 void ReadMutilRows(int, int); 插入多行到内存中 void ReadClass(int, int); 读取一个类 void AnalysisClass(int &); 分析一行代码(在类中) void ReadClassRows(int, int); 插入多行到内存(在类中) void ReadClassScope(const BaseItem &); 插入域(在类中) void ReadClassFuncation(const BaseItem &); 插入函数(在类中) void ReadClassEnd(int, int); 更新类结束标致 QString GenerateString(int start, int end); 根据行号生成串
QtGrammaAnalysis analysis; QString oldFilePath = fileInfo.absoluteFilePath(); analysis.SetHeaderFile(oldFilePath); analysis.GenerateDeclaration("\tvoid test1();"); analysis.SetScopeType(FT_PROTECT_SLOT); analysis.GenerateDeclaration("\tvoid test1_1();"); analysis.SetScopeType(FT_PUBLIC_SLOT); analysis.GenerateDeclaration("\tvoid test1_2();"); analysis.Save();
执行如上插入操做后,以下图所示
代码下载地址:C++解析头文件-Qt自动生成信号声明