3. 窗口与屏幕管理

在 Qt Quick 多屏交互工程中,窗口与屏幕管理是连接“界面逻辑”和“物理显示设备”的桥梁。很多初学者只关注 QML 界面本身,却忽略了窗口究竟显示在哪块屏幕上、以什么尺寸显示、系统缩放比例是多少。一旦进入双屏、三屏或高 DPI 场景,这些问题会直接导致界面模糊、窗口错位、坐标偏移甚至内容溢出。本节围绕 Window、Screen、ScreenInfo 三个核心对象,讲清屏幕分辨率、DPI 缩放和多屏枚举的工程方法。

一、核心对象与职责划分

Qt Quick 中与屏幕相关的对象主要有三类,它们分别处于不同层级:

  • Window:窗口对象,代表一个顶层可视窗口。它拥有 x、y、width、height、visibility、screen 等属性,决定窗口在哪个屏幕上、以多大尺寸呈现。
  • Screen:屏幕对象,描述一块物理或逻辑显示设备。它提供 name、width、height、devicePixelRatio、logicalDotsPerInch、geometry、availableGeometry 等关键信息。
  • ScreenInfo:通常指 Qt 提供的屏幕信息辅助接口或自定义封装,用于集中枚举、查询和监听多屏变化,避免在业务代码中散落大量 Screen 查询逻辑。

理解三者的关系,可以用一句话概括:Window 是“显示载体”,Screen 是“显示目标”,ScreenInfo 是“屏幕信息管理器”。Window 通过 screen 属性绑定到某块 Screen,而 ScreenInfo 负责把系统中所有 Screen 汇总起来供业务层使用。

Window / Screen / ScreenInfo 多屏管理关系 Window x / y / width / height visibility / screen flags / color ScreenInfo 枚举所有屏幕 监听 screenAdded / Removed 提供统一查询接口 Screen name / width / height devicePixelRatio geometry / availableGeometry 查询 枚举 多屏枚举与窗口分配示意 Screen 0 1920×1080 @1.0x Screen 1 2560×1440 @1.5x Screen 2 3840×2160 @2.0x Window 通过 screen 属性绑定到目标 Screen,ScreenInfo 负责统一枚举与监听

二、屏幕分辨率与 DPI 缩放

屏幕分辨率分为物理分辨率和逻辑分辨率。物理分辨率是面板真实像素数,例如 3840×2160;逻辑分辨率是应用看到的尺寸,等于物理分辨率除以 devicePixelRatio。在 2.0 倍缩放下,3840×2160 的屏幕在 QML 中表现为 1920×1080 的逻辑尺寸。

Qt Quick 中与 DPI 相关的关键属性包括:

属性 含义 典型用途
devicePixelRatio 物理像素与逻辑像素的比值 判断是否高 DPI,决定图片资源倍率
logicalDotsPerInch 逻辑每英寸点数 计算物理尺寸、适配打印或大屏
width / height 逻辑分辨率 窗口布局、全屏尺寸计算
geometry 屏幕在虚拟桌面中的矩形区域 多屏坐标换算、窗口定位
availableGeometry 扣除任务栏后的可用区域 避免窗口被任务栏遮挡
在高 DPI 屏幕上,如果只按逻辑像素布局而不提供 @2x 资源,图片会被放大导致模糊。推荐使用 Qt 资源系统配合 @2x 命名规则,或使用矢量图 SVG。

三、多屏枚举方法

在 C++ 侧,可以通过 QGuiApplication::screens() 获取所有屏幕列表;在 QML 侧,可以通过 Qt.application.screens 访问。更工程化的做法是封装一个 ScreenInfo 单例,集中暴露屏幕数量、主屏、当前屏和屏幕列表。

// C++ 侧枚举屏幕
const auto screens = QGuiApplication::screens();
for (QScreen *screen : screens) {
    qDebug() << "name:" << screen->name()
           << "size:" << screen->size()
           << "dpr:" << screen->devicePixelRatio()
           << "geometry:" << screen->geometry();
}

// QML 侧枚举屏幕
Component.onCompleted: {
    for (var i = 0; i < Qt.application.screens.length; i++) {
        var s = Qt.application.screens[i];
        console.log(s.name, s.width, s.height, s.devicePixelRatio)
    }
}

枚举时要注意:屏幕顺序并不等于物理排列顺序,必须依赖 geometry 的 x、y 坐标判断左右或上下关系。例如主屏 geometry 为 (0,0,1920,1080),副屏为 (1920,0,2560,1440),说明副屏位于主屏右侧。

四、窗口跨屏定位案例

假设需要把窗口移动到第二块屏幕并全屏显示,可以这样实现:

Window {
    id: root
    visible: true

    function moveToScreen(index) {
        var screens = Qt.application.screens
        if (index < 0 || index >= screens.length) return
        var target = screens[index]
        root.screen = target
        root.x = target.geometry.x
        root.y = target.geometry.y
        root.width = target.geometry.width
        root.height = target.geometry.height
        root.visibility = Window.FullScreen
    }
}

这段代码体现了多屏窗口管理的核心步骤:选择目标屏幕 → 设置 screen 属性 → 按 geometry 定位 → 设置尺寸 → 切换可见性状态。如果跳过 screen 属性直接设置 x、y,窗口可能仍停留在原屏幕的坐标系中,导致定位错误。

五、常见误区与复盘

  • 误区一:把物理分辨率当逻辑分辨率使用。在 2.0 倍缩放下,直接设置 width 为 3840 会导致窗口超出屏幕。应使用 Screen.width 获取逻辑宽度。
  • 误区二:忽略 availableGeometry。全屏或最大化时若使用 geometry,窗口可能被任务栏遮挡。应优先使用 availableGeometry。
  • 误区三:硬编码屏幕索引。不同机器屏幕顺序不同,应通过 name 或 geometry 特征匹配目标屏幕。
  • 误区四:不监听屏幕变化。热插拔显示器后,屏幕列表会变化,必须监听 screenAdded、screenRemoved 并刷新界面布局。
在多屏工程中,窗口坐标是虚拟桌面坐标,不是单屏坐标。跨屏移动窗口时,必须基于目标屏幕的 geometry.x 和 geometry.y 计算,否则窗口会“跳”到错误位置。

总结来说,Window 负责承载界面,Screen 提供屏幕参数,ScreenInfo 负责统一枚举与监听。掌握分辨率、DPI 缩放和多屏枚举方法,才能让 Qt Quick 应用在单屏、双屏和高 DPI 环境下都保持正确的布局与清晰的显示效果。