From 76e1a301b434c6dee4f1dab7fdd62383bd76193d Mon Sep 17 00:00:00 2001 From: noubar Date: Thu, 17 Oct 2024 13:51:29 +0200 Subject: [PATCH] Squashed commit of the following: commit 6cc09dd824da3fc45c555eb14da100da8187a5e0 Author: Dave Amies Date: Sat Feb 26 14:09:19 2022 +1000 My tweaks to atest to make it run locally commit 2dd9d9d5d653c82b273f8c6bd29ef7111af538f0 Author: Dave Amies Date: Sat Feb 26 14:07:38 2022 +1000 Will now detect the screen scaling ratio and adjust This update will now detect the screen scaling ratio and adjust accordingly. I tested it using "tests/atest/calculator.robot" on a mac with a Retina display running OSX 12.0.1 I don't have a windows machine to test on, but based on comments in [pyautogui issue #589](https://github.com/asweigart/pyautogui/issues/589) this should work for the various windows scaling ratios. This replaces the self.has_retina which wasn't working as it was returning false on my machine even though it does indeed have a retina display. __get_pixel_ratio should only get called the first time _locate is called, that was my experience in testing, though perhaps it should be called every time as windows users could potentially change their display scaling during the test. Also I don't know of an OS that supports it now but I guess it's possible in the future an OS may have a scaling factor less than 100% and this change won't support that either. Issue #62 --- .../recognition/_recognize_images.py | 13 ++++++++++--- tests/atest/calculator.robot | 9 +++++---- .../{ => close_button}/close_button.png | Bin .../close_button/close_button_macos.png | Bin 0 -> 6484 bytes .../calculator/inputs_folder/inputs_macos.png | Bin 0 -> 5243 bytes .../calculator/{ => or_button}/or_button.png | Bin .../calculator/or_button/or_button_macos.png | Bin 0 -> 5616 bytes 7 files changed, 15 insertions(+), 7 deletions(-) rename tests/atest/reference_images/calculator/{ => close_button}/close_button.png (100%) create mode 100644 tests/atest/reference_images/calculator/close_button/close_button_macos.png create mode 100644 tests/atest/reference_images/calculator/inputs_folder/inputs_macos.png rename tests/atest/reference_images/calculator/{ => or_button}/or_button.png (100%) create mode 100644 tests/atest/reference_images/calculator/or_button/or_button_macos.png diff --git a/src/ImageHorizonLibrary/recognition/_recognize_images.py b/src/ImageHorizonLibrary/recognition/_recognize_images.py index b2ed9d5..d0a2bc4 100644 --- a/src/ImageHorizonLibrary/recognition/_recognize_images.py +++ b/src/ImageHorizonLibrary/recognition/_recognize_images.py @@ -12,6 +12,11 @@ class _RecognizeImages(object): + pixel_ratio = 0.0 + + def __get_pixel_ratio(self): + self.pixel_ratio = ag.screenshot().size[0]/ag.size().width + def __normalize(self, path): if (not self.reference_folder or not isinstance(self.reference_folder, str) or @@ -203,9 +208,11 @@ def try_locate(ref_image): center_point = ag.center(location) x = center_point.x y = center_point.y - if self.has_retina: - x = x / 2 - y = y / 2 + if self.pixel_ratio == 0.0: + self.__get_pixel_ratio() + if self.pixel_ratio>1: + x = x / self.pixel_ratio + y = y / self.pixel_ratio return (x, y) def does_exist(self, reference_image): diff --git a/tests/atest/calculator.robot b/tests/atest/calculator.robot index 8fd167d..7b7afe5 100755 --- a/tests/atest/calculator.robot +++ b/tests/atest/calculator.robot @@ -1,20 +1,21 @@ *** Settings *** -Library ImageHorizonLibrary ${CURDIR}${/}reference_images${/}calculator screenshot_folder=${OUTPUT_DIR} +Library ${CURDIR}${/}..${/}..${/}..${/}robotframework-imagehorizonlibrary${/}src${/}ImageHorizonLibrary ${CURDIR}${/}reference_images${/}calculator screenshot_folder=${OUTPUT_DIR} *** Test cases *** Calculator Set Confidence 0.9 - Launch application python tests/atest/calculator/calculator.py + Launch application python3 tests/atest/calculator/calculator.py ${location1}= Wait for inputs_folder timeout=30 Click to the above of ${location1} 20 Type 1010 Click to the below of ${location1} 20 Type 1001 - ${location2}= Locate or_button.png + ${location2}= Locate or_button Click to the below of ${location2} 0 Click to the below of ${location2} 50 + Sleep 0.1 ${result}= Copy Should be equal as integers ${result} 1011 - Click Image close_button.png + Click Image close_button [Teardown] Terminate application diff --git a/tests/atest/reference_images/calculator/close_button.png b/tests/atest/reference_images/calculator/close_button/close_button.png similarity index 100% rename from tests/atest/reference_images/calculator/close_button.png rename to tests/atest/reference_images/calculator/close_button/close_button.png diff --git a/tests/atest/reference_images/calculator/close_button/close_button_macos.png b/tests/atest/reference_images/calculator/close_button/close_button_macos.png new file mode 100644 index 0000000000000000000000000000000000000000..c7d4ef296857eb7ff9190508f4b327150d33eef4 GIT binary patch literal 6484 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

78C=3hN-m)$;9^*&tt zT4mv{JF`5POP4(V;Y+2BqW|HV@f(u!==&f6^NuyTu^PavuY&jfl6HMf(AfcIa|KtY zS|7QfvxV_~Laqsx){ra9+o}CZ7PuN5ekWyQ4hg6r2)%|tui_TB7h?5pfop4Py1CkS zbJE;DXX;K%x$3>y7Jp8xXEdfP-PPxj~#Pi|N&Dk)?4+E{xe9j5ZamKr3lMSfl z3w!`CRl7JlEIrzmhdXB-+exK@KW0*}chQUE3(c)uc}bLh|Nm=PpR3GWpcHwdC<;fo-rvCn)#wK=y6F&B@NHmly8SO?=rF4ep=! z5^joiZPPQGB?cUAygI?gbWmj(pU9e;!Ropc3T%^!H7z(d#w|a(672l1-C$nnU!Q&dK2oSj_2{Voi-@DWA2KE{#Q-o1M8HO$64U zp~vo$(OQY9%bw0e;uc?p@Iw>TSWPZo2P1d96luS$6{BQaFAdduZA=IY$Vt9~%gN^` z?O0S;y%$UuynicG%z1vC{D?`)lYl9TEiwIj7~yw~%z9R@%adKsRZ#JRudkIw1r}F) zO`O(xQ+XyqUN-8gu_S?!q}pQEGbKj}=lpMZ6x-y08LCOFbQ8S1VIih*-HrmR}W zE5n>8W<#}yna;HSqwdn$H`bm@xZOfo7kqeJ7n z`G@}j-eEh102}M0;mmL&bGUnSe&yS=+_WU`)7sHjz>>`>Fk8XBo5x|W&+mfHFKl(7 zxpQ*KC+9Av2GJZ9lh*XlGsRuf^0LG05hS^!TifzjK6;L2Ju&wwn1OOS(F@!@Qbs?{ zccHKD@9U$LtGF{HnXoe5-0;%4H0|y+kzFJ@Tb zIaUdKyMe5#J6I~@P@T`p5PQY*BDvu=cJpuNvmy$2hUGs$7ytH*WS(!x2gjvcJnEs@ z&@9o(9t~q3uV^4#s;_(5;J@$iKG%lHZK6^^=b2EL0LA|D>3s&F4n8U<7SMOFI>LR* zmU3sg+GFd4VXPCOcAO3Yz=QZX0XoyvjKD5yi$ zLlo!x7g;TN8+gSvK;i*itt}1Dt{AFazi4SU66(>y!1}C{i3=vEyV7K5RIylOu=NY# zW4pPJQO+H&q;B6Wi|rxpAKaH_N=9q^!BViwAN6WXnsR>uy?swUTxx&m^P~HBKW-Yz zM7A2MG$&;_06sS;*oi1uKYgUvuWtxmjg6<&Yz;W1etR)hjnJff%GGTIHpM+T#eXm;xZ)E>U3Z`J z7C+_3Ym=60uxUReF6ama;wv@#ZW>vwT+3R+pGMNXML&(E^xT-ND`gOKN{&0?&{5RP z%|l2hydCxVDXC`QvUSVB0l02+`YB+ZvDyJhMD6lIfVJ%gSEp#&Siroz?dRG~&WW%h zbk`SiU1qhijCwSIbTaLrsrfnvA0s{jO0p(OnZK#&BI^aNa+sW_+K@Mpv2!lxxxqE2 sWg!#Jq88`KUw+sT*74l=k?M}eolOe@U8XzAzrOF)m35RV?pTNa51WR)_5c6? literal 0 HcmV?d00001 diff --git a/tests/atest/reference_images/calculator/inputs_folder/inputs_macos.png b/tests/atest/reference_images/calculator/inputs_folder/inputs_macos.png new file mode 100644 index 0000000000000000000000000000000000000000..6df1a72bf717211821982a26b412ebfc98fae7eb GIT binary patch literal 5243 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 gUdzAqf5Xm+0tXdgs2cm8i~k