web-dev-qa-db-fra.com

Est-il possible d'appeler un code Python dans Excel-VBA?

J'ai un fichier Excel (Main.xlsm) contenant des macros. J'ai un fichier Python (python.py) pour générer un fichier Excel auxiliaire (sub.xlsx) que j'appellerais plus loin dans les macros du fichier Main.xlsm. Ce fichier sub.xlsx généré par l'exécution de python.py est enregistré dans le même répertoire de travail. 

Maintenant, je veux que ce fichier python.py soit exécuté lors de l'exécution des macros Main.xlsm, puis j'utilise ce fichier xlsx. Je veux fondamentalement réduire l'étape d'exécution de python.py en externe. Y a-t-il une commande pour ça? Je suis nouveau à VBA.

18
Pratheek P Manangi

Le moyen le plus simple consiste à exécuter l'interpréteur python avec la commande Shell

Shell ("python.exe " & yourScript & " " & arguments)
26
Uri Goren

Oui il y a. Ma façon préférée de procéder consiste à utiliser xlwings ( https://www.xlwings.org/ ), mais il existe plusieurs autres options. XlWings est génial car il est gratuit, à code source ouvert et facile à utiliser, avec une excellente documentation. Cependant, il y a quelques limitations de fonctionnalités, vous devrez donc vérifier si cela correspond à vos besoins.

19
Gabor

Il existe plusieurs façons de lancer un script python avec VBA selon que vous devez attendre la fin de l'exécution et savoir s'il s'est déroulé sans erreur.

Avec Shell , asynchrone avec console:

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

Avec Shell , synchrone sans console:

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

Avec WScript.Shell , synchrone sans console et avec code de sortie:

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
15
Florent B.

J'ai eu un mois entier Python sur mon blog ici . J'établis un modèle que j'appelle la classe de passerelle qui est une classe Python activée par COM. Elle s'enregistrera si elle est exécutée à partir de la ligne de commande et une fois enregistrée, elle sera instanciée avec CreateObject ("foo.bar"). 

Voici un bon exemple d’appel VBA d’une classe Python utilisant certaines fonctions scipy

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 __== '__main__':
    print("Registering COM server...")
    import win32com.server.register
    win32com.server.register.UseCommandLine(PythonSkewedNormal)

et le code VBA appelant

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

Le commentaire complet peut être trouvé à l'entrée originale blog ici

L'avantage ici est qu'il n'y a pas de bombardements. Lorsque le code est renvoyé, vous savez qu'il est terminé, avec le shelling une fois pour vérifier si le processus shell est terminé, etc. Cette classe de passerelle est bien meilleure à mon humble avis.

0
S Meaden