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新技能吧~~~

原文地址:https://www.cnblogs.com/new-june/p/12994634.html