赞
踩
执行LAN_RPA.exe文件
点击 连接-编辑,会打开一个excel
填入描述(必须和sap gui里填写的描述一致,区分大小写),客户端,用户名,语言,密码
保存excel,点击 连接-重读,读取配置的sap连接
TCODE RZ11,输入 sapgui/user_scripting 设置值为TRUE,这个参数是启动SAP服务器gui script的意思。服务器重启后会失效,可以RZ10改,RZ10改完服务器重启不会失效。
gui-选项
把脚本通知的勾去掉,这个配置的作用是执行脚本时不会发出gui 通知
选择配置好的SAP连接,点击 打开 ,即可快速打开连接,快速切换用户和语言
选中打开的会话,右键
双击:跳转到窗口,如果双击窗口没跳出来,再双击一次就可以了
新会话:打开新的窗口
关闭其他:关闭其他窗口
关闭全部:关闭全部窗口
选中一个会话,点击 开始录制,跳转到sap窗口,在窗口输入事务码开始操作,操作结束之后点击 结束录制 即可完成制作。
注意工具只会根据输入的值生成模板,输入框的默认值如果要作为模板可输入字段,需要手工输入一下。
选择一个系统,点击 打开 按钮,打开一个连接
打开后选中一个会话,点击 开始录制 ,此时会自动跳转到SAP窗口。(如果选中的会话不是初始的tcode,可以点 初始化 按钮,还原到开始菜单)
输入 mm02 ,回车
输入物料号(注意mm02进来可能默认有物料号,此时需要重新输入,不能直接默认不输入物料直接回车进去),回车
选中视图,回车
修改描述,点击保存
操作完毕后点 结束录制
此时会弹出一个文件保存框,输入文件名
保存后会自动打开一个excel模板文件
插入一行,填写描述(此步骤可选)
在 字段 页签,添加多行数据,其中doc_num表示条目数,如果有行项目,也通过doc_num把抬头和行项目关联
在 表1 页签,添加多行数据(这里的表1是根据选择视图的弹出框自动生成的,作用是进来选中视图。如果视图位置不固定,可以在mm02保存默认视图,这次每次直接回车,不用再选视图)
保存excel,选中一个会话,点击 执行 按钮
此时会跳转到SAP窗口开始执行,执行完毕会自动打开excel,查看返回消息
打开或者选择一个会话,点击 开始录制
输入tcode,输入选择条件,执行
右键下载
输入文件名称(注意必须输入,默认带出来的删掉输一次),点击生成(或如果文件已存在就点击替换)
点 结束录制
保存模板
填一下选择条件,和导出的文件名称
保存后点击执行
查看返回消息
录制好的模板放在网盘的 demo 文件夹了,注意录制工序的时候录一行就好了
点击 开始录制,输入se37
点击 功能模块-执行-测试序列
输入
BAPI_GOODSMVT_CANCEL
BAPI_TRANSACTION_COMMIT
点击执行
输入物料凭证和年度,点击执行
点击返回
再点执行
点击 结束录制,在弹出的excel填写数据
在 字段 页签填写需要冲销的物料凭证
在 表1 页签按doc_num复制数据
保存excel,点击 执行
正常录制,没什么特别的,模板在demo文件夹中
录制的时候双击第一行行项目,在明细界面操作完之后,点击返回,然后点保存
在 字段 页签填入计划协议号
在 表1 页签填入行,用doc_num和和计划协议对应,要处理几行就复制几行
对于 科目分配 页签位置不固定 的解决方法:分场景录两个模板分别跑对应的数据,数据不分开用不同模板跑两次都可以,程序遇到找不到屏幕的情况会自动跳过不会卡住的
以安装ZLAN_ACC为例,直接执行demo文件夹中的 se38安装zlan_acc.xlsx 即可
自动执行到这个界面,敲一下回车就完成安装了
如果要自己录制,也正常从se38进去录制即可
关于alv录制,和表一样只录入一行即可,有两种做法,推荐第二种
1.点击插入行按钮,在excel的sheet“表”中填入多行,会重复执行插入行和填写alv单元格的操作,如果此操作导致alv错位,建议每次插入一行,保存好了再进来插入一行
2.在空白行输入,如果有多行数据,每次执行会在下一行输入。
如果进来alv已有多行数据,需要翻页才能到空白行,可以在录制前选中alv一列筛选为空的数据,然后保存用户的缺省布局,这样每次进来从第一行开始都是alv可输入的空白行了
此模板是每次进入spro配置一条,可以优化成:用一个窗口先打开spro,展开节点到指定位置,只录制添加新条目和返回,执行的时候也从已经展开节点的窗口执行,提升效率
点击 配置-编辑
会打开配置文件
[general]
theme = yeti 主题
delay = 0 执行延时,如1则会等待1秒再执行下一步。
template_no_ask = False 录制结束时会根据填写的模板路径直接保存模板,不会弹出文件选择框
bar_total = False 是否在进度条显示总条目数和当前已完成的条目数
refresh_interval = 1000 会话自动刷新间隔。如果打开的窗口太多1秒可能刷新不完,在前台会看到卡顿,此时需要调大刷新间隔
log = False 是否输出日志,如果为True,则执行完之后会把日志输出到excel上
[path]
vbs = C:\Users\10042832\AppData\Roaming\SAP\SAP GUI\Scripts\SCRIPT6.VBS vbs文件位置
template = C:/Users/DELL/Desktop/temp/ca01.xlsx 模板位置
初始版本,后续考虑要不要录windows窗口和网页,以及多窗口的并发。有需求可以交流一下
网盘中只更新了exe文件,用exe文件覆盖之前下载的文件夹中的exe
增加alv场景时,把table control换行逻辑改到了,修复table不能自动写到第二行的bug
处理了录制结束没点返回,也没点保存的表操作场景。如只在sm30界面操作数据,不从tcode进去,也没有保存
- import datetime
- import sys
- import configparser
- # from math import ceil
- import webbrowser
-
- import openpyxl
- import pyperclip
- from openpyxl.utils import get_column_letter
- import xlsxwriter
- import json
- import numpy as np
- import re
- import time
- from operator import methodcaller
-
- # import eventlet
- import subprocess
- import os
- import tkinter as tk
- from tkinter import ttk, font, messagebox, filedialog
- from tkinter import *
-
- import pandas as pd
- import ttkbootstrap
- from ttkbootstrap.constants import *
- from ttkbootstrap import Style
- import pythoncom
- # import pypiwin32
- import win32com
- from win32com import client
- from win32com.client import Dispatch
-
- class lan_rpa:
-
- def __init__(self):
- # 布局
- self.v_excption = 'lan stop'
- self.v_column = 0
- self.v_pady = 3
- self.v_padx = (10, 0)
-
- # 初始化sapgui
- self.__sapgui__start()
-
- # 变量
- self.v_filename_script = 'Script6.vbs'
- self.v_input = {}
- self.v_con_sel = {'desc': '', 'client': '', 'user': '', 'langu': ''}
- self.v_path_connection = 'connection.xlsx'
- self.v_path_config = 'config.ini'
-
- # 读连接
- self.__connection_read(True)
-
- # 读配置
- self.v_config = configparser.ConfigParser()
- self.v_config.read('config.ini')
-
- def __tkinter_main(self):
- root = tk.Tk()
- self.v_root = root
- root.wm_title('LAN RPA v1.2')
- # root.iconphoto(False, tk.PhotoImage(file=self.__resource_path('lan.png')))
- root.iconphoto(False, tk.PhotoImage(file='lan.png'))
- ttkbootstrap.Style().theme_use(self.v_config['general']['theme'])
-
- # 刷新事件
- root.after(self.v_config['general']['refresh_interval'], self.__session_refresh)
-
- # 布局
- frame1 = tk.Frame(root) # 连接选择
- self.v_frame1 = frame1
- frame2 = tk.Frame(root) # 会话列表+资源列表
- # paned = tk.PanedWindow(frame2, orient=tk.HORIZONTAL) # 默认是左右分布的
- # paned.pack(fill=tk.BOTH, expand=1) # 放到主窗口,上下左右缩放
- # frame_session = tk.Frame(paned) # 会话列表
- # paned.add(frame_session)
- # frame_explorer = tk.Frame(paned) # 资源列表
- # paned.add(frame_explorer)
- frame3 = tk.Frame(root)
- frame4 = tk.Frame(root)
- frame1.grid(row=0, column=0)
- frame2.grid(row=1, column=0, sticky='w', padx=self.v_padx, pady=10)
- frame3.grid(row=2, column=0, sticky='w', pady=10)
- frame4.grid(row=3, column=0, sticky='w', pady=10)
-
- # 菜单
- self.__menubar(root)
-
- # 连接选择
- self.__tkinter_frame_con()
- # 会话
- # self.__tkinter_frame_session(frame_session)
- self.__tkinter_frame_session(frame2)
- # # 资源浏览器
- # self.__tkinter_frame_explorer(frame_explorer)
-
- # 收藏列表
- # self.__tkinter_frame_favorite(frame3, root)
- self.__tkinter_frame_record(frame3)
- # 模板文件
- self.__tkinter_frame_file(frame4)
-
- # root.attributes('-topmost', True) #置顶
-
- # 居中显示
- dialog_width = 600
- dialog_height = 400
- # root.geometry('600x450')
- self.__window_geometry(dialog_width, dialog_height, root)
- root.mainloop()
-
- def __window_geometry(self, dialog_width, dialog_height, root):
- screenwidth = root.winfo_screenwidth()
- screenheight = root.winfo_screenheight()
- root.geometry("%dx%d+%d+%d" % (
- dialog_width, dialog_height, (screenwidth - dialog_width) / 2, (screenheight - dialog_height) / 2))
-
- def __tkinter_frame_record(self, frame3):
- # ttk.Button(frame3, text='刷新', command=self.__session_refresh).grid(row=0, column=self.__column_next(0),
- # padx=self.v_padx)
- # tk.Button(frame3, text="刷新", command=self.__session_refresh).grid(row=0, column=self.__column_next(0),
- # padx=self.v_padx)
- tk.Button(frame3, text="初始化", command=self.__session_init).grid(row=0, column=self.__column_next(0),
- padx=self.v_padx)
- tk.Button(frame3, text="开始录制", command=self.__record_start).grid(row=0, column=self.__column_next(),
- padx=self.v_padx)
- tk.Button(frame3, text="结束录制", command=self.__record_end).grid(row=0, column=self.__column_next(),
- padx=self.v_padx)
- frame_progressbar = tk.Frame(frame3)
- frame_progressbar.grid(row=0, column=self.__column_next(), padx=self.v_padx)
- self.v_progress_bar = ttk.Progressbar(frame_progressbar, length='100', mode="determinate", orient=tk.HORIZONTAL)
- self.v_progress_bar.grid(row=0)
- self.v_progress_count = tk.Label(frame_progressbar, text='')
- if self.v_config['general']['bar_total'] == 'True':
- self.v_progress_count.grid(row=1)
- self.v_progress_remain = tk.Label(frame3, text='')
- self.v_progress_remain.grid(row=0, column=self.__column_next())
-
- def __menubar(self, root):
- menubar = tk.Menu(root)
- menu_connection = tk.Menu(menubar)
- menu_config = tk.Menu(menubar)
- menu_about = tk.Menu(menubar)
-
- menu_connection.add_command(label='编辑', command=self.__connection_edit)
- menu_connection.add_command(label='重读', command=self.__connection_read)
-
- menu_config.add_command(label='主题', command=self.__win_theme)
- menu_config.add_command(label='编辑', command=self.__config_edit)
- menu_config.add_command(label='重读', command=self.__config_read)
-
- menu_about.add_command(label='帮助', command=self.__about_document)
- menu_about.add_command(label='交流', command=self.__about_conmunicate) # 777363609
-
- menubar.add_cascade(label='连接', menu=menu_connection)
- menubar.add_cascade(label='配置', menu=menu_config)
- menubar.add_cascade(label='关于', menu=menu_about)
- root.config(menu=menubar)
-
- def __tkinter_frame_session(self, frame_session):
- # session info list treeview
- col_text = ('id', 'SID', '客户端', 'TCODE', '用户', '语言')
- col_name = ('id', 'sid', 'client', 'tcode', 'user', 'langu')
- col_width = ('50', '50', '50', '180', '110', '80')
- self.v_column_len = len(col_name) # 记下tv展示的列,因为SystemSessionId和SessionNumber在后面两列
- xbar = ttk.Scrollbar(frame_session, orient='horizontal') # 水平滚动条
- ybar = ttk.Scrollbar(frame_session, orient='vertical') # 垂直滚动条
- xbar.pack(side=BOTTOM, fill=X)
- ybar.pack(side=RIGHT, fill=Y)
- tv = ttk.Treeview(frame_session, column=col_name, yscrollcommand=ybar.set, xscrollcommand=xbar.set,
- show='headings') # 隐藏首列
- self.v_tv_session = tv
- for i in range(len(col_text)):
- tv.column(col_name[i], width=col_width[i], anchor='n')
- tv.heading(col_name[i], text=col_text[i])
- ybar['command'] = self.v_tv_session.yview
- xbar['command'] = self.v_tv_session.xview
- tv.pack()
-
- # 数据
- # self.__session_refresh()
- # 右键菜单
- self.v_right_click_menu = tk.Menu(frame_session, tearoff=0)
- self.v_right_click_menu.add_command(label='新会话', command=self.__session_create)
- self.v_right_click_menu.add_command(label='关闭其他', command=self.__session_close_other)
- self.v_right_click_menu.add_command(label='关闭全部', command=self.__session_close_all)
- # self.v_right_click_menu.add_command(label='关闭后面', command=self.__session_close_back)
- # 绑定事件
- tv.bind('<Double-1>', self.__session_JumpForward) # 双击左键跳转窗口
- tv.bind('<Button-3>', self.__session_right_click) # 右键菜单
-
- # def __tkinter_connection_config(self):
- # self.v_tkt_SAPConnectionConfig = tkt_SAPConnectionConfig()
- # self.v_tkt_SAPConnectionConfig.open()
-
- def main(self):
-
- # self.__sapgui_init()
-
- # session处理
- # close_all()
- # return
-
- # logoin = self.login_init()
-
- self.__tkinter_main()
-
- return
-
- def __session_close_other(self):
- session_info = self.__session_info_selected()
- for connection in self.v_application.Connections:
- for session in connection.Sessions:
- if session.Busy == False:
- if session.Info.SystemSessionId != session_info.SystemSessionId:
- session.StartTransaction('ex')
- break
- if session.Info.SessionNumber != session_info.SessionNumber:
- session.findById("wnd[0]").close()
- continue
- # self.__session_refresh()
-
- def __session_close_all(self):
- for connection in self.v_application.Connections:
- for session in connection.Sessions:
- if session.Busy == False:
- session.StartTransaction('ex')
- break
- # self.__session_refresh()
-
- def __handle_connection_create(self):
- self.__connection_create(self.v_input['desc'].get(), self.v_input['client'].get(),
- self.v_input['user'].get(),
- self.v_input['password'], self.v_input['langu'].get())
-
- # self.__session_refresh()
-
- def __session_create(self):
- session = self.__session_selected()
- session.CreateSession()
-
- def __session_refresh(self):
- session_info_sel = self.__session_info_selected() # 在刷新tv数据之前先取到选中的行,为了刷新之后重新选中会话
-
- self.v_session_info_list = self.__session_info_list_get()
- i = 1
- data_list = []
- if self.v_session_info_list:
- for session_info in self.v_session_info_list:
- try:
- data_obj = (str(i), session_info.SystemName, session_info.Client, session_info.Transaction,
- session_info.User,
- session_info.Language,
- session_info.SystemSessionId, session_info.SessionNumber)
- data_list.append(data_obj)
- i = i + 1
- except Exception as e: # 可能窗口关闭,连接断开了
- continue
-
- self.v_tv_session.delete(*self.v_tv_session.get_children())
- for i in range(len(data_list)):
- self.v_tv_session.insert('', 'end', values=data_list[i])
-
- # 选中会话
- try:
- for child in self.v_tv_session.get_children():
- if session_info_sel.SystemSessionId == self.v_tv_session.item(child)['values'][self.v_column_len] \
- and session_info_sel.SessionNumber == self.v_tv_session.item(child)['values'][
- self.v_column_len + 1]:
- self.v_tv_session.selection_set(child)
- break
- except Exception as e:
- if self.v_tv_session.get_children():
- self.v_tv_session.selection_set(self.v_tv_session.get_children()[0]) # 默认选中第一个窗口
-
- # 继续刷新
- self.v_root.after(self.v_config['general']['refresh_interval'], self.__session_refresh)
-
- def __on_focus_in(self, event):
- pass
- # self.__session_list_refresh()
-
- def __session_right_click(self, event):
- row_id = self.v_tv_session.identify_row(event.y)
- if row_id:
- self.v_tv_session.selection_set(row_id)
- self.v_right_click_menu.post(event.x_root, event.y_root)
-
- def __session_JumpForward(self, event):
- session = self.__session_selected()
- if session:
- session.findById("wnd[0]").Visualize(True)
- session.findById("wnd[0]").JumpForward()
- return session
-
- def __session_selected(self):
- session_info = self.__session_info_selected()
- try: # 如果session关闭了,session_info.就会异常
- session = self.__session_get(session_info.SystemSessionId, session_info.SessionNumber)
- return session
- except Exception as e:
- messagebox.showinfo('选择会话', '当前会话繁忙或已关闭:' + str(e))
-
- def __session_info_selected(self):
- item_text = None
- for item in self.v_tv_session.selection(): # 这里values全部转成str!!
- item_text = self.v_tv_session.item(item, "values")
- if item_text:
- for session_info in self.v_session_info_list:
- try:
- if session_info.SystemSessionId == item_text[self.v_column_len] and str(
- session_info.SessionNumber) == \
- item_text[self.v_column_len + 1]:
- return session_info
- except Exception as e:
- continue
-
- def __tkinter_frame_con(self):
- # Input控件
- self.v_con_sel['desc'] = tk.StringVar()
- self.v_input['desc'] = ttk.Combobox(self.v_frame1, textvariable=self.v_con_sel['desc'], state='readonly',
- width='15')
- self.v_con_sel['client'] = tk.StringVar()
- self.v_input['client'] = ttk.Combobox(self.v_frame1, textvariable=self.v_con_sel['client'],
- state='readonly',
- width='3')
- self.v_con_sel['user'] = tk.StringVar()
- self.v_input['user'] = ttk.Combobox(self.v_frame1, textvariable=self.v_con_sel['user'], state='readonly',
- width='12')
- self.v_con_sel['langu'] = tk.StringVar()
- self.v_input['langu'] = ttk.Combobox(self.v_frame1, textvariable=self.v_con_sel['langu'], state='readonly',
- width='5')
-
- # 布局
- tk.Label(self.v_frame1, text='描述').grid(row=0, column=0, padx=self.v_padx)
- self.v_input['desc'].grid(row=0, column=1)
- tk.Label(self.v_frame1, text='客户端').grid(row=0, column=2, padx=self.v_padx)
- self.v_input['client'].grid(row=0, column=3)
- tk.Label(self.v_frame1, text='用户').grid(row=0, column=4, padx=self.v_padx)
- self.v_input['user'].grid(row=0, column=5)
- tk.Label(self.v_frame1, text='语言').grid(row=0, column=6, padx=self.v_padx)
- self.v_input['langu'].grid(row=0, column=7)
-
- # 设置连接值
- self.__con_sel_refresh()
-
- # 绑定事件:下拉框列表值
- self.v_input['desc'].bind("<<ComboboxSelected>>", self.__input_selected_desc)
- self.v_input['client'].bind("<<ComboboxSelected>>", self.__input_selected_client)
- self.v_input['user'].bind("<<ComboboxSelected>>", self.__input_selected_user)
-
- ttk.Button(self.v_frame1, text='打开', command=self.__handle_connection_create).grid(row=0, column=8,
- padx=self.v_padx,
- pady=10, )
-
- def __con_sel_refresh(self):
- self.v_input['desc']['value'] = list(self.v_con_list.keys())
- self.v_input['desc'].current(0) # 设置默认值
- self.__input_selected_desc(None)
-
- def __input_selected_desc(self, event):
- self.v_input['client'].delete(0, tk.END)
- self.v_input['client']['value'] = list(self.v_con_list[self.v_con_sel['desc'].get()].keys())
- self.v_input['client'].current(0) # 设置默认值
- self.__input_selected_client(event)
- self.__input_selected_user(event)
-
- def __input_selected_client(self, event):
- self.v_input['user'].delete(0, tk.END)
- self.v_input['user']['value'] = list(self.v_con_list[self.v_con_sel['desc'].get()]
- [self.v_con_sel['client'].get()].keys())
- self.v_input['user'].current(0) # 设置默认值
- self.__input_selected_user(event)
-
- def __input_selected_user(self, event):
- self.v_input['langu'].delete(0, tk.END)
- self.v_input['langu']['value'] = list(self.v_con_list[self.v_con_sel['desc'].get()]
- [self.v_con_sel['client'].get()][self.v_con_sel['user'].get()].keys())
- self.v_input['langu'].current(0) # 设置默认值
- self.v_input['password'] = \
- self.v_con_list[self.v_con_sel['desc'].get()][self.v_con_sel['client'].get()][
- self.v_con_sel['user'].get()][
- self.v_con_sel['langu'].get()]
-
- def __filedialog_select_vbs(self):
- (filepath, tempfilename) = os.path.split(self.v_config['path']['vbs'])
- selected_file_path = filedialog.askopenfilename(initialdir=filepath,
- filetypes=[('脚本', '*.vbs')]) # 使用askopenfilename函数选择单个文件
- if selected_file_path:
- self.v_config['path']['vbs'] = selected_file_path
- self.__config_write()
- self.v_stringvar_vbs.set(selected_file_path)
-
- def __filedialog_select_template(self):
- (filepath, tempfilename) = os.path.split(self.v_config['path']['template'])
- selected_file_path = filedialog.askopenfilename(initialdir=filepath,
- filetypes=[('模板', '*.xlsx')]) # 使用askopenfilename函数选择单个文件
- if selected_file_path:
- self.v_config['path']['template'] = selected_file_path
- self.__config_write()
- self.v_stringvar_template.set(selected_file_path)
-
- def __tkinter_frame_file(self, frame3):
- v_width_input = 55
-
- self.v_stringvar_vbs = tk.StringVar()
- self.v_stringvar_template = tk.StringVar()
- self.__refresh_file()
- # 布局控件
- # tk.Label(frame3, text="脚本").grid(column=0, row=0, pady=v_pady)
- # tk.Entry(frame3, textvariable=self.v_stringvar_vbs, width=v_width_input).grid(column=1, row=0)
- # tk.Button(frame3, text="选择", command=self.__filedialog_select_vbs).grid(row=0, column=2)
- # tk.Button(frame3, text="解析", command=self.__file_explain).grid(row=0, column=3, padx=self.v_padx)
- tk.Label(frame3, text="模板").grid(column=0, row=1, pady=self.v_pady, padx=self.v_padx)
- tk.Entry(frame3, textvariable=self.v_stringvar_template, width=v_width_input).grid(column=1, row=1)
- tk.Button(frame3, text="选择", command=self.__filedialog_select_template).grid(row=1, column=2)
- tk.Button(frame3, text="打开", command=self.__file_open_template).grid(row=1, column=3, padx=self.v_padx)
- tk.Button(frame3, text="执行", command=self.__button_file_execute).grid(row=1, column=4, padx=self.v_padx)
- # button_exec = tk.Button(frame3, text="执行")
- # button_exec.after(1,self.__button_file_execute)
- # button_exec.grid(row=1, column=4, padx=self.v_padx)
- tk.Button(frame3, text="终止", command=self.__button_stop).grid(row=1, column=5, padx=self.v_padx)
-
- def __refresh_file(self):
- # self.v_stringvar_vbs.set(self.v_config['path']['vbs'])
- self.v_stringvar_template.set(self.v_config['path']['template'])
-
- # def __config_get(self, key):
- # try:
- # return self.v_config.loc[self.v_config['key'] == key, 'value'].iloc[0]
- # except Exception as e:
- # return ''
-
- def __file_close(self, file):
- xlApp = Dispatch('Excel.Application')
- try:
- xlApp.DisplayAlerts = False # 设置不显示警告和消息框
- except Exception as e:
- messagebox.showerror('关闭excel', e)
- return
- # xlBook = xlApp.Workbooks.Open(file)
- workbooks_n = xlApp.Workbooks.Count
- # print(f'已打开工作簿的数量为:{workbooks_n}个')
-
- if workbooks_n < 0: return
-
- for i in range(1, workbooks_n + 1): # 工作簿索引从1开始
- path_ = xlApp.Workbooks(i).Path
- name_ = xlApp.Workbooks(i).Name
- file_ = os.path.join(path_, name_)
- # path = path_ + "\\backup_" + name_
- # print(f'第{i}个excel的文件路径为:{path}')
- if file == "/".join(file_.split("\\")): # 转义的\\替换回/
- xlApp.Workbooks(i).Close() # 关闭当前打开的文件,不保存文件
- # xlApp.Workbooks(i).Activate()
- # # xlApp.Workbooks(i).SaveAs(path)
- # xlApp.Workbooks(path).Close()
-
- # xlApp.Quit() #关闭所有打开的excel文件
- del xlApp
-
- def __file_open_template(self):
- try:
- os.startfile(self.v_stringvar_template.get())
- except Exception as e:
- messagebox.showerror('打开文件', e)
-
- def __file_execute(self):
- self.v_stop = True
- if not self.v_stringvar_template.get():
- messagebox.showerror('文件不能为空')
- return
- self.v_config['path']['template'] = self.v_stringvar_template.get()
- self.__config_write()
-
- # self.v_root.state('icon') # 最小化
- oper_start = 0
- oper_end = 0
- session = self.__session_JumpForward(None)
- if not session:
- return
- self.__file_close(self.v_stringvar_template.get()) # 先关闭,再回写消息,再打开
- dict = pd.read_excel(self.v_stringvar_template.get(), sheet_name=None,dtype='str') #str防止001.1这样的被当时float
-
- # 循环操作
- tabix = -1
- total = len(dict['字段']['doc_num'])
- start_time = time.time() # 记录程序开始运行时间
- for doc_num in dict['字段']['doc_num']:
- if not self.v_stop:
- break
- path_t = ''
- log = ''
- tabix = tabix + 1
- self.v_root.update()
- if str(doc_num) == 'nan':
- continue
- table_entry = False
- for i in range(len(dict['操作']['path'])):
- attr, equal, path, value = self.__file_oper_get(dict, i)
- table_entry = self.__table_entry_begin(dict['操作'], i, attr, value,
- table_entry) # 要传上一个table的Path,因为此时是win0
- table_flag = self.__file_path_check_table(path)
- alv_flag = self.__check_alv(attr)
- if alv_flag:
- table_flag = True
- if not table_flag and not table_entry: # 字段:也可能不是字段,比如win0
- if oper_start != 0: # 记录了表开始,现在回到了字段,说明表结束了
- oper_end = i - 1
- if attr == 'text': # 避免focus和double click等匹配到了值(或者参考表哪里用 = 判断?)
- for key in dict['字段']:
- if key == path + '+' + attr:
- value = dict['字段'].loc[dict['字段']['doc_num'] == doc_num, key].iloc[
- 0] # dict.loc根据行+列返回一个列的series,取series第一个值
- if re.findall('/okcd', path):
- if value[:2] != '/n' and value[:2] != '/N':
- value = '/n' + value
- break
- elif attr == 'insertText':
- for key in dict['代码']:
- if key == value: # 在 操作 页签的值记录了插入代码的位置
- value_oper = value
- if len(dict['代码'][key]) > 1:
- value = '\n\r'.join(dict['代码'][key]) + ',' + value_oper
- else:
- value = str(dict['代码'][key][0]) + ',' + value_oper
- break
- else: # 表:第一行正常循环,多行后面处理
- if oper_start == 0:
- oper_start = i
- # table = path.split(',')[0]
- for key in dict: # 循环每个sheet匹配表字段
- if re.findall('表', key):
- for key2 in dict[key]: # 循环取列名
- if key2 == path.split(',')[0] + '+' + attr: # 匹配一次列名就定位到表了
- row_doc_tab = len(dict[key].loc[dict[key]['doc_num'] == doc_num, 'doc_num'])
- sheet_tbl = key
- break
- else: # 里面break了则触发外面break跳出第二层循环
- continue
- break
-
- # 开始执行
- if oper_start != 0 and oper_end != 0: # 表:循环完了,开始一次性执行表全部条目
- pagesize = 0
- row_current = 0
- for x in range(row_doc_tab): # 条目数
- # 翻页:规定表和表不关联,处理完表1再处理表2,所以在表条目维度翻页就可以
- attr_t, equal_t, path_t, value_t = self.__file_oper_get(dict, oper_start)
- if self.__file_path_check_table(path_t): # 进了表才考虑定位行
- if 'getAbsoluteRow' in attr_t: # 选表行
- row_start = int(attr_t.split('(')[1].strip(')')[0]) # 选中绝对行要不要翻页的?
- path_tc = path_t
- else:
- row_start = int(path_t.split(',')[1].strip(']')[0]) # 开始录制的表行(可能不是从第一行开始的)
- path_tc = re.findall(r"(.+)/", path_t)[0]
- if pagesize == 0:
- pagesize = session.findById(path_tc).verticalScrollbar.pagesize
- if not row_current:
- row_current = row_start
-
- # div_mod = divmod(row_current, pagesize)
- # if div_mod[0] != 0 and div_mod[1] == 0: # 翻页:用翻页按钮是因为发现CA01不填第二行无法定位到第二行
- if row_current == pagesize: # 翻页
- log_r = self.__script_exec('sendVKey', '', session.findById("wnd[0]"), '82', "wnd[0]")
- if log_r:
- log = log + log_r
- row_current = row_start
- for oper_now in range(oper_start, oper_end + 1): # range不包含上限
- attr_t, equal_t, path_t, value_t = self.__file_oper_get(dict, oper_now)
-
- if self.__file_path_check_table(path_t):
- if 'getAbsoluteRow' in attr_t: # 选表行
- attr_t = attr_t.split('(')[0] + '(' + str(row_current) + ')' + attr_t.split(')')[
- 1] # path_t就是tc的位置不用处理,但是行在attr上
- elif 'modifyCell' in attr_t or 'insertRows': #修改alv单元格
- pass
- else:
- path_t = path_t.split(',')[0] + ',' + str(row_current) + ']'
- try:
- obj_t = session.findById(path_t)
- except Exception as e:
- pass
- for key2 in dict[sheet_tbl]: # 循环取列名
- if key2 == path_t.split(',')[0] + '+' + attr_t \
- or key2 == path_t + '+' + attr_t: # 选中行
- break
- if equal_t == 'X': # 有=号才是可输入字段
- value_t = dict[sheet_tbl].loc[dict[sheet_tbl]['doc_num'] == doc_num, key2].iloc[
- x] # 取series第x个值
- elif path_t == 'wnd[0]' or attr_t == 'insertRows': # 双击/ alv插入行
- value_t = value_t
- elif attr_t == 'modifyCell':
- value_t = [row_current,re.findall(r"\"(.+?)\"", value_t.rsplit(',',2)[1])[0],dict[sheet_tbl].loc[dict[sheet_tbl]['doc_num'] == doc_num, key2].iloc[
- x]] #行/列名/值
- else:
- value_t = 'nan'
- log_r = self.__script_exec(attr_t, equal_t, obj_t, value_t, path_t)
- if log_r:
- log = log + log_r
- row_current = row_current + 1
- oper_start = 0
- oper_end = 0
-
- if oper_start == 0: # 非表直接执行
- try:
- obj = session.findById(path) # 到执行的时候才可以取,因为可能前面在执行表
- except Exception as e:
- pass
- log_r = self.__script_exec(attr, equal, obj, value, path)
- if log_r:
- log = log + log_r
- # 判断表的双击是否返回或者保存了
- table_entry = self.__table_entry_end(path, attr, value, table_entry)
- # 回写消息
- msg_type = session.findById('wnd[0]/sbar').MessageType
- msg_text = session.findById('wnd[0]/sbar').Text
- dict['字段'].loc[
- tabix, 'msg_type'] = msg_type # 正确改值方式,否则:SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
- dict['字段'].loc[tabix, 'msg_text'] = msg_text
- if self.v_config['general']['log'] == 'True':
- dict['字段'].loc[tabix, 'log'] = log
- end_time = time.time() # 记录程序结束运行时间
- self.v_progress_bar['value'] = (tabix + 1) / total * 100
- self.v_progress_count['text'] = str(tabix + 1) + '/' + str(total)
- self.v_progress_remain['text'] = '剩余时间:' + str(
- datetime.timedelta(
- seconds=int((end_time - start_time) / (tabix + 1) * (total - (tabix + 1))))) # int向下取整
- try: # 只覆盖一个sheet 只有openpyxl有 a mode
- with pd.ExcelWriter(self.v_stringvar_template.get(), mode="a", engine="openpyxl",
- if_sheet_exists="replace", ) as writer:
- pd.DataFrame(dict['字段']).to_excel(writer, sheet_name="字段", index=False)
- # 按列遍历:单元格改回文本格式
- for x in range(1, writer.sheets['字段'].max_row + 1): # 先改有文本的单元格,再改列,直接改列不起作用
- for y in range(0, writer.sheets['字段'].max_column):
- writer.sheets['字段'][x][y].number_format = '@'
- for i in range(1, writer.sheets['字段'].max_column + 1):
- writer.sheets['字段'].column_dimensions[get_column_letter(i)].number_format = '@'
- except Exception as e:
- messagebox.showerror('消息回写excel出错', e)
-
- self.__file_open_template()
-
- def __script_exec(self, attr, equal, obj, value, path): # path可选,用于报错,可以优化成传入path和session
- value = str(value) # 避免1.0被当做float丢失.0了
- if attr == 'ResizeWorkingPane':
- return
- elif 'getAbsoluteRow' in attr:
- attr_1 = attr.split('(')[0]
- value_1 = attr.split('(')[1].split(')')[0]
- obj = getattr(obj, attr_1)(value_1) # 动态调用只能分两步:先取table row,再设置为True
- attr = attr.split('.')[1]
-
- time.sleep(int(self.v_config['general']['delay']))
- try:
- # if str(equal) != 'nan':
- if str(equal) == 'X':
- setattr(obj, attr, value) # 设置属性值
- else:
- if str(value) == 'nan':
- getattr(obj, attr)() # 直接调用方法
- else:
- if obj.type == 'GuiShell': # 如果是abap编辑器则传入多个参数
- if attr == 'insertText': # 代码直接转tuple可能有特殊字符
- getattr(obj, attr)(value.rsplit(',',2)[0], int(value.rsplit(',',2)[1]),int(value.rsplit(',',2)[2]))
- elif attr == 'insertRows':
- getattr(obj, attr)(value) # 调用方法传参
- else:
- value = eval(value) # 转成tuple
- getattr(obj, attr)(*value) # 加* tuple变参数
- else:
- getattr(obj, attr)(value) # 调用方法传参
- except Exception as e:
- return str(e) + str(path) + ':' + str(attr) + ':' + str(value) + ';'
-
- def __file_oper_get(self, df, i):
- path = df['操作']['path'][i]
- attr = df['操作']['attr'][i]
- equal = df['操作']['equal'][i]
- value = df['操作']['value'][i]
- if attr == 'insertText' or attr == 'modifyCell': # 如果是代码则不去掉引号
- pass
- else:
- value_n = re.findall(r"\"(.+?)\"", str(value)) # 去掉引号
- if value_n:
- value = value_n[0]
- return attr, equal, path, value
-
- def __file_explain(self):
- # v_len_max = 30000 #excel单元格最大字符数 32000
-
- if self.v_config['general']['template_no_ask'] == 'False': # 是否问模板路径
- self.v_path = self.__filedialog_create_template()
- if self.v_path:
- self.v_stringvar_template.set(self.v_path)
- else:
- return
- operation_list = []
- try:
- with open(self.v_path_script, encoding='UTF-16') as f: # 代码保存出来就是UTF-16不知道为啥
- for line in f.readlines(): # readlines(),函数把所有的行都读取进来;
- line = line.strip() # 删除行后的换行符,img_file 就是每行的内容啦
- path = re.findall(r"session.findById\(\"(.+?)\"\).", line) # (" ").
- if not path:
- continue
- attr = re.findall(r"\).(.+?) ", line) # 测试空格不用转义
- if not attr: # 没有值的情况,比如setFocus
- attr = re.findall('(?<=\).).*$', line)
- value = re.findall('(?<=\s).*$', line) # \s空格 取空格右边全部
- equal = ''
- if value:
- # equal = re.search('=', value[0]) # 值中找=号
- # if equal:
- if value[0][:1] == '=': # 如果第一位是等于号(值中可能也有=号)
- equal = 'X'
- value = re.findall('(?<=\s).*$', value[0]) # \s空格 取空格右边全部
- path = self.__array_get_first(path)
- attr = self.__array_get_first(attr)
- value = self.__array_get_first(value)
- operation = (path, attr, equal, value)
- operation_list.append(operation)
- except Exception as e:
- messagebox.showerror('解析文件', e)
- return
- dict_field = {}
- dict_tables = {}
- dict_code = {} # 代码
- dict_operation = {'path': {}, 'attr': {}, 'equal': {}, 'value': {}}
-
- # 开始解析操作
- i = -1
- table_entry = False
- df_operation_list = pd.DataFrame(operation_list)
- df_operation_list.columns = ['path', 'attr', 'equal', 'value']
- for oper in operation_list:
- i = i + 1
- path = oper[0]
- attr = oper[1]
- equal = oper[2]
- value = oper[3]
- path_t = '' # 上一个表的path
- if attr == 'caretPosition' or attr == 'verticalScrollbar.position' or attr == 'currentCellColumn':
- continue
- elif attr == 'insertText': # 代码:先写 代码 页签,再写操作
- value_code = value.rsplit(",", 2)[0] # 从右向左按 , 拆两次
- code_list = value_code.split(' + vbCr + "" + vbLf + ') # 根据换行符号拆开
- for x in range(len(code_list)):
- code_list[x] = code_list[x][1:len(code_list[x]) - 1] # 去掉首尾引号
- dict_code[value.rsplit(",", 2)[1] + ',' + value.rsplit(",", 2)[
- 2]] = code_list # 录制的脚本中文会变成?,需要录完了在excel自己覆盖一下数据
- value = value.rsplit(",", 2)[1] + ',' + value.rsplit(",", 2)[2] # 如果插入代码,则把位置写到操作页签,把值写入代码页签
- dict_operation['path'][i] = path
- dict_operation['attr'][i] = attr
- dict_operation['equal'][i] = equal
- dict_operation['value'][i] = value
-
- # 判断表的双击是否跳转屏幕了
- # oper_next = operation_list[i + 1]
- table_entry = self.__table_entry_begin(df_operation_list, i, attr, value, table_entry)
-
- table_flag = self.__file_path_check_table(path)
- alv_flag = self.__check_alv(attr)
- if alv_flag: #alv跟表一样处理
- table_flag = True
- if table_flag: # 如果是跳转的就沿用上一个表名,所以没有第一个表没有填值,只是双击的情况也需要记录表名
- if 'getAbsoluteRow' in attr: # 选表行
- table = path
- tabix = re.findall(r"\((.+?)\)", attr)[0]
- else: # 表字段
- if not alv_flag: #table control
- table = re.findall(r"(.+)/", path)[0] # 贪婪匹配:从整个字符去掉最后一个匹配
- tabix = re.findall(r",(.+?)\]", path)[0]
- else: #alv
- table = path
- tabix = value_code = value.rsplit(",", 2)[0] # 从右向左按 , 拆两次,第一个值为行
-
- if equal == 'X' or attr == 'setFocus' or attr == 'modifyCell': # setFocus必须要有,有些表没有输入直接双击的,不然匹配不到表
- if alv_flag:
- value = value.rsplit(',',2)[2]
- quotes = []
- quotes = re.findall(r"\"(.+?)\"", value) # 值中找引号
- if quotes:
- value = re.findall(r"\"(.+?)\"", value)[0]
- elif '"' in value: # 如果引号中间是空,正则也是找不到的
- value = ''
-
- if table_flag or table_entry: # 当前为表或者是表双击跳转的屏幕
- if table_flag and not alv_flag: # 表上还是要拆分行,只保留列;alv列不写入path了,直接读operation的value取到列
- path = path.split(',')[0]
- if not dict_tables.__contains__(table):
- dict_tables[table] = {}
- if not dict_tables[table].__contains__(path + '+' + attr):
- dict_tables[table][path + '+' + attr] = {}
- dict_tables[table][path + '+' + attr][tabix] = value
- else: # 字段
- dict_field[path + '+' + attr] = {0: value} # 一个path可能有多个属性操作,拼接起来作为字段名
-
- # 判断表的双击是否返回或者保存了
- table_entry = self.__table_entry_end(path, attr, value, table_entry)
-
- dict_field['msg_type'] = {0: ''}
- dict_field['msg_text'] = {0: ''}
- dict_field['log'] = {0: ''}
-
- # 写入excel
- self.__file_close(self.v_stringvar_template.get()) # 先关闭,避免写入的时候占用了
- excel_colum = []
- try: # with就不用再save了
- with pd.ExcelWriter(self.v_stringvar_template.get(),
- engine='xlsxwriter') as writer: # xlsxwriter才能写格式,但是xlsxwriter只能写不能读(所以不能修改)
- df1 = pd.DataFrame(dict_field)
- df1.insert(0, 'doc_num', 1)
- df1.to_excel(writer, sheet_name='字段', index=False) # 不要第一列的序号
- excel_colum.append(len(df1.keys()))
- i = 0
- for key in dict_tables.keys():
- i = i + 1
- dict_table = dict_tables[key]
- df2 = pd.DataFrame(dict_table)
- df2.insert(0, 'doc_num', 1)
- df2.to_excel(writer, sheet_name='表' + str(i), index=False)
- excel_colum.append(len(dict_table.keys()))
- # df_code = pd.DataFrame(dict_code)
- df_code = pd.DataFrame(pd.DataFrame.from_dict(dict_code, orient='index').values.T,
- columns=list(dict_code.keys())) # dict_code列长度可能不一样需要特殊处理
- df_code.to_excel(writer, sheet_name='代码', na_rep='', index=False)
- excel_colum.append(len(df_code.keys()))
- df3 = pd.DataFrame(dict_operation)
- df3.to_excel(writer, sheet_name='操作', na_rep='', index=False) # na_rep 缺失数据表示
- excel_colum.append(len(df3.keys()))
-
- # 设置单元格格式为文本
- workbook = writer.book
- format_text = workbook.add_format({'num_format': '@'})
- i = -1
- for sheet_name in writer.sheets:
- i = i + 1
- writer.sheets[sheet_name].set_column(0, excel_colum[i] - 1, None, format_text) # 不设置宽度
- except Exception as e:
- # raise e
- messagebox.showerror('写入excel', str(e))
- else:
- self.__file_open_template()
-
- def __table_entry_end(self, path, attr, value, table_entry):
- if table_entry and ((attr == 'sendVKey' and value == '11') or path == "wnd[0]/tbar[0]/btn[11]"): # 保存
- messagebox.showerror('请检查操作', '双击表跳转之后要返回,不要直接保存')
- raise self.v_excption
-
- if table_entry and ((attr == 'sendVKey' and value == '3') or path == "wnd[0]/tbar[0]/btn[3]" # 返回
- ):
- table_entry = False
- return table_entry
-
- def __table_entry_begin(self, operation_list, i, attr, value, table_entry): # 双击就关联表,否则就算点了button 2进到明细也认为互不关联
- if i == 0:
- return
- oper_path_pre = operation_list['path'][i - 1]
- if operation_list['path'][i] == 'wnd[0]' and (attr == 'sendVKey' and value == '2') and i + 1 < len(
- operation_list): # 上一个path_t存在表名是表上双击的
- oper_path_next = operation_list['path'][i + 1]
- table_next = oper_path_next.split('/')[0:len(oper_path_next.split('/')) - 1]
- table_pre = oper_path_pre.split('/')[0:len(oper_path_pre.split('/')) - 1]
- if table_next != table_pre: # 其实这样也不准确,因为跳过去可能直接翻页了,这样取到的也不是屏幕名
- table_entry = True
- return table_entry
-
- def __file_path_check_table(self, path):
- if path.split('/')[len(path.split('/')) - 2][:3] == 'tbl' \
- or path.split('/')[len(path.split('/')) - 1][:3] == 'tbl': # 如果是表选中行则最后一个不是字段是表名
- return TRUE
- else:
- return False
-
- def __check_alv(self, attr):
- if attr == 'modifyCell' or attr == 'insertRows':
- return TRUE
- else:
- return False
-
- def __array_get_first(self, field):
- if field:
- field = field[0]
- else:
- field = ''
- return field
-
- def __connection_read(self, init=False):
- con = {}
- df = pd.read_excel(self.v_path_connection, sheet_name=None)
- # 连接
- for i in range(len(df['连接']['desc'])):
- desc = str(df['连接']['desc'][i])
- client = str(df['连接']['client'][i])
- user = str(df['连接']['user'][i])
- langu = str(df['连接']['langu'][i])
- password = str(df['连接']['password'][i])
- if not con.__contains__(desc):
- con[desc] = {}
- if not con[desc].__contains__(client):
- con[desc][client] = {}
- if not con[desc][client].__contains__(user):
- con[desc][client][user] = {}
- if not con[desc][client][user].__contains__(langu):
- con[desc][client][user][langu] = password
- self.v_con_list = con
- if not self.v_con_list:
- self.v_con_list = {"未配置": {"300": {"abap01": {"zh": "123456"}}}}
- # with open(self.v_path_connection, 'w') as fp:
- # json.dump(self.v_con_list, fp)
- if not init:
- self.__con_sel_refresh()
-
- def __config_write(self):
- with open(self.v_path_config, "w+") as f:
- self.v_config.write(f)
-
- def __win_theme(self):
- # root = ttkbootstrap.Window()
- root = tk.Tk()
- root.wm_title('主题')
- # root.iconphoto(False, tk.PhotoImage(file='lan.png'))
- style = ttkbootstrap.Style()
- theme_names = style.theme_names() # 以列表的形式返回多个主题名
- theme_selection = ttkbootstrap.Frame(root, padding=(10, 10, 10, 0))
- theme_selection.pack(fill=X, expand=YES)
- lbl = ttkbootstrap.Label(theme_selection, text="主题:")
- theme_cbo = ttkbootstrap.Combobox(
- master=theme_selection,
- text=style.theme.name,
- values=theme_names,
- )
- theme_cbo.pack(padx=10, side=RIGHT)
- theme_cbo.current(theme_names.index(style.theme.name))
- lbl.pack(side=RIGHT)
-
- def change_theme(event):
- theme_cbo_value = theme_cbo.get()
- style.theme_use(theme_cbo_value)
- # theme_selected.configure(text=theme_cbo_value)
- theme_cbo.selection_clear()
- self.v_config['general']['theme'] = theme_cbo_value
- self.__config_write()
-
- theme_cbo.bind('<<ComboboxSelected>>', change_theme)
- # theme_selected = ttkbootstrap.Label(
- # master=theme_selection,
- # text="litera",
- # font="-size 24 -weight bold"
- # )
- # theme_selected.pack(side=LEFT)
- self.__window_geometry(200, 50, root)
- root.mainloop()
-
- def __filedialog_create_template(self):
- path = filedialog.asksaveasfilename(filetypes=[('excel文件', ['.xlsx'])],
- title='生成模板',
- initialfile='template.xlsx'
- )
- return path
-
- def __resource_path(self, relative_path):
- if getattr(sys, 'frozen', False): # 是否Bundle Resource
- base_path = sys._MEIPASS
- else:
- base_path = os.path.abspath(".")
- return os.path.join(base_path, relative_path)
-
- def __session_info_list_get(self):
- out_list = []
- try: # 有可能超时连接断开,一个连接都没有了
- for connection in self.v_application.Connections:
- for session in connection.Sessions:
- outdict = {}
- try: # 有可能超时断开,刚好执行到session.Info,就出异常了
- if session.Busy == False:
- session_info = session.Info
- outdict = session_info
- out_list.append((outdict))
- except Exception as e:
- pass
- except Exception as e:
- pass
- return out_list
-
-
- def __sapgui__start(self):
- self.v_path_sapgui = "C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe"
- if not self.__sapgui_check_logon():
- if self.v_path_sapgui and os.path.exists(self.v_path_sapgui):
- subprocess.Popen(self.v_path_sapgui)
- time.sleep(7)
- return self.__sapgui_check_logon()
- else:
- raise ValueError('Path to saplogon.exe not found')
-
- def __sapgui_check_logon(self):
- try:
- self.v_application = win32com.client.GetObject('SAPGUI').GetScriptingEngine
- except Exception as e:
- return False
- else:
- return True
-
- def __connection_create(self, desc, client, user, pwd, langu=None, close_conn=False, change_pwd=True):
- try:
- self.v_application.OpenConnection(desc, True)
- except pythoncom.com_error as error:
- hr, msg, exc, arg = error.args
- sap_error = "COM: {0} ({1})".format(msg, hr)
- msg = "Connection '{0}' not found. {1}".format(desc, sap_error)
- # raise RuntimeError(msg)
- messagebox.showerror('打开连接错误', error)
- try:
- sap_session = self.v_application.Children(self.v_application.Children.Count - 1).Children(0)
- except Exception as e:
- messagebox.showerror('获取会话', '检查SAP是否开启了sapgui script(RZ11配置)')
- return
- sap_session.findById("wnd[0]").JumpBackward()
- sap_session.findById('wnd[0]/usr/txtRSYST-BNAME').text = user
- sap_session.findById('wnd[0]/usr/pwdRSYST-BCODE').text = pwd
- sap_session.findById('wnd[0]/usr/txtRSYST-MANDT').text = client
- sap_session.findById('wnd[0]/usr/txtRSYST-LANGU').text = langu
- sap_session.findById('wnd[0]').sendVKey(0)
-
- # msg = sap_session.findById('wnd[0]/sbar')
- # if msg and msg[0] == "E":
- # sap_session.findById('wnd[0]').sendVKey(15) #shift + f3
- # raise RuntimeError("Could not log to the SAP server. {0}".format(msg[2]))
-
- # 重复登录
- try:
- sap_session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2")
- except pythoncom.com_error as error:
- pass
- else:
- sap_session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").select()
- sap_session.findById("wnd[1]/tbar[0]/btn[0]").press()
-
- # copyright弹框
- try:
- sap_session.findById("wnd[1]").sendVKey(0)
- except pythoncom.com_error as error:
- pass
-
- # return_pwd = SAPLogonPwd.is_change_password_window_while_connect(sap_session, change_pwd=change_pwd)
- # SAPNewSession.close_new_windows_while_connect(sap_session)
- return sap_session
-
- def __session_get(self, SystemSessionId, SessionNumber):
- for connection in self.v_application.Connections:
- for session in connection.Sessions:
- if session.Busy == False:
- if session.Info.SystemSessionId == SystemSessionId and session.Info.SessionNumber == SessionNumber:
- return session
-
- def __session_init(self):
- session = self.__session_selected()
- session.StartTransaction('')
- # self.__session_refresh()
-
- def __record_start(self):
- session = self.__session_selected()
- self.v_session = session # 获得session的句柄,这样方法执行完了,session的record可以保持不丢失
- session.Record = False
- session.SaveAsUnicode = True # 会存UTF-16
- session.RecordFile = self.v_filename_script
- self.v_path_script = session.RecordFile # 本地变量丢失句柄会清掉/关闭Record会丢掉先记下来
- self.v_config['path']['vbs'] = self.v_path_script
- self.__config_write()
- session.findById("wnd[0]").JumpForward()
- session.Record = True # 执行这句之后就会创建文件,之后前台操作都是实时写文件的
-
- def __record_end(self):
- session = self.__session_selected()
- if not session:
- return
- if session.RecordFile:
- self.v_path_script = session.RecordFile
- else:
- self.v_path_script = self.v_config['path']['vbs']
- session.Record = False # 会清空 session.RecordFile
- self.v_session = None # 释放session,此时gui右下角的脚本状态会立即消失
- self.__file_explain()
-
- # class SAPGUI:
-
- # def __tv_data_set(self,tv, data):
- def __column_next(self, column=None):
- if column != None:
- self.v_column = column
- self.v_column = self.v_column + 1
- return self.v_column
-
- def __connection_edit(self):
- os.startfile(self.v_path_connection)
-
- def __config_edit(self):
- os.startfile('config.ini')
-
- def __config_read(self):
- self.v_config.read('config.ini')
-
- def __button_file_execute(self):
- # self.__file_execute()
- self.v_root.after(1, self.__file_execute)
-
- def __button_stop(self):
- messagebox.showinfo('终止', '收到,当前条目执行完就会停止执行')
- self.v_stop = False
-
- def __about_document(self):
- # webbrowser.open('https://xiaolan.blog.csdn.net/article/details/128494871')
- webbrowser.open('https://blog.csdn.net/cylcylcylcylwo/article/details/128494871')
-
- def __about_conmunicate(self):
- pyperclip.copy('777363609') # 相当如写入到剪切板
-
-
-
- if __name__ == "__main__":
- lan_rpa = lan_rpa()
- lan_rpa.main()
-

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。