面向开发者:插件框架、宿主程序启动流程、功能页开发流程与构建调试要点。
使用者请阅读 HugoWidgets 用户文档。
| 项 | 内容 |
|---|---|
| 语言 / 标准 | C++,CONFIG += c++2b |
| UI 框架 | Qt6 |
| 构建系统 | qmake |
| 插件框架 | WECore |
| 核心能力库 | HugoUtils(含 WinUtils) |
项目基于 WECore 插件框架(宿主 = src/WidgetExplorer.pro,功能以插件形式提供),界面样式由框架的 WStyle 统一提供。
HugoWidgets/
├── WidgetExplorer.pro # 顶层 qmake 工程(SUBDIRS)
├── README.md
├── docs/ # 文档
├── icons/ # 图标
├── licenses/ # 第三方许可证副本
├── deps/ # 依赖库
│ ├── WECore/ # WECore 插件框架(子模块)
│ ├── HugoUtils/ # HugoUtils 核心能力库(子模块)
│ └── HugoUtils.pri
├── src/
│ └── WidgetExplorer.pro # 宿主程序工程
└── plugins/
├── HugoMain/ # 主界面插件
│ ├── HugoMain.pro
│ ├── hugomainplugin.h/.cpp # WPlugin 实现
│ ├── hugomainwidget.h/.cpp # 主窗口(菜单栏 + 标签页 + 首页 + 设置)
│ ├── IHugoMain.h # 事件常量(插件间共享)
│ └── functionpagebase.h # 功能页基类(插件间共享接口)
└── HugoWidgetsPlugin/ # 功能页插件
├── HugoWidgetsPlugin.pro
├── HugoWidgetsPlugin.h/.cpp
└── HugoPages/ # 功能页
- Qt6
- Git(拉取子模块)
git clone https://github.com/HugoWidget/HugoWidgets --recursive用 Qt Creator 打开顶层 WidgetExplorer.pro
HugoMain 负责「壳」:菜单栏、标签页与功能页的装载。
HugoMainPlugin::init 依次完成:
-
通过
PluginData::setData/setPlugin取回框架对象(WEBase/WPlugin)。 -
创建并注册主窗口:
auto widgetManager = PClass->widgetManager(); widget = new HugoMainWidget(PData); widgetManager->addWidget(QUuid::createUuid(), widget, this);
-
setMetaData(Plugin::Name, "HugoMain")设置插件元数据。
HugoMainWidget 是 QWidget,内部持有一个 QMainWindow 与一个 QTabWidget(标签页可拖动、可关闭)。
- 构造函数中先订阅功能页装载事件,再通过
pluginManager()依次loadPlugin/initPlugin其余插件——功能页正是在这些插件初始化时发布的(见 4.3)。 addHomePage()依据m_allPagesById为每个功能页生成一个磁贴按钮(FlowLayout自动换行),标签页以id()/name()驱动。- 标题栏右上角(菜单栏 corner widget)显示当前是否以管理员身份运行。
- 「帮助 → 设置」由
openSettings()打开:宿主模板描述本对话框补充的“说明”子页。
功能页插件不直接依赖主界面,而是通过 WECore 事件总线发布功能页:
| 项 | 值 |
|---|---|
| 事件常量 | HugoWidgets::Consts::Event::AddPage |
| 事件主题 | hugo.hugowidget.hugowidgets.addpage |
| 消息对象 | FunctionPageBase*(WMessage::object) |
事件常量定义在 plugins/HugoMain/IHugoMain.h(HugoWidgets::Consts::Event 命名空间),由两个插件共同引用,避免两端硬编码字符串。主界面在构造时 subscribe 该主题,收到后把页面登记进 m_allPagesById 并调用其 init()。
functionpagebase.h 是功能页插件与主界面之间的契约,位于 HugoMain 目录但被两个插件共同引用(在 HugoWidgetsPlugin.pro 的 HEADERS 中以 ../HugoMain/FunctionPageBase.h 形式加入)。
开发新功能页时必须实现 id() 与 name();init() / cleanup() / adminRequired() 可按需重写:
id():全局唯一标识;name():显示名称。adminRequired():返回true表示该页面需要管理员权限,主界面在非管理员运行时打开该页会弹出一次警告。- 静态方法
isRunningAsAdmin()用于查询当前进程权限(内部调用WinUtils::IsCurrentProcessAdmin())。 - 信号
closeRequested():页面可主动请求关闭自身标签页。
HugoWidgetsPlugin::init 在设置元数据后,把各功能页交给主界面:
auto createPage = [&](FunctionPageBase* base) {
WMessage msg2;
msg2.object = base;
WEvent extEvent(QString(HugoWidgets::Consts::Event::AddPage), msg2);
widgetManager->publish(extEvent);
};
createPage(new HugoXxxPage);要点:
- 通过
hugo.hugowidget.hugowidgets.addpage事件发布功能页,由主界面插件接收并登记(加入m_allPagesById、在主页生成入口)。这就是功能页插件与主界面解耦的关键。新增页面只需在此处createPage一行,主页与标签页入口会自动生成。 - 功能页对象以裸指针创建,生命周期由接收方(主界面)管理,页面关闭时调用
cleanup()。
当前页面:Hugo.Info、Hugo.Logs、Hugo.Password、Hugo.Freeze、Hugo.FreezeFile、Hugo.Mount、Hugo.LaunchTool、Hugo.Protect。
-
在
plugins/HugoWidgetsPlugin/HugoPages/下新建HugoXxxPage.h/.cpp(需要界面时可加.ui)。 -
继承
FunctionPageBase,实现两个纯虚函数:class HugoXxxPage : public FunctionPageBase { Q_OBJECT public: explicit HugoXxxPage(QWidget *parent = nullptr); ~HugoXxxPage() override; QString id() const override; // 唯一 id,形如 "Hugo.Xxx" QString name() const override; // 显示名称 bool adminRequired() const override; // 可选:是否需要管理员权限 void init() override; // 可选:装载时初始化 void cleanup() override; // 可选:关闭时清理 private slots: void on_m_btnDo_clicked(); };
-
把新文件加入 HugoWidgetsPlugin.pro 的
SOURCES/HEADERS(含.ui时加入FORMS)。 -
在
HugoWidgetsPlugin::init()中追加createPage(new HugoXxxPage);。 -
重新构建插件——功能入口会自动出现在主页(由主界面根据
id()/name()生成)。
WECore 2.x 不再使用旧的“数字键插件清单”,改由插件配置树发现插件:LightWidget 在初始化时创建 WPluginScanner 并调用 findAllPlugin(),从 Consts::Plugins::ConfigPath(plugins/plugins.json)开始深度优先扫描整棵树,读取每个插件描述并实例化、注册。
-
在
plugins/下新建目录,参照 HugoMain.pro 编写.pro:TEMPLATE = lib、CONFIG += plugin、CONFIG += c++2b,TARGET为插件名,DESTDIR指向src/<cfg>/plugins/HugoWidget,按 debug/release 选择LIBS += -lwecore,并includeWECore.pri/HugoUtils.pri。 -
实现
WPluginInterface(而非旧版的WPlugin)的init/recMsg/deinit,并用 Qt 插件元数据导出:class HugoXxxPlugin : public QObject, public WPluginInterface { Q_OBJECT Q_PLUGIN_METADATA(IID WPluginInterface_iid) Q_INTERFACES(WPluginInterface) public: bool init(we::WMessage &msg) override; void recMsg(we::WMessage &msg) override; bool deinit(we::WMessage &msg) override; signals: void sendMsg(we::WMessage &); };
init中用PluginData::setData(qvariant_cast<WEBase *>(msg.map[Data::WEBase]))与PluginData::setPlugin(qvariant_cast<WPlugin *>(msg.map[Data::Plugin]))取回框架对象,再通过setMetaData(Plugin::Name, ...)等设置元数据。 -
编写插件描述 JSON(与二进制放在同一目录),至少包含
File(指向插件二进制的相对路径,扫描器据此定位 DLL/EXE);其余元数据可选:{ "File": "./HugoXxx.dll", "Name": "HugoXxx", "Uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "Version": "1.0.0", "Author": "HugoWidget", "MainWidget": false }常用键(见
Consts::Plugin):File、Name、Uuid、Version、Author、Desc、Type、Init、MainWidget、Autorun、Admin、Depends、DependsPath。 -
在插件配置树中登记。树由多层 JSON 组成:容器文件用
"Plugins"对象列出子项,每项以"Path"指向下一层 JSON(容器或插件描述),Path相对当前文件所在目录解析。根文件
plugins/plugins.json:{ "Plugins": { "HugoWidget": { "Path": "./HugoWidget/HugoWIdget.json" } } }容器
plugins/HugoWidget/HugoWIdget.json:{ "Plugins": { "HugoMain": { "Path": "./HugoMain.json" }, "HugoWidgetsPlugin": { "Path": "./HugoWidgetsPlugin.json" } } }整棵树也可用安全模式(
<程序> -pluginmanager,该模式不加载任何插件)在“插件配置管理器”中可视化地增删节点、修改Path。 -
把新插件加入顶层 WidgetExplorer.pro 的
SUBDIRS(注意CONFIG += ordered,插件应排在src/WidgetExplorer.pro之后)。 -
构建后在程序目录验证:插件描述能被扫描到并成功加载;再将
MainWidget设为true的插件会被LightWidget::initMainPlugin()选作主界面(存在多个时弹出选择对话框,并可用Config::DefaultMain记忆默认项)。