Skip to content

Commit 9d2d1ee

Browse files
committed
Update dev version
Update dev version * Add function that get x, y pixel rgb or rgba * Add tests
1 parent e93d8ce commit 9d2d1ee

File tree

12 files changed

+127
-5
lines changed

12 files changed

+127
-5
lines changed

.github/workflows/dev_python3_10.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ jobs:
3030
run: python ./test/unit_test/screen/screen_test.py
3131
- name: Test Screenshot
3232
run: python ./test/unit_test/screen/screenshot_test.py
33+
- name: Test Screen Get Pixel
34+
run: python ./test/unit_test/screen/screen_test.py
3335
- name: Save Screenshot Image
3436
uses: actions/upload-artifact@v4
3537
with:

.github/workflows/dev_python3_11.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ jobs:
3030
run: python ./test/unit_test/screen/screen_test.py
3131
- name: Test Screenshot
3232
run: python ./test/unit_test/screen/screenshot_test.py
33+
- name: Test Screen Get Pixel
34+
run: python ./test/unit_test/screen/screen_test.py
3335
- name: Save Screenshot Image
3436
uses: actions/upload-artifact@v4
3537
with:

.github/workflows/dev_python3_12.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ jobs:
3030
run: python ./test/unit_test/screen/screen_test.py
3131
- name: Test Screenshot
3232
run: python ./test/unit_test/screen/screenshot_test.py
33+
- name: Test Screen Get Pixel
34+
run: python ./test/unit_test/screen/screen_test.py
3335
- name: Save Screenshot Image
3436
uses: actions/upload-artifact@v4
3537
with:

je_auto_control/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
# import screen
109109
from je_auto_control.wrapper.auto_control_screen import screen_size
110110
from je_auto_control.wrapper.auto_control_screen import screenshot
111+
from je_auto_control.wrapper.auto_control_screen import get_pixel
111112

112113
__all__ = [
113114
"click_mouse", "mouse_keys_table", "get_mouse_position", "press_mouse", "release_mouse",
@@ -125,5 +126,5 @@
125126
"generate_xml_report", "get_dir_files_as_list", "create_project_dir", "start_autocontrol_socket_server",
126127
"callback_executor", "package_manager", "get_special_table", "ShellManager", "default_shell_manager",
127128
"RecordingThread", "send_key_event_to_window", "send_mouse_event_to_window", "windows_window_manage",
128-
"ScreenRecorder"
129+
"ScreenRecorder", "get_pixel"
129130
]

je_auto_control/linux_with_x11/screen/x11_linux_screen.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,19 @@
77
if sys.platform not in ["linux", "linux2"]:
88
raise AutoControlException(linux_import_error)
99

10+
from Xlib import X
1011
from je_auto_control.linux_with_x11.core.utils.x11_linux_display import display
1112

12-
1313
def size() -> Tuple[int, int]:
1414
"""
1515
get screen size
1616
"""
1717
return display.screen().width_in_pixels, display.screen().height_in_pixels
18+
19+
20+
def get_pixel_rgb(x: int, y: int) -> Tuple[int, int, int]:
21+
root = display.Display().screen().root
22+
root.get_image(x, y, 1, 1, X.ZPixmap, 0xffffffff)
23+
raw = root.get_image(x, y, 1, 1, X.ZPixmap, 0xffffffff)
24+
pixel = tuple(raw.data)[:3] # (R, G, B)
25+
return pixel

je_auto_control/osx/screen/osx_screen.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import sys
2+
import ctypes
3+
from ctypes import c_void_p, c_double, c_uint32
24
from typing import Tuple
35

46
from je_auto_control.utils.exception.exception_tags import osx_import_error
@@ -15,3 +17,62 @@ def size() -> Tuple[int, int]:
1517
get screen size
1618
"""
1719
return Quartz.CGDisplayPixelsWide((Quartz.CGMainDisplayID())), Quartz.CGDisplayPixelsHigh(Quartz.CGMainDisplayID())
20+
21+
def get_pixel(x: int, y: int) -> Tuple[int, int, int, int]:
22+
# Load CoreGraphics and CoreFoundation frameworks
23+
cg = ctypes.CDLL("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics")
24+
cf = ctypes.CDLL("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")
25+
26+
# Define CGRect structure as 4 doubles: x, y, width, height
27+
CGRect = ctypes.c_double * 4
28+
29+
# Function signatures
30+
cg.CGWindowListCreateImage.argtypes = [CGRect, c_uint32, c_uint32, c_uint32]
31+
cg.CGWindowListCreateImage.restype = c_void_p
32+
33+
cg.CGImageGetDataProvider.argtypes = [c_void_p]
34+
cg.CGImageGetDataProvider.restype = c_void_p
35+
36+
cg.CGDataProviderCopyData.argtypes = [c_void_p]
37+
cg.CGDataProviderCopyData.restype = c_void_p
38+
39+
cf.CFDataGetLength.argtypes = [c_void_p]
40+
cf.CFDataGetLength.restype = ctypes.c_long
41+
42+
cf.CFDataGetBytePtr.argtypes = [c_void_p]
43+
cf.CFDataGetBytePtr.restype = ctypes.POINTER(ctypes.c_ubyte)
44+
45+
cf.CFRelease.argtypes = [c_void_p]
46+
cf.CFRelease.restype = None
47+
48+
# Constants
49+
kCGWindowListOptionOnScreenOnly = 1
50+
kCGNullWindowID = 0
51+
kCGWindowImageDefault = 0
52+
rect = CGRect(x, y, 1.0, 1.0)
53+
img = cg.CGWindowListCreateImage(rect,
54+
kCGWindowListOptionOnScreenOnly,
55+
kCGNullWindowID,
56+
kCGWindowImageDefault)
57+
if not img:
58+
raise RuntimeError("Unable to capture screen image. Please ensure Screen Recording permission is granted.")
59+
60+
# Get the data provider from the image
61+
provider = cg.CGImageGetDataProvider(img)
62+
# Copy image data
63+
cfdata = cg.CGDataProviderCopyData(provider)
64+
# Get length of data
65+
length = cf.CFDataGetLength(cfdata)
66+
# Get pointer to byte data
67+
buf = cf.CFDataGetBytePtr(cfdata)
68+
69+
# Default pixel format is BGRA
70+
b, g, r, a = buf[0], buf[1], buf[2], buf[3]
71+
72+
# Release CoreFoundation objects to avoid memory leaks
73+
cf.CFRelease(cfdata)
74+
cf.CFRelease(provider)
75+
cf.CFRelease(img)
76+
77+
return r, g, b, a
78+

je_auto_control/windows/screen/win32_screen.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import sys
2-
from typing import List, Union
2+
from typing import List, Union, Tuple
33

44
from je_auto_control.utils.exception.exception_tags import windows_import_error
55
from je_auto_control.utils.exception.exceptions import AutoControlException
@@ -11,10 +11,30 @@
1111

1212
_user32: ctypes.windll.user32 = ctypes.windll.user32
1313
_user32.SetProcessDPIAware()
14+
_gdi32 = ctypes.windll.gdi32
1415

1516

1617
def size() -> List[Union[int, int]]:
1718
"""
1819
get screen size
1920
"""
2021
return [_user32.GetSystemMetrics(0), _user32.GetSystemMetrics(1)]
22+
23+
24+
def get_pixel(x: int, y: int, hwnd: int = 0) -> Tuple[int, int, int]:
25+
dc = _user32.GetDC(hwnd)
26+
if not dc:
27+
raise RuntimeError("GetDC failed")
28+
29+
try:
30+
pixel = _gdi32.GetPixel(dc, x, y)
31+
if pixel == 0xFFFFFFFF:
32+
raise RuntimeError("GetPixel failed")
33+
34+
r = pixel & 0xFF
35+
g = (pixel >> 8) & 0xFF
36+
b = (pixel >> 16) & 0xFF
37+
return r, g, b
38+
finally:
39+
_user32.ReleaseDC(hwnd, dc)
40+

je_auto_control/windows/window/windows_window_manage.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,11 @@ def set_foreground_window(hwnd) -> None:
4646
def set_window_positon(hwnd, position: int) -> None:
4747
swp_no_size = 0x0001
4848
swp_no_move = 0x0002
49-
user32.SetWindowPos(hwnd, position, 0, 0, 0, 0, swp_no_move | swp_no_size)
49+
user32.SetWindowPos(hwnd, position, 0, 0, 0, 0, swp_no_move | swp_no_size)
50+
51+
def show_window(hwnd, size: int) -> None:
52+
if size < 0 or size > 3:
53+
size = 3
54+
user32.ShowWindow(hwnd, size)
55+
user32.SetForegroundWindow(hwnd)
56+

je_auto_control/wrapper/auto_control_screen.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,18 @@ def screenshot(file_path: str = None, screen_region: list = None) -> List[int]:
5252
autocontrol_logger.info(
5353
f"screen_size, file_path: {file_path}, screen_region: {screen_region}, "
5454
f"failed: {repr(error)}")
55+
56+
57+
def get_pixel(x: int, y: int, hwnd=None):
58+
autocontrol_logger.info(f"get_pixel, x: {x}, y: {y}, hwnd: {hwnd}")
59+
param = locals()
60+
try:
61+
if hwnd is None:
62+
return screen.get_pixel(x, y)
63+
else:
64+
return screen.get_pixel(x, y, hwnd)
65+
except Exception as error:
66+
record_action_to_list("AC_get_pixel", None, repr(error))
67+
autocontrol_logger.info(
68+
f"get_pixel, x: {x}, y: {y}, hwnd: {hwnd}"
69+
f"failed: {repr(error)}")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
66

77
[project]
88
name = "je_auto_control_dev"
9-
version = "0.0.125"
9+
version = "0.0.126"
1010
authors = [
1111
{ name = "JE-Chen", email = "[email protected]" },
1212
]

0 commit comments

Comments
 (0)