Jelajahi Sumber

logic:测试NT72 setOptionValue成功,修改了其中出现的各种问题。

scbc.sat2 5 tahun lalu
induk
melakukan
8dece5d569

+ 29 - 15
ssat_sdk/MenuTree3/BaseLog.py

@@ -24,6 +24,15 @@ inspect.stack()[2][3]:获取当前函数的上上一层函数的名称;
 
 class CBaseLog:
     def __init__(self):
+        self.__enableDebug = True
+        self.__enableInfo = True
+        self.__enableWarn = True
+        self.__enableError = True
+        self.__enableLog = True
+        # 如果有配置文件,加载;
+        self.__loadConfig()
+    
+    def __loadConfig(self):
         pass
 
     def __printlog(self, msg):
@@ -31,29 +40,34 @@ class CBaseLog:
         print("%s %s") % (datetime.now().strftime('%Y-%m-%d %H:%M:%S'), msg)
 
     def debug(self, msg):
-        # 类型/类名:函数/内容
-        log_str = "【DEBUG】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
-        self.__printlog(log_str)
+        if self.__enableDebug:
+            # 类型/类名:函数/内容
+            log_str = "【DEBUG】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
+            self.__printlog(log_str)
 
     def info(self, msg):
-        # 类型/类名:函数/内容
-        log_str = "【INFO】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
-        self.__printlog(log_str)
+        if self.__enableInfo:
+            # 类型/类名:函数/内容
+            log_str = "【INFO】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
+            self.__printlog(log_str)
 
     def warn(self, msg):
-        # 类型/类名:函数/内容
-        log_str = "【WARN】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
-        self.__printlog(log_str)
+        if self.__enableWarn:
+            # 类型/类名:函数/内容
+            log_str = "【WARN】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
+            self.__printlog(log_str)
 
     def error(self, msg):
-        # 类型/类名:函数/内容
-        log_str = "【ERROR】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
-        self.__printlog(log_str)
+        if self.__enableError:
+            # 类型/类名:函数/内容
+            log_str = "【ERROR】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
+            self.__printlog(log_str)
 
     def log(self, msg):
-        # 类型/类名/内容
-        log_str = "【LOG】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
-        self.__printlog(log_str)
+        if self.__enableLog:
+            # 类型/类名/内容
+            log_str = "【LOG】<%s::%s> %s" % (self.__class__.__name__, inspect.stack()[1][3], msg)
+            self.__printlog(log_str)
         
         
 if __name__ == "__main__":

+ 6 - 1
ssat_sdk/MenuTree3/ExtraData.py

@@ -15,6 +15,7 @@ from BaseLog import CBaseLog
 # 外部数据,禁止外部修改;
 class CExtraData(CBaseLog):
     def __init__(self):
+        CBaseLog.__init__(self)
         # 配置文件路径;
         self.__configPath = ""
         # menu tree资源目录(包含了Excel文件路径);
@@ -32,7 +33,11 @@ class CExtraData(CBaseLog):
         # 加载数据;
         self.loadData()
 
-    # 装载数据;
+    '''
+    函数:装载所需要的额外数据。
+    参数:无
+    返回:无
+    '''
     def loadData(self):
         # 1、加载menu tree 路径;
         self.menuTreeDir = getMenuTree3SelectedProjectCfgPath()

+ 251 - 105
ssat_sdk/MenuTree3/OptionAction.py

@@ -1,6 +1,5 @@
 # -*- coding:utf-8 -*-
 import os
-from UIT_PathManage import UITPathManage
 from BaseLog import CBaseLog
 from ExtraData import CExtraData
 from OptionExcel import COptionExcel
@@ -15,12 +14,6 @@ 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 = ''
@@ -69,6 +62,7 @@ class COptionAction(CBaseLog):
             COptionAction.__redRat3.sendKey(aliveKey, 1, 0.1)
 
     def __init__(self, optionName, optionValue, optionConfig, optionExcel):
+        CBaseLog.__init__(self)
         # 层级位置;
         self.__pos = 0
         # 目标option;
@@ -86,11 +80,13 @@ class COptionAction(CBaseLog):
         self.__optionPaths = self.__optionExcel.getOptionPaths(self.__optionName)
         # 如果__optionValue空则不取value表任何内容;
         if self.__optionValue != "":
-            self.__optionValues = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
+            self.__optionValueInfo = self.__optionExcel.getOptionValueInfo(self.__optionName, self.__optionValue)
         self.__optionInfo = self.__optionExcel.getOptionInfo(self.__optionName)
         # 当前状态下的变量,与__pos对应;
         self.__curOptionName = ''
         self.__curOptionInfo = None
+        # 到达value节点后,记录value值(一般只用于range() 数值记录,其他值无意义);
+        self.__optionValueText = ""
         # 获取一次当前层级信息;
         self.getCurOptionInfo()
 
@@ -106,6 +102,22 @@ class COptionAction(CBaseLog):
     def curOptionInfo(self):
         return self.__curOptionInfo
 
+    @property
+    def optionValueText(self):
+        return self.__optionValueText
+
+    '''
+    函数:截图;
+    参数:无
+    返回:无    
+    '''
+    def takePicture(self):
+        img = os.path.join(getSATTmpDIR(), "menutree_runpath.png")
+        COptionAction.__ccard.takePicture(img)
+        if os.path.exists(img):
+            self.error(u"截图失败:%s" % img)
+        return img
+
     '''
     函数:调用根节点快捷键(中间节点不需要快捷键;);
     参数:
@@ -114,9 +126,9 @@ class COptionAction(CBaseLog):
 
     def callFirstOptionShortCutKey(self):
         if 'shortcut_key' in self.__optionPaths['First']:
-            COptionAction.__redRat3.sendKey(self.__optionPaths['First']['shortcut_key'])
+            self.sendKey(self.__optionPaths['First']['shortcut_key'])
         else:
-            COptionAction.__redRat3.sendKey(self.__optionPaths['First']['parent'])
+            self.sendKey(self.__optionPaths['First']['parent'])
             self.warn(u"表格没有shortcut_key字段,执行默认的parent按键:%s" % self.__optionPaths['First']['parent'])
 
     '''
@@ -128,22 +140,10 @@ class COptionAction(CBaseLog):
     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'])
+            self.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
+            self.sendKey('return')
 
     '''
     函数:是否在父节点菜单上。一般在执行了callFirstOptionShortCutKey后调用;
@@ -157,11 +157,11 @@ class COptionAction(CBaseLog):
     '''
 
     def isOnFirstOption(self):
-        pic = takePicture()
+        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表格;
@@ -173,16 +173,27 @@ class COptionAction(CBaseLog):
     测试:。
     '''
 
-    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']
+    def isOnTargetNode(self):
         # 是否在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']['option']
 
         # 获取文本识别的参数;
         ocrConfigList = self.__optionConfig.getOptionOCRConfig(curOption)
@@ -206,92 +217,210 @@ class COptionAction(CBaseLog):
                 self.info(u"识别的内容是value表数字内容(range(0,xx)类型)")
                 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:
-            return self.__getStaticPicText(takePicture(), curOption, optionTextList, siblingTextList, ocrConfigList,
+            return self.__getStaticPicText(self.takePicture(), curOption, optionTextList, siblingTextList, ocrConfigList,
                                            ocrThreshold,
                                            isNumberText, isValueSheet)
         # 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 True if self.__pos >= self.__optionPaths.__len__() else False
+
+    '''
+    函数:移动到下一兄弟节点;
+    参数:无
+    返回:无
+    
+    注意:
+        sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
+        同时,按键等待时间,应该有所区分。
+        
+        如果不截图,可以不考虑sendKey的等待时间.
     '''
 
-    def move2NextSiblingOption(self):
+    def move2NextSiblingNode(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)
+        if self.getCurOptionInfo():
+            # 析出move key;
+            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)
         else:
-            self.sendKey(optionMoveKey[1], 1, 0.1)
+            valueMoveKey = self.__optionValueInfo['move_key']
+            self.sendKey(valueMoveKey[1], 1, 1)
 
     '''
     函数:移动到上一兄弟节点
-    参数:
-    返回:
+    参数:无
+    返回:无
+    
+    注意:
+        sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
+        同时,按键等待时间,应该有所区分。
+        
+        如果不截图,可以不考虑sendKey的等待时间.
     '''
 
-    def move2PrevSiblingOption(self):
+    def move2PrevSiblingNode(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)
+        if self.getCurOptionInfo():
+            # 析出move key;
+            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)
         else:
-            self.sendKey(optionMoveKey[0], 1, 0.1)
+            valueMoveKey = self.__optionValueInfo['move_key']
+            self.sendKey(valueMoveKey[0], 1, 1)
 
     '''
     函数:返回到父节点
-    参数:
-    返回:
+    参数:无
+    返回:无
+    
+    注意:
+        sendKey的等待时间太短,会导致画面未响应,截图时还是截上一状态的,比如0.1秒就很经常出现这问题。
+        同时,按键等待时间,应该有所区分。
+        
+        如果不截图,可以不考虑sendKey的等待时间.
     '''
 
-    def back2ParentOption(self):
+    def back2ParentNode(self):
         # 获取当前option信息;
-        self.getCurOptionInfo()
-        # 析出move key;
-        backKey = self.__curOptionInfo['back_key']
+        if self.getCurOptionInfo():
+            backKey = self.__curOptionInfo['back_key']
+        else:
+            backKey = self.__optionValueInfo['back_key']
+
         if backKey.__len__() == 0:
-            self.sendKey('back', 1, 0.1)
+            self.sendKey('return', 1, 1)
         else:
-            self.sendKey(backKey, 1, 0.1)
+            self.sendKey(backKey, 1, 1)
+        # 返回,自减;
+        self.__pos -= 1
 
     '''
-    函数:进入当前节点;
-    参数:
-    返回:
+    函数:进入当前节点,只对路径节点有效,value节点不处理;
+    参数:
+    返回:
     '''
 
-    def enterOption(self):
+    def enterNode(self):
         # 获取当前option信息;
-        self.getCurOptionInfo()
-        # 析出enter key;
-        optionEnterKey = self.__curOptionInfo['option_enter_key']
-        if optionEnterKey.__len__() == 0:
-            self.sendKey(self.__curOptionInfo['enter_key'], 1, 0.)
+        if self.getCurOptionInfo():
+            # 是否有等待时间
+            waitTime = self.__optionConfig.getParentWaitTime(self.__curOptionInfo['parent'])
+            # 析出enter key;
+            optionEnterKey = self.__curOptionInfo['option_enter_key']
+            if optionEnterKey.__len__() == 0:
+                self.sendKey(self.__curOptionInfo['enter_key'], 1, waitTime)
+            else:
+                self.sendKey(optionEnterKey, 1, waitTime)
+            # 进入下层,自增
+            self.__pos += 1
         else:
-            self.sendKey(optionEnterKey, 1, 0.1)
+            self.info(u"节点已在value上,无法再进入")
 
     '''
-    函数:设置当前option的值;
+    函数:设置当前节点位置;
     参数:
-    返回:
+        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
 
-    def setCurOptionValue(self, value):
-        # 获取当前option信息;
-        self.getCurOptionInfo()
-        # 获取当前option的value表信息;
-        curOptionValueInfo = self.__optionExcel.getOptionValueInfo(self.__curOptionName, value)
+    '''
+    函数:设置目标option的值, 只设置数值型value和输入型value(选择型value不需要此步骤).
+    参数:
+    返回:
+    
+    注意:此函数必须是已聚焦在目标value节点上,否则无效。
+    重要:
+        在此函数enter后,UI是否返回到上一层父节点上,还是停留在本层节点不返回。
+        建议在excel中配置这个关键信息,以便此函数可以正确更改self.__pos的值。
+    '''
+
+    def setOptionValue(self):
+        self.info(u"【在此函数enter后,UI是否返回到上一层父节点上,还是停留在本层节点不返回。\
+        建议在excel中配置这个关键信息,以便此函数可以正确更改self.__pos的值。】")
+        if 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 = float(others['duration']) if "duration" in others else 0.1
+
+        # 是否为数字文本(特指:range(0, 100));
+        isNumberText = self.isNumberText(valueText)
+        # 数值型value;
+        if isNumberText:
+            if moveKey[0] == 'input':
+                # 将数值转成字符;
+                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:
+                # 相差值;
+                num = int(self.__optionValue) - int(self.__optionValueText)
+                # 正->往右或下,负->往左或上;
+                self.sendKey(moveKey[1] if num > 0 else moveKey[0], abs(num), duration)
+        elif moveKey[0] == 'input':
+            # 将字符转成list;
+            chList = list(self.__optionValue)
+            self.sendKey(chList, 1, duration)
+
+        # 最后,如果有进入键执行;
+        if enterKey != 'default':
+            self.sendKey(enterKey, 1, 0.1)
 
     '''
     函数:
@@ -300,6 +429,10 @@ class COptionAction(CBaseLog):
     '''
 
     def getCurOptionInfo(self):
+        if self.__optionPaths.__len__() == 0:
+            self.error(u"paths路径空")
+            return False
+
         if self.__pos >= self.__optionPaths.__len__():
             self.warn(u"已到达value节点,无法获取路径信息")
             return False
@@ -307,21 +440,29 @@ class COptionAction(CBaseLog):
         # 只有第一次或层级移动了才需要更新;
         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)
+            outResult, outData = self.__optionExcel.getOptionInfo(self.__curOptionName, self.__optionPaths)
+            if outResult is False:
+                return False
+            self.__curOptionInfo = outData
 
         return True
 
     '''
-    函数:当前节点是否在value sheet层级中;
+    函数:检测路径是否有效;
     参数:
-    返回:
+    返回:Boolean
     '''
+    def checkRunOptionPath(self):
+        outData = self.__optionExcel.checkOptionPaths(self.__optionPaths)
+        if str(outData[1]) == 'NoExit':
+            self.error(u"表格中不存在到达Option:[%s]的路径,在表格中排查到达该Option路径" % self.__optionName)
+            return False
 
-    def isOnValueSheet(self):
-        if self.__optionValue == "":  # 该值空,表明不会移动到value sheet中;
+        if str(outData[1]) == 'Fail':
+            self.error(u"表格中到达Option:[%s]的路径出现数据断层找不到First层级,在表格中排查到达该Option路径" % self.__optionName)
             return False
-        else:
-            return True if self.__pos >= self.__optionPaths.__len__() else False
+
+        return True
 
     '''
     函数:
@@ -340,12 +481,11 @@ class COptionAction(CBaseLog):
 
         return isNumberText
 
-
     '''
     函数:获取静态图片文本内容
     参数:
     注意:
-    返回:
+    返回:Boolean、文本识别内容
 
     测试:。
     '''
@@ -366,7 +506,7 @@ class COptionAction(CBaseLog):
         textBox = self.__optionFocus.getFocusTextBox(optionName, focusBox, isValueSheet)
         # 配置文本图片路径,保存文本区域截图;
         text_pic = os.path.join(getSATTmpDIR(), "meuttree_area_text.png")
-        self.__imgCMP.saveCropImage(pic, text_pic, textBox)
+        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
@@ -383,9 +523,11 @@ class COptionAction(CBaseLog):
             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('>')
@@ -397,8 +539,10 @@ class COptionAction(CBaseLog):
                     continue
 
                 try:
-                    numberText = numberTextList[numberTextList.__len__() - 1]
-                    return True, float(numberText)
+                    numberText = float(numberTextList[numberTextList.__len__() - 1])
+                    # 记录value值;
+                    self.__optionValueText = numberText
+                    return True, numberText
                 except Exception:
                     continue
             else:
@@ -434,17 +578,16 @@ class COptionAction(CBaseLog):
         # 默认返回;
         return False, 0 if isNumberText else ""
 
-    # 获取动态图片文本内容;
     '''
-    函数:
+    函数:获取动态图片文本内容
     参数:
-    返回:
+    返回:返回:Boolean、文本识别内容
     '''
 
     def __getDynamicPicText(self, optionName, optionTextList, siblingTextList, ocrConfigList, ocrThreshold, marqueeDict,
                             isNumberText, isValueSheet):
         # 判断图片是否动态:截图两次,判断两次文本内容是否相同;
-        firstRetsult, firstText = self.__getStaticPicText(takePicture(), optionName, optionTextList,
+        firstRetsult, firstText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList,
                                                           siblingTextList, ocrConfigList,
                                                           ocrThreshold, isNumberText, isValueSheet)
         if firstRetsult is False:
@@ -454,7 +597,7 @@ class COptionAction(CBaseLog):
         # 发送鲜活键, 保证界面鲜活;
         COptionAction.__sendAliveKey(marqueeDict['alive_key'])
         # 第二次截图;
-        secondRetsult, secondText = self.__getStaticPicText(takePicture(), optionName, optionTextList,
+        secondRetsult, secondText = self.__getStaticPicText(self.takePicture(), optionName, optionTextList,
                                                             siblingTextList, ocrConfigList,
                                                             ocrThreshold, isNumberText, isValueSheet)
         if secondRetsult is False:
@@ -478,7 +621,7 @@ class COptionAction(CBaseLog):
         picList = []
         # 如果有多项同级option都是跑马灯, 要成功识别文本需要间隔截图5次(大概会成功截图到最全的文本);
         for i in range(0, 5):
-            picList.append(takePicture())
+            picList.append(self.takePicture())
             # 间隔多久截图;
             time.sleep(marqueeDict['sleep_time'])
             # 发送鲜活键;
@@ -538,9 +681,8 @@ class COptionAction(CBaseLog):
         # endfor
         return OptionInfoDict
 
-    # 找到两个字符串左边或者右边相同的部分
     '''
-    函数:
+    函数:找到两个字符串左边或者右边相同的部分
     参数:
     返回:
     '''
@@ -567,9 +709,8 @@ class COptionAction(CBaseLog):
                 index += 1
             return str1[:index]
 
-    # 去掉字符串数组中每个字符串 左边或右边相同的部分
     '''
-    函数:
+    函数:去掉字符串数组中每个字符串 左边或右边相同的部分
     参数:
     返回:
     '''
@@ -611,15 +752,20 @@ class COptionAction(CBaseLog):
     返回:无
     '''
 
-    def sendKey(self, key, count, wait):
+    def sendKey(self, key, count=1, wait=1):
         if key is not None and key.__len__() > 0:
-            if type(key) == str:
-                COptionAction.__redRat3.sendKey(key, count, wait)
-            elif type(key) == list:
+            if type(key) == list:
                 for k in key:
+                    # 清除前后空格;
+                    k = k.lstrip()
+                    k = k.rstrip()
                     COptionAction.__redRat3.sendKey(k, 1, wait)
             else:
-                self.error(u"error:无效按键内容=%s" % key)
+                key = str(key)
+                # 清除前后空格;
+                key = key.lstrip()
+                key = key.rstrip()
+                COptionAction.__redRat3.sendKey(key, count, wait)
         else:
             self.error(u"error:按键内容空")
 

+ 91 - 5
ssat_sdk/MenuTree3/OptionConfig.py

@@ -15,6 +15,7 @@ g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth',
 # 这样便于后期维护时,根据对外的变量和函数来做处理。
 class COptionConfig(TConfig, CBaseLog):
     def __init__(self, exData, optionExcel):
+        CBaseLog.__init__(self)
         self.__exData = exData
         self.__optionExcel = optionExcel
         if self.__optionExcel is None:
@@ -70,8 +71,18 @@ class COptionConfig(TConfig, CBaseLog):
         self.info("value_dict:%s,%s" % (value_dict, type(value_dict)))
         return value_dict
 
-    # 获取Option的图片配置;
-    def __getICONConfig(self, optionName, is_value_sheet=False):
+    '''
+    函数:获取Option的图片配置,方式1
+    参数:
+    返回:Boolean, 字典
+    示例:True, {"icon_path": "", "dcfg": {}, "dir_path": ""}
+    
+    说明:
+        查找顺序:
+            [当前父节点][当前层级][当前节点] ->[当前父节点][当前层级] ->[根节点][当前层级][当前节点]
+            [curParent][curLevel][curOption] ->[curParent][curLevel] ->[firstParent][curLevel][curOption]
+    '''
+    def __getICONConfig1(self, optionName, is_value_sheet=False):
         # 默认值;
         def_cfg = {"icon_path": "", "dcfg": {}, "dir_path": ""}
         paths = self.__optionExcel.getOptionPaths(optionName)
@@ -125,13 +136,87 @@ class COptionConfig(TConfig, CBaseLog):
 
         return True, {"icon_path": icon_path, "dcfg": opc_cfg, "dir_path": icon_dir_path}
 
+    '''
+    函数:获取Option的图片配置,方式2
+    参数:
+    返回:Boolean, 字典
+    示例:True, {"icon_path": "", "dcfg": {}, "dir_path": ""}
+
+    说明:
+        查找顺序:
+            [根节点][当前层级][当前节点] ->[根节点][当前层级] ->[根节点][根层级]
+            [firstParent][curLevel][curOption] ->[firstParent][curLevel] ->[firstParent][firstLevel]
+    '''
+    def __getICONConfig2(self, optionName, is_value_sheet=False):
+        # 默认值;
+        def_cfg = {"icon_path": "", "dcfg": {}, "dir_path": ""}
+        paths = self.__optionExcel.getOptionPaths(optionName)
+        # 判断路径节点是否空;
+        if paths.__len__() == 0:
+            self.error(u"当前【%s】的路径节点空,使用默认的配置值%s" % (optionName, def_cfg))
+            return False, def_cfg
+
+        # 获取first parent;
+        first_parent = paths[g_level[0]]['parent']
+        # 获取当前option的level;
+        cur_level = g_level[paths.__len__() - 1]
+        # 当前option的父节点名称;
+        cur_parent = paths[cur_level]['parent']
+        if is_value_sheet is True:
+            cur_level = "value"
+
+        # option聚焦的首配图片路径;
+        icon_path = os.path.join(self.__uiTreeDir, "icon\\",
+                                 first_parent + "." + cur_level + "_" + optionName + ".png")
+        # option聚焦的dir首配图片路径;
+        icon_dir_path = os.path.join(self.__uiTreeDir,
+                                     "icon\\" + first_parent + "." + cur_level + "_" + optionName + ".dir.png")
+        # 图片聚焦时的定位参数;
+        opc_cfg = self.get_value_dict(cur_level, first_parent)
+
+        # 首配图片判断是否存在,不存在取用次配图片路径;
+        if not os.path.exists(icon_path):
+            # 使用次配图片,父级配图;
+            self.warn(u"Option(%s)首配图片不存在:%s" % (optionName, icon_path))
+            icon_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + ".png")
+            icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + cur_level + ".dir.png")
+            self.warn(u"Option(%s)首配图片不存在,尝试使用次配图(%s):%s,配置%s" % (optionName, cur_parent, icon_path, opc_cfg))
+            # 如果次配都不存在,使用顶层配图;
+            if not os.path.exists(icon_path):
+                self.warn(u"Option(%s)次配图片不存在:%s" % (optionName, icon_path))
+                # 使用顶层配图(first parent)
+                icon_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + "First.png")
+                icon_dir_path = os.path.join(self.__uiTreeDir, "icon\\" + first_parent + "." + "First.dir.png")
+                self.warn(
+                    u"Option(%s)次配图片不存在,尝试使用顶层配图(%s):%s,配置%s" % (optionName, first_parent, icon_path, opc_cfg))
+                # 如果顶层配图不存在,退出;
+                if not os.path.exists(icon_path):
+                    self.error(u"%s对应的顶层菜单配图不存在%s,使用默认配置%s" % (optionName, icon_path, def_cfg))
+                    return False, def_cfg
+                # endif
+            # endif
+        # endif
+
+        if opc_cfg.__len__() == 0:
+            opc_cfg = {"offset": 20, "minPeri": 0, "maxPeri": 0, "minArea": 0, "maxArea": 0, "morphology": []}
+
+        return True, {"icon_path": icon_path, "dcfg": opc_cfg, "dir_path": icon_dir_path}
+
     # 获取Option的图片配置;
     def getOptionICONConfig(self, optionName):
-        return self.__getICONConfig(optionName, False)
+        outResutl, outData = self.__getICONConfig1(optionName, False)
+        if outResutl is False:
+            return self.__getICONConfig2(optionName, False)
+
+        return outResutl, outData
 
     # 获取Value的图片配置;
     def getValueICONConfig(self, valueName):
-        return self.__getICONConfig(valueName, True)
+        outResutl, outData = self.__getICONConfig1(valueName, True)
+        if outResutl is False:
+            return self.__getICONConfig2(valueName, True)
+
+        return outResutl, outData
 
     # 获取Option的OCR配置;
     def getOptionOCRConfig(self, optionName):
@@ -197,7 +282,8 @@ if __name__ == "__main__":
     # print "getParentWaitTime", optionConfig.getParentWaitTime('setting')
     # print "getThresholdDict", optionConfig.getThresholdDict('setting')
     # print "getOptionICONConfig", optionConfig.getOptionICONConfig('picture')
+    print "getOptionICONConfig", optionConfig.getOptionICONConfig('picture_preset')
     # print "getValueICONConfig", optionConfig.getValueICONConfig('picture')
     # print "getOptionOCRConfig", optionConfig.getOptionOCRConfig('picture')
-    print "getICONResolutionConfig", optionConfig.getICONResolutionConfig()
+    # print "getICONResolutionConfig", optionConfig.getICONResolutionConfig()
 

+ 6 - 1
ssat_sdk/MenuTree3/OptionExcel.py

@@ -20,6 +20,7 @@ g_level = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth',
 
 class CPathParams(CBaseLog):
     def __init__(self):
+        CBaseLog.__init__(self)
         self.paths = OrderedDict()
 
     def addParent(self, level, pDict):
@@ -45,6 +46,7 @@ class CPathParams(CBaseLog):
 
 class CValueParams(CBaseLog):
     def __init__(self):
+        CBaseLog.__init__(self)
         self.values = OrderedDict()
 
     def addParent(self, pDict):
@@ -64,6 +66,7 @@ class CValueParams(CBaseLog):
 
 class CDialogParams(CBaseLog):
     def __init__(self):
+        CBaseLog.__init__(self)
         self.dialogs = OrderedDict()
 
     def addParent(self, level, pDict):
@@ -86,6 +89,7 @@ class CDialogParams(CBaseLog):
 # 这样便于后期维护时,根据对外的变量和函数来做处理。
 class COptionExcel(CBaseLog):
     def __init__(self, exData):
+        CBaseLog.__init__(self)
         self.__excelParse = CExcelParser(self)
         self.__exData = exData
         self.__pathParams = CPathParams()
@@ -518,6 +522,7 @@ 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']
         }
         # 找到value对应的ocr;
@@ -632,7 +637,7 @@ class COptionExcel(CBaseLog):
                     dict_option['enter_key'] = paths[level]['enter_key']
                     dict_option['move_key'] = paths[level]['move_key']
                     dict_option['back_key'] = ''  # 预留;
-                    dict_option['option_ocr'] = paths[level]['option_for_ocr'].split(';')
+                    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']
                     dict_option['option_others'] = paths[level]['others']

+ 1 - 4
ssat_sdk/MenuTree3/OptionFocus.py

@@ -11,14 +11,10 @@ import numpy as np
 import cv2 as cv
 import json
 from BaseLog import CBaseLog
-from OptionConfig import COptionConfig
 from ExtraData import CExtraData
 from OptionConfig import COptionConfig
 from OptionExcel import COptionExcel
 
-# 测试加头文件;
-from UIT_PathManage import UITPathManage
-
 
 class COptionFocus(CBaseLog):
     __rgbColor = RGBColor()
@@ -26,6 +22,7 @@ class COptionFocus(CBaseLog):
     __fd = FeatureDetect()
 
     def __init__(self, optionConfig):
+        CBaseLog.__init__(self)
         # CConfigManager对象;
         self.__optionConfig = optionConfig
         if self.__optionConfig is None:

+ 1 - 4
ssat_sdk/MenuTree3/OptionOCR.py

@@ -9,10 +9,6 @@ from BaseLog import CBaseLog
 from OptionConfig import COptionConfig
 from ssat_sdk import OCRConvert, ImageCMP, getSATTmpDIR
 
-# 测试加头文件;
-from TData import CTData
-from UIT_PathManage import UITPathManage
-
 
 class COptionOCR(CBaseLog):
     # 创建OCR对象
@@ -21,6 +17,7 @@ class COptionOCR(CBaseLog):
     __imgCMP = ImageCMP()
 
     def __init__(self, optionConfig, optionExcel):
+        CBaseLog.__init__(self)
         # COptionConfig对象;
         self.__optionConfig = optionConfig
         if self.__optionConfig is None:

+ 1 - 43
ssat_sdk/MenuTree3/TConfig.py

@@ -7,22 +7,10 @@ from configparser import ConfigParser
 import json
 
 
-# import ConfigParser
-
-
-# class MyConfigParser(ConfigParser.ConfigParser):
-#     def __init__(self, defaults=None):
-#         ConfigParser.ConfigParser.__init__(self, defaults=defaults)
-#
-#     def optionxform(self, optionstr):
-#         return optionstr
-
 class TConfig:
     def __init__(self, ini_path):
         self.path = ini_path
         self.cfgParser = ConfigParser()
-        # self.cfgParser = MyConfigParser()
-        # self.cfgParser.read(ini_path)
 
     # 保存修改;
     def save_config(self):
@@ -138,39 +126,9 @@ class TConfig:
         self.cfgParser.read(self.path)
         return self.cfgParser.has_option(section, option)
 
-    def getParentWaitTime(self, parent):
-        optionName = parent + "WaitTime"
-        try:
-            waitTime = float(self.get_value("waitTime", optionName))
-        except Exception:
-            waitTime = 1.0
-            print "获取%s界面的等待界面时间失败,使用默认值1.0s" % str(parent)
-        return waitTime
-
-    def getThresholdDict(self, option):
-        section = "Threshold"
-        thresholdDict = {}
-        value = self.get_value(section, option)
-        print "value:", value, type(value)
-        if not value:
-            return thresholdDict
-        value_str = str(value)
-        print "value_str:", value_str, type(value_str)
-        value_dict = eval(value_str)
-        print "value_dict:", value_dict, type(value_dict)
-        return value_dict
-
 
 if __name__ == "__main__":
-    # tconfig = TConfig(r'E:\SAT\SAT_API\ssat_sdk\MenuTree3\MenuTree.ini')
     tconfig = TConfig(r'E:\SAT\resource\MenuTree\NT72\DVB_nafrican\MenuTree.ini')
-    # print tconfig.get_options('First')
-    # print tconfig.get_sections()
-    # print tconfig.get_items('First')
     value = tconfig.get_value('value', 'Setting')
     print value
-    # print tconfig.get_dict(value)
-    # print tconfig.set_value('First', 'fffff','aaaa')
-    # tconfig.del_option('First','fffff')
-    # tconfig.del_section('Second')
-    # tconfig.set_value('second','setting','{"offset":10, "minPeri":0, "maxPeri":0, "minArea":0, "maxArea":0}')
+

+ 1 - 0
ssat_sdk/MenuTree3/TExcelParser.py

@@ -12,6 +12,7 @@ from BaseLog import CBaseLog
 
 class CExcelParser(CBaseLog):
     def __init__(self, optionExcel, xls_path=None):
+        CBaseLog.__init__(self)
         self.__optionExcel = optionExcel
         if type(xls_path) == str:
             xls_path = xls_path.decode('utf-8')

+ 157 - 0
ssat_sdk/MenuTree3/logic.py

@@ -0,0 +1,157 @@
+# -*- coding:utf-8 -*-
+import os, time
+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 OptionAction import COptionAction
+from ssat_sdk.source_input import SourceGenInput
+
+
+class CMenuLogic(CBaseLog):
+    sourceInput = SourceGenInput()
+
+    def __init__(self):
+        CBaseLog.__init__(self)
+        self.__exData = CExtraData()
+        self.__optionExcel = COptionExcel(self.__exData)
+        self.__optionConfig = COptionConfig(self.__exData, self.__optionExcel)
+
+    '''
+    函数:移动到目标节点上(路径节点和value节点)
+    参数:
+        opa                 COptionAction对象;
+        moveDirection       移动方向,True表示调用move_key[1](向下或向右), False表示调用move_key[0]。
+        maxTry              最大移动次数,防止死循环。
+    返回:Boolean。
+    '''
+    def __move2TargetNode(self, opa, moveDirection, maxTry=15):
+        tryCount = 0
+        # 是否到目标结点了;
+        isOnTargetNode = False
+        # 移动到目标节点;
+        while True:
+            # 判断是否移动成功;
+            if opa.isOnTargetNode()[0] is True:
+                # 如果已在目标结点,退出循环;
+                if opa.isOnTargetOption() or opa.isOnValueSheet():
+                    self.info(u"<到达目标路径节点或value节点>")
+                    isOnTargetNode = True
+                    break
+
+                # 未到目标,进入下一节点;
+                self.info(u"<进入下一节点>")
+                opa.enterNode()
+                continue
+            # endif
+
+            # 移动到下/上一节点;
+            if moveDirection:
+                opa.move2NextSiblingNode()
+            else:
+                opa.move2PrevSiblingNode()
+
+            tryCount += 1
+            if tryCount > maxTry:
+                self.warn(u"已%s遍历超过%d次,仍未找到焦点" % ("正向" if moveDirection else "逆向", maxTry))
+                break
+        # end-while
+
+        return isOnTargetNode
+
+    '''
+    函数:返回到主页(不一定就是Home页,一般指返回运行前的那个画面)
+    参数:
+        opa         COptionAction对象;
+    返回:
+    
+    '''
+    def __back2Home(self, opa):
+        self.info(u"返回主页")
+        while opa.pos > 0:
+            opa.back2ParentNode()
+        self.info(u"【如果设置value时,会自动回退到上一层父节点,此处操作正常。否则要多返回一层。】")
+
+    '''
+    函数:设置option的值。
+    参数:
+    返回:
+    '''
+    def setOptionValue(self, optionName, optionValue):
+        CMenuLogic.sourceInput.setPattern(11)
+        opa = COptionAction(optionName, optionValue, self.__optionConfig, self.__optionExcel)
+
+        # 检测路径是否有效;
+        if opa.checkRunOptionPath() is False:
+            return False
+
+        # 首先,调用根菜单;
+        opa.callFirstOptionShortCutKey()
+        time.sleep(1)
+        if opa.isOnFirstOption() is False:
+            self.error(u"未聚焦到根节点上,退出")
+            return False
+
+        # 遍历到目标option中;
+        if self.__move2TargetNode(opa, True) is False:
+            if self.__move2TargetNode(opa, False) is False:
+                return False
+
+        # 到达目标option后,进入value表;
+        self.info(u"进入value表")
+        opa.enterNode()
+
+        # 遍历到value节点中;
+        if self.__move2TargetNode(opa, True) is False:
+            if self.__move2TargetNode(opa, False) is False:
+                return False
+
+        # 找到value节点,设置值;
+        self.info(u"设置value值:一般只适用于Number类型或Input类型,不适合选择类型。但都必须调用,因为里面有enterkey操作")
+        opa.setOptionValue()
+
+        # 返回主页;
+        self.__back2Home(opa)
+
+    '''
+    函数:检测option的值;
+    参数:
+    返回:
+    '''
+
+    def checkOptionValue(self, optionName, optionValue):
+        CMenuLogic.sourceInput.setPattern(11)
+        opa = COptionAction(optionName, optionValue, self.__optionConfig, self.__optionExcel)
+
+        # 检测路径是否有效;
+        if opa.checkRunOptionPath() is False:
+            return False
+
+        # 首先,调用根菜单;
+        opa.callFirstOptionShortCutKey()
+        if opa.isOnFirstOption() is False:
+            self.error(u"未聚焦到根节点上,退出")
+            return False
+
+        # 遍历到目标option中;
+        if self.__move2TargetNode(opa, True) is False:
+            if self.__move2TargetNode(opa, False) is False:
+                return False
+
+        # 到达目标option后,进入value表;
+        opa.enterNode()
+
+        # 遍历到value节点中;
+        if self.__move2TargetNode(opa, True) is False:
+            if self.__move2TargetNode(opa, False) is False:
+                return False
+
+        # 找到value节点,设置值;
+        opa.setOptionValue()
+
+
+if __name__ == "__main__":
+    ml = CMenuLogic()
+    ml.setOptionValue('picture_preset', 'movie')