windows-mcp 0.5.7__py3-none-any.whl → 0.5.9__py3-none-any.whl
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.
- windows_mcp/__main__.py +69 -82
- windows_mcp/analytics.py +170 -171
- windows_mcp/desktop/config.py +20 -20
- windows_mcp/desktop/service.py +279 -99
- windows_mcp/desktop/views.py +59 -57
- windows_mcp/tree/cache_utils.py +126 -0
- windows_mcp/tree/config.py +75 -50
- windows_mcp/tree/service.py +424 -348
- windows_mcp/tree/utils.py +21 -21
- windows_mcp/tree/views.py +51 -25
- windows_mcp/uia/__init__.py +4 -0
- windows_mcp/uia/controls.py +4790 -0
- windows_mcp/uia/core.py +3278 -0
- windows_mcp/uia/enums.py +1963 -0
- windows_mcp/uia/events.py +83 -0
- windows_mcp/uia/patterns.py +2106 -0
- windows_mcp/vdm/__init__.py +1 -0
- windows_mcp/vdm/core.py +490 -0
- windows_mcp/watchdog/__init__.py +1 -0
- windows_mcp/watchdog/event_handlers.py +55 -0
- windows_mcp/watchdog/service.py +199 -0
- {windows_mcp-0.5.7.dist-info → windows_mcp-0.5.9.dist-info}/METADATA +30 -24
- windows_mcp-0.5.9.dist-info/RECORD +29 -0
- windows_mcp-0.5.7.dist-info/RECORD +0 -17
- {windows_mcp-0.5.7.dist-info → windows_mcp-0.5.9.dist-info}/WHEEL +0 -0
- {windows_mcp-0.5.7.dist-info → windows_mcp-0.5.9.dist-info}/entry_points.txt +0 -0
- {windows_mcp-0.5.7.dist-info → windows_mcp-0.5.9.dist-info}/licenses/LICENSE.md +0 -0
windows_mcp/tree/utils.py
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import random
|
|
2
|
-
from
|
|
3
|
-
|
|
4
|
-
def random_point_within_bounding_box(node: Control, scale_factor: float = 1.0) -> tuple[int, int]:
|
|
5
|
-
"""
|
|
6
|
-
Generate a random point within a scaled-down bounding box.
|
|
7
|
-
|
|
8
|
-
Args:
|
|
9
|
-
node (Control): The node with a bounding rectangle
|
|
10
|
-
scale_factor (float, optional): The factor to scale down the bounding box. Defaults to 1.0.
|
|
11
|
-
|
|
12
|
-
Returns:
|
|
13
|
-
tuple: A random point (x, y) within the scaled-down bounding box
|
|
14
|
-
"""
|
|
15
|
-
box = node.BoundingRectangle
|
|
16
|
-
scaled_width = int(box.width() * scale_factor)
|
|
17
|
-
scaled_height = int(box.height() * scale_factor)
|
|
18
|
-
scaled_left = box.left + (box.width() - scaled_width) // 2
|
|
19
|
-
scaled_top = box.top + (box.height() - scaled_height) // 2
|
|
20
|
-
x = random.randint(scaled_left, scaled_left + scaled_width)
|
|
21
|
-
y = random.randint(scaled_top, scaled_top + scaled_height)
|
|
1
|
+
import random
|
|
2
|
+
from windows_mcp.uia import Control
|
|
3
|
+
|
|
4
|
+
def random_point_within_bounding_box(node: Control, scale_factor: float = 1.0) -> tuple[int, int]:
|
|
5
|
+
"""
|
|
6
|
+
Generate a random point within a scaled-down bounding box.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
node (Control): The node with a bounding rectangle
|
|
10
|
+
scale_factor (float, optional): The factor to scale down the bounding box. Defaults to 1.0.
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
tuple: A random point (x, y) within the scaled-down bounding box
|
|
14
|
+
"""
|
|
15
|
+
box = node.BoundingRectangle
|
|
16
|
+
scaled_width = int(box.width() * scale_factor)
|
|
17
|
+
scaled_height = int(box.height() * scale_factor)
|
|
18
|
+
scaled_left = box.left + (box.width() - scaled_width) // 2
|
|
19
|
+
scaled_top = box.top + (box.height() - scaled_height) // 2
|
|
20
|
+
x = random.randint(scaled_left, scaled_left + scaled_width)
|
|
21
|
+
y = random.randint(scaled_top, scaled_top + scaled_height)
|
|
22
22
|
return (x, y)
|
windows_mcp/tree/views.py
CHANGED
|
@@ -2,37 +2,39 @@ from dataclasses import dataclass,field
|
|
|
2
2
|
from tabulate import tabulate
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
@dataclass
|
|
6
|
-
class DOMInfo:
|
|
7
|
-
horizontal_scrollable: bool
|
|
8
|
-
horizontal_scroll_percent: float
|
|
9
|
-
vertical_scrollable: bool
|
|
10
|
-
vertical_scroll_percent: float
|
|
11
|
-
|
|
12
5
|
@dataclass
|
|
13
6
|
class TreeState:
|
|
7
|
+
root_node:Optional['TreeElementNode']=None
|
|
8
|
+
dom_node:Optional['ScrollElementNode']=None
|
|
14
9
|
interactive_nodes:list['TreeElementNode']=field(default_factory=list)
|
|
15
10
|
scrollable_nodes:list['ScrollElementNode']=field(default_factory=list)
|
|
16
11
|
dom_informative_nodes:list['TextElementNode']=field(default_factory=list)
|
|
17
|
-
dom_info:Optional['DOMInfo']=None
|
|
18
12
|
|
|
19
13
|
def interactive_elements_to_string(self) -> str:
|
|
20
14
|
if not self.interactive_nodes:
|
|
21
15
|
return "No interactive elements"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
# TOON-like format: Pipe-separated values with clear header
|
|
17
|
+
# Using abbreviations in header to save tokens
|
|
18
|
+
header = "# id|app|type|name|coords|focus"
|
|
19
|
+
rows = [header]
|
|
20
|
+
for idx, node in enumerate(self.interactive_nodes):
|
|
21
|
+
row = f"{idx}|{node.app_name}|{node.control_type}|{node.name}|{node.center.to_string()}|{node.is_focused}"
|
|
22
|
+
rows.append(row)
|
|
23
|
+
return "\n".join(rows)
|
|
25
24
|
|
|
26
25
|
def scrollable_elements_to_string(self) -> str:
|
|
27
26
|
if not self.scrollable_nodes:
|
|
28
27
|
return "No scrollable elements"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
]
|
|
28
|
+
# TOON-like format
|
|
29
|
+
header = "# id|app|type|name|coords|h_scroll|h_pct|v_scroll|v_pct|focus"
|
|
30
|
+
rows = [header]
|
|
33
31
|
base_index = len(self.interactive_nodes)
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
for idx, node in enumerate(self.scrollable_nodes):
|
|
33
|
+
row = (f"{base_index + idx}|{node.app_name}|{node.control_type}|{node.name}|"
|
|
34
|
+
f"{node.center.to_string()}|{node.horizontal_scrollable}|{node.horizontal_scroll_percent}|"
|
|
35
|
+
f"{node.vertical_scrollable}|{node.vertical_scroll_percent}|{node.is_focused}")
|
|
36
|
+
rows.append(row)
|
|
37
|
+
return "\n".join(rows)
|
|
36
38
|
|
|
37
39
|
@dataclass
|
|
38
40
|
class BoundingBox:
|
|
@@ -43,6 +45,17 @@ class BoundingBox:
|
|
|
43
45
|
width:int
|
|
44
46
|
height:int
|
|
45
47
|
|
|
48
|
+
@classmethod
|
|
49
|
+
def from_bounding_rectangle(cls,bounding_rectangle:'BoundingRectangle')->'BoundingBox':
|
|
50
|
+
return cls(
|
|
51
|
+
left=bounding_rectangle.left,
|
|
52
|
+
top=bounding_rectangle.top,
|
|
53
|
+
right=bounding_rectangle.right,
|
|
54
|
+
bottom=bounding_rectangle.bottom,
|
|
55
|
+
width=bounding_rectangle.width(),
|
|
56
|
+
height=bounding_rectangle.height()
|
|
57
|
+
)
|
|
58
|
+
|
|
46
59
|
def get_center(self)->'Center':
|
|
47
60
|
return Center(x=self.left+self.width//2,y=self.top+self.height//2)
|
|
48
61
|
|
|
@@ -68,16 +81,28 @@ class Center:
|
|
|
68
81
|
|
|
69
82
|
@dataclass
|
|
70
83
|
class TreeElementNode:
|
|
71
|
-
name: str
|
|
72
|
-
control_type: str
|
|
73
|
-
app_name: str
|
|
74
|
-
value:str
|
|
75
|
-
shortcut: str
|
|
76
84
|
bounding_box: BoundingBox
|
|
77
85
|
center: Center
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
name: str=''
|
|
87
|
+
control_type: str=''
|
|
88
|
+
app_name: str=''
|
|
89
|
+
value:str=''
|
|
90
|
+
shortcut: str=''
|
|
91
|
+
xpath:str=''
|
|
92
|
+
is_focused:bool=False
|
|
93
|
+
|
|
94
|
+
def update_from_node(self,node:'TreeElementNode'):
|
|
95
|
+
self.name=node.name
|
|
96
|
+
self.control_type=node.control_type
|
|
97
|
+
self.app_name=node.app_name
|
|
98
|
+
self.value=node.value
|
|
99
|
+
self.shortcut=node.shortcut
|
|
100
|
+
self.bounding_box=node.bounding_box
|
|
101
|
+
self.center=node.center
|
|
102
|
+
self.xpath=node.xpath
|
|
103
|
+
self.is_focused=node.is_focused
|
|
80
104
|
|
|
105
|
+
# Legacy method kept for compatibility if needed, but not used in new format
|
|
81
106
|
def to_row(self, index: int):
|
|
82
107
|
return [index, self.app_name, self.control_type, self.name, self.value, self.shortcut, self.center.to_string(),self.is_focused]
|
|
83
108
|
|
|
@@ -95,6 +120,7 @@ class ScrollElementNode:
|
|
|
95
120
|
vertical_scroll_percent: float
|
|
96
121
|
is_focused: bool
|
|
97
122
|
|
|
123
|
+
# Legacy method kept for compatibility
|
|
98
124
|
def to_row(self, index: int, base_index: int):
|
|
99
125
|
return [
|
|
100
126
|
base_index + index,
|
|
@@ -113,4 +139,4 @@ class ScrollElementNode:
|
|
|
113
139
|
class TextElementNode:
|
|
114
140
|
text:str
|
|
115
141
|
|
|
116
|
-
ElementNode=TreeElementNode|ScrollElementNode
|
|
142
|
+
ElementNode=TreeElementNode|ScrollElementNode|TextElementNode
|