Background
In Qt development, slots can be connected to signals either via connect() or by naming conventions. For example, a button named pushButton_ok requires the following slot declaration:
private slots:
void on_pushButton_ok_clicked();
When using Qt Designer directly (without QtCreator’s built-in editor), the context menu "Go to slot" may be missing. The solution is to manually insert slot declarations and implementations into the corresponding .h and .cpp files. This article focuses on automatical generating signal declarations; the implementation code generation will be covered separately.
Approach
Two strategies exist:
- Simple insertion: Locate the scope identifier of a specific class and insert code directly. This is easy to implement but lacks flexibility (e.g., inserting after all functions in a scope is diffficult).
- Parse the header file: Build an in-memory representation of the class structure, then insert code flexibly. This is more complex but provides better extensibility.
The second approach is adopted. The parser reads the header file line by line, identifies constructs (comments, pre‑declarations, classes, functions, scopes), and updates an internal data model. After parsing, insertion becomes straightforward.
Data Structures for In-Memory Representation
The following structures store parsed header information:
struct OffsetItem {
int start; // starting line offset
int number; // line count
};
struct BaseItem {
BaseItem() : start(0), end(0) {}
QString name; // e.g., comment text, block name
int start; // line where code begins
int end; // line where code ends
BaseItem &operator+=(const OffsetItem &);
};
struct ScopePiece : public BaseItem {
ScopePiece() : g_end(-1) {}
int g_end; // end line of the entire scope
QList<BaseItem> functions; // list of functions (or variables)
ScopePiece &operator+=(const OffsetItem &);
};
struct ClassDescription : public BaseItem {
ClassDescription() : g_end(-1) {}
int g_end; // end line of the whole class
QList<QString> parents; // base classes
QMap<int, ScopePiece> pieces; // scope pieces keyed by line number
QMap<int, ScopePiece> pieceIndexes; // quick access by scope type
void RowNumber(const OffsetItem &);
};
struct HeaderFile {
QString name; // file name
QList<BaseItem> notes; // comments
QList<BaseItem> macros; // macro definitions
QList<BaseItem> includeHeaders; // included headers
QList<BaseItem> predeclarations; // forward declarations
QMap<QString, ClassDescription> classDeclare; // class map
QMap<int, QString> classOrder; // class insertion order
QList<BaseItem> cStyleFunctions; // global C‑style functions/variables
void CleanUp();
void RowNumber(int, int);
};
When parsing is complete, the memory representation for a class might look like (simplified):
ClassDescription:
name: "MyWidget"
start: 10, end: 100
parents: ["QPushButton"]
pieces:
12: ScopePiece (private slots) [start=12, end=80, functions=[...]]
82: ScopePiece (public slots) [start=82, end=98, functions=[...]]
Key Classes
- QtGrammaAnalysis: Public interface for users.
- QtFileCache: Caches file contents; supports multiple files for different types.
- QtHeaderDescription: Core parsing and manipulation logic. Main public methods: ```
void SetFile(const QString &); // assign header file
void Refresh(); // re‑parse
void CleanUp(bool = true); // clear memory
void GenerateDeclaration(FuncType, const QString &code, const QString &classname = "");
int GetClassStart(const QString & = "") const;
int GetClassEnd(const QString & = "") const;
int GetScopeStart(FuncType, const QString & = "") const;
int GetScopeEnd(FuncType, const QString & = "") const;
void DeleteRow(int);
QString GetDefaultClass() const;
void Save(); // write changes to file
Internal private helpers:
StatementType GuessType(int lineNo);
void ReadFile();
void AnalysisFile();
void AnalysisOne(int &lineNo);
void ReadSingleRow(int lineNo);
void ReadMutilRows(int start, int end);
void ReadClass(int start, int end);
void AnalysisClass(int &lineNo);
void ReadClassRows(int start, int end);
void ReadClassScope(const BaseItem &scope);
void ReadClassFunction(const BaseItem &func);
void ReadClassEnd(int classStart, int classEnd);
QString GenerateString(int start, int end);
Usage Example
QtGrammaAnalysis analyzer;
QString headerPath = fileInfo.absoluteFilePath();
analyzer.SetHeaderFile(headerPath);
analyzer.GenerateDeclaration("\tvoid test1();");
analyzer.SetScopeType(FT_PROTECTED_SLOT);
analyzer.GenerateDeclaration("\tvoid test1_1();");
analyzer.SetScopeType(FT_PUBLIC_SLOT);
analyzer.GenerateDeclaration("\tvoid test1_2();");
analyzer.Save();
After execution, the header file will contain the inserted declarations in the correct scopes, as shown in the illustration below:
[Illustration showing the resulting .h file with new slot declarations in proper sections]