VirtualDesktop 1.2.0__tar.gz → 1.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/PKG-INFO +33 -17
  2. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/README.md +32 -16
  3. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/__init__.py +1 -1
  4. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/PKG-INFO +33 -17
  5. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/pyproject.toml +1 -1
  6. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/LICENSE +0 -0
  7. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/__main__.py +0 -0
  8. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_bundle.py +0 -0
  9. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_compat.py +0 -0
  10. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_desktop_runtime.py +0 -0
  11. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_devices.py +0 -0
  12. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_download.py +0 -0
  13. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_helper.py +0 -0
  14. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_log.py +0 -0
  15. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_process.py +0 -0
  16. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_sigcheck.ps1 +0 -0
  17. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_sigcheck.py +0 -0
  18. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_winapi.py +0 -0
  19. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/agent.py +0 -0
  20. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/capture.py +0 -0
  21. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/cli.py +0 -0
  22. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/config.py +0 -0
  23. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/desktop.py +0 -0
  24. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/desktop_capture.py +0 -0
  25. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/desktop_session.py +0 -0
  26. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/display_setup.py +0 -0
  27. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/driver.py +0 -0
  28. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/driver_manifest.json +0 -0
  29. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/errors.py +0 -0
  30. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/manifest.py +0 -0
  31. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/session.py +0 -0
  32. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/window.py +0 -0
  33. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/workspace.py +0 -0
  34. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/SOURCES.txt +0 -0
  35. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/dependency_links.txt +0 -0
  36. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/entry_points.txt +0 -0
  37. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/requires.txt +0 -0
  38. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/top_level.txt +0 -0
  39. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/setup.cfg +0 -0
  40. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_accelerators.py +0 -0
  41. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_agent.py +0 -0
  42. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_artifact_regression.py +0 -0
  43. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_capture.py +0 -0
  44. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_cli.py +0 -0
  45. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_compat.py +0 -0
  46. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_config_and_winapi.py +0 -0
  47. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_desktop_isolation.py +0 -0
  48. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_destructive_lifecycle.py +0 -0
  49. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_display_setup.py +0 -0
  50. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_download.py +0 -0
  51. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_driver.py +0 -0
  52. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_hide_modes.py +0 -0
  53. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_integration_windows.py +0 -0
  54. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_manifest.py +0 -0
  55. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_no_dialogs.py +0 -0
  56. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_packaging.py +0 -0
  57. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_process.py +0 -0
  58. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_virtual_desktop.py +0 -0
  59. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_workspace.py +0 -0
  60. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_workspace_instances.py +0 -0
  61. {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_workspace_move.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: VirtualDesktop
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Multi-window virtual-display workspaces on Windows: one pip install bootstraps the virtual display driver, hosts programs on a virtual or private desktop, keeps them off the physical screens, and hands out real window handles for automation and PrintWindow capture.
5
5
  Author: VirtualDesktop contributors
6
6
  License: MIT License
@@ -77,18 +77,33 @@ with VirtualWorkspace(VirtualWorkspace.SHARED) as space:
77
77
 
78
78
  整个库只建立在 Windows 的**两套机制**上。三个类只是它们的不同封装,搞清这点其余都好懂。
79
79
 
80
- | 机制 | 是什么 | 需要什么 | 外部进程能控制里面的窗口吗 |
81
- |---|---|---|---|
82
- | **虚拟显示器** | 一块额外的屏幕(`\\.\DISPLAYn`)。窗口搬上去,但**仍在你的交互桌面上**,只是画在你看不到的那块屏 | 装一次驱动(1 次 UAC) | **能**。窗口是普通窗口,句柄是真的,pywinauto / UIA / pyautogui 直接用 |
83
- | **私有桌面** | 用 `CreateDesktopW` 建的独立窗口命名空间。与你的桌面完全隔离 | 什么都不需要 | **不能**。跨桌面拿不到窗口,必须在里面起代理进程 |
80
+ | 机制 | 是什么 | 需要什么 |
81
+ |---|---|---|
82
+ | **留在交互桌面** | 窗口仍是普通窗口,只把位置挪到虚拟显示器上(`\\.\DISPLAYn`),或挪到所有显示器之外的坐标区 | 虚拟显示器要装一次驱动(1 次 UAC);挪出坐标区什么都不需要 |
83
+ | **私有桌面** | 用 `CreateDesktopW` 建的独立窗口命名空间。与你的桌面完全隔离 | 什么都不需要 |
84
+
85
+ **关键区别只有一条**:窗口是否还留在交互桌面上。由此决定两件事——外部进程能不能直接控制它、能不能截图。
86
+
87
+ | 模式 | 窗口在交互桌面 | 外部库能直接控制 | **能截图** | 需要驱动 |
88
+ |---|---|---|---|---|
89
+ | `VirtualWorkspace(SHARED)` + `virtual_screen` | 是 | 能 | **能** | 是 |
90
+ | `VirtualWorkspace(SHARED)` + `offscreen` | 是 | 能 | **能** | 否 |
91
+ | `OffscreenSession` + `virtual_screen` | 是 | 能 | **能** | 是 |
92
+ | `OffscreenSession` + `offscreen` | 是 | 能 | **能** | 否 |
93
+ | `VirtualWorkspace("名字")` | 否 | 不能 | **不能** | 否 |
94
+ | `VirtualDesktop` | 否 | 不能 | **不能** | 否 |
95
+
96
+ 也就是说,**两种隐藏方式(`virtual_screen` / `offscreen`)都能截图**,只是把窗口分别放到"另一块屏"
97
+ 和"屏幕之外的坐标区"。唯一截不到图的是**私有桌面**——那里的窗口没有被系统合成,没有任何后端能
98
+ 读到像素(详见「已知限制」)。
84
99
 
85
100
  三个类与机制的关系:
86
101
 
87
102
  | 类 | 用途 | 底层机制 |
88
103
  |---|---|---|
89
- | `VirtualWorkspace(SHARED)` | **多窗口**工作区,外部可自动化 | 虚拟显示器 |
104
+ | `VirtualWorkspace(SHARED)` | **多窗口**工作区,外部可自动化 | 留在交互桌面 |
90
105
  | `VirtualWorkspace("名字")` | **多窗口**工作区,强隔离 | 私有桌面 |
91
- | `OffscreenSession` | **单窗口**:挂一个窗口、截图、还原 | 虚拟显示器 |
106
+ | `OffscreenSession` | **单窗口**:挂一个窗口、截图、还原 | 留在交互桌面 |
92
107
  | `VirtualDesktop` | 私有桌面**原语**(`VirtualWorkspace("名字")` 内部就用它) | 私有桌面 |
93
108
  | `configure_virtual_display()` | 设置虚拟显示器的分辨率 / DPI | (作用于显示器本身) |
94
109
 
@@ -96,14 +111,11 @@ with VirtualWorkspace(VirtualWorkspace.SHARED) as space:
96
111
 
97
112
  ```
98
113
  只处理一个窗口、要截图 → OffscreenSession
99
- 要同时跑多个程序、外部要控制 → VirtualWorkspace(SHARED)
114
+ 要同时跑多个程序、外部要控制、要截图 → VirtualWorkspace(SHARED)
100
115
  要多实例强隔离、不需要截图 → VirtualWorkspace("名字")
101
116
  要自己管理桌面、不需要工作区封装 → VirtualDesktop
102
117
  ```
103
118
 
104
- > 截图只能走**虚拟显示器**那一侧。私有桌面里的窗口没有被系统合成,没有任何后端能读到像素
105
- > (详见「已知限制」)。
106
-
107
119
  ---
108
120
 
109
121
  # 二、使用说明
@@ -111,12 +123,16 @@ with VirtualWorkspace(VirtualWorkspace.SHARED) as space:
111
123
  ## 安装
112
124
 
113
125
  ```bash
114
- pip install VirtualDesktop
126
+ pip install VirtualDesktop # pip
127
+ uv add VirtualDesktop # uv(写入 pyproject.toml 并锁定)
115
128
  ```
116
129
 
117
130
  运行时依赖只有 **Pillow**。需要 **Windows 10 1809 (build 17763) 或更高**(更低版本没有 IddCx
118
131
  框架,装不了虚拟显示器)。不装驱动也能用私有桌面模式。
119
132
 
133
+ > 国内镜像(如清华源)同步新版本可能滞后;若 `uv add` / `pip install` 找不到最新版,加
134
+ > `--default-index https://pypi.org/simple`(uv)或 `-i https://pypi.org/simple`(pip)即可。
135
+
120
136
  ## 多窗口工作区
121
137
 
122
138
  `desktop` 是**必填参数**,因为它决定窗口能不能被外部进程控制,这不该由库替你猜:
@@ -265,8 +281,8 @@ with VirtualWorkspace("lab") as space, space.agent() as agent:
265
281
  `exec` 是逃生舱:里面的代码能 `import VirtualDesktop`、能用 `list_windows()`、`CaptureEngine`,
266
282
  也能用你自己装的任何库(包括 pywinauto)。代理本身**只依赖 ctypes**,不需要额外安装。
267
283
 
268
- > `agent.screenshot()` 会**明确报错**而不是产出空白图:私有桌面的窗口没有被系统合成,没有任何
269
- > 截图后端能读到像素。要截图请用 `VirtualWorkspace(SHARED)`。
284
+ > 代理**不能截图**:私有桌面的窗口没有被系统合成,`agent.screenshot()` 会明确报错。需要截图就用
285
+ > `VirtualWorkspace(SHARED)`。
270
286
 
271
287
  ## 用自动化库控制窗口
272
288
 
@@ -327,7 +343,7 @@ with VirtualWorkspace("lab-a") as space:
327
343
  | `children()` | 可用,需 `visible_only=False` | 私有桌面窗口不带 `WS_VISIBLE` |
328
344
  | `is_visible()` | 返回 `False` | 桌面未被显示 |
329
345
  | `click()` / `click_input()` | 不可用 | 前者做可见性前置检查,后者需要活动桌面移动光标 |
330
- | 点击控件 | 发 `BM_CLICK` 消息 | `agent.click()` 内部就是这么做的 |
346
+ | 点击控件 | 发 `BM_CLICK` 消息 | 用 `agent.click()` 即可 |
331
347
 
332
348
  > pyautogui 这类依赖真实鼠标/键盘的库只能用于共享工作区——私有桌面上没有可移动的指针。
333
349
 
@@ -462,8 +478,8 @@ set VIRTUALDESKTOP_GITHUB_MIRRORS=https://gh-proxy.com/,https://internal.example
462
478
 
463
479
  ## 已知限制
464
480
 
465
- - **私有桌面里的窗口无法截图**:没有被系统合成,没有任何后端能读到像素;`agent.screenshot()`
466
- 会明确报错。需要截图就用共享工作区。
481
+ - **私有桌面里的窗口无法截图**:两种隐藏方式(`virtual_screen` / `offscreen`)都支持截图,唯独
482
+ 私有桌面不行——它的窗口没有被系统合成,没有任何后端能读到像素,`agent.screenshot()` 会明确报错。
467
483
  - **窗口无法在私有桌面之间搬运**:Windows 不允许跨桌面移动窗口。
468
484
  - 部分应用(UWP、硬件叠加层、DRM 受保护内容)任何离屏方式都抓不到——系统限制。
469
485
  - 自绘界面的应用(如微信 3.9.x)没有子窗口,UI Automation 只暴露顶层 `Pane`,组件粒度只能到
@@ -21,18 +21,33 @@ with VirtualWorkspace(VirtualWorkspace.SHARED) as space:
21
21
 
22
22
  整个库只建立在 Windows 的**两套机制**上。三个类只是它们的不同封装,搞清这点其余都好懂。
23
23
 
24
- | 机制 | 是什么 | 需要什么 | 外部进程能控制里面的窗口吗 |
25
- |---|---|---|---|
26
- | **虚拟显示器** | 一块额外的屏幕(`\\.\DISPLAYn`)。窗口搬上去,但**仍在你的交互桌面上**,只是画在你看不到的那块屏 | 装一次驱动(1 次 UAC) | **能**。窗口是普通窗口,句柄是真的,pywinauto / UIA / pyautogui 直接用 |
27
- | **私有桌面** | 用 `CreateDesktopW` 建的独立窗口命名空间。与你的桌面完全隔离 | 什么都不需要 | **不能**。跨桌面拿不到窗口,必须在里面起代理进程 |
24
+ | 机制 | 是什么 | 需要什么 |
25
+ |---|---|---|
26
+ | **留在交互桌面** | 窗口仍是普通窗口,只把位置挪到虚拟显示器上(`\\.\DISPLAYn`),或挪到所有显示器之外的坐标区 | 虚拟显示器要装一次驱动(1 次 UAC);挪出坐标区什么都不需要 |
27
+ | **私有桌面** | 用 `CreateDesktopW` 建的独立窗口命名空间。与你的桌面完全隔离 | 什么都不需要 |
28
+
29
+ **关键区别只有一条**:窗口是否还留在交互桌面上。由此决定两件事——外部进程能不能直接控制它、能不能截图。
30
+
31
+ | 模式 | 窗口在交互桌面 | 外部库能直接控制 | **能截图** | 需要驱动 |
32
+ |---|---|---|---|---|
33
+ | `VirtualWorkspace(SHARED)` + `virtual_screen` | 是 | 能 | **能** | 是 |
34
+ | `VirtualWorkspace(SHARED)` + `offscreen` | 是 | 能 | **能** | 否 |
35
+ | `OffscreenSession` + `virtual_screen` | 是 | 能 | **能** | 是 |
36
+ | `OffscreenSession` + `offscreen` | 是 | 能 | **能** | 否 |
37
+ | `VirtualWorkspace("名字")` | 否 | 不能 | **不能** | 否 |
38
+ | `VirtualDesktop` | 否 | 不能 | **不能** | 否 |
39
+
40
+ 也就是说,**两种隐藏方式(`virtual_screen` / `offscreen`)都能截图**,只是把窗口分别放到"另一块屏"
41
+ 和"屏幕之外的坐标区"。唯一截不到图的是**私有桌面**——那里的窗口没有被系统合成,没有任何后端能
42
+ 读到像素(详见「已知限制」)。
28
43
 
29
44
  三个类与机制的关系:
30
45
 
31
46
  | 类 | 用途 | 底层机制 |
32
47
  |---|---|---|
33
- | `VirtualWorkspace(SHARED)` | **多窗口**工作区,外部可自动化 | 虚拟显示器 |
48
+ | `VirtualWorkspace(SHARED)` | **多窗口**工作区,外部可自动化 | 留在交互桌面 |
34
49
  | `VirtualWorkspace("名字")` | **多窗口**工作区,强隔离 | 私有桌面 |
35
- | `OffscreenSession` | **单窗口**:挂一个窗口、截图、还原 | 虚拟显示器 |
50
+ | `OffscreenSession` | **单窗口**:挂一个窗口、截图、还原 | 留在交互桌面 |
36
51
  | `VirtualDesktop` | 私有桌面**原语**(`VirtualWorkspace("名字")` 内部就用它) | 私有桌面 |
37
52
  | `configure_virtual_display()` | 设置虚拟显示器的分辨率 / DPI | (作用于显示器本身) |
38
53
 
@@ -40,14 +55,11 @@ with VirtualWorkspace(VirtualWorkspace.SHARED) as space:
40
55
 
41
56
  ```
42
57
  只处理一个窗口、要截图 → OffscreenSession
43
- 要同时跑多个程序、外部要控制 → VirtualWorkspace(SHARED)
58
+ 要同时跑多个程序、外部要控制、要截图 → VirtualWorkspace(SHARED)
44
59
  要多实例强隔离、不需要截图 → VirtualWorkspace("名字")
45
60
  要自己管理桌面、不需要工作区封装 → VirtualDesktop
46
61
  ```
47
62
 
48
- > 截图只能走**虚拟显示器**那一侧。私有桌面里的窗口没有被系统合成,没有任何后端能读到像素
49
- > (详见「已知限制」)。
50
-
51
63
  ---
52
64
 
53
65
  # 二、使用说明
@@ -55,12 +67,16 @@ with VirtualWorkspace(VirtualWorkspace.SHARED) as space:
55
67
  ## 安装
56
68
 
57
69
  ```bash
58
- pip install VirtualDesktop
70
+ pip install VirtualDesktop # pip
71
+ uv add VirtualDesktop # uv(写入 pyproject.toml 并锁定)
59
72
  ```
60
73
 
61
74
  运行时依赖只有 **Pillow**。需要 **Windows 10 1809 (build 17763) 或更高**(更低版本没有 IddCx
62
75
  框架,装不了虚拟显示器)。不装驱动也能用私有桌面模式。
63
76
 
77
+ > 国内镜像(如清华源)同步新版本可能滞后;若 `uv add` / `pip install` 找不到最新版,加
78
+ > `--default-index https://pypi.org/simple`(uv)或 `-i https://pypi.org/simple`(pip)即可。
79
+
64
80
  ## 多窗口工作区
65
81
 
66
82
  `desktop` 是**必填参数**,因为它决定窗口能不能被外部进程控制,这不该由库替你猜:
@@ -209,8 +225,8 @@ with VirtualWorkspace("lab") as space, space.agent() as agent:
209
225
  `exec` 是逃生舱:里面的代码能 `import VirtualDesktop`、能用 `list_windows()`、`CaptureEngine`,
210
226
  也能用你自己装的任何库(包括 pywinauto)。代理本身**只依赖 ctypes**,不需要额外安装。
211
227
 
212
- > `agent.screenshot()` 会**明确报错**而不是产出空白图:私有桌面的窗口没有被系统合成,没有任何
213
- > 截图后端能读到像素。要截图请用 `VirtualWorkspace(SHARED)`。
228
+ > 代理**不能截图**:私有桌面的窗口没有被系统合成,`agent.screenshot()` 会明确报错。需要截图就用
229
+ > `VirtualWorkspace(SHARED)`。
214
230
 
215
231
  ## 用自动化库控制窗口
216
232
 
@@ -271,7 +287,7 @@ with VirtualWorkspace("lab-a") as space:
271
287
  | `children()` | 可用,需 `visible_only=False` | 私有桌面窗口不带 `WS_VISIBLE` |
272
288
  | `is_visible()` | 返回 `False` | 桌面未被显示 |
273
289
  | `click()` / `click_input()` | 不可用 | 前者做可见性前置检查,后者需要活动桌面移动光标 |
274
- | 点击控件 | 发 `BM_CLICK` 消息 | `agent.click()` 内部就是这么做的 |
290
+ | 点击控件 | 发 `BM_CLICK` 消息 | 用 `agent.click()` 即可 |
275
291
 
276
292
  > pyautogui 这类依赖真实鼠标/键盘的库只能用于共享工作区——私有桌面上没有可移动的指针。
277
293
 
@@ -406,8 +422,8 @@ set VIRTUALDESKTOP_GITHUB_MIRRORS=https://gh-proxy.com/,https://internal.example
406
422
 
407
423
  ## 已知限制
408
424
 
409
- - **私有桌面里的窗口无法截图**:没有被系统合成,没有任何后端能读到像素;`agent.screenshot()`
410
- 会明确报错。需要截图就用共享工作区。
425
+ - **私有桌面里的窗口无法截图**:两种隐藏方式(`virtual_screen` / `offscreen`)都支持截图,唯独
426
+ 私有桌面不行——它的窗口没有被系统合成,没有任何后端能读到像素,`agent.screenshot()` 会明确报错。
411
427
  - **窗口无法在私有桌面之间搬运**:Windows 不允许跨桌面移动窗口。
412
428
  - 部分应用(UWP、硬件叠加层、DRM 受保护内容)任何离屏方式都抓不到——系统限制。
413
429
  - 自绘界面的应用(如微信 3.9.x)没有子窗口,UI Automation 只暴露顶层 `Pane`,组件粒度只能到
@@ -63,7 +63,7 @@ from .workspace import SHARED, VirtualWorkspace, WorkspaceWindow
63
63
  from .agent import AgentProcess, AgentSession
64
64
  from .display_setup import configure_virtual_display, current_primary_dpi, current_primary_size
65
65
 
66
- __version__ = "1.2.0"
66
+ __version__ = "1.2.1"
67
67
 
68
68
  __all__ = [
69
69
  "__version__",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: VirtualDesktop
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Multi-window virtual-display workspaces on Windows: one pip install bootstraps the virtual display driver, hosts programs on a virtual or private desktop, keeps them off the physical screens, and hands out real window handles for automation and PrintWindow capture.
5
5
  Author: VirtualDesktop contributors
6
6
  License: MIT License
@@ -77,18 +77,33 @@ with VirtualWorkspace(VirtualWorkspace.SHARED) as space:
77
77
 
78
78
  整个库只建立在 Windows 的**两套机制**上。三个类只是它们的不同封装,搞清这点其余都好懂。
79
79
 
80
- | 机制 | 是什么 | 需要什么 | 外部进程能控制里面的窗口吗 |
81
- |---|---|---|---|
82
- | **虚拟显示器** | 一块额外的屏幕(`\\.\DISPLAYn`)。窗口搬上去,但**仍在你的交互桌面上**,只是画在你看不到的那块屏 | 装一次驱动(1 次 UAC) | **能**。窗口是普通窗口,句柄是真的,pywinauto / UIA / pyautogui 直接用 |
83
- | **私有桌面** | 用 `CreateDesktopW` 建的独立窗口命名空间。与你的桌面完全隔离 | 什么都不需要 | **不能**。跨桌面拿不到窗口,必须在里面起代理进程 |
80
+ | 机制 | 是什么 | 需要什么 |
81
+ |---|---|---|
82
+ | **留在交互桌面** | 窗口仍是普通窗口,只把位置挪到虚拟显示器上(`\\.\DISPLAYn`),或挪到所有显示器之外的坐标区 | 虚拟显示器要装一次驱动(1 次 UAC);挪出坐标区什么都不需要 |
83
+ | **私有桌面** | 用 `CreateDesktopW` 建的独立窗口命名空间。与你的桌面完全隔离 | 什么都不需要 |
84
+
85
+ **关键区别只有一条**:窗口是否还留在交互桌面上。由此决定两件事——外部进程能不能直接控制它、能不能截图。
86
+
87
+ | 模式 | 窗口在交互桌面 | 外部库能直接控制 | **能截图** | 需要驱动 |
88
+ |---|---|---|---|---|
89
+ | `VirtualWorkspace(SHARED)` + `virtual_screen` | 是 | 能 | **能** | 是 |
90
+ | `VirtualWorkspace(SHARED)` + `offscreen` | 是 | 能 | **能** | 否 |
91
+ | `OffscreenSession` + `virtual_screen` | 是 | 能 | **能** | 是 |
92
+ | `OffscreenSession` + `offscreen` | 是 | 能 | **能** | 否 |
93
+ | `VirtualWorkspace("名字")` | 否 | 不能 | **不能** | 否 |
94
+ | `VirtualDesktop` | 否 | 不能 | **不能** | 否 |
95
+
96
+ 也就是说,**两种隐藏方式(`virtual_screen` / `offscreen`)都能截图**,只是把窗口分别放到"另一块屏"
97
+ 和"屏幕之外的坐标区"。唯一截不到图的是**私有桌面**——那里的窗口没有被系统合成,没有任何后端能
98
+ 读到像素(详见「已知限制」)。
84
99
 
85
100
  三个类与机制的关系:
86
101
 
87
102
  | 类 | 用途 | 底层机制 |
88
103
  |---|---|---|
89
- | `VirtualWorkspace(SHARED)` | **多窗口**工作区,外部可自动化 | 虚拟显示器 |
104
+ | `VirtualWorkspace(SHARED)` | **多窗口**工作区,外部可自动化 | 留在交互桌面 |
90
105
  | `VirtualWorkspace("名字")` | **多窗口**工作区,强隔离 | 私有桌面 |
91
- | `OffscreenSession` | **单窗口**:挂一个窗口、截图、还原 | 虚拟显示器 |
106
+ | `OffscreenSession` | **单窗口**:挂一个窗口、截图、还原 | 留在交互桌面 |
92
107
  | `VirtualDesktop` | 私有桌面**原语**(`VirtualWorkspace("名字")` 内部就用它) | 私有桌面 |
93
108
  | `configure_virtual_display()` | 设置虚拟显示器的分辨率 / DPI | (作用于显示器本身) |
94
109
 
@@ -96,14 +111,11 @@ with VirtualWorkspace(VirtualWorkspace.SHARED) as space:
96
111
 
97
112
  ```
98
113
  只处理一个窗口、要截图 → OffscreenSession
99
- 要同时跑多个程序、外部要控制 → VirtualWorkspace(SHARED)
114
+ 要同时跑多个程序、外部要控制、要截图 → VirtualWorkspace(SHARED)
100
115
  要多实例强隔离、不需要截图 → VirtualWorkspace("名字")
101
116
  要自己管理桌面、不需要工作区封装 → VirtualDesktop
102
117
  ```
103
118
 
104
- > 截图只能走**虚拟显示器**那一侧。私有桌面里的窗口没有被系统合成,没有任何后端能读到像素
105
- > (详见「已知限制」)。
106
-
107
119
  ---
108
120
 
109
121
  # 二、使用说明
@@ -111,12 +123,16 @@ with VirtualWorkspace(VirtualWorkspace.SHARED) as space:
111
123
  ## 安装
112
124
 
113
125
  ```bash
114
- pip install VirtualDesktop
126
+ pip install VirtualDesktop # pip
127
+ uv add VirtualDesktop # uv(写入 pyproject.toml 并锁定)
115
128
  ```
116
129
 
117
130
  运行时依赖只有 **Pillow**。需要 **Windows 10 1809 (build 17763) 或更高**(更低版本没有 IddCx
118
131
  框架,装不了虚拟显示器)。不装驱动也能用私有桌面模式。
119
132
 
133
+ > 国内镜像(如清华源)同步新版本可能滞后;若 `uv add` / `pip install` 找不到最新版,加
134
+ > `--default-index https://pypi.org/simple`(uv)或 `-i https://pypi.org/simple`(pip)即可。
135
+
120
136
  ## 多窗口工作区
121
137
 
122
138
  `desktop` 是**必填参数**,因为它决定窗口能不能被外部进程控制,这不该由库替你猜:
@@ -265,8 +281,8 @@ with VirtualWorkspace("lab") as space, space.agent() as agent:
265
281
  `exec` 是逃生舱:里面的代码能 `import VirtualDesktop`、能用 `list_windows()`、`CaptureEngine`,
266
282
  也能用你自己装的任何库(包括 pywinauto)。代理本身**只依赖 ctypes**,不需要额外安装。
267
283
 
268
- > `agent.screenshot()` 会**明确报错**而不是产出空白图:私有桌面的窗口没有被系统合成,没有任何
269
- > 截图后端能读到像素。要截图请用 `VirtualWorkspace(SHARED)`。
284
+ > 代理**不能截图**:私有桌面的窗口没有被系统合成,`agent.screenshot()` 会明确报错。需要截图就用
285
+ > `VirtualWorkspace(SHARED)`。
270
286
 
271
287
  ## 用自动化库控制窗口
272
288
 
@@ -327,7 +343,7 @@ with VirtualWorkspace("lab-a") as space:
327
343
  | `children()` | 可用,需 `visible_only=False` | 私有桌面窗口不带 `WS_VISIBLE` |
328
344
  | `is_visible()` | 返回 `False` | 桌面未被显示 |
329
345
  | `click()` / `click_input()` | 不可用 | 前者做可见性前置检查,后者需要活动桌面移动光标 |
330
- | 点击控件 | 发 `BM_CLICK` 消息 | `agent.click()` 内部就是这么做的 |
346
+ | 点击控件 | 发 `BM_CLICK` 消息 | 用 `agent.click()` 即可 |
331
347
 
332
348
  > pyautogui 这类依赖真实鼠标/键盘的库只能用于共享工作区——私有桌面上没有可移动的指针。
333
349
 
@@ -462,8 +478,8 @@ set VIRTUALDESKTOP_GITHUB_MIRRORS=https://gh-proxy.com/,https://internal.example
462
478
 
463
479
  ## 已知限制
464
480
 
465
- - **私有桌面里的窗口无法截图**:没有被系统合成,没有任何后端能读到像素;`agent.screenshot()`
466
- 会明确报错。需要截图就用共享工作区。
481
+ - **私有桌面里的窗口无法截图**:两种隐藏方式(`virtual_screen` / `offscreen`)都支持截图,唯独
482
+ 私有桌面不行——它的窗口没有被系统合成,没有任何后端能读到像素,`agent.screenshot()` 会明确报错。
467
483
  - **窗口无法在私有桌面之间搬运**:Windows 不允许跨桌面移动窗口。
468
484
  - 部分应用(UWP、硬件叠加层、DRM 受保护内容)任何离屏方式都抓不到——系统限制。
469
485
  - 自绘界面的应用(如微信 3.9.x)没有子窗口,UI Automation 只暴露顶层 `Pane`,组件粒度只能到
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "VirtualDesktop"
7
- version = "1.2.0"
7
+ version = "1.2.1"
8
8
  description = "Multi-window virtual-display workspaces on Windows: one pip install bootstraps the virtual display driver, hosts programs on a virtual or private desktop, keeps them off the physical screens, and hands out real window handles for automation and PrintWindow capture."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
File without changes
File without changes