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 汇总起来供业务层使用。
二、屏幕分辨率与 DPI 缩放
屏幕分辨率分为物理分辨率和逻辑分辨率。物理分辨率是面板真实像素数,例如 3840×2160;逻辑分辨率是应用看到的尺寸,等于物理分辨率除以 devicePixelRatio。在 2.0 倍缩放下,3840×2160 的屏幕在 QML 中表现为 1920×1080 的逻辑尺寸。
Qt Quick 中与 DPI 相关的关键属性包括:
| 属性 | 含义 | 典型用途 |
|---|---|---|
| devicePixelRatio | 物理像素与逻辑像素的比值 | 判断是否高 DPI,决定图片资源倍率 |
| logicalDotsPerInch | 逻辑每英寸点数 | 计算物理尺寸、适配打印或大屏 |
| width / height | 逻辑分辨率 | 窗口布局、全屏尺寸计算 |
| geometry | 屏幕在虚拟桌面中的矩形区域 | 多屏坐标换算、窗口定位 |
| availableGeometry | 扣除任务栏后的可用区域 | 避免窗口被任务栏遮挡 |
@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 并刷新界面布局。
总结来说,Window 负责承载界面,Screen 提供屏幕参数,ScreenInfo 负责统一枚举与监听。掌握分辨率、DPI 缩放和多屏枚举方法,才能让 Qt Quick 应用在单屏、双屏和高 DPI 环境下都保持正确的布局与清晰的显示效果。