時間過的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自動生成信號聲明