How do I send/read data from VBA in Python? - python

Background
Right now I'm creating a macro to help automate the creation of some graphs in VBA. However, the creation of the graphs requires specific tasks to be done, for example, certain points in a series to be larger depending on previous instances. I would much rather do this data manipulation in python.
Problem
I want to use excel for its user-friendly interface but want to handle all the data manipulation within Python. How can I send data I create in VBA to python. To clarify I'm not trying to read specific cells in the excel sheet.
If I define a string in VBA say...
Dim example_string as String
example_string = "Hello, 1, 2, 3, Bye"
How can I send this information I created within VBA to Python for manipulation?
More Specifics
I have a textbox in excel that is filled by the user, which I read using VBA. I want to send that txt data from VBA to python. The user highlights the desired cells, which are not necessarily the same each time, clicks a button and fills a textbox. I don't want to use range or specific cell selection since this would require the user to specifically enter all the desired data into cells (too time-consuming).
I want to understand the basic procedure of how to send data between VBA and python.

You can do the whole thing in python, it will be more efficient and you can either use excel or sqlite3 as database, go here to read about graphic interfaces with tkinter, use pandas and numpy to process your data.
If you insist in sending data to python, import sys to your python script to read parameters and then run it from vba with the shell() method.
EDIT: You wanted an example, here it is =>
Open a new excel file, create a procedure like this (VBA CODE):
Sub sendToPython()
Dim shell As Object
Dim python As String
Dim callThis As String
Dim passing
Set shell = VBA.CreateObject("Wscript.Shell")
'/* This is where you installed python (Notice the triple quotes and use your own path *always)*/
python = """C:\Users\yourUserName\appdata\local\programs\python\python37\python.exe"""
'/* This is the data you'll be passing to python script*/
passing = "The*eye*of*the*tiger"
callThis = "C:\Users\yourUserName\desktop\yourScriptName.py " & passing & ""
shell.Run python & callThis
End Sub
The idea is to create some kind of a parser in python, this is my silly example (PYTHON CODE):
import sys
f = open("log.txt", "w")
arg = (sys.argv[1]).split("*")
s = " "
arg = s.join(arg)
print("This is the parameter i've entered: " + arg, file=f)
Notice how i used sys to read a parameter and i exported to actually see some results because otherwise you'll just see a black screen popping up for like a millisecond.
I also found this article, but it requires you to wrap the python script in a class and i don't know if that works for you

Related

How to get LibreOffice headless Calc calculate to save new values from uno?

I am trying to open an excel file from python, get it to recalculate and then save it with the newly calculated values.
The spreadsheet is large and opens fine in LibreOffice with GUI, and initially shows old values. If I then do a Data->Calculate->Recalculate Hard I see the correct values, and I can of course saveas and all seems fine.
But, there are multiple large spreadsheets I want to do it from so I don't want to use a GUI instead I want to use Python. The following all seems to work to create a new spreadsheet but it doesn't have the new values (unless I again manually do a recalculate hard)
I'm running on Linux. First I do this:
soffice --headless --nologo --nofirststartwizard --accept="socket,host=0.0.0.0,port=8100,tcpNoDelay=1;urp"
Then, here is sample python code:
import uno
local = uno.getComponentContext()
resolver = local.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", local)
context = resolver.resolve("uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager")
remoteContext = context.getPropertyValue("DefaultContext")
desktop = context.createInstanceWithContext("com.sun.star.frame.Desktop", remoteContext)
document = desktop.getCurrentComponent()
file_url="file://foo.xlsx"
document = desktop.loadComponentFromURL(file_url, "_blank", 0, ())
controller=document.getCurrentController()
sheet=document.getSheets().getByIndex(0)
controller.setActiveSheet(sheet)
document.calculateAll()
file__out_url="file://foo_out.xlsx"
from com.sun.star.beans import PropertyValue
pv_filtername = PropertyValue()
pv_filtername.Name = "FilterName"
pv_filtername.Value = "Calc MS Excel 2007 XML"
document.storeAsURL(file__out_url, (pv_filtername,))
document.dispose()
After running the above code, and opening foo_out.xlsx it shows the "old" values, not the recalculated values. I know that the calculateAll() is taking a little while, as I would expect for it to do the recalculation. But, the new values don't seem to actually get saved.
If I open it in Excel it does an auto-recalculate and shows the correct values and if I open in LibreOffice and do Recalculate Hard it shows the correct values. But, what I need is to save it, from python like above, so that it already contains the recalculated values.
Is there any way to do that?
Essentially, what I want to do from python is:
open, recalculate hard, saveas
It seems that this was a problem with an older version of LibreOffice. I was using 5.0.6.2, on Linux, and even though I was recalculating, the new values were not even showing up when I extracted the cell values directly.
However, I upgraded to 6.2 and the problem has gone away, using the same code and the same input files.
I decided to just answer my own question, instead of deleting it, as this was leading to a frustration until I solved it.

Is there a way for 2 scripts to write to the same file?

I have limited python experience, but determined to learn. I am trying to create a script that would write some data inputs to excel until stopped. It is very straightforward when a single person is using it but the problem is that 2 people will be using it at once.
I am thinking about making it simple and just having 2 exact same scripts running at the same time, but the problem comes in when the file is going to be saved. If I have two files being saved with the same name, one is going to overwrite the other and the data will be lost. Is there a way to have the scripts create files with different names without having to manually change the code? (This would eventually be scaled to up to 20 computers running it)
The loop looks like:
import xlwt
from xlwt import Workbook
wb = Workbook()
s1 = wb.add_sheet('Sheet 1')
data = []
while user != '0':
user = input('Scan ID Badge: ')
data.append(user)
order = input('Scan order: ')
data.append(order)
item = input('Scan item barcode: ')
data.append(item)
for i in range(len(data)):
s1.write(row,i,data[i])
wb.save('OrderData.xls')
data = []
row += 1
If you want to use a tabular form of data storage anyways, you could switch to a real database and on interval create an excel-like summary of the db file.
If you know all of the users using this script will be using machines with different network names, you could include the computer name in the XLS name:
import platform
filename = 'AssociateEfficiencyTemp-' + platform.node() + '.xls'
# ...
wb.save(filename)
(You can also use getpass.getuser() to (try and) get the username of the user running the script.)
You can then write another script that reads all of the separate files (glob.glob('AssociateEfficiencyTemp-*.xls') etc.) and combines them.
(I would suggest using another format than .xls for the intermediary files though, such as plain text files of JSON lines.)

VBScript kills program before text file is made

I made a vbscript to open an excel doc, then runs a python program that pulls data from the documents tables and prints it to a text file. The script is supposed to wait until the python program is done creating the text doc then close the excel doc, but for whatever reason my python program closes before it even has a chance to make that text doc.
I even changed the python code to just print a simple 'Hello World' into a new text document in case pulling data from excel was causing problems but the text document still wasn't created.
This is the script that i'm running:
Set xl = CreateObject("Excel.application")
xl.Application.Workbooks.Open "C:\Users\V\Documents\_PROGRAMS_\TEST.xlsx"
xl.Application.Visible = True
Dim oshell
Set oshell = WScript.CreateObject("WScript.Shell")
oshell.CurrentDirectory = "C:\Users\V\Documents\_PROGRAMS_\"
windowStyle = 1
waitUntilFinished = True
oshell.run "python table.py", windowStyle, waitUntilFinished
xl.Application.Quit
I don't think adding the python program is important since that isn't really the problem. Although I will say that I tried putting a delay in the python program to see if that would change anything (it didn't).
I though adding the two extra arguments to .run would make it wait until the process is finished but I guess I must be missing something?
I'm just starting to learn how to use vbscript so any explanations of code would be welcomed!
Thanks!
EDIT: So after more testing it seems that it does have something to do with accessing the excel document, as just printing 'Hello World' to a file did actually work and the file was created (I made it in the wrong directory by accident so I was looking in the wrong place). But trying it with the data from the excel document no file is created, the program just ends
So here's the python code I wrote:
#!/usr/bin/python27
import pandas as pd
table = pd.read_excel("TEST.xlsx") #Get excel doc
file = open("text.txt", "w") #Open new file
file.write(table.columns.values) #Print out column headers
file.write("Hello!")
file.close()

How to operate on unsaved Excel file?

I'd like to automate a loop:
ABAQUS generates a Excel file;
Matlab utilises data in Excel file;
loop 1 and 2.
Now my question is: after step 1, the Excel file from ABAQUS is unsaved as Book1. I cannot use Matlab command to save it. Is there a way not to save this ''Book1'' file, but use the data in it? Or if I can find where it is so I can use the data inside? (I assume that Excel always saves the file even though user doesn't?)
Thank you! 
As agentp mentioned, if you are running Abaqus via a Python script, you can just use Python to create a .txt file to save all the relevant information. If well structured, a .txt file can be as readable as an Excel spreadsheet. Because Matlab and Python have intrinsic functions to read and write files this communication can be easily done.
As for Matlab calling Abaqus, you can use something similar to:
system('abaqus cae nogui=YOUR_SCRIPT.py')
Your script that pipes to Excel should have some code similar to this:
abq_ExcelUtilities.excelUtilities.XYtoExcel(
xyDataNames='S:Mises PI: PART-1-1 E: 4309 IP: 1', trueName='')
writing the same data to a report (.rpt) file the code looks like this:
x0 = session.xyDataObjects['S:Mises PI: PART-1-1 E: 4309 IP: 1']
session.writeXYReport(fileName='abaqus.rpt', xyData=(x0, ))
now to "roll your own", use that x0 object: x0.data is a regular python tuple holding the actual data which you can write to a file however you like, eg:
file=open('myfile.csv','w')
for point in x0.data: file.write('%g,%g\n'%point)
file.close()
(you can comment or delete the writeXYReport call )

Using Python to read VBA from an Excel spreadsheet

I would like to write a VBA diff program in (preferably) Python. Is there a Python library that will allow me to read the VBA contained in an Excel spreadsheet?
Here's some quick and dirty boilerplate to get you started. It uses the Excel COM object (a Windows only solution):
from win32com.client import Dispatch
wbpath = 'C:\\example.xlsm'
xl = Dispatch("Excel.Application")
xl.Visible = 1
wb = xl.Workbooks.Open(wbpath)
vbcode = wb.VBProject.VBComponents(1).CodeModule
print vbcode.Lines(1, vbcode.CountOfLines)
This prints the silly macro I recorded for this example:
Sub silly_macro()
'
' silly_macro Macro
'
'
Range("B2").Select
End Sub
Note that Lines and VBComponents use 1-based indexing. VBComponents also supports indexing by module name. Also note that Excel requires backslashes in paths.
To dive deeper see Pearson's Programming The VBA Editor. (The above example was cobbled together from what I skimmed from there.)
I have created an application that does this called VbaDiff. If you provide it two Excel files it will compare the VBA code in each. You can also run it from the command line, or use the version that comes with an API if you want to integrate it with your own programs.
You can find out more at http://www.technicana.com/vbadiff-information.html
Chris

Categories

Resources