how to communicate between plugin or app?
-
Hi ,all.
according to Plug & Paint example, it shows app how to invoke a plugin's method.
but if plugin-A want to use plugin-B's method ,how to invoke?
Or, plugin-B needs to connect signal to plugin-A's slot, how to do that.
afterall , the interface is a pure c++ class, not inherited from QObject.
If I qobject_cast <MyClass*>, not qobject_cast<Interface*>, the complier will throw an error. -
plugin-A want to use plugin-B's method
Plugin A and B have no way to know if the other is present in the program address space or not.
I suppose the main program could advise plugin B (through a call in InterfaceB) of a pointer to plugin A (an InterfaceA*) after it has loaded them. B could them invoke InterfaceA methods through the pointer. Pointer lifetime and validity would have be considered if plugins can be unloaded. Handling of multiple plugins implementing IntefaceA is also an issue: which A do you tell B about? There may be issues in this I have not considered.
You would have to ask yourself if A and B are actually independent and whether they are better implemented as a single plugin (perhaps with multiple interfaces as in the example). Perhaps InterfaceA plugins should actually be loaded by plugin B and not the main program
Or, plugin-B needs to connect signal to plugin-A's slot, how to do that.
If they need to be connected signal-slot then the only place that can happen is in the program that has loaded the plugins and has access to the two QObjects.
-
plugin-A want to use plugin-B's method
Plugin A and B have no way to know if the other is present in the program address space or not.
I suppose the main program could advise plugin B (through a call in InterfaceB) of a pointer to plugin A (an InterfaceA*) after it has loaded them. B could them invoke InterfaceA methods through the pointer. Pointer lifetime and validity would have be considered if plugins can be unloaded. Handling of multiple plugins implementing IntefaceA is also an issue: which A do you tell B about? There may be issues in this I have not considered.
You would have to ask yourself if A and B are actually independent and whether they are better implemented as a single plugin (perhaps with multiple interfaces as in the example). Perhaps InterfaceA plugins should actually be loaded by plugin B and not the main program
Or, plugin-B needs to connect signal to plugin-A's slot, how to do that.
If they need to be connected signal-slot then the only place that can happen is in the program that has loaded the plugins and has access to the two QObjects.
@ChrisW67 thanks for replying.
I wrote an example, an issue makes me confused.
interface:#ifndef MYINTERFACE5_H #define MYINTERFACE5_H #include <QWidget> #include <QObject> class _MyPlugin5 : public QObject { Q_OBJECT public: virtual ~_MyPlugin5() = default; public slots: virtual void onNotify(QString txt) = 0; // cannot be virtual signals: void sigAdd(QString txt); }; Q_DECLARE_INTERFACE(_MyPlugin5,"com.myif._MyPlugin5/1.0"); #endif // MYINTERFACE_Hplugin project, the h:
#ifndef MYPLUGIN5_H #define MYPLUGIN5_H #include <QObject> #include "_myplugin5.h" class MyPlugin5 :public _MyPlugin5 { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QInterface5" FILE "MyPlugin5.json") Q_INTERFACES(_MyPlugin5); public: QWidget *CreateWidget(QWidget *parent); int add(int a,int b); signals: // already defined in base class // void sigAdd(QString txt) override; public slots: void onNotify(QString txt) override; }; #endifthe cpp:
#include "myplugin5.h" #include "utility.h" #include <QDebug> QWidget *MyPlugin5::CreateWidget(QWidget *parent) { return parent; } int MyPlugin5::add(int a, int b) { int sum = a+b; emit sigAdd(QString::number(sum)); return a+b; } void MyPlugin5::onNotify(QString txt) { T_INF <<"recv slot:" <<txt; emit sigAdd("aaa"); }now the Mainwindow to invoke:
// pb pbMyplugin5Add void MainWindow::on_pbMyplugin5Add_clicked() { QObject *obj = PluginManager::i().plugin("MyPlugin5"); if(obj == nullptr){ T_ERR << "nullptr"; return; } _MyPlugin5 *iface = qobject_cast<_MyPlugin5*>(obj); if(iface != nullptr){ // cannot be connected ,why? // connect(iface,&_MyPlugin5::sigAdd,this,&MainWindow::onAdd); // connection is OK and work connect(iface,SIGNAL(sigAdd(QString)),this,SLOT(onAdd(QString)),Qt::UniqueConnection); connect(this,&MainWindow::notifyPlugin,iface,&_MyPlugin5::onNotify,Qt::UniqueConnection); emit notifyPlugin("mainwindow to plugin"); } }as shown in the code,
connect(iface,&_MyPlugin5::sigAdd,this,&MainWindow::onAdd,Qt::UniqueConnection);is failed. it says unresolved external symbol _MyPlugin5::sigAdd
butconnect(iface,SIGNAL(sigAdd(QString)),this,SLOT(onAdd(QString)),Qt::UniqueConnection);is OK and work fine, why???
-
@JianJian Which "same issue"?
New-style connects (the only type you should be using in new code) are resolved entirely at build time:
connect(iface,&_MyPlugin5::sigAdd,this,&MainWindow::onAdd,Qt::UniqueConnection);This will compile if the appropriate header files are present, but fail to link because there is no corresponding link library containing a
_MyPlugin5::sigAdd()exported symbol.That would be quite normal for a Qt plugin: they implement interfaces and instances created at run time are only driven through those interfaces. The main program and plugins are not directly linked to each other.
Old-style connects are resolved at program run time:
connect(iface,SIGNAL(sigAdd(QString)),this,SLOT(onAdd(QString)),Qt::UniqueConnection);This will compile and link just fine. At the time it is executed it might work if
ifaceandthisobject contain the relevant signals and slots. If the objects do not have the named signals or slots then a runtime warning is output and the connect fails... the program will continue to run but not behave as expected. -
@JianJian Which "same issue"?
New-style connects (the only type you should be using in new code) are resolved entirely at build time:
connect(iface,&_MyPlugin5::sigAdd,this,&MainWindow::onAdd,Qt::UniqueConnection);This will compile if the appropriate header files are present, but fail to link because there is no corresponding link library containing a
_MyPlugin5::sigAdd()exported symbol.That would be quite normal for a Qt plugin: they implement interfaces and instances created at run time are only driven through those interfaces. The main program and plugins are not directly linked to each other.
Old-style connects are resolved at program run time:
connect(iface,SIGNAL(sigAdd(QString)),this,SLOT(onAdd(QString)),Qt::UniqueConnection);This will compile and link just fine. At the time it is executed it might work if
ifaceandthisobject contain the relevant signals and slots. If the objects do not have the named signals or slots then a runtime warning is output and the connect fails... the program will continue to run but not behave as expected. -
@ChrisW67 "the same issue" is use New-style connects will get the error "undefined reference to 'xxxx::staticMetaObject'" and function symbol error,but if i use the Old-style connects ,every thing is ok
-
@JianJian do you have a library that defines the plugin interface and link the main application and the plugin to it ?
@SGaist yes i do have the interface
// interface.h header class BasePluginInterface { public: explicit BasePluginInterface(); virtual ~BasePluginInterface(); virtual BasePluginInterface* plugin() = 0; virtual BasePluginInterface* newPlugin() = 0; }; #define BasePluginInterface_iid "QtPluginDemo.QPlugins.BasePluginInterface" Q_DECLARE_INTERFACE(BasePluginInterface, BasePluginInterface_iid)// qlogshowplugin.h header #include "BasePluginInterface.h" class QLogShowPluginInterface { public: //explicit QLogShowPluginInterface(QWidget* parent = nullptr); virtual ~QLogShowPluginInterface(); virtual int add(int a, int b) = 0; //signals: not work this way //virtual void sig_msg(QString) = 0; // slots //public slots: virtual void slot_showMsg(QString msg) = 0; }; #define QLogShowPluginInterface_iid "QtPluginDemo.plugins.QLogShowPluginInterface" Q_DECLARE_INTERFACE(QLogShowPluginInterface, QLogShowPluginInterface_iid) class QLogShow_Plugin : public QWidget, public QLogShowPluginInterface, public BasePluginInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "qtPluginDemo.plugins.QLogShowPlugin") Q_INTERFACES(BasePluginInterface QLogShowPluginInterface) public: explicit QLogShow_Plugin(QWidget* parent = nullptr); ~QLogShow_Plugin(); QLogShow_Plugin* plugin() ; QLogShow_Plugin* newPlugin() ; int add(int a, int b) ; public slots: void slot_showMsg(QString msg); signals: void sig_msg(QString); private slots: void on_pb_msg_clicked(); private: Ui::QLogShow_Plugin *ui; };the plugin .pro file QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 TARGET = QLogShow_Plugin TEMPLATE = lib CONFIG += plugin //the other content....and i use the plugin in another dll ,in the same application
// plugin magr dll QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 TARGET = QPluginMagr TEMPLATE = lib DEFINES += $${upper($$member(TARGET))}_LIBRARYthe plugin magr cpp file,use plugin
#include "QLogShow_Plugin.h" QPluginLoader loader(path); QObject* plugin = loader.instance(); if(!plugin){ return; } p = qobject_cast<BasePluginInterface*>(plugin); if(p == nullptr){ return; } else { auto plug = static_cast<QLogShow_Plugin*>(p); // show error undefined reference to `QLogShow_Plugin::staticMetaObject' connect(plug, &QLogShow_Plugin::sig_msg, [](){}); // error } -
@JianJian Which "same issue"?
New-style connects (the only type you should be using in new code) are resolved entirely at build time:
connect(iface,&_MyPlugin5::sigAdd,this,&MainWindow::onAdd,Qt::UniqueConnection);This will compile if the appropriate header files are present, but fail to link because there is no corresponding link library containing a
_MyPlugin5::sigAdd()exported symbol.That would be quite normal for a Qt plugin: they implement interfaces and instances created at run time are only driven through those interfaces. The main program and plugins are not directly linked to each other.
Old-style connects are resolved at program run time:
connect(iface,SIGNAL(sigAdd(QString)),this,SLOT(onAdd(QString)),Qt::UniqueConnection);This will compile and link just fine. At the time it is executed it might work if
ifaceandthisobject contain the relevant signals and slots. If the objects do not have the named signals or slots then a runtime warning is output and the connect fails... the program will continue to run but not behave as expected.@ChrisW67 said in how to communicate between plugin or app?:
New-style connects (the only type you should be using in new code)
Internally there are still a lot of string-based connection in Qt, which you also have to face when Qt pImpl style to create your own widgets from scratch.
See,Q_PRIVATE_SLOTfor example. -
@ChrisW67 said in how to communicate between plugin or app?:
New-style connects (the only type you should be using in new code)
Internally there are still a lot of string-based connection in Qt, which you also have to face when Qt pImpl style to create your own widgets from scratch.
See,Q_PRIVATE_SLOTfor example. -
@JianJian No, it does not. It does mean you need to organise your project a bit differently.
Here is a complete example: https://github.com/ChrisW67/qtforum-pluginplay
It works by creating a library containing the interfaces (abstract base classes) that is used by the application and the plugins. The application does not know of the specific plugin classes, only the interface classes. The application connects objects from the plugins using the interface. Neither plugin knows anything about the other or the application.There is probably more than one (neater) way to achieve this.
-
@JianJian No, it does not. It does mean you need to organise your project a bit differently.
Here is a complete example: https://github.com/ChrisW67/qtforum-pluginplay
It works by creating a library containing the interfaces (abstract base classes) that is used by the application and the plugins. The application does not know of the specific plugin classes, only the interface classes. The application connects objects from the plugins using the interface. Neither plugin knows anything about the other or the application.There is probably more than one (neater) way to achieve this.
@ChrisW67 thanks for your reply, i use qmake, and the most important is that i use plugin because i really do not want to add library import in my pro file, so i can also compile my application without plugin lib/dll.
i have learned your code,it seems needs to add lib import in cmake file, i think it will have compile error if i have no plugin dll in my application. -
@ChrisW67 thanks for your reply, i use qmake, and the most important is that i use plugin because i really do not want to add library import in my pro file, so i can also compile my application without plugin lib/dll.
i have learned your code,it seems needs to add lib import in cmake file, i think it will have compile error if i have no plugin dll in my application.@JianJian said in how to communicate between plugin or app?:
I use qmake
All of this works with
qmake. CMake and qmake just coordinate building the project and do not change the behaviour.I have added qmake project files to the example. I also removed some stray files from the app directory.
i think it will have compile error if i have no plugin dll in my application.
No. The application requires the static library containing a skeleton interface. It does not require any of the actual dynamically loaded plugins to build. Try removing the
src/pluginsdirectory from the top-level PRO file and do a clean build. -
In addition to @ChrisW67, if memory serves well, the library that is common to the plugins and the application shall not be static as it will result in duplicated static meta objects being loaded and will cause trouble.
-
@JianJian No, it does not. It does mean you need to organise your project a bit differently.
Here is a complete example: https://github.com/ChrisW67/qtforum-pluginplay
It works by creating a library containing the interfaces (abstract base classes) that is used by the application and the plugins. The application does not know of the specific plugin classes, only the interface classes. The application connects objects from the plugins using the interface. Neither plugin knows anything about the other or the application.There is probably more than one (neater) way to achieve this.
@ChrisW67 i have code like your way,and it still not work,this is my code: [https://github.com/xiaobingcaicai/qtpluginplay-demo],
1.uncheck the shadow build
2.compile the plugins sub-project
3.compile the app sub-project
show the error "undefined reference to `SenderInterface::staticMetaObject'" -
@ChrisW67 i have code like your way,and it still not work,this is my code: [https://github.com/xiaobingcaicai/qtpluginplay-demo],
1.uncheck the shadow build
2.compile the plugins sub-project
3.compile the app sub-project
show the error "undefined reference to `SenderInterface::staticMetaObject'"No need to build the sub-projects independently: just build the top level and it will recurse into the subdirectories.
PluginPlayproject should build everything in order but:- you are not building sub-project
interfaces(commented out, and missing). - you have omitted the CONFIG setting for ordered building (there are other ways to ensure the correct order but this is simplest)
- you are not building sub-project
- Sub-project
appdepends oninterfacesand so is unsatisfied. - Sub-project
pluginsdepend oninterfaces.
The
interfaceslibrary needs to be independent of bothappandplugins: you have wedged it into plugins. -
No need to build the sub-projects independently: just build the top level and it will recurse into the subdirectories.
PluginPlayproject should build everything in order but:- you are not building sub-project
interfaces(commented out, and missing). - you have omitted the CONFIG setting for ordered building (there are other ways to ensure the correct order but this is simplest)
- you are not building sub-project
- Sub-project
appdepends oninterfacesand so is unsatisfied. - Sub-project
pluginsdepend oninterfaces.
The
interfaceslibrary needs to be independent of bothappandplugins: you have wedged it into plugins.@ChrisW67 i have also tested the way you said:The interfaces library needs to be independent of both app and plugins. still get same error.
i think the primary cause is the plugin is not imported in the pro file, so the signs symbol can not be reconized, but if i import the plugin in the pro,the app can not compile success if there is no pugin(.dll/.so) filemaybe i have also maked other mistakes 😂😂
-
@ChrisW67 i have also tested the way you said:The interfaces library needs to be independent of both app and plugins. still get same error.
i think the primary cause is the plugin is not imported in the pro file, so the signs symbol can not be reconized, but if i import the plugin in the pro,the app can not compile success if there is no pugin(.dll/.so) filemaybe i have also maked other mistakes 😂😂
-
@JianJian Yes, look more closely at my example on GitHub.
The app does not need to link to, or include headers from, the plugin(s). The app only needs to know about the interface and the minimal link library it produces.