You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Sources/pyOpenRPA/Robot/Screen.py

311 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from pyautogui import *
import pyscreeze
import ctypes
from pyOpenRPA.Tools import CrossOS
if CrossOS.IS_WINDOWS_BOOL:
from pywinauto import win32defines, win32structures, win32functions
def BoxCreate(inTopInt:int, inLeftInt:int, inHeightInt:int, inWidthInt:int) -> pyscreeze.Box:
"""Создать экземпляр прямоугольной области.
!ВНИМАНИЕ! Координаты inTopInt, inLeftInt определяют верхний левый край прямоугольной области.
:param inTopInt: Координата левой верхней точки в пикселях по оси X (горизонталь)
:type inTopInt: int
:param inLeftInt: Координата левой верхней точки в пикселях по оси Y (вертикаль)
:type inLeftInt: int
:param inHeightInt: Расстояние вниз от левой верхней точки в пикселях
:type inHeightInt: int
:param inWidthInt: Расстояние вправо от левой верхней точки в пикселях
:type inWidthInt: int
"""
return pyscreeze.Box(top = inTopInt, left = inLeftInt, height = inHeightInt, width = inWidthInt)
def BoxNormalize(*inArgList, **inAgrDict) -> list:
pass
def BoxMoveTo(inBox, inDXInt=None, inDYInt=None):
"""Переместить прямоугольную область (сохранить длину/ширину).
!ВНИМАНИЕ! ПОДДЕРЖИВАЕТ ПАКЕТНУЮ ОБРАТКУ ПРИ ПЕРЕДАЧЕ СПИСКА ЭКЗЕМПЛЯРОВ BOX
.. code-block:: python
# Screen: Взаимодействие с экраном
from pyOpenRPA.Robot import Screen
# Вариант изменения 1-го элемента
# Создать пробную прямоугольную область
lBox = Screen.BoxCreate(inTopInt=10, inLeftInt=10, inHeightInt=10, inWidthInt=10)
# Переместить пробную прямоугольную область
lBox = Screen.BoxMoveTo(lBox, inDXInt=100, inDYInt=200)
:param inBox: Экземпляр класса прямоугольной области Box
:type inBox: pyscreeze.Box
:param inDXInt: Смещение левой верхней координаты по оси X в пикселях (горизонтальная ось).
:type inDXInt: int, опциональный
:param inDYInt: Смещение левой верхней координаты по оси Y в пикселях (вертикальная ось).
:type inDYInt: int, опциональный
:return: Экземпляр класса прямоугольной области Box
:rtype: pyscreeze.Box
"""
if type(inBox) is list:
lResult = []
for lBox in inBox:
lResult.append(BoxMoveTo(lBox, inDXInt=inDXInt, inDYInt=inDYInt))
return lResult
else:
lTopInt = inBox.top
lLeftInt = inBox.left
if inDXInt: lLeftInt = inBox.left + inDXInt
if inDYInt: lTopInt = inBox.top + inDYInt
return BoxCreate(inTopInt=lTopInt, inLeftInt=lLeftInt,
inHeightInt=inBox.height, inWidthInt=inBox.width)
def BoxModify(inBox, inDWidthInt=None, inDHeightInt=None, inPointRuleStr="CC"):
"""Изменить ширину / высоту прямоугольной области.
!ВНИМАНИЕ! ПОДДЕРЖИВАЕТ ПАКЕТНУЮ ОБРАТКУ ПРИ ПЕРЕДАЧЕ СПИСКА ЭКЗЕМПЛЯРОВ BOX
!ВНИМАНИЕ! ЕСЛИ СМЕЩЕНИЕ ПРИВЕДЕТ К ОБРАЗОВАНИЮ ДРОБНОГО ЧИСЛА, БУДЕТ ВЫПОЛНЕНО ОКРУГЛЕНИЕ ПО МАТЕМАТИЧЕСКИМ ПРАВИЛАМ
.. code-block:: python
# Screen: Взаимодействие с экраном
from pyOpenRPA.Robot import Screen
# Вариант изменения 1-го элемента
# Создать пробную прямоугольную область
lBox = Screen.BoxCreate(inTopInt=10, inLeftInt=10, inHeightInt=10, inWidthInt=10)
# Скорректировать пробную прямоугольную область
lBox2 = Screen.BoxModify(lBox,10,10,"CC"); print(lBox2)
lBox2 = Screen.BoxModify(lBox,10,10,"LU"); print(lBox2)
lBox2 = Screen.BoxModify(lBox,10,10,"LD"); print(lBox2)
lBox2 = Screen.BoxModify(lBox,10,10,"RU"); print(lBox2)
lBox2 = Screen.BoxModify(lBox,10,10,"RD"); print(lBox2)
:param inBox: Экземпляр класса прямоугольной области Box
:type inBox: pyscreeze.Box
:param inDXInt: Смещение левой верхней координаты по оси X в пикселях (горизонтальная ось).
:type inDXInt: int, опциональный
:param inDYInt: Смещение левой верхней координаты по оси Y в пикселях (вертикальная ось).
:type inDYInt: int, опциональный
:param inPointRuleStr: Символьное указание точки (подробнее см. выше), относительно которой выполнить изменение прямоугольной области. Допустимые значения: "CC" (по умолчанию), "LU", "LD", "RD", "RU"
:type inPointRuleStr: str, опциональный
:return: Экземпляр класса прямоугольной области Box
:rtype: pyscreeze.Box
"""
if type(inBox) is list:
lResult = []
for lBox in inBox:
lResult.append(BoxModify(lBox, inDWidthInt=inDWidthInt, inDHeightInt=inDHeightInt, inPointRuleStr=inPointRuleStr))
return lResult
else:
lTopInt = inBox.top
lLeftInt = inBox.left
lWidthInt = inBox.width + inDWidthInt
lHeightInt = inBox.height + inDHeightInt
inPointRuleStr = inPointRuleStr.upper() # ВЕРХНИЙ РЕГИСТР
if inDWidthInt: # Изменения по ширине
if "C" in inPointRuleStr:
lLeftInt = round(lLeftInt - inDWidthInt / 2)
elif "R" in inPointRuleStr:
lLeftInt = lLeftInt - inDWidthInt
if inDHeightInt: # Изменения по высоте
if "C" in inPointRuleStr:
lTopInt = round(lTopInt - inDHeightInt / 2)
elif "D" in inPointRuleStr:
lTopInt = lTopInt - inDHeightInt
return BoxCreate(inTopInt=lTopInt, inLeftInt=lLeftInt,
inHeightInt=lHeightInt, inWidthInt=lWidthInt)
def BoxDraw(inBox, inColorStr='green',inThicknessInt = 2):
""" Выполнить подсветку прямоугольной области inBox на экране
!ВНИМАНИЕ! РАБОТАЕТ ТОЛЬКО НА ОС WINDOWS
!ВНИМАНИЕ! ПОДДЕРЖИВАЕТ ПАКЕТНУЮ ОБРАТКУ ПРИ ПЕРЕДАЧЕ СПИСКА ЭКЗЕМПЛЯРОВ BOX
.. code-block:: python
# Screen: Взаимодействие с экраном
from pyOpenRPA.Robot import Screen
# ВАРИАНТ ОТРИСОВКИ 1ГО ЭЛЕМЕНТА
# Создать пробную прямоугольную область
lBox = Screen.BoxCreate(inTopInt=10, inLeftInt=10, inHeightInt=10, inWidthInt=10)
Screen.BoxDraw(lBox)
# ВАРИАНТ ПАКЕТНОЙ ОТРИСОВКИ
# Создать пробную прямоугольную область
lBox = Screen.BoxCreate(inTopInt=10, inLeftInt=10, inHeightInt=100, inWidthInt=100)
lBox2 = Screen.BoxCreate(inTopInt=60, inLeftInt=60, inHeightInt=100, inWidthInt=100)
Screen.BoxDraw([lBox, lBox2])
:param inBox: Экземпляр класса прямоугольной области Box
:type inBox: pyscreeze.Box
:param inColorStr: цвет подсветки прямоугольной области. Варианты: 'red', 'green', 'blue'. По умолчанию 'green'
:type inColorStr: str, необязательный
:param inThicknessInt: толщина подсветки прямоугольной области. По умолчанию 2
:type inThicknessInt: int, необязательный
"""
if type(inBox) is list:
for lBox in inBox:
BoxDraw(inBox=lBox, inColorStr=inColorStr,inThicknessInt = inThicknessInt)
else:
fill = win32defines.BS_NULL
if inBox is not None:
"""
Draw an outline around the window.
* **inColorStr** can be either an integer or one of 'red', 'green', 'blue'
(default 'green')
* **inThicknessInt** inThicknessInt of rectangle (default 2)
* **fill** how to fill in the rectangle (default BS_NULL)
"""
# don't draw if dialog is not visible
#if not lWrapperObject.is_visible():
# return
colours = {
"green": 0x00ff00,
"blue": 0xff0000,
"red": 0x0000ff,
}
# if it's a known colour
if inColorStr in colours:
inColorStr = colours[inColorStr]
# create the pen(outline)
pen_handle = win32functions.CreatePen(
win32defines.PS_SOLID, inThicknessInt, inColorStr)
# create the brush (inside)
brush = win32structures.LOGBRUSH()
brush.lbStyle = fill
brush.lbHatch = win32defines.HS_DIAGCROSS
brush_handle = win32functions.CreateBrushIndirect(ctypes.byref(brush))
# get the Device Context
dc = win32functions.CreateDC("DISPLAY", None, None, None )
# push our objects into it
win32functions.SelectObject(dc, brush_handle)
win32functions.SelectObject(dc, pen_handle)
# draw the rectangle to the DC
win32functions.Rectangle(
dc, inBox.left, inBox.top, inBox.left+inBox.width, inBox.top+inBox.height)
# Delete the brush and pen we created
win32functions.DeleteObject(brush_handle)
win32functions.DeleteObject(pen_handle)
# delete the Display context that we created
win32functions.DeleteDC(dc)
def BoxAnchorRuleCheck(inBox, inAnchorBox=None, inAnchorRuleStr=None) -> bool:
"""Выполнить проверку соответствия всем условиям вхождения inBox в inAnchorBox с учетом правил inAnchorRule
.. code-block:: python
# Screen: Взаимодействие с экраном
from pyOpenRPA.Robot import Screen
lBox1 = Screen.BoxCreate(inTopInt=265, inLeftInt=62, inHeightInt=100, inWidthInt=90)
lBox2 = Screen.BoxCreate(inTopInt=160, inLeftInt=160, inHeightInt=100, inWidthInt=100)
lBox3 = Screen.BoxCreate(inTopInt=460, inLeftInt=60, inHeightInt=100, inWidthInt=100)
l = Screen.BoxAnchorRuleCheck(inBox=lBox1, inAnchorBox=[lBox2,lBox3], inAnchorRuleStr=["LD","CUS"])
Screen.BoxDraw([lBox1,lBox2,lBox3])
:param inBox: Экземпляр класса прямоугольной области Box
:type inBox: pyscreeze.Box
:param inAnchorBox: Экземпляр класса прямоугольной области Box
:type inAnchorBox: pyscreeze.Box или list из pyscreeze.Box
:param inAnchorRuleStr: Символьное указание области проверки соответствия. Может принимать единственное значение (единый формат для всех inAnchorBox), или список форматов для каждого inAnchorBox (если inAnchorBox является списком Box)
:type inAnchorRuleStr: str или list из str
:return: True - соответствует всем правилам
:rtype: bool
"""
# Формирование стартовых переменных
if inAnchorBox is None: inAnchorBox = []
if type(inAnchorBox) is not list:
inAnchorBox = [inAnchorBox]
lAnchorRuleStr = "CC,S"
if inAnchorRuleStr is None or inAnchorRuleStr=="": inAnchorRuleStr = [lAnchorRuleStr]
if type(inAnchorRuleStr) is not list:
inAnchorRuleStr = [inAnchorRuleStr]
lResult = True
# Дополнение списка правил до длины якорей, если они расходятся и список правил равен длине 1 или 0 (по умолчанию CC,S)
if len(inAnchorRuleStr)==1 and len(inAnchorBox)==1:
if inAnchorRuleStr[0]=="" or inAnchorRuleStr[0] is None:
inAnchorRuleStr = [lAnchorRuleStr]
elif len(inAnchorRuleStr)==1 and len(inAnchorBox)!=1:
if inAnchorRuleStr[0]!="" and inAnchorRuleStr[0] is not None:
lAnchorRuleStr = inAnchorRuleStr[0]
if len(inAnchorRuleStr) != len(inAnchorBox):
inAnchorRuleStr = []
for lItem in inAnchorBox:
inAnchorRuleStr.append(lAnchorRuleStr)
for lIndexInt, lItemBox in enumerate(inAnchorBox): # Остановиться, если итог False
lItemRuleStr = inAnchorRuleStr[lIndexInt].upper()
print(lItemRuleStr)
# Подготовка вспомогательных областей
lScreenWidthPXInt = 9999
lScreenHeightPXInt = 5555
lAnchorLUBox = BoxCreate(inTopInt=0, inLeftInt=0, inHeightInt=lItemBox.top, inWidthInt=lItemBox.left)
lAnchorRUBox = BoxCreate(inTopInt=0, inLeftInt=lItemBox.left+lItemBox.width, inHeightInt=lItemBox.top, inWidthInt=lScreenWidthPXInt)
lAnchorCUBox = BoxCreate(inTopInt=0, inLeftInt=lItemBox.left, inHeightInt=lItemBox.top, inWidthInt=lItemBox.width)
lAnchorLCBox = BoxCreate(inTopInt=lItemBox.top, inLeftInt=0, inHeightInt=lItemBox.height, inWidthInt=lItemBox.left)
lAnchorRCBox = BoxCreate(inTopInt=lItemBox.top, inLeftInt=lItemBox.left+lItemBox.width, inHeightInt=lItemBox.height, inWidthInt=lScreenWidthPXInt)
lAnchorLDBox = BoxCreate(inTopInt=lItemBox.top+lItemBox.height, inLeftInt=0, inHeightInt=lScreenHeightPXInt, inWidthInt=lItemBox.left)
lAnchorRDBox = BoxCreate(inTopInt=lItemBox.top+lItemBox.height, inLeftInt=lItemBox.left+lItemBox.width, inHeightInt=lScreenHeightPXInt, inWidthInt=lScreenWidthPXInt)
lAnchorCDBox = BoxCreate(inTopInt=lItemBox.top+lItemBox.height, inLeftInt=lItemBox.left, inHeightInt=lScreenHeightPXInt, inWidthInt=lItemBox.width)
#import pdb
#pdb.set_trace()
if "S" not in lItemRuleStr: # Проверка без S - Strict
lResult = False
# Алгоритм проверки соответствия хотя бы на одно вхождение
if ("CC" in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lItemBox)==True: lResult = True
elif ("LU" in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorLUBox)==True: lResult = True
elif ("RU" in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorRUBox)==True: lResult = True
elif ("CU" in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorCUBox)==True: lResult = True
elif ("LC" in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorLCBox)==True: lResult = True
elif ("RC" in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorRCBox)==True: lResult = True
elif ("LD" in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorLDBox)==True: lResult = True
elif ("RD" in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorRDBox)==True: lResult = True
elif ("CD" in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorCDBox)==True: lResult = True
if lResult == False: break # Остановиться, если итог False
else: # Проверка с S - Strict
lResult = True
# Алгоритм проверки соответствия хотя бы на одно вхождение для того сегмента, который недоступен
if ("CC" not in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lItemBox)==True: lResult = False
elif ("LU" not in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorLUBox)==True: lResult = False
elif ("RU" not in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorRUBox)==True: lResult = False
elif ("CU" not in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorCUBox)==True: lResult = False
elif ("LC" not in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorLCBox)==True: lResult = False
elif ("RC" not in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorRCBox)==True: lResult = False
elif ("LD" not in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorLDBox)==True: lResult = False
elif ("RD" not in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorRDBox)==True: lResult = False
elif ("CD" not in lItemRuleStr) and BoxOverlay(inBox1=inBox, inBox2=lAnchorCDBox)==True: lResult = False
if lResult == False: break # Остановиться, если итог False
return lResult
def BoxOverlay(inBox1, inBox2) -> bool:
"""Проверить наложение 2-х прямоугольных областей друг на друга.
.. code-block:: python
# Screen: Взаимодействие с экраном
from pyOpenRPA.Robot import Screen
lBox1 = Screen.BoxCreate(inTopInt=10, inLeftInt=10, inHeightInt=100, inWidthInt=1000)
lBox2 = Screen.BoxCreate(inTopInt=160, inLeftInt=160, inHeightInt=100, inWidthInt=100)
Screen.BoxDraw([lBox1, lBox2])
Screen.BoxOverlay(lBox1,lBox2)
:param inBox1: Экземпляр класса прямоугольной области Box
:type inBox1: pyscreeze.Box
:param inBox2: Экземпляр класса прямоугольной области Box
:type inBox2: pyscreeze.Box
:return: True - inBox1 наложен на inBox2
:rtype: bool
"""
return not ((inBox1.left>inBox2.left + inBox2.width or inBox2.left>inBox1.left + inBox1.width) or (inBox1.top>inBox2.top + inBox2.height or inBox2.top>inBox1.top + inBox1.height))