Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Send QJsonObject from plugin to main app



  • Hi, I have a main application that will load many future developed plugins with the same interface:

    #ifndef PLUGIN_API_H
    #define PLUGIN_API_H
    
    #include <QString>
    #include <QDebug>
    
    class Plugin_API
    {
    public:
        virtual ~Plugin_API() = default;
    
        virtual QJsonObject data_from_plugin2app(void) = 0;
        virtual void open_ui(const int id) = 0;
        virtual void run(const int id) = 0;
    };
    
    // Declare our interface:
    Q_DECLARE_INTERFACE(Plugin_API, "com.lamar.plugin")
    
    #endif // PLUGIN_H
    

    The loaded plugins will be placed by the user in a custom serial sequence of execution.

    Now I can call the plugin->open_ui() and I get the plugin GUI on my screen. So I can fill the plugin input text with some data.

    My problem is:
    I have to save this plugin data (QJsonObject) in a main QJsonArray plugins_data that is a variable located in the main application.

    How can I send the data from my plugin (*.dll) to my main application?



  • Thank you @SGaist and @jsulm
    With your help I think that I found a solution! Here is the code:

    My plugin interface is called Plugin_API. So the plugin_api.h is:

    #ifndef PLUGIN_API_H
    #define PLUGIN_API_H
    
    #include <QString>
    #include <QDebug>
    #include <QJsonObject>
    #include <QObject>
    
    // You have to import and inherit QOBject in the plugin interface
    class Plugin_API : public QObject
    {
        // USE Q_OBJECT MACRO inside the interface
        Q_OBJECT
    public:
        virtual ~Plugin_API() = default;
    
        virtual QJsonObject get_data(void) = 0;
        virtual void set_project_path(const QString project_path) = 0;
        virtual void open_ui(const QJsonObject ui_data) = 0;
        virtual void run(const int job_id) = 0;
    
        // ===== SIGNALS ===== //
        // You have to declare the signals in the interface too:
        virtual void send_msg(const QString msg) = 0;
    };
    
    // Declare our interface:
    Q_DECLARE_INTERFACE(Plugin_API, "com.lamar.plugin")
    
    #endif // PLUGIN_API_H
    

    After that, in the plugin.h I have:

    // Do NOT inherit plublic QObject here:
    class PLUGINSHARED_EXPORT Plugin: public Plugin_API
    {
        // ADD Q_OBJECT MACRO HERE TOO:
        Q_OBJECT
        Q_PLUGIN_METADATA(IID "com.lamar.plugin")
        Q_INTERFACES(Plugin_API)
    
       ...
       // Overide the signal method:
       signals:
        void send_msg(const QString msg) override;
    

    In my Main Application header main.h I have:

    public slots:
        void receive_msg(const QString msg);
    

    And finally, the implementaion of the main application slot is:

    void main_app::receive_msg(const QString msg)
    {
        qDebug() << "I RECEIVE THIS MSG: " << msg;
    }
    

    Thank you @SGaist and @jsulm for your help and patience!


  • Lifetime Qt Champion

    Hi,

    Well, you load your plugin and then call the corresponding method from its interface.

    What have you tried ?



  • @SGaist Sorry, but I'm not seeing how can I do it. Could you help me?

    How the main application will know when the button save was clicked in the plugin UI window?
    Is possible to inform that only using interfaces? Or I need to use signals and slots.

    Today I tried to connect the plugin and the main app using signals and slots.
    So, inside the plugin.cpp in the constructor(), I type:

    connect(this, SIGNAL(send_msg(QString)), ???, SLOT(receive_msg(QString)));
    

    But, what I have to write in ????

    Obs.: I'm loading the plugins using QPluginLoader:

    QPluginLoader loader(file);
    loader.load()
    Plugin_API* plugin = qobject_cast<Plugin_API*>(loader.instance());
    

    I'm confused about this topic...
    Could you please help me a little more?


  • Lifetime Qt Champion

    @fem_dev Do not connect inside plug-in as it does not know anything about receiver of its signals (and it should not!).
    Do the connect in main window (or where-ever you use the plug-in).
    So, like

    MainWindow::MainWindow(...)
    {
        connect(plugin, SIGNAL(send_msg(QString)), this, SLOT(receive_msg(QString)));
    }
    
    plugin->data_from_plugin2app();
    


  • @jsulm thank you....I think that this is the half of solution...

    foreach(QString file, plugins) {
            QPluginLoader loader(file);
            if(loader.load())
            {
                Plugin_API* plugin = qobject_cast<Plugin_API*>(loader.instance());
    
                if(plugin) {
                    // HERE I GOT A COMPILATION ERROR:
                    // Cannot convert argument 1 from 'Plugin_API *' to 'const QObject *'
                    connect(plugin, SIGNAL(send_msg(QString)), this, SLOT(receive_msg(QString)));   
                } else {
                    qCritical() << "Wrong Plugin API! Could not cast: " << loader.fileName();
                }
            } else {
                qCritical() << "ERROR: " << loader.fileName() << " - Msg: " << loader.errorString();
            }
        }
    

    Buy my plugin.h have Q_OBJECT macro:

    #ifndef PLUGIN_H
    #define PLUGIN_H
    
    #include <QObject>
    #include <QtPlugin>
    
    #include <QJsonDocument>
    #include <QJsonArray>
    #include <QJsonObject>
    #include <QFile>
    #include <QByteArray>
    
    #include "analysis_x_global.h"
    #include "../plugin_api.h"
    #include "dialog.h"
    
    class PLUGIN_SHARED_EXPORT Plugin: public QObject, public Plugin_API
    {
        Q_OBJECT
        Q_PLUGIN_METADATA(IID "com.lamar.plugin")
        Q_INTERFACES(Plugin_API)
    
    public:
        explicit Plugin(QObject* parent = nullptr);
        ~Plugin() override;
    
        QJsonObject get_data(void) override;
        void open_ui(const QJsonObject ui_data) override;
        void run(const int job_id) override;
    
    signals:
        void send_msg(const QString msg);
    
    public slots:
        void onMessage(QJsonObject updated_ui_data);
    };
    
    #endif // PLUGIN_H
    

    Second: to call a plugin method from the main app, I know that can be done using:

    plugin->method()
    

    But my plugin have a Window (UI) and some buttons, like save.
    How can I send a plugin data to my main app when the user hits the save button in the plugin Window UI?


  • Lifetime Qt Champion

    @fem_dev said in Send QJsonObject from plugin to main app:

    How can I send a plugin data to my main app when the user hits the save button in the plugin Window UI?

    Via signals/slots.

    "Cannot convert argument 1 from 'Plugin_API *' to 'const QObject *'" - this is because your plug-in class is not derived from QObject and does not contain Q_OBJECT macro.



  • @jsulm please, look my plugin.h header above. It have the Q_OBJECT macro.
    And the class declaration is:

    class PLUGIN_SHARED_EXPORT Plugin: public QObject, public Plugin_API
    

    What is wrong?

    I tried to add the public QObject and Q_OBJECT macro inside the plugin_API class, but I got these compilation warnings/errors:

    'Plugin': base-class 'QObject' is already a base-class of 'Plugin_API'
    

  • Lifetime Qt Champion

    @fem_dev said in Send QJsonObject from plugin to main app:

    What is wrong?

    Problem is that plugin bellow is Plugin_API* and not Plugin*:

    connect(plugin, SIGNAL(send_msg(QString)), this, SLOT(receive_msg(QString))); 
    

    Cast to Plugin* instead of Plugin_API*.



  • @jsulm it's not possible because I have many different plugins that share the same Plugin_API...

    As you can see in the top, the Plugin_API have all methods that I use in all different plugins.

    So, when I load all plugins inside a foreach() loop, I have to cast they using the Plugin_API.

    Each individual plugin inherit the Plugin_API class.
    And inside each individual plugin I have declared:

    class PLUGIN_SHARED_EXPORT Plugin: public QObject, public Plugin_API
    {
        Q_OBJECT
        Q_PLUGIN_METADATA(IID "com.lamar.plugin")
        Q_INTERFACES(Plugin_API)
    

    What I'm doing wrong?



  • Thank you @SGaist and @jsulm
    With your help I think that I found a solution! Here is the code:

    My plugin interface is called Plugin_API. So the plugin_api.h is:

    #ifndef PLUGIN_API_H
    #define PLUGIN_API_H
    
    #include <QString>
    #include <QDebug>
    #include <QJsonObject>
    #include <QObject>
    
    // You have to import and inherit QOBject in the plugin interface
    class Plugin_API : public QObject
    {
        // USE Q_OBJECT MACRO inside the interface
        Q_OBJECT
    public:
        virtual ~Plugin_API() = default;
    
        virtual QJsonObject get_data(void) = 0;
        virtual void set_project_path(const QString project_path) = 0;
        virtual void open_ui(const QJsonObject ui_data) = 0;
        virtual void run(const int job_id) = 0;
    
        // ===== SIGNALS ===== //
        // You have to declare the signals in the interface too:
        virtual void send_msg(const QString msg) = 0;
    };
    
    // Declare our interface:
    Q_DECLARE_INTERFACE(Plugin_API, "com.lamar.plugin")
    
    #endif // PLUGIN_API_H
    

    After that, in the plugin.h I have:

    // Do NOT inherit plublic QObject here:
    class PLUGINSHARED_EXPORT Plugin: public Plugin_API
    {
        // ADD Q_OBJECT MACRO HERE TOO:
        Q_OBJECT
        Q_PLUGIN_METADATA(IID "com.lamar.plugin")
        Q_INTERFACES(Plugin_API)
    
       ...
       // Overide the signal method:
       signals:
        void send_msg(const QString msg) override;
    

    In my Main Application header main.h I have:

    public slots:
        void receive_msg(const QString msg);
    

    And finally, the implementaion of the main application slot is:

    void main_app::receive_msg(const QString msg)
    {
        qDebug() << "I RECEIVE THIS MSG: " << msg;
    }
    

    Thank you @SGaist and @jsulm for your help and patience!


Log in to reply