20

I have an Excel file (Main.xlsm) containing macros. I have a Python file (python.py) to generate a subsidiary Excel file (sub.xlsx) which I would further call in the macros of Main.xlsm-file. This sub.xlsx-file which is generated by the run of python.py is saved in the same working directory.

Now I want to make this python.py to be executed during the run of the Main.xlsm macros and then use this xlsx-file. I basically want to reduce the step of executing python.py externally. Is there a command for that? I am new to VBA.

6
  • 1
    then stay new to (and away from) VBA :) I hope you find what you need. Interesting question. Commented Jul 31, 2017 at 8:13
  • 1
    @Jean-FrançoisFabre Thanks for your interest shown in the question. :p Commented Jul 31, 2017 at 11:15
  • from my point of view (also applicable to other proprietary and cumbersome systems), sometimes it's best to 1) export data as CSV 2) run a python script to process the data easily and 3) reimport to excel/whatever so we also use this trick instead of trying to code in sub-languages :) Commented Jul 31, 2017 at 12:01
  • I use a "gateway class". So called gateway because it opens the rich world of the Python ecosystem to the Excel VBA Developer exceldevelopmentplatform.blogspot.com/2018/06/… Commented Jun 11, 2018 at 20:32
  • I have added a full answer (with a different example). Commented Dec 31, 2018 at 0:33

4 Answers 4

30

The simplest way is to run the Python interpreter with the Shell command

Shell("python.exe " & yourScript & " " & arguments)
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your reply @Uri. But a small question is it a command for python or for VBA? I need to execute it in VBA.
@UriGoren yourscript according to this context is Main.xlsm?
No, it's your python script filename
You also might need to use the full path to your python interpreter, in case you don't have it your PATH
24

Yes, there is. My preferred way of doing this is through xlwings (https://www.xlwings.org/), but there are several other options as well. XlWings is great because it's free, open source and easy to use, with great documentation. There are some feature limitations though, so you'd have to check if it fits your needs.

4 Comments

Nice idea, have an upvote. Doesn't XlWings work in a single thread though?
Great add-in! Didn't know this one, thanks! Have an(other) upvote.
Sure I will check @Gabor. Thanks for your response.
@Bathsheba Support for threading has recently been added to xlwings.
21

There are multiple ways to run a Python script with VBA, depending on whether you need to wait for the end of the execution and know if it went without error.

With the Shell() function, asynchronous with the console:

Public Sub RunPython(file As String, ParamArray args())
  Shell "python.exe """ & file & """ " & Join(args, " ")
End Sub

With Shell(), synchronous without console:

Public Function RunPython(file As String, ParamArray args())
  Shell "pythonw.exe """ & file & """ " & Join(args, " ")
End Function

With WScript.Shell, synchronous without the console and with exit code:

Public Function RunPython(file As String, ParamArray args()) As Long
  Dim obj As Object
  Set obj = CreateObject("WScript.Shell")
  RunPython = obj.Run("pythonw.exe """ & file & """ " & Join(args, " "), 0, True)
End Function

1 Comment

Excellent - I would upvote this even more if I could.
4
+100

I had a whole Python month on my blog right here. I establish a pattern, which I call the gateway class, which is a COM-enabled Python class, it will register itself if run from the command line and once registered is instantiated with CreateObject("foo.bar").

Here is a good example of VBA calling a Python class that uses some SciPy functions:

import numpy as np
import pandas as pd
from scipy.stats import skewnorm


class PythonSkewedNormal(object):
    _reg_clsid_ = "{1583241D-27EA-4A01-ACFB-4905810F6B98}"
    _reg_progid_ = 'SciPyInVBA.PythonSkewedNormal'
    _public_methods_ = ['GeneratePopulation', 'BinnedSkewedNormal']

    def GeneratePopulation(self, a, sz):
        # <https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.seed.html
        np.random.seed(10)
        # <https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.stats.skewnorm.html>
        return skewnorm.rvs(a, size=sz).tolist()

    def BinnedSkewedNormal(self, a, sz, bins):
        # <https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.seed.html>
        np.random.seed(10)
        # <https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.stats.skewnorm.html>
        pop = skewnorm.rvs(a, size=sz)
        bins2 = np.array(bins)
        bins3 = pd.cut(pop, bins2)

        table = pd.value_counts(bins3, sort=False)

        table.index = table.index.astype(str)

        return table.reset_index().values.tolist()

if __name__ == '__main__':
    print("Registering COM server...")
    import win32com.server.register
    win32com.server.register.UseCommandLine(PythonSkewedNormal)

And the calling VBA code

Option Explicit

Sub TestPythonSkewedNormal()

    Dim skewedNormal As Object
    Set skewedNormal = CreateObject("SciPyInVBA.PythonSkewedNormal")

    Dim lSize As Long
    lSize = 100

    Dim shtData As Excel.Worksheet
    Set shtData = ThisWorkbook.Worksheets.Item("Sheet3") '<--- Change sheet to your circumstances
    shtData.Cells.Clear

    Dim vBins
    vBins = Array(-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5)

    ' Stop
    Dim vBinnedData
    vBinnedData = skewedNormal.BinnedSkewedNormal(-5, lSize, vBins)

    Dim rngData As Excel.Range
    Set rngData = shtData.Cells(2, 1).Resize(UBound(vBins) - LBound(vBins), 2)

    rngData.Value2 = vBinnedData

    'Stop

End Sub

Full commentary can be found at the original blog entry here.

The advantage here is that there is no shelling. When the code it returns, you know it has finished. With shelling, one has to check if the shelled process has ended, etc. This gateway class is much better, in my humble opinion.

8 Comments

Interesting proposal! However, correct me if I'm wrong, but now you've got two processes to start (python script, then Excel), correct?
@Joel: If if were two processes than that would be the same as shelling. However, if the bitness is the same then the python runtime dll is loaded into Excel.exe. If bitness differs then yes there are two processes, here is my investigation on that subject exceldevelopmentplatform.blogspot.com/2018/06/…
ModuleNotFoundError: No module named 'win32com'
@drgs No module named 'xyz' means you need to install 'xyz'.
@SMeaden After running your code, I am getting this error: TypeError: Can't locate the script hosting the COM object - please set _reg_class_spec_ in your object. I also tried registering the same in cmd using test1.py --register. I then get a module not found error for NumPy. Although I already have NumPy installed. Can you please help me?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.