|
@@ -0,0 +1,354 @@
|
|
|
+# -*- 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']
|
|
|
+
|
|
|
+
|
|
|
+# 注意:所有不对外暴露的变量和函数需要私有化,以明确哪些接口和参数是对外的。
|
|
|
+# 这样便于后期维护时,根据对外的变量和函数来做处理。
|
|
|
+class COptionAction(CBaseLog):
|
|
|
+ # ==============设备对象============== #
|
|
|
+ # 红老鼠遥控对象;
|
|
|
+ __redRat3 = TvOperator()
|
|
|
+ # 创建视频采集对象
|
|
|
+ __ccard = CCardManager()
|
|
|
+ # 图片切割对象
|
|
|
+ __imgCMP = ImageCMP()
|
|
|
+
|
|
|
+ # 发送鲜活键;
|
|
|
+ @staticmethod
|
|
|
+ def __sendAliveKey(aliveKey):
|
|
|
+ if aliveKey is not None:
|
|
|
+ COptionAction.__redRat3.sendKey(aliveKey, 1, 0.1)
|
|
|
+
|
|
|
+ def __init__(self, optionName, optionValue, optionConfig, optionExcel):
|
|
|
+ # 层级位置;
|
|
|
+ self.__pos = 0
|
|
|
+ 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)
|
|
|
+ # 当前状态下的变量
|
|
|
+ self.__curOptionName = ''
|
|
|
+ self.__curOptionInfo = None
|
|
|
+
|
|
|
+ '''
|
|
|
+ 函数:
|
|
|
+ 参数:
|
|
|
+ 注意:
|
|
|
+ 返回:
|
|
|
+
|
|
|
+ 测试:。
|
|
|
+ '''
|
|
|
+
|
|
|
+ def strSplit(self, 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
|
|
|
+
|
|
|
+ # 截图;
|
|
|
+ def takePicture(self):
|
|
|
+ pic = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
|
|
|
+ COptionAction.__ccard.takePicture(pic)
|
|
|
+ return pic
|
|
|
+
|
|
|
+ # 调用根节点快捷键(中间节点不需要快捷键;);
|
|
|
+ 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 = self.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
|
|
|
+
|
|
|
+ # 获取文本识别的参数;
|
|
|
+ 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('):
|
|
|
+ isNumberText = True
|
|
|
+
|
|
|
+ # 是否为静态焦点识别(动态则为跑马灯);
|
|
|
+ if curOthers.__len__() and 'marquee' in curOthers:
|
|
|
+ pic = self.takePicture()
|
|
|
+ return self.__getDynamicPicText(pic, curOption, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, curOthers['marquee'],
|
|
|
+ isNumberText, isValueSheet)
|
|
|
+ else:
|
|
|
+ return self.__getStaticPicText(curOption, optionTextList, siblingTextList, ocrConfigList, ocrThreshold,
|
|
|
+ isNumberText, isValueSheet)
|
|
|
+ # endif
|
|
|
+
|
|
|
+ # 获取静态图片文本内容;
|
|
|
+ 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 = self.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(self.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(self.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:
|
|
|
+ return True, firstText
|
|
|
+
|
|
|
+ # 文本不相同,为动态图片;
|
|
|
+ menuList = marqueeDict['menu']
|
|
|
+ # 如果只有一项跑马灯,且目标option亦在跑马灯列表中,则直接返回成功结果
|
|
|
+ if menuList.__len__() == 1 and (optionName in menuList):
|
|
|
+ return True, firstText
|
|
|
+
|
|
|
+ picList = []
|
|
|
+ # 如果有多项同级option都是跑马灯, 要成功识别文本需要间隔截图5次(大概会成功截图到最全的文本);
|
|
|
+ for i in range(0, 5):
|
|
|
+ picList.append(self.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'])
|
|
|
+
|
|
|
+
|
|
|
+ return False, 0 if isNumberText else None
|
|
|
+
|
|
|
+ def move2NextSiblingOption(self, optionName):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def move2PrevSiblingOption(self, optionName):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def back2ParentOption(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ exData = CExtraData()
|
|
|
+ optionExcel = COptionExcel(exData)
|
|
|
+ optionConfig = COptionConfig(exData, optionExcel)
|
|
|
+ optionAction = COptionAction('picture', '', optionConfig, optionExcel)
|
|
|
+ # ====================================== #
|
|
|
+ optionAction.callFirstOptionShortCutKey()
|