Return result from Python to Vba
Consider using VBA Shell's StdOut
to capture a stream of the output lines. Be sure to have the Python script print to screen the value:
Python
...
print(outputval)
VBA (s
below would be string output)
Public Sub PythonOutput()
Dim oShell As Object, oCmd As String
Dim oExec As Object, oOutput As Object
Dim arg As Variant
Dim s As String, sLine As String
Set oShell = CreateObject("WScript.Shell")
arg = "somevalue"
oCmd = "python ""C:\Path\To\Python\Script.py""" & " " & arg
Set oExec = oShell.Exec(oCmd)
Set oOutput = oExec.StdOut
While Not oOutput.AtEndOfStream
sLine = oOutput.ReadLine
If sLine <> "" Then s = s & sLine & vbNewLine
Wend
Debug.Print s
Set oOutput = Nothing: Set oExec = Nothing
Set oShell = Nothing
End Sub
Credit
Script borrowed from @bburns.km, non-accepted answer, from this SO post
Calling Python from VBA - returning Python function value
I couldn't find any direct way to transfer value returned by Python function, so I handled it by saving Python result to text file on Desktop and reading + deleting the same file with VBA.
Important things are:
shellObj.Run
unlikeShell
allows to set a Boolean parameter to wait for return toTrue
, so that the rest of VBA code doesn't try to get data from text file while there isn't any created yet.If text file doesn't have a full file path specified, only a file name, Python won't create text file in directory in which Python script is located, as it happens when I run script through command line or Visual Studio. Apparently when called from VBA, Python has a different perception of default directory and it isn't a directory of VBA file either.
Here is my Python code:
def myFunction():
# do stuff
return stuff_output
import os
f = open(os.path.expanduser("~\Desktop") + "\myTemp.txt","w") #create a new text file on Desktop in write mode
f.write(str(myFunction())) #write output to created text file
f.close() #close file object
Here is my VBA code:
Option Explicit
Sub RunPython()
Dim shellObj As Object: Set shellObj = CreateObject("WScript.Shell")
Dim tempTxtPath As String
Const scriptPath As String = "C:\Users\Rychu\Desktop\python\myPythonScript.py"
Const pythonPath As String = "C:\Python36-32\python.exe"
tempTxtPath = CreateObject("WScript.Shell").specialfolders("Desktop") & "\myTemp.txt"
shellObj.Run pythonPath & " " & scriptPath, vbHide, True
Debug.Print (getTextAndDelete(tempTxtPath))
End Sub
Function getTextAndDelete(filePath) As String
Dim fileNum As Long
Dim myData As String
fileNum = FreeFile
Open filePath For Binary As #fileNum
myData = Space$(LOF(1))
Get #fileNum, , myData
Close #fileNum
Kill filePath
getTextAndDelete = myData
End Function
Return to Word vba the result of a python script called by the vba macro in Windows
If you use print()
in your Python script then the printed content will be sent to stdout.
Using a WScript.Shell
object to run your script gives you access to the stdout content.
Here's a very basic example:
Test python script test.py:
print("hello world")
VBA:
Sub TestIt()
Debug.Print ShellOutput("python D:\temp\test.py") ''>> hello world
End Sub
Function ShellOutput(cmd As String) As String
Dim oShell As Object, oCmd As String
Dim oExec As Object, oOutput As Object
Dim arg As Variant
Dim s As String, sLine As String
Set oShell = CreateObject("WScript.Shell")
Set oExec = oShell.Exec(cmd)
Set oOutput = oExec.StdOut
While Not oOutput.AtEndOfStream
sLine = oOutput.ReadLine
If sLine <> "" Then s = s & sLine & vbNewLine
Wend
ShellOutput = s
Set oOutput = Nothing
Set oExec = Nothing
Set oShell = Nothing
End Function
Adapted from: Return result from Python to Vba
Returning result of an external script to VBA
Answer extracted from question:
I got it working!
I was able to call my Python script using the result = MacScript(command)
function where I defined my command
as the following:
command = "do shell script """ & pyPath & "python " & getURLpath & "getURL.py --formula '" _
& Latex_Str & "' --fontsize " & Font_Size & " " & WebAdd & """"
My Python script is called getURL.py
, and handles the request to the server based on the optional arguments --formula
and --fontsize
defined by the user and stored in VBA as Latex_Str
and Font_Size
respectively, and the web address of the server WebAdd
. I also added some functionality to my Python script to handle passing proxy settings. The above command passed through MacScript
returns the stdout of the Python script, which is the return from the server. The Python script is as follows:
# Import the required libraries
from urllib import urlencode
from urllib2 import Request, urlopen, URLError, ProxyHandler, build_opener, install_opener
import argparse
# Set up our argument parser
parser = argparse.ArgumentParser(description='Sends LaTeX string to web server and returns meta data used by LaTeX in Word project')
parser.add_argument('webAddr', type=str, help='Web address of LaTeX in Word server')
parser.add_argument('--formula', metavar='FRML', type=str, help='A LaTeX formula string')
parser.add_argument('--fontsize', metavar='SIZE', type=int, default=10, help='Integer representing font size (can be 10, 11, or 12. Default 10)')
parser.add_argument('--proxServ', metavar='SERV', type=str, help='Web address of proxy server, i.e. http://proxy.server.com:80')
parser.add_argument('--proxType', metavar='TYPE', type=str, default='http', help='Type of proxy server, i.e. http')
# Get the arguments from the parser
args = parser.parse_args()
# Define formula string if input
if args.formula:
values = {'formula': str(args.fontsize) + '.' + args.formula} # generate formula from args
else:
values = {}
# Define proxy settings if proxy server is input.
if args.proxServ: # set up the proxy server support
proxySupport = ProxyHandler({args.proxType: args.proxServ})
opener = build_opener(proxySupport)
install_opener(opener)
# Set up the data object
data = urlencode(values)
data = data.encode('utf-8')
# Send request to the server and receive response, with error handling!
try:
req = Request(args.webAddr, data)
# Read the response and print to a file
response = urlopen(req)
print response.read()
except URLError, e:
if hasattr(e, 'reason'): # URL error case
# a tuple containing error code and text error message
print 'Error: Failed to reach a server.'
print 'Reason: ', e.reason
elif hasattr(e, 'code'): # HTTP error case
# HTTP error code, see section 10 of RFC 2616 for details
print 'Error: The server could not fulfill the request.'
print 'Error code: ', e.code
# print e.read()
If anyone is curious, the full code will be available on the project page once I am done fixing a couple additional bugs and testing it. The (working) Windows version is already up there.
Related Topics
Python - Initializing Multiple Lists/Line
How to Break a Line of Chained Methods in Python
Python Daemon and Systemd Service
Why Are Default Arguments Evaluated at Definition Time
How to Run a Python Script in a Web Page
What's 0Xff for in Cv2.Waitkey(1)
Attributeerror: 'List' Object Has No Attribute 'Click' - Selenium Webdriver
How to Create an SQL View with SQLalchemy
How to Log Server Errors on Django Sites
Django JSONfield Inside Arrayfield
Logging, Streamhandler and Standard Streams
How to Get Element-Wise Matrix Multiplication (Hadamard Product) in Numpy
Is Tensorflow Compatible with a Windows Workflow
How to Make a Python Script Executable
How to Convert a Password into Asterisks While It Is Being Entered
Export a Pandas Dataframe as a Table Image
Extract a String Between Double Quotes
Trying to Delay a Specific Function for Spawning Enemy After a Certain Amount of Time