I'd like to assign path to some file (let's say it's a log file) in a script tool as a parameter. This file may already exist (some information might have been written and I don't want to lose it) or may not (then I want to create it).
Now - if I set Direction to Input in Tool Properties -> Parameters - then it is only possible to point to existing file (it won't allow me to create a new name). If I set Direction to Output then I can create new file, but if I point to old one, it will automatically overwrite it (not what I want as I want to retain the contents of the file)
Is there any smart way to make the tool parameter to behave the way I want?
Solved! Go to Solution.
Matrix glitched and the JeffK account was deleted...
Ok, sure. Didn't know that the Folder/File dialog was a requirement but with that, get the folder first using the Folder Datatype, then list the files for folder selected through the ToolValidator class. The user can select one from the list if it does exists or type in a file name if it doesn't exist. New files will have to be typed in- how would you select something that doesn't exist? You can include/exclude the returned files list by extension or name in the ToolValidator to narrow the selection down.
import os
class ToolValidator:
# Class to add custom behavior and properties to the tool and tool parameters.
def __init__(self):
# set self.params for use in other function
self.params = arcpy.GetParameterInfo()
self.fileNames = []
def initializeParameters(self):
# Customize parameter properties.
# This gets called when the tool is opened.
return
def updateParameters(self):
# Modify parameter values and properties.
# This gets called each time a parameter is modified, before
# standard validation.
if self.params[0].altered:
fileFilter = self.params[1].filter # <- change this param to the country list parameter index.
# use list comprehension and set to return a distinct list of file names.
self.fileNames = [filenames for filenames in os.listdir(self.params[0].valueAsText) if os.path.isfile(os.path.join(self.params[0].valueAsText, filenames))]
fileFilter.list = self.fileNames
if self.params[1].altered:
if self.params[1].valueAsText not in self.fileNames:
self.fileNames.append(self.params[1].valueAsText)
fileFilter.list = self.fileNames
return
def updateMessages(self):
# Customize messages for the parameters.
# This gets called after standard validation.
return
def isLicensed(self):
# set tool isLicensed.
return True
def postExecute(self):
# This method takes place after outputs are processed and
# added to the display.
return
import os
"""
Script documentation
- Tool parameters are accessed using arcpy.GetParameter() or
arcpy.GetParameterAsText()
- Update derived parameter values using arcpy.SetParameter() or
arcpy.SetParameterAsText()
"""
import arcpy
def script_tool(filePath, fileName):
"""Script code goes below"""
arcpy.AddMessage(f'param1: {filePath} param2: {fileName}')
outPath = os.path.join(filePath, fileName)
file_type = 'w' if not os.path.exists(outPath) else 'a'
with open(outPath, file_type) as tFile:
tFile.write(f'hello dude\n')
if __name__ == "__main__":
filePath = arcpy.GetParameterAsText(0) if arcpy.GetParameterAsText(0) else None
fileName = arcpy.GetParameterAsText(1) if arcpy.GetParameterAsText(1) else None
script_tool(filePath, fileName)
You need to handle that in your script logic. Set the parameter to input, and use it the code
import arcpy
def script_tool(fileName, filePath):
"""Script code goes below"""
file_type = 'w' if not os.path.exists(filePath) else 'a'
with open(filePath, file_type) as tFile:
tFile.write(f'hello dude {fileName}\n')
if __name__ == "__main__":
fileName = arcpy.GetParameterAsText(0)
filePath = arcpy.GetParameterAsText(1)
script_tool(fileName, filePath)
I am aware that I'll have to handle file creation or appending in script. My question is related to parameter Data Type. I'd like to use Data Type = File (as shown in the screenshot) so that I can take advantage of ArcGIS file selecting window and logic (i.e. I can also filter files by extension). In your example you suggest separate parameters for fileName and filePath (but it looks like filePath contains full path to the file, including its name, and fileName is just used to write the data). What Data Type would you suggest for filePath parameter? Of course using string Data Type would be a workaround - but definitely not a smart one - as there would be no window dialog.
Matrix glitched and the JeffK account was deleted...
Ok, sure. Didn't know that the Folder/File dialog was a requirement but with that, get the folder first using the Folder Datatype, then list the files for folder selected through the ToolValidator class. The user can select one from the list if it does exists or type in a file name if it doesn't exist. New files will have to be typed in- how would you select something that doesn't exist? You can include/exclude the returned files list by extension or name in the ToolValidator to narrow the selection down.
import os
class ToolValidator:
# Class to add custom behavior and properties to the tool and tool parameters.
def __init__(self):
# set self.params for use in other function
self.params = arcpy.GetParameterInfo()
self.fileNames = []
def initializeParameters(self):
# Customize parameter properties.
# This gets called when the tool is opened.
return
def updateParameters(self):
# Modify parameter values and properties.
# This gets called each time a parameter is modified, before
# standard validation.
if self.params[0].altered:
fileFilter = self.params[1].filter # <- change this param to the country list parameter index.
# use list comprehension and set to return a distinct list of file names.
self.fileNames = [filenames for filenames in os.listdir(self.params[0].valueAsText) if os.path.isfile(os.path.join(self.params[0].valueAsText, filenames))]
fileFilter.list = self.fileNames
if self.params[1].altered:
if self.params[1].valueAsText not in self.fileNames:
self.fileNames.append(self.params[1].valueAsText)
fileFilter.list = self.fileNames
return
def updateMessages(self):
# Customize messages for the parameters.
# This gets called after standard validation.
return
def isLicensed(self):
# set tool isLicensed.
return True
def postExecute(self):
# This method takes place after outputs are processed and
# added to the display.
return
import os
"""
Script documentation
- Tool parameters are accessed using arcpy.GetParameter() or
arcpy.GetParameterAsText()
- Update derived parameter values using arcpy.SetParameter() or
arcpy.SetParameterAsText()
"""
import arcpy
def script_tool(filePath, fileName):
"""Script code goes below"""
arcpy.AddMessage(f'param1: {filePath} param2: {fileName}')
outPath = os.path.join(filePath, fileName)
file_type = 'w' if not os.path.exists(outPath) else 'a'
with open(outPath, file_type) as tFile:
tFile.write(f'hello dude\n')
if __name__ == "__main__":
filePath = arcpy.GetParameterAsText(0) if arcpy.GetParameterAsText(0) else None
fileName = arcpy.GetParameterAsText(1) if arcpy.GetParameterAsText(1) else None
script_tool(filePath, fileName)
Thank you for the code, it works excatly as I wish. Below is the code for ToolValidator with small modification - I wanted to filter files by extensions so I introduced self.extensionsFilter list defined in __init__ function to achieve this.
import os
class ToolValidator:
# Class to add custom behavior and properties to the tool and tool parameters.
def __init__(self):
# set self.params for use in other function
self.params = arcpy.GetParameterInfo()
self.fileNames = []
self.extensionsFilter=['.txt','.log'] #list of allowed extensions should be with dots and in lowercase.
#It can be also empty list, no filtering will be applied then.
def initializeParameters(self):
# Customize parameter properties.
# This gets called when the tool is opened.
return
def updateParameters(self):
# Modify parameter values and properties.
# This gets called each time a parameter is modified, before
# standard validation.
if self.params[0].altered:
fileFilter = self.params[1].filter #
# use list comprehension and set to return a distinct list of file names.
if len (self.extensionsFilter)>0: #only files matching extension list should be returned
self.fileNames = [filename for filename in os.listdir(self.params[0].valueAsText)
if os.path.isfile(os.path.join(self.params[0].valueAsText, filename))
and os.path.splitext(filename)[1].lower() in self.extensionsFilter ]
else: #all files should be returned
self.fileNames = [filename for filename in os.listdir(self.params[0].valueAsText)
if os.path.isfile(os.path.join(self.params[0].valueAsText, filename))]
fileFilter.list = self.fileNames
if self.params[1].altered:
if self.params[1].valueAsText not in self.fileNames:
self.fileNames.append(self.params[1].valueAsText)
fileFilter.list = self.fileNames
return
def updateMessages(self):
# Customize messages for the parameters.
# This gets called after standard validation.
return
# def isLicensed(self):
# # set tool isLicensed.
# return True
# def postExecute(self):
# # This method takes place after outputs are processed and
# # added to the display.
# return