小爬在之前的文章中(Python結合SAP GUI Script操作sap的簡易教程)
討論過如何利用工具Scripting Tracker錄制python操縱SAP GUI的py腳本。軟件的錄制和生成的代碼界面如下:
場景一:實際業務中,除了一些常規步驟,我們可能會驅動SAP GUI中的一些布局,改變數據的展現形式,如:
譬如這段點擊“選擇布局”,選中“/GX發出商品"布局的操作,我們用工具得到的python代碼如下:
通過觀察實際界面可知,我們要的“/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新技能吧~~~