使用Qt插件管理Qt对象

Qt object management with Qt Plugins

本文关键字:Qt 对象 管理 插件 使用      更新时间:2023-10-16

我的问题是在使用Qt插件时如何进行正确的对象/资源管理。默认RAII似乎无法很好地与Qt配合使用。

在我们的应用程序中,我们使用在运行时动态加载的模块(Qt插件)。当加载的插件可以初始化自己时,作为初始化阶段的一部分,他们可以将自己的小部件添加到应用程序中。-到工具栏-到侧板-等等。添加到主窗口的小工具的所有权也会转移。

这一切都很好,但现在我们的应用程序变得越来越复杂,我们也需要注意关闭阶段。简单地卸载模块就会给我们带来各种各样的麻烦。不存在的对象或在其对象仍处于活动状态时卸载的类型。

要想可靠地关闭,似乎唯一正确的方法就是进行反向初始化。这也意味着,每个向主窗口添加小部件的模块也必须删除它们。已经尝试用第一个小部件来做这件事了,这让我陷入了麻烦。

模块A向主窗口注册小部件W。最好我想返回一个作用域对象,在超出作用域时删除和删除小部件。然而,给定的小部件W似乎已经无法简单地从工具栏中删除它,因为它与操作一起工作(删除操作并不会删除小部件!参见下面的示例)。

最后,在我看来,Qt是以这样一种方式制作的,它获得了所有东西的所有权,你必须依靠Qt来删除它。这对模块来说不太好。我在这里寻找解决方案/最佳实践。

编辑:
我添加了一个示例,其中一个模块将一个自定义小部件添加到MainWindow的工具栏中。我的目标是,模块负责删除小部件的时间,原因如上所述。这个例子是为了使问题更加具体。它代表了将这种模式与插件结合使用的一般问题——qt对象的所有权。

示例:
可执行.cpp

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0) {
        ui->setupUi(this);
        LoadPlugin();
    }
    void LoadPlugin() {
        m_plugin = new QPluginLoader("module.dll");
        m_plugin->load();
        IModule* moduleInstance = qobject_cast<IModule*>(m_plugin->instance());
        moduleInstance->Initialize(this);
    }
    void AddToolbarSection(QWidget* widget) {
        /** ownership is transferred here to Qt */
        mainToolBar->insertWidget(pWidget);
    }
    void RemoveToolbarSection(){
        /** How to get the widget deleted? */
    }
    /** this is called before the destructor */
    void UnloadPlugin() {
        moduleInstance->Shutdown();
        m_plugin->unload();
    }
    ~MainWindow() {
        /** deletion of toolbar sections must already been done here
            as the modules are already unloaded. Otherwise access violations occur
            because specific type information is not accessible anymore.                
        */
    }
private:        
    Ui::MainWindow *ui;
    QPluginLoader* m_plugin;
    IModule* m_moduleInstance;
};

模块.cpp

class EXPORT_MODULE IModule : public QObject
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID IModuleIID)
    Q_INTERFACES(IModule)
public:
    IModule() {
    }
    void Initialize(QMainWindow* window) {
        /** QMyToolbarSectionWidget is a custom widget defined in this module (module.dll)
            it has a specific destructor and triggers all kinds application
            specific cleanup */
        m_toolbarSection = new QMyToolbarSectionWidget();
        window->AddToolbarSection(m_toolbarSection);
    }
    void Shutdown() {
        window->RemoveToolbarSection(m_toolbarSection);
    }
private:
    QWidget* m_toolbarSection;
};

这有点难以回答,因为它取决于您的体系结构。

一般来说,Qt的清理思想与父指针有关。即

QObject *root;
QObject *leaf = new QObject();
leaf->setParent(root);
root->deleteLater();

QPluginLoader甚至会在卸载时清除你的根组件,所以理论上,插件下面的任何树都会被清除。只需确保从根返回的所有内容都是根的QObject。如果它不是QObject,请将其包装在QObject中。

class MyExtension : public QWidget {
   QAction *myAction;
   MyExtension() : QWidget() {
      myAction = new QAction(this);
   }
   QAction *getAction() {
      return myAction
   }
}

根据你的问题,我知道你可能也在这样工作:

class MyExtension : public QObject {
   MyWindow * myWindow;
   QAction  * myAction;
   MyExtension() : QObject() {
      myWindow = new MyWindow(this);
      myAction = new QAction(this);
   }
   void addToMainThing(TheMainThing *tmt) {
        tmt->addWidget(myAction);
   }
}

同样的事情。只需始终确保您的QObject是插件根目录的父对象。

在Qt论坛上交叉发布这个问题得到了以下答案。即使Qt拥有添加到Ui的QWidgets/QObjects的所有权,仍然可以从客户端删除它们。Qts资源管理系统的构建方式是,它将知道QObject何时被删除,并通过更新UI和删除对它的内部引用来处理此删除。有关更多详细信息,请参阅链接。