AutoMonkey 0.4.2__tar.gz → 0.4.3__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.
- {automonkey-0.4.2 → automonkey-0.4.3/AutoMonkey.egg-info}/PKG-INFO +1 -1
- {automonkey-0.4.2 → automonkey-0.4.3}/AutoMonkey.egg-info/SOURCES.txt +1 -0
- {automonkey-0.4.2/AutoMonkey.egg-info → automonkey-0.4.3}/PKG-INFO +1 -1
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/mouse_tracker.py +49 -17
- {automonkey-0.4.2 → automonkey-0.4.3}/pyproject.toml +1 -1
- automonkey-0.4.3/tests/test_mouse_tracker.py +62 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/AUTHORS.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/AutoMonkey.egg-info/dependency_links.txt +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/AutoMonkey.egg-info/entry_points.txt +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/AutoMonkey.egg-info/not-zip-safe +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/AutoMonkey.egg-info/requires.txt +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/AutoMonkey.egg-info/top_level.txt +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/CONTRIBUTING.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/HISTORY.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/LICENSE +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/MANIFEST.in +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/README.md +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/README.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/__init__.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/ai/__init__.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/ai/__main__.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/ai/mcp_launch.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/ai/mcp_server.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/ai/ocr.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/ai/screen.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/ai/script.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/ai/windows.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/app_funcs.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/atspi_funcs.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/automonkey.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/constants.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/exceptions.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/img_funcs.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/key_funcs.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/mouse_funcs.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/pywinauto_funcs.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/automonkey/utils.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/Makefile +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/authors.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/conf.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/contributing.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/history.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/index.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/installation.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/make.bat +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/readme.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/docs/usage.rst +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/requirements.txt +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/setup.cfg +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/setup.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/tests/test_ai.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/tests/test_atspi_funcs.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/tests/test_atspi_integration.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/tests/test_key_funcs.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/tests/test_mcp_launch.py +0 -0
- {automonkey-0.4.2 → automonkey-0.4.3}/tests/test_pywinauto_funcs.py +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from time import sleep
|
|
2
|
+
from typing import Any
|
|
2
3
|
|
|
3
4
|
from tkinter import Tk
|
|
4
5
|
from tkinter import Canvas
|
|
@@ -24,38 +25,59 @@ def track_mouse():
|
|
|
24
25
|
print("Tracking mouse position stopped")
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
class PositionTracker:
|
|
28
|
-
"""
|
|
28
|
+
class PositionTracker:
|
|
29
|
+
"""Full-screen overlay that shows the live cursor coordinates.
|
|
30
|
+
|
|
31
|
+
Constructing a tracker puts nothing on screen - the Tk window is built by
|
|
32
|
+
start(). (GitHub issue #3: the window used to be created in __init__, so
|
|
33
|
+
merely instantiating this class opened a stray empty window, and the
|
|
34
|
+
documented "tracker = PositionTracker(); tracker.start()" usage looked
|
|
35
|
+
broken. The coordinate display itself is unchanged and still drawn by the
|
|
36
|
+
overlay.)
|
|
29
37
|
"""
|
|
38
|
+
|
|
30
39
|
def __init__(self, follow_mouse: bool = False):
|
|
31
40
|
self.follow_mouse = follow_mouse
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
# Deliberately not created here: the overlay is built by start(), so that
|
|
42
|
+
# constructing a tracker is side-effect free (issue #3). Annotated Any because
|
|
43
|
+
# Tk instances carry the ad-hoc .canvas attribute added in start().
|
|
44
|
+
self.window: Any = None
|
|
34
45
|
self.coords = None
|
|
35
46
|
self.get_coords = False
|
|
36
47
|
self.after = None
|
|
37
48
|
|
|
38
49
|
def start(self, get_coords: bool = False):
|
|
39
|
-
"""
|
|
50
|
+
"""Show the overlay and display the live cursor coordinates.
|
|
51
|
+
|
|
52
|
+
get_coords=True lets the user pick a point with CTRL+Left Click; the
|
|
53
|
+
coordinates of that point are returned. Otherwise press ESC to end
|
|
54
|
+
tracking, and None is returned.
|
|
40
55
|
"""
|
|
41
|
-
if self.after:
|
|
56
|
+
if self.after is not None and self.window is not None:
|
|
42
57
|
self.window.after_cancel(self.after)
|
|
58
|
+
self.after = None
|
|
59
|
+
|
|
60
|
+
self.get_coords = get_coords
|
|
61
|
+
|
|
62
|
+
if self.window is None:
|
|
63
|
+
self.window = Tk()
|
|
64
|
+
self.window.canvas = None
|
|
43
65
|
if get_coords:
|
|
44
|
-
self.get_coords = True
|
|
45
66
|
self.window.bind('<Control-Button-1>', lambda e: self.destroy_())
|
|
46
67
|
else:
|
|
47
68
|
self.window.bind('<Escape>', lambda e: self.destroy_())
|
|
48
69
|
self.window.attributes('-fullscreen', True, '-alpha', 0.3)
|
|
49
70
|
self.window.configure(bg='black')
|
|
50
71
|
|
|
51
|
-
self.window.canvas
|
|
52
|
-
self.window
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
if self.window.canvas is None:
|
|
73
|
+
self.window.canvas = Canvas(
|
|
74
|
+
self.window,
|
|
75
|
+
width=self.window.winfo_screenwidth(),
|
|
76
|
+
height=self.window.winfo_screenheight(),
|
|
77
|
+
cursor="crosshair"
|
|
78
|
+
)
|
|
79
|
+
self.window.canvas.configure(highlightthickness=0, bg='black')
|
|
80
|
+
self.window.canvas.pack()
|
|
59
81
|
|
|
60
82
|
self.after = self.window.after(1, self._crosshair, None)
|
|
61
83
|
self.window.mainloop()
|
|
@@ -129,5 +151,15 @@ class PositionTracker: # was Toplevel
|
|
|
129
151
|
return y_point - 100
|
|
130
152
|
|
|
131
153
|
def destroy_(self):
|
|
132
|
-
|
|
133
|
-
|
|
154
|
+
"""Close the overlay.
|
|
155
|
+
|
|
156
|
+
Safe on a tracker that was never started (it used to raise ValueError
|
|
157
|
+
from after_cancel(None)), and leaves the instance reusable: a later
|
|
158
|
+
start() builds a fresh window.
|
|
159
|
+
"""
|
|
160
|
+
if self.after is not None and self.window is not None:
|
|
161
|
+
self.window.after_cancel(self.after)
|
|
162
|
+
self.after = None
|
|
163
|
+
if self.window is not None:
|
|
164
|
+
self.window.destroy()
|
|
165
|
+
self.window = None
|
|
@@ -14,7 +14,7 @@ name = "AutoMonkey"
|
|
|
14
14
|
# .github/workflows/python-package.yml, right after a successful upload to PyPI, so the
|
|
15
15
|
# tree always holds the *next* version to be released. Bump it by hand with
|
|
16
16
|
# `python bump_version.py` (patch) / `python bump_version.py --part minor` / `--set 1.0.0`.
|
|
17
|
-
version = "0.4.
|
|
17
|
+
version = "0.4.3"
|
|
18
18
|
description = "Python Automation using Mouse and Keyboard, for the masses"
|
|
19
19
|
readme = "README.md"
|
|
20
20
|
requires-python = ">=3.8"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Regression tests for GitHub issue #3 ("PositionTracker not working").
|
|
2
|
+
|
|
3
|
+
The issue had two halves: merely constructing a PositionTracker opened a Tk
|
|
4
|
+
window it shouldn't, and destroy_() blew up on an instance that was never
|
|
5
|
+
started. The coordinate rendering was the part that already worked, and is
|
|
6
|
+
covered by test_start_shows_the_cursor_coordinates.
|
|
7
|
+
"""
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
tkinter = pytest.importorskip("tkinter")
|
|
11
|
+
|
|
12
|
+
from automonkey import PositionTracker # noqa: E402
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_constructing_a_tracker_opens_no_window():
|
|
16
|
+
# Documented usage is `tracker = PositionTracker(); tracker.start()`, so nothing
|
|
17
|
+
# may appear on screen before start() runs.
|
|
18
|
+
root_before = tkinter._default_root
|
|
19
|
+
tracker = PositionTracker()
|
|
20
|
+
assert tracker.window is None
|
|
21
|
+
assert tkinter._default_root is root_before, "constructing created a Tk root"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_destroy_is_safe_on_a_tracker_that_was_never_started():
|
|
25
|
+
tracker = PositionTracker()
|
|
26
|
+
tracker.destroy_() # used to raise ValueError from after_cancel(None)
|
|
27
|
+
assert tracker.window is None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_start_shows_the_cursor_coordinates():
|
|
31
|
+
tracker = PositionTracker(follow_mouse=False)
|
|
32
|
+
captured = {}
|
|
33
|
+
real_tk = tkinter.Tk
|
|
34
|
+
|
|
35
|
+
def tk_factory(*args, **kwargs):
|
|
36
|
+
"""Hand back a real Tk root, primed to snapshot the canvas and close."""
|
|
37
|
+
root = real_tk(*args, **kwargs)
|
|
38
|
+
|
|
39
|
+
def grab_and_close():
|
|
40
|
+
canvas = tracker.window.canvas
|
|
41
|
+
captured["texts"] = [
|
|
42
|
+
canvas.itemcget(item, "text")
|
|
43
|
+
for item in canvas.find_all()
|
|
44
|
+
if canvas.type(item) == "text"
|
|
45
|
+
]
|
|
46
|
+
tracker.destroy_()
|
|
47
|
+
|
|
48
|
+
root.after(500, grab_and_close)
|
|
49
|
+
return root
|
|
50
|
+
|
|
51
|
+
monkeypatch = pytest.MonkeyPatch()
|
|
52
|
+
monkeypatch.setattr("automonkey.mouse_tracker.Tk", tk_factory)
|
|
53
|
+
try:
|
|
54
|
+
tracker.start()
|
|
55
|
+
except tkinter.TclError as exc: # no display available (headless CI without xvfb)
|
|
56
|
+
pytest.skip(f"no usable display: {exc}")
|
|
57
|
+
finally:
|
|
58
|
+
monkeypatch.undo()
|
|
59
|
+
|
|
60
|
+
texts = captured.get("texts", [])
|
|
61
|
+
assert any(t.startswith("x=") and ", y=" in t for t in texts), \
|
|
62
|
+
f"cursor coordinates were not drawn on the overlay, got {texts!r}"
|
|
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
|