Automatically Generating Qt Signal Declarations by Parsing C++ Headers

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:

  1. 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).
  2. 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]

Tags: Qt C++ header parsing signal slot

Posted on Mon, 03 Aug 2026 16:11:57 +0000 by markyoung1984