How to build a custom module like OPENTURNS_SALOME

I’m interested in building a module that loads GUI from other libraries like OPENTURNS_SALOME.
I wrote a demo, but it can’t be run.

// MyDemo.hxx
#ifndef __MYDEMOGUI_HXX__
#define __MYDEMOGUI_HXX__

#include "LightApp_Module.h"

#include <QObject>

class MyDemoGui : public LightApp_Module
{
    Q_OBJECT
public:
    MyDemoGui(const QString &module_name);
    void initialize(CAM_Application *app) override;
    void windows(QMap<int, int> &aMap) const override;
    bool activateModule(SUIT_Study *study) override;
    bool deactivateModule(SUIT_Study *study) override;
    virtual ~MyDemoGui();
};
#endif
// MyDemo.cxx
#include "MyDemoGui.hxx"

#include "LightApp_Application.h"
#include "SUIT_Desktop.h"
#include "SUIT_Session.h"
#include "SUIT_ViewManager.h"

MyDemoGui::MyDemoGui(const QString &module_name) : LightApp_Module(module_name)
{
}

void MyDemoGui::initialize(CAM_Application *app)
{
    LightApp_Module::initialize(app);
    LightApp_Application *anApp = getApp();
    SUIT_Desktop *dsk = anApp->desktop();
    // get resources manager
    QWidget *widget = new QWidget(dsk);
    widget->setWindowTitle("MyDemo Window");
    SUIT_ResourceMgr *resMgr = app->resourceMgr();
}

void MyDemoGui::windows(QMap<int, int> &aMap) const
{
#ifndef DISABLE_PYCONSOLE
    aMap.insert(LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea);
#endif
}

bool MyDemoGui::activateModule(SUIT_Study *study)
{
    bool isDone(LightApp_Module::activateModule(study));
    if (isDone)
    {
        LightApp_Application *app(dynamic_cast<LightApp_Application *>(SUIT_Session::session()->activeApplication()));
        if (!app)
            return false;
        SUIT_ViewManager *aViewManager(app->getViewManager("MyDemo Window",
                                                           false)); // create if necessary
        if (aViewManager)
            aViewManager->setShown(false);
        // showView(true);
        setMenuShown(true);
        setToolShown(true);
    }
    return isDone;
}

bool MyDemoGui::deactivateModule(SUIT_Study *study)
{
    // hide own menus
    setMenuShown(false);
    // hide own toolbars
    setToolShown(false);

    // call parent implementation and return the activation status
    return LightApp_Module::deactivateModule(study);
}

MyDemoGui::~MyDemoGui()
{
    // do nothing
}

I compiled it, put it into W64/MyDemo dir and edit salome file

 #[MYDEMO]
    context.setVariable(r"MYDEMO_ROOT_DIR", r"D:\D\SALOME-9.11.0\W64\MyDemo", overwrite=True)
    context.addToPath(r"%MYDEMO_ROOT_DIR%\bin\salome")
    context.addToPath(r"%MYDEMO_ROOT_DIR%\lib\salome")
    context.addToPythonPath(r"%MYDEMO_ROOT_DIR%\bin\salome")
    context.addToPythonPath(r"%MYDEMO_ROOT_DIR%\lib\salome")
    context.appendVariable(r"SALOME_MODULES", r"MYDEMO",separator=",")
    context.appendVariable(r"SalomeAppConfig", r"D:\D\SALOME-9.11.0\W64\MyDemo\share\salome\resources\mydemo",separator=";")

then I use python salome -m MYDEMO to run it, it can be loaded but the software always stay here.

I don’t know how to display my custom view.
Any advice would be appreciated :saluting_face: :saluting_face:

Ideally, share a github repo link if any, such that we can check on our side.

Here is the repo https://github.com/kiky-lee/SalomeGuiModuleDemo

Maybe I find the reason. I have to implement the createModule() to export the module.
I add this at the end of the MyDemo.cxx and it works.

extern "C"
{
    MYDEMO_EXPORT CAM_Module *createModule()
    {
        return new MyDemoGui("MYDEMO");
    }
}