|
|
| Line 1: |
Line 1: |
| # Qt在HarmonyOS上的主窗口与子窗口开发指南
| |
|
| |
|
| ## 1. 主窗口与子窗口概念
| |
|
| |
| ### 1.1 HarmonyOS的主窗口与子窗口
| |
|
| |
| - **主窗口**:代表一个"任务"的窗口,有Dock栏图标和任务图标(Alt+Tab切换任务时),一般有独立的生命周期,可最大化、最小化、窗口化
| |
| - **子窗口**:任务产生的某个子功能的交互界面,是独立窗口可在主窗口以外显示,但无Dock栏图标和任务图标,必须从属于一个主窗口,主窗口关闭时会联动关闭,主窗口最小化时联动隐藏,主窗口恢复显示时联动恢复显示。一般在鸿蒙上对应各种弹窗和对话框。
| |
|
| |
| ### 1.2 Qt Widget或窗口创建
| |
|
| |
| Qt创建`Widget`、`Window`时并无类似HarmonyOS的主、子关系的概念(部分widget如`QDialog`有`Transient Parent`概念,该概念与HarmonyOS主、子窗口接近)
| |
|
| |
| **示例:**
| |
| ```cpp
| |
| // 独立窗口
| |
| QWidget w;
| |
| w.show();
| |
|
| |
| // 有父子关系的窗口(假设w关联了一个原生窗口)
| |
| QWidget wp;
| |
| wp.show();
| |
| QWidget w(&wp);
| |
|
| |
| // 对话框
| |
| QDialog dlg;
| |
| dlg.exec();
| |
|
| |
| // 有父窗口的对话框(Transient Parent)
| |
| QWidget w;
| |
| w.show();
| |
| QDialog dlg(&w);
| |
| dlg.exec();
| |
| ```
| |
|
| |
| ## 2. HarmonyOS适配规则
| |
|
| |
| ### 2.1 核心规则
| |
|
| |
| 1. **标记函数**:使用`QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(QObject *windowOrWidgetToTag, QWindow *mainWindow)`函数,用于告诉Qt在为指定的顶层窗口创建HarmonyOS原生窗口时创建OHOS的子窗口,并指定其父窗口。该接口需要在mainWindow已创建,subWindow创建前调用。
| |
|
| |
| 2. **首个窗口规则**:进程的首个`QWindow`(或者无父亲的`QWidget`)创建时,会自动绑定到系统为应用创建的首个主窗口。
| |
|
| |
| 3. **后续窗口规则**:非首个`QWindow`(或者无父节点的`QWidget`)创建时,如果没有指定`AsSubWindowOf`标签,它将会创建一个OHOS的主窗口。
| |
|
| |
| 4. **Transient Parent处理**:对于Qt原生就有`Transient Parent`的`QWidget`对象(如QDialog的构造函数中可以传递`Transient Parent`对象),如果开发者传递了`Transient Parent`,则该对象的主窗口为`Transient Parent`对应的主窗口;如果没有传递,与普通的`QWidget`确定主窗口的方法相同。
| |
|
| |
| 5. **自动Fallback机制**:当标记的父窗口无效时,系统会自动为特定类型的窗口(如`Qt::Popup`、`Qt::ToolTip`、`Qt::Dialog`、`Qt::Tool`)寻找合适的父窗口:
| |
| - 优先使用当前有焦点的窗口
| |
| - 其次使用第一个可用的顶级窗口
| |
| - 确保窗口总能找到合适的父窗口
| |
|
| |
| ### 2.2 开发建议
| |
|
| |
| 开发者需要理解HarmonyOS的主、子窗口概念,并在使用Qt时注意新创建的顶层Widget(无parent)是否需要创建HarmonyOS的子窗口,如果需要,则需要调用`QOhosFunctions::tagWindowOrWidgetAsSubWindowOf()`函数,并指定其父窗口。
| |
|
| |
| **以下几种情况可以简化处理:**
| |
|
| |
| 1. **单实例应用**:如果应用为单实例(即单主窗口),需要在创建子窗口时调用`AsSubWindowOf`标签。
| |
|
| |
| 2. **多实例应用**:如果应用有多实例(即单进程多主窗口),但所有子窗口的弹出均是用户通过操作界面触发,则需要在创建子窗口时调用`AsSubWindowOf`标签,创建新的主窗口不需要特殊操作。
| |
|
| |
| 3. **Transient Parent窗口**:如果创建`QDialog`等支持`Transient Parent`的`QWidget`,且传递了`Transient Parent`参数,则无需调用`AsSubWindowOf`标签,使用默认行为即可。
| |
|
| |
| ## 3. 接口说明
| |
|
| |
| ### 3.1 主要函数
| |
|
| |
| ```cpp
| |
| static void QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(QObject *windowOrWidgetToTag, QWindow *mainWindow)
| |
| ```
| |
|
| |
| 该函数为窗口系统设置一个提示,使其不将给定的QWindow或QWidget窗口实例化为主窗口,而是将其实例化为主窗口的子窗口。
| |
|
| |
| **参数说明:**
| |
| - `windowOrWidgetToTag`:要标记为子窗口的QObject(必须是QWindow或QWidget)
| |
| - `mainWindow`:指定的父主窗口
| |
|
| |
| ### 3.2 辅助函数
| |
|
| |
| ```cpp
| |
| // 标记窗口为主窗口
| |
| static void QOhosFunctions::tagWindowOrWidgetAsMainWindow(QObject *windowOrWidgetToTag, bool forceMainWindow = true)
| |
|
| |
| // 获取通过tagWindowOrWidgetAsSubWindowOf标记的父窗口指针。
| |
| static QWindow *QOhosFunctions::getWindowOrWidgetAsSubWindowOfTagValue(QObject *targetWindowOrWidget)
| |
| ```
| |
|
| |
| ## 4. 重要注意事项
| |
|
| |
| > ⚠️ **警告:**
| |
| > 1. 在调用标记函数之前调用`winId()`或`show()`会导致标签无效。
| |
| > 2. `mainWindow`必须是一个有效的`QWindow`指针。
| |
| > 3. 如果`mainWindow`指向一个有效的`QWindow`,但这个`QWindow`不是主窗口,Qt框架会启用自动fallback机制,尝试找到合适的主窗口作为所有者。如果找不到合适的主窗口,就会创建一个主窗口而不是子窗口。
| |
|
| |
| ## 5. 代码示例
| |
|
| |
| ### 5.1 基本用法
| |
|
| |
| ```cpp
| |
| #include <QtPlatformHeaders/QOhosFunctions>
| |
|
| |
| // 创建一个子窗口,指定它属于当前这个主窗口
| |
| m_subwindow = new QWidget();
| |
| QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(m_subwindow, this->windowHandle());
| |
| m_subwindow->resize(800, 600);
| |
| m_subwindow->show();
| |
| ```
| |
|
| |
| ### 5.2 对话框示例
| |
|
| |
| ```cpp
| |
| // 方式1:使用Transient Parent(推荐)
| |
| QDialog *dialog = new QDialog(this); // this是主窗口widget
| |
| dialog->exec(); // 无需额外标记
| |
|
| |
| // 方式2:手动标记
| |
| QDialog *dialog = new QDialog();
| |
| QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(dialog, this->windowHandle());
| |
| dialog->exec();
| |
| ```
| |
|
| |
| ### 5.3 多窗口应用示例
| |
|
| |
| ```cpp
| |
| class MainWindow : public QMainWindow
| |
| {
| |
| public:
| |
| void createSubWindow() {
| |
| // 创建子窗口
| |
| QWidget *subWindow = new QWidget();
| |
|
| |
| // 标记为当前主窗口的子窗口
| |
| QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(subWindow, this->windowHandle());
| |
|
| |
| subWindow->setWindowTitle("子窗口");
| |
| subWindow->resize(400, 300);
| |
| subWindow->show();
| |
| }
| |
|
| |
| void createNewMainWindow() {
| |
| // 创建新的主窗口(无需特殊标记)
| |
| MainWindow *newMainWindow = new MainWindow();
| |
| newMainWindow->show();
| |
| }
| |
| };
| |
| ```
| |
|
| |
| ## 6. 最佳实践
| |
|
| |
| 1. **及时标记**:在调用`show()`或`winId()`之前完成窗口标记
| |
| 2. **合理分类**:明确区分哪些窗口应该是主窗口,哪些应该是子窗口
| |
| 3. **利用Transient Parent**:对于对话框等临时窗口,优先使用Qt原生的parent机制
| |
| 4. **测试验证**:在HarmonyOS设备上验证窗口的层级关系和生命周期行为
| |
| 5. **错误处理**:考虑父窗口无效时的fallback行为
| |
|
| |
| ## 7. 常见问题
| |
|
| |
| ### Q: 为什么我的子窗口显示为主窗口?
| |
| A: 可能的原因:
| |
| - 在调用`show()`之后才调用标记函数
| |
| - 传入的`mainWindow`参数无效
| |
| - 忘记调用标记函数
| |
|
| |
| ### Q: 子窗口可以脱离主窗口独立存在吗?
| |
| A: 不可以。在HarmonyOS中,子窗口必须依附于主窗口,主窗口关闭时子窗口会自动关闭。
| |
|
| |
| ### Q: 如何确认窗口的父子关系是否正确?
| |
| A: 可以使用`QOhosFunctions::getWindowOrWidgetAsSubWindowOfTagValue()`函数查询窗口的标记状态。
| |
|
| |
| ---
| |
|
| |
| *本文档基于Qt for HarmonyOS的实际实现编写,如有疑问请参考相关源码或联系技术支持。*
| |