Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
ericremoreynolds committed Aug 19, 2014
2 parents 495639c + 88e3dd3 commit 5acf2d2
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 50 deletions.
50 changes: 20 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,32 @@
# ExcelPython v2

### Get started
Write Excel user-defined functions in Python!

* Download the [latest release](https://github.com/ericremoreynolds/excelpython/releases)
* Unzip it into the folder containing your workbook (this will create the `xlpython` folder)
* Import the module `xlpython\xlpython.bas` into your workbook

and you're set! No registration or typelibs. All that is needed is Python with PyWin32 installed.

```vb.net
Sub test1()
MsgBox Py.Str(Py.Eval("1+2"))
End Sub

Sub test2()
Set vars = Py.Dict()
Py.Exec "from datetime import date", vars
MsgBox Py.Str(Py.Eval("date.today()", vars))
End Sub

Sub test3()
' Assumes there's a file called MyScript.py in the same folder as the workbook
' with a function called MyFunction taking 3 arguments
Set m = Py.Module("MyScript")
MsgBox Py.Str(Py.Call(m, "MyFunction", Py.Tuple(1, 2, 3)))
End Sub
```python
from xlpython import *

@xlfunc
@xlarg("x", "nparray", 2)
@xlarg("y", "nparray", 2)
def matrixmult(x, y):
return x.dot(y)
```

Note that the latest release zip also contains an Excel add-in `xlpython.xlam`. We're still in the process of developing that, not to mention writing the documentation - but feel welcome to take a look, and see what it does! Sneak preview: automatic generation of VBA wrappers for Python functions.
![image](https://cloud.githubusercontent.com/assets/5197585/3907706/6c3a2cea-22fd-11e4-812f-41c814d1cc54.png)

### About ExcelPython

ExcelPython is a lightweight, easily distributable library for interfacing Excel and Python. It enables easy access to Python scripts from Excel VBA, allowing you to substitute VBA with Python for complex automation tasks which would be facilitated by Python's extensive standard library.
ExcelPython is a lightweight, easily distributable library for interfacing Excel and Python. It enables easy access to Python scripts from Excel VBA, allowing you to substitute VBA with Python for complex automation tasks which would be facilitated by Python's extensive standard library while sparing you the complexities of Python COM programming.

### Getting started

* Download the [latest release](https://github.com/ericremoreynolds/excelpython/releases)
* Check out the [tutorials](docs/) to help you get going

v2 is a major rewrite of the previous ExcelPython, moving over to a new approach which is more robust and configurable - and importantly should eliminate all those irritating DLL not found problems! The technical details: Python now runs out-of-process and communication happens over COM.
The only prerequisites are Excel and Python with PyWin32 installed.

### Help me!

Check out the [docs](docs/) folder for tutorials to help you get started and links to other resources.
Check out the [docs](docs/) folder for tutorials to help you get started and links to other resources. Failing that, try the [issues section](https://github.com/ericremoreynolds/excelpython/issues?q=) or the [discussion forum on SourceForge](https://sourceforge.net/p/excelpython/discussion/general/).

If you don't find your answer, need more help, find a bug, think of a useful new feature, or just want to give some feedback by letting me and everyone else know what you're doing with ExcelPython, please create an issue ticket!
If you still don't find your answer, need more help, find a bug, think of a useful new feature, or just want to give some feedback by letting us know what you're doing with ExcelPython, please go ahead and create an [issue ticket](https://github.com/ericremoreynolds/excelpython/issues/new)!
13 changes: 10 additions & 3 deletions docs/Readme.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
Learn how to manipulate Python objects in VBA:
**Getting started with the ExcelPython add-in**

1. [Loading the add-in and writing your first user-defined function](tutorials/Addin01.md)
1. [Dealing with array arguments](tutorials/Addin02.md)
1. [Something a bit more interesting - NumPy arrays](tutorials/Addin03.md)
1. [Permanently installing the add-in](tutorials/Addin04.md)

**Learn how to manipulate Python objects in VBA**

1. [A very simple usage example](tutorials/Usage01.md)
2. [A more practical use of ExcelPython](tutorials/Usage02.md)
3. [Putting it all together](tutorials/Usage03.md)
4. [Ranges, lists and SAFEARRAYs](tutorials/Usage04.md)

Delve deeper into how to target a particular Python installation and working environment:
**Delve deeper into how to target a particular Python installation and working environment**

* [Configuration](tutorials/Configuration01.md)

Learn how to analyse the problem when something goes wrong:
**Learn how to analyse the problem when something goes wrong**

* [Troubleshooting](tutorials/Troubleshooting01.md)
49 changes: 49 additions & 0 deletions docs/tutorials/Addin01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## Loading the ExcelPython add-in

* Download the latest [release](https://github.com/ericremoreynolds/excelpython/releases) and unzip it somewhere.

* Open the add-in `xlpython.xlam` in Excel.

* If all goes well you should see the ExcelPython tab in Excel's toolbar.

![image](https://cloud.githubusercontent.com/assets/5197585/3917034/3623f40a-2385-11e4-9754-5e3b924e38a9.png)

* You may get an error saying `Programmatic access to Visual Basic Project is not trusted`. If so check out the [add-in troubleshooting guide](./AddinTrouble.md).

Note that it is possible to [permanently install the add-in](./Addin04.md) so you don't need to open it manually each time.

## Writing a user-defined function in Python

To interact with Python, a workbook must first be setup to use ExcelPython. To do this it is first necessary to save it as a macro-enabled workbook.

* Choose an empty folder and in it save an empty workbook as `Book1.xlsm`.

* From the ExcelPython tab in the toolbar click 'Setup ExcelPython'.

Next write your user-defined function in Python. In the previous step ExcelPython will have created a file called `Book1.py` in the same folder as `Book1.xlsm` in which the Python functions to be used in the workbook can be defined.

* Edit `Book1.py` to contain the following code:

```python
# Book1.py
from xlpython import *

@xlfunc
def DoubleSum(x, y):
'''Returns twice the sum of the two arguments'''
return 2 * (x + y)
```

* Switch back to Excel and click 'Import Python UDFs' in the ExcelPython tab to pick up the changes made to `Book1.py`.

* Enter the formula `=DoubleSum(1, 2)` into a cell and you should get the correct result:

![image](https://cloud.githubusercontent.com/assets/5197585/3917596/e5365b3c-238e-11e4-8bce-0d97caceca2e.png)

* Note that the `DoubleSum` function is usable from VBA as well. Open the VBA window (`Alt+F11`), switch to the Immediate Window (`Ctrl+G`) and type

```
?DoubleSum(1, 2)
```

To continue move onto the [next tutorial](./Addin02.md).
69 changes: 69 additions & 0 deletions docs/tutorials/Addin02.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
## Array arguments

You can pass a range as a function argument, as opposed to a single cell. Its value will be converted to a tuple of tuples.

* Add the following code to `Book1.py` from the [previous tutorial](./Addin01.md)

```python
@xlfunc
def MyUDF(x):
return repr(x)
```

This function simply returns its argument converted to string representation. This will allow us to explore how formula arguments are converted into Python objects.

* Click 'Import Python UDFs' to pick up the changes

* Modify the workbook as below

![image](https://cloud.githubusercontent.com/assets/5197585/3918899/302a5790-23a0-11e4-80fe-7c75b63c4225.png)

As you can see the value of the 2x2 range `F1:G2` has been convert to a tuple containing tuples representing the range's two rows.

At this point it is worth talking about one of Excel's oddities, namely that the value of a 1x1 range is always a scalar, whereas the value of any range larger than 1x1 is represented by a two-dimensional array.

![image](https://cloud.githubusercontent.com/assets/5197585/3918954/f2a67fce-23a0-11e4-810d-52870204e77f.png)

![image](https://cloud.githubusercontent.com/assets/5197585/3918991/7e2187ec-23a1-11e4-8fd8-6405c3bbc7b7.png)

ExcelPython provides a mechanism for normalizing the input arguments so that your function can safely make assumptions about their dimensionality.

* Modify `Book1.py` as follows

```python
@xlfunc
@xlarg("x", dims=2) # add this line
def MyUDF(x):
return str(x)
```

* Click 'Import Python UDFs' to pick up the changes.

* Now 1x1 ranges are passed as two-dimensional

![image](https://cloud.githubusercontent.com/assets/5197585/3919574/8c257714-23aa-11e4-82d5-97da8b5a5fb2.png)

At other times it you may want to assume that an argument that is one-dimensional

* Modify `Book1.py` as follows

```python
@xlfunc
@xlarg("x", dims=1) # modify this line
def MyUDF(x):
return str(x)
```

* Click 'Import Python UDFs' to pick up the changes.

![image](https://cloud.githubusercontent.com/assets/5197585/3919614/54cedaa2-23ab-11e4-8ba9-56dcd86815ad.png)

![image](https://cloud.githubusercontent.com/assets/5197585/3919622/6b5ed790-23ab-11e4-9dce-52b45bb72717.png)

![image](https://cloud.githubusercontent.com/assets/5197585/3919656/00f96f9a-23ac-11e4-8d7c-c1ae1896002e.png)

* Clearly having specified the argument as one-dimensional, an error is raised if a two-dimensional range is passed

![image](https://cloud.githubusercontent.com/assets/5197585/3919669/379cdd66-23ac-11e4-8e47-dfe333143a45.png)

To continue move onto the [next tutorial](./Addin03.md).
29 changes: 29 additions & 0 deletions docs/tutorials/Addin03.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## Something a bit more interesting - NumPy arrays

One of the attractions of using Python from Excel is to gain access to the vast range of publicly available Python libraries for numerical computing. Since [NumPy](http://www.numpy.org/) is the cornerstone of many of these libraries, the ExcelPython add-in makes it easy to pass function arguments as and convert return values from [numpy arrays](http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html).

We will now define a simple function for doing matrix multiplication using NumPy.

* Add the following code to `Book1.py` from the [first tutorial](./Addin01.md)

```python
@xlfunc
@xlarg("x", "nparray", dims=2)
@xlarg("y", "nparray", dims=2)
def MatrixMult(x, y):
return x.dot(y)
```

* Click 'Import Python UDFs' to pick up the changes

* The function `MatrixMult` can now be used as an array function from Excel

![image](https://cloud.githubusercontent.com/assets/5197585/3918420/9ab74be2-2399-11e4-9b55-8a8005afeabc.png)

To enter the above array formula in Excel
* fill in the values in the ranges `D1:E2` and `G1:H2`
* select cells `A1:B2`
* type in the formula `=MatrixMult(D1:E2, G1:H2)`
* press `Ctrl+Shift+Enter`.

To continue move onto the [next tutorial](./Addin04.md).
29 changes: 29 additions & 0 deletions docs/tutorials/Addin04.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## Permanently installing the ExcelPython add-in

The best place to put the ExcelPython add-in is in the Excel startup folder. All files placed in this folder are automatically opened by Excel on startup, so if you place ExcelPython there you do not need to manually open it each time you want to use it. The `xlpython.xlam` file must be placed in the `XLSTART` folder and the `xlpython` folder must be copied alongside it like so:

![image](https://cloud.githubusercontent.com/assets/5197585/3917303/0ef6b35e-238a-11e4-9017-ab8cdb74719d.png)

Unfortunately the folder's location varies depending on the version of Excel. Some candidates are

Excel 2013 current user: %APPDATA%\Roaming\Microsoft\Excel\XLSTART
Excel 2010 current user: %APPDATA%\Microsoft\Excel\XLSTART
Excel 2007 current user: %APPDATA%\Microsoft\Excel\XLSTART
Excel 2003 current user: %APPDATA%\Microsoft\Excel\XLSTART

Excel 2013 all users: %PROGRAMFILES%\Microsoft Office 15\root\Office15\XLSTART
Excel 2010 all users: %PROGRAMFILES%\Microsoft Office\Office14\XLSTART
Excel 2007 all users: %PROGRAMFILES%\Microsoft Office\Office12\XLSTART
Excel 2003 all users: %PROGRAMFILES%\Microsoft Office\Office11\XLSTART

Note also that `%PROGRAMFILES%` may need to be substituted with `%PROGRAMFILES(x86)%` for 32-bit Excel installed on a 64-bit machine.

If you are in doubt as to where the folder is located, you can also determine it by opening Excel, opening the VBA window (`Alt+F11`), switching to the Immediate Window (`Ctrl+G`) and typing

?Application.StartupPath

for the current user and

?Application.Path + "\XLSTART"

for all users.
7 changes: 7 additions & 0 deletions docs/tutorials/AddinTrouble.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Programmatic access to Visual Basic Project is not trusted

This appears because your Excel trust settings do not allow an add-in to manipulate another workbook's VBA code, which the ExcelPython add-in needs to be able to do to perform its tasks.

To change this trust setting, select File > Options > Trust Center > Trust Center Settings > Macro Settings and ensure the checkbox labeled 'Trust access to the VBA project object model' is checked.

![image](https://cloud.githubusercontent.com/assets/5197585/3921677/77648a04-23c3-11e4-9af4-2a14ca47787e.png)
24 changes: 7 additions & 17 deletions docs/tutorials/Configuration01.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
### Note: this documentation is out of date!
**NB: work in progress**

# Configuring ExcelPython

On of the most important features of ExcelPython is the ability to specify exactly which Python installation you wish to distribute, and to define an isolated Python execution environment with which your workbook interacts.
On of the most important features of ExcelPython is the ability to specify exactly which Python installation you wish to use, and to define an isolated Python execution environment with which your workbook interacts.

In tutorials, the default configuration is used, which runs the PC's default Python installation (i.e. the one that appears by running `python.exe` in the Start > Run box).
In many cases there is no need to make any changes to the default configuration, which runs the PC's default Python installation, i.e. the one that appears by entering `python.exe` in the Start > Run box. In other cases however, you may be interested in targeting a specific copy of Python installed on your PC, or executing Python with specific environment variables.

However when distributing a workbook which has been developed with xlpython, it is better to include a custom `.xlpy` configuration file which ensures that all the workbook's code will execute in a seperate Python process, thus not interfering with other xlpython workbooks which might have been opened by the user in Excel at the same time.
Let's assume we're developing an Excel workbook called `MatrixAlgebra.xlsm` in the folder `%SOMEFOLDER%\MatrixAlgebra`, and that the Python code is in a file called `MatrixAlgebra.py` in the same folder. Let's suppose we want to distribute this workbook in a zip file, and we want to include a copy of a portable Python distribution in the folder `%SOMEFOLDER%\MatrixAlgebra\PortablePython` which we want to use to execute our Python code within the context of the workbook. The ExcelPython runtime has already been copied to `%SOMEFOLDER%\MatrixAlgebra\xlpython`.

Let's assume we're developing an Excel workbook called `MatrixAlgebra.xlsm` in the folder `...\Desktop\MatrixAlgebra`, and that the Python code is in a file called `MatrixAlgebra.py` in the same folder. Let's suppose we want to distribute this workbook in a zip file, and we want to include a copy of a portable Python distribution in the folder `...\Desktop\MatrixAlgebra\PortablePython` which we want to use to execute our Python code within the context of the workbook.
Inside this last folder there is a file called `xlpython.cfg` which determines how the Python process is launched when some functionality within the workbook (e.g. a VBA function or a worksheet formula) tries to interact with Python.

To create a config file for this setup, copy `xlpython.xlpy` into `...\Desktop\MatrixAlgebra` and rename it `MatrixAlgebra.xlpy`, then edit it as follows:
To specify that the Python distribution in `%SOMEFOLDER%\MatrixAlgebra\PortablePython` must be used, make the following modification to `xlpython.cfg`

CLSID = {66496c37-eb73-4b42-baf6-fad4296e464c}

Command = $(ConfigDir)\PortablePython\python.exe $(DllDir)\xlpython.py $(CLSID)

WorkingDir = $(ConfigDir)

Then, in VBA, edit the xlpython module to use this new config file:

Function Py()
If 0 <> GetPythonInterface(Py, ThisWorbook.Path + "\MatrixAlgebra.xlpy") Then Err.Raise 1000, Description:=Py
End Function
Command = $(ConfigDir)\..\PortablePython\pythonw.exe -u "$(ConfigDir)\xlpyserver.py" $(CLSID)

0 comments on commit 5acf2d2

Please sign in to comment.