Skip to content

Latest commit

 

History

History
248 lines (182 loc) · 11 KB

Developer.md

File metadata and controls

248 lines (182 loc) · 11 KB

HugoWidgets 开发者文档

面向开发者:插件框架、宿主程序启动流程、功能页开发流程与构建调试要点。

使用者请阅读 HugoWidgets 用户文档。

目录

1. 技术栈与总体架构

项 内容
语言 / 标准 C++,CONFIG += c++2b
UI 框架 Qt6
构建系统 qmake
插件框架 WECore
核心能力库 HugoUtils(含 WinUtils)

项目基于 WECore 插件框架(宿主 = src/WidgetExplorer.pro,功能以插件形式提供),界面样式由框架的 WStyle 统一提供。

2. 仓库结构

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/          # 功能页

3. 构建

3.1 环境要求

  • Qt6
  • Git(拉取子模块)

3.2 拉取与构建

git clone https://github.com/HugoWidget/HugoWidgets --recursive

用 Qt Creator 打开顶层 WidgetExplorer.pro

4. 主界面插件 HugoMain

HugoMain 负责「壳」:菜单栏、标签页与功能页的装载。

4.1 插件初始化

HugoMainPlugin::init 依次完成:

  1. 通过 PluginData::setData/setPlugin 取回框架对象(WEBase / WPlugin)。

  2. 创建并注册主窗口:

    auto widgetManager = PClass->widgetManager();
    widget = new HugoMainWidget(PData);
    widgetManager->addWidget(QUuid::createUuid(), widget, this);
  3. setMetaData(Plugin::Name, "HugoMain") 设置插件元数据。

4.2 主窗口结构

HugoMainWidget 是 QWidget,内部持有一个 QMainWindow 与一个 QTabWidget(标签页可拖动、可关闭)。

  • 构造函数中先订阅功能页装载事件,再通过 pluginManager() 依次 loadPlugin / initPlugin 其余插件——功能页正是在这些插件初始化时发布的(见 4.3)。
  • addHomePage() 依据 m_allPagesById 为每个功能页生成一个磁贴按钮(FlowLayout 自动换行),标签页以 id() / name() 驱动。
  • 标题栏右上角(菜单栏 corner widget)显示当前是否以管理员身份运行。
  • 「帮助 → 设置」由 openSettings() 打开:宿主模板描述本对话框补充的“说明”子页。

4.3 页面装载事件

功能页插件不直接依赖主界面,而是通过 WECore 事件总线发布功能页:

项 值
事件常量 HugoWidgets::Consts::Event::AddPage
事件主题 hugo.hugowidget.hugowidgets.addpage
消息对象 FunctionPageBase*(WMessage::object)

事件常量定义在 plugins/HugoMain/IHugoMain.h(HugoWidgets::Consts::Event 命名空间),由两个插件共同引用,避免两端硬编码字符串。主界面在构造时 subscribe 该主题,收到后把页面登记进 m_allPagesById 并调用其 init()。

4.4 功能页基类 FunctionPageBase

functionpagebase.h 是功能页插件与主界面之间的契约,位于 HugoMain 目录但被两个插件共同引用(在 HugoWidgetsPlugin.pro 的 HEADERS 中以 ../HugoMain/FunctionPageBase.h 形式加入)。

开发新功能页时必须实现 id() 与 name();init() / cleanup() / adminRequired() 可按需重写:

  • id():全局唯一标识;name():显示名称。
  • adminRequired():返回 true 表示该页面需要管理员权限,主界面在非管理员运行时打开该页会弹出一次警告。
  • 静态方法 isRunningAsAdmin() 用于查询当前进程权限(内部调用 WinUtils::IsCurrentProcessAdmin())。
  • 信号 closeRequested():页面可主动请求关闭自身标签页。

5. 功能页插件 HugoWidgetsPlugin

5.1 页面注册

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。

6. 新增功能页与插件

6.1 在现有插件中新增一个功能页

  1. 在 plugins/HugoWidgetsPlugin/HugoPages/ 下新建 HugoXxxPage.h / .cpp(需要界面时可加 .ui)。

  2. 继承 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();
    };
  3. 把新文件加入 HugoWidgetsPlugin.pro 的 SOURCES / HEADERS(含 .ui 时加入 FORMS)。

  4. 在 HugoWidgetsPlugin::init() 中追加 createPage(new HugoXxxPage);。

  5. 重新构建插件——功能入口会自动出现在主页(由主界面根据 id()/name() 生成)。

6.2 新增一个独立插件

WECore 2.x 不再使用旧的“数字键插件清单”,改由插件配置树发现插件:LightWidget 在初始化时创建 WPluginScanner 并调用 findAllPlugin(),从 Consts::Plugins::ConfigPath(plugins/plugins.json)开始深度优先扫描整棵树,读取每个插件描述并实例化、注册。

  1. 在 plugins/ 下新建目录,参照 HugoMain.pro 编写 .pro:TEMPLATE = lib、CONFIG += plugin、CONFIG += c++2b,TARGET 为插件名,DESTDIR 指向 src/<cfg>/plugins/HugoWidget,按 debug/release 选择 LIBS += -lwecore,并 include WECore.pri / HugoUtils.pri。

  2. 实现 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, ...) 等设置元数据。

  3. 编写插件描述 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。

  4. 在插件配置树中登记。树由多层 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。

  5. 把新插件加入顶层 WidgetExplorer.pro 的 SUBDIRS(注意 CONFIG += ordered,插件应排在 src/WidgetExplorer.pro 之后)。

  6. 构建后在程序目录验证:插件描述能被扫描到并成功加载;再将 MainWidget 设为 true 的插件会被 LightWidget::initMainPlugin() 选作主界面(存在多个时弹出选择对话框,并可用 Config::DefaultMain 记忆默认项)。

相关文档