Selecting a folder from a VBA popup that was run from Python - python

I am using Python and the win32com.client library to run a VBA macro. The macro requires a directory (which is a substring of the .xlsm path itself). How do I pass this directory/string to the VBA popup?
import os, os.path
import win32com.client
if os.path.exists('C:/test_folder/excel_file.xlsm'):
xl=win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Open('C:/test_folder/excel_file.xlsm', ReadOnly=1)
xl.Application.Run('excel_file.xlsm!Sheet1.Macro1')
After this code is run, the folder explorer window pops up asking for a directory. How would I pass the directory to Excel?

This line of code will help you save the exact directory you want making it non dependent to the python path, and associates it to a variable, after that you can just pass on that string variable onto your vba popup. You can try making it global or just reusing it on the continuation of the code, after that you can just reference so you can put it in excel. I hope this helps.
Sub Selectingadirectory()
Dim fd As FileDialog
Dim ActionClicked As Boolean
Dim SelectedFolderPath As String
Set fd = Application.FileDialog(msoFileDialogFolderPicker)
fd.title = "Chose your directory path"
fd.InitialFileName = Environ("UserProfile") & "\Desktop"
fd.AllowMultiSelect = False
ActionClicked = fd.Show
If ActionClicked Then
SelectedFolderPath = fd.SelectedItems(1)
Else
Msgbox "You didnt chose a folder"
Exit Sub
End If
End sub

Related

How to start Outlook with Python and win32com

I manage to open Outlook Elements like mails or task with this code:
import win32com.client as win32
outlook = win32.gencache.EnsureDispatch('Outlook.Application')
new_mail = outlook.CreateItem(4)
new_mail.Display(True)
And its possible to open Excel with this code:
excel = win32.gencache.EnsureDispatch('Excel.Application')
excel.Visible = True
But how can I just open the main window of outlook? Can't find any functions or numbers for the "createItem" to just open the normal outlook window.
Someone got ideas?
I tried to search throu the MS VBA Api but couldnt find any solution
Inspired by this, one option might be:
outlook.Session.GetDefaultFolder(6).Display()
where 6 corresponds to the inbox as documented here.
You need to display an Explorer window of Outlook. The Explorers.Add method creates a new instance of the explorer window, then you just need to call the Explorer.Display method which displays a new Explorer object for the folder. The Display method is supported for explorer and inspector windows for the sake of backward compatibility. To activate an explorer or inspector window, use the Activate method instead. Here is the sample VBA code which opens a new explorer window for the Drafts folder in Outlook:
Sub DisplayDrafts()
Dim myExplorers As Outlook.Explorers
Dim myOlExpl As Outlook.Explorer
Dim myFolder As Outlook.Folder
Set myExplorers = Application.Explorers
Set myFolder = Application.GetNamespace("MAPI").GetDefaultFolder(olFolderDrafts)
Set myOlExpl = myExplorers.Add(myFolder, olFolderDisplayNoNavigation)
myOlExpl.Display
End Sub
The Outlook object model is common for all programming languages, so I think you will find the sequence of property and method calls that should be used to get the job done.
To start Outlook application, try os.startfile("outlook") or see example
import psutil
import os
class Outlook:
#staticmethod
def is_outlook_running():
for p in psutil.process_iter(attrs=['pid', 'name']):
if p.info['name'] == "OUTLOOK.EXE":
print("Yes", p.info['name'], "is running")
break
else:
print("No, Outlook is not running")
os.startfile("outlook")
print("Outlook is starting now...")
if __name__ == "__main__":
outlook = Outlook()
outlook.is_outlook_running()

Is there a way to open a text file into python code (similar to doing CTRL+O on windows)? [duplicate]

I have a simple script which parses a file and loads it's contents to a database. I don't need a UI, but right now I'm prompting the user for the file to parse using raw_input which is most unfriendly, especially because the user can't copy/paste the path. I would like a quick and easy way to present a file selection dialog to the user, they can select the file, and then it's loaded to the database. (In my use case, if they happened to chose the wrong file, it would fail parsing, and wouldn't be a problem even if it was loaded to the database.)
import tkFileDialog
file_path_string = tkFileDialog.askopenfilename()
This code is close to what I want, but it leaves an annoying empty frame open (which isn't able to be closed, probably because I haven't registered a close event handler).
I don't have to use tkInter, but since it's in the Python standard library it's a good candidate for quickest and easiest solution.
Whats a quick and easy way to prompt for a file or filename in a script without any other UI?
Tkinter is the easiest way if you don't want to have any other dependencies.
To show only the dialog without any other GUI elements, you have to hide the root window using the withdraw method:
import tkinter as tk
from tkinter import filedialog
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename()
Python 2 variant:
import Tkinter, tkFileDialog
root = Tkinter.Tk()
root.withdraw()
file_path = tkFileDialog.askopenfilename()
You can use easygui:
import easygui
path = easygui.fileopenbox()
To install easygui, you can use pip:
pip3 install easygui
It is a single pure Python module (easygui.py) that uses tkinter.
Try with wxPython:
import wx
def get_path(wildcard):
app = wx.App(None)
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
dialog = wx.FileDialog(None, 'Open', wildcard=wildcard, style=style)
if dialog.ShowModal() == wx.ID_OK:
path = dialog.GetPath()
else:
path = None
dialog.Destroy()
return path
print get_path('*.txt')
pywin32 provides access to the GetOpenFileName win32 function. From the example
import win32gui, win32con, os
filter='Python Scripts\0*.py;*.pyw;*.pys\0Text files\0*.txt\0'
customfilter='Other file types\0*.*\0'
fname, customfilter, flags=win32gui.GetOpenFileNameW(
InitialDir=os.environ['temp'],
Flags=win32con.OFN_ALLOWMULTISELECT|win32con.OFN_EXPLORER,
File='somefilename', DefExt='py',
Title='GetOpenFileNameW',
Filter=filter,
CustomFilter=customfilter,
FilterIndex=0)
print 'open file names:', repr(fname)
print 'filter used:', repr(customfilter)
print 'Flags:', flags
for k,v in win32con.__dict__.items():
if k.startswith('OFN_') and flags & v:
print '\t'+k
Using tkinter (python 2) or Tkinter (python 3) it's indeed possible to display file open dialog (See other answers here). Please notice however that user interface of that dialog is outdated and does not corresponds to newer file open dialogs available in Windows 10.
Moreover - if you're looking on way to embedd python support into your own application - you will find out soon that tkinter library is not open source code and even more - it is commercial library.
(For example search for "activetcl pricing" will lead you to this web page: https://reviews.financesonline.com/p/activetcl/)
So tkinter library will cost money for any application wanting to embedd python.
I by myself managed to find pythonnet library:
Overview here: http://pythonnet.github.io/
Source code here: https://github.com/pythonnet/pythonnet
(MIT License)
Using following command it's possible to install pythonnet:
pip3 install pythonnet
And here you can find out working example for using open file dialog:
https://stackoverflow.com/a/50446803/2338477
Let me copy an example also here:
import sys
import ctypes
co_initialize = ctypes.windll.ole32.CoInitialize
# Force STA mode
co_initialize(None)
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import OpenFileDialog
file_dialog = OpenFileDialog()
ret = file_dialog.ShowDialog()
if ret != 1:
print("Cancelled")
sys.exit()
print(file_dialog.FileName)
If you also miss more complex user interface - see Demo folder
in pythonnet git.
I'm not sure about portability to other OS's, haven't tried, but .net 5 is planned to be ported to multiple OS's (Search ".net 5 platforms", https://devblogs.microsoft.com/dotnet/introducing-net-5/ ) - so this technology is also future proof.
If you don't need the UI or expect the program to run in a CLI, you could parse the filepath as an argument. This would allow you to use the autocomplete feature of your CLI to quickly find the file you need.
This would probably only be handy if the script is non-interactive besides the filepath input.
Another os-agnostic option, use pywebview:
import webview
def webview_file_dialog():
file = None
def open_file_dialog(w):
nonlocal file
try:
file = w.create_file_dialog(webview.OPEN_DIALOG)[0]
except TypeError:
pass # user exited file dialog without picking
finally:
w.destroy()
window = webview.create_window("", hidden=True)
webview.start(open_file_dialog, window)
# file will either be a string or None
return file
print(webview_file_dialog())
Environment: python3.8.6 on Mac - though I've used pywebview on windows 10 before.
I just stumbled on this little trick for Windows only: run powershell.exe from subprocess.
import subprocess
sys_const = ssfDESKTOP # Starts at the top level
# sys_const = 0x2a # Correct value for "Program Files (0x86)" folder
powershell_browse = "(new-object -COM 'Shell.Application')."
powershell_browse += "BrowseForFolder(0,'window title here',0,sys_const).self.path"
ret = subprocess.run(["powershell.exe",powershell_browse], stdout=subprocess.PIPE)
print(ret.stdout.decode())
Note the optional use of system folder constants. (There's an obscure typo in shldisp.h that the "Program Files (0x86)" constant was assigned wrong. I added a comment with the correct value. Took me a bit to figure that one out.)
More info below:
System folder constants

Run python script from VBA

I have tried several solutions to run python script out of VBA.
My current solution that still doesnt work
Dim wsh As Object
Set wsh = VBA.CreateObject("WScript.Shell")
Dim windowStyle As Integer: windowStyle = 1
Dim waitOnReturn As Boolean: waitOnReturn = True
wsh.Run "C:\Python33\python.exe C:\Users\***\Documents\Promo models\get_sku_data.py", windowStyle, waitOnReturn
Any ideas?
xlwings claims to make python from excel very easy:
Sub RandomNumbers()
RunPython ("import mymodule; mymodule.rand_numbers()")
End Sub
To make this run, just import the VBA module xlwings.bas in the VBA editor (Open the VBA editor with Alt-F11, then go to File > Import File... and import the xlwings.bas file. ). It can be found in the directory of your xlwings installation.
Finally I ended up with creation of .exe files instead of trying to call scripts itself.
So, I applied cx_Freeze, created .exe files and then called them as
Script_path1 = "C:\Users\" + Environ("Username") & "\Documents\Promo_models\get_sku_data\gui_changed.exe"
wsh.Run Script_path1, windowStyle, waitOnReturn
Here are a couple options to try.
RetVal = Shell("<full path to python.exe> " & "<full path to your python script>")
Or if the python script is in the same folder as the workbook, then you can try :
RetVal = Shell("<full path to python.exe> " & "ActiveWorkBook.Path & \<python script name>")

Running Libreoffice BASIC macro from python

I've a macro in LibreOffice BASIC and I want to run it from my python program. I've found some threads in which they use this code:
import os
import win32com.client
if os.path.exists("excelsheet.xlsm"):
xl=win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Open(Filename="C:\Full Location\To\excelsheet.xlsm", ReadOnly=1)
xl.Application.Run("excelsheet.xlsm!modulename.macroname")
## xl.Application.Save() # if you want to save then uncomment this line and change delete the ", ReadOnly=1" part from the open function.
xl.Application.Quit() # Comment this out if your excel script closes
del xl
But this is for windows Excell program and I want for the LibreOffice program. Is it possible to do this?
Thanks :)
My preferred way is to put the python script in the Scripts/python subfolder of your LibreOffice user directory. Then add this function at the bottom:
def call_basic_macro():
document = XSCRIPTCONTEXT.getDocument()
frame = document.getCurrentController().getFrame()
ctx = XSCRIPTCONTEXT.getComponentContext()
dispatcher = ctx.ServiceManager.createInstanceWithContext(
'com.sun.star.frame.DispatchHelper', ctx)
url = document.getURL()
macro_call = ('macro:///Standard.Module1.Macro1("%s")' % url)
dispatcher.executeDispatch(frame, macro_call, "", 0, ())
g_exported_scripts=call_basic_macro,
Now run the python script from Writer by going to Tools -> Macros -> Run Macro. Expand My Macros and select the name of the script.
Another way which seems closer to your Excel example is to start a listening instance of LibreOffice with a system call:
start soffice -accept=socket,host=0,port=2002;urp;
I typically do that part in a shell script (batch file on Windows) rather than python. Then in python, get the document context from the instance:
import uno
localContext = uno.getComponentContext()
After that, the code would look similar to what is above. Note that with this approach on Windows, the python.exe included with LibreOffice must be used in order to load the uno module.
A third way is to simply do a system call:
soffice "macro:///Standard.Module1.Macro1()"
For more on this third approach, see https://forum.openoffice.org/en/forum/viewtopic.php?f=20&t=8232.

Open specific file type with Python script?

How can I make a Python script to be a specific file type's (e.g., *.foo) default application? As in, when I double click the file in the Finder / Explorer I want the file to open in the Python script.
Is this possible to do in Win and/or OS X? The application is a PySide app if that matters.
Mac OS X
On Mac OS X you can use Automator to create an application that calls your python app and passes the input file path as a string argument. In the application workflow wizard, add action "Run Shell Script", select Pass input: as as arguments, and in the text box add:
python /path/to/my/app/myapp.py "$#"
The "$#" passes along whatever arguments were in the input (aka the selected file) as strings. As long as your script is set up to deal with the input (sys.argv) as a list of strings (the first one being the python app path), then it will work.
When you save that Automator workflow, it is treated by OS X like any other app, and you can set that app as the default for files of type "*.foo". To associate "*.foo" with that app, right click a .foo file, Get Info, Open with: Other..., choose the app you created in Automator, then click the Change All... button.
Windows
A similar but hopefully less-involved approach might work in Windows. You could probably create a batch file (.bat) with the following:
python C:\path\to\my\app\myapp.py %*
The %* expands to all arguments.
As long as you can associate a file extension with that batch file, then you could do that, and that's your solution. However, I haven't tried this Windows solution, so take it with a grain of salt. The Mac solution, on the other hand, I have tested.
By example, here's a universal solution I wrote for:
1) opening a Windows desktop link (*.URL) that's been copied to a Linux box.
Or
2) opening a Linux .Desktop link that's been copied to a Windows box.
Here's the Python script that handles both cases:
# UseDesktopLink.py
import sys
import webbrowser
script, filename = sys.argv
file_object = open(filename,'r')
for line in file_object:
if line[0:4]=="URL=":
url=line[4:]
webbrowser.open_new(url)
file_object.close()
On Windows, use Scott H's method (via a bat file) to handle the association.
On Linux, right-click a Windows URL file. Choose Properties, and Open With. Click Add to add a new application. Then at the bottom of the "Add Application" window, click "Use a custom command". Then browse to the UseDesktopLink.py file and click Open. But before you click Add, in the textbox below "Use a custom command", put "python " before the filename (without the quotes). Then click Add and Close.
Hope that helps.
Find any file of type foo
right-click -> Get Info or Click on the file icon,then click Get info or click on the file and hit Command+I
In the Open With pane that shows up, select the path to the python binary
Once selected, You can click the change All button
It'll ask for confirmation, just say continue
I found this old question while looking for an answer myself, and I thought I would share my solution. I used a simple c program to direct the arguments to a python script, allowing the python script to stay a script instead of needing to compile it to make things work. Here is my c program:
int main(int argc, char *argv[]){
char cmd[0xFF];
// For me, argv[1] is the location of the file that is being opened. I'm not sure if this is different on other OSes
snprintf(cmd,sizeof cmd,"python YOUR_PYTHON_SCRIPT_HERE.py -a %s", argv[1]);
system(cmd);
return 0;
}
I then compiled the c program and set that as the default application for the file extension.
Then, in the python script YOUR_PYTHON_SCRIPT_HERE.py, I receive the argument like this:
import sys
assert len(sys.argv) > 2 # Breaks if you call the script without the arguments
theFile = " ".join(sys.argv[2:]) # What the c program gives us
print(theFile) # Print it out to prove that it works
theFile will contain the location of the file that is being opened
Get the contents of the file by using:
with open(theFile,"r") as f:
fileContents = f.read()
On Windows:
Right click the file (I used a .docx file for this example)
Select Open with...
From the applications list, select python
Optional: Select the Always use the selected program to open this kind of file.
Note: this will run the contents of the .docx file in context of the python shell. It will immediately close once it is finished evaluating the contents of the file. If you'd like to edit the file in a word processor, perhaps you should download notepad++, and select that application as the default.

Categories

Resources