OptionAction.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. # -*- coding:utf-8 -*-
  2. import os
  3. from UIT_PathManage import UITPathManage
  4. from BaseLog import CBaseLog
  5. from ExtraData import CExtraData
  6. from OptionExcel import COptionExcel
  7. from OptionConfig import COptionConfig
  8. from OptionFocus import COptionFocus
  9. from OptionOCR import COptionOCR
  10. from ssat_sdk.tv_detect import *
  11. from ssat_sdk.device_manage.capturecard_manager import CCardManager
  12. from ssat_sdk.utils.string_util import strcmp
  13. g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth',
  14. 'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh', 'Twelfth']
  15. # 注意:所有不对外暴露的变量和函数需要私有化,以明确哪些接口和参数是对外的。
  16. # 这样便于后期维护时,根据对外的变量和函数来做处理。
  17. class COptionAction(CBaseLog):
  18. # ==============设备对象============== #
  19. # 红老鼠遥控对象;
  20. __redRat3 = TvOperator()
  21. # 创建视频采集对象
  22. __ccard = CCardManager()
  23. # 图片切割对象
  24. __imgCMP = ImageCMP()
  25. # 发送鲜活键;
  26. @staticmethod
  27. def __sendAliveKey(aliveKey):
  28. if aliveKey is not None:
  29. COptionAction.__redRat3.sendKey(aliveKey, 1, 0.1)
  30. def __init__(self, optionName, optionValue, optionConfig, optionExcel):
  31. # 层级位置;
  32. self.__pos = 0
  33. self.__optionName = optionName
  34. # __optionValue可空;
  35. self.__optionValue = optionValue
  36. self.__optionExcel = optionExcel
  37. self.__optionConfig = optionConfig
  38. # 焦点定位及文字识别;
  39. self.__optionFocus = COptionFocus(optionConfig)
  40. self.__optionOCR = COptionOCR(optionConfig, optionExcel)
  41. if self.__optionExcel is None:
  42. self.error(u"表格对象空")
  43. # ==============常用对象数据==============;
  44. self.__optionPaths = self.__optionExcel.getOptionPaths(self.__optionName)
  45. # 如果__optionValue空则不取value表任何内容;
  46. if self.__optionValue != "":
  47. self.__optionValues = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
  48. self.__optionInfo = self.__optionExcel.getOptionInfo(self.__optionName)
  49. # 当前状态下的变量
  50. self.__curOptionName = ''
  51. self.__curOptionInfo = None
  52. '''
  53. 函数:
  54. 参数:
  55. 注意:
  56. 返回:
  57. 测试:。
  58. '''
  59. def strSplit(self, text):
  60. ret = []
  61. str_int = ''
  62. str_ch = ''
  63. ch_last = ' '
  64. for ch in text:
  65. if 47 < ord(ch) < 58:
  66. str_int += ch
  67. if str_ch.__len__():
  68. ret.append(str_ch)
  69. str_ch = ''
  70. else:
  71. if 47 < ord(ch_last) < 58 and ch == '.':
  72. str_int += ch
  73. if str_ch.__len__():
  74. ret.append(str_ch)
  75. str_ch = ''
  76. else:
  77. str_ch += ch
  78. if str_int.__len__():
  79. ret.append(str_int)
  80. str_int = ''
  81. ch_last = ch
  82. if str_ch.__len__():
  83. ret.append(str_ch)
  84. if str_int.__len__():
  85. ret.append(str_int)
  86. return ret
  87. # 截图;
  88. def takePicture(self):
  89. pic = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
  90. COptionAction.__ccard.takePicture(pic)
  91. return pic
  92. # 调用根节点快捷键(中间节点不需要快捷键;);
  93. def callFirstOptionShortCutKey(self):
  94. if 'shortcut_key' in self.__optionPaths['First']:
  95. COptionAction.__redRat3.sendKey(self.__optionPaths['First']['shortcut_key'])
  96. else:
  97. COptionAction.__redRat3.sendKey(self.__optionPaths['First']['parent'])
  98. self.warn(u"表格没有shortcut_key字段,执行默认的parent按键:%s" % self.__optionPaths['First']['parent'])
  99. # 调用当前结点的toparent_key;
  100. def callCurOptionBackKey(self, curOptionName):
  101. curOptionInfo = self.__optionExcel.getOptionInfo(curOptionName)
  102. if 'toparent_key' in self.__optionPaths[curOptionInfo['level']]:
  103. COptionAction.__redRat3.sendKey(self.__optionPaths[curOptionInfo['level']]['toparent_key'])
  104. else:
  105. self.error(u"表格没有toparent_key字段,执行默认按键return")
  106. COptionAction.__redRat3.sendKey('return')
  107. '''
  108. 函数:打开指定的option(到达option后,执行enter_key进入)。
  109. 参数:
  110. 返回:
  111. 示例:
  112. 测试:。
  113. '''
  114. def openOption(self, optionName):
  115. pass
  116. '''
  117. 函数:是否在父节点菜单上。一般在执行了callFirstOptionShortCutKey后调用;
  118. 参数:
  119. 返回:
  120. 注意:由于所有父节点上的子项都共用一个图片定位参数,所以只要随意一个父节点的子项option即可获取定位参数;
  121. 示例:
  122. 测试:。
  123. '''
  124. def isOnFirstOption(self):
  125. pic = self.takePicture()
  126. return self.__optionFocus.findFocusByIcon(pic, self.__optionPaths['First']['option'])[0]
  127. '''
  128. 函数:是否在目标节点(移动时,每一个目标option,并不仅是终点目标option)上.
  129. 说明:
  130. 每次移动到下一目标节点(option)上时,self.__pos + 1,表示移动到下一层路径。
  131. 当self.__pos >= self.__optionPaths.__len__()时,表示到达value表格;
  132. 所以,该类的重点在self.__pos的移动;
  133. 参数:
  134. 返回:Boolean, 识别的文本/数字;
  135. 示例:
  136. 测试:。
  137. '''
  138. def isOnTargetOption(self):
  139. # 析出参数;
  140. curLevel = g_level[self.__pos]
  141. curParent = self.__optionPaths[curLevel]['parent']
  142. curOption = self.__optionPaths[curLevel]['option']
  143. curOthers = json.loads(self.__optionPaths[curLevel]['others'])
  144. firstParent = self.__optionPaths['First']['option']
  145. # 是否在value表中;
  146. if self.__optionValue == "":
  147. isValueSheet = False
  148. else:
  149. isValueSheet = True if self.__pos >= self.__optionPaths.__len__() else False
  150. # 获取文本识别的参数;
  151. ocrConfigList = self.__optionConfig.getOptionOCRConfig(curOption)
  152. ocrThreshold = self.__optionConfig.getThresholdDict(firstParent) # 注意,此处使用firstParent;
  153. # 获取当前option的ocr值/value name下所有ocr值;
  154. if isValueSheet:
  155. optionTextList = self.__optionExcel.getOptionValueText(curOption)
  156. else:
  157. optionTextList = self.__optionExcel.getOptionText(curOption)
  158. # 获取option下所有兄弟项的ocr:option字典内容;
  159. siblingTextDict = self.__optionExcel.getOptionAllSiblingItemDict(curOption, not isValueSheet)
  160. # 获取所有option兄弟项(包括自己)的ocr值;
  161. siblingTextList = list(siblingTextDict.keys())
  162. # 是否获取数值文本;
  163. isNumberText = False
  164. # 如果是value表,且兄弟项文本为range
  165. # 注:value表中的option实际并没有兄弟项,取的是所有value项
  166. if isValueSheet and siblingTextList.__len__():
  167. if siblingTextList[0].startswith('range('):
  168. isNumberText = True
  169. # 是否为静态焦点识别(动态则为跑马灯);
  170. if curOthers.__len__() and 'marquee' in curOthers:
  171. pic = self.takePicture()
  172. return self.__getDynamicPicText(pic, curOption, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, curOthers['marquee'],
  173. isNumberText, isValueSheet)
  174. else:
  175. return self.__getStaticPicText(curOption, optionTextList, siblingTextList, ocrConfigList, ocrThreshold,
  176. isNumberText, isValueSheet)
  177. # endif
  178. # 获取静态图片文本内容;
  179. def __getStaticPicText(self, pic, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, isNumberText,
  180. isValueSheet, aliveKey=None):
  181. # 获取图片焦点框;
  182. found, focusBox = self.__optionFocus.findFocusByIcon(pic, optionName, isValueSheet)
  183. if found is False:
  184. self.debug(u"未找到[%s]聚集框" % optionName)
  185. return False, None
  186. # 如果有鲜活键;
  187. COptionAction.__sendAliveKey(aliveKey)
  188. # 获取文本区域框;
  189. textBox = self.__optionFocus.getFocusTextBox(optionName, focusBox, isValueSheet)
  190. # 配置文本图片路径,保存文本区域截图;
  191. text_pic = os.path.join(getSATTmpDIR(), "meuttree_area_text.png")
  192. self.__imgCMP.saveCropImage(pic, text_pic, textBox)
  193. if not os.path.exists(text_pic):
  194. self.error(u"%s截取文本图片失败:%s" % (optionName, text_pic))
  195. return False, None
  196. # 是否在某个兄弟项中;
  197. isOnSibling = False
  198. # 遍历所有ocr识别选项;
  199. for ocrConfig in ocrConfigList:
  200. # 如果有鲜活键;
  201. COptionAction.__sendAliveKey(aliveKey)
  202. # 识别出当前聚焦文本;
  203. curFocusText = self.__optionOCR.getImageText(text_pic, ocrConfig, ocrThreshold)
  204. # 判断识别文本是来正常;
  205. if curFocusText == "ERR<Exp>" or curFocusText.__len__() == 0:
  206. continue
  207. self.info("[%s]当前识别出的文本=%s" % (optionName, curFocusText))
  208. # 是否取数字文本;
  209. if isNumberText is True:
  210. # 特殊情况处理:某些情况下,会将包含数字以外的区域一起识别;
  211. curFocusText = curFocusText.strip('>')
  212. # 将数字分组
  213. numberTextList = self.strSplit(curFocusText)
  214. # 只判断最后一位是否为数字;
  215. if numberTextList.__len__() < 1:
  216. self.error(u"当前识别的文本不是数字文本:%s" % curFocusText)
  217. continue
  218. try:
  219. numberText = numberTextList[numberTextList.__len__() - 1]
  220. return True, float(numberText)
  221. except Exception:
  222. continue
  223. else:
  224. # 当前option识别的文本与所有兄弟项文本比较;
  225. for siblingText in siblingTextList:
  226. # 转为小写,保证所有比较都是小写;
  227. siblingText = siblingText.lower()
  228. # 兄弟项文本是否被包含在curFocusText中或相同;
  229. if siblingText in curFocusText or strcmp(siblingText, curFocusText):
  230. isOnSibling = True
  231. self.info(u"当前焦点在[%s], 目标焦点为[%s]" % (siblingText, optionName))
  232. # 再判断,该兄弟项是否为目标节点(curOption);
  233. for optionText in optionTextList:
  234. optionText = optionText.lower()
  235. # 若当前兄弟项为目标option返回True、文本;
  236. if strcmp(optionText, siblingText):
  237. return True, curFocusText
  238. # endif
  239. # endfor
  240. # 在兄弟项中,退出循环;
  241. break
  242. # endif
  243. # endfor
  244. if isOnSibling is False:
  245. self.error(u"未聚集到任何[%s]的兄弟项中" % optionName)
  246. else:
  247. self.info("未聚集到目标节点[%s],当前文本=%s" % (optionName, curFocusText))
  248. return False, curFocusText
  249. # endif
  250. # endfor
  251. # 默认返回;
  252. return False, 0 if isNumberText else ""
  253. # 获取动态图片文本内容;
  254. def __getDynamicPicText(self, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, marqueeDict, isNumberText, isValueSheet):
  255. # 判断图片是否动态:截图两次,判断两次文本内容是否相同;
  256. firstRetsult, firstText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList, siblingTextList, ocrConfigList,
  257. ocrThreshold, isNumberText, isValueSheet)
  258. if firstRetsult is False:
  259. self.error(u"[%s]第一次截图未识别出聚焦框" % optionName)
  260. return False, None
  261. # 发送鲜活键, 保证界面鲜活;
  262. COptionAction.__sendAliveKey(marqueeDict['alive_key'])
  263. # 第二次截图;
  264. secondRetsult, secondText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList, siblingTextList, ocrConfigList,
  265. ocrThreshold, isNumberText, isValueSheet)
  266. if secondRetsult is False:
  267. self.error(u"[%s]第二次截图未识别出聚焦框" % optionName)
  268. return False, None
  269. # 发送鲜活键, 保证界面鲜活;
  270. COptionAction.__sendAliveKey(marqueeDict['alive_key'])
  271. # 比较两文本是否相同;
  272. if firstText.__len__() and firstText == secondText:
  273. return True, firstText
  274. # 文本不相同,为动态图片;
  275. menuList = marqueeDict['menu']
  276. # 如果只有一项跑马灯,且目标option亦在跑马灯列表中,则直接返回成功结果
  277. if menuList.__len__() == 1 and (optionName in menuList):
  278. return True, firstText
  279. picList = []
  280. # 如果有多项同级option都是跑马灯, 要成功识别文本需要间隔截图5次(大概会成功截图到最全的文本);
  281. for i in range(0, 5):
  282. picList.append(self.takePicture())
  283. # 间隔多久截图;
  284. time.sleep(marqueeDict['sleep_time'])
  285. # 发送鲜活键;
  286. COptionAction.__sendAliveKey(marqueeDict['alive_key'])
  287. ocrTextList = []
  288. # 对截图进行文本识别分析;
  289. for pic in picList:
  290. result, text = self.__getStaticPicText(pic, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, isNumberText, isValueSheet, marqueeDict['alive_key'])
  291. if result is True:
  292. ocrTextList.append(text)
  293. # 发送鲜活键;
  294. COptionAction.__sendAliveKey(marqueeDict['alive_key'])
  295. return False, 0 if isNumberText else None
  296. def move2NextSiblingOption(self, optionName):
  297. pass
  298. def move2PrevSiblingOption(self, optionName):
  299. pass
  300. def back2ParentOption(self):
  301. pass
  302. if __name__ == "__main__":
  303. exData = CExtraData()
  304. optionExcel = COptionExcel(exData)
  305. optionConfig = COptionConfig(exData, optionExcel)
  306. optionAction = COptionAction('picture', '', optionConfig, optionExcel)
  307. # ====================================== #
  308. optionAction.callFirstOptionShortCutKey()