调试经验——使用VBA显示进度条 (Display Progress Bar with VBA)

需求:有时候运行VBA进行数据处理的时候,运行时间较长(四五十分钟甚至更长),如果能显示一个进度条的话让人比较安心。

今天在网上看到了网友的解决方案:

'自定义的进度条,在状态栏显示
Function GetProgress(curValue, maxValue)
Dim i As Single, j As Integer, s As String
i = maxValue / 20
j = curValue / i
 
For m = 1 To j
    s = s & "■"
Next m
For n = 1 To 20 - j
    s = s & "□"
Next n
GetProgress = s & FormatNumber(curValue / maxValue * 100, 2) & "%"
End Function

然后,在主程序循环体内调用该函数:

rs.Open sql,  connXls, 1
    Dim p As Integer: p = 0
    Do While Not rs.EOF
        p = p + 1
        '在状态栏显示
        Application.StatusBar = GetProgress(p, rs.RecordCount)
    ……

最后,记得在程序使用以下语句恢复状态栏为系统默认状态,否则,进度条将一直显示在那里。

Application.Statusbar = False

补记:

看到了一种更简单的方式(仅显示数值,百分比),无需模拟进度条。个人感觉这种方式更轻量级一点,简单而实用!

Dim x               As Integer 
Dim MyTimer         As Double 

'Change this loop as needed.
For x = 1 To 50
    ' Do stuff
    Application.StatusBar = "Progress: " & x & " of 50: " & Format(x / 50, "0%")
Next x 

Application.StatusBar = False

参考文章:

https://www.cnblogs.com/gxlxzys/archive/2010/10/16/1852967.html

编程语言:Python 运行环境:Pycharm 解释器版本:Python 3.13 程序设计:基于Python的Tkinter技术编写一个分辨率"600x300"UI界面程序。 该程序可针对WPS的Excel文件进行VBA宏模块(*.bas)文件进行添加并执行指定宏函数等操作,具体功能设计如下: 第一:程序UI界面布局设计如下: 1.进行网格布局三行三列。 2.第一行第一列网格A为标题:“日志文件选择:”,字体:黑体12; 第一行第二列网格B为标题:“分析模块选择:”,字体:黑体12; 第一行第三列网格C为标题:“宏主程序读取:”,字体:黑体12。 3.第二行第一列网格D为文本选择框,可选择本地(*.xls)(*.xlsx)后缀的Excel文件; 第二行第二列网格E为文本选择框,可选择本地(*.bas)VBA宏模块文件; 第二行第三列网格F将网格E文本选择框中选择的(*.bas)VBA宏模块文件中解析宏函数并将格式"Main_*()"函数名称赋值到网格F下拉选项框中,同时注意函数名称为中英文混合,使其中文字符正常显示,防止中文字符显示乱码现象。 4.第三行第一列网格G为一个可点击按钮键,标题为"执行宏命令分析"; 第三行第二列与第三列合并居中为网格H,显示为一个"宏命令程序处理进程进度条"。 5.网格布局:每列网格左对齐,每行网格相同行间隔对齐;网格布局自适应不同分辨率; 第二:程序可执行功能设计如下: 1.点击执行网格G"执行宏命令分析"按钮键后:往网格D手动选择的WPS Excel文件添加网格E手动选择的VBA宏模块文件,解析宏函数并执行由网格F选择的该VBA宏模块文件中的宏函数。同时网格H显示<从点击网格G开始到执行完网格F选择的VBA宏模块文件中的宏函数为止>这一完整过程的进度条显示动画并显示百分比进度。 2.若网格D或网格E二者其中有一个未在文本选择框中选择文件,在点击执行网格G"执行宏命令分析"时则弹出错误提示框"请先选择需要分析的文件或宏模块"; 3.等待网格H进度条动画显示完毕100%即网格F选择的宏函数运行完后,另存生成一个Excel新文件同时弹出一个文本浏览框,可手动选择该Excel新文件保存的路径位置和文件名。该文件生成后自动在其文件名后缀加上当前生成时间戳。
07-23
在UI界面顶部生成菜单栏选项:一级目录<Excel文件处理>,点击该目录生成二级目录<宏模块加载分析>、二级目录<文件合并统计分析>。点击二级目录<宏模块加载分析>时加载出如下UI界面:import tkinter as tk from tkinter import ttk, filedialog, messagebox import os import win32com.client as win32 import time from datetime import datetime import threading import re class VBAExecutorApp: def __init__(self, root): self.root = root self.root.title("WPS Excel宏执行器") self.root.geometry("600x280") self.root.resizable(True, True) self.style = ttk.Style() # 配置按钮样式 self.style.configure("Run.TButton", foreground="white", background="#4CAF50", font=("Arial", 10, "bold"), padding=6) self.style.map("Run.TButton", foreground=[('disabled', 'gray')], background=[('disabled', '#CCCCCC')]) # 创建网格布局 self.root.columnconfigure((0, 1, 2), weight=1) self.root.rowconfigure((0, 1, 2), weight=1) # 第一行标题 self.create_label("日志文件选择:", 0, 0, "黑体", 12) self.create_label("分析模块选择:", 0, 1, "黑体", 12) self.create_label("宏主程序读取:", 0, 2, "黑体", 12) # 第二行文件选择 self.excel_path = tk.StringVar() self.bas_path = tk.StringVar() self.create_file_entry(1, 0, self.excel_path, [("Excel文件", "*.xls *.xlsx")]) self.create_file_entry(1, 1, self.bas_path, [("BAS文件", "*.bas")]) # 宏函数下拉框 self.macro_var = tk.StringVar() self.macro_combobox = ttk.Combobox( self.root, textvariable=self.macro_var, state="readonly", height=8 ) self.macro_combobox.grid(row=1, column=2, padx=5, pady=2, sticky="ew") # 第三行按钮 self.run_button = ttk.Button( self.root, text="执行宏命令分析", command=self.start_execution, width=23, style="Run.TButton" ) self.run_button.grid(row=2, column=0, padx=5, pady=(7, 8), sticky="ew") # 添加悬停效果绑定 self.run_button.bind("<Enter>", self.on_enter) self.run_button.bind("<Leave>", self.on_leave) self.run_button.bind("<ButtonPress-1>", self.on_press) self.run_button.bind("<ButtonRelease-1>", self.on_release) # 进度条容器 progress_container = ttk.Frame(self.root) progress_container.grid(row=2, column=1, columnspan=2, padx=5, pady=0, sticky="ew") progress_container.columnconfigure(0, weight=1) progress_container.columnconfigure(1, weight=0) # 进度条 self.progress_var = tk.DoubleVar() self.progress_bar = ttk.Progressbar( progress_container, variable=self.progress_var, maximum=100, mode="determinate", length=200 ) self.progress_bar.grid(row=0, column=0,ipadx=30,ipady=7,padx=(0, 0), sticky="ew") # 百分比标签 self.percent_label = ttk.Label( progress_container, text="0%", font=("Arial", 12), width=5 ) self.percent_label.grid(row=0, column=1, ipadx=2,ipady=7,padx=(10, 0),sticky="e") # 绑定事件 self.bas_path.trace_add("write", self.parse_bas_file) self.progress_var.trace_add("write", self.update_percent_label) def update_percent_label(self, *args): """更新百分比标签显示""" progress_value = self.progress_var.get() self.percent_label.config(text=f"{int(progress_value)}%") # 根据进度改变标签颜色 if progress_value < 30: self.percent_label.config(foreground="red") elif progress_value < 70: self.percent_label.config(foreground="orange") else: self.percent_label.config(foreground="green") # 关键修改:当进度达到100%时重新启用按钮 if progress_value == 100: self.run_button.config(state=tk.NORMAL) def create_label(self, text, row, column, font, size): label = tk.Label( self.root, text=text, font=(font, size), anchor="w" ) label.grid(row=row, column=column, padx=0, pady=(25, 0), sticky="w") return label def create_file_entry(self, row, column, text_var, filetypes): frame = ttk.Frame(self.root) frame.grid(row=row, column=column, padx=5, pady=2, sticky="ew") frame.columnconfigure(1, weight=1) entry = ttk.Entry(frame, textvariable=text_var) entry.grid(row=0, column=0, sticky="ew", padx=(0, 2)) browse = ttk.Button(frame, text="浏览", command=lambda: self.browse_file(text_var, filetypes)) browse.grid(row=0, column=1, padx=(0, 0)) def browse_file(self, text_var, filetypes): file_path = filedialog.askopenfilename(filetypes=filetypes) if file_path: text_var.set(file_path) def parse_bas_file(self, *args): bas_path = self.bas_path.get() if not bas_path or not os.path.exists(bas_path): return try: # 尝试使用UTF-8和GBK解码 try: with open(bas_path, "r", encoding="utf-8") as f: content = f.read() except UnicodeDecodeError: with open(bas_path, "r", encoding="gbk") as f: content = f.read() # 查找Main_开头的宏函数 macro_pattern = r"Sub\s+(Main_\w+)\s*\(\)" macros = re.findall(macro_pattern, content) if macros: self.macro_combobox["values"] = macros if self.macro_combobox["values"]: self.macro_combobox.current(0) except Exception as e: messagebox.showerror("错误", f"解析BAS文件失败: {str(e)}") def start_execution(self): """关键修改:点击后立即禁用按钮""" if not self.excel_path.get() or not self.bas_path.get(): messagebox.showerror("错误", "请先选择需要分析的文件或宏模块") return # 禁用按钮防止重复点击 self.run_button.config(state=tk.DISABLED) self.progress_var.set(0) # 重置进度条 # 在后台线程中执行宏 threading.Thread(target=self.execute_macro, daemon=True).start() def on_enter(self, event): self.style.configure("Run.TButton", background="#45a049") def on_leave(self, event): self.style.configure("Run.TButton", background="#4CAF50") def on_press(self, event): self.style.configure("Run.TButton", background="#2E7D32") def on_release(self, event): self.style.configure("Run.TButton", background="#45a049") def execute_macro(self): try: # 初始化WPS Excel excel = win32.gencache.EnsureDispatch("Excel.Application") excel.Visible = False excel.DisplayAlerts = False # 打开Excel文件 wb = excel.Workbooks.Open(os.path.abspath(self.excel_path.get())) self.update_progress(20) # 导入BAS文件 vb_project = wb.VBProject vb_project.VBComponents.Import(os.path.abspath(self.bas_path.get())) self.update_progress(40) # 执行宏 macro_name = self.macro_var.get() excel.Application.Run(macro_name) self.update_progress(70) time.sleep(1) # 模拟执行时间 # 另存为新文件 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") save_path = filedialog.asksaveasfilename( defaultextension=".xlsx", filetypes=[("Excel文件", "*.xlsx"), ("Excel 97-2003", "*.xls")], initialfile=f"{self.macro_combobox["values"]}{timestamp}" ) if save_path: wb.SaveAs(save_path) self.update_progress(90) messagebox.showinfo("成功", f"文件已保存至: {save_path}") wb.Close(False) excel.Quit() self.update_progress(100) # 关键点:设置进度为100% except Exception as e: messagebox.showerror("执行错误", f"宏执行失败: {str(e)}") self.update_progress(100) # 出错时也设置进度为100% finally: # 确保进度达到100%(update_progress(100)会启用按钮) if self.progress_var.get() < 100: self.update_progress(100) def update_progress(self, value): """更新进度条值(线程安全)""" # 使用after确保在主线程更新UI self.root.after(0, lambda: self.progress_var.set(value)) self.root.update_idletasks() if __name__ == "__main__": root = tk.Tk() app = VBAExecutorApp(root) root.mainloop()
07-24
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值