Skip to content

Commit 8691ec3

Browse files
committed
add readme in Chinese
1 parent bed3ce4 commit 8691ec3

File tree

2 files changed

+256
-39
lines changed

2 files changed

+256
-39
lines changed

README_CN.md

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
中文 | [English](./README_CN.md)
2+
3+
# PyAddin
4+
5+
[![pypi-version](https://img.shields.io/pypi/v/pyaddin.svg)](https://pypi.python.org/pypi/pyaddin/)
6+
![license](https://img.shields.io/pypi/l/pyaddin.svg)
7+
8+
VBA日渐式微,但 Excel 依旧是表格数据存储、处理和传阅的通用工具。对于一些复杂的业务逻辑,一个带宏的 Excel 文件(xlsm)通常包含数据本身和处理数据的VBA脚本。当需要频繁复用这些脚本时,例如按相同的业务逻辑处理月度数据,建议将其拆分为两部分: Excel 纯数据文件(xlsx),以及一个负责数据处理的 Excel 插件(xlam)。
9+
10+
`PyAddin`是一个辅助创建上述 Excel 插件的命令行工具,同时支持使用 Python 语言来实现原本 VBA 负责的数据处理流程。两个基本功能如下:
11+
12+
- 创建一个模板插件,支持自定义菜单功能区(Ribbon)和近“无缝”调用 Python 脚本。
13+
- 在开发插件过程中,方便根据自定义的`CustomUI.xml`更新插件菜单功能区。
14+
15+
集成 VBA 和 Python 的主要思路:VBA 通过后台运行控制台程序调用 Python 脚本,Python 脚本执行计算并以写临时文件的方式保存返回值,最后 VBA 读取返回值。此外,借助 Python 第三方库`pywin32`,可以直接在 Python 脚本中进行与 Excel 的交互,例如获取/设置单元格内容,设置单元格样式等等。
16+
17+
18+
## 限制
19+
20+
- 仅支持 Windows 平台
21+
- 要求 Microsoft Excel 2007 及以上
22+
- VBA 和 Python 之间传参仅限于 **字符串** 格式简单数据类型
23+
- 与 Excel 的交互能力取决于 `pywin32/win32com`
24+
25+
26+
## 安装
27+
28+
支持 `Pypi` 或者本地安装:
29+
30+
```
31+
# pypi
32+
pip install pyaddin
33+
34+
# local
35+
python setup.py install
36+
37+
# local in development mode
38+
python setup.py develop
39+
```
40+
41+
使用 `pip` 卸载:
42+
43+
```
44+
pip uninstall pyaddin
45+
```
46+
47+
## 命令说明
48+
49+
- 创建模板插件
50+
51+
```
52+
pyaddin init --name=xxx --quiet=True|False
53+
```
54+
55+
- 更新插件功能区
56+
57+
```
58+
pyaddin update --name=xxx --quiet=True|False
59+
```
60+
61+
其中,`quiet`是可选参数,表明是否以后台模式创建插件(不显式打开 Excel)。默认值`True`,即后台模式。
62+
63+
## 使用帮助
64+
65+
### 1. 初始化模板插件
66+
67+
```
68+
D:\WorkSpace>pyaddin init --name=sample
69+
```
70+
71+
在当前目录下新建了文件夹`sample`,其中包含模板插件`sample.xlam`,以及实现VBA 与 Python 互联所需的支持文件。目录结构如下:
72+
73+
```
74+
sample\
75+
|- scripts\
76+
| |- utils\
77+
| | |- __init__.py
78+
| | |- context.py
79+
| |- __init__.py
80+
| |- sample.py
81+
|- main.cfg
82+
|- main.py
83+
|- CustomUI.xml
84+
|- sample.xlam
85+
```
86+
87+
其中,
88+
89+
- `main.py`是 VBA 调用 Python 脚本的入口文件。
90+
- `main.cfg`是基本配置参数文件,例如指定 Python 解释器的路径。
91+
- `scripts`存放处理具体业务的 Python 脚本,例如`sample.py`是其中的一个示意模块,开发者根据需要在此目录下创建其他模块。
92+
- `CustomUI.xml`定义了插件的 Ribbon 界面,例如包含的控件及样式。
93+
- `sample.xlam`为模板插件,开发者可以在此基础上添加和扩展自定义的功能。
94+
95+
当前模板的 Ribbon 区域参考下图。
96+
97+
![add-in.png](add-in.png)
98+
99+
100+
### 2. 自定义 Ribbon 区域
101+
102+
在上一步创建的 [`CustomUI.xml`](./pyaddin/resources/CustomUI.xml) 的基础上,根据具体需求设计界面和样式,然后运行以下命令将其更新到插件中去。当然, Excel 2007及以上的文件本质上是一个压缩文件,因此可以手动解压后替换相应的`CustomUI.xml`,或者直接借助其他的 Ribbon XML 编辑器进行修改。
103+
104+
105+
```
106+
D:\WorkSpace\sample>pyaddin update --name=sample
107+
```
108+
109+
110+
以此模板为例,定义了两个分组:
111+
112+
- `setting`组提供基础功能,请直接保留在你的项目中。例如,运行前需要设置 Python 解释器路径。
113+
- `Your Group 1`是一个示例分组,其中设计了两个按钮。实际开发中请替换为需要的控件,或者增加其他更多分组。
114+
115+
关于 Ribbon 界面的具体介绍及格式规范,参考以下链接。
116+
117+
- [General Format of XML Markup Files](https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2007/aa338202(v%3doffice.12)#general-format-of-xml-markup-files)
118+
- [Custom UI](https://docs.microsoft.com/en-us/openspecs/office_standards/ms-customui/edc80b05-9169-4ff7-95ee-03af067f35b1)
119+
120+
121+
### 3. 实现 Ribbon 控件响应函数
122+
123+
这一步需要在 VBA 中实现 `CustomUI.xml`定义的控件事件及其响应函数。
124+
125+
以模板插件为例,下面的 XML 片段表明按钮`Sample 1`的点击事件由 VBA 过程`CB_Sample_1`响应。
126+
127+
```xml
128+
<button id="sample1" label="Sample 1"
129+
imageMso="AppointmentColor3" size="large"
130+
onAction="CB_Sample_1"
131+
...>
132+
```
133+
134+
进一步,查看插件的`UserRibbon`模块中定义的两个响应过程:
135+
136+
137+
```vb
138+
Sub CB_Sample_1(control As IRibbonControl)
139+
'''onAction for control: Sample 1'''
140+
Dim res As Object
141+
Dim x As Integer: x = Range("A1").Value
142+
Dim y As Integer: y = Range("A2").Value
143+
144+
Set res = RunPython("scripts.sample.run_example_1", x, y)
145+
Range("A3") = res("value")
146+
End Sub
147+
148+
Sub CB_Sample_2(control As IRibbonControl)
149+
'''onAction for control: Sample 2'''
150+
RunPython "scripts.sample.run_example_2"
151+
End Sub
152+
```
153+
154+
可以看到,二者都是通过`RunPython()`函数来调用 Python 脚本并获取返回值,函数签名如下:
155+
156+
```vb
157+
Function RunPython(methodName As String, ParamArray args()) As Object
158+
```
159+
160+
- 第一个参数`methodName`表示调用的 Python 方法,具体格式为`package.module.method`。本例 "scripts.sample.run_example_1" 表示调用脚本`sample/scripts/sample.py`中的`run_example_1`方法。
161+
162+
- 第二个参数`args`是可变长参数,可以传入任意个数的参数给相应 Python 方法。注意,仅支持字符串、数字等简单数据类型,并且经过控制台参数传递,最终到 Python 端都被转成了字符串格式。
163+
164+
- 返回值为 VBA 的`Dictionary`格式,其中包含两个键:
165+
- `status`: 调用成功与否,True 或者 False
166+
- `value`: 返回值(错误信息如果`status`为 False)
167+
168+
169+
### 4. 实现 Python 脚本
170+
171+
根据 VBA 端`RunPython()`调用路径和参数列表,创建相应的 Python 脚本文件及函数。
172+
173+
以模板插件为例,在项目目录下查看`scripts\sample.py`文件:
174+
175+
```python
176+
# sample.py
177+
from .utils import context
178+
179+
def run_example_1(x:str, y:str):
180+
return int(x) + int(y)
181+
182+
def run_example_2():
183+
# get the workbook calling this method, then do anything with win32com
184+
wb = context.get_caller()
185+
sheet = wb.ActiveSheet
186+
187+
# get cells value
188+
x = sheet.Range('A1').Value
189+
y = sheet.Range('A2').Value
190+
191+
# set cell value
192+
sheet.Range('A3').Value = x + y
193+
```
194+
195+
根据上下文可知,上述两个方法实现相同的事情,将单元格 A1、A2 的和写到单元格 A3,进一步可以分为三步:取值,求和,设置值。但实现的思路略有不同:
196+
197+
- `run_example_1`的思路是仅将 Excel 无关的流程(求和)交由 Python 实现,所有 Excel 相关的操作(取值、设置值)仍通过 VBA 执行,二者的桥梁是参数传递。
198+
199+
- `run_example_2`的思路是在 Python 端获取到调用此脚本的工作簿,然后直接对其做所有需要的操作(取值,求和,设置值)。
200+
201+
对比可以发现,前者按各自强项分工,运行效率较高,但 VBA 和 Python 之间存在强耦合,且容易受限于参数传递的复杂度;后者几乎完全解耦 VBA 和 Python,且避免了参数传递,但操作 Excel 的能力受限于`pywin32`,某些操作可能无法实现。
202+
203+
204+
### 5. 交付插件
205+
206+
因为依赖关系,整个工程应作为整体(第一步中所示结构)交付,且需要最终用户配置好 Python 环境及相应第三方库(主要是`pywin32`)。有时候 Python 环境对用户来说是一个挑战,因此可以考虑可移植的便携式 Python,事先安装好依赖库后随插件工程一起发布,如此便于最终用户开箱即用。
207+
208+
209+
## 许可
210+
211+
MIT License

readme.md

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1+
[中文](./README_CN.md) | English
2+
13
# PyAddin
24

3-
VBA is not as popular as it used to be, while Excel is still widely used for holding, processing and reviewing data. Generally, an Excel file with macro (xlsm) is filled with data and embedded with complicated VBA scripts. The idea of this project is to split such Excel file into two separate parts: worksheets without macro (xlsx) as data container, and an Excel add-in (xlam) as computing engine.
5+
[![pypi-version](https://img.shields.io/pypi/v/pyaddin.svg)](https://pypi.python.org/pypi/pyaddin/)
6+
![license](https://img.shields.io/pypi/l/pyaddin.svg)
7+
8+
VBA is not as popular as it used to be, while Excel is still widely used for holding, processing and reviewing data. Generally, an Excel file with macro (xlsm) is filled with data and embedded with VBA scripts for data processing. When the data processing scripts need to be reused, e.g., applying to monthly data, it's more reasonable to split such Excel file into two separate parts: worksheets without macro (xlsx) as data container, and an Excel add-in (xlam) as computing engine.
49

510
`PyAddin` is a command-line tool facilitating the creation of Excel add-in (*.xlam), with ability to process data with Python.
611

712
- provides an Excel add-in template with customized Ribbon menu and a combination of VBA and Python.
813
- helps to update the Ribbon UI during the development of your add-in.
914

10-
The main idea is VBA macro calling Python script by console arguments, then retrieving return value stored in a temporary file as a bridge. In addition, leverage the ability of `pywin32`, all the interaction with Excel, e.g., reading/writing cell data, setting cell format, could be completed in Python script.
15+
The main idea is VBA macro calling Python script by console arguments, then retrieving return value stored in a temporary file as a bridge. In addition, leveraging the ability of `pywin32`, all the interaction with Excel, e.g., reading/writing data, setting format, could be completed in Python script.
1116

1217

1318
## Limitation
@@ -41,28 +46,20 @@ pip uninstall pyaddin
4146

4247
## Usage
4348

44-
```
45-
> pyaddin -h
46-
47-
NAME
48-
pyaddin - Command line interface for ``PyAddin``.
49-
50-
SYNOPSIS
51-
pyaddin COMMAND | -
52-
53-
DESCRIPTION
54-
Command line interface for ``PyAddin``.
49+
- Create template project with specified name
5550

56-
COMMANDS
57-
COMMAND is one of the following:
51+
```
52+
pyaddin init --name=xxx --quiet=True|False
53+
```
5854

59-
init
60-
Create template project with specified name under current path.
55+
- Update add-in based on ribbon UI file
6156

62-
update
63-
Update add-in file (name.xlam) based on ribbon UI file (CustomUI.xml) under working path.
57+
```
58+
pyaddin update --name=xxx --quiet=True|False
6459
```
6560

61+
`quiet` is an optional parameter. Run the process in background mode if `True`, i.e., do not launch Excel application explicitly. Default to `True`.
62+
6663
## Tutorial
6764

6865
### 1. Initialize template project
@@ -91,32 +88,33 @@ sample\
9188
- `main.cfg` is the configuration file with parameters like Python interpreter path.
9289
- `scripts` is the default package for user defined Python scripts, e.g., `sample.py` is the first example.
9390
- `CustomUI.xml` defines the Ribbon UI of this template add-in.
94-
- `sample.xlam` is a template add-in, see screenshot below.
91+
- `sample.xlam` is a template add-in, which is the base line to extend your new features.
9592

93+
See screenshot of this template add-in below.
9694

9795
![add-in.png](add-in.png)
9896

9997

10098

10199
### 2. Customize Ribbon Tab
102100

103-
Check [`CustomUI.xml`](./pyaddin/resources/CustomUI.xml) created in Step 1, then define UI structures as needed. In this template case, two groups are defined:
101+
Check [`CustomUI.xml`](./pyaddin/resources/CustomUI.xml) created in Step 1, then define your UI structures as needed. After that, run command below to apply to your add-in. Alternatively, you can do it by unzipping and updating the add-in manually, or with other Custom UI editor.
102+
103+
```
104+
D:\WorkSpace\sample>pyaddin update --name=sample
105+
```
106+
107+
In this template case, two groups are defined:
104108

105109
- `setting` group provides supporting features. It should be reused in your add-in.
106110
- `Your Group 1` is an example for quick start, here two buttons are defined. Feel free to delete or modify it for your own purpose.
107111

108-
For detailed information on Microsoft Ribbon UI, please refer to links below .
112+
For detailed information on Microsoft Ribbon UI, please refer to links below.
109113

110114
- [General Format of XML Markup Files](https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2007/aa338202(v%3doffice.12)#general-format-of-xml-markup-files)
111115
- [Custom UI](https://docs.microsoft.com/en-us/openspecs/office_standards/ms-customui/edc80b05-9169-4ff7-95ee-03af067f35b1)
112116

113117

114-
After finishing your feature design in `CustomUI.xml`, run command below to apply to the add-in. Alternatively, you can do it by unzipping and updating the add-in file manually, or with other Custom UI editor.
115-
116-
```
117-
D:\WorkSpace\sample>pyaddin update --name=sample
118-
```
119-
120118

121119
### 3. Implement Ribbon Control Actions
122120

@@ -129,16 +127,7 @@ Actions defined in `CustomUI.xml` should be implemented in VBA module. For examp
129127
...>
130128
```
131129

132-
Check the following sample codes from VBA module `UserRibbon` of `sample.xlam`. `RunPython` is a pre-defined VBA function to call Python scripts, and get return value.
133-
134-
- The first argument is the path to python method, `package.module.method`. For example, "scripts.sample.run_example_1" refers to `run_example_1` from python script `sample/scripts/sample.py`.
135-
136-
- The second, third, ..., arguments refer to any count of parameters passing to python. Note that they **MUST** be simple type like string and number, and are converted to string type when passing to python with command line.
137-
138-
- The return value is `Dictionary` type with two keys:
139-
- `status`: True or False, indicates the status of calling python script.
140-
- `value`: the return value, or error message depends on `status`.
141-
130+
Check the following sample codes from VBA module `UserRibbon` of `sample.xlam`.
142131

143132
```vb
144133
Sub CB_Sample_1(control As IRibbonControl)
@@ -158,6 +147,23 @@ End Sub
158147
```
159148

160149

150+
`RunPython()` is a pre-defined VBA function to call Python scripts, and get return value. The function signature:
151+
152+
```vb
153+
Function RunPython(methodName As String, ParamArray args()) As Object
154+
```
155+
156+
- The first argument is the path to python method, `package.module.method`. For example, "scripts.sample.run_example_1" refers to `run_example_1` from python script `sample/scripts/sample.py`.
157+
158+
- The second argument accepts any count of parameters passing to python. Note that they **MUST** be simple type like string and number, and are converted to string type when passing to python with command line.
159+
160+
- The return value is `Dictionary` type with two keys:
161+
- `status`: True or False, indicates the status of calling python script.
162+
- `value`: the return value, or error message depends on `status`.
163+
164+
165+
166+
161167
### 4. Implement Python Script
162168

163169
Based on the calling from `RunPython`, create module file and implement associated methods.
@@ -182,7 +188,7 @@ def run_example_2():
182188
sheet.Range('A3').Value = x + y
183189
```
184190

185-
The two sample methods above did same thing: add values in cell `A1` and `A2`, then fill to cell `A3`, but in two different direction.
191+
The two sample methods above did same thing: add values in cell `A1` and `A2`, then fill to cell `A3`, but in two different directions.
186192

187193
- `run_example_1` performs the pure computation with parameters received from VBA, then passes back to VBA for further processing, e.g., fill in `A3`. It applies for simle case with small amount of data.
188194

0 commit comments

Comments
 (0)