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.
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/PKG-INFO +33 -17
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/README.md +32 -16
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/__init__.py +1 -1
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/PKG-INFO +33 -17
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/pyproject.toml +1 -1
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/LICENSE +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/__main__.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_bundle.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_compat.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_desktop_runtime.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_devices.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_download.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_helper.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_log.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_process.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_sigcheck.ps1 +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_sigcheck.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/_winapi.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/agent.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/capture.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/cli.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/config.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/desktop.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/desktop_capture.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/desktop_session.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/display_setup.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/driver.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/driver_manifest.json +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/errors.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/manifest.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/session.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/window.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop/workspace.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/SOURCES.txt +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/dependency_links.txt +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/entry_points.txt +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/requires.txt +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/VirtualDesktop.egg-info/top_level.txt +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/setup.cfg +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_accelerators.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_agent.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_artifact_regression.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_capture.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_cli.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_compat.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_config_and_winapi.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_desktop_isolation.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_destructive_lifecycle.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_display_setup.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_download.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_driver.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_hide_modes.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_integration_windows.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_manifest.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_no_dialogs.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_packaging.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_process.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_virtual_desktop.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_workspace.py +0 -0
- {virtualdesktop-1.2.0 → virtualdesktop-1.2.1}/tests/test_workspace_instances.py +0 -0
- {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.
|
|
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
|
-
|
|
|
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
|
-
|
|
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
|
-
>
|
|
269
|
-
>
|
|
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
|
-
-
|
|
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
|
-
|
|
|
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
|
-
|
|
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
|
-
>
|
|
213
|
-
>
|
|
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
|
-
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
|
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
|
-
|
|
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
|
-
>
|
|
269
|
-
>
|
|
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
|
-
-
|
|
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.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|