Qt for HarmonyOS/user development/mainwindow and subwindow guide zh: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
(Created page with "# Qt在HarmonyOS上的主窗口与子窗口开发指南 ## 1. 主窗口与子窗口概念 ### 1.1 HarmonyOS的主窗口与子窗口 - **主窗口**:代表一个"任务"的窗口,有Dock栏图标和任务图标(Alt+Tab切换任务时),一般有独立的生命周期,可最大化、最小化、窗口化 - **子窗口**:任务产生的某个子功能的交互界面,是独立窗口可在主窗口以外显示,但无Dock栏图标和任务图标,必须从...")
 
No edit summary
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
# Qt在HarmonyOS上的主窗口与子窗口开发指南
'''中文''' [[Qt_for_HarmonyOS/user_development_guide/mainwindow_and_subwindow_guide|English]]
== Qt在HarmonyOS上的主窗口与子窗口开发指南 ==


## 1. 主窗口与子窗口概念
=== 1. 主窗口与子窗口概念 ===


### 1.1 HarmonyOS的主窗口与子窗口
==== 1.1 HarmonyOS的主窗口与子窗口 ====
'''主窗口''':代表一个"任务"的窗口,有Dock栏图标和任务图标(Alt+Tab切换任务时),一般有独立的生命周期,可最大化、最小化、窗口化


- **主窗口**:代表一个"任务"的窗口,有Dock栏图标和任务图标(Alt+Tab切换任务时),一般有独立的生命周期,可最大化、最小化、窗口化
'''子窗口''':任务产生的某个子功能的交互界面,是独立窗口可在主窗口以外显示,但无Dock栏图标和任务图标,必须从属于一个主窗口,主窗口关闭时会联动关闭,主窗口最小化时联动隐藏,主窗口恢复显示时联动恢复显示。一般在鸿蒙上对应各种弹窗和对话框。
- **子窗口**:任务产生的某个子功能的交互界面,是独立窗口可在主窗口以外显示,但无Dock栏图标和任务图标,必须从属于一个主窗口,主窗口关闭时会联动关闭,主窗口最小化时联动隐藏,主窗口恢复显示时联动恢复显示。一般在鸿蒙上对应各种弹窗和对话框。


### 1.2 Qt Widget或窗口创建
==== 1.2 Qt Widget或窗口创建 ====
Qt创建Widget、Window时并无类似HarmonyOS的主、子关系的概念(部分widget如QDialog有Transient Parent概念,该概念与HarmonyOS主、子窗口接近)


Qt创建`Widget`、`Window`时并无类似HarmonyOS的主、子关系的概念(部分widget如`QDialog`有`Transient Parent`概念,该概念与HarmonyOS主、子窗口接近)


**示例:**
'''示例''':<syntaxhighlight lang="c++">
```cpp
// 独立窗口
// 独立窗口
QWidget w;
QWidget w;
Line 32: Line 32:
QDialog dlg(&w);
QDialog dlg(&w);
dlg.exec();
dlg.exec();
```
</syntaxhighlight>


## 2. HarmonyOS适配规则
=== 2. HarmonyOS适配规则 ===


### 2.1 核心规则
==== 2.1 核心规则 ====
1. 标记函数:使用'''QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(QObject *windowOrWidgetToTag, QWindow *mainWindow)'''函数,用于告诉Qt在为指定的顶层窗口创建'''HarmonyOS'''原生窗口时创建OHOS的子窗口,并指定其父窗口。该接口需要在'''mainWindow'''已创建,'''subWindow'''创建前调用。


1. **标记函数**:使用`QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(QObject *windowOrWidgetToTag, QWindow *mainWindow)`函数,用于告诉Qt在为指定的顶层窗口创建HarmonyOS原生窗口时创建OHOS的子窗口,并指定其父窗口。该接口需要在mainWindow已创建,subWindow创建前调用。
2. 首个窗口规则:进程的首个'''QWindow'''(或者无父亲的'''QWidget''')创建时,会自动绑定到系统为应用创建的首个主窗口。


2. **首个窗口规则**:进程的首个`QWindow`(或者无父亲的`QWidget`)创建时,会自动绑定到系统为应用创建的首个主窗口。
3. 后续窗口规则:非首个'''QWindow'''(或者无父节点的'''QWidget''')创建时,如果没有指定'''AsSubWindowOf'''标签,它将会创建一个'''OHOS'''的主窗口。


3. **后续窗口规则**:非首个`QWindow`(或者无父节点的`QWidget`)创建时,如果没有指定`AsSubWindowOf`标签,它将会创建一个OHOS的主窗口。
4. '''Transient Parent'''处理:对于Qt原生就有'''Transient Parent'''的'''QWidget'''对象(如QDialog的构造函数中可以传递'''Transient Parent'''对象),如果开发者传递了'''Transient Parent''',则该对象的主窗口为'''Transient Parent'''对应的主窗口;如果没有传递,与普通的'''QWidget'''确定主窗口的方法相同。


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''')寻找合适的父窗口:


5. **自动Fallback机制**:当标记的父窗口无效时,系统会自动为特定类型的窗口(如`Qt::Popup`、`Qt::ToolTip`、`Qt::Dialog`、`Qt::Tool`)寻找合适的父窗口:
*    优先使用当前有焦点的窗口
  - 优先使用当前有焦点的窗口
*    其次使用第一个可用的顶级窗口
  - 其次使用第一个可用的顶级窗口
*    确保窗口总能找到合适的父窗口
  - 确保窗口总能找到合适的父窗口


### 2.2 开发建议
==== 2.2 开发建议 ====
开发者需要理解HarmonyOS的主、子窗口概念,并在使用Qt时注意新创建的顶层'''Widget'''(无'''parent''')是否需要创建'''HarmonyOS'''的子窗口,如果需要,则需要调用 '''QOhosFunctions::tagWindowOrWidgetAsSubWindowOf()''' 函数,并指定其父窗口。


开发者需要理解HarmonyOS的主、子窗口概念,并在使用Qt时注意新创建的顶层Widget(无parent)是否需要创建HarmonyOS的子窗口,如果需要,则需要调用`QOhosFunctions::tagWindowOrWidgetAsSubWindowOf()`函数,并指定其父窗口。
以下几种情况可以简化处理:


**以下几种情况可以简化处理:**
* 1. 单实例应用:如果应用为单实例(即单主窗口),需要在创建子窗口时调用'''AsSubWindowOf'''标签。
* 2. 多实例应用:如果应用有多实例(即单进程多主窗口),但所有子窗口的弹出均是用户通过操作界面触发,则需要在创建子窗口时调用'''AsSubWindowOf'''标签,创建新的主窗口不需要特殊操作。
* 3. Transient Parent窗口:如果创建'''QDialog'''等支持'''Transient Parent'''的'''QWidget''',且传递了'''Transient Parent'''参数,则无需调用'''AsSubWindowOf'''标签,使用默认行为即可。


1. **单实例应用**:如果应用为单实例(即单主窗口),需要在创建子窗口时调用`AsSubWindowOf`标签。
=== 3. 接口说明 ===


2. **多实例应用**:如果应用有多实例(即单进程多主窗口),但所有子窗口的弹出均是用户通过操作界面触发,则需要在创建子窗口时调用`AsSubWindowOf`标签,创建新的主窗口不需要特殊操作。
==== 3.1 主要函数 ====
 
<syntaxhighlight lang="c++">
3. **Transient Parent窗口**:如果创建`QDialog`等支持`Transient Parent`的`QWidget`,且传递了`Transient Parent`参数,则无需调用`AsSubWindowOf`标签,使用默认行为即可。
 
## 3. 接口说明
 
### 3.1 主要函数
 
```cpp
static void QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(QObject *windowOrWidgetToTag, QWindow *mainWindow)
static void QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(QObject *windowOrWidgetToTag, QWindow *mainWindow)
```
</syntaxhighlight>该函数为窗口系统设置一个提示,使其不将给定的QWindow或QWidget窗口实例化为主窗口,而是将其实例化为主窗口的子窗口。


该函数为窗口系统设置一个提示,使其不将给定的QWindow或QWidget窗口实例化为主窗口,而是将其实例化为主窗口的子窗口。
'''参数说明''':


**参数说明:**
* '''windowOrWidgetToTag''':要标记为子窗口的QObject(必须是QWindow或QWidget)
- `windowOrWidgetToTag`:要标记为子窗口的QObject(必须是QWindow或QWidget)
* '''mainWindow''':指定的父主窗口
- `mainWindow`:指定的父主窗口


### 3.2 辅助函数
==== 3.2 辅助函数 ====
<syntaxhighlight lang="cpp">
// 标记窗口为主窗口


```cpp
// 标记窗口为主窗口
static void QOhosFunctions::tagWindowOrWidgetAsMainWindow(QObject *windowOrWidgetToTag, bool forceMainWindow = true)
static void QOhosFunctions::tagWindowOrWidgetAsMainWindow(QObject *windowOrWidgetToTag, bool forceMainWindow = true)


// 获取通过tagWindowOrWidgetAsSubWindowOf标记的父窗口指针。
// 获取通过tagWindowOrWidgetAsSubWindowOf标记的父窗口指针。
static QWindow *QOhosFunctions::getWindowOrWidgetAsSubWindowOfTagValue(QObject *targetWindowOrWidget)
static QWindow *QOhosFunctions::getWindowOrWidgetAsSubWindowOfTagValue(QObject *targetWindowOrWidget)
```
</syntaxhighlight>


## 4. 重要注意事项
=== 4. 重要注意事项 ===


> ⚠️ **警告:**
==== ⚠️ 警告: ====
> 1. 在调用标记函数之前调用`winId()`或`show()`会导致标签无效。
> 2. `mainWindow`必须是一个有效的`QWindow`指针。
> 3. 如果`mainWindow`指向一个有效的`QWindow`,但这个`QWindow`不是主窗口,Qt框架会启用自动fallback机制,尝试找到合适的主窗口作为所有者。如果找不到合适的主窗口,就会创建一个主窗口而不是子窗口。


## 5. 代码示例
# 在调用标记函数之前调 winId() 或 show() 会导致标签无效。
# mainWindow 必须是一个有效的 QWindow 指针。
# 如果 mainWindow 指向一个有效的QWindow,但这个QWindow不是主窗口,Qt框架会启用自动fallback机制,尝试找到合适的主窗口作为所有者。如果找不到合适的主窗口,就会创建一个主窗口而不是子窗口。


### 5.1 基本用法
=== 5. 代码示例 ===


```cpp
==== 5.1 基本用法 ====
<syntaxhighlight lang="c++">
#include <QtPlatformHeaders/QOhosFunctions>
#include <QtPlatformHeaders/QOhosFunctions>


// 创建一个子窗口,指定它属于当前这个主窗口
// 创建一个子窗口,指定它属于当前这个主窗口
m_subwindow = new QWidget();
m_subwindow = new QWidget();
QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(m_subwindow, this->windowHandle());
QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(m_subwindow, this->windowHandle());
m_subwindow->resize(800, 600);
m_subwindow->resize(800, 600);
m_subwindow->show();
m_subwindow->show();
```
</syntaxhighlight>


### 5.2 对话框示例
==== 5.2 对话框示例 ====
<syntaxhighlight lang="c++">
// 方式1:使用Transient Parent(推荐)


```cpp
QDialog *dialog = new QDialog(this);  // this是主窗口widget
// 方式1:使用Transient Parent(推荐)
 
QDialog *dialog = new QDialog(this); // this是主窗口widget
dialog->exec();  // 无需额外标记
dialog->exec(); // 无需额外标记


// 方式2:手动标记
// 方式2:手动标记
QDialog *dialog = new QDialog();
QDialog *dialog = new QDialog();
QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(dialog, this->windowHandle());
QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(dialog, this->windowHandle());
dialog->exec();
dialog->exec();
```
</syntaxhighlight>


### 5.3 多窗口应用示例
==== 5.3 多窗口应用示例 ====
<syntaxhighlight lang="c++">
class MainWindow : public QMainWindow


```cpp
class MainWindow : public QMainWindow
{
{
public:
public:
    void createSubWindow() {
        // 创建子窗口
        QWidget *subWindow = new QWidget();


        // 标记为当前主窗口的子窗口
    void createSubWindow() {
        QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(subWindow, this->windowHandle());
 
        // 创建子窗口
 
        QWidget *subWindow = new QWidget();
 
        // 标记为当前主窗口的子窗口
 
        QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(subWindow, this->windowHandle());
 
        subWindow->setWindowTitle("子窗口");
 
        subWindow->resize(400, 300);
 
        subWindow->show();
 
    }
 
    void createNewMainWindow() {


        subWindow->setWindowTitle("子窗口");
        // 创建新的主窗口(无需特殊标记)
        subWindow->resize(400, 300);
 
        subWindow->show();
        MainWindow *newMainWindow = new MainWindow();
    }
 
        newMainWindow->show();
 
    }


    void createNewMainWindow() {
        // 创建新的主窗口(无需特殊标记)
        MainWindow *newMainWindow = new MainWindow();
        newMainWindow->show();
    }
};
};
```
</syntaxhighlight>


## 6. 最佳实践
=== 6. 最佳实践 ===


1. **及时标记**:在调用`show()``winId()`之前完成窗口标记
* '''及时标记''':在调用 '''show'''() 或 '''winId'''() 之前完成窗口标记
2. **合理分类**:明确区分哪些窗口应该是主窗口,哪些应该是子窗口
* '''合理分类''':明确区分哪些窗口应该是主窗口,哪些应该是子窗口
3. **利用Transient Parent**:对于对话框等临时窗口,优先使用Qt原生的parent机制
* '''利用Transient Parent''':对于对话框等临时窗口,优先使用'''Qt'''原生的'''parent'''机制
4. **测试验证**:在HarmonyOS设备上验证窗口的层级关系和生命周期行为
* '''测试验证''':在'''HarmonyOS'''设备上验证窗口的层级关系和生命周期行为
5. **错误处理**:考虑父窗口无效时的fallback行为
* '''错误处理''':考虑父窗口无效时的'''fallback'''行为


## 7. 常见问题
=== 7. 常见问题 ===
'''Q: 为什么我的子窗口显示为主窗口?'''


### Q: 为什么我的子窗口显示为主窗口?
A: 可能的原因:
A: 可能的原因:
- 在调用`show()`之后才调用标记函数
- 传入的`mainWindow`参数无效
- 忘记调用标记函数


### Q: 子窗口可以脱离主窗口独立存在吗?
* 在调用 show() 之后才调用标记函数
* 传入的 mainWindow 参数无效
* 忘记调用标记函数
 
 
 
'''Q: 子窗口可以脱离主窗口独立存在吗?'''
 
A: 不可以。在HarmonyOS中,子窗口必须依附于主窗口,主窗口关闭时子窗口会自动关闭。
A: 不可以。在HarmonyOS中,子窗口必须依附于主窗口,主窗口关闭时子窗口会自动关闭。


### Q: 如何确认窗口的父子关系是否正确?
A: 可以使用`QOhosFunctions::getWindowOrWidgetAsSubWindowOfTagValue()`函数查询窗口的标记状态。


---


*本文档基于Qt for HarmonyOS的实际实现编写,如有疑问请参考相关源码或联系技术支持。*
'''Q: 如何确认窗口的父子关系是否正确?'''
 
A: 可以使用 QOhosFunctions::getWindowOrWidgetAsSubWindowOfTagValue() 函数查询窗口的标记状态。

Latest revision as of 05:53, 29 July 2025

中文 English

Qt在HarmonyOS上的主窗口与子窗口开发指南

1. 主窗口与子窗口概念

1.1 HarmonyOS的主窗口与子窗口

主窗口:代表一个"任务"的窗口,有Dock栏图标和任务图标(Alt+Tab切换任务时),一般有独立的生命周期,可最大化、最小化、窗口化

子窗口:任务产生的某个子功能的交互界面,是独立窗口可在主窗口以外显示,但无Dock栏图标和任务图标,必须从属于一个主窗口,主窗口关闭时会联动关闭,主窗口最小化时联动隐藏,主窗口恢复显示时联动恢复显示。一般在鸿蒙上对应各种弹窗和对话框。

1.2 Qt Widget或窗口创建

Qt创建Widget、Window时并无类似HarmonyOS的主、子关系的概念(部分widget如QDialog有Transient Parent概念,该概念与HarmonyOS主、子窗口接近)


示例

// 独立窗口
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 ParentQWidget对象(如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 ParentQWidget,且传递了Transient Parent参数,则无需调用AsSubWindowOf标签,使用默认行为即可。

3. 接口说明

3.1 主要函数

static void QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(QObject *windowOrWidgetToTag, QWindow *mainWindow)

该函数为窗口系统设置一个提示,使其不将给定的QWindow或QWidget窗口实例化为主窗口,而是将其实例化为主窗口的子窗口。

参数说明

  • windowOrWidgetToTag:要标记为子窗口的QObject(必须是QWindow或QWidget)
  • mainWindow:指定的父主窗口

3.2 辅助函数

// 标记窗口为主窗口

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 基本用法

#include <QtPlatformHeaders/QOhosFunctions>

// 创建一个子窗口,指定它属于当前这个主窗口

m_subwindow = new QWidget();

QOhosFunctions::tagWindowOrWidgetAsSubWindowOf(m_subwindow, this->windowHandle());

m_subwindow->resize(800, 600);

m_subwindow->show();

5.2 对话框示例

// 方式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 多窗口应用示例

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. 最佳实践

  • 及时标记:在调用 show() 或 winId() 之前完成窗口标记
  • 合理分类:明确区分哪些窗口应该是主窗口,哪些应该是子窗口
  • 利用Transient Parent:对于对话框等临时窗口,优先使用Qt原生的parent机制
  • 测试验证:在HarmonyOS设备上验证窗口的层级关系和生命周期行为
  • 错误处理:考虑父窗口无效时的fallback行为

7. 常见问题

Q: 为什么我的子窗口显示为主窗口?

A: 可能的原因:

  • 在调用 show() 之后才调用标记函数
  • 传入的 mainWindow 参数无效
  • 忘记调用标记函数


Q: 子窗口可以脱离主窗口独立存在吗?

A: 不可以。在HarmonyOS中,子窗口必须依附于主窗口,主窗口关闭时子窗口会自动关闭。


Q: 如何确认窗口的父子关系是否正确?

A: 可以使用 QOhosFunctions::getWindowOrWidgetAsSubWindowOfTagValue() 函数查询窗口的标记状态。