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

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))