So, I'm a big fan of ESRI's pythonaddins module. Especially the GUI functions in it. Granted, there's not much there. Just a Messagebox and an Open and Save Dialog function but when leveraged properly, those are three functions that actually go a really long way in improving the end user experience.
Unfortunately, ESRI decided to restrict use of the pythonaddins module to the ArcGIS Desktop environment which is really a shame because that means we can't use any of those nifty little GUI functions in standlone scripts or script tools. Now I'm a guy that has quite a few of these standalone scripts that really run the gamut from basic data QA/QC to simple end user scipts to perform data extracts or ETL processes.
So since it didn't look like ESRI was going to make this functionality available to me outside of ArcGIS for Desktop, I decided to take a shot at recreating the functions using native python libraries. I should caveat that I'm only interested in the MessageBox, OpenDialog and SaveDialog functions. It seemed to me that there would really be no utility in trying to recreate the GetSelectedTOCLayerOrDataFrame function since you would have to be in an ArcMap session already for that to be of any use. As for the GPToolDialog function, I seriously doubt it's even possible to raise a GPTool Dialog outside of the ArcGIS for Desktop environment. If someone knows of some wizardry to accomplish that feat, please let me know. I would love to see it.
pythonaddins.MessageBox() = MessageBox
I'm actually rather proud of this one. Not only does it behave exactly as the pythonaddins.MessageBox function, but I was also able to add an additional parameter to allow for the inclusion of MessageBox icons to give a better visual indicator of the message's nature.
def MessageBox(Title, Message, ButtonStyle=0, MessageboxType="NOICON"): """ Raises a custom messagebox with the given title and message with the specified ButtonStyle and MessageboxType Value ButtonType 0 OK (Default) 1 OK | Cancel 2 Abort | Retry | Ignore 3 Yes | No | Cancel 4 Yes | No 5 Retry | No 6 Cancel | Try Again | Continue Value MessageboxType NOICON No Icon (Default) INFO Information Icon QUESTION Question Mark Icon WARNING Warning Exclamation ERROR Error Icon Returns an integer value indicating the button in the Messagebox pressed by the user Return Value Button Pressed 1 OK 2 Cancel 3 Abort 4 Retry 5 Ignore 6 Yes 7 No 10 Try Again 11 Continue Usage: MessageBox("RUIN DAY?", "Would you ArcGIS to ruin your day?", 4, "QUESTION") """ if MessageboxType == "NOICON": MB_STYLE = 0x00 elif MessageboxType == "INFO": MB_STYLE = 0x40 elif MessageboxType == "QUESTION": MB_STYLE = 0x20 elif MessageboxType == "WARNING": MB_STYLE = 0x30 elif MessageboxType == "ERROR": MB_STYLE = 0x10 else: raise Exception("Value given for 'MessageboxType' parameter is not valid") if ButtonStyle not in range(0,7): raise Exception("Value given for 'ButtonStyle' parameter is not valid") import ctypes messagebox = ctypes.windll.user32.MessageBoxA(0, Message, Title, ButtonStyle | MB_STYLE) return messagebox
pythonaddins.OpenDialog = selectFileDialog
This one was a bit trickier but not really that bad. The nuance is the file filter. You have to pass in a tupled list of filetype descriptions and extensions which in turn, populates the combobox to the right of the FileName text box. If you don't provide an input for the InitialDirectory, it defaults to the user's My Documents folder. I would have preferred to use ctypes here as well but couldn't find any examples that were less than 100 lines so I switched to Tk. No crashes yet, so fingers crossed!
def selectFileDialog(FileTypes, InitialDirectory, DialogTitle): """ Raises a standard File Browser dialog and returns the absolute path of the file selected by the user in the dialog. Files displayed in the dialog can be filtered by passing a tuple of lists into the FileTypes parameter where the value at index 0 of each list is the filetype name and the value at index 1 of each list is the filetype extension. Usage: selectFileDialog([("Layer Package", "*lpk"), ("Shapefile", "*.shp")], r"C:\Temp", "Select a file") """ import os if InitialDirectory == "": InitialDirectory = os.path.expanduser('~\Documents') if DialogTitle == "": DialogTitle = "Select a file" import Tkinter, tkFileDialog root = Tkinter.Tk() root.withdraw() FileToOpen = tkFileDialog.askopenfilename(filetypes=FileTypes, initialdir=InitialDirectory, title=DialogTitle) return FileToOpen
I'd like to figure out how to get rid of that stupid Tk icon in the title bar and browse inside the Geodatabase.
pythonaddins.SaveDialog = saveFileDialog
This one is almost identical to OpenDialog except that instead of filtering files, we have the ability to automatically append a particular extension to the user's input. It's also possible to pass in a tupled list of filetype descriptions and extension which would then populate the Save As Filetype Dropdown, but again the only thing that is doing is filtering the files displayed. It's actually the FileExtension parameter that is appending the extension to the return value.
def saveFileDialog(FileExtension, InitialDirectory, DialogTitle): """ Raises a standard File Save dialog and returns the absolute path of the file given by the user in the dialog. An extension can automatically be appended to the end of the return value by specifying the extension type in the 'FileExtension' parameter. Usage: selectFileDialog(".lyr", r"C:\Temp", "Save a file") """ if not FileExtension: raise Exception("File extension is a required parameter") if InitialDirectory == "": import os InitialDirectory = os.path.expanduser('~\Documents') if DialogTitle == "": DialogTitle = "Save As" import Tkinter, tkFileDialog root = Tkinter.Tk() root.withdraw() FileToSave = tkFileDialog.asksaveasfilename(defaultextension=FileExtension, initialdir=InitialDirectory, title=DialogTitle) return FileToSave
So now you're probably thinking to yourself, that's neat but the pythonaddins module already does all of this.
That's true. The catch is the pythonaddins module will only do these things inside of the ArcGIS for Desktop environment. You cannot use it in Standalone Scripts or Script Tools. These custom functions however, should work just fine outside of ArcGIS without any third party libraries whatsoever.