I have an interesting issue I've run into where I can easily tap into python code from VB in an excel instance, as described here: Calling python script from excel/vba
However, when I mimic the exact same code structure in visual studio in a form, the pyscript.language = "python" line fails. Does anyone know if MSScriptControl.ScriptControl can be used in VB .Net to control python like you can in excel? This would be a lot easier than setting up a com object for my python scripts.
Example code (need to add microsoft script control 1.0 and excel object library):
Imports Microsoft.Office.Interop
Public Class Form1
Dim WithEvents PyScript As MSScriptControl.ScriptControl
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim xlApp As New Excel.Application
xlApp.Visible = True
Dim xlwbook As Excel.Workbook = xlApp.Workbooks.Add(Excel.XlWBATemplate.xlWBATWorksheet)
Dim xlSheet As Excel.Worksheet = CType(xlwbook.Worksheets(1), Excel.Worksheet)
If PyScript Is Nothing Then
PyScript = New MSScriptControl.ScriptControl
PyScript.Language = "python"
PyScript.AddObject("Sheet", xlSheet)
PyScript.AllowUI = True
End If
PyScript.ExecuteStatement("Sheet.cells(1,1).value='Hello'")
End Sub
End Class
According to this http://msdn.microsoft.com/en-us/library/aa227633(v=vs.60) the scriptcontrol is a VS6 item which means that it may not be completely compatible with later versions. There is also an issue of context as the two environments (Windows forms and Excel) are different in fundamental ways so what is possible in one is not always possible in another.
Also it would be well worth double checking the first four steps in the given example and making sure that you can do this from Excel.
Related
When I run the code below, I get different results when I run in .py compared to when I run in .exe using pyinstaller
import win32com.client
import os
ConfigMacroName = "test.xls"
xl=win32com.client.Dispatch("Excel.Application")
Configmacrowb = xl.Workbooks.Open(os.getcwd()+ "\\Completed\\" + ConfigMacroName)
SlotPlansheet = Configmacrowb.Sheets("SlotPlan")
Header = SlotPlansheet.Rows(1)
SOcol = Header.Find('SO', LookAt=1).Column #I used LookAt=1 which is equivalent to LookAt:=xlWhole in excel VBA
SOlinecol = Header.Find('SO Line').Column
print("SO is " + str(SOcol) + "\nSo line is " + str(SOlinecol))
SlotPlansheet = None
Configmacrowb.Close(False)
Configmacrowb = None
xl.Quit()
xl = None
The excel input
The output in .py
The output in .exe
The output in .py file is the correct output I need. If I run it in .exe there will be duplicate variable since they both will refer to column B. For temporary solution I can just loop through the header to check each cell.
But I'm using find() function a lot so I don't know if my other programs are also affected by this inconsistency
Try changing the object creation line to:
xl=win32com.client.gencache.EnsureDispatch(‘Excel.Application’)
In my experience, the win32com.client.Dispatch() function can sometimes cause issues in that it does not guarantee the same result every time it runs. The caller doesn't know if they have an early- or late-bound object. If you have no cached makepy files then you will get a late-bound IDispatch automation interface, but if win32com finds an early-bound interface then it will use it (even if it wasn't your programme that created it). Hence code that ran fine previously may stop working.
Unless you have a good reason to be indifferent, I think it is better to be explicit and choose win32com.client.gencache.EnsureDispatch() or win32com.client.dynamic.Dispatch() for early- or late-binding respectively. I generally choose the EnsureDispatch() route, as it is quicker, enforces case-sensitivity, and gives access to any constants in the type library (eg win32com.client.constants.xlWhole) rather than rely on 'magic' integers.
Also, in the past, I have experienced odd behaviour around indexing (eg this SO question), and this was cured by deleted any gencache wrappers (see below).
Add this line to your debug code:
print('Cache directory:',win32com.client.gencache.GetGeneratePath())
This will tell you where the gencache early-binding python files are being generated, and where win32com.client.Dispatch() will look for any cached wrapper files to attempt early-binding. If you want to clear the cached of generated files just delete the contents of this directory. It will be interesting to see if the OP's two routes have the same directory.
Hello programmers of SO,
I am making a C# interface for a socketing,
The interface is made with C# but the code ressponsable for the actual socketing part is written in Python because writing that in C# Would be insanity,
i used some ironPython to execute the python code:
var engine = Python.CreateEngine();
engine.ExecuteFile(""); //directory is inside the quotes
but i need to pass some values (IP and all that jazz)
anyone know how?
I looked at How do I pass arguments to a Python script with IronPython and it would be too hard to implement so dont ask if that thread answers my question please
You could store the data as a string in some file in c sharp, and you could have a thread in python constantly looking for the information. It would know when the information has been stored in the file if a certain keyword is passed along with it. For example (if you were using a textfile with a location called location)
File.WriteAllText(location, IPAdress + "[IP]");
and here is the code for searching for it
foreach(string datainfo in File.ReadAllText(location).Split(Convert.ToChar("\n"))
{
if(datainfo.Contains("[IP]")
datainfo.Replace("[IP]","");
string Ip = datainfo;
}
I don't know how to deal with files in python but the python equivalent of the csharp code above should work.
I've written some code in Python using numpy, pandas, scikit-learn. Is it possible to call this Python code from a Julia Program?
I think you can think to three different way to call Python code from Julia, in order from the most low level to the highest level they are:
Use the Foreign Funcall Interface as suggested by #madbird. However you will almost surelly not want to do this, as a package that exploits it, PyCall.jl, already exists;
Use the abovementioned PyCall.jl package to call any python code and/or wrap a python library (well.. "most".. "any" is a dangerous word). Details of this below;
As wrapping a Python library using PyCall is very easy, the most important Python libraries have been already wrapped in Julia, like Pandas in Pandas.jl, Scikit-learn in ScikitLearn.jl, etc. If you need them, just use the corresponding Julia package.
How to use PyCall.jl to call Python code and libraries.
Note: The following is an excerpt from my "Julia Quick Syntax Reference" book (Apress, 2019)
Julia ⇄ Python
The "standard" way to call Python code in Julia is to use the PyCall package.
Some of its nice features are: (a) it can automatically download and install a local copy of Python, private to Julia, in order to avoid messing with version dependency from our "main" Python installation and provide a consistent environment in Linux, Windows and MacOS; (b) it provides automatic conversion between Julia and Python types; (c) it is very simple to use.
Concerning the first point, PyCall by default install the "private" Python environment in Windows and MacOS while it will use the system default Python environment in Linux. +
We can override such behaviour with (from the Julia prompt) ENV["PYTHON"]="blank or /path/to/python"; using Pkg; Pkg.build("PyCall"); where, if the environmental variable is empty, PyCall will install the "private" version of Python.
Given the vast amount of Python libraries, it is no wonder that PyCall is one of the most common Julia packages.
Embed Python code in a Julia program
Embedding Python code in a Julia program is similar to what we saw with C++, except that we don't need (for the most) to wonder about transforming data. We both define and call the Python functions with py"...", and in the function call we can use directly our Julia data:
using PyCall
py"""
def sumMyArgs (i, j):
return i+j
def getNElement (n):
a = [0,1,2,3,4,5,6,7,8,9]
return a[n]
"""
a = py"sumMyArgs"(3,4) # 7
b = py"sumMyArgs"([3,4],[5,6]) # [8,10]
typeof(b) # Array{Int64,1}
c = py"sumMyArgs"([3,4],5) # [8,9]
d = py"getNElement"(1) # 1
Note that we don't need to convert even for complex data like arrays, and the results are converted back to Julia types.
Type conversion is automatic for numeric, boolean, string, IO stream, date/period, and function types, along with tuples, arrays/lists, and dictionaries of these types. Other types are instead converted to the generic PyObject type.
Note from the last line of the previous example that PyCall doesn't attempt index conversion (Python arrays are 0-based while Julia ones are 1-based): calling the python getNElement() function with "1" as argument will retrieve what in Python is the element "1" of the array.
Use Python libraries
Using a Python library is straightforward as well, as shown in the below example that use the ezodf module to create an OpenDocument spreadsheet (a wrapper of ezodf for ODS documents - that internally use PyCall - already exists, OdsIO).
Before attempting to replicate the following code, please be sure that the ezodf module is available to the Python environment you are using in Julia. If this is an independent environment, just follow the Python way to install packages (e.g. with pip). If you are using the "private" Conda environment, you can use the Conda.jl package and type using Conda; Conda.add_channel("conda-forge"); Conda.add("ezodf").
const ez = pyimport("ezodf") # Equiv. of Python `import ezodf as ez`
destDoc = ez.newdoc(doctype="ods", filename="anOdsSheet.ods")
sheet = ez.Sheet("Sheet1", size=(10, 10))
destDoc.sheets.append(sheet)
dcell1 = get(sheet,(2,3)) # Equiv. of Python `dcell1 = sheet[(2,3)]`. This is cell "D3" !
dcell1.set_value("Hello")
get(sheet,"A9").set_value(10.5) # Equiv. of Python `sheet['A9'].set_value(10.5)`
destDoc.backup = false
destDoc.save()
The usage in Julia of the module follows the Python API with few syntax differences.
The module is imported and assigned to a shorter alias, ez.
We can then directly call its functions with the usual Python syntax module.function().
The doc object returned by newdoc is a generic PyObject type. We can then access its attributes and methods with myPyObject.attribute and myPyObject.method() respectively.
In the cases where we can't directly access some indicized values, like sheet[(2,3)] (where the index is a tuple) we can invoke instead the get(object,key) function. +
Finally, note again that index conversion is not automatically implemented: when asking for get(sheet,(2,3)) these are interpreted as Python-based indexes, and cell D3 of the spreadsheet is returned, not B2.
I guess Foreign Funcall Interface is what you're looking for. Here is the example for Julia. More info in PyCall.jl repository.
I am trying write a simple user defined function in Python that I pass a value to from Excel via Xlwings. I ran across some examples with an Add-in that you need to import user defined functions, but that seems overly complex.
Why isn't my example working?
VBA:
Function Hello(name As String) As String
RunPython ("import Test; Test.sayhi(name)")
End Function
Python (Test.py):
from xlwings import Workbook, Range
def sayhi(name):
wb = Workbook.caller()
return 'Hello {}'.format(name)
Error:
NameError: name 'name' is not defined
Make sure you're supplying the argument correctly:
RunPython ("import Test; Test.sayhi('" & name & "')")
The text inside RunPython() should be a valid python script. So the comment from "Tim Williams" is a quick solution. You just need to be careful to avoid ' character inside the name variable to break the python script.
If you need to write UDF (User Defined Function) a lot, or need to pass in value to get the output and then handle the result via VBA, try use ExcelPython instead of Xlwings.
Note, ExcelPython and Xlwings can work together, you can have both without conflict.
I think it's better you play the example to understand the difference. My understanding is limited to my knowledge, which might not be correct.
To summarize the difference:
ExcelPython needs a installer to be installed, it is good in UDF, which helps if you want to pass arguments in and out, and the function is cached in memory, so later call will be very quick.
Xlwings is simpler by just add the VBA module, no installer needed. It trigger Python in the background each time to run the script (each call starts a new process), in the script you can manipulate (read/write) Excel via COM.
I have the same question in the beginning, but later I find out using VBA in Excel side (intellisense) plus ExcelPython on UDF (simply process and return the data back) is a nice combination, so I think ExcelPython is only what I need.
As mentioned earlier, the 2 components have no conflict, you can have both. If the 2 author agree too, it is a good idea to combine them.
I am trying to use the python ctypes library to access various functions in a COM DLL created in Visual Fox Pro (from a .prg file).
Here is an example in fox pro (simplified from the actual code)
DEFINE CLASS Testing AS CUSTOM OLEPUBLIC
PROCEDURE INIT
ON ERROR
SET CONSOLE OFF
SET NOTIFY OFF
SET SAFETY OFF
SET TALK OFF
SET NOTIFY OFF
ENDPROC
FUNCTION get_input_out(input AS STRING) AS STRING
output = input
RETURN output
ENDFUNC
ENDDEFINE
In python i am doing something along the lines of:
import ctypes
link = ctypes.WinDLL("path\to\com.dll")
print link.get_input_out("someinput")
The dll registers fine and is loaded but i just get the following when I try to call the function.
AttributeError: function 'get_input_out' not found
I can verfiy the dll does work as i was able to access the functions with a php script using the COM libary.
I would really like to get this working in python but so far my attempts have all been in vain, will ctypes even work with VFP? Any advice would be appreciated.
In the case of VFP COM objects using the OLEPUBLIC clause, you should use the python Win32 extensions, not the ctypes module. Python Win32 extensions provide a full featured set of components that allow Python to be a COM client, which is what you want in this case.
Your code should look something like this:
from win32com.client import Dispatch
oFox = Dispatch("com.testing")
# where the file name compiled by VFP is com.dll and the olepublic class is testing.
# in Windows this stuff is not case sensitive.
print oFox.get_input_out("something")
# to close things down..
oFox = None
If all you want to do is access VFP tables, Microsoft supplies a free ADO compliant component vfpoledb which can be used to access tables via ADODB, which in turn can be accessed through this same Dispatch() capability in the Win32 extensions.
Try removing the parentheses from the call to the function. Change print link.get_input_out("someinput") to print link.get_input_out "someinput".