1. 直观对比实验
假设我们要在800x600像素的窗口上画一条从(0,0)到(1000,500)的对角线:
不使用 setWindow(物理坐标模式)
// 直接使用像素坐标
painter.drawLine(0, 0, 1000, 500);
问题:
- 1000 > 窗口宽度(800),500 > 窗口高度(600) → 线会超出窗口看不见
- 必须手动计算缩放比例(非常麻烦)
使用 setWindow(逻辑坐标模式)
painter.setWindow(0, 0, 1000, 500); // 设定逻辑坐标系
painter.drawLine(0, 0, 1000, 500); // 自动适配到窗口大小
效果:
- 无论窗口多大,Qt会自动将(1000,500)映射到当前窗口右下角
- 画布内容永远完整显示且保持比例
2. 原理图解
逻辑坐标系 (setWindow) 物理坐标系(实际窗口)
(0,0)───────┐ (0,0)───────┐
│ 虚拟画布 │ │ 实际显示 │
│ 1000x500 │ ───自动映射──> │ 800x600 │
└─────────(1000,500) └─────────(800,600)
3. 为什么要用逻辑坐标?
场景1:数据可视化
假设您有:
- 温度数据范围:X轴时间(0-24小时),Y轴温度(0-50℃)
painter.setWindow(0, 0, 24, 50); // 设置与数据匹配的坐标系
painter.drawLine(0, 20, 24, 35); // 直接使用真实数据值绘制!
无需关心:
- 窗口实际大小是800x600还是400x300
- 像素换算比例
场景2:多分辨率适配
// 同一段代码在不同设备上:
pc(1920x1080): 自动放大显示
手机(400x800): 自动缩小显示
内容始终保持相同逻辑结构
4. 与setViewport的配合
// 将逻辑坐标的(0,0,1000,500)映射到窗口中央的600x400区域
painter.setViewport(100, 100, 600, 400);
painter.setWindow(0, 0, 1000, 500);
效果:
- 画布内容只会出现在指定的600x400区域内
- 相当于创建了一个"子画布"
5. 实际案例演示
绘制一个永远居中的钟表:
void paintEvent(QPaintEvent*){
QPainter painter(this);
// 设置逻辑坐标系:(-100,-100)到(100,100)
painter.setWindow(-100, -100, 200, 200);
// 绘制表盘(始终居中,自动适应窗口大小)
painter.drawEllipse(QPoint(0,0), 90, 90);
// 绘制时针(指向3点钟方向)
painter.drawLine(0, 0, 50, 0);
}
特点:
- 无论窗口如何缩放,钟表永远居中
- 图形元素比例保持不变
6. 关键总结
场景 | 使用物理坐标 | 使用逻辑坐标(setWindow) |
---|---|---|
窗口缩放时 | 需要手动重新计算所有坐标 | 自动适应,代码无需修改 |
数据可视化 | 需要数据值→像素的转换公式 | 直接使用原始数据值绘制 |
多设备适配 | 需要为每个分辨率写不同代码 | 一套代码通用所有分辨率 |
图形比例保持 | 易变形 | 始终保持原始比例 |
7. 什么时候不需要setWindow?
- 绘制UI控件(按钮/文字等需要精确定位像素时)
- 处理鼠标交互(需要转换回物理坐标计算)
- 性能敏感场景(逻辑坐标有额外计算开销)
8. 动手实验建议
尝试修改这段代码观察效果:
// 实验1:修改setWindow参数
painter.setWindow(0, 0, 2000, 1000); // 内容会缩小一半
painter.setWindow(0, 0, 500, 500); // 内容会放大且变形
// 实验2:注释掉setWindow
// painter.setWindow(0, 0, 1000, 500);
// 观察图形如何超出窗口边界
通过这种方式,您会直观感受到逻辑坐标系如何像"虚拟摄像机"一样控制图形的显示范围与比例。