fix(macljqCtrl): add CropToScreen helper + crop-origin coord hints (#614)
On macOS the screenshot path lacked a counterpart to win32's ClientToScreen. Detecting a target inside a cropped GrabScreen(bbox) image and feeding the in-image (px,py) straight to Click silently mis-clicks because the crop-origin offset is dropped. - add CropToScreen(bbox, x, y): in-crop coords -> absolute physical coords (pure crop-origin add, no scaling) - GrabScreen docstring: warn about the crop-origin trap and not to hand-roll screencapture -R (it takes logical points) - Click 0%-pixel-change warning now names the common causes - ljqCtrl_sop.md: add macOS subsection mirroring the win32 trap note
This commit is contained in:
1 parent
1f2e38c6e3
commit
b006a84b7c
2 files changed
+17
-3
No files matched your search
@@ -33,12 +33,14 @@ ljqCtrl.Click(ox + (bbox[0]+bbox[2])//2, oy + (bbox[1]+bbox[3])//2)
|
||||
```
|
||||
禁止全屏ImageGrab(必须针对窗口),所有逻辑坐标都要转物理。
|
||||
|
||||
**macOS (`macljqCtrl`)**:`GrabScreen(bbox)` 区域截图后,图内点转屏幕物理坐标用 `CropToScreen(bbox, px, py)`,别手搓 `screencapture -R`(它吃逻辑点,会点歪)。
|
||||
|
||||
## 4. 避坑指南
|
||||
- **⚠️ 一律使用物理坐标**:传给 ljqCtrl.Click/SetCursorPos 的坐标必须是物理坐标(=截图像素坐标)。禁止传入逻辑坐标。
|
||||
- **物理验证**:模拟操作前必须确保窗口已通过 `activate()` 置于前台。
|
||||
- **坐标对齐**: 物理坐标 = 截图坐标;ljqCtrl 自动处理 DPI 换算,禁止手动重复计算。
|
||||
- **⚠️ 窗口坐标转换陷阱**:使用 `win32gui.GetWindowRect(hwnd)` 获取的矩形包含标题栏和边框,而截图内容是客户区。点击截图内元素时,必须用 `win32gui.ClientToScreen(hwnd, (0, 0))` 获取客户区原点的屏幕坐标,再加上截图内坐标。禁止直接用 GetWindowRect 左上角 + 截图坐标。**同理禁止 `DwmGetWindowAttribute(hwnd, 9, ...)` 取窗口矩形替代 ClientToScreen,它也包含标题栏/阴影。**
|
||||
- **⚠️ Click 后 0% 像素变化 = 点歪了**:ljqCtrl.Click 会报告像素变化百分比。若为 0% 或接近 0%,说明点击落在了错误位置(坐标计算有误),必须立即停下来诊断坐标转换逻辑,禁止盲目重试。常见原因:用了错误的窗口原点API、忘记 `/dpi_scale`、混淆了客户区与窗口矩形。
|
||||
- **⚠️ Click 后 0% 像素变化 = 点歪了**:ljqCtrl.Click 会报告像素变化百分比。若为 0% 或接近 0%,说明点击落在了错误位置(坐标计算有误),必须立即停下来诊断坐标转换逻辑,禁止盲目重试。常见原因:用了错误的窗口原点API、忘记 `/dpi_scale`、混淆了客户区与窗口矩形。macOS 上多为忘加裁剪原点(应走 `CropToScreen`)。
|
||||
- **⚠️ win32 DPI 坐标陷阱**:未调用 `SetProcessDPIAware()` 时,`GetWindowRect/ClientToScreen/GetClientRect` 等拿到的窗口/客户区坐标通常是**逻辑坐标**,必须进行换算!
|
||||
- **文本输入**:ljqCtrl 无 TypeText/SendKeys。向输入框键入文本:先点击/三击选中字段,再 `pyperclip.copy('文本'); ljqCtrl.Press('ctrl+v')`。
|
||||
|
||||
|
||||
+14
-2
@@ -209,7 +209,9 @@ def Paste(text):
|
||||
|
||||
# ---------- 截图 (screencapture, 输出物理像素) ----------
|
||||
def GrabScreen(bbox=None):
|
||||
"""全屏或区域截图 -> PIL Image。bbox=(l,u,r,b) 物理像素坐标。"""
|
||||
"""全屏或区域截图 -> PIL Image。bbox=(l,u,r,b) 物理像素坐标。
|
||||
传 bbox 后图内坐标相对裁剪原点; 转屏幕绝对坐标用 CropToScreen, 勿手搓 screencapture -R。
|
||||
"""
|
||||
fd, fn = tempfile.mkstemp(suffix='.png'); os.close(fd)
|
||||
try:
|
||||
if bbox:
|
||||
@@ -231,6 +233,14 @@ def ScreenCapAt(x, y, r=100):
|
||||
"""物理坐标(x,y)为中心±r 截图 -> PIL Image。"""
|
||||
return GrabScreen((x-r, y-r, x+r, y+r))
|
||||
|
||||
def CropToScreen(bbox, x, y=None):
|
||||
"""裁剪图内坐标 -> 屏幕绝对物理坐标。bbox=GrabScreen 用的 (l,u,r,b) 物理像素。
|
||||
(x,y)=在 GrabScreen(bbox) 返回图内找到的点。返回可直接喂给 Click 的 (X,Y)。
|
||||
macOS 版的 ClientToScreen: 纯加裁剪原点偏移, 不做缩放(裁剪图与 bbox 同物理像素)。"""
|
||||
if y is None and isinstance(x, (tuple, list)):
|
||||
x, y = x[0], x[1]
|
||||
return int(bbox[0] + x), int(bbox[1] + y)
|
||||
|
||||
def GrabWindow(win):
|
||||
"""窗口截图 -> PIL Image。win=窗口号(int) 或 标题/应用名子串(str)。物理像素。"""
|
||||
if isinstance(win, str):
|
||||
@@ -387,7 +397,9 @@ def Click(x, y=None, check=True, r=60):
|
||||
if verbose_click:
|
||||
print(f'Click({x},{y}) pixel change: {diff:.1f}%')
|
||||
if diff < 0.5:
|
||||
print(f'[WARN] Click({x},{y}) 像素变化≈0%, 可能点歪了, 请诊断坐标!')
|
||||
print(f'[WARN] Click({x},{y}) 像素变化≈0%, 可能点歪了, 请诊断坐标! '
|
||||
'常见错因: 用了裁剪图内坐标却没加裁剪原点(用 CropToScreen), '
|
||||
'或对已是物理像素的坐标又做了 *dpi_scale 换算。')
|
||||
return diff, Image.fromarray(after)
|
||||
return None
|
||||
|
||||
|
||||
Reference in new issue
Block a user