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.
Le moyen le plus simple consiste à exécuter l'interpréteur python avec la commande Shell
Shell ("python.exe " & yourScript & " " & arguments)
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.
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
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.