zoukankan      html  css  js  c++  java
  • Python驱动SAP GUI完成自动化(选择布局+动态获取节点值)

      小爬在之前的文章中(Python结合SAP GUI Script操作sap的简易教程

    讨论过如何利用工具Scripting Tracker录制python操纵SAP GUI的py脚本。软件的录制和生成的代码界面如下:

      场景一:实际业务中,除了一些常规步骤,我们可能会驱动SAP GUI中的一些布局,改变数据的展现形式,如:

      譬如这段点击“选择布局”,选中“/GX发出商品"布局的操作,我们用工具得到的python代码如下:

        session.findById("wnd[0]/tbar[1]/btn[33]").press() # 点击”选择布局"按钮
        session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").setCurrentCell(14, "TEXT") # 选中某个单元格
        session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").firstVisibleRow = 3 # 驱动纵向滚动条
        session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").selectedRows = "14" # 选中第15行
        session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").clickCurrentCell()  # 单击选中的单元格

      通过观察实际界面可知,我们要的“/GX发出商品"属于从上至下的第15个布局(索引号从0开始,因此这里的索引号应该是14),但是考虑到很多用户都有权限去设置布局或者变式,导致我们的自动化脚本如果直接以索引号来定位某个布局的做法,非常不稳定,易出错。更稳妥的做法是基于变式的“名称”,这个名称具备唯一性,哪怕将来有新增的布局,或者用户排版布局所用到的排序变了,我们依然可以通过布局的名称来定位它。

    通过SAP GuiContainerShell的rowCount和visibleRowCount属性,可以分别得到该布局的shell对应的行数和 可见行数,我们遍历所有的行,然后根据 每个cell的getCellValue方法就能拿到布局的名称,最终找到相应的布局。代码就可以这样写:

        session.findById("wnd[0]/tbar[1]/btn[33]").press() # 点击”选择布局"按钮
        e=session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell")
        row_count=e.rowCount
        variant="" # 变式
        for i in range(row_count):
            variant=e.getCellValue(i,"VARIANT")
            if variant=="/GX发出商品":
                e.setCurrentCell(i, "VARIANT") # 选中某个单元格
                e.clickCurrentCell()
        if variant=="":
            print("该布局中没有找到“/GX发出商品”变式,请核实!") 

    录制代码中以下这两行,在实际中,可以省略,不影响代码的执行:

        session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").firstVisibleRow = 3 # 驱动纵向滚动条
        session.findById("wnd[1]/usr/ssubD0500_SUBSCREEN:SAPLSLVC_DIALOG:0501/cntlG51_CONTAINER/shellcont/shell").selectedRows = "14" # 选中第15行

      场景二:实际业务中,我们可能会碰到这种行数不固定,节点可以逐级展开的sap 界面,我们又该如何取到对应节点对应字段的值呢?

     我们逐级展开节点到想要的位置,然后,选中某个值的过程,用tracker录制得到python代码,形式类如:

    session.findById("wnd[0]").resizeWorkingPane(234, 40, 0)
    session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]").expandNode("        339")
    session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]").expandNode("        576")
    session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]").expandNode("        608")
    session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]").expandNode("        612")
    session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]").expandNode("        613")
    session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]").selectItem("        614", "C          1")
    session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]").ensureVisibleHorizontalItem("        614", "C          1")
    session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]").topNode = "          2"

    这结构,每一个cell可以看作是一个Item对象,我们可以用sap GUI Scripting API手册查到它的GetItemText方法:

     这里的Key 和Name就是上面录制得到的脚本中的("        614","C          1")形式:

      由于这种含可展开节点的SAP GUI界面,行数都是不确定的,为了一劳永逸,我们还是要先动态得到 key,再基于Key Name,依据getItemText方法,得到节点的名称,如果跟我们要的节点名一样,我们就可找到 对应节点的 其他字段的值。

    于是关键问题演化成了,这个"        614"的key是怎么来的,我们大胆猜测,它就是一个变化的数字,前面填充空格,实际的几次试错后,小爬发现这个空格数不是固定的,而Key的长度是却固定的。

      所以,我们先用字符串的len("        614")方法得到key的长度为11,假如key中的数字 i 从1往1000(假定最大值不超过1000)变,我们可以计算其长度len(str(i)),来得到该填充的空格数,我们把整个界面用循环遍历一遍,接下来代码验证开始:

        col1_value="";col2_value="";col3_value="";node_name=""
    
        e=session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]")
        for i in range(1,1000): # 这里如果不清楚具体有多少行,可以设置一个很大的数,最后break跳出循环即可
            node_name = e.getItemText(" "*(11-len(str(i)))+str(i), "&Hierarchy")  # 索引号总共11位,除了数字,开头用多个空格符补齐,数字位数越大,则要补的空格越少
            if node_name == "损益表":
                col1_value=e.getItemText(" "*(11-len(str(i)))+str(i), "C          1")
                col2_value=e.getItemText(" "*(11-len(str(i)))+str(i), "C          3")
                col3_value=e.getItemText(" "*(11-len(str(i)))+str(i), "C          5")
                # 我们要获取 含“存货变动”关键字的 科目的cmp金额
            elif "存货变动" in node_name:
                cmp_value=e.getItemText(" "*(11-len(str(i)))+str(i), "C          5")
            if col1_value!="" and col2_value!="" and col3_value!="":
                break

    (更新日期:2020-05-31 :感谢评论区的童鞋友情提示,这段字符串右对齐,左边补空格的操作,python中有原生的字符串方法支持:str.rjust(width[, fillchar]),同样也有str.ljust(width[, fillchar])方法,上面代码中的的key可以用 str(i).rjust(11) 来快速得到……)

    (更新日期:2020-05-31 :上面的代码中,假定的这个GUI Tree有1000个节点,这还不是最严谨的做法,实际上,我们如果想遍历这个tree的所有节点,只需要遍历所有的key,而SAP其实提供了这样的API来得到tree中所有的节点的key)

     于是上面的代码可以改为下面更简单,更严谨的方式,每个节点的key值不再需要去构造:

    e=session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell[1]")
    node_total=len(e.GetAllNodeKeys()) # 得到所有的节点的总数
    for key in e.GetAllNodeKeys():
        node_name=e.getItemText(key, "&Hierarchy")
        if node_name == "损益表":
            col1_value=e.getItemText(key, "C          1")
            col2_value=e.getItemText(key, "C          3")
            col3_value=e.getItemText(key, "C          5") 
        elif "存货变动" in node_name:
            cmp_value=e.getItemText(key, "C          5")  
        if col1_value!="" and col2_value!="" and col3_value!="":
            break  

      这段代码完美运行,上面的col1_value,col2_value,col3_value就对应上图中“tot.rpt.pr  tot.cmp.pr  绝对差异”  三个字段的值。从上面的代码可以看出来,我们手工想要看到某个根节点的值,需要对节点逐级展开(expand node),操作非常繁琐,通过python对过程自动化,后台的代码不需要展开节点本身,只要拿到节点的key 和name,我们原则上就可以取到每个节点的Itemtext,非常高效~~~

      各位正在研究python-sap自动化的童鞋,一起来get新技能吧~~~

  • 相关阅读:
    路由器安全-FPM
    网络基础设施保护和局域网安全
    DMVPN基础配置
    IPSec的链路和设备备份
    IPSec的高可用性技术
    关于Mobility Express转LAP注意事项
    实际中可能遇到的NAT问题(IPsec)
    NAT-T和PAT(IPSec)
    加密设备NAT对IPSec的影响
    影响IPSec的网络问题
  • 原文地址:https://www.cnblogs.com/new-june/p/12994634.html
Copyright © 2011-2022 走看看