C++解析头文件-Qt自动生成信号声明

目录

  • 一、瞎白话
  • 二、背景
  • 三、思路分析
  • 四、代码讲解
    • 1、类图
    • 2、内存结构声明
    • 3、QtHeaderDescription
    • 4、私有函数讲解
  • 五、分析结果
  • 六、下载

一、瞎白话

时间过的ZTMK,距离上一篇文章已经小半年过去了。为了安家、装修和结婚,搞得自己焦头烂额,这不是也正好赶上过年,一直没有时间写篇文章,最近终于慢慢回归正轨,所以决定写下这一篇文章,记录工作中的一些经验和内容。对于写文章这件事,我是这么认为的:一个是回顾自己的工作内容;另一方面也是为了能让有同样需求的同学用于借鉴。同时这也是我对自己的一个要求,每个阶段都应该有所输出,并有所记录,倘若若干年后,有机会再次看到这些东西的时候,能有一丝感动。。。

废话不多说了,那我们直接进入今天要分享的内容,怎么去自动生成Qt的信号声明

二、背景

用过Qt的人应该都知道,Qt中的每个控件都有很多信号,基于这些信号,我们可以实现我们自己的响应函数,也就是槽函数,通常槽函数和信号是通过connect连接起来的。除此之外呢,还有一种书写槽的方式,我们可以不用connect来连接,那就是我们的槽函数名称需要满足一定的规律,比如我们要实现一个名称为pushButton_ok的按钮点击事件,那么我们的槽函数声明可能会像下面这样:

private slots:
    void on_pushButton_ok_clicked();

用过QtCreator写代码的人可能都知道,上述的代码可以通过QtCreator内嵌的界面设计工具直接生成,但是当我们直接用QtDesigner工具编辑时,没有了转到槽这个菜单,如下图左侧的截图所示

上图中的右侧截图是我们修改过后的截图,当我们直接编辑UI文件时,也可以转到槽,不同的是我们需要自己去相应的.h和.cpp文件中去把声明和实现添加上,本篇文章我们先分析怎么添加函数声明,下篇文章在分析怎么添加函数实现定义,函数声明和定义是怎么构造的,这里不会讲解,Qt源码中都有,有兴趣的同学可以自己去了解下。

三、思路分析

既然我们的函数声明已经有了,我们只需要打开ui文件对应的头文件,然后把代码插入到合适的位置上即可,这里有2种方式实现。

  • 方式一:直接查找指定类的指定作用域标识符,插入到标识符之后
  • 方式二:分析头文件,解析类和其他有用信息,在内存中把类描述出来,插入时更灵活

以上两种方式,各有利弊,第一种方式简单粗暴,比较容易实现功能,但扩展性差,比如说要插入到指定域的所有函数之后,就比较难;第二种方式实现起来比较复杂,解析头文件是一个比较大的活,但是一旦文件解析成功后,插入工作就变得很简单。这里我选择了第二种方式来实现这个功能。

首先,解析头文件,我画了一个大致的流程图,主要是为了理解起来方便,并不是特别专业,凑合着看下

头文件解析时,主要的规则还是按行读取代码,然后去检测是否满足某一个类型条件,比如说已\\开头的我们认为是注释。

当满足条件时,我们去更新相应的内存结构,然后继续往下读,有时候我们可能需要连续读取好几行才能知道当前的内容是什么,

class
    A
        ;

如上述代码所示,是一个不标准的C++类预声明,我们只有读到;时,才知道这是一个类预声明,而不是一个类声明,有点儿绕口,但是这个很重要。

图中对于解析一个类模块,只是简单的用了一个块来表示,实际上解析一个类也是比较费劲的。当我们解析完类文件之后,就是简单的插入操作了,插入流程如下图所示

四、代码讲解

1、类图

类图中总共有3个模块:对外暴露的QtGrammaAnalysis类,提供给用户操作;QtFileCache是文件缓存类,供QtGrammaAnalysis类调用,文件缓存类可以有多个,主要是为了分析不同类型的文件;QtHeaderDescription是真正的文件描述类,所有的实际操作都是通过这个类来进行的。

2、内存结构声明

哈哈哈,好的代码自带注释,下面的结构体是为了我们解析头文件而声明的,每个重要的字段都有注释,这里不在做解释

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);
};

当我们把程序运行起来后,解析一个类文件时,他的内存描述可能会像这样

3、QtHeaderDescription

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(); 保存新的文件

4、私有函数讲解

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自动生成信号声明



转载声明:本站文章无特别说明,皆为原创,版权所有,转载请注明:朝十晚八 or Twowords



C++解析头文件-Qt自动生成信号声明

原文地址:https://www.cnblogs.com/swarmbees/p/10784399.html

时间: 2024-08-25 20:27:54

C++解析头文件-Qt自动生成信号声明的相关文章

上传文件时自动生成文件夹

#region 返回当前日期的文件夹 /// <summary>        ///上传文件时自动生成文件夹        /// </summary>        /// <param name="path">要保存的路径</param>        /// <returns>返回生成的文件夹</returns>        public static string CreateDis(string pa

sublime打开文件时自动生成并打开.dump文件

GBK Encoding Support 没有安装前打开ASNI格式编码文件会乱码,安装成功重启则可以打开正常 关于.dump文件生成的解释: 当打开一个非utf-8格式且包含汉字的文件时,sublime text 2会自动生成一个dump文件,文件修改过程中,不会修改原文件,只有按"保存"了才会将dump的数据更新到原文件里,关闭当前编辑的dump文件则会自动删除dump文件. 但是有时候 GBK Encoding Support 插件也会出些bug,就想这样,生成.dump文件后自

Eclipse中R文件不能自动生成

R文件不能自动生成主要是因为编译有错误,这时你想什么办法都是没有用的,clean, fix properties,都不是从根上解决问题. R文件主要是自动生成资源文件的id的,里边静态子类 主要都是对 res目录下的资源文件的引用 所以,只要是R文件不能自动生成或更新,主要是因为 编译的时候资源文件里有不合法的语法. 所以,遇到这种情况的话,不要去乱修改别的地方,一般会使问题变得更复杂. 解决方法: 1.如果console里有红色的编译不过的报错信息,直接定位到报错位置,修改错误OK! 2.首先

webstorm创建js文件时自动生成js注释

设置webstorm创建js文件时自动生成js注释 settings--Editor--File and Code Temlates 黑色框框里的内容自己填写上去,以下是参考的代码块: /** * @author ${USER} * @date ${DATE} ${TIME} */ 或者 /** * @author 张三 * @date ${YEAR}-${MONTH}-${DAY} ${HOUR}:${MINUTE} */ 变量解释: ${USER}:当前系统登录的用户名 ${DATE}:当前

(转)autoconfig.mk文件的自动生成

autoconf.mk uboot的顶层Makefile中有如下的一段代码 [plain] view plaincopy # # Auto-generate the autoconf.mk file (which is included by all makefiles) # # This target actually generates 2 files; autoconf.mk and autoconf.mk.dep. # the dep file is only include in th

在WebStorm里配置watcher实现编辑less文件时自动生成.css文件

1.安装 nodejs     //查看nodejs版本  node -v //查看npm版本  npm -v //全局安装less  npm install -g less 2.配置webStrom 添加external Tools parameters是默认地址的话  直接点insert就行 (Progrem 自己的安装位置   F:\NODE\node.exe) (parameters:C:\Users\Administrator\AppData\Roaming\npm) 添加watche

[C++]关于头文件中的防卫式声明(#ifndef...#pragma once)

大家知道,我们写.h文件时,通常会加上防卫式声明,有以下两种方式: 1. 宏定义 #ifndef _FILENAME_ #define _FILENAME_ //... #endif 2. 编译器指令 #pragma once 但是,为什么头文件中需要添加这种防卫式声明呢?如果没有这样的声明,会出现怎样的问题.这里,先看一个例子. -- "Car.h",代码如下(并没有添加防卫式声明): // Car.h class Car { // ... }; -- "Person.h&

设置pycharm 创建文件时自动生成头文件

找到该路径并添加以下信息 File->settings->Editor->File and Code Templates->Python Script  #!/usr/bin/env python# -*- coding: utf-8 -*-# @Time    : ${DATE} ${TIME}# @Author  : Aries# @Site    : ${SITE}# @File    : ${NAME}.py# @Software: ${PRODUCT_NAME}

Android开发_*.R文件无法自动生成

问题描述:             今天是我决定专注Android开发的第一天,我在网上下载了一个数独游戏的源码,准备开始着手学习.在导入之后出现Java文件中import *.R文件报错,在gen目录下面没有找到R.java,于是自己创建了个,然后开始了寻找自动更新的方法. 问题解决:             在经过寻找后,最后发现是自己的项目中没有mainactivity.java.原因是项目的安装版本不同,我使用的是eclipse3.7.2加插件,因为版本比较低,不支持自定义自定义主界面,