使用 Qt 插件管理 Qt 对象

Posted

技术标签:

【中文标题】使用 Qt 插件管理 Qt 对象【英文标题】:Qt object management with Qt Plugins 【发布时间】:2015-12-16 13:07:53 【问题描述】:

我的问题是在使用 Qt 插件时如何进行适当的对象/资源管理。默认 RAII 似乎不适用于 Qt。

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

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

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

模块 A 将小部件 W 注册到 MainWindow。最好我想返回一个范围对象,当超出范围时删除和删除小部件。然而,似乎给定的小部件 W 不能简单地从工具栏中删除它,因为它适用于操作(并且删除操作不会删除小部件!参见下面的示例)。

最后,在我看来,Qt 的制作方式使它拥有一切的所有权,而您必须依靠 Qt 来删除它。这不适用于模块。我在这里寻找解决方案/最佳实践。

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

示例:executable.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;
;

module.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;
;

【问题讨论】:

【参考方案1】:

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

一般而言,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 的父对象是您的插件根目录的父对象。

【讨论】:

谢谢,但我不确定我是否明白这如何解决我的问题。我在开篇文章中添加了一个示例,以使其更加具体。除非我弄错了,否则您建议让小部件将插件对象作为所有者,但这不起作用,因为如果我理解正确,MainWindow 将窃取此所有权。【参考方案2】:

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

【讨论】:

以上是关于使用 Qt 插件管理 Qt 对象的主要内容,如果未能解决你的问题,请参考以下文章

在 Visual Studio 中使用 Qt 远程对象(带有 qt 插件)

使用带有新信号槽语法的 Qt 插件系统在接口类中声明信号

Qt的内存管理机制

QT 控件内存管理

Qt Creator插件工作流程代码走读

我可以重复使用 QRemoteObjectNode 吗?