Plugin oriented App Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
User avatar
xaviou
Super wx Problem Solver
Super wx Problem Solver
Posts: 437
Joined: Mon Aug 21, 2006 3:18 pm
Location: Annecy - France
Contact:

Post by xaviou »

Hi

Here is what I've done (I post a simplified code, but the idea is here).
The declaration file : pluginsmanager.h

Code: Select all

#include "pluginbase.h"
WX_DECLARE_OBJARRAY(PluginBase*, ArrayOfPlugins);
class PluginsManager
{
    public:
        PluginsManager();
        virtual ~PluginsManager();
        static PluginsManager& Get();
        static void Kill();
        int LoadPlugins(const wxString& appPath);
        void UnloadPlugins();
    private:
        ArrayOfPlugins m_plugins;
        static PluginsManager *m_instance;
}
The declaration file : pluginsmanager.cpp

Code: Select all

#include "pluginsmanager.h"
#include <wx/filename.h>
#include <wx/dynload.h>
#include <wx/dir.h>

PluginsManager* PluginsManager::m_instance=NULL;
#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(ArrayOfPlugins);

PluginsManager::PluginsManager()
{
    //
}

PluginsManager::~PluginsManager()
{
    //
}

PluginsManager& PluginsManager::Get()
{
    if (m_instance==NULL)
    {
        m_instance=new PluginsManager();
    }
    return *m_instance;
}

void PluginsManager::Kill()
{
    if (m_instance!=NULL)
    {
        delete m_instance;
    }
    m_instance=NULL;
}

int PluginsManager::LoadPlugins(const wxString& appPath)
{
    // The folder where we can find the plugins files (*.dll or *.so)
    wxString sPlgPath=appPath;
    sPlgPath << _T("Plugins") << wxFileName::GetPathSeparator();
    wxDir dir(sPlgPath);
    // If the folder does not exists, we get out of here
    if (!wxDirExists(sPlgPath)) return 0;
    if (!dir.IsOpened()) return 0;

    wxString sFName;
#ifdef __WXMSW__
	bool bRes=dir.GetFirst(&sFName, _T("*.dll"),wxDIR_FILES);
#else
	bool bRes=dir.GetFirst(&sFName, _T("*.so"),wxDIR_FILES);
#endif

    while(bRes)
    {
        //Loading the ".dll" or ".so" file using the wxPluginManager class
        wxPluginManager::LoadLibrary(sPlgPath+sFName);
        bRes=dir.GetNext(&sFName);
    }
    // Searching, in the loaded libraries, the instances of classes
    // whitch name start with "wxTdlPlugin_"
    wxHashTable_Node* node;
    wxClassInfo::sm_classTable->BeginFind();
    int iPlgCount=0;
    while((node = wxClassInfo::sm_classTable->Next()))
    {
        wxClassInfo *classInfo = (wxClassInfo *)node->GetData();
        wxString classname = classInfo->GetClassName();
        if(classname.StartsWith(_T("wxTdlPlugin_")))
        {
            // Adding the founded instance to the plugins array
            PluginBase *object = (PluginBase*)classInfo->CreateObject();
            m_plugins.Add(object);
            iPlgCount++;
        }
    }
    return iPlgCount;
}

void PluginsManager::UnloadPlugins()
{
    PluginBase *plg;
    while(m_plugins.Count())
    {
        plg=*m_plugins.Detach(0);
        delete plg;
    }
}
As I said, this is a simplified (but working) code.

Now, here is how to use it :
The plugins files (*.dll or *.so) are in a "plugins" sub-folder of the application.
In the "OnInit" method of the wxApp derived class, you'll have to call "PluginsManager::Get()" to initialize the manager.
Then, call the "LoadPlugins" method (with the app path as parameter). It will load all the ".dll" or ".so" files founded in the plugins subfolder, and create an instance of each classes of type "wxTdlPlugin_........" (and storing a pointer of each intance created in an array, for future use).
You can do the manager initialization and the plugins loading at the same time :

Code: Select all

PluginsManager::Get().LoadPlugins(TheAppPath);
Do not forget to call the "UnloadPlugins" before closing the application :

Code: Select all

int MyApp::OnExit()
{
    PluginsManager::Get().UnloadPlugins();
    PluginsManager::Kill();
    return wxApp::OnExit();
}
Hope it will help you...
Feel free to ask for more details.

Regards

Xav'
My wxWidgets stuff web page : X@v's wxStuff
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2408
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Post by evstevemd »

Thanks man,
I can see I have some stuffs to learn on RTTI
But please help me elaborate this point in code, I cannot grasp it fully.
Thanks :P

Code: Select all

PluginsManager* PluginsManager::m_instance=NULL;
#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(ArrayOfPlugins);



    // Searching, in the loaded libraries, the instances of classes
    // whitch name start with "wxTdlPlugin_"
    wxHashTable_Node* node;
    wxClassInfo::sm_classTable->BeginFind();
    int iPlgCount=0;
    while((node = wxClassInfo::sm_classTable->Next()))
    {
        wxClassInfo *classInfo = (wxClassInfo *)node->GetData();
        wxString classname = classInfo->GetClassName();
        if(classname.StartsWith(_T("wxTdlPlugin_")))
        {
            // Adding the founded instance to the plugins array
            PluginBase *object = (PluginBase*)classInfo->CreateObject();
            m_plugins.Add(object);
            iPlgCount++;
        }
    }
    return iPlgCount;
}
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
User avatar
xaviou
Super wx Problem Solver
Super wx Problem Solver
Posts: 437
Joined: Mon Aug 21, 2006 3:18 pm
Location: Annecy - France
Contact:

Post by xaviou »

Code: Select all

PluginsManager* PluginsManager::m_instance=NULL;
The "PluginsManager" is defined as a singleton : only one object is created, and the same object is always returned by "PluginsManager::Get()"
The first time this method is called, the object is created and stored in tis var (as you can see in the header file, it is a static member)

Code: Select all

#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(ArrayOfPlugins);
This (and the WX_DECLARE_OBJARRAY macro in the header file) creates a new "type" named "ArrayOfPlugins". It will be used to store an array of objects of type "PluginBase*"

For the rest of the code, as I remember what I've understood the first time I used this :
When loading a library with "wxPluginManager::LoadLibrary()", a list of "dynamic classes" is created.
So, this code iterats into this list to find the desired class (witch name starts with "wxTdlPlugin_" in my case).

Regards

Xav'
My wxWidgets stuff web page : X@v's wxStuff
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2408
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Post by evstevemd »

xaviou wrote:

Code: Select all

PluginsManager* PluginsManager::m_instance=NULL;
The "PluginsManager" is defined as a singleton : only one object is created, and the same object is always returned by "PluginsManager::Get()"
The first time this method is called, the object is created and stored in tis var (as you can see in the header file, it is a static member)

Code: Select all

#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(ArrayOfPlugins);
This (and the WX_DECLARE_OBJARRAY macro in the header file) creates a new "type" named "ArrayOfPlugins". It will be used to store an array of objects of type "PluginBase*"

For the rest of the code, as I remember what I've understood the first time I used this :
When loading a library with "wxPluginManager::LoadLibrary()", a list of "dynamic classes" is created.
So, this code iterats into this list to find the desired class (witch name starts with "wxTdlPlugin_" in my case).

Regards

Xav'
I understand now, thanks.

I was looking at the macro and found wxDocs are against MyClass* and propose wxArrey for that. Check here

http://docs.wxwidgets.org/trunk/dynarra ... c42179abde

They say

Code: Select all

class MyClass;
    WX_DECLARE_OBJARRAY(MyClass, wxArrayOfMyClass); // note: not "MyClass *"!
They propose

Code: Select all

class MyClass;
    WX_DEFINE_ARRAY(MyClass *, ArrayOfMyClass);
I hope I have to read to get their point. I wanted to point because I see it used in your code
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2408
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Post by evstevemd »

Now, when I'm making concrete class that depends on wxWindow* then How do I supply it to that plugin? For example in the code belo, how do I supply wxFrame* to CreatePluginPanel(wxWindow* parent) method since plugins are loaded by manager?

Code: Select all

class DataPlugin : public  PluginBase
{
        public:
                DataPlugin() : PluginBase(PT_DATAS) { }
                virtual wxPanel* CreatePluginPanel(wxWindow* parent)=0;
                //...
};
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
User avatar
xaviou
Super wx Problem Solver
Super wx Problem Solver
Posts: 437
Joined: Mon Aug 21, 2006 3:18 pm
Location: Annecy - France
Contact:

Post by xaviou »

Hi

Well, as I said when I gave you the code of my PluginsManager, I posted a simplified code.

The "real" PluginsManager has a method named "CreateDatasPluginsPanels", witch takes 2 parameters :
  • A pointer to the future parent of the panels
  • A pointer to the wxSizer associated with the parent window
The PluginsManager iterates through its "ArrayOfPlugins", and for each one (if it has the correct type), calls the "CreatePluginPanel" method, and add the returned wxPanel to the sizer

Here is the code of the "CreateDatasPluginsPanels" method :

Code: Select all

void PluginsManager::CreateDatasPluginsPanels(wxWindow* parent, wxSizer* szr)
{
    PluginBase *plg;
    DataPlugin *dplg;
    wxPanel* pnl;
    // Getting the total number of loaded plugins
    int iPlgCount=m_plugins.GetCount();
    // Iterate through the list of plugins
    for (int i=0;i<iPlgCount; i++)
    {
        plg=m_plugins[i];
        // If the plugin has the correct type
        if (plg->GetPluginType()==wxTDL_PT_DATAS)
        {
            dplg=(DataPlugin*) plg;
            // Asks the plugin to create its panel
            pnl=dplg->CreatePluginPanel(parent);
            // If successfull
            if (pnl!=NULL)
            {
                // Add the created panel to the sizer associated with the parent
                szr->Add(pnl, 0, wxALL|wxEXPAND, 0);
            }
        }
    }
}
Regards

Xav'
My wxWidgets stuff web page : X@v's wxStuff
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2408
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Post by evstevemd »

thanks Xav,
I can see now the way to Go!
Thanks
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2408
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Post by evstevemd »

Hi Xav and everybody,
I see definition

Code: Select all

private:
        ArrayOfPlugins m_plugins; 
How do you access this seeing that it is private member?
I believe main app does not inherit its class nor it is a friend.
So How do you go about using plugins in the array?

Thanks
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
User avatar
xaviou
Super wx Problem Solver
Super wx Problem Solver
Posts: 437
Joined: Mon Aug 21, 2006 3:18 pm
Location: Annecy - France
Contact:

Post by xaviou »

Hi, and sorry for the late response.

The application does not access directly the array of plugins.
It just calls methods of the plugins manager, and this one makes all the job.

For example, the datas plugins have a panel that is displayed onto the main interface.
So, the application calls a "CreateDatasPluginsPanels" method of the manager, with the parent and the sizer as parameters.
The manager iterates into the array of plugins, and if it finds one that has the correct type, it calls its "CreatePluginPanel" method with the "parent" parameter, and then, adds the created panel to the sizer :

Code: Select all

void PluginsManager::CreateDatasPluginsPanels(wxWindow* parent, wxSizer* szr)
{
    PluginBase *plg;
    DataPlugin *dplg;
    wxPanel* pnl;
    // Iterate into the whole array
    for (int i=0;i<m_iPlgCount; i++)
    {
        // Get the current plugins
        plg=m_plugins[i];
        // Check if it has the correct type
        if (plg->GetPluginType()==wxTDL_PT_DATAS)
        {
            // If Ok, cast the plugin with the correct var type
            dplg=(DataPlugin*) plg;
            // The ask it to create the panel
            pnl=dplg->CreatePluginPanel(parent);
            // If successful, add the panel to the sizer
            if (pnl!=NULL)
            {
                szr->Add(pnl, 0, wxALL|wxEXPAND, 0);
            }
        }
    }
}
Sorry again for the late response.
Regards

Xav'
My wxWidgets stuff web page : X@v's wxStuff
Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria »

Xaviou,

plugin based apps is a question that seems to come up often, maybe you could take your little example and add it to the wiki? Just a thought like that, would be great :)
"Keyboard not detected. Press F1 to continue"
-- Windows
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2408
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Post by evstevemd »

Auria wrote:Xaviou,

plugin based apps is a question that seems to come up often, maybe you could take your little example and add it to the wiki? Just a thought like that, would be great :)
I second that!
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2408
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Post by evstevemd »

xaviou wrote:Hi, and sorry for the late response.

The application does not access directly the array of plugins.
It just calls methods of the plugins manager, and this one makes all the job.

For example, the datas plugins have a panel that is displayed onto the main interface.
So, the application calls a "CreateDatasPluginsPanels" method of the manager, with the parent and the sizer as parameters.
The manager iterates into the array of plugins, and if it finds one that has the correct type, it calls its "CreatePluginPanel" method with the "parent" parameter, and then, adds the created panel to the sizer :

Code: Select all

void PluginsManager::CreateDatasPluginsPanels(wxWindow* parent, wxSizer* szr)
{
    PluginBase *plg;
    DataPlugin *dplg;
    wxPanel* pnl;
    // Iterate into the whole array
    for (int i=0;i<m_iPlgCount; i++)
    {
        // Get the current plugins
        plg=m_plugins[i];
        // Check if it has the correct type
        if (plg->GetPluginType()==wxTDL_PT_DATAS)
        {
            // If Ok, cast the plugin with the correct var type
            dplg=(DataPlugin*) plg;
            // The ask it to create the panel
            pnl=dplg->CreatePluginPanel(parent);
            // If successful, add the panel to the sizer
            if (pnl!=NULL)
            {
                szr->Add(pnl, 0, wxALL|wxEXPAND, 0);
            }
        }
    }
}
Sorry again for the late response.
Regards

Xav'
No problem and thanks.
It makes sense now!
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
User avatar
xaviou
Super wx Problem Solver
Super wx Problem Solver
Posts: 437
Joined: Mon Aug 21, 2006 3:18 pm
Location: Annecy - France
Contact:

Post by xaviou »

Auria wrote:Xaviou,

plugin based apps is a question that seems to come up often, maybe you could take your little example and add it to the wiki? Just a thought like that, would be great :)
I'll try to do it as soom as I'll have the time to

Regards

Xav'
My wxWidgets stuff web page : X@v's wxStuff
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2408
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Post by evstevemd »

xaviou wrote:Hi
jfouche wrote:OUps, that's not the good way.
I agree with that...

I think you should only derive you plugins from wxEvtHandler :

Here is what I've done to create the base class of plugins that can have differents types :

Code: Select all

#include <wx/wx.h>

enum Pugin_Type
{
	PT_UNKNOWN=0,
	PT_DATAS,
	PT_IMPEXP,
	PT_TOOL
};

class PluginBase : public wxEvtHandler
{
	public:
		PluginBase(Pugin_Type type) { m_Type=type; }
		Pugin_Type GetPluginType() { return m_Type; }
		virtual wxString GetPluginName()=0;
	private:
		Pugin_Type m_Type;
};
Then, for each plugin type, I have something like that :
The first type of plugins have to create a wxPanel to extent the main interface :

Code: Select all

class DataPlugin : public PluginBase
{
	public:
		DataPlugin() : PluginBase(PT_DATAS) { }
		virtual wxPanel* CreatePluginPanel(wxWindow* parent)=0;
		//...
};
And, for example, the "Tool" plugin type, that only have a function to be executed from a menu :

Code: Select all

class ToolPlugin : public PluginBase
{
	public:
		ToolPlugin() : PluginBase(PT_TOOL) { }
		virtual bool ExecuteTool(wxXmlNode* datas)=0;
};
Hope it will help you understand the concept...

Regards

Xav'
Suppose I want to have one type of plugin but with alot of methods let say to access toolbar menubar and notebook but I want to leave the choice of implementation to user without forcing him *to implement* ALL of memebers. I mean user can choose to either access menubar alone or menubar and toolbar or menubear, toolbar and notebook. How do I go making it?

I have the concept, but I fail to implement it.
Thanks for helping me a lot. I'm almost there. :P

EDIT:
I view this as impossible in my above thinking, but I somehow can't connect the concepts. :x
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2408
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Post by evstevemd »

Now that I can load Menus from DLL to App, I have question:
We know events are generated by App Menu and are connected to Specific wxID. So How do I ensure there is no ID conflict within plugins and that I have Unique ID?

Thanks
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
Post Reply