Kaynağa Gözat

COptionConfig
修正getOptionOCRConfig最后一层配置值的问题。

TExcelParser
新增字段:toparent_key、layout

COptionExcel
适配新增字段;

COptionAction
1、takePicture适配lyaout;
2、setCurPos需getCurOptionInfo
3、enterNode需getCurOptionInfo

scbc.sat2 5 yıl önce
ebeveyn
işleme
cad45df390

+ 119 - 41
ssat_sdk/MenuTree3/OptionAction.py

@@ -81,7 +81,7 @@ class COptionAction(CBaseLog):
         # ==============常用对象数据==============;
         self.__optionPaths = self.__optionExcel.getOptionPaths(self.__optionName)
         # 如果__optionValue空则不取value表任何内容;
-        if self.__optionValue != "":
+        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对应;
@@ -92,8 +92,8 @@ class COptionAction(CBaseLog):
         # 获取一次当前层级信息;
         self.getCurOptionInfo()
         # 相关返回操作;
-        self.__isEnterKeyInValue = False        # 节点在value表时,是否发送enter key.
-        self.__valueType = VType.SelectType     # 节点在value表时,value值的类型(0:选择型, 1:输入型,2:range()数值型。)
+        self.__isEnterKeyInValue = False  # 节点在value表时,是否发送enter key.
+        self.__valueType = VType.SelectType  # 节点在value表时,value值的类型(0:选择型, 1:输入型,2:range()数值型。)
 
     @property
     def pos(self):
@@ -116,7 +116,7 @@ class COptionAction(CBaseLog):
         return self.__optionValueInfo
 
     '''
-    函数:截图并返回截图路径;
+    函数:截图并返回截图路径,如果当前option/optionValue含有layout坐标参数,则返回坐标截图;
     参数:无
     返回:截图路径;    
     '''
@@ -126,7 +126,24 @@ class COptionAction(CBaseLog):
         COptionAction.__captureCard.takePicture(img)
         if not os.path.exists(img):
             self.error(u"截图失败:%s" % img)
-        return 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
 
     '''
     函数:调用根节点快捷键(中间节点不需要快捷键;);
@@ -217,6 +234,7 @@ class COptionAction(CBaseLog):
             # 获取父节点
             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
@@ -319,7 +337,7 @@ class COptionAction(CBaseLog):
         # 注:value表中的option实际并没有兄弟项,取的是所有value项
         if isValueSheet and siblingTextList.__len__():
             if siblingTextList[0].startswith('range('):
-                self.info(u"识别的内容是value表数字内容(range(0,xx)类型)")
+                self.info(u"识别的内容是value表数字内容(range(x,y)类型)")
                 isNumberText = True
 
         # 清除之前的value值;
@@ -330,12 +348,14 @@ class COptionAction(CBaseLog):
                                             ocrThreshold, curOthers['marquee'],
                                             isNumberText, isValueSheet)
         else:
-            return self.__getStaticPicText(self.takePicture() if staticPic is None else staticPic, curOption,
-                                           optionTextList,
-                                           siblingTextList,
-                                           ocrConfigList,
-                                           ocrThreshold,
-                                           isNumberText, isValueSheet)
+            isFocus, isTarget, text = self.__getStaticPicText(self.takePicture() if staticPic is None else staticPic,
+                                                              curOption,
+                                                              optionTextList,
+                                                              siblingTextList,
+                                                              ocrConfigList,
+                                                              ocrThreshold,
+                                                              isNumberText, isValueSheet)
+            return isTarget, text
         # endif
 
     '''
@@ -426,10 +446,12 @@ class COptionAction(CBaseLog):
 
     def back2ParentNode(self, isReduce=True):
         # 获取当前option信息;
+        wait = 1
+        duration = 1
         if self.getCurOptionInfo():
-            backKey = self.__curOptionInfo['back_key']
+            tKeyDict = self.__curOptionInfo['toparent_key']
         else:
-            backKey = self.__optionValueInfo['back_key']
+            tKeyDict = self.__optionValueInfo['toparent_key']
             # 同时,根据是否有发送enter key和值类型来判断是否需要多发次返回。
             if not self.__isEnterKeyInValue:
                 # 标记为True,防止重复进入.
@@ -450,13 +472,39 @@ class COptionAction(CBaseLog):
                     # 如果是进度条数据,没有enter键;
                     self.back2ParentNode(False)
 
-        if backKey.__len__() == 0:
-            self.sendKey('return', 1, 1)
+        # 有可能没有toParent_key参数栏,或者有toParent_key参数栏,但参数栏为空
+        if tKeyDict.__len__() == 0 or tKeyDict['key'].__len__() == 0:
+            self.sendKey('return', duration=duration)
         else:
-            self.sendKey(backKey, 1, 1)
+            keyList = tKeyDict['key'].split(",")
+            if tKeyDict["duration"] != "":
+                duration = tKeyDict['duration']
+            if tKeyDict["wait"] != "":
+                wait = float(tKeyDict['wait'])
+            self.info(u"toParentKey存在参数,keyList=%s,duration=%s,wait=%s" % (keyList, duration, wait))
+            for key in keyList:
+                self.sendKey(key, duration=duration)
+
+        time.sleep(wait)
         # 返回,自减;
         if isReduce:
-            self.__pos -= 1
+            if tKeyDict.__len__() > 0 and tKeyDict["tolevel"] != "":
+                level = tKeyDict['tolevel'].lower()
+                self.info(u"toParentkey将回退至level:%s" % level)
+                self.__pos = self.__getLevelIndex(level) + 1
+            else:
+                self.__pos -= 1
+                self.getCurOptionInfo()
+
+    '''
+        在忽略大小写的情况下,获取到该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节点不处理;
@@ -477,6 +525,8 @@ class COptionAction(CBaseLog):
                 self.sendKey(optionEnterKey, 1, waitTime)
             # 进入下层,自增
             self.__pos += 1
+            # 重新获取次信息;
+            self.getCurOptionInfo()
         else:
             if not self.__isEnterKeyInValue:
                 self.info(u"节点已在value上,且没有触发过enter key")
@@ -506,6 +556,8 @@ class COptionAction(CBaseLog):
             self.error(u"pos值[%d]超出路径范围:[0-%d]" % (pos, self.__optionPaths.__len__()))
             return
         self.__pos = pos
+        # 变量层级后,需要重新获取当前节点信息,保证外部调用正常;
+        self.getCurOptionInfo()
 
     '''
     函数:设置目标option的值, 只设置数值型value和输入型value(选择型value不需要此步骤).
@@ -610,6 +662,27 @@ class COptionAction(CBaseLog):
 
         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)
+
+    def getCurParentInfo(self):
+        pass
+
     '''
     函数:检测路径是否有效;
     参数:无
@@ -651,17 +724,17 @@ class COptionAction(CBaseLog):
     函数:获取静态图片文本内容
     参数:(略,请看调用函数)
     注意:
-    返回:Boolean、文本识别内容。成功识别出文本,返回True及文本内容。
+    返回:Boolean、Boolean、文本识别内容。
+        是否成功聚焦、是否聚焦在目标节点上、聚焦框识别的文本内容。
     '''
 
     def __getStaticPicText(self, pic, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold,
-                           isNumberText,
-                           isValueSheet, aliveKey=None):
+                           isNumberText, isValueSheet, aliveKey=None):
         # 获取图片焦点框;
         found, focusBox = self.__optionFocus.findFocusByIcon(pic, optionName, isValueSheet)
         if found is False:
             self.debug(u"未找到[%s]聚集框" % optionName)
-            return False, None
+            return False, False, None
 
         # 如果有鲜活键;
         self.sendAliveKey(aliveKey)
@@ -673,7 +746,7 @@ class COptionAction(CBaseLog):
         self.__imgCMP.saveCropPic(pic, text_pic, textBox)
         if not os.path.exists(text_pic):
             self.error(u"%s截取文本图片失败:%s" % (optionName, text_pic))
-            return False, None
+            return False, False, None
 
         # 是否在某个兄弟项中;
         isOnSibling = False
@@ -706,7 +779,7 @@ class COptionAction(CBaseLog):
                     numberText = float(numberTextList[numberTextList.__len__() - 1])
                     # 记录value值;
                     self.__optionValueText = numberText
-                    return True, numberText
+                    return True, True, numberText
                 except Exception:
                     continue
             else:
@@ -718,12 +791,13 @@ class COptionAction(CBaseLog):
                     if siblingText in curFocusText 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, curFocusText
+                                return True, True, curFocusText
                             # endif
                         # endfor
                         # 在兄弟项中,退出循环;
@@ -735,12 +809,12 @@ class COptionAction(CBaseLog):
                     self.error(u"未聚集到任何[%s]的兄弟项中" % optionName)
                 else:
                     self.info("未聚集到目标节点[%s],当前文本=%s" % (optionName, curFocusText))
-                    return False, curFocusText
+                    return found, False, curFocusText
             # endif
         # endfor
 
         # 默认返回;
-        return False, 0 if isNumberText else ""
+        return found, False, 0 if isNumberText else ""
 
     '''
     函数:获取动态图片文本内容
@@ -752,20 +826,22 @@ class COptionAction(CBaseLog):
     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:
+        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
 
         # 发送鲜活键, 保证界面鲜活;
         self.sendAliveKey(marqueeDict['alive_key'])
         # 第二次截图;
-        secondRetsult, secondText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList,
-                                                            siblingTextList, ocrConfigList,
-                                                            ocrThreshold, isNumberText, isValueSheet)
-        if secondRetsult is False:
+        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
 
@@ -795,9 +871,11 @@ class COptionAction(CBaseLog):
         ocrTextList = []
         # 对截图进行文本识别分析;
         for pic in picList:
-            result, text = self.__getStaticPicText(pic, optionName, optionTextList, siblingTextList, ocrConfigList,
-                                                   ocrThreshold, isNumberText, isValueSheet, marqueeDict['alive_key'])
-            if result is True:
+            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'])
@@ -941,20 +1019,20 @@ class COptionAction(CBaseLog):
     返回:无
     '''
 
-    def sendKey(self, key, count=1, wait=1):
+    def sendKey(self, key, count=1, duration=1):
         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, wait)
+                    COptionAction.__redRat3.sendKey(k, 1, duration)
             else:
                 key = str(key)
                 # 清除前后空格;
                 key = key.lstrip()
                 key = key.rstrip()
-                COptionAction.__redRat3.sendKey(key, count, wait)
+                COptionAction.__redRat3.sendKey(key, count, duration)
         else:
             self.error(u"error:按键内容空")
 

+ 4 - 2
ssat_sdk/MenuTree3/OptionConfig.py

@@ -262,6 +262,8 @@ class COptionConfig(TConfig, CBaseLog):
                 # 当前option的父节点名称;
                 cur_parent = paths[cur_level]['parent']
 
+                # 优先级逻辑解释:
+
                 # 是否有当前option的ocr配置;
                 if self.has_option(cur_level, optionName + '.ocr'):
                     ocr_dict = self.get_dict(self.get_value(cur_level, optionName + '.ocr'))
@@ -273,8 +275,8 @@ class COptionConfig(TConfig, CBaseLog):
                         self.warn(u"%s使用父节点%s的ocr配置%s" % (optionName, cur_parent, ocr_dict))
                     else:
                         # 如果option父级没有配置,则获取顶层配置(first parent);
-                        if self.has_option(cur_level, first_parent + '.ocr'):
-                            ocr_dict = self.get_dict(self.get_value(cur_level, first_parent + '.ocr'))
+                        if self.has_option("First", first_parent + '.ocr'):
+                            ocr_dict = self.get_dict(self.get_value("First", first_parent + '.ocr'))
                             self.warn(u"%s使用顶层节点%s的ocr配置%s" % (optionName, first_parent, ocr_dict))
                     #endif
                 #endif

+ 58 - 13
ssat_sdk/MenuTree3/OptionExcel.py

@@ -526,8 +526,14 @@ class COptionExcel(CBaseLog):
             "value_for_ocr": valueOcrList,
             "enter_key": self.__valueParams.values[optionName]["enter_key"],
             "move_key": self.__valueParams.values[optionName]["move_key"],
-            "back_key": "",  # 预留
-            "others": self.__valueParams.values[optionName]['others']
+            "others": self.__valueParams.values[optionName]['others'],
+            # toparent_key,有则读取,无则留空
+            "toparent_key": self.__valueParams.values[optionName]['toparent_key'] \
+                if 'toparent_key' in self.__valueParams.values[optionName]\
+                else "",
+            "layout": self.__valueParams.values[optionName]['layout'] \
+                if 'layout' in self.__valueParams.values[optionName]\
+                else ""
         }
         # 找到value对应的ocr;
         if value is not None and value != "":
@@ -563,9 +569,9 @@ class COptionExcel(CBaseLog):
         if optionName in self.__valueParams.values:
             valueDictList = self.valueParams.values[optionName]["value"]
             for valueDict in valueDictList:
-                if value == "":
+                if value is None or value == "":
                     value_for_ocr = valueDict["value_for_ocr"]
-                    valueTextList.append(value_for_ocr.split(';'))
+                    valueTextList.extend(value_for_ocr.split(';'))
                 elif valueDict["value"] == value:
                     value_for_ocr = valueDict["value_for_ocr"]
                     valueTextList = value_for_ocr.split(';')
@@ -625,7 +631,16 @@ class COptionExcel(CBaseLog):
                         dict_option['enter_key'] = self.__pathParams.paths[level][parent]['enter_key']
                         dict_option['move_key'] = self.__pathParams.paths[level][parent]['move_key']
                         # 根节点没有以下信息,置空。
-                        dict_option['back_key'] = ''  # 预留;
+                        # 读取toparent_key
+                        if 'toparent_key' in self.__pathParams.paths[level][parent]:
+                            dict_option['toparent_key'] = self.__pathParams.paths[level][parent]['toparent_key']
+                        else:
+                            dict_option['toparent_key'] = ''
+                        # 读取layout
+                        if 'layout' in self.__pathParams.paths[level][parent]:
+                            dict_option['layout'] = self.__pathParams.paths[level][parent]['layout']
+                        else:
+                            dict_option['layout'] = ''
                         dict_option['option_ocr'] = []
                         dict_option['option_move_key'] = []
                         dict_option['option_enter_key'] = []
@@ -643,7 +658,16 @@ class COptionExcel(CBaseLog):
                                     dict_option['others'] = self.__pathParams.paths[level][parent]['others']
                                     dict_option['enter_key'] = self.__pathParams.paths[level][parent]['enter_key']
                                     dict_option['move_key'] = self.__pathParams.paths[level][parent]['move_key']
-                                    dict_option['back_key'] = ''  # 预留;
+                                    # 读取toparent_key
+                                    if 'toparent_key' in self.__pathParams.paths[level][parent]:
+                                        dict_option['toparent_key'] = self.__pathParams.paths[level][parent]['toparent_key']
+                                    else:
+                                        dict_option['toparent_key'] = ''
+                                    # 读取layout
+                                    if 'layout' in self.__pathParams.paths[level][parent]:
+                                        dict_option['layout'] = self.__pathParams.paths[level][parent]['layout']
+                                    else:
+                                        dict_option['layout'] = ''
                                     dict_option['option_ocr'] = vals['option_for_ocr'].split(';')
                                     dict_option['option_move_key'] = vals['move_key']
                                     dict_option['option_enter_key'] = vals['enter_key']
@@ -669,7 +693,14 @@ class COptionExcel(CBaseLog):
                     dict_option['others'] = paths[level]['others']
                     dict_option['enter_key'] = paths[level]['enter_key']
                     dict_option['move_key'] = paths[level]['move_key']
-                    dict_option['back_key'] = ''  # 预留;
+                    if 'toparent_key'in paths[level]:
+                        dict_option['toparent_key'] = paths[level]['toparent_key']
+                    else:
+                        dict_option['toparent_key'] = ''
+                    if 'layout'in paths[level]:
+                        dict_option['layout'] = paths[level]['layout']
+                    else:
+                        dict_option['layout'] = ''
                     dict_option['option_ocr'] = paths[level]['option_for_ocr']  # 已经是list了。
                     dict_option['option_move_key'] = paths[level]['move_key']
                     dict_option['option_enter_key'] = paths[level]['enter_key']
@@ -693,7 +724,7 @@ class COptionExcel(CBaseLog):
 
     def getOptionAllValueName(self, optionName):
         valueList = []
-        if self.valueParams.values.has_key(optionName):
+        if optionName in self.valueParams.values:
             valueDictList = self.valueParams.values[optionName]["value"]
             for valueDict in valueDictList:
                 valueList.append(valueDict["value"])
@@ -736,6 +767,12 @@ class COptionExcel(CBaseLog):
                             "move_key": self.__pathParams.paths[level][parent][xlsc.move_key],
                             "enter_key": self.__pathParams.paths[level][parent][xlsc.enter_key],
                             "others": self.__pathParams.paths[level][parent][xlsc.others],
+                            "toparent_key": self.__pathParams.paths[level][parent][xlsc.toparent_key]\
+                                if xlsc.toparent_key in self.__pathParams.paths[level][parent]\
+                                else "",
+                            "layout": self.__pathParams.paths[level][parent][xlsc.layout]\
+                                if xlsc.layout in self.__pathParams.paths[level][parent]\
+                                else "",
                             "option": item[xlsc.option],
                             "option_for_ocr": item[xlsc.option_for_ocr].split(';'),
                             "option_move_key": item[xlsc.move_key],
@@ -754,6 +791,12 @@ class COptionExcel(CBaseLog):
                                     "move_key": self.__pathParams.paths[level][parent][xlsc.move_key],
                                     "enter_key": self.__pathParams.paths[level][parent][xlsc.enter_key],
                                     "others": self.__pathParams.paths[level][parent][xlsc.others],
+                                    "toparent_key": self.__pathParams.paths[level][parent][xlsc.toparent_key]\
+                                        if xlsc.toparent_key in self.__pathParams.paths[level][parent]\
+                                        else "",
+                                    "layout": self.__pathParams.paths[level][parent][xlsc.layout]\
+                                        if xlsc.layout in self.__pathParams.paths[level][parent]\
+                                        else "",
                                     # 根结点没有以下信息,置空;
                                     "option": parent,
                                     "option_for_ocr": [],
@@ -852,11 +895,13 @@ if __name__ == "__main__":
     # print "getOptionValueInfo", opxls.getOptionValueInfo('source')
     # print "getOptionValueText", opxls.getOptionValueText('source', 'atv')
     # print "getOptionValueText", opxls.getOptionValueText('source', 'dtv')
-    print "getOptionInfo", opxls.getOptionInfo('source')
-    print "getOptionAllValueName", opxls.getOptionAllValueName('source')
-    ##########################################
-    paths = opxls.getOptionPaths('source')
-    print "getOptionPaths", paths
+    print "getOptionInfo", opxls.getOptionInfo('lock')
+    print "getOptionInfo", opxls.getOptionInfo('picture_preset')
+    print "getOptionInfo", opxls.getOptionInfo('picture')
+    # print "getOptionAllValueName", opxls.getOptionAllValueName('source')
+    # ##########################################
+    # paths = opxls.getOptionPaths('source')
+    # print "getOptionPaths", paths
     # print "checkOptionPaths", opxls.checkOptionPaths('sharpness')
     # print "checkOptionPaths", opxls.checkOptionPaths(opxls.getOptionPaths('sharpness'))
     # print "getOptionPathsAndValue", opxls.getOptionPathsAndValue('picture', 'sharpness')

+ 53 - 2
ssat_sdk/MenuTree3/TExcelParser.py

@@ -51,7 +51,12 @@ class CExcelParser(CBaseLog):
                 rowDict = {}
                 for index in range(oneRow.__len__()):
                     item = oneRow[index]
-                    rowDict[pKeys[index]] = item
+                    if pKeys[index] == "toparent_key" and oneRow[index] != "":
+                        rowDict[pKeys[index]] = self.__parseToParentKey(item)
+                    elif pKeys[index] == "layout" and oneRow[index] != "":
+                        rowDict[pKeys[index]] = self.__parseLayout(item)
+                    else:
+                        rowDict[pKeys[index]] = item
                 if rowDict[xlsc.value_name] is None or rowDict[xlsc.value_name].__len__() < 1:
                     if parentName is None:
                         self.error(u"Level %s error row %s:" % (sheet.name, str(oneRow)))
@@ -75,7 +80,12 @@ class CExcelParser(CBaseLog):
             rowDict = {}
             for index in range(oneRow.__len__()):
                 item = oneRow[index]
-                rowDict[pKeys[index]] = item
+                if pKeys[index] == "toparent_key" and oneRow[index] != "":
+                    rowDict[pKeys[index]] = self.__parseToParentKey(item)
+                elif pKeys[index] == "layout" and oneRow[index] != "":
+                    rowDict[pKeys[index]] = self.__parseLayout(item)
+                else:
+                    rowDict[pKeys[index]] = item
             if rowDict[xlsc.parent] is None or rowDict[xlsc.parent].__len__() < 1:
                 if parentName is None:
                     self.error(u"Level %s error row %s:" % (sheet.name, str(oneRow)))
@@ -214,6 +224,47 @@ class CExcelParser(CBaseLog):
     #     # print "valueList:", valueList, type(valueList)
     #     return valueList
 
+    def __parseToParentKey(self, keyStr):
+        keyList = ["key", "duration", "wait", "tolevel", "dialog"]
+        # 按照"key[]"的方式切片并解析成字典
+        keyDict = self.__parseMulParam(keyList, keyStr)
+        return keyDict
+
+    def __parseLayout(self, layoutStr):
+        keyList = ["bounds"]
+        keyDict = self.__parseMulParam(keyList, layoutStr)
+        keyDict["bounds"] = tuple(keyDict["bounds"].split(","))
+        return keyDict
+
+    '''
+    返回:{key:value,key2:value2,...}
+    '''
+    def __parseMulParam(self, keyList, params):
+        paramDict = {}
+        for key in keyList:
+            value = self.__parseParam(key, params)
+            paramDict[key] = value
+        return paramDict
+
+    '''
+    char为该项参数的括符符号,必须成对带入。默认为中括号[]
+    '''
+    #将数据从excel表格里取出来,只返回括号里面的内容
+    def __parseParam(self, key, params, char="[]"):
+        # 为防止key在其他地方存在相同关键字,添加一个"["做区别
+        key1 = key + char[0]
+        keyIndex = params.find(key1)
+        if keyIndex == -1:
+            return ""
+        # key.__len__()-1 为去掉"["的处理
+        str1 = params[keyIndex + key1.__len__()-1:params.__len__()]
+        i1 = str1.find(char[0])
+        i2 = str1.find(char[1])
+        if i1 == -1 or i2 == -1:
+            return ""
+        str2 = str1[i1 + 1: i2]
+        return str2.strip()
+
 
 if __name__ == "__main__":
     pass

+ 82 - 45
ssat_sdk/MenuTree3/TMenu.py

@@ -55,6 +55,7 @@ class CTMenu(CBaseLog):
         tagValue        目标值,ocr识别的结果值。
     返回:相同返回True。
     '''
+
     def __isValueEqual(self, srcValue, tagValue):
         self.info(u"源值=%s, 目标值=%s" % (str(srcValue), str(tagValue)))
 
@@ -66,6 +67,7 @@ class CTMenu(CBaseLog):
         maxTry              最大移动次数,防止死循环。
     返回:Boolean是否在目标节点上, 文本或数值。
     '''
+
     def __move2TargetNode(self, opa, moveDirection, maxTry=15):
         tryCount = 0
         # 是否到目标结点了;
@@ -85,14 +87,15 @@ class CTMenu(CBaseLog):
                     isOnTargetNode = True
                     break
 
+                # 未到目标,进入下一节点;
+                self.info(u"<进入下一节点>")
+                opa.enterNode()
+
                 # 先处理密码框;
                 hasBox, dealStatus = opa.dealPasswordBox()
                 if hasBox is True and dealStatus is False:
                     break
 
-                # 未到目标,进入下一节点;
-                self.info(u"<进入下一节点>")
-                opa.enterNode()
                 continue
             # endif
 
@@ -114,38 +117,42 @@ class CTMenu(CBaseLog):
     函数:退出 usb 信源界面;
     参数:
         opa     COptionAction对象;
-    返回:
+    返回:如果可以直接调用信源菜单返回True,否则返回False;
     '''
+
     def __exitUsbSource(self, opa):
-        count = 0
-        while count < 3:
-            count += 1
-            # 因为信源界面消失了,需要再调用一次.
-            opa.callFirstOptionShortCutKey()
-            # 判断是否聚焦成功;
-            if opa.isOnFirstOption()[0] is True:
-                break
+        # 有些信源,需要手动enter
+        enterKey = opa.curOptionInfo['enter_key']
 
-            self.tvOperator.sendKey("ok", duration=2)
-            # 因为信源界面消失了,需要再调用一次.
-            opa.callFirstOptionShortCutKey()
-            # 判断是否聚焦成功;
-            if opa.isOnFirstOption()[0] is True:
-                break
+        # 因为信源界面消失了,需要再调用一次.
+        opa.callFirstOptionShortCutKey()
+        # 判断是否聚焦成功;
+        if opa.isOnFirstOption() is True:
+            return True
 
-            self.tvOperator.sendKey("exit", duration=2)
-            # 因为信源界面消失了,需要再调用一次.
-            opa.callFirstOptionShortCutKey()
-            # 判断是否聚焦成功;
-            if opa.isOnFirstOption()[0] is True:
-                break
+        self.redRat3.sendKey("ok", duration=2)
+        # 因为信源界面消失了,需要再调用一次.
+        opa.callFirstOptionShortCutKey()
+        # 判断是否聚焦成功;
+        if opa.isOnFirstOption() is True:
+            self.redRat3.sendKey("ok", duration=2)
+            return False
 
-            self.tvOperator.sendKey("return", duration=2)
-            # 因为信源界面消失了,需要再调用一次.
-            opa.callFirstOptionShortCutKey()
-            # 判断是否聚焦成功;
-            if opa.isOnFirstOption()[0] is True:
-                break
+        self.redRat3.sendKey("exit", duration=2)
+        # 因为信源界面消失了,需要再调用一次.
+        opa.callFirstOptionShortCutKey()
+        # 判断是否聚焦成功;
+        if opa.isOnFirstOption() is True:
+            self.redRat3.sendKey("ok", duration=2)
+            return False
+
+        self.redRat3.sendKey("return", duration=2)
+        # 因为信源界面消失了,需要再调用一次.
+        opa.callFirstOptionShortCutKey()
+        # 判断是否聚焦成功;
+        if opa.isOnFirstOption() is True:
+            self.redRat3.sendKey("ok", duration=2)
+            return False
 
     '''
     函数:移动到目标信源节点上。
@@ -155,6 +162,7 @@ class CTMenu(CBaseLog):
         maxTry              最大移动次数,防止死循环。
     返回:Boolean是否在目标节点上, 文本或数值。
     '''
+
     def __move2SourceNode(self, opa, moveDirection, maxTry=15):
         tryCount = 0
         # 目标节点聚焦状态;
@@ -180,15 +188,22 @@ class CTMenu(CBaseLog):
 
             # 如果是usb信源,特殊处理;
             if targetText and 'usb' in targetText.lower():
-                self.__exitUsbSource(opa)
+                if not self.__exitUsbSource(opa):
+                    self.info(u"///////////////////////退出USB成功。///////////////////////")
+                    # 因为信源界面消失了,需要再调用一次.
+                    opa.callFirstOptionShortCutKey()
+                    # 2次:移动到下/上一节点;
+                    opa.move2NextSiblingNode() if moveDirection else opa.move2PrevSiblingNode()
+                    opa.move2NextSiblingNode() if moveDirection else opa.move2PrevSiblingNode()
+                    self.redRat3.sendKey(enterKey, duration=2)
+                    # 因为信源界面消失了,需要再调用一次.
+                    opa.callFirstOptionShortCutKey()
+                    continue
 
             # 因为信源界面消失了,需要再调用一次.
             opa.callFirstOptionShortCutKey()
             # 移动到下/上一节点;
-            if moveDirection:
-                opa.move2NextSiblingNode()
-            else:
-                opa.move2PrevSiblingNode()
+            opa.move2NextSiblingNode() if moveDirection else opa.move2PrevSiblingNode()
 
             tryCount += 1
             if tryCount > maxTry:
@@ -233,20 +248,21 @@ class CTMenu(CBaseLog):
         opa         COptionAction对象。
     返回:Boolean,成功返回True.
     '''
+
     def __openOption(self, opa):
         if self.__focusOption(opa) is False:
             return False
 
+        # 到达目标option后;
+        self.info(u"进入option")
+        opa.enterNode()
+
         # 先处理密码框;
         hasBox, dealStatus = opa.dealPasswordBox()
         if hasBox is True and dealStatus is False:
             self.error(u"密码框处理失败")
             return False
 
-        # 到达目标option后;
-        self.info(u"进入option")
-        opa.enterNode()
-
         return True
 
     '''
@@ -256,12 +272,17 @@ class CTMenu(CBaseLog):
     返回:
     
     '''
+
     def __back2Home(self, opa):
         self.info(u"返回主页")
         while opa.pos > 0:
             opa.back2ParentNode()
         self.info(u"【如果设置value时,会自动回退到上一层父节点,此处操作正常。否则要多返回一层。】")
 
+        # value层的enter_key,如果按了ok会返回父层,则少退出一层return。但是目前menutree要兼容适配的旧问题,只能多退出一次。
+        # 如果未来出现了新的机芯,多按return键会导致退出,需要把这个机制完善
+        self.redRat3.sendKey("return", 1, 1)
+
     '''
     函数:设置指定的option的值为optionValue。
     参数:
@@ -269,6 +290,7 @@ class CTMenu(CBaseLog):
         optionValue     option要设置的值;
     返回: Boolean。
     '''
+
     def setOptionValue(self, optionName, optionValue):
         self.info(u"【%s】【%s】" % (optionName, optionValue))
         CTMenu.sourceInput.setPattern(11)
@@ -290,6 +312,7 @@ class CTMenu(CBaseLog):
 
         # 返回主页;
         self.__back2Home(opa)
+        return True
 
     '''
     函数:移动到目标option后,读取该option的value值,与optionValue进行比较,相等表示检测值与目标值一致;
@@ -326,7 +349,8 @@ class CTMenu(CBaseLog):
         self.__back2Home(opa)
 
         # 聚焦状态就是结果;
-        self.info(u"checkOptionValue结果:optionValue=%s, ocrValue=%s, 结果=%d" % (str(optionValue), str(valueText), isValueEqual))
+        self.info(
+            u"checkOptionValue结果:optionValue=%s, ocrValue=%s, 结果=%d" % (str(optionValue), str(valueText), isValueEqual))
         return isValueEqual
 
     '''
@@ -335,6 +359,7 @@ class CTMenu(CBaseLog):
         optionName          指定要进入的option节点。
     返回:Boolean。成功进入返回True.
     '''
+
     def openOption(self, optionName):
         self.info(u"【%s】" % optionName)
         CTMenu.sourceInput.setPattern(11)
@@ -357,6 +382,7 @@ class CTMenu(CBaseLog):
     注意:如果optionValue是数值类型,将不会变更值。
           如optionValue=10,实现该optionName的value是100,则不变更其值。
     '''
+
     def focusOptionValue(self, optionName, optionValue):
         self.info(u"【%s】【%s】" % (optionName, optionValue))
         CTMenu.sourceInput.setPattern(11)
@@ -380,6 +406,7 @@ class CTMenu(CBaseLog):
         optionName      要获取值的option;
     返回:Boolean, str/int/float
     '''
+
     def getOptionValue(self, optionName):
         self.info(u"【%s】" % optionName)
         CTMenu.sourceInput.setPattern(11)
@@ -419,6 +446,7 @@ class CTMenu(CBaseLog):
     参数:
     返回:
     '''
+
     def inputUnlock(self, stdText, password=''):
         opa = COptionAction(None, None, self.__optionConfig, self.__optionExcel)
         return opa.inputUnlock(stdText, password)
@@ -431,10 +459,11 @@ class CTMenu(CBaseLog):
     
     注意:该函数不是从根节点开始移动到目标节点,而是假定焦点已经在目标节点层级菜单上。
     '''
+
     def moveToOption(self, optionName):
         opa = COptionAction(optionName, None, self.__optionConfig, self.__optionExcel)
         # 已经在该层级上.
-        opa.setCurPos(opa.getPathLength()-1)
+        opa.setCurPos(opa.getPathLength() - 1)
 
         # 检测路径是否有效;
         if opa.checkRunOptionPath() is False:
@@ -457,6 +486,7 @@ class CTMenu(CBaseLog):
     返回:Boolean, []
         如果有频道没找到,返回False,并返回没找到的频道列表。如果全部找到返回True。
     '''
+
     def checkChannelList(self, channelList, maxTry=15):
         failChannelList = []
         checkResult = True
@@ -486,6 +516,7 @@ class CTMenu(CBaseLog):
         blackRate              黑屏比率。
     返回:Boolean。找到返回True。
     '''
+
     def getBlackChannel(self, channelCount, blackThreshold=20, blackRate=2.0):
         result = False
         # 根据频道数量遍历;
@@ -517,6 +548,7 @@ class CTMenu(CBaseLog):
         1、信源菜单消失过快,会导致ocr识别完成后,菜单已消失。
         2、usb信源下,可能会调用source按键失败,所以需要调用其他退出键(ok、exit、return)
     '''
+
     def setSourceValue(self, optionName):
         self.info(u"【%s】" % optionName)
         CTMenu.sourceInput.setPattern(11)
@@ -530,8 +562,8 @@ class CTMenu(CBaseLog):
         # 首先,调用根菜单;
         opa.callFirstOptionShortCutKey()
         # 遍历到目标option中;
-        if self.__move2SourceNode(opa, True, sourceList.__len__()*1.5)[0] is False:
-            if self.__move2SourceNode(opa, False, sourceList.__len__()*1.5)[0] is False:
+        if self.__move2SourceNode(opa, True, sourceList.__len__() * 2)[0] is False:
+            if self.__move2SourceNode(opa, False, sourceList.__len__() * 2)[0] is False:
                 self.__back2Home(opa)
                 return False
 
@@ -700,9 +732,14 @@ if __name__ == "__main__":
         ml.moveToOption('picture_reset')
         ml.moveToOption('picture_preset')
 
-    if 1:
+    if 0:
         # ml.setSourceValue('hdmi1')
         ml.setSourceValue('hdmi2')
         # ml.setSourceValue('dtv')
         print u'------------------------------'
-        ml.setSourceValue('av')
+        ml.setSourceValue('av')
+
+    if 1:
+        # ml.setOptionValue('auto_volume_control', 'off')
+        # ml.setOptionValue('lock', 'on')
+        ml.setSourceValue('dtv')