Python 实现 西门子smart200 程序中的变量 批量替换
写smart200的PLC代码时,官方软件的变量批量替换功能,很是不好用.于是专门用python写了一个辅助软件.
经常需要为设备1和设备2写程序,代码几乎完全一样,只需要把相关变量中的"变量1"改为"变量2"
但用官方软件,一点全部替换,就会把其他子程序里的变量一起替换了.一个一个替换又很麻烦.
这段代码需要提前把需要修改的子程序(POU)导出,成为一个xxx.awl文件.
接着把所有变量整理到excel里面,复制到一个文本文件中,格式是每一行写一条变量和地址,中间用TAB分割
运行代码后,程序会要求导入这2个文件,并写出替换规则,最后程序会自动生成替换的awl文件,用记事本打开,修改子程序名称和子程序地址,即可导入回Step7 Mirco Smart 了
1 import os 2 import re 3 import tkinter as tk 4 from tkinter import filedialog 5 from tkinter import messagebox 6 from tkinter import simpledialog 7 import datetime 8 9 ''' 10 替换表的格式要求: 11 第1列为模板变量名,第2列为模板变量地址,第3列为实例变量名,第4列为实例变量地址 12 ''' 13 14 15 root = tk.Tk() 16 root.withdraw() 17 18 ''' 19 # 读入 替换列表文件 20 messagebox.showwarning(title = '提示',message='请选择"替换列表"文件') 21 替换列表文件路径 = filedialog.askopenfilename() 22 替换列表文件路径 = 替换列表文件路径.replace('/',chr(92)) 23 输入文件 = open(替换列表文件路径,'r',encoding = "utf-8") 24 # 读替换数据 25 替换列表 = [] 26 for 行文本 in 输入文件: 27 替换列表.append(行文本.replace('\n','').split('\t')) 28 输入文件.close() 29 ''' 30 31 ''' 32 可以考虑直接读入全部变量列表,根据要替换的内容,自动生成替换列表,从而更大程度减少人工 33 ''' 34 35 # 读入 全部变量列表 36 messagebox.showwarning(title = '提示',message='请选择"全部变量"文件') 37 全部PLC变量文件路径 = filedialog.askopenfilename() 38 全部PLC变量文件路径 = 全部PLC变量文件路径.replace('/',chr(92)) 39 输入文件 = open(全部PLC变量文件路径,'r',encoding = "utf-8") 40 # 读全部PLC变量 41 全部PLC变量列表 = [] 42 for 行文本 in 输入文件: 43 全部PLC变量列表.append(行文本.replace('\n','').split('\t')) 44 输入文件.close() 45 46 # 要求用户输入替换规则,被替换部分支持正则表达式,用 >>> 分隔 47 替换规则 = simpledialog.askstring(title = '获取替换规则',prompt='请输入替换规则(被替换字符串支持正则表达式):',initialvalue = '被替换字符串>>>替换的字符串') 48 49 # 生成替换列表 50 替换规则 = 替换规则.split('>>>') 51 符合被替换的PLC变量列表 = [] 52 替换变量列表 = [] 53 for PLC变量 in 全部PLC变量列表: 54 # print('分析变量%s是否需要替换' % PLC变量) 55 if re.search(替换规则[0],PLC变量[0]) is not None: 56 # print('变量%s符合替换规则' % PLC变量) 57 替换后变量名 = re.sub(替换规则[0],替换规则[1],PLC变量[0]) 58 发现可替换变量 = False 59 for PLC变量1 in 全部PLC变量列表: 60 临时列表 = [] 61 if PLC变量1[0] == 替换后变量名: 62 发现可替换变量 = True 63 临时列表.append(PLC变量[0]) 64 临时列表.append(PLC变量[1]) 65 临时列表.append(PLC变量1[0]) 66 临时列表.append(PLC变量1[1]) 67 符合被替换的PLC变量列表.append(临时列表) 68 print('变量%s找到可替换变量,并追加到 "符合被替换的PLC变量列表"' % PLC变量[0]) 69 if not 发现可替换变量: 70 临时列表 = [] 71 临时列表.append(PLC变量[0]) 72 临时列表.append(PLC变量[1]) 73 临时列表.append('未发现可配对的变量') 74 临时列表.append('未发现可配对的变量') 75 符合被替换的PLC变量列表.append(临时列表) 76 print('\n变量%s未找到可替换变量\n' % PLC变量[0]) 77 78 79 80 # 读入POU文档 81 messagebox.showwarning(title = '提示',message='请选择"POU"文件') 82 POU文件路径 = filedialog.askopenfilename() 83 POU文件路径 = POU文件路径.replace('/',chr(92)) 84 输入文件 = open(POU文件路径,'r',encoding = "GB2312") 85 POU内容 = 输入文件.read() 86 输入文件.close() 87 88 # 替换处理 89 替换列表 = 符合被替换的PLC变量列表 90 变量次序号 = 0 91 替换信息 = [] 92 def 替换变量处理(查找到的PLC变量地址字符串组): 93 查找到的PLC变量地址字符串 = 查找到的PLC变量地址字符串组.group() 94 if 查找到的PLC变量地址字符串.find(',') == -1: 95 return ' ' + 替换信息[3] + '\n' 96 else: 97 return ' ' + 替换信息[3] + ',' 98 for 替换信息 in 替换列表: 99 查找到的PLC变量数量 = len(re.findall(' ' + 替换信息[1] + r'(,|\n)',POU内容)) 100 if 查找到的PLC变量数量 > 0: 101 变量次序号 += 1 102 print("进行第%04d个变量替换,替换变量为%s,该变量在程序中出现%s次" % (变量次序号,替换信息,查找到的PLC变量数量)) 103 if 替换信息[2] == '未发现可配对的变量': 104 print('发现如下异常:\n变量名:%s 地址:%s 出现在程序中,也符合替换规则,但未找到对应变量,请检查"全部变量"文件,是否完整\n' % (替换信息[0],替换信息[1])) 105 else: 106 POU内容 = re.sub(' ' + 替换信息[1] + r'(,|\n)',替换变量处理,POU内容) 107 # print(POU内容) 108 输出文件路径 = re.search(r'^.+\.',POU文件路径).group() 109 输出文件路径 = re.sub(r'\.$','_变量替换_' + datetime.datetime.now().strftime('%y_%m_%d_%H%M%S') + '.awl',输出文件路径) 110 输出文件 = open(输出文件路径,'w',encoding = "GB2312") 111 输出文件.write(POU内容) 112 输出文件.close() 113 os.system(r"explorer /select," + 输出文件路径)
后来,又修改了一下,支持多个输入文件,输出多个替换结果
import os import re import tkinter as tk from tkinter import filedialog from tkinter import messagebox from tkinter import simpledialog import datetime ''' 替换表的格式要求: 第1列为模板变量名,第2列为模板变量地址,第3列为实例变量名,第4列为实例变量地址 ''' root = tk.Tk() root.withdraw() ''' # 读入 替换列表文件 messagebox.showwarning(title = '提示',message='请选择"替换列表"文件') 替换列表文件路径 = filedialog.askopenfilename() 替换列表文件路径 = 替换列表文件路径.replace('/',chr(92)) 输入文件 = open(替换列表文件路径,'r',encoding = "utf-8") # 读替换数据 替换列表 = [] for 行文本 in 输入文件: 替换列表.append(行文本.replace('\n','').split('\t')) 输入文件.close() ''' ''' 可以考虑直接读入全部变量列表,根据要替换的内容,自动生成替换列表,从而更大程度减少人工 ''' # 读入 全部变量列表 messagebox.showwarning(title = '提示',message='请选择"全部变量"文件') 全部PLC变量文件路径 = filedialog.askopenfilename() 全部PLC变量文件路径 = 全部PLC变量文件路径.replace('/',chr(92)) 输入文件 = open(全部PLC变量文件路径,'r',encoding = "utf-8") # 读全部PLC变量 全部PLC变量列表 = [] for 行文本 in 输入文件: 全部PLC变量列表.append(行文本.replace('\n','').split('\t')) 输入文件.close() # 要求用户输入替换规则,被替换部分支持正则表达式,用 >>> 分隔. # 为增加效率,替换部分以,为间隔,可生成多个设备的程序 替换规则 = simpledialog.askstring(title = '获取替换规则',prompt='请输入替换规则(被替换字符串支持正则表达式):',initialvalue = '设备1>>>设备2,设备3...') # 生成替换列表 替换规则 = 替换规则.split('>>>') 替换规则[1] = 替换规则[1].split(',') 符合被替换的PLC变量列表 = [] 替换变量列表 = [] ''' for PLC变量 in 全部PLC变量列表: # print('分析变量%s是否需要替换' % PLC变量) if re.search(替换规则[0],PLC变量[0]) is not None: # print('变量%s符合替换规则' % PLC变量) 替换后变量名 = re.sub(替换规则[0],替换规则[1],PLC变量[0]) 发现可替换变量 = False for PLC变量1 in 全部PLC变量列表: 临时列表 = [] if PLC变量1[0] == 替换后变量名: 发现可替换变量 = True 临时列表.append(PLC变量[0]) 临时列表.append(PLC变量[1]) 临时列表.append(PLC变量1[0]) 临时列表.append(PLC变量1[1]) 符合被替换的PLC变量列表.append(临时列表) print('变量%s找到可替换变量,并追加到 "符合被替换的PLC变量列表"' % PLC变量[0]) if not 发现可替换变量: 临时列表 = [] 临时列表.append(PLC变量[0]) 临时列表.append(PLC变量[1]) 临时列表.append('未发现可配对的变量') 临时列表.append('未发现可配对的变量') 符合被替换的PLC变量列表.append(临时列表) print('\n变量%s未找到可替换变量\n' % PLC变量[0]) ''' # 生成列表框架,其0,1列为符合替换规则的变量名及地址,并按替换规则生成替换后变量名,其变量地址为空字符 ''' 生成的结构如下[[设备1名,设备1地址],[[设备2名,设备2地址],[设备3名,设备3地址],[设备3名,设备3地址]...]] ''' 替换列表框架 = [] for PLC变量 in 全部PLC变量列表: # 分析变量是否符合替换规则 if re.search(替换规则[0],PLC变量[0]) is not None: 替换后变量名组 = [] for 被替换部分 in 替换规则[1]: 替换后变量名组.append([re.sub(替换规则[0],被替换部分,PLC变量[0]),'']) 替换列表框架.append([PLC变量,替换后变量名组]) # 轮询PLC变量表,如果替换后的变量有定义,则填入地址,如果无定义,则填入 未发现可配对的变量 for 替换列表框架行 in 替换列表框架: for 生成的变量 in 替换列表框架行[1]: 发现可替换变量 = False for PLC变量 in 全部PLC变量列表: if PLC变量[0] == 生成的变量[0]: 生成的变量[1] = PLC变量[1] 发现可替换变量 = True if not 发现可替换变量: 生成的变量[1] = '未发现可配对的变量' print('变量 %s 未定义' % 生成的变量) # 读入POU文档 messagebox.showwarning(title = '提示',message='请选择"POU"文件') POU文件路径列表 = filedialog.askopenfilenames() 输出文件夹路径 = re.search(r'^.+\.',POU文件路径列表[0]).group().replace('.','') + '_输出_' + datetime.datetime.now().strftime('%y_%m_%d_%H%M%S') os.mkdir(输出文件夹路径) 起始子函数序号 = 90 for POU文件路径 in POU文件路径列表: POU文件路径 = POU文件路径.replace('/',chr(92)) 输入文件 = open(POU文件路径,'r',encoding = "GB2312") 原始POU内容 = 输入文件.read() 输入文件.close() # 生成替换列表 替换规则数量 = len(替换规则[1]) for 替换批次 in range(替换规则数量): 替换列表 = [] for 替换列表框架行 in 替换列表框架: 替换列表.append([替换列表框架行[0][0],替换列表框架行[0][1],替换列表框架行[1][替换批次][0],替换列表框架行[1][替换批次][1]]) # 替换处理 变量次序号 = 0 替换信息 = [] POU内容 = '%s' % 原始POU内容 def 替换变量处理(查找到的PLC变量地址字符串组): 查找到的PLC变量地址字符串 = 查找到的PLC变量地址字符串组.group() if 查找到的PLC变量地址字符串.find(',') == -1: return ' ' + 替换信息[3] + '\n' else: return ' ' + 替换信息[3] + ',' for 替换信息 in 替换列表: 查找到的PLC变量数量 = len(re.findall(' ' + 替换信息[1] + r'(,|\n)',POU内容)) if 查找到的PLC变量数量 > 0: 变量次序号 += 1 print("进行第%04d个变量替换,替换变量为%s,该变量在程序中出现%s次" % (变量次序号,替换信息,查找到的PLC变量数量)) if 替换信息[3] == '未发现可配对的变量': print('发现如下异常:\n变量名:%s 地址:%s 出现在程序中,也符合替换规则,但未找到对应变量,请检查"全部变量"文件,是否完整\n' % (替换信息[0],替换信息[1])) else: POU内容 = re.sub(' ' + 替换信息[1] + r'(,|\n)',替换变量处理,POU内容) # 修改子函数名称及序号 原文件名 = re.sub(r'^.+\\','',POU文件路径) 原文件名 = re.sub(r'\..+','',原文件名) POU内容 = re.sub('SUBROUTINE_BLOCK.+','SUBROUTINE_BLOCK ' + 原文件名 + 'to' + 替换规则[1][替换批次] + ':SBR' + str(起始子函数序号),POU内容) 起始子函数序号 += 1 # print(POU内容) # 输出文件路径 = re.search(r'^.+\.',POU文件路径).group() 输出文件路径 = 输出文件夹路径 + chr(92) + '.' 输出文件路径 = re.sub(r'\.$',原文件名 + '_变量替换%s_' % 替换规则[1][替换批次] + datetime.datetime.now().strftime('%y_%m_%d_%H%M%S') + '.awl',输出文件路径) 输出文件 = open(输出文件路径,'w',encoding = "GB2312") 输出文件.write(POU内容) 输出文件.close() # 文件生成后,打开所在文件夹 路径太长,打不开了 # os.system(r"explorer /select," + 输出文件路径)
更新一下,加入UI,改了一下流程
import os import re import tkinter as tk from tkinter import filedialog from tkinter import messagebox from tkinter import simpledialog import datetime '''这次重构的目的 在初版中,是分析生成 地址-地址 的映射,然后进行替换 后来发现,这样只适合同一个PLC项目中 不同设备的生成 对于不同的PLC项目,地址段很可能不同,所以更改一下生成方式 1, 导入模板点表,实例点表,模板POU 2, 将模板POU中的地址改为变量(依据模板点表) 3, 依据替换规则,修改POU中的变量名 4, 依据实列点表,将POU中的变量名替换回地址 此版本将使用 tkinter 图形界面 ''' def 选取文件(): 文件路径 = filedialog.askopenfilename() 文件路径 = 文件路径.replace('/',chr(92)) return 文件路径 def 读入点表(点表文件路径): 输入文件 = open(点表文件路径,'r',encoding = "utf-8") # 读全部PLC变量 点表变量列表 = [] for 行文本 in 输入文件: 点表变量列表.append(行文本.replace('\n','').split('\t')) 输入文件.close() return 点表变量列表 def 读入模板(POU文件路径): POU文件路径 = POU文件路径.replace('/',chr(92)) 输入文件 = open(POU文件路径,'r',encoding = "GB2312") 原始POU内容 = 输入文件.read() 输入文件.close() return 原始POU内容 def 提取替换规则(替换规则文本): 替换规则 = 替换规则文本.split('>>>') 替换规则[1] = 替换规则[1].split(',') return 替换规则 def 地址换成变量(模板, 点表): '''生成以变量名描述的程序文件,为方便后期变量识别,加前缀 qsblmc 和后缀 jsblmc''' 模板内容 = '%s' % 模板 def 替换变量处理(查找到的PLC变量地址字符串组): 查找到的PLC变量地址字符串 = 查找到的PLC变量地址字符串组.group() if 查找到的PLC变量地址字符串.find(',') == -1: return ' ' + 'qsblmc' + 变量[0] + 'jsblmc' + '\n' else: return ' ' + 'qsblmc' + 变量[0] + 'jsblmc' + ',' for 变量 in 点表: 模板内容 = re.sub(' ' + 变量[1] + r'(,|\n)',替换变量处理,模板内容) print(模板内容) global 模板中间文件 模板中间文件 = 模板内容 return 模板内容 def 变成换成地址(模板, 点表): 模板内容 = '%s' % 模板 变量字典 = dict(点表) 查询到的变量信息 = re.findall('qsblmc.*?' + '.*?jsblmc', 模板内容) for 变量带前后缀 in 查询到的变量信息: 去除前后缀的变量名 = 变量带前后缀.replace('qsblmc', '').replace('jsblmc', '') if 去除前后缀的变量名 in 变量字典: 模板内容 = re.sub(变量带前后缀, 变量字典[去除前后缀的变量名], 模板内容) else: print('变量 %s 不在点表内,请核实') return 模板内容 def 变量名替换(模板, 替换前字符, 替换后字符, 变量表B): 模板内容 = '%s' % 模板 # 模板内容 = re.sub('qsblmc.*?' + 替换前字符 + '.*?jsblmc',替换后字符,模板内容) 变量名列表 = [] for 变量信息 in 变量表B: 变量名列表.append(变量信息[0]) 查询到的变量信息 = re.findall('qsblmc.*?' + '.*?jsblmc', 模板内容) # print(查询到的变量信息) for 变量带前后缀 in 查询到的变量信息: 替换后的变量名 = re.sub(替换前字符, 替换后字符, 变量带前后缀) 去除前后缀的变量名 = 替换后的变量名.replace('qsblmc', '').replace('jsblmc', '') if 去除前后缀的变量名 in 变量名列表: # print('变量 %s 正常' % 去除前后缀的变量名) 模板内容 = re.sub(变量带前后缀, 替换后的变量名, 模板内容) else: print('在对变量 %s 将 %s 替换为 %s 过程中,发现替换结果不在点表内,请核实' % (去除前后缀的变量名, 替换前字符, 替换后字符)) 模板内容 = re.sub(变量带前后缀, 替换后的变量名 + '超出点表',模板内容) return 模板内容 ''' def 从文档提取变量名称(文档, 前缀, 后缀): # 从文档中提取变量名,去除前缀后缀,并去重 变量列表 = re.findall(前缀 + '.*?' + 后缀, 文档) 新变量列表 = [] 新变量列表2 = [] # 去除前后缀 for 变量 in 变量列表: 新变量列表.append(变量.replace(前缀, '').replace(后缀, '')) for 变量 in 新变量列表: if 变量 not in 新变量列表2: 新变量列表2.append(变量) return 新变量列表2 def 替换后超出点表范围检查(全部点表, 文档变量, 替换规则): ''' def 获取模板文件路径(): '''获取模板文件路径,并写入 模板文件路径输入框''' global 模板文件路径 模板文件路径 = 选取文件() 模板文件路径输入框.delete(0, 'end') 模板文件路径输入框.insert(0, 模板文件路径) def 获取模板点表路径(): '''获取模板点表路径,并写入 模板文件路径输入框''' global 模板点表路径 模板点表路径 = 选取文件() 模板点表路径输入框.delete(0, 'end') 模板点表路径输入框.insert(0, 模板点表路径) def 获取实例点表路径(): '''获取实例点表路径,并写入 模板文件路径输入框''' global 实例点表路径 实例点表路径 = 选取文件() 实例点表路径输入框.delete(0, 'end') 实例点表路径输入框.insert(0, 实例点表路径) def 同上处理(): global 实例点表路径 global 模板点表路径 实例点表路径 = 模板点表路径 实例点表路径输入框.delete(0, 'end') 实例点表路径输入框.insert(0, 实例点表路径) def 测试生成中间模板(): global 模板点表 global 实例点表 global 输出结果 模板文件路径 = 模板文件路径输入框.get() 模板内容 = 读入模板(模板文件路径) 模板点表 = 读入点表(模板点表路径输入框.get()) 实例点表 = 读入点表(实例点表路径输入框.get()) 替换规则 = 提取替换规则(替换规则输入框.get()) 子程序起始序号 = int(子程序起始编号输入框.get()) 子程序名称前缀 = 子程序名称前缀输入框.get() 子程序名称后缀 = 子程序名称后缀输入框.get() 模板中间文件 = 地址换成变量(模板内容,模板点表) 输出文件内容 = '' for 替换后字符 in 替换规则[1]: 替换变量名的模板 = 变量名替换(模板中间文件,替换规则[0],替换后字符,实例点表) 替换变量为地址后的文件 = 变成换成地址(替换变量名的模板, 实例点表) 模板子程序名 = re.search('SUBROUTINE_BLOCK.+', 替换变量为地址后的文件).group() 模板子程序名 = re.search(' .+:', 模板子程序名).group().replace(' ', '').replace(':', '') POU内容 = re.sub('SUBROUTINE_BLOCK.+','SUBROUTINE_BLOCK ' + 子程序名称前缀 + 替换后字符 + 子程序名称后缀 + ':SBR' + str(子程序起始序号),替换变量为地址后的文件) 子程序起始序号 += 1 输出文件内容 += POU内容 输出结果 = 输出文件内容 原文件名 = re.sub(r'^.+\\','',模板文件路径) 原文件名 = re.sub(r'\..+','',原文件名) 输出文件夹路径 = re.search(r'^.+\.', 模板文件路径).group().replace('.','') + '_输出_' + datetime.datetime.now().strftime('%y_%m_%d_%H%M%S') os.mkdir(输出文件夹路径) 输出文件路径 = 输出文件夹路径 + chr(92) + '.' 输出文件路径 = re.sub(r'\.$',原文件名 + '_变量替换_' + datetime.datetime.now().strftime('%y_%m_%d_%H%M%S') + '.awl',输出文件路径) 输出文件 = open(输出文件路径,'w',encoding = "GB2312") 输出文件.write(输出文件内容) 输出文件.close() return 输出文件内容 模板文件路径 = '' 模板点表路径 = '' 实例点表路径 = '' 模板中间文件 = '' 模板点表 = [] 实例点表 = [] 输出结果 = [] if __name__ == '__main__': 主窗口 = tk.Tk() 主窗口.title('西门子PLC程序POU依据模板生成') # 定义窗口大小 主窗口.geometry('1024x800') # 设置窗口颜色 主窗口['background'] = '#CCCCCC' 文件选择区 = tk.Frame(主窗口) 文件选择区.pack() tk.Label(文件选择区, text = '模板文件 路径:').grid(row = 0, column = 0) 模板文件路径输入框 = tk.Entry(文件选择区, width = 100) 模板文件路径输入框.grid(row = 0,column = 1, padx = '5px') 模板文件路径输入框.delete(0, 'end') 模板文件路径输入框.insert(0, '请输入模板文件路径,或用右侧按钮选取') 模板文件路径选择按钮 = tk.Button(文件选择区,text = '...', width = 5, command = 获取模板文件路径) 模板文件路径选择按钮.grid(row = 0,column = 2, padx = '5px') tk.Label(文件选择区, text = '模板点表 路径:').grid(row = 1, column = 0) 模板点表路径输入框 = tk.Entry(文件选择区, width = 100) 模板点表路径输入框.grid(row = 1,column = 1, padx = '5px') 模板点表路径输入框.delete(0, 'end') 模板点表路径输入框.insert(0, '请输入模板点表路径,或用右侧按钮选取') 模板点表路径选择按钮 = tk.Button(文件选择区,text = '...', width = 5, command = 获取模板点表路径) 模板点表路径选择按钮.grid(row = 1,column = 2, padx = '5px') tk.Label(文件选择区, text = '实例点表 路径:').grid(row = 2, column = 0) 实例点表路径输入框 = tk.Entry(文件选择区, width = 100) 实例点表路径输入框.grid(row = 2,column = 1, padx = '5px') 实例点表路径输入框.delete(0, 'end') 实例点表路径输入框.insert(0, '请输入实例点表路径,或用右侧按钮选取') 实例点表路径选择按钮 = tk.Button(文件选择区,text = '...', width = 5, command = 获取实例点表路径) 实例点表路径选择按钮.grid(row = 2,column = 2, padx = '5px') 同上按钮 = tk.Button(文件选择区,text = '同上', width = 5, command = 同上处理) 同上按钮.grid(row = 2,column = 3, padx = '5px') tk.Label(文件选择区, text = '替换规则').grid(row = 3, column = 0) 替换规则输入框 = tk.Entry(文件选择区, width = 100) 替换规则输入框.grid(row = 3,column = 1, padx = '5px') 替换规则输入框.delete(0, 'end') 替换规则输入框.insert(0, '设备1>>>设备2,设备3,设备4...') # 调试完成后,该区删除 功能调试区 = tk.Frame(主窗口) 功能调试区.pack() tk.Label(功能调试区, text = '子程序起始编号:').grid(row = 0, column = 0) 子程序起始编号输入框 = tk.Entry(功能调试区, width = 5) 子程序起始编号输入框.grid(row = 0,column = 1, padx = '5px') 测试生成中间模板按钮 = tk.Button(功能调试区,text = '生成POU文件', width = 15, command = 测试生成中间模板) 测试生成中间模板按钮.grid(row = 3,column = 0, padx = '5px') tk.Label(功能调试区, text = '子程序名称前缀:').grid(row = 1, column = 0) 子程序名称前缀输入框 = tk.Entry(功能调试区, width = 10) 子程序名称前缀输入框.grid(row = 1,column = 1, padx = '5px') tk.Label(功能调试区, text = '子程序名称后缀:').grid(row = 1, column = 2) 子程序名称后缀输入框 = tk.Entry(功能调试区, width = 10) 子程序名称后缀输入框.grid(row = 1,column = 3, padx = '5px') 主窗口.mainloop()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了