1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246 |
- # -*- coding:utf-8 -*-
- import os
- from enum import Enum
- from BaseLog import CBaseLog
- from ExtraData import CExtraData
- from OptionExcel import COptionExcel
- from OptionConfig import COptionConfig
- from OptionFocus import COptionFocus
- from OptionOCR import COptionOCR
- from ssat_sdk.tv_detect import *
- from ssat_sdk.device_manage.capturecard_manager import CCardManager
- from ssat_sdk.utils.string_util import strcmp
- g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth',
- 'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh', 'Twelfth']
- def strSplit(text):
- ret = []
- str_int = ''
- str_ch = ''
- ch_last = ' '
- for ch in text:
- if 47 < ord(ch) < 58:
- str_int += ch
- if str_ch.__len__():
- ret.append(str_ch)
- str_ch = ''
- else:
- if 47 < ord(ch_last) < 58 and ch == '.':
- str_int += ch
- if str_ch.__len__():
- ret.append(str_ch)
- str_ch = ''
- else:
- str_ch += ch
- if str_int.__len__():
- ret.append(str_int)
- str_int = ''
- ch_last = ch
- if str_ch.__len__():
- ret.append(str_ch)
- if str_int.__len__():
- ret.append(str_int)
- return ret
- # 枚举:value类型
- class VType(Enum):
- SelectType = 0
- InputType = 1
- RangeType = 2
- # 注意:所有不对外暴露的变量和函数需要私有化,以明确哪些接口和参数是对外的。
- # 这样便于后期维护时,根据对外的变量和函数来做处理。
- class COptionAction(CBaseLog):
- # ==============设备对象============== #
- # 红老鼠遥控对象;
- __redRat3 = TvOperator()
- # 创建视频采集对象
- __captureCard = CCardManager()
- # 图片切割对象
- __imgCMP = ImageCMP()
- def __init__(self, optionName, optionValue, optionConfig, optionExcel):
- CBaseLog.__init__(self)
- # 层级位置;
- self.__pos = 0
- # 目标option;
- self.__optionName = optionName
- # __optionValue可空;
- self.__optionValue = optionValue
- self.__optionExcel = optionExcel
- self.__optionConfig = optionConfig
- # 焦点定位及文字识别;
- self.__optionFocus = COptionFocus(optionConfig)
- self.__optionOCR = COptionOCR(optionConfig, optionExcel)
- if self.__optionExcel is None:
- self.error(u"表格对象空")
- # ==============常用对象数据==============;
- self.__optionPaths = self.__optionExcel.getOptionPaths(self.__optionName)
- # 如果__optionValue空则不取value表任何内容;
- if self.__optionValue != "" or self.__optionValue is not None:
- self.__optionValueInfo = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
- self.__optionInfo = self.__optionExcel.getOptionInfo(self.__optionName)
- # 当前状态下的变量,与__pos对应;
- self.__curOptionName = ''
- self.__curOptionInfo = None
- self.__prevOptionName = ''
- self.__prevOptionInfo = None
- # 到达value节点后,记录value值(一般只用于range() 数值记录,其他值无意义);
- self.__optionValueText = ""
- # 获取一次当前层级信息;
- self.getCurOptionInfo()
- # 相关返回操作;
- self.__isEnterKeyInValue = False # 节点在value表时,是否发送enter key.
- self.__valueType = VType.SelectType # 节点在value表时,value值的类型(0:选择型, 1:输入型,2:range()数值型。)
- # 是否在Value层
- self.__isOnValueSheet = False
- @property
- def pos(self):
- return self.__pos
- @property
- def optionName(self):
- return self.__optionName
- @property
- def optionValue(self):
- return self.__optionValue
- @property
- def curOptionName(self):
- return self.__curOptionName
- @property
- def curOptionInfo(self):
- return self.__curOptionInfo
- @property
- def optionValueText(self):
- return self.__optionValueText
- @property
- def optionValueInfo(self):
- return self.__optionValueInfo
- @property
- def optionPaths(self):
- return self.__optionPaths
- '''
- 函数:截图并返回截图路径,如果当前option/optionValue含有layout坐标参数,则返回坐标截图;
- 参数:无
- 返回:截图路径;
- '''
- def takePicture(self):
- img = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
- COptionAction.__captureCard.takePicture(img)
- if not os.path.exists(img):
- self.error(u"截图失败:%s" % img)
- retImg = img
- # 判断当前处于option层还是value层
- if self.__pos >= self.__optionPaths.__len__():
- curInfo = self.__optionValueInfo
- else:
- curInfo = self.__curOptionInfo
- # 判断当前层是否存在layout参数
- if "layout" in curInfo and 'bounds' in curInfo['layout']:
- if curInfo['layout']['bounds'] is ['']:
- self.info("当前option未配置layout参数")
- elif curInfo['layout']['bounds'].__len__() != 4:
- self.info("当前option的layout坐标配置异常:%s" % str(curInfo['layout']['bounds']))
- else:
- self.info("当前option的layout坐标为:%s" % str(curInfo['layout']['bounds']))
- retImg = os.path.join(getSATTmpDIR(), "menutree_runpath_layout.png")
- COptionAction.__imgCMP.saveCropPic(img, retImg, curInfo['layout']['bounds'])
- return retImg
- '''
- 函数:调用根节点快捷键(中间节点不需要快捷键;);
- 参数:无
- 返回:无
- '''
- def callFirstOptionShortCutKey(self):
- # 获取父节点等待时间(如果没找到,getParentWaitTime默认返回1)。
- waitTime = self.__optionConfig.getParentWaitTime(self.__optionPaths['First']['parent'])
- # 是否有另配置快捷按键代替根节点按键;
- '''
- 由于NT72561的source区分了tv信源与非tv信源,为了统一脚本,故others(first_key)新增逻辑:
- 1、优先读取option的others(first_key)信息;
- 2、options的others信息没有的情况下,读取parent的others(first_key)信息
- '''
- if 'shortcut_key' in self.__optionPaths['First']:
- self.sendKey(self.__optionPaths['First']['shortcut_key'], 1, waitTime)
- elif self.__optionPaths['First']['option_others'] != "":
- others = json.loads(self.__optionPaths['First']['option_others'])
- if 'first_key' in others:
- self.info(u"option:%s包含first_key信息,将使用first_key进入。\noption first_key:%s"%(self.__optionPaths['First']['option'], others['first_key']))
- keyList = others['first_key']
- for key in keyList:
- self.sendKey(key, 1, waitTime)
- elif self.__optionPaths['First']['others'] != "":
- others = json.loads(self.__optionPaths['First']['others'])
- if 'first_key' in others:
- self.info(u"parent:%s包含first_key信息,将使用first_key进入。\nparent first_key:%s"%(self.__optionPaths['First']['parent'], others['first_key']))
- keyList = others['first_key']
- for key in keyList:
- self.sendKey(key, 1, waitTime)
- else:
- self.sendKey(self.__optionPaths['First']['parent'], 1, waitTime)
- self.warn(u"表格没有shortcut_key字段,执行默认的parent按键:%s" % self.__optionPaths['First']['parent'])
- '''
- 函数:调用当前结点的toparent_key(叫back_key会简单点)
- 参数:
- curOptionName 当前层级的目标节点.
- 返回:无
- '''
- def callCurOptionBackKey(self, curOptionName):
- curOptionInfo = self.__optionExcel.getOptionInfo(curOptionName)
- if 'toparent_key' in self.__optionPaths[curOptionInfo['level']]:
- self.sendKey(self.__optionPaths[curOptionInfo['level']]['toparent_key'])
- else:
- self.error(u"表格没有toparent_key字段,执行默认按键return")
- self.sendKey('return')
- '''
- 函数:
- 参数:
- 返回:
- '''
- def inputUnlock(self, stdText, password=''):
- # 如果锁住,按ok会弹出输入密码框;
- self.sendKey("ok")
- # 获取密码;
- if password.__len__() == 0:
- password = self.__optionConfig.getSuperPassword("Password", "super")
- # 发送按键;
- for key in list(password):
- self.sendKey(key, 1, 0.2)
- time.sleep(1)
- # 发送ok键;
- self.sendKey('ok')
- # 判断是否成功输入密码;
- textPic = self.takePicture()
- # 遍历ocr类型;
- found = False
- ocrDict = [{"lan": "ChinesePRC+English", "type": 4}, {"lan": "ChinesePRC+English", "type": 253},
- {"lan": "ChinesePRC+English", "type": 10001}]
- for item in ocrDict:
- # 识别ocr;
- ocrText = self.__optionOCR.getImageText(textPic, item, {})
- ocrText = unicode(ocrText).lower()
- self.info("lan=%s,type=%d,ocr=%s" % (item["lan"], item["type"], ocrText))
- if ocrText in stdText or stdText == ocrText:
- found = True
- break
- return not found
- '''
- 函数:处理弹出密码框.
- 参数:
- 返回:Boolean, Boolean。
- 如何没有密码框处理,返回False,False。
- 如果有,且成功输入密码后密码框消失,返回True, True
-
- 注意:对应以前的dealOthers函数。
- '''
- def dealPasswordBox(self):
- parentOption = ''
- if self.isOnValueSheet():
- if self.__optionValueInfo is None or self.__optionValueInfo.__len__() == 0:
- self.error(u"当前value[%s]信息空" % str(self.__optionValue))
- return False, False
- others = self.__optionValueInfo['others']
- if others is None or others.__len__() == 0:
- self.info(u"[%s]others字段空" % str(self.__curOptionName))
- return False, False
- # 获取父节点
- parentOption = self.__optionValueInfo['option']
- else:
- getStatus, nextOptionInfo = self.getNextOptionInfo()
- if self.__curOptionInfo is None or self.__curOptionInfo.__len__() == 0:
- self.error(u"当前option[%s]信息空" % str(self.__curOptionName))
- return False, False
- others = self.__curOptionInfo['others']
- if others is None or others.__len__() == 0:
- self.info(u"[%s]others字段空" % str(self.__curOptionName))
- return False, False
- # 获取父节点;
- parentOption = self.__curOptionInfo['parent']
- # 转换为字典;
- others = json.loads(others)
- if "password" not in others:
- self.info(u"[%s]others没有密码框处理" % str(self.__curOptionName))
- return False, False
- password = self.__optionConfig.get_value("Password", others["password"])
- # 发送密码前,停2秒(因为像6586机芯响应很慢,密码框还没弹出就完成了密码输入的操作);
- time.sleep(2)
- # 发送按键;
- for key in list(password):
- self.sendKey(key, 1, 0.2)
- time.sleep(1)
- # 发送ok键;
- if not strcmp(others["enter_key"], "default"):
- self.sendKey(others["enter_key"])
- # 判断是否成功输入密码;
- current_uiPic = self.takePicture()
- # 此处findRectByIcon参数3传递的不是first_parent,而是当前option的parent;
- found, contourRect = self.__optionFocus.findFocusByIcon(current_uiPic, parentOption)
- return True, not found
- '''
- 函数:是否在父节点菜单上。一般在执行了callFirstOptionShortCutKey后调用;
- 参数:无
- 返回:Boolean, 数组(坐标)。 如:True, [0,0,1920,1080]
- 注意:由于所有父节点上的子项都共用一个图片定位参数,所以只要随意一个父节点的子项option即可获取定位参数;
- 测试:。
- '''
- def isOnFirstOption(self):
- pic = self.takePicture()
- return self.__optionFocus.findFocusByIcon(pic, self.__optionPaths['First']['option'])[0]
- '''
- 函数:是否在当前节点(移动后,判断是否移动到下一目标节点)上.
- 说明:
- 每次移动到下一目标节点(option)上时,self.__pos + 1,表示移动到下一层路径。
- 当self.__pos >= self.__optionPaths.__len__()时,表示到达value表格;
- 所以,该类的重点在self.__pos的移动;
- 参数:
- staticPic 静态图片路径。
- 主要用于将截图和聚焦、ocr识别分离,然后可以截图和聚焦中间添加其他处理。
- 返回:Boolean, 识别的文本/数字;
- 示例:
- '''
- def isOnTargetNode(self, staticPic=None):
- # 是否在value表中;
- isValueSheet = self.isOnValueSheet()
- self.info(u"当前层级在:%s" % ("value表" if isValueSheet else "路径表"))
- # 析出参数;
- if isValueSheet:
- curLevel = 'value'
- curParent = self.__optionPaths[g_level[self.__pos - 1]]['parent']
- curOption = self.__optionValueInfo['option']
- curOthers = self.__optionValueInfo['others']
- else:
- curLevel = g_level[self.__pos]
- curParent = self.__optionPaths[curLevel]['parent']
- curOption = self.__optionPaths[curLevel]['option']
- curOthers = self.__optionPaths[curLevel]['others']
- self.info("当前[%s]others=[%s]" % (curOption, curOthers))
- if curOthers.__len__() == 0:
- curOthers = {}
- else:
- curOthers = json.loads(curOthers)
- firstParent = self.__optionPaths['First']['parent']
- # 获取文本识别的参数;
- ocrConfigList = self.__optionConfig.getOptionOCRConfig(curOption)
- ocrThreshold = self.__optionConfig.getThresholdDict(firstParent) # 注意,此处使用firstParent;
- # 获取当前option的ocr值/value name下所有ocr值;
- if isValueSheet:
- if curOption.lower() == self.__optionName.lower():
- optionTextList = self.__optionExcel.getOptionValueText(curOption, self.__optionValue)
- else:
- optionTextList = self.__optionExcel.getOptionValueText(curOption)
- else:
- optionTextList = self.__optionExcel.getOptionText(curOption)
- # 获取option下所有兄弟项的ocr:option字典内容;
- siblingTextDict = self.__optionExcel.getOptionAllSiblingItemDict(curOption, not isValueSheet)
- # 获取所有option兄弟项(包括自己)的ocr值;
- siblingTextList = list(siblingTextDict.keys())
- # 是否获取数值文本;
- isNumberText = False
- # 如果是value表,且兄弟项文本为range
- # 注:value表中的option实际并没有兄弟项,取的是所有value项
- if isValueSheet and siblingTextList.__len__():
- if siblingTextList[0].startswith('range('):
- self.info(u"识别的内容是value表数字内容(range(x,y)类型)")
- isNumberText = True
- # 清除之前的value值;
- self.__optionValueText = ""
- # 是否为静态焦点识别(动态则为跑马灯);
- if curOthers.__len__() and 'marquee' in curOthers:
- return self.__getDynamicPicText(curOption, optionTextList, siblingTextList, ocrConfigList,
- ocrThreshold, curOthers['marquee'],
- isNumberText, isValueSheet)
- else:
- isFocus, isTarget, text = self.__getStaticPicText(self.takePicture() if staticPic is None else staticPic,
- curOption,
- optionTextList,
- siblingTextList,
- ocrConfigList,
- ocrThreshold,
- isNumberText, isValueSheet)
- return isTarget, text
- # endif
- '''
- 函数:是否移到目标节点上(在isOnOption后,判断__pos位置是否在__paths最后);
- 参数:无
- 返回:Boolean.
-
- '''
- def isOnTargetOption(self):
- return True if self.__pos == (self.__optionPaths.__len__() - 1) else False
- '''
- 函数:当前节点是否在value sheet层级中;
- 参数:无
- 返回:Boolean
- '''
- def isOnValueSheet(self):
- if self.__optionValue == "": # 该值空,表明不会移动到value sheet中;
- return False
- else:
- return self.__isOnValueSheet
- '''
- 函数:移动到下一兄弟节点;
- 参数:无
- 返回:无
-
- 注意:
- sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
- 同时,按键等待时间,应该有所区分。
-
- 如果不截图,可以不考虑sendKey的等待时间.
- '''
- def move2NextSiblingNode(self):
- # 析出move key;
- if self.isOnValueSheet():
- valueMoveKey = self.__optionValueInfo['move_key']
- self.sendKey(valueMoveKey[1], 1, 1)
- else:
- optionMoveKey = self.__curOptionInfo['option_move_key']
- if optionMoveKey.__len__() == 0:
- self.sendKey(self.__curOptionInfo['move_key'][1], 1, 1)
- else:
- self.sendKey(optionMoveKey[1], 1, 1)
-
- '''
- 函数:移动到上一兄弟节点
- 参数:无
- 返回:无
-
- 注意:
- sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
- 同时,按键等待时间,应该有所区分。
-
- 如果不截图,可以不考虑sendKey的等待时间.
- '''
- def move2PrevSiblingNode(self):
- # 析出move key;
- if self.isOnValueSheet():
- valueMoveKey = self.__optionValueInfo['move_key']
- self.sendKey(valueMoveKey[0], 1, 1)
- else:
- optionMoveKey = self.__curOptionInfo['option_move_key']
- if optionMoveKey.__len__() == 0:
- self.sendKey(self.__curOptionInfo['move_key'][0], 1, 1)
- else:
- self.sendKey(optionMoveKey[0], 1, 1)
- '''
- 函数:返回到父节点
- 参数:
- 返回:无
- 注意:
- sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
- 同时,按键等待时间,应该有所区分。
- 如果不截图,可以不考虑sendKey的等待时间.
- '''
- def back2ParentNode(self):
- # 获取当前option信息;
- if self.isOnValueSheet():
- tKeyDict = self.__optionValueInfo['toparent_key']
- peKeyDict = self.__prevOptionInfo['enter_key']
- else:
- tKeyDict = self.__curOptionInfo['toparent_key']
- peKeyDict = self.__prevOptionInfo['enter_key']
- '''
- 说明:
- toparent_key由于旧menutree无此参数,分以下数种情况:
- 当toparent_key没有配时:
- 是否为value层:
- 如果为value层,上一父层的enter_key是否为default:
- 如果是default,则toparent_key也按default处理;
- 如果不是default,则toparent_key按照return处理;
- 如果不为value层:
- toparent_key统一按return处理。
- 如果配了toparent_key:
- toparent_key是否为default:
- 如果是,按照default处理:
- 如果不是,按照配置的key来处理。
- tKeyDict中是否包含tolevel字段;
- 如果不包含,执行完toparent_key后self.__pos自减1
- 如果包含,执行完toparent_key后self.__pos等于tolevel指定的层级
- '''
- # 当toparent_key没有配时;
- if tKeyDict == "" or tKeyDict['key'][0] == "":
- # 是否为value层:
- if self.isOnValueSheet():
- # 如果为value层,上一父层的enter_key是否为default
- if strcmp(peKeyDict["key"][0], "default"):
- # 如果是default,则toparent_key也按default处理;
- self.info("当前处于value层,未配toparent_key,且父层enter_key为default,默认toparent_key为default")
- tKeyDict = {'key': ["default", ]}
- else:
- self.info("当前处于value层,未配toparent_key,toparent_key为return")
- # 如果不是default,则toparent_key按照return处理;
- tKeyDict = {'key': ["return", ]}
- else:
- self.info("当前层未配toparent_key,toparent_key为return")
- # 如果不为value层,toparent_key统一按return处理。
- tKeyDict = {'key': ["return", ]}
- # 如果配了toparent_key
- else:
- # toparent_key是否为default
- if strcmp(tKeyDict['key'][0], "default"):
- # 如果是default,则toparent_key也按default处理;
- self.info("当前层toparent_key为default")
- else:
- self.info("当前层toparent_key为%s" % str(tKeyDict['key']))
- self.__executeToParentKey(tKeyDict)
- # 从value层执行完返回后,则不在valuesheet里了
- self.__isOnValueSheet = False
- '''
- 在忽略大小写的情况下,获取到该level对应的g_level的下标值
- '''
- def __getLevelIndex(self, level):
- s_level = []
- for lvStr in g_level:
- s_level.append(lvStr.lower())
- return s_level.index(level)
- '''
- 函数:进入当前节点,只对路径节点有效,value节点不处理;
- 参数:无
- 返回:无
- '''
- def enterNode(self):
- # 析出enter key;
- if self.isOnValueSheet():
- if not self.__isEnterKeyInValue:
- self.info(u"节点已在value上,且没有触发过enter key")
- enterKey = self.__optionValueInfo['enter_key']
- self.__executeEnterKey(enterKey)
- self.__isEnterKeyInValue = True
- else:
- self.info(u"节点已在value上,已触发过enter key")
- else:
- # 优先使用option本身的enter_key
- optionEnterKey = self.__curOptionInfo['option_enter_key']
- # 如果option本身的enter_key为空,则使用该层parent的enter_key
- if optionEnterKey['key'] == "":
- optionEnterKey = self.__curOptionInfo['enter_key']
- self.__executeEnterKey(optionEnterKey)
- '''
- 函数:设置当前节点位置;
- 参数:
- pos: 外部节点位置值。
- 返回:无
-
- 注意:此函数用于外部创建两个【路径具体子集关系】的COptionAction对象,才可以使用此函数。
- 假设, a对象路径{p,p1,p2,p3,p4, v1},b = {p, p1, p2, p3, p4, p5, p6, v2}, c = {p, p2, p5, p6, v3}
- 其中, v表示value值,不属于路径。那么,a和b具有子集关系, a与c或b与c都不具有子集关系。
-
- a移动到p4上,完成了v1设置后,a.back2ParentNode()后,此时如果要操作b并设置v2,就要b.SetCurPos(a.pos).
- '''
- def setCurPos(self, pos):
- if pos < 0 or pos > self.__optionPaths.__len__():
- self.error(u"pos值[%d]超出路径范围:[0-%d]" % (pos, self.__optionPaths.__len__()))
- return
- self.__pos = pos
- # 变更后要重新读一次当前option
- self.getCurOptionInfo()
- '''
- 函数:设置目标option的值, 只设置数值型value和输入型value(选择型value不需要此步骤).
- 参数:无
- 返回:无
-
- 注意:此函数必须是已聚焦在目标value节点上,否则无效。
- 重要:
- 在此函数enter后,UI是否返回到上一层父节点上,还是停留在本层节点不返回。
- 建议在excel中配置这个关键信息,以便此函数可以正确更改self.__pos的值。
-
- '''
- def setOptionValue(self):
- self.info(u"【在此函数enter后,UI是否返回到上一层父节点上,还是停留在本层节点不返回。\
- 建议在excel中配置这个关键信息,以便此函数可以正确更改self.__pos的值。】")
- if type(self.__optionValue) == str and self.__optionValue.__len__() == 0:
- self.error(u"[%s]的值为空,没有设置的值" % self.__optionName)
- return
- enterKey = self.__optionValueInfo['enter_key']
- moveKey = self.__optionValueInfo['move_key']
- valueText = self.__optionValueInfo['value_for_ocr']
- others = self.__optionValueInfo['others']
- if others.__len__():
- others = json.loads(others)
- else:
- others = {}
- # 是否有按键延时值;<注意:有些地方duration=0.1时,会变成长按的效果。>
- duration = float(others['duration']) if "duration" in others else 0.2
- # 值类型:
- # 0 表示默认选择类型.
- # 1 表示输入类型(有些输入完了,正确值会自动进入).
- # 2 表示进度条数据型.
- self.__valueType = VType.SelectType
- # 是否为数字文本(特指:range(0, 100));
- isNumberText = self.isNumberText(valueText)
- # 数值型value;
- if isNumberText:
- if moveKey[0] == 'input':
- # 标记值类型;
- self.__valueType = VType.InputType
- # 将数值转成字符;
- optionValue = self.__optionValue
- if type(optionValue) == int or type(optionValue) == float:
- optionValue = str(self.__optionValue)
- # 再将字符转成list;
- chList = list(optionValue)
- self.sendKey(chList, 1, duration)
- else:
- # 标记值类型;
- self.__valueType = VType.RangeType
- # 相差值;
- num = int(self.__optionValue) - int(self.__optionValueText)
- # 正->往右或下,负->往左或上;
- self.sendKey(moveKey[1] if num > 0 else moveKey[0], abs(num), duration)
- elif moveKey[0] == 'input':
- # 标记值类型;
- self.__valueType = VType.InputType
- # 将字符转成list;
- chList = list(self.__optionValue)
- self.sendKey(chList, 1, duration)
- # 最后,如果有进入键执行;
- if not strcmp(enterKey['key'][0], 'default') and enterKey['key'][0] != "":
- self.info(u"value节点具有enter key")
- self.__executeEnterKey(enterKey)
- '''
- 函数:获取当前层级的目标option详细信息.
- 参数:无
- 返回:Boolean, 获取成功返回True
- '''
- def getCurOptionInfo(self):
- if self.__optionPaths is None or self.__optionPaths.__len__() == 0:
- self.error(u"paths路径空")
- return False
- if 'First' not in self.__optionPaths:
- self.error(u"构建的paths不连续,路径存在断点")
- self.error(u"当前paths内容:%s"%self.__optionPaths)
- return False
- if self.__pos >= self.__optionPaths.__len__():
- # 有可能存在使用openOption打开某个option,但是下一层未到value层的情况。
- # 此时pos会等于整个路径,但是我们要取的数据又不在value层。需要返回该option下任意一个子option的参数
- outResult, parentData = self.__optionExcel.getParentInfo(self.__optionName)
- if outResult:
- for optionName in parentData['option']:
- outResult, outData = self.__optionExcel.getOptionInfo(optionName)
- if outResult:
- self.__curOptionInfo = outData
- self.info(u"当前使用openOption方式打开目标option:%s,下一层未到value层,将以其子option:%s的参数信息作为返回值"%(self.__optionName, optionName))
- break
- else:
- self.warn(u"获取到了目标option:%s在下一层存在parent配置,但无法获取到任何子option信息!"%self.__optionName)
- return False
- else:
- # self.__curOptionInfo = None # 是否需要在到达value层级后置空,可能保留值会有比较多用处!
- self.warn(u"已到达value节点,无法获取路径信息")
- self.__prevOptionName = self.__curOptionName
- self.__prevOptionInfo = self.__curOptionInfo
- self.__isOnValueSheet = True
- return False
- # 只有第一次或层级移动了才需要更新;
- if self.__curOptionInfo is None or self.__pos != self.__curOptionInfo['layers']:
- self.__curOptionName = self.__optionPaths[g_level[self.__pos]]['option']
- outResult, outData = self.__optionExcel.getOptionInfo(self.__curOptionName, self.__optionPaths)
- if outResult is False:
- return False
- self.__curOptionInfo = outData
- # 当处于第二层或以后的层级时,读取前一层的数据
- if self.__pos > 0:
- # 是否在value层
- if not self.isOnValueSheet():
- self.__prevOptionName = self.__optionPaths[g_level[self.__pos - 1]]['option']
- outResult, outData = self.__optionExcel.getOptionInfo(self.__prevOptionName, self.__optionPaths)
- if outResult is False:
- return False
- self.__prevOptionInfo = outData
- # 特殊情况:如果只一层且该层与value相同(信源source,要在value表中配置一份相同配置)。
- # if self.__curOptionInfo['first_parent'] == self.__curOptionName:
- # # 自动进入value表;
- # self.__pos += 1
- return True
- '''
- 函数:返回下一个option的详细信息;
- 参数:无
- 返回:Boolean、option info。
- '''
- def getNextOptionInfo(self):
- if self.__optionPaths is None or self.__optionPaths.__len__() == 0:
- self.error(u"paths路径空")
- return False, None
- if self.__pos + 1 >= self.__optionPaths.__len__():
- self.warn(u"已到达value节点,无法获取路径信息")
- return False, None
- nextOptionName = self.__optionPaths[g_level[self.__pos + 1]]['option']
- return self.__optionExcel.getOptionInfo(nextOptionName, self.__optionPaths)
- '''
- 函数:获取指定位置的option信息;
- 参数:
- pos 指定的层级位置;
- 返回:
- 成功返回option信息,失败返回None
- '''
- def getOptionInfo(self, pos):
- if pos > self.__optionPaths.__len__() and pos < 0:
- return None
- outResult, outData = self.__optionExcel.getOptionInfo(self.__optionPaths[g_level[pos]]['option'],
- self.__optionPaths)
- if outResult is False:
- return None
- return outData
- '''
- 函数:指定option是否存在该路径中;
- 参数:
- optionName 指定的option名称;
- 返回:
- 存在返回True+位置, 不存在返回False+位置;
- '''
- def isOptionInPaths(self, pos, optionNameOrValueName):
- bExist = False
- # 在路径表中;
- if 0 < pos < self.__optionPaths.__len__():
- posOptionName = self.__optionPaths[g_level[pos]]['option'].lower()
- siblingText = self.__optionExcel.getOptionAllSiblingItemName(posOptionName)
- for name in siblingText:
- if name.lower() == optionNameOrValueName.lower():
- bExist = True
- break
- # 在value表中;
- if self.isOnValueSheet():
- valueOptionName = self.__optionPaths[g_level[self.__optionPaths.__len__() - 1]]['option']
- valueNames = self.__optionExcel.getOptionAllValueName(valueOptionName)
- for name in valueNames:
- if name.lower() == optionNameOrValueName.lower():
- bExist = True
- break
- return bExist
- '''
- 函数:检测路径是否有效;
- 参数:无
- 返回:Boolean, 检测正常返回True
- '''
- def checkRunOptionPath(self):
- outData = self.__optionExcel.checkOptionPaths(self.__optionPaths)
- if str(outData[1]) == 'NoExit':
- self.error(u"表格中不存在到达Option:[%s]的路径,在表格中排查到达该Option路径" % self.__optionName)
- return False
- if str(outData[1]) == 'Fail':
- self.error(u"表格中到达Option:[%s]的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % self.__optionName)
- return False
- return True
- '''
- 函数:指定的value_for_ocr或option_for_ocr数组是否为range(xx,xx)类型。
- 参数:
- textList value_for_ocr或option_for_ocr数组,一般只可能会是value_for_ocr
- 返回:Boolean, 如果是range(xx,xx)类型返回True.
- '''
- def isNumberText(self, textList):
- # 是否获取数值文本;
- isNumberText = False
- # 如果是value表,且兄弟项文本为range
- # 注:value表中的option实际并没有兄弟项,取的是所有value项
- if self.isOnValueSheet() and textList.__len__():
- if textList[0].startswith('range('):
- self.info(u"识别的内容是value表数字内容(range(0,xx)类型)")
- isNumberText = True
- return isNumberText
- '''
- 函数:获取静态图片文本内容
- 参数:(略,请看调用函数)
- 注意:
- 返回:Boolean、Boolean、文本识别内容。
- 是否成功聚焦、是否聚焦在目标节点上、聚焦框识别的文本内容。
- '''
- def __getStaticPicText(self, pic, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold,
- isNumberText, isValueSheet, aliveKey=None):
- # 获取图片焦点框;
- found, focusBox = self.__optionFocus.findFocusByIcon(pic, optionName, isValueSheet)
- if found is False:
- self.debug(u"未找到[%s]聚集框" % optionName)
- return False, False, None
- # 如果有鲜活键;
- self.sendAliveKey(aliveKey)
- # 获取文本区域框;
- textBox = self.__optionFocus.getFocusTextBox(optionName, focusBox, isValueSheet)
- # 配置文本图片路径,保存文本区域截图;
- text_pic = os.path.join(getSATTmpDIR(), "menutree_area_text.png")
- self.__imgCMP.saveCropPic(pic, text_pic, textBox)
- if not os.path.exists(text_pic):
- self.error(u"%s截取文本图片失败:%s" % (optionName, text_pic))
- return False, False, None
- # 是否在某个兄弟项中;
- isOnSibling = False
- # 遍历所有ocr识别选项;
- for ocrConfig in ocrConfigList:
- # 如果有鲜活键;
- self.sendAliveKey(aliveKey)
- # 识别出当前聚焦文本;
- curFocusText = self.__optionOCR.getImageText(text_pic, ocrConfig, ocrThreshold)
- # 判断识别文本是来正常;
- if curFocusText == "ERR<Exp>" or curFocusText.__len__() == 0:
- continue
- # 转成小写;
- curFocusText = curFocusText.lower()
- self.info("[%s]当前识别出的文本=%s" % (optionName, curFocusText))
- # 是否取数字文本(肯定在value节点上);
- if isNumberText is True:
- # 特殊情况处理:某些情况下,会将包含数字以外的区域一起识别;
- curFocusText = curFocusText.strip('>')
- # 将数字分组
- numberTextList = strSplit(curFocusText)
- # 只判断最后一位是否为数字;
- if numberTextList.__len__() < 1:
- self.error(u"当前识别的文本不是数字文本:%s" % curFocusText)
- continue
- try:
- numberText = float(numberTextList[numberTextList.__len__() - 1])
- # 记录value值;
- self.__optionValueText = numberText
- return True, True, numberText
- except Exception:
- continue
- else:
- # 当前option识别的文本与所有兄弟项文本比较;
- for siblingText in siblingTextList:
- # 转为小写,保证所有比较都是小写;
- siblingText = siblingText.lower()
- # 兄弟项文本是否被包含在curFocusText中或相同;
- if siblingText.lower() in curFocusText.lower() or strcmp(siblingText, curFocusText):
- isOnSibling = True
- self.info(u"当前焦点在[%s], 目标焦点为[%s]" % (siblingText, optionName))
- self.info(u"optionTextList:%s" % optionTextList)
- # 再判断,该兄弟项是否为目标节点(curOption);
- for optionText in optionTextList:
- optionText = optionText.lower()
- # 若当前兄弟项为目标option返回True、文本;
- if strcmp(optionText, siblingText):
- return True, True, curFocusText
- # endif
- # endfor
- # 在兄弟项中,退出循环;
- break
- # endif
- # endfor
- if isOnSibling is False:
- self.error(u"未聚集到任何[%s]的兄弟项中" % optionName)
- else:
- self.info("未聚集到目标节点[%s],当前文本=%s" % (optionName, curFocusText))
- return found, False, curFocusText
- # endif
- # endfor
- # 默认返回;
- return found, False, 0 if isNumberText else curFocusText
- '''
- 函数:获取动态图片文本内容
- 参数:(略,请看调用函数)
- 注意:
- 返回:Boolean、文本识别内容。成功识别出文本,返回True及文本内容。
- '''
- def __getDynamicPicText(self, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, marqueeDict,
- isNumberText, isValueSheet):
- # 判断图片是否动态:截图两次,判断两次文本内容是否相同;
- firstFocus, firstTarget, firstText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList,
- siblingTextList,
- [{"lan": "ChinesePRC+English", "type": 10001}],
- ocrThreshold, isNumberText, isValueSheet)
- if firstFocus is False:
- self.error(u"[%s]第一次截图未识别出聚焦框" % optionName)
- return False, None
- # 目标option不是跑马灯,且已经聚焦到了目标option
- elif firstTarget is True:
- self.info(u"已聚焦到了目标option:%s" % optionName)
- return firstTarget, firstText
- # 发送鲜活键, 保证界面鲜活;
- self.sendAliveKey(marqueeDict['alive_key'])
- # 第二次截图;
- secondFocus, secondTarget, secondText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList,
- siblingTextList,
- [{"lan": "ChinesePRC+English", "type": 10001}],
- ocrThreshold, isNumberText, isValueSheet)
- if secondFocus is False:
- self.error(u"[%s]第二次截图未识别出聚焦框" % optionName)
- return False, None
- # 发送鲜活键, 保证界面鲜活;
- self.sendAliveKey(marqueeDict['alive_key'])
- # 比较两文本是否相同;
- if firstText.__len__() and firstText == secondText:
- self.info(u"截图两次,文本(%s)识别相同,聚焦的不是跑马灯Option" % firstText)
- return False, firstText
- elif firstText.__len__() == 0:
- self.warn(u"未能识别出当前option")
- return False, firstText
- # 文本不相同,为动态图片;
- menuList = marqueeDict['menu']
- # 如果只有一项跑马灯,且目标option亦在跑马灯列表中,则直接返回成功结果
- if menuList.__len__() == 1 and (optionName in menuList):
- self.info(u"该层菜单只有一项跑马灯, 找到即成功返回")
- return True, firstText
- picList = []
- # 如果有多项同级option都是跑马灯, 要成功识别文本需要间隔截图5次(大概会成功截图到最全的文本);
- for i in range(0, 5):
- picList.append(self.takePicture())
- # 间隔多久截图;
- time.sleep(marqueeDict['sleep_time'])
- # 发送鲜活键;
- self.sendAliveKey(marqueeDict['alive_key'])
- ocrTextList = []
- # 对截图进行文本识别分析;
- for pic in picList:
- isFocus, isTarget, text = self.__getStaticPicText(pic, optionName, optionTextList, siblingTextList,
- ocrConfigList,
- ocrThreshold, isNumberText, isValueSheet,
- marqueeDict['alive_key'])
- if isTarget is True:
- ocrTextList.append(text)
- # 发送鲜活键;
- self.sendAliveKey(marqueeDict['alive_key'])
- # 过滤重复的字符;
- ocrTextList = self.__removeDuplicateString(ocrTextList)
- self.info(u"识别到的跑马灯ocr文字列表:%s" % ocrTextList)
- # 获取动态文本的option字典;
- dynamicOptionOcrDict = self.__getOptionInfoDict(menuList)
- self.info(u"获取到的跑马灯Option对应的ocr字典:%s" % dynamicOptionOcrDict)
- # 遍历:识别结果与xls结果进行比较;
- for dynamicOption in dynamicOptionOcrDict:
- dynamicOptionOcrList = dynamicOptionOcrDict[dynamicOption]
- for dynamicOptionOcr in dynamicOptionOcrList:
- # 只要有3张满足,判断找到option;
- count = 0
- for ocrText in ocrTextList:
- if ocrText.lower() in dynamicOptionOcr:
- count += 1
- if count >= 3 and optionName == dynamicOption:
- return True, ocrText
- else:
- self.info(u"当前聚焦的跑马灯实际为:%s" % dynamicOption)
- return False, ocrText
- # endfor
- # endfor
- self.info("未能识别到当前聚焦的跑马灯Option")
- return False, 0 if isNumberText else None
- '''
- 函数:获取option名称数组内所有option的详细信息.
- 参数:
- optionNameList option名称数组。
- 返回:字典。
- {
- "option1": {self.__optionExcel.getOptionInfo(option1)[1]},
- "option2": {self.__optionExcel.getOptionInfo(option2)[1]},
- "option3": {self.__optionExcel.getOptionInfo(option3)[1]},
- }
- '''
- def __getOptionInfoDict(self, optionNameList):
- OptionInfoDict = {}
- for optionName in optionNameList:
- found, optionDict = self.__optionExcel.getOptionInfo(optionName)
- if found:
- OptionInfoDict[optionName] = optionDict
- # endif
- # endfor
- return OptionInfoDict
- '''
- 函数:找到两个字符串左边或者右边相同的部分
- 参数:
- str1:
- str2:
- direction 方向,默认为right
- 返回:
- '''
- def __findDuplicateString(self, str1, str2, direction="right"):
- index = 0
- if direction == "right":
- while True:
- index -= 1
- if abs(index) > str1.__len__() or abs(index) > str2.__len__():
- break
- if not str1[index] == str2[index]:
- break
- if index == -1:
- self.info(u"没有找到重复文本")
- return ""
- return str1[index + 1:]
- elif direction == "left":
- while True:
- if abs(index) >= str1.__len__() or abs(index) >= str2.__len__():
- break
- if not str1[index] == str2[index]:
- break
- index += 1
- return str1[:index]
- '''
- 函数:获取路径长度。
- 参数:
- 返回:
- '''
- def getPathLength(self):
- if self.__optionPaths is None:
- self.error(u"路径空,返回0")
- return 0
- self.info(u"路径长度:%d" % self.__optionPaths.__len__())
- return self.__optionPaths.__len__()
- '''
- 函数:去掉字符串数组中每个字符串 左边或右边相同的部分
- 参数:
- strList
- 返回:
- '''
- def __removeDuplicateString(self, strList):
- finishedList = strList
- directionList = ["left", "right"]
- for direction in directionList:
- same_str = self.__findDuplicateString(strList[0], strList[1], direction)
- if same_str == "":
- continue
- else:
- for i in range(2, strList.__len__()):
- same_str = self.__findDuplicateString(same_str, strList[i], direction)
- if same_str == "":
- break
- if same_str != "":
- finishedList = []
- for text in strList:
- if direction == "left":
- text = str[same_str.__len__():]
- else:
- text = str[:-same_str.__len__()]
- finishedList.append(text)
- # endfor
- # endif
- # endif
- # endfor
- return finishedList
- '''
- 函数:发送红老鼠按键;
- 参数:
- key 1、如果是字符串时,当作单个按键; 2、如果是list时,当作多个按键;
- count 执行多少次key;
- wait 1、执行单个key后,等待时长(因为电视机响应遥控需要时间);
- 2、执行list多个key后,每个key的等待时间;
- 返回:无
- '''
- def sendKey(self, key, count=1, duration=1.0):
- if key is not None and key.__len__() > 0:
- if type(key) == list:
- for k in key:
- # 清除前后空格;
- k = k.lstrip()
- k = k.rstrip()
- COptionAction.__redRat3.sendKey(k, 1, duration)
- else:
- key = str(key)
- # 清除前后空格;
- key = key.lstrip()
- key = key.rstrip()
- COptionAction.__redRat3.sendKey(key, count, duration)
- else:
- self.error(u"error:按键内容空")
- '''
- 函数:发送鲜活键;
- 参数:
- aliveKey 鲜活按键;
- 返回:无
- 注意:鲜活键等待时间是0.1秒,因为不考虑截图。
- '''
- def sendAliveKey(self, aliveKey):
- self.sendKey(aliveKey, 1, 0.1)
- '''
- 函数:按照新格式执行enter_key。
- 参数:
- eKeyDict 即enterKey的字典对象,包含的wait和duration等参数。
- wait,duration是为了兼容旧menutree按照others配置参数的处理方法。
- 如果others中有配waitTime和duration,优先采用others里的配置,否则则采用enterKey里的配置。
- '''
- def __executeEnterKey(self, eKeyDict):
- # 先从config读取
- wait = self.__optionConfig.getParentWaitTime(self.__curOptionInfo['parent'])
- # config中读取不到则尝试从others中读取
- if wait == 1.0:
- try:
- others = json.loads(self.__curOptionInfo['others'])
- if "waitTime" in others:
- if others['waitTime'] != "":
- wait = float(others['waitTime'])
- except Exception,e:
- pass
- # 读出来的enter_key,不为空或者default时,按格式执行enter_key
- if not strcmp(eKeyDict['key'][0], 'default') and eKeyDict['key'][0] != "":
- self.__executeKey(eKeyDict, wait=wait)
- if eKeyDict['tolevel'] != "":
- level = eKeyDict['tolevel']
- else:
- level = ""
- # value层的enter_key存在特殊字段,isback。用于判断执行enter_key以后会不会返回父层。
- # isback为0或为""时:
- # 则认为不返回父层;
- # 为1时:
- # 如果enter_key中没有配tolevel,则默认返回上一层父层;
- # 如果有,则默认返回到tolevel指定层级的父层。
- # 非value层,则self.__pos默认进入下一层。
- if self.isOnValueSheet():
- if eKeyDict['isback'] == "" or int(eKeyDict['isback']) == 0:
- pass
- else:
- if level == "":
- self.__pos -= 1
- else:
- self.__pos = self.__getLevelIndex(level) + 1
- else:
- self.__pos += 1
- # 重新获取次信息;
- self.getCurOptionInfo()
- def __executeToParentKey(self, tKeyDict):
- self.__executeKey(tKeyDict)
- # 层级变化
- if 'tolevel' in tKeyDict and tKeyDict['tolevel'] != "":
- level = tKeyDict['tolevel']
- self.__pos = self.__getLevelIndex(level) + 1
- else:
- self.__pos -= 1
- # 重新获取次信息;
- self.getCurOptionInfo()
- def __executeKey(self, keyDict, wait=1.0, duration=1.0):
- if wait != 1.0 and keyDict['wait'] != "":
- wait = float(keyDict['wait'])
- if duration != 1.0 and keyDict['duration'] != "":
- duration = float(keyDict['duration'])
- for key in keyDict['key']:
- self.sendKey(key, 1, duration)
- time.sleep(wait)
- def executeKey(self, keyDict, wait=1.0, duration=1.0):
- self.__executeKey(keyDict, wait, duration)
- def setValue(self, optionValue):
- self.__optionValue = optionValue
- if self.__optionValue != "" or self.__optionValue is not None:
- self.__optionValueInfo = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
- return True if self.__optionValueInfo and self.__optionValueInfo.__len__() else False
- if __name__ == "__main__":
- exData = CExtraData()
- optionExcel = COptionExcel(exData)
- optionConfig = COptionConfig(exData, optionExcel)
- optionAction = COptionAction('source', None, optionConfig, optionExcel)
- # print optionAction.optionValueInfo
- print optionAction.curOptionInfo
- # ====================================== #
- # optionAction.callFirstOptionShortCutKey()
|