Qt插件的Qt对象管理

4
我的问题是在使用Qt插件时如何进行正确的对象/资源管理。默认的RAII似乎与Qt不兼容。
在我们的应用程序中,我们使用模块(Qt插件),它们在运行时动态加载。当加载插件时,插件可以初始化自己,并作为初始化阶段的一部分将自己的小部件添加到应用程序中。 - 到工具栏 - 到侧面板 - 等等。 添加到主窗口的小部件也会转移它们的所有权。
这一切都很好,但现在我们的应用程序变得更加复杂,我们还需要注意关闭阶段。简单地卸载模块会让我们陷入各种麻烦。不存在的对象或仍然存活的对象类型已被卸载。
为了实现可靠的关闭,似乎唯一正确的方法是进行反向初始化。这也意味着每个将小部件添加到主窗口的模块也必须将它们删除。尝试使用第一个小部件就已经让我陷入了麻烦。
模块A向MainWindow注册小部件W。最好我想返回一个作用域对象,在作用域结束时删除和释放小部件。然而,已经看出来,给定小部件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;
};
2个回答

0

这个问题有点难回答,因为它取决于你的架构。

一般来说,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是父对象,它是父对象到您的插件根。


谢谢,但我不确定这如何解决我的问题。我在帖子中添加了一个示例以使其更具体化。除非我弄错了,您建议使小部件具有插件对象作为所有者,但是如果我理解正确,主窗口将窃取此所有权而导致它无效。 - Freek Nossin

0
Qt论坛上发布这个问题后,我得到了以下答案。 尽管Qt接管了添加到Ui中的QWidgets/QObjects,但仍然可以从客户端删除它们。Qt的资源管理系统是以这样一种方式构建的,即它将知道何时删除QObject,并通过更新UI以及删除对其的内部引用来处理此删除。有关更多详细信息,请参见链接。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接