Qt Creator source code learning note 02, understanding the framework structure

It takes about 6 minutes to read this article

After learning about the basic knowledge of Qt Creator in the previous article [^ 1], this article first learns the basic structure of the framework, so that you can clearly know the files, folders and project files contained in the framework, what they mean and what their functions are

file structure

Open the downloaded source code, as shown in the following directory

It can be seen that there are many files and folders. Don't be frightened by these surfaces. There are few things we really need to care about. I show them in bold

  • bin folder
  • dist folder
  • doc folder
  • qbs folder
  • scripts folder
  • share folder
  • src folder
  • tests folder
  • docs.pri
  • qtcreator.pri
  • qtcreator.pro
  • qtcreator.qbs
  • qtcreatordata.pri
  • README.md

Here we mainly focus on the src folder. Here is the source code of the framework. We won't look at other folders first

qtcreator.pri file is a general configuration file in the project, such as version number and output path definition of some libraries. Each plug-in or sub project will contain this configuration file to facilitate direct configuration of some variables of the project (the specific configuration will be explained later)

qtcreator.pro file is the main project file. To open the compiled source code, you also need to open the project file for loading

PS: you don't need to pay attention to the content related to qbs. Qt Build Suite is also a cross platform compilation tool, which is rarely used at present

Frame structure

Let's take a detailed look at how the engineering structure is managed and the principle of the whole framework

After opening the project with Qt Creator, you will find that there are many sub projects. Don't be confused and afraid at this time. At present, we only need to care about three parts

  • libs
  • plugins
  • app

libs section

The libs project contains some common methods. We can focus on three at present

Aggregation project

This class provides the "packaging" function, which can package many components into a whole. The whole understanding is a little abstract. You can understand it as encapsulating multiple objects into an object, which provides the interface properties and methods of all objects

  • Each component object within the Aggregation collection can be converted to each other
  • The life cycle of each object in the Aggregation collection is bound together, that is, one is in all, the other is deleted and destructed, and the other components will be destructed

Extension system project

This class implements the management function of plug-ins and is the core part of the whole framework. All plug-in life cycle management is implemented in this class

  • IPlugin plug-in base class. All subsequent plug-ins inherit from it to realize all functions. There are three key methods to pay attention to
    virtual bool initialize(const QStringList &arguments, QString *errorString) = 0;
    virtual void extensionsInitialized() = 0;
    virtual bool delayedInitialize() { return false; }

Plug in initialization, external dependency initialization and delay initialization. These three virtual functions are used to initialize some resource information of each plug-in. External dependency is also particularly important. For example, if a plug-in depends on multiple other plug-ins at the same time, it needs to be processed here and wait for other plug-ins to load

  • PluginManager plug-in manages singleton classes. There is only one copy of the whole framework. It is responsible for the management of framework plug-ins. Its declaration cycle ends when the program exits
  • PluginManagerPrivate plug-in manages the specific implementation logic class. It's clear from the name. It's a typical P-D pointer relationship. This is to hide the specific implementation of the plug-in system extension from the outside. This skill is often seen in many later codes and is worth learning
  • PluginSpec plug-in core class, which implements all properties of the plug-in
class EXTENSIONSYSTEM_EXPORT PluginSpec
{
public:
    enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};

    ~PluginSpec();
    
    // Plug in name
    QString name() const;
    // Plug in version
    QString version() const;
    // Plug in compatible version
    QString compatVersion() const;

    // Plug in provider
    QString vendor() const;

    // Plug in copyright
    QString copyright() const;

    // Plug in protocol
    QString license() const;

    // Plug in description
    QString description() const;

    // Plugin home page URL
    QString url() const;
    
    // Plug in category, used to group and display plug-in information in the interface
    QString category() const;
}

Each plug-in (each dynamic library) has a copy of this object, which is used to record all the attribute information of the plug-in. These attribute information is read through the json configuration file. This information is called the "meta information" of the plug-in, which will be mentioned later

utils project

This project encapsulates some basic function algorithm classes, such as file operation, data sorting operation, json interactive operation, string operation set, etc. there are also some basic encapsulated control implementations in this project

For example, the QMainWindow class of the main window of the core plug-in to be mentioned later, and the base class is here

class QTCREATOR_UTILS_EXPORT AppMainWindow : public QMainWindow
{
    Q_OBJECT
public:
    AppMainWindow();

public slots:
    void raiseWindow();

signals:
    void deviceChange();

#ifdef Q_OS_WIN
protected:
    virtual bool winEvent(MSG *message, long *result);
    virtual bool event(QEvent *event);
#endif

private:
    const int m_deviceEventId;
};

This is mainly to notify external processing after some events change. For example, if the subject changes, send the corresponding signal, and if the device changes (plug and unplug the optical drive, etc.), send a device change event to the Qt event queue for processing

plugin section

This part is the implementation part of each plug-in. We need to focus on the implementation of the core plug-in corePlugin. Other plug-ins rely on the core plug-in to realize business functions

This plug-in mainly initializes the main window, menu management class instances and some mode management objects

Later, we will see various plug-ins. For example, when you open Qt Creator, the content displayed on the home page is also a separate plug-in called weilcome

Each plug-in has an identification ID, which is used to distinguish the plug-ins written by yourself to prevent others from maliciously modifying the plug-ins

Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Core.json")
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Welcome.json")

Each plug-in also has a corresponding metadata description configuration file, which configures some basic information of the plug-in, such as plug-in name, version, ownership, dependent plug-ins, etc. These configuration information will be written into the plug-in dynamic library during compilation, which is implemented by Qt meta object technology, In this way, when the plug-ins are loaded and run, these information can be dynamically obtained through reflection, and then used to verify the loading relationship between some plug-ins

A simple configuration description is shown below

{
    \"Name\" : \"Welcome\",
    \"Version\" : \"$$QTCREATOR_VERSION\",
    \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
    \"Vendor\" : \"The Qt Company Ltd\",
    \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
    \"License\" : [ \"Commercial Usage\",
                  \"\",
                  \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
                  \"\",
                  \"GNU General Public License Usage\",
                  \"\",
                  \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
    ],
    \"Category\" : \"Qt Creator\",
    \"Description\" : \"Secondary Welcome Screen Plugin.\",
    \"Url\" : \"http://www.qt.io\",
    $$dependencyList
}

Key among them are some variable values, such as $$QTCREATOR_VERSION, through this variable, we can directly read the variable values defined in the project qtcreator.pri, and then quickly and uniformly load and display, and obtain similar values of other variables

Secondly, we should pay attention to the configuration dependency files of each plug-in, such as welcome_dependencies.pri, which contains the libraries and plug-ins that depend on them

# Plug in name
QTC_PLUGIN_NAME = Welcome

# Libraries on which plug-ins depend
QTC_LIB_DEPENDS += \
    extensionsystem \
    utils
    
# Plug in dependent plug-ins
QTC_PLUGIN_DEPENDS += \
    coreplugin

If a plug-in depends on those plug-ins and dynamic libraries, you only need to add their names at the corresponding positions, and the project configuration file will be loaded automatically. In this way, writing can reduce a lot of repetitive work, and the plug-in dependencies can be clearly seen

app part

This part is the implementation part of the program entry. It mainly obtains the plug-in path, initializes the plug-ins and configuration files, and loads each plug-in. If there are no errors, the main interface will be displayed after initialization. Just look at the main function entry directly

The key part is the initialization of the plug-in manager. After setting the plug-in search path, initialize each plug-in

    PluginManager pluginManager;
    PluginManager::setPluginIID(QLatin1String("org.qt-project.Qt.QtCreatorPlugin"));
    PluginManager::setGlobalSettings(globalSettings);
    PluginManager::setSettings(settings);
    ......
    const QStringList pluginPaths = getPluginPaths() + customPluginPaths;
    PluginManager::setPluginPaths(pluginPaths);
    
    ......
    PluginManager::loadPlugins();

Another thing to pay attention to here is the file app_version.h.in

This is a template file. After qmake is loaded and executed, the corresponding header file app will be generated in the temporary directory_ version.h

#pragma once

namespace Core {
namespace Constants {

#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)

const char IDE_DISPLAY_NAME[] = \"$${IDE_DISPLAY_NAME}\";
const char IDE_ID[] = \"$${IDE_ID}\";
const char IDE_CASED_ID[] = \"$${IDE_CASED_ID}\";

#define IDE_VERSION $${QTCREATOR_VERSION}
#define IDE_VERSION_STR STRINGIFY(IDE_VERSION)
#define IDE_VERSION_DISPLAY_DEF $${QTCREATOR_DISPLAY_VERSION}

#define IDE_VERSION_MAJOR $$replace(QTCREATOR_VERSION, "^(\\d+)\\.\\d+\\.\\d+(-.*)?$", \\1)
#define IDE_VERSION_MINOR $$replace(QTCREATOR_VERSION, "^\\d+\\.(\\d+)\\.\\d+(-.*)?$", \\1)
#define IDE_VERSION_RELEASE $$replace(QTCREATOR_VERSION, "^\\d+\\.\\d+\\.(\\d+)(-.*)?$", \\1)

const char * const IDE_VERSION_LONG      = IDE_VERSION_STR;
const char * const IDE_VERSION_DISPLAY   = STRINGIFY(IDE_VERSION_DISPLAY_DEF);
const char * const IDE_AUTHOR            = \"The Qt Company Ltd\";
const char * const IDE_YEAR              = \"$${QTCREATOR_COPYRIGHT_YEAR}\";

#ifdef IDE_REVISION
const char * const IDE_REVISION_STR      = STRINGIFY(IDE_REVISION);
#else
const char * const IDE_REVISION_STR      = \"\";
#endif
...

} // Constants
} // Core

This template file defines some constants, and some variable values refer to macro definitions. Finally, after compilation, the macro definitions will be replaced with real values, which will really work when we introduce them into our code. The more detailed use process will be mentioned later when we uniformly analyze the techniques of pro files

summary

After learning this, I have roughly understood the basic structure of Qt Creator framework. First, there are some basic libraries. These dynamic libraries encapsulate some basic functions and usages to facilitate repeated calls in multiple modules. Second, the implementation of plug-in management system, mainly including plug-in object declaration cycle management, plug-in loading, plug-in unloading and plug-in direct dependency processing

For example, there are plug-ins a, B and C. plug-ins C now depend on plug-ins A and B at the same time, so they need special consideration when loading

Finally, the initialization of multiple plug-ins and the management class of main window and menu components are convenient to expand to other plug-ins for access management

The whole QTC plug-in system is composed of dynamic libraries. Each plug-in cooperates with each other to realize such a complex cross platform IDE. A lot of wonderful usage and knowledge can be found after careful study

Related reading

Tags: C++ Programming source code QtCreator

Posted on Sat, 20 Nov 2021 23:08:29 -0500 by Ristiisa