Skip to content

Commit 3c14a0c

Browse files
committed
Images - Done
1 parent 5a44f99 commit 3c14a0c

3 files changed

Lines changed: 154 additions & 3 deletions

File tree

README.md

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ In practice it aims to do a lot more and is more flexible. Here are some of the
1111
- Responsiveness when rapidly typing or clicking.
1212
- Per-window or otherwise conditional hotkeys.
1313
- Suppressing the key that triggered the action. (surprising how commonly this is absent in other tools)
14-
- Built-in suite of convenient, well-tested command functions.
14+
- Built-in suite of convenient, well-tested helper functions.
1515

1616
## Example
1717

@@ -437,6 +437,155 @@ to `record_toggle` or `record_stop` to modify details about the string you get.
437437

438438
---
439439

440+
### Image and pixel search, snip
441+
442+
```python
443+
from tapper.helper import img
444+
```
445+
446+
allows you to:
447+
448+
- search for an image on a screen (or in image), fuzzy search is an option
449+
- wait for image (or one of the images) to appear
450+
- snip the screen easily and save in a format convenient for **tapper** usage
451+
- all of this for a single pixel, which works much faster
452+
453+
---
454+
455+
```python
456+
Tap("f2", img.snip())
457+
```
458+
459+
Move cursor to a spot, press `f2`, move to another spot, press `f2` - and now you have
460+
a saved `.png` file - partial screenshot of a rectangle between first and second mouse
461+
positions, with name like `snip-(BBOX_953_531_997_686).png`, where in brackets are
462+
coordinates of top-left and bottom-right corners of the image on the screen.
463+
464+
This now can be used to search the picture:
465+
466+
```python
467+
if img.find("snip-(BBOX_953_531_997_686).png"):
468+
do_my_stuff()
469+
```
470+
471+
You can also specify the bounding box separately, or not at all:
472+
473+
```python
474+
img.find(("my_pic.png", (953, 531, 997, 686))) # bbox separate from name
475+
img.find("my_pic.png") # search the whole screen
476+
```
477+
478+
Note that using the same bbox the image was snipped with means searching that exact
479+
position on the screen. To search a more broad position but not the whole screen, set
480+
coordinates appropriately.
481+
482+
Fuzzy search allows you to search for an image like what you supplied:
483+
484+
```python
485+
img.find("my_pic.png", precision=0.8)
486+
```
487+
488+
---
489+
490+
To wait for an image:
491+
492+
```python
493+
img.wait_for("my_pic.png")
494+
```
495+
496+
This will regularly take screenshots and check if the picture is found on screen.
497+
498+
You can control the search interval and timeout:
499+
500+
```python
501+
img.wait_for("my_pic.png", timeout=15, interval=0.5)
502+
```
503+
504+
---
505+
506+
If you expect one of the images to appear, use `wait_for_one_of`:
507+
508+
```python
509+
yes_btn = "yes.png", (-100, 213, -56, 412)
510+
no_btn = "no(BBOX_-100_213_-56_412).png"
511+
close_btn = "close.png"
512+
513+
btn = img.wait_for_one_of([yes_btn, no_btn, close_btn])
514+
515+
if btn == yes_btn:
516+
continue_flow()
517+
elif btn == no_btn:
518+
warn()
519+
elif btn == close_btn:
520+
close_app()
521+
else:
522+
raise ValueError
523+
```
524+
525+
Each image may have a different bounding box.
526+
527+
---
528+
529+
Similar functions for pixel:
530+
531+
```python
532+
"f2": img.pixel_str(pyperclip.copy)
533+
```
534+
535+
Press `f2` once, and you'll get a string of RGB color and xy-position of the pixel to your clipboard, like:
536+
537+
`"(255, 255, 255), (1919, 1079)"`
538+
539+
Or get the same thing as tuples:
540+
541+
```python
542+
def my_pixel_callback(color: tuple[int, int, int], coords: tuple[int, int]):
543+
...
544+
545+
"f2": img.pixel_info(my_pixel_callback)
546+
```
547+
548+
There is an immediate, non-callback option:
549+
550+
```python
551+
my_pixel = pixel_get_color(1920 - 1, 1080 - 1)
552+
assert my_pixel == (64, 45, 182)
553+
```
554+
555+
---
556+
557+
Use the string from `img.pixel_str` to find or wait for a pixel:
558+
559+
```python
560+
my_pixel_precise_coords = (255, 255, 255), (1919, 1079)
561+
if img.pixel_find(my_pixel_precise_coords):
562+
...
563+
564+
if img.pixel_wait_for((255, 255, 255), (1000, 1020, 1919, 1079)): # search in wider area
565+
...
566+
567+
yes_btn_pixel = (67, 240, 13), (560, 780) # will only be searched for in one spot
568+
no_btn_pixel = (255, 13, 13), None # will be searched for on the whole screen, as bbox=None
569+
if img.pixel_wait_for_one_of([yes_btn_pixel, no_btn_pixel]):
570+
...
571+
```
572+
573+
---
574+
575+
Functions for image and pixel allow searching and snipping an image instead of screen.
576+
577+
This may be a pathname, or numpy-array.
578+
579+
This exists for efficiency, as taking screenshot every time in a loop or a function is slow.
580+
581+
```python
582+
sct = img.get_snip()
583+
for i in range(100):
584+
bbox = (i, i, i + 100, i + 1)
585+
if img.find((my_img_of_horizontal_line_100_by_1_px, bbox), sct):
586+
return i
587+
```
588+
440589
## Config
441590

442591
See [config file](https://github.com/IGalat/tapper/blob/master/src/tapper/config.py) for information on what you can configure.

docs/dev/plans.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ By having user configure triggers and corresponding actions, and running a loop
4747
23. Tree and config validation with tkinter warnings, Error handling, Logging
4848
24. Moar trackers: kb lang, process, device connect/disconnect, service/daemon, file, resource(cpu/gpu/network) load
4949
25. Group actions when it goes on/off, timer for checking group states? Tap without trigger, only for on/off active
50-
26. Helpers galore: picture assist, on repeat/hold, ?
50+
26. DONE. Helpers galore: picture assist, on repeat/hold
5151
27. Good docs, with sphinx/readthedocs/doctest
5252
28. Optimization: profile everything, make caching, active tracking, for window controller in particular
5353

src/tapper/helper/img.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ def wait_for_one_of(
111111
no_btn = "no(BBOX_-100_213_-56_412).png"
112112
close_btn = "close.png"
113113
114-
if (btn := img.wait_for_one_of([yes_btn, no_btn, close_btn], timeout=2) == yes_btn:
114+
btn = img.wait_for_one_of([yes_btn, no_btn, close_btn])
115+
116+
if btn == yes_btn:
115117
continue_flow()
116118
elif btn == no_btn:
117119
warn()

0 commit comments

Comments
 (0)