AutoMonkey 0.4.2__tar.gz → 0.4.4__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 (56) hide show
  1. {automonkey-0.4.2 → automonkey-0.4.4/AutoMonkey.egg-info}/PKG-INFO +10 -2
  2. {automonkey-0.4.2 → automonkey-0.4.4}/AutoMonkey.egg-info/SOURCES.txt +1 -0
  3. {automonkey-0.4.2/AutoMonkey.egg-info → automonkey-0.4.4}/PKG-INFO +10 -2
  4. {automonkey-0.4.2 → automonkey-0.4.4}/README.md +9 -1
  5. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/mouse_tracker.py +49 -17
  6. {automonkey-0.4.2 → automonkey-0.4.4}/pyproject.toml +1 -1
  7. automonkey-0.4.4/tests/test_mouse_tracker.py +62 -0
  8. {automonkey-0.4.2 → automonkey-0.4.4}/AUTHORS.rst +0 -0
  9. {automonkey-0.4.2 → automonkey-0.4.4}/AutoMonkey.egg-info/dependency_links.txt +0 -0
  10. {automonkey-0.4.2 → automonkey-0.4.4}/AutoMonkey.egg-info/entry_points.txt +0 -0
  11. {automonkey-0.4.2 → automonkey-0.4.4}/AutoMonkey.egg-info/not-zip-safe +0 -0
  12. {automonkey-0.4.2 → automonkey-0.4.4}/AutoMonkey.egg-info/requires.txt +0 -0
  13. {automonkey-0.4.2 → automonkey-0.4.4}/AutoMonkey.egg-info/top_level.txt +0 -0
  14. {automonkey-0.4.2 → automonkey-0.4.4}/CONTRIBUTING.rst +0 -0
  15. {automonkey-0.4.2 → automonkey-0.4.4}/HISTORY.rst +0 -0
  16. {automonkey-0.4.2 → automonkey-0.4.4}/LICENSE +0 -0
  17. {automonkey-0.4.2 → automonkey-0.4.4}/MANIFEST.in +0 -0
  18. {automonkey-0.4.2 → automonkey-0.4.4}/README.rst +0 -0
  19. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/__init__.py +0 -0
  20. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/ai/__init__.py +0 -0
  21. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/ai/__main__.py +0 -0
  22. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/ai/mcp_launch.py +0 -0
  23. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/ai/mcp_server.py +0 -0
  24. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/ai/ocr.py +0 -0
  25. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/ai/screen.py +0 -0
  26. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/ai/script.py +0 -0
  27. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/ai/windows.py +0 -0
  28. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/app_funcs.py +0 -0
  29. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/atspi_funcs.py +0 -0
  30. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/automonkey.py +0 -0
  31. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/constants.py +0 -0
  32. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/exceptions.py +0 -0
  33. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/img_funcs.py +0 -0
  34. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/key_funcs.py +0 -0
  35. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/mouse_funcs.py +0 -0
  36. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/pywinauto_funcs.py +0 -0
  37. {automonkey-0.4.2 → automonkey-0.4.4}/automonkey/utils.py +0 -0
  38. {automonkey-0.4.2 → automonkey-0.4.4}/docs/Makefile +0 -0
  39. {automonkey-0.4.2 → automonkey-0.4.4}/docs/authors.rst +0 -0
  40. {automonkey-0.4.2 → automonkey-0.4.4}/docs/conf.py +0 -0
  41. {automonkey-0.4.2 → automonkey-0.4.4}/docs/contributing.rst +0 -0
  42. {automonkey-0.4.2 → automonkey-0.4.4}/docs/history.rst +0 -0
  43. {automonkey-0.4.2 → automonkey-0.4.4}/docs/index.rst +0 -0
  44. {automonkey-0.4.2 → automonkey-0.4.4}/docs/installation.rst +0 -0
  45. {automonkey-0.4.2 → automonkey-0.4.4}/docs/make.bat +0 -0
  46. {automonkey-0.4.2 → automonkey-0.4.4}/docs/readme.rst +0 -0
  47. {automonkey-0.4.2 → automonkey-0.4.4}/docs/usage.rst +0 -0
  48. {automonkey-0.4.2 → automonkey-0.4.4}/requirements.txt +0 -0
  49. {automonkey-0.4.2 → automonkey-0.4.4}/setup.cfg +0 -0
  50. {automonkey-0.4.2 → automonkey-0.4.4}/setup.py +0 -0
  51. {automonkey-0.4.2 → automonkey-0.4.4}/tests/test_ai.py +0 -0
  52. {automonkey-0.4.2 → automonkey-0.4.4}/tests/test_atspi_funcs.py +0 -0
  53. {automonkey-0.4.2 → automonkey-0.4.4}/tests/test_atspi_integration.py +0 -0
  54. {automonkey-0.4.2 → automonkey-0.4.4}/tests/test_key_funcs.py +0 -0
  55. {automonkey-0.4.2 → automonkey-0.4.4}/tests/test_mcp_launch.py +0 -0
  56. {automonkey-0.4.2 → automonkey-0.4.4}/tests/test_pywinauto_funcs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AutoMonkey
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Python Automation using Mouse and Keyboard, for the masses
5
5
  Author-email: Mihail-Cosmin Munteanu <munteanumihailcosmin@gmail.com>
6
6
  License: GNU General Public License v3 (GPLv3)
@@ -74,13 +74,21 @@ pip install git+https://github.com/MihailCosmin/AutoMonkey
74
74
  - clipboard
75
75
  - keyboard
76
76
  - screeninfo
77
- - pywin32
77
+ - pywin32 (Windows only)
78
78
  - pillow
79
79
  - opencv-python
80
80
  - numpy
81
81
  - pytesseract
82
82
  - <a href="https://github.com/pywinauto/pywinauto">pywinauto</a> (Windows only, for the control actions)
83
83
 
84
+ The control actions (`get_window`, `click_control`, `type_in_control`, ...) are **not** Windows-only:
85
+ on Linux the same functions run on AT-SPI, with identical names and arguments, so a `chain` script
86
+ needs no changes between the two. Windows gets pywinauto from pip (above); Linux uses distro packages
87
+ instead of pip - `python3-gi`, `gir1.2-atspi-2.0`, `at-spi2-core` on Debian/Ubuntu/Raspberry Pi OS -
88
+ plus accessibility enabled (`gsettings set org.gnome.desktop.interface toolkit-accessibility true`).
89
+ Setup, the `control_type` → AT-SPI role mapping and the known Wayland limits are in
90
+ *Control actions* → *Linux (AT-SPI)* under Usage. macOS is not implemented yet.
91
+
84
92
  # Usage
85
93
  Main function to be used is "chain"
86
94
 
@@ -51,4 +51,5 @@ tests/test_atspi_funcs.py
51
51
  tests/test_atspi_integration.py
52
52
  tests/test_key_funcs.py
53
53
  tests/test_mcp_launch.py
54
+ tests/test_mouse_tracker.py
54
55
  tests/test_pywinauto_funcs.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AutoMonkey
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Python Automation using Mouse and Keyboard, for the masses
5
5
  Author-email: Mihail-Cosmin Munteanu <munteanumihailcosmin@gmail.com>
6
6
  License: GNU General Public License v3 (GPLv3)
@@ -74,13 +74,21 @@ pip install git+https://github.com/MihailCosmin/AutoMonkey
74
74
  - clipboard
75
75
  - keyboard
76
76
  - screeninfo
77
- - pywin32
77
+ - pywin32 (Windows only)
78
78
  - pillow
79
79
  - opencv-python
80
80
  - numpy
81
81
  - pytesseract
82
82
  - <a href="https://github.com/pywinauto/pywinauto">pywinauto</a> (Windows only, for the control actions)
83
83
 
84
+ The control actions (`get_window`, `click_control`, `type_in_control`, ...) are **not** Windows-only:
85
+ on Linux the same functions run on AT-SPI, with identical names and arguments, so a `chain` script
86
+ needs no changes between the two. Windows gets pywinauto from pip (above); Linux uses distro packages
87
+ instead of pip - `python3-gi`, `gir1.2-atspi-2.0`, `at-spi2-core` on Debian/Ubuntu/Raspberry Pi OS -
88
+ plus accessibility enabled (`gsettings set org.gnome.desktop.interface toolkit-accessibility true`).
89
+ Setup, the `control_type` → AT-SPI role mapping and the known Wayland limits are in
90
+ *Control actions* → *Linux (AT-SPI)* under Usage. macOS is not implemented yet.
91
+
84
92
  # Usage
85
93
  Main function to be used is "chain"
86
94
 
@@ -32,13 +32,21 @@ pip install git+https://github.com/MihailCosmin/AutoMonkey
32
32
  - clipboard
33
33
  - keyboard
34
34
  - screeninfo
35
- - pywin32
35
+ - pywin32 (Windows only)
36
36
  - pillow
37
37
  - opencv-python
38
38
  - numpy
39
39
  - pytesseract
40
40
  - <a href="https://github.com/pywinauto/pywinauto">pywinauto</a> (Windows only, for the control actions)
41
41
 
42
+ The control actions (`get_window`, `click_control`, `type_in_control`, ...) are **not** Windows-only:
43
+ on Linux the same functions run on AT-SPI, with identical names and arguments, so a `chain` script
44
+ needs no changes between the two. Windows gets pywinauto from pip (above); Linux uses distro packages
45
+ instead of pip - `python3-gi`, `gir1.2-atspi-2.0`, `at-spi2-core` on Debian/Ubuntu/Raspberry Pi OS -
46
+ plus accessibility enabled (`gsettings set org.gnome.desktop.interface toolkit-accessibility true`).
47
+ Setup, the `control_type` → AT-SPI role mapping and the known Wayland limits are in
48
+ *Control actions* → *Linux (AT-SPI)* under Usage. macOS is not implemented yet.
49
+
42
50
  # Usage
43
51
  Main function to be used is "chain"
44
52
 
@@ -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: # was Toplevel
28
- """PositionTracker
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
- self.window = Tk() # Was Tk()
33
- self.window.canvas = None
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
- """Take the screenshot
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 = Canvas(
52
- self.window,
53
- width=self.window.winfo_screenwidth(),
54
- height=self.window.winfo_screenheight(),
55
- cursor="crosshair"
56
- )
57
- self.window.canvas.configure(highlightthickness=0, bg='black')
58
- self.window.canvas.pack()
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
- self.window.after_cancel(self.after)
133
- self.window.destroy()
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.2"
17
+ version = "0.4.4"
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