☰
插件式框架代码分析(2)
2026/10/27 2:01:36 网站建设 项目流程

讲讲WidgetContainer,WidgetFactory,WidgetProvider还有ContentPlugin的关系

这四者分别负责:装窗口、找创建方法、规定创建接口、实现具体插件。

类可以理解为职责
WidgetContainer窗口的外壳装入、替换和释放内容窗口
WidgetFactory创建方法的登记表根据名称找到对应的创建函数
WidgetProvider公共接口规定插件如何声明名称、创建窗口
ContentPlugin接口的具体实现提供plugin.f、plugin.g,创建FWidget、GWidget

它们的关系如下,箭头上的文字表示具体关系:

用main.ui中选择plugin.f的例子串起来:

第一步:插件声明自己能提供什么。

ContentPlugin实现了WidgetProvider:

QStringList ContentPlugin::widgetClasses() const { return {"plugin.f", "plugin.g"}; }

这相当于告诉主程序:“我可以创建这两种内容。”

第二步:加载器把创建方法登记到工厂。

PluginLoader读取这些名称,将对应回调注册到WidgetFactory:

plugin.f → 调用插件,创建 FWidget plugin.g → 调用插件,创建 GWidget

此时只是登记方法,还没有创建内容窗口。

第三步:容器根据 UI 配置请求内容。

main.ui给某个容器配置:

contentClass = "plugin.f"

UI 加载器读取后,让容器调用setContentClass("plugin.f")。容器再通过自己持有的工厂副本创建:

这里的this就是容器,作为新窗口的父对象。

第四步:工厂执行回调,插件创建窗口。

回调最终通过WidgetProvider接口,进入:

QWidget *ContentPlugin::createWidget( const QString &name, QWidget *parent) { if (name == "plugin.f") return new FWidget(services, parent); // 其他名称…… }

返回的FWidget随后被放进容器的布局中。

最关键的一点是:ContentPlugin不是那个被显示的内容窗口。它是插件入口,负责创建内容;实际显示的是FWidget或GWidget。而WidgetProvider只是它遵守的创建规则。


思考,已有main.ui,怎么知道当前我有6个WidgetContainer对象?

程序不是提前写死“有六个”,而是加载main.ui后,从生成的对象树中查找出来的。

分为两步:

第一步:根据 UI 文件创建对象。

main.ui里有六处这样的描述:

<widget class="WidgetContainer" name="contentCounterContainer"> ... </widget>

RuntimeUiLoader读到每一处时,都会进入它重写的createWidget():

if (className == "WidgetContainer") { auto *container = new WidgetContainer(factory, parent); container->setObjectName(name); return container; }

因此,六处描述就会创建六个对象。

第二步:从主窗口下面查找所有容器。

UI 加载完成后,populateContainers()执行:

auto containers = root->findChildren<WidgetContainer *>();

findChildren默认会递归查找所有后代对象。所以即使容器放在QGroupBox内部,也能找到。

当前对象树大致是:

主窗口 root ├── 内容计数组 → WidgetContainer ├── 内容文本组 → WidgetContainer ├── 设备监控组 → WidgetContainer ├── 设备设置组 → WidgetContainer ├── 报表预览组 → WidgetContainer └── 报表编辑组 → WidgetContainer

然后逐个读取contentClass,装入对应内容。

这样看的话,这个RuntimeUiLoader类很有意思啊

是的,RuntimeUiLoader正好处在UI 文件描述和实际 C++ 对象之间,所以它很适合做扩展。

Qt 原有的QUiLoader已经知道如何创建QPushButton、QLabel等标准控件。我们重写createWidget(),让它额外认识WidgetContainer:

if (className == "WidgetContainer") { // 创建我们自己的容器。 ... } // 其他控件继续交给 Qt 创建。 return QUiLoader::createWidget(className, parent, name);

它在当前程序中做了两件事:

  1. 加载时创建容器:看到WidgetContainer,就创建对应的 C++ 对象。
  2. 加载后填充内容:找到所有容器,读取各自的contentClass,再让容器装入插件界面。

这也解释了为什么分成两个阶段:执行createWidget()时,该控件在 UI 文件中的属性还没有全部设置好。等整个 UI 加载完成,再读取contentClass,才能可靠地知道要装入什么内容。

你可以把它看成一个“装配员”:main.ui给出布局和配置,RuntimeUiLoader把这些描述组装成真正可运行的界面。


你已经把主要流程串起来了:

main.ui描述界面 →RuntimeUiLoader创建容器 → 容器找工厂 → 工厂调用插件 → 插件创建 QWidget。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询