123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- # -*- coding:utf-8 -*-
- import os
- from UIT_PathManage import UITPathManage
- 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 takePicture():
- img = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
- COptionAction.__ccard.takePicture(img)
- return img
- 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
- # 注意:所有不对外暴露的变量和函数需要私有化,以明确哪些接口和参数是对外的。
- # 这样便于后期维护时,根据对外的变量和函数来做处理。
- class COptionAction(CBaseLog):
- # ==============设备对象============== #
- # 红老鼠遥控对象;
- __redRat3 = TvOperator()
- # 创建视频采集对象
- __ccard = CCardManager()
- # 图片切割对象
- __imgCMP = ImageCMP()
- # 发送鲜活键;
- @staticmethod
- def __sendAliveKey(aliveKey):
- if aliveKey is not None and aliveKey.__len__() > 0:
- COptionAction.__redRat3.sendKey(aliveKey, 1, 0.1)
- def __init__(self, optionName, optionValue, optionConfig, optionExcel):
- # 层级位置;
- 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 != "":
- self.__optionValues = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
- self.__optionInfo = self.__optionExcel.getOptionInfo(self.__optionName)
- # 当前状态下的变量,与__pos对应;
- self.__curOptionName = ''
- self.__curOptionInfo = None
- # 获取一次当前层级信息;
- self.getCurOptionInfo()
- @property
- def pos(self):
- return self.__pos
- @property
- def curOptionName(self):
- return self.__curOptionName
- @property
- def curOptionInfo(self):
- return self.__curOptionInfo
- '''
- 函数:调用根节点快捷键(中间节点不需要快捷键;);
- 参数:
- 返回:
- '''
- def callFirstOptionShortCutKey(self):
- if 'shortcut_key' in self.__optionPaths['First']:
- COptionAction.__redRat3.sendKey(self.__optionPaths['First']['shortcut_key'])
- else:
- COptionAction.__redRat3.sendKey(self.__optionPaths['First']['parent'])
- self.warn(u"表格没有shortcut_key字段,执行默认的parent按键:%s" % self.__optionPaths['First']['parent'])
- '''
- 函数:调用当前结点的toparent_key
- 参数:
- 返回:
- '''
- def callCurOptionBackKey(self, curOptionName):
- curOptionInfo = self.__optionExcel.getOptionInfo(curOptionName)
- if 'toparent_key' in self.__optionPaths[curOptionInfo['level']]:
- COptionAction.__redRat3.sendKey(self.__optionPaths[curOptionInfo['level']]['toparent_key'])
- else:
- self.error(u"表格没有toparent_key字段,执行默认按键return")
- COptionAction.__redRat3.sendKey('return')
- '''
- 函数:打开指定的option(到达option后,执行enter_key进入)。
- 参数:
- 返回:
- 示例:
- 测试:。
- '''
- def openOption(self, optionName):
- pass
- '''
- 函数:是否在父节点菜单上。一般在执行了callFirstOptionShortCutKey后调用;
- 参数:
- 返回:
- 注意:由于所有父节点上的子项都共用一个图片定位参数,所以只要随意一个父节点的子项option即可获取定位参数;
- 示例:
-
- 测试:。
- '''
- def isOnFirstOption(self):
- pic = takePicture()
- return self.__optionFocus.findFocusByIcon(pic, self.__optionPaths['First']['option'])[0]
- '''
- 函数:是否在目标节点(移动时,每一个目标option,并不仅是终点目标option)上.
- 说明:
- 每次移动到下一目标节点(option)上时,self.__pos + 1,表示移动到下一层路径。
- 当self.__pos >= self.__optionPaths.__len__()时,表示到达value表格;
- 所以,该类的重点在self.__pos的移动;
- 参数:
- 返回:Boolean, 识别的文本/数字;
- 示例:
-
- 测试:。
- '''
- def isOnTargetOption(self):
- # 析出参数;
- curLevel = g_level[self.__pos]
- curParent = self.__optionPaths[curLevel]['parent']
- curOption = self.__optionPaths[curLevel]['option']
- curOthers = json.loads(self.__optionPaths[curLevel]['others'])
- firstParent = self.__optionPaths['First']['option']
- # 是否在value表中;
- if self.__optionValue == "":
- isValueSheet = False
- else:
- isValueSheet = True if self.__pos >= self.__optionPaths.__len__() else False
- self.info(u"当前层级在:%s" % ("value表" if isValueSheet else "路径表"))
- # 获取文本识别的参数;
- ocrConfigList = self.__optionConfig.getOptionOCRConfig(curOption)
- ocrThreshold = self.__optionConfig.getThresholdDict(firstParent) # 注意,此处使用firstParent;
- # 获取当前option的ocr值/value name下所有ocr值;
- if isValueSheet:
- 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(0,xx)类型)")
- isNumberText = True
- # 是否为静态焦点识别(动态则为跑马灯);
- if curOthers.__len__() and 'marquee' in curOthers:
- return self.__getDynamicPicText(curOption, optionTextList, siblingTextList, ocrConfigList,
- ocrThreshold, curOthers['marquee'],
- isNumberText, isValueSheet)
- else:
- return self.__getStaticPicText(takePicture(), curOption, optionTextList, siblingTextList, ocrConfigList,
- ocrThreshold,
- isNumberText, isValueSheet)
- # endif
- '''
- 函数:移动到下一兄弟节点;
- 参数:
- 返回:
- '''
- def move2NextSiblingOption(self):
- # 获取当前option信息;
- self.getCurOptionInfo()
- # 析出move key;
- optionMoveKey = self.__curOptionInfo['option_move_key']
- if optionMoveKey.__len__() == 0:
- self.sendKey(self.__curOptionInfo['move_key'][1], 1, 0.1)
- else:
- self.sendKey(optionMoveKey[1], 1, 0.1)
- '''
- 函数:移动到上一兄弟节点
- 参数:
- 返回:
- '''
- def move2PrevSiblingOption(self):
- # 获取当前option信息;
- self.getCurOptionInfo()
- # 析出move key;
- optionMoveKey = self.__curOptionInfo['option_move_key']
- if optionMoveKey.__len__() == 0:
- self.sendKey(self.__curOptionInfo['move_key'][0], 1, 0.1)
- else:
- self.sendKey(optionMoveKey[0], 1, 0.1)
- '''
- 函数:返回到父节点
- 参数:
- 返回:
- '''
- def back2ParentOption(self):
- # 获取当前option信息;
- self.getCurOptionInfo()
- # 析出move key;
- backKey = self.__curOptionInfo['back_key']
- if backKey.__len__() == 0:
- self.sendKey('back', 1, 0.1)
- else:
- self.sendKey(backKey, 1, 0.1)
- '''
- 函数:进入当前节点;
- 参数:
- 返回:
- '''
- def enterOption(self):
- # 获取当前option信息;
- self.getCurOptionInfo()
- # 析出enter key;
- optionEnterKey = self.__curOptionInfo['option_enter_key']
- if optionEnterKey.__len__() == 0:
- self.sendKey(self.__curOptionInfo['enter_key'], 1, 0.)
- else:
- self.sendKey(optionEnterKey, 1, 0.1)
- '''
- 函数:设置当前option的值;
- 参数:
- 返回:
- '''
- def setCurOptionValue(self, value):
- # 获取当前option信息;
- self.getCurOptionInfo()
- # 获取当前option的value表信息;
- curOptionValueInfo = self.__optionExcel.getOptionValueInfo(self.__curOptionName, value)
- '''
- 函数:
- 参数:
- 返回:
- '''
- def getCurOptionInfo(self):
- if self.__pos >= self.__optionPaths.__len__():
- self.warn(u"已到达value节点,无法获取路径信息")
- return False
- # 只有第一次或层级移动了才需要更新;
- if self.__curOptionInfo is None or self.__pos != self.__curOptionInfo['layers']:
- self.__curOptionName = self.__optionPaths[g_level[self.__pos]]['option']
- self.__curOptionInfo = self.__optionExcel.getOptionInfo(self.__curOptionName, self.__optionPaths)
- return True
- '''
- 函数:获取静态图片文本内容
- 参数:
- 注意:
- 返回:
- 测试:。
- '''
- 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, None
- # 如果有鲜活键;
- COptionAction.__sendAliveKey(aliveKey)
- # 获取文本区域框;
- textBox = self.__optionFocus.getFocusTextBox(optionName, focusBox, isValueSheet)
- # 配置文本图片路径,保存文本区域截图;
- text_pic = os.path.join(getSATTmpDIR(), "meuttree_area_text.png")
- self.__imgCMP.saveCropImage(pic, text_pic, textBox)
- if not os.path.exists(text_pic):
- self.error(u"%s截取文本图片失败:%s" % (optionName, text_pic))
- return False, None
- # 是否在某个兄弟项中;
- isOnSibling = False
- # 遍历所有ocr识别选项;
- for ocrConfig in ocrConfigList:
- # 如果有鲜活键;
- COptionAction.__sendAliveKey(aliveKey)
- # 识别出当前聚焦文本;
- curFocusText = self.__optionOCR.getImageText(text_pic, ocrConfig, ocrThreshold)
- # 判断识别文本是来正常;
- if curFocusText == "ERR<Exp>" or curFocusText.__len__() == 0:
- continue
- self.info("[%s]当前识别出的文本=%s" % (optionName, curFocusText))
- # 是否取数字文本;
- if isNumberText is True:
- # 特殊情况处理:某些情况下,会将包含数字以外的区域一起识别;
- curFocusText = curFocusText.strip('>')
- # 将数字分组
- numberTextList = strSplit(curFocusText)
- # 只判断最后一位是否为数字;
- if numberTextList.__len__() < 1:
- self.error(u"当前识别的文本不是数字文本:%s" % curFocusText)
- continue
- try:
- numberText = numberTextList[numberTextList.__len__() - 1]
- return True, float(numberText)
- except Exception:
- continue
- else:
- # 当前option识别的文本与所有兄弟项文本比较;
- for siblingText in siblingTextList:
- # 转为小写,保证所有比较都是小写;
- siblingText = siblingText.lower()
- # 兄弟项文本是否被包含在curFocusText中或相同;
- if siblingText in curFocusText or strcmp(siblingText, curFocusText):
- isOnSibling = True
- self.info(u"当前焦点在[%s], 目标焦点为[%s]" % (siblingText, optionName))
- # 再判断,该兄弟项是否为目标节点(curOption);
- for optionText in optionTextList:
- optionText = optionText.lower()
- # 若当前兄弟项为目标option返回True、文本;
- if strcmp(optionText, siblingText):
- return True, curFocusText
- # endif
- # endfor
- # 在兄弟项中,退出循环;
- break
- # endif
- # endfor
- if isOnSibling is False:
- self.error(u"未聚集到任何[%s]的兄弟项中" % optionName)
- else:
- self.info("未聚集到目标节点[%s],当前文本=%s" % (optionName, curFocusText))
- return False, curFocusText
- # endif
- # endfor
- # 默认返回;
- return False, 0 if isNumberText else ""
- # 获取动态图片文本内容;
- '''
- 函数:
- 参数:
- 返回:
- '''
- def __getDynamicPicText(self, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, marqueeDict,
- isNumberText, isValueSheet):
- # 判断图片是否动态:截图两次,判断两次文本内容是否相同;
- firstRetsult, firstText = self.__getStaticPicText(takePicture(), optionName, optionTextList,
- siblingTextList, ocrConfigList,
- ocrThreshold, isNumberText, isValueSheet)
- if firstRetsult is False:
- self.error(u"[%s]第一次截图未识别出聚焦框" % optionName)
- return False, None
- # 发送鲜活键, 保证界面鲜活;
- COptionAction.__sendAliveKey(marqueeDict['alive_key'])
- # 第二次截图;
- secondRetsult, secondText = self.__getStaticPicText(takePicture(), optionName, optionTextList,
- siblingTextList, ocrConfigList,
- ocrThreshold, isNumberText, isValueSheet)
- if secondRetsult is False:
- self.error(u"[%s]第二次截图未识别出聚焦框" % optionName)
- return False, None
- # 发送鲜活键, 保证界面鲜活;
- COptionAction.__sendAliveKey(marqueeDict['alive_key'])
- # 比较两文本是否相同;
- if firstText.__len__() and firstText == secondText:
- self.info(u"截图两次,文本(%s)识别相同,聚焦的不是跑马灯Option" % firstText)
- 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(takePicture())
- # 间隔多久截图;
- time.sleep(marqueeDict['sleep_time'])
- # 发送鲜活键;
- COptionAction.__sendAliveKey(marqueeDict['alive_key'])
- ocrTextList = []
- # 对截图进行文本识别分析;
- for pic in picList:
- result, text = self.__getStaticPicText(pic, optionName, optionTextList, siblingTextList, ocrConfigList,
- ocrThreshold, isNumberText, isValueSheet, marqueeDict['alive_key'])
- if result is True:
- ocrTextList.append(text)
- # 发送鲜活键;
- COptionAction.__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
- '''
- 函数:
- 参数:
- 返回:
- '''
- def __getOptionInfoDict(self, optionNameList):
- OptionInfoDict = {}
- for optionName in optionNameList:
- found, optionDict = self.__optionExcel(optionName)
- if found:
- OptionInfoDict[optionName] = optionDict
- # endif
- # endfor
- return OptionInfoDict
- # 找到两个字符串左边或者右边相同的部分
- '''
- 函数:
- 参数:
- 返回:
- '''
- 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 __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, wait):
- if key is not None and key.__len__() > 0:
- if type(key) == str:
- COptionAction.__redRat3.sendKey(key, count, wait)
- elif type(key) == list:
- for k in key:
- COptionAction.__redRat3.sendKey(k, 1, wait)
- else:
- self.error(u"error:无效按键内容=%s" % key)
- else:
- self.error(u"error:按键内容空")
- if __name__ == "__main__":
- exData = CExtraData()
- optionExcel = COptionExcel(exData)
- optionConfig = COptionConfig(exData, optionExcel)
- optionAction = COptionAction('picture', '', optionConfig, optionExcel)
- # ====================================== #
- optionAction.callFirstOptionShortCutKey()
|