├── test_pro ├── test_pro.gdb │ ├── gdb │ ├── timestamps │ ├── a00000009.spx │ ├── a00000001.gdbtable │ ├── a00000001.gdbtablx │ ├── a00000002.gdbtable │ ├── a00000002.gdbtablx │ ├── a00000003.gdbtable │ ├── a00000004.gdbtable │ ├── a00000004.gdbtablx │ ├── a00000004.horizon │ ├── a00000005.gdbtable │ ├── a00000005.gdbtablx │ ├── a00000006.gdbtable │ ├── a00000006.gdbtablx │ ├── a00000007.gdbtable │ ├── a00000007.gdbtablx │ ├── a00000009.freelist │ ├── a00000009.gdbtable │ ├── a00000009.gdbtablx │ ├── a00000009.horizon │ ├── a00000001.gdbindexes │ ├── a00000003.gdbindexes │ ├── a00000004.gdbindexes │ ├── a00000005.gdbindexes │ ├── a00000006.gdbindexes │ ├── a00000007.gdbindexes │ ├── a00000009.gdbindexes │ ├── a00000001.TablesByName.atx │ ├── a00000007.CatRelTypesByName.atx │ ├── a00000005.CatItemTypesByName.atx │ ├── a00000004.CatItemsByPhysicalName.atx │ ├── a00000007.CatRelTypesByBackwardLabel.atx │ ├── a00000007.CatRelTypesByForwardLabel.atx │ ├── a00000004.spx │ ├── a00000004.FDO_UUID.atx │ ├── a00000006.FDO_UUID.atx │ ├── a00000004.CatItemsByType.atx │ ├── a00000006.CatRelsByType.atx │ ├── a00000006.CatRelsByOriginID.atx │ ├── a00000005.CatItemTypesByUUID.atx │ ├── a00000006.CatRelsByDestinationID.atx │ ├── a00000007.CatRelTypesByUUID.atx │ ├── a00000005.CatItemTypesByParentTypeID.atx │ ├── a00000007.CatRelTypesByDestItemTypeID.atx │ ├── a00000007.CatRelTypesByOriginItemTypeID.atx │ └── a00000003.gdbtablx ├── ImportLog │ └── cf3568e975c14dc4a773bc1c883da06a_Import.xml ├── test_pro.aprx └── test_pro.tbx ├── images └── screenshot_hello.png ├── debugger_test.py ├── .vscode └── launch.json ├── .gitignore ├── LICENSE ├── hello_toolbox.pyt ├── python_toolbox_template.pyt ├── field_update_code.py ├── README.md └── field_update_tool.py /test_pro/test_pro.gdb/gdb: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /test_pro/ImportLog/cf3568e975c14dc4a773bc1c883da06a_Import.xml: -------------------------------------------------------------------------------- 1 | No error, no warning -------------------------------------------------------------------------------- /test_pro/test_pro.aprx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.aprx -------------------------------------------------------------------------------- /test_pro/test_pro.tbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.tbx -------------------------------------------------------------------------------- /images/screenshot_hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/images/screenshot_hello.png -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/timestamps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/timestamps -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000009.spx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000009.spx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000001.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000001.gdbtable -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000001.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000001.gdbtablx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000002.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000002.gdbtable -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000002.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000002.gdbtablx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000003.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000003.gdbtable -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000004.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000004.gdbtable -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000004.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000004.gdbtablx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000004.horizon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000004.horizon -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000005.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000005.gdbtable -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000005.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000005.gdbtablx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000006.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000006.gdbtable -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000006.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000006.gdbtablx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000007.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000007.gdbtable -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000007.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000007.gdbtablx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000009.freelist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000009.freelist -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000009.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000009.gdbtable -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000009.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000009.gdbtablx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000009.horizon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000009.horizon -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000001.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000001.gdbindexes -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000003.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000003.gdbindexes -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000004.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000004.gdbindexes -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000005.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000005.gdbindexes -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000006.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000006.gdbindexes -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000007.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000007.gdbindexes -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000009.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000009.gdbindexes -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000001.TablesByName.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000001.TablesByName.atx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000007.CatRelTypesByName.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000007.CatRelTypesByName.atx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000005.CatItemTypesByName.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000005.CatItemTypesByName.atx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000004.CatItemsByPhysicalName.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000004.CatItemsByPhysicalName.atx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000007.CatRelTypesByBackwardLabel.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000007.CatRelTypesByBackwardLabel.atx -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000007.CatRelTypesByForwardLabel.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wildsong/ArcGIS_Python_Template/HEAD/test_pro/test_pro.gdb/a00000007.CatRelTypesByForwardLabel.atx -------------------------------------------------------------------------------- /debugger_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script is just here to test attaching the VS Code debugger. 3 | Run it from the command line, then connect to it from VSCode. 4 | Set a breakpoint inside the loop and it should stop there. 5 | Step through (F10) the loop a few times. 6 | When you are done you could set loop to False in the debugger 7 | to see it exit the loop and terminate. 8 | """ 9 | import time 10 | import debugpy 11 | debugpy.listen(5678) 12 | 13 | tock = 1 14 | loop = True # Set this to False in the debugger to end the program. 15 | 16 | while loop : 17 | print("Tick", tock) 18 | tock += 1 19 | time.sleep(1) 20 | 21 | print("We're done here.") 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: Attach using Process Id", 9 | "type": "python", 10 | "request": "attach", 11 | "processId": "${command:pickProcess}" 12 | }, 13 | { 14 | "name": "Python: Current File", 15 | "type": "python", 16 | "request": "launch", 17 | "program": "${file}", 18 | "console": "integratedTerminal" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # toolbox xml files are built by ArcGIS 2 | *.xml 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | 59 | # PyBuilder 60 | target/ 61 | 62 | # Some kind of Visual Studio thing? 63 | .vs/ 64 | 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2022 Brian H Wilson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /hello_toolbox.pyt: -------------------------------------------------------------------------------- 1 | """ 2 | This is the simplest python toolbox I could make, to help test your set up. 3 | 4 | @author: Brian Wilson 5 | """ 6 | import arcpy 7 | import time 8 | 9 | class Toolbox(object): 10 | def __init__(self): 11 | self.label = "Hello Toolbox" 12 | self.alias = "HelloToolbox" 13 | self.tools = [Hello_Tool] 14 | 15 | class Hello_Tool(object): 16 | 17 | def __init__(self): 18 | self.label = "Hello Tool" 19 | self.description = "Sends a friendly greeting as a message." 20 | self.canRunInBackground = False 21 | return 22 | 23 | def getParameterInfo(self): 24 | # I have no parameters! 25 | return [] 26 | 27 | def isLicensed(self): 28 | return True 29 | 30 | def updateParameters(self, parameters): 31 | return 32 | 33 | def updateMessages(self, parameters): 34 | return 35 | 36 | def execute(self, parameters, messages): 37 | messages.addMessage("Hello, ArcGIS!") 38 | # Pretend we're doing something. 39 | loop = 5 40 | while loop: 41 | loop -= 1 42 | time.sleep(1) 43 | messages.addMessage("Process completed successfully!") 44 | return 45 | 46 | # Unit test 47 | if __name__ == "__main__": 48 | 49 | class Messenger(object): 50 | def addMessage(self, message): 51 | print(message) 52 | 53 | hello = Hello_Tool() 54 | hello.execute(None, Messenger()) 55 | 56 | # That's all! 57 | -------------------------------------------------------------------------------- /python_toolbox_template.pyt: -------------------------------------------------------------------------------- 1 | """ 2 | Python Toolbox Template (a ".pyt" file) 3 | 4 | @author: Brian Wilson 5 | """ 6 | from __future__ import print_function 7 | import arcpy 8 | 9 | # This is for development, so that you can edit code while running in ArcGIS Pro. 10 | import importlib 11 | import field_update_tool 12 | importlib.reload(field_update_tool) 13 | 14 | # Import all the tool classes that will be included in this toolbox. 15 | from field_update_tool import Field_Update_tool 16 | #from python_tool_template import Sample_Tool_2 17 | 18 | class Toolbox(object): 19 | def __init__(self): 20 | """Define the toolbox (the name of the toolbox is the name of this .pyt file).""" 21 | self.description = """Sample toolbox containing sample tools.""" 22 | 23 | self.label = "My Python Toolbox" 24 | self.alias = "SampleToolbox" # no special characters including spaces! 25 | self.description = """My toolbox containing python tools!""" 26 | 27 | # List of tool classes associated with this toolbox 28 | self.tools = [ 29 | Field_Update_tool, 30 | #Sample_Tool_2 31 | ] 32 | 33 | def list_tools(): 34 | toolbox = Toolbox() 35 | print("toolbox:", toolbox.label) 36 | print("description:", toolbox.description) 37 | print("tools:") 38 | for t in toolbox.tools: 39 | tool = t() 40 | print(' ', tool.label) 41 | print(' description:', tool.description) 42 | for param in tool.getParameterInfo(): 43 | print(' ',param.name,':',param.displayName) 44 | print() 45 | 46 | 47 | if __name__ == "__main__": 48 | # Running this as a standalone script lists information about the toolbox and each tool. 49 | list_tools() 50 | #exit(0) # This causes the toolbox not to load in ArcGIS Pro. Whatever. 51 | 52 | # That's all! 53 | -------------------------------------------------------------------------------- /field_update_code.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script contains the business logic for a sample in the ArcGIS Python Toolbox. 3 | 4 | It shows how to iterate a feature class and update a field with a new value. 5 | You could more easily do this using a call to arcpy.CalculateField_management() 6 | but that's not as interesting an example! 7 | 8 | @author: Brian Wilson 9 | """ 10 | from __future__ import print_function 11 | from collections import namedtuple 12 | from datetime import datetime 13 | from time import sleep 14 | import arcpy 15 | 16 | __version__ = "2020-07-16.1" 17 | 18 | def set_field_value(input_fc, fieldname, value): 19 | """ Update the named field in every row of the input feature class with the given value. """ 20 | 21 | arcpy.AddMessage("Version %s" % __version__) 22 | print("field, value = ", fieldname, value) 23 | 24 | start = 0 25 | step = 1 26 | maxcount = int(arcpy.GetCount_management(input_fc).getOutput(0)) 27 | 28 | arcpy.SetProgressor("step", "Doing serious work here.", start, maxcount, step) 29 | 30 | # We don't use OID here, this just an example 31 | # The updateRow operation is faster if you load only the fields you need, 32 | # in our case that would be specified by 'fieldname'. 33 | fields = ["OID@", fieldname] 34 | 35 | with arcpy.da.UpdateCursor(input_fc, fields) as cursor: 36 | t = 0 37 | for row in cursor: 38 | msg = "Working.. step %d of %d" % (t,maxcount) 39 | print(msg) # This shows up in the IDE Debug Console. 40 | 41 | arcpy.SetProgressorLabel(msg) 42 | 43 | # If there is a type error here, I really expect arcpy 44 | # to throw an error but it does not appear to! 45 | row[1] = value 46 | cursor.updateRow(row) 47 | sleep(.50) # pretend we're doing something so progressor will work. 48 | arcpy.SetProgressorPosition(t) 49 | t += 1 50 | return 51 | 52 | def dump_contents(input_fc): 53 | """ Print the contents of the feature class, this is just a namedtuple sample. """ 54 | fcrow = namedtuple("fcrow", ["oid", "datestamp"]) 55 | with arcpy.da.SearchCursor(input_fc, ["OID@", "datestamp"]) as cursor: 56 | for row in cursor: 57 | feature = fcrow._make(row) 58 | print(feature.oid, feature.datestamp) 59 | return 60 | 61 | # ====================================================================== 62 | 63 | # UNIT TESTING 64 | # You can run this file directly when writing it to aid in debugging. 65 | # For example, "Set as Startup File" when running under Visual Studio. 66 | 67 | if __name__ == '__main__': 68 | arcpy.env.workspace = ".\\test_pro\\test_pro.gdb" 69 | input_fc = "testing_data" 70 | fieldname = "datestamp" 71 | datestring = datetime.datetime.today().strftime("%Y/%m/%d %H:%M:%S") 72 | 73 | arcpy.AddMessage("starting geoprocessing") 74 | set_field_value(input_fc, fieldname, datestring) 75 | 76 | dump_contents(input_fc) 77 | 78 | print("Tests successful!") 79 | 80 | # That's all 81 | -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000004.spx: -------------------------------------------------------------------------------- 1 | @ -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000004.FDO_UUID.atx: -------------------------------------------------------------------------------- 1 | {3BD19A64-EB37-4CCA-BD3D-CEA18B092465}{86A0B8D2-CCAD-41B8-84B5-C8332E3063E0}{D9773CF2-D3DE-4402-8F6F-1A3A6CD96004}& -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000006.FDO_UUID.atx: -------------------------------------------------------------------------------- 1 | {ADAC3799-2174-433E-AC62-49EC6CC45A90}& -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000004.CatItemsByType.atx: -------------------------------------------------------------------------------- 1 | {70737809-852C-4A03-9E22-2CECEA5B9BFA}{C673FE0F-7280-404F-8532-20755DD8FC06}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}& -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000006.CatRelsByType.atx: -------------------------------------------------------------------------------- 1 | {DC78F1AB-34E4-43AC-BA47-1C4EABD0E7C7}& -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000006.CatRelsByOriginID.atx: -------------------------------------------------------------------------------- 1 | {D9773CF2-D3DE-4402-8F6F-1A3A6CD96004}& -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000005.CatItemTypesByUUID.atx: -------------------------------------------------------------------------------- 1 | '$%  2 |  ' & "!#{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{2D0BFE5D-66C0-4536-8534-B9C50771AAE4}{35B601F7-45CE-4AFF-ADB7-7702D3839B12}{37672BD2-B9F3-48C1-89B5-8C43BBBB6D57}{4945A015-D612-448D-AE82-24CEB89508AE}{4ED4A58E-621F-4043-95ED-850FBA45FCBC}{5B966567-FB87-4DDE-938B-B4B37423539D}{5ED667A3-9CA9-44A2-8029-D95BF23704B9}{60EA40CF-2667-45E2-BDFF-7F6892538FE8}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{73718A66-AFB9-4B88-A551-CFFA0AE12620}{74737149-DCB5-4257-8904-B9724E32A530}{76357537-3364-48AF-A4BE-783C7C28B5CB}{767152D3-ED66-4325-8774-420D46674E07}{77292603-930F-475D-AE4F-B8970F42F394}{7771FC7D-A38B-4FD3-8225-639D17E9A131}{787BEA35-4A86-494F-BB48-500B96145B58}{8405ADD5-8DF8-4227-8FAC-3FCADE073386}{8637F1ED-8C04-4866-A44A-1CB8288B3C63}{8C368B12-A12E-4C7E-9638-C9C64E69E98F}{A300008D-0CEA-4F6A-9DFA-46AF829A3DF2}{A3803369-5FC2-4963-BAE0-13EFFC09DD73}{B606A7E1-FA5B-439C-849C-6E9C2481537B}{C29DA988-8C3E-45F7-8B5C-18E51EE7BEB4}{C673FE0F-7280-404F-8532-20755DD8FC06}{CC53CC54-4CCA-43B7-9A9B-64DC59C999BD}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{D4912162-3413-476E-9DA4-2AEFBBC16939}{D86502F9-9758-45C6-9D23-6DD1A0107B47}{D98421EB-D582-4713-9484-43304D0810F6}{DB1B697A-3BB6-426A-98A2-6EE7A4C6AED3}{DC64B6E4-DC0F-43BD-B4F5-F22385DCF055}{DC9EF677-1AA3-45A7-8ACD-303A5202D0DC}{E6302665-416B-44FA-BE33-4E15916BA101}{EBEEE2C9-FA73-4BED-AC7D-AEE7D68AFC80}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}{F8413DCB-2248-4935-BFE9-315F397E5110}{FBDD7DD6-4A25-40B7-9A1A-ECC3D1172447}{FFD09C28-FE70-4E25-907C-AF8E8A5EC5F3}&' -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000006.CatRelsByDestinationID.atx: -------------------------------------------------------------------------------- 1 | {3BD19A64-EB37-4CCA-BD3D-CEA18B092465}& -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000007.CatRelTypesByUUID.atx: -------------------------------------------------------------------------------- 1 |   2 |    {0D10B3A7-2F64-45E6-B7AC-2FC27BF2133C}{17E08ADB-2B31-4DCD-8FDD-DF529E88F843}{20D1F656-14BA-44CB-9A60-58D2D44F7A83}{55D2F4DC-CB17-4E32-A8C7-47591E8C71DE}{583A5BAA-3551-41AE-8AA8-1185719F3889}{5DD0C1AF-CB3D-4FEA-8C51-CB3BA8D77CDB}{5F9085E0-788F-4354-AE3C-34C83A7EA784}{6EEDDDCE-64F9-4549-BAAD-F05A36C205ED}{725BADAB-3452-491B-A795-55F32D67229C}{74DAC6C1-A6F2-4603-88A8-D09BCCFDCF21}{79CC71C8-B7D9-4141-9014-B6373E236ABB}{7D2A7A69-ABD8-4AA7-AC08-9E4DDB86289E}{8DB31AF1-DF7C-4632-AA10-3CC44B0C6914}{908A4670-1111-48C6-8269-134FDD3FE617}{94CD4FC6-307E-430B-A883-5FE68CF19AE8}{95C22AFD-AA36-431D-B195-1779A256C6B2}{A1633A59-46BA-4448-8706-D8ABE2B2B02E}{B32B8563-0B96-4D32-92C4-086423AE9962}{CC28387C-441F-4D7C-A802-41A160317FE0}{CCD6E1C9-9238-40D4-843F-C5DAFD3D2BDE}{D022DE33-45BD-424C-88BF-5B1B6B957BD3}{D088B110-190B-4229-BDF7-89FDDD14D1EA}{DC739A70-9B71-41E8-868C-008CF46F16D7}{DC78F1AB-34E4-43AC-BA47-1C4EABD0E7C7}{E79B44E3-F833-4B12-90A1-364EC4DDC43E}{FB166A8C-C09C-4121-9FA7-CB73406E2944}& -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000005.CatItemTypesByParentTypeID.atx: -------------------------------------------------------------------------------- 1 | '& !"%' # 2 |  ${00000000-0000-0000-0000-000000000000}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{8405ADD5-8DF8-4227-8FAC-3FCADE073386}{8405ADD5-8DF8-4227-8FAC-3FCADE073386}{8637F1ED-8C04-4866-A44A-1CB8288B3C63}{8637F1ED-8C04-4866-A44A-1CB8288B3C63}{D4912162-3413-476E-9DA4-2AEFBBC16939}{D4912162-3413-476E-9DA4-2AEFBBC16939}{D4912162-3413-476E-9DA4-2AEFBBC16939}{D4912162-3413-476E-9DA4-2AEFBBC16939}{FFD09C28-FE70-4E25-907C-AF8E8A5EC5F3}&' -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000007.CatRelTypesByDestItemTypeID.atx: -------------------------------------------------------------------------------- 1 |    2 | {28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{8405ADD5-8DF8-4227-8FAC-3FCADE073386}{8637F1ED-8C04-4866-A44A-1CB8288B3C63}{A300008D-0CEA-4F6A-9DFA-46AF829A3DF2}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{D86502F9-9758-45C6-9D23-6DD1A0107B47}{D98421EB-D582-4713-9484-43304D0810F6}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}& -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000007.CatRelTypesByOriginItemTypeID.atx: -------------------------------------------------------------------------------- 1 |   2 |  {28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{37672BD2-B9F3-48C1-89B5-8C43BBBB6D57}{37672BD2-B9F3-48C1-89B5-8C43BBBB6D57}{4ED4A58E-621F-4043-95ED-850FBA45FCBC}{5B966567-FB87-4DDE-938B-B4B37423539D}{60EA40CF-2667-45E2-BDFF-7F6892538FE8}{60EA40CF-2667-45E2-BDFF-7F6892538FE8}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{73718A66-AFB9-4B88-A551-CFFA0AE12620}{74737149-DCB5-4257-8904-B9724E32A530}{76357537-3364-48AF-A4BE-783C7C28B5CB}{767152D3-ED66-4325-8774-420D46674E07}{7771FC7D-A38B-4FD3-8225-639D17E9A131}{7771FC7D-A38B-4FD3-8225-639D17E9A131}{A3803369-5FC2-4963-BAE0-13EFFC09DD73}{A3803369-5FC2-4963-BAE0-13EFFC09DD73}{CC53CC54-4CCA-43B7-9A9B-64DC59C999BD}{CC53CC54-4CCA-43B7-9A9B-64DC59C999BD}{D86502F9-9758-45C6-9D23-6DD1A0107B47}{D98421EB-D582-4713-9484-43304D0810F6}{EBEEE2C9-FA73-4BED-AC7D-AEE7D68AFC80}{EBEEE2C9-FA73-4BED-AC7D-AEE7D68AFC80}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}& -------------------------------------------------------------------------------- /test_pro/test_pro.gdb/a00000003.gdbtablx: -------------------------------------------------------------------------------- 1 | $  -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ArcGIS_Python_Template 2 | 3 | Template for a Python Toolbox for ESRI ArcGIS Pro. 4 | 5 | Tested with ArcGIS Pro 2.8.2 / Python 3.7.10. 6 | 7 | *2020-03-29 This project has been updated to work with either ArcGIS Desktop or ArcGIS Pro.* 8 | In former days I tested it with ArcGIS Desktop 10.8 / Python 2.7 9 | It *might* still work with ArcGIS Desktop. I have not tested it. 10 | 11 | ## Overview 12 | 13 | This project serves as a starting point when creating ArcGIS Python Tools. 14 | 15 | In the ESRI world, "Python Tool" and "Python Toolbox" have specific 16 | meanings. They were added starting sometime around ArcGIS 10.1. 17 | 18 | You can write a "script tool" in Python but this approach splits apart 19 | the definition of parameters (done in Pro) from the Python and further, 20 | and validator code remains trapped inside the proprietary binary ArcGIS "Toolbox" (.tbx) file where it cannot be maintained. 21 | 22 | With Python Toolboxes, everything is stored in the plain text Python 23 | source files, so it's cleaner and very easy to put into version 24 | control. 25 | 26 | Conceptually, "Python tools" live inside "Python toolboxes" and that's 27 | how they appear in Catalog. There can be many tools in one toolbox. 28 | 29 | In practice, I usually break everything out into separate files, 30 | so that's what I have done in this template. 31 | 32 | This template just shows you how to set up a Python toolbox, it does nothing interesting on its own. 33 | 34 | To use it, copy the repository as a functioning starting point, then add your own code. Github recognizes it as a template; just click their "Use this template" button. 35 | 36 | Of course if you want, you could clone the template, make improvements, and send pull requests to me! 37 | 38 | I use Visual Studio Code to develop and test Python. It's free and has excellent code completion ("Intellisense") and debugging. Therefore I have added the associated project files. 39 | 40 | ### PYT files 41 | 42 | You can add the .pyt extension to VS Code so that it treats them as python instead of text. 43 | 44 | Under File->Preferences->Settings->Files->Associations 45 | I add *.pyt: python. 46 | 47 | ## Files 48 | 49 | **hello_toolbox.pyt** - This is a very minimal toolbox. It just says "hello"! Everything is in this one file. There are no parameters. Just run it. 50 | 51 | **python_toolbox_template.pyt** - This is the complete template for a new Python toolbox. 52 | 53 | **field_update_tool.py** - This is a class defining a tool. It acts as "glue" to connect the "business logic" in field_update_code.py to the toolbox. 54 | 55 | **field_update_code.py** - This is a sample which updates a datestamp field in a feature class. 56 | 57 | **test_pro/** - contains an ArcGIS Pro project for unit testing and development. 58 | Includes sample data. 59 | 60 | The template Esri provides has all the code in one PYT file. This is okay for little tiny tools, in fact you can easily share a toolbox written this way because it's all in one file. With everything in one file though it's hard to debug and hard to maintain. 61 | 62 | My intention is that once you set up the toolbox and tool files, you will seldom touch them. Minor errors in those sections can confuse Catalog, causing your entire toolbox to fail to load. 63 | 64 | Keeping the toolbox code and the tool code in separate files also means you can very easily move tools from one toolbox to another. You just have to change one line in PYT files to add or remove a tool. 65 | 66 | Keeping the code doing the geoprocessing work in a separate file means it can be tested stand-alone in a debugger. You can hard code paths and settings in the unit test section and run the business logic by itself. It's much faster than trying to do everything through ArcGIS Pro, typing in the parameters in over and over. 67 | In real life, sometimes it makes more sense to combine the tool and business logic in one file. 68 | 69 | In ArcGIS Pro, I can edit the code in VS Code and then use "refresh" on the PYT to get Pro to read it. Sometimes I have to refresh twice to get it to reload. 70 | There is a bug in ArcCatalog; to get them to notice changes in the toolbox you have to exit and restart it; this really slows down debugging. If you know a work around ("refresh" does not do it), please tell me. By contrast keeping business logic in separate files means you can update and save that file and then immediately re-run the tool in ArcMap or ArcCatalog. 71 | 72 | ### Hello Toolbox 73 | 74 | The file called called hello_toolbox.pyt is a complete Python toolbox in a single file that will show up in the ArcGIS Catalog, 75 | containing a single tool called "Hello Tool". "Hello Tool" has no parameters. You can run it and then check messages to see its output. 76 | 77 | ![Output of Hello tool](images/screenshot_hello.png) 78 | 79 | ## Unit Tests 80 | 81 | There is a unit test in each Python file. This means you can develop the code in that file independently, running it in a debugger and confirming it does what you expect before putting together all the pieces. 82 | 83 | So for example, you can start by testing field_update_code.py, 84 | and once you are satisfied it runs, move on to field_update_tool.py, 85 | and then finally run python_toolbox.pyt as a standalone script. 86 | 87 | Of course, you could run each from a command line, but you can run it in 88 | Visual Studio Code and watch its operation in the debugger, executing 89 | one line at a time. 90 | 91 | ## Visual Studio Code 92 | 93 | ### ArcGIS Pro set up 94 | 95 | I followed some suggestions found [here, in GIS Stackexchange.](https://gis.stackexchange.com/questions/203380/setting-up-python-arcpy-with-arcgis-pro-and-visual-studio/356487#356487) 96 | 97 | Clone the Esri default Python environment. You can do this from the manager inside ArcGIS Pro or from the command line. The command I used was 98 | 99 | conda create --clone C:/Program\ Files/ArcGIS/Pro/bin/Python/envs/arcgispro-py3 -n arcgispro-vscode 100 | 101 | I renamed the clone arcgispro-vscode. Then make the clone the default in ArcGIS Pro. 102 | Use the cloned environment in Visual Studio Code, adding any additional packages you need. 103 | VS Code wants you to add "autopep8". If you want to use the debugger when running a toolbox in ArcGIS Pro you will also need "debugpy". 104 | 105 | conda install -n arcgispro-vscode autopep8 debugpy 106 | 107 | Another way is to start a clean new conda environment; this will give you the latest available versions of everything, that might not be what you want. 108 | 109 | conda create -c esri -n arcgispro-latest python arcpy arcgis autopep8 debugpy 110 | 111 | ### Selecting Python version in VS Code 112 | 113 | Set the version using Ctl-Shift-P or F1 and then select "Python: Select interpreter". 114 | You should see the conda version(s) that you created above listed. 115 | 116 | ### Debugging a running toolbox with VS Code 117 | 118 | **ArcGIS Pro version 2.8.6 I really want to be able to run a toolbox from inside ArcGIS Pro, and attach a debugger. So far, when I add the debugpy lines, it launches new copies of ArcGIS Pro!!! I will try again when I have Pro 3 installed and see if things are different.** 119 | 120 | Test the VS Code debugger by running the debugger_test.py from a command line, like this: 121 | 122 | conda activate arcgispro-vscode 123 | python debugger_test.py 124 | 125 | You should see it start generating output, one line per second. 126 | 127 | Tick 1 128 | Tick 2 129 | Tick 3 130 | (etc) 131 | 132 | Now in VS Code, you should be able to attach to the running instance of the script. 133 | (Note that the .vscode/launch.json file included with this repository has the set up for this already.) 134 | 135 | Open the file debugger_test.py in VS Code. 136 | 137 | Select the debugger from the left nav bar, and select "Python: Attach using Process ID" in the drop down list. 138 | 139 | Press F5 to start the debugger. It will offer you a list of running processes. You have to pick the Python with the name of the script at the end. 140 | 141 | Set a breakpoint inside the loop, say for instance on line 19. Then wait a few seconds for it to take effect. 142 | 143 | Use F5 to step through the loop a few times or use F10 to step one line at a time. 144 | 145 | If you want, you can reset the value of "loop" to False to see the program leave the loop and exit. 146 | 147 | **So this is as far as I have gotten...** 148 | 149 | ## Resources 150 | 151 | ### Esri 152 | 153 | 2018 video: [Building Geoprocessing Tools With Python: Getting Started](https://www.youtube.com/watch?v=iTZytnBcagQ) 154 | 155 | 2019 slides for [Python: Beyond the Basics](https://proceedings.esri.com/library/userconf/devsummit19/papers/DevSummitPS_51.pdf) The current video link I found is [here](https://www.youtube.com/watch?v=y84onLbW-_M). 156 | 157 | [Defining parameter data types in a python toolbox](https://desktop.arcgis.com/en/arcmap/latest/analyze/creating-tools/defining-parameter-data-types-in-a-python-toolbox.htm) 158 | 159 | [Controlling the progress dialog box](https://desktop.arcgis.com/en/arcmap/latest/analyze/creating-tools/controlling-the-progress-dialog-box.htm) 160 | 161 | Official VSC docs: [Python debugging in VS Code](https://code.visualstudio.com/docs/python/debugging) 162 | 163 | **Note this page references Visual Studio not Visual Studio Code!** but maybe it will help you anyway. 164 | [Debug Python code](https://pro.arcgis.com/en/pro-app/2.8/arcpy/get-started/debugging-python-code.htm) from the Pro docs -------------------------------------------------------------------------------- /field_update_tool.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python code that implements implements an ArcGIS Tool, 3 | to be included in an ArcGIS Python Toolbox. 4 | 5 | @author: Brian Wilson 6 | """ 7 | import os 8 | import arcpy 9 | from datetime import datetime 10 | 11 | # This is for development, so that you can edit code while running in ArcGIS Pro. 12 | import importlib 13 | import field_update_code 14 | importlib.reload(field_update_code) 15 | 16 | from field_update_code import set_field_value 17 | 18 | 19 | class Field_Update_tool(object): 20 | """This class has the methods you need to define 21 | to use your code as an ArcGIS Python Tool.""" 22 | 23 | def __init__(self) -> None: 24 | """Define the tool (tool name is the name of the class).""" 25 | self.label = "Field Update tool" 26 | self.description = """Update a field in a feature class.""" 27 | self.canRunInBackground = False 28 | self.category = "Example" # Use your own category here, or an existing one. 29 | #self.stylesheet = "" # I don't know how to use this yet. 30 | 31 | def getParameterInfo(self) -> list: 32 | """Define parameter definitions. 33 | Refer to https://pro.arcgis.com/en/pro-app/latest/arcpy/geoprocessing_and_python/defining-parameters-in-a-python-toolbox.htm 34 | """ 35 | 36 | # params[0] = feature class 37 | input_fc = arcpy.Parameter(name="input_fc", 38 | displayName="Feature Class (NOTE, contents will be modified!)", 39 | # Using a composite type here means I can 40 | # enter either a feature class or a string into the form. 41 | datatype=["GPFeatureLayer", "DEFeatureClass", "GPString"], 42 | parameterType="Required", # Required|Optional|Derived 43 | direction="Input", # Input|Output 44 | ) 45 | # You can set filters here for example 46 | #input_fc.filter.list = ["Polygon"] 47 | # You can set a default if you want -- this makes debugging a little easier. 48 | input_fc.value = "" 49 | 50 | # params[1] = field name 51 | field = arcpy.Parameter(name="field", 52 | displayName="Name of field that will have the date written into it", 53 | datatype="Field", 54 | parameterType="Required", # Required|Optional|Derived 55 | direction="Input", # Input|Output 56 | ) 57 | # Define this so that the list of field names will be filled in automatically. 58 | field.parameterDependencies = [input_fc.name] 59 | 60 | # params[2] = a date/time thing 61 | datestamp = arcpy.Parameter(name="datestamp", 62 | displayName="A date time string", 63 | datatype="GPDate", 64 | parameterType="Required", # Required|Optional|Derived 65 | direction="Input", # Input|Output 66 | ) 67 | # You can set a default value here. 68 | datestamp.value = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 69 | 70 | # params[3] = a number that has to be in a range 71 | fixedrange = [100,500] 72 | number = arcpy.Parameter(name="a_number", 73 | displayName="A number in the range %s-%s" % (fixedrange[0],fixedrange[1]), 74 | datatype="GPLong", 75 | parameterType="Required", # Required|Optional|Derived 76 | direction="Input", # Input|Output 77 | ) 78 | # You could set a list of acceptable values here for example 79 | number.filter.type = "Range" 80 | number.filter.list = [100,500] 81 | # You can set a default value here. 82 | number.value = 200 83 | 84 | # params[4] = a number that has to be in a range 85 | depnumber = arcpy.Parameter(name="dependent_number", 86 | displayName="a number that's set by the previous field", 87 | datatype="GPLong", 88 | parameterType="Required", # Required|Optional|Derived 89 | direction="Input", # Input|Output 90 | ) 91 | depnumber.value = number.value * 10 92 | 93 | # params[5] = the output 94 | output_fc = arcpy.Parameter(name="output_fc", 95 | displayName="Output feature class", 96 | datatype="DEFeatureClass", 97 | parameterType="Derived", # Required|Optional|Derived 98 | direction="Output", # Input|Output 99 | ) 100 | # This is a derived parameter; it depends on the input feature class parameter. 101 | # You usually use this to define output for using the tool in ESRI models. 102 | output_fc.parameterDependencies = [input_fc.name] 103 | # Cloning tells arcpy you want the schema of this output fc to be the same as input_fc 104 | # See http://desktop.arcgis.com/en/desktop/latest/analyze/creating-tools/updating-schema-in-a-python-toolbox.htm#ESRI_SECTION1_0F3D82FC6ACA421E97AC6D23D95AF19D 105 | output_fc.schema.clone = True 106 | 107 | return [input_fc, field, datestamp, number, depnumber, output_fc] 108 | 109 | def isLicensed(self) -> bool: 110 | """Set whether tool is licensed to execute.""" 111 | return True 112 | 113 | def updateParameters(self, parameters) -> None: 114 | """Modify the values and properties of parameters before internal 115 | validation is performed. This method is called whenever a parameter 116 | has been changed.""" 117 | if parameters[0].altered: 118 | if not arcpy.Exists(parameters[0].value): 119 | parameters[0].setErrorMessage("Feature class could not be opened.") 120 | else: 121 | try: 122 | # Get a new list of fields. (Note the docs say not to use "ListFields" method.) 123 | l = [f.name for f in arcpy.da.Describe(parameters[0].valueAsText)['fields']] 124 | # If the field is not in the list, default to the first field found. 125 | if not parameters[1].valueAsText in l: 126 | parameters[1].value = l[0] 127 | except: 128 | parameters[1].value = "" # Clear out the old field name 129 | parameters[1].setWarningMessage("Could not read the field list.") 130 | 131 | if parameters[1].altered: 132 | # It might be better to only show a list of date fields but this is a demo... 133 | found = False 134 | for f in arcpy.da.Describe(parameters[0].valueAsText)['fields']: 135 | if parameters[1].valueAsText == f.name: 136 | if f.type != 'Date': 137 | parameters[1].setErrorMessage("Selected field has to be a date.") 138 | found = True 139 | if not found: 140 | parameters[1].setErrorMessage("No such field [%s]" % parameters[1].valueAsText) 141 | 142 | if parameters[3].altered: 143 | # When you change the field called "a_number" then this method will 144 | # be called and the next field will change to its value * 10. 145 | parameters[4].value = int(parameters[3].value)*10 146 | 147 | return 148 | 149 | def updateMessages(self, parameters) -> None: 150 | """Modify the messages created by internal validation for each tool 151 | parameter. This method is called after internal validation.""" 152 | 153 | if parameters[2].value == 2: 154 | parameters[2].setWarningMessage("Sample warning, you set the number to 2.") 155 | return 156 | 157 | def execute(self, parameters, messages) -> None: 158 | """This is the code that executes when you click the "Run" button.""" 159 | 160 | # Let's dump out what we know here. 161 | messages.addMessage("This is a test of your tool.") 162 | for param in parameters: 163 | self.describeParameter(messages,param) 164 | 165 | # Get the parameters from our parameters list, 166 | # then call a generic python function. 167 | # 168 | # This separates the code doing the work from all 169 | # the crazy code required to talk to ArcGIS. 170 | 171 | # See http://resources.arcgis.com/en/help/main/10.2/index.html#//018z00000063000000 172 | input_fc = parameters[0].valueAsText 173 | fieldname = parameters[1].valueAsText 174 | datestamp = parameters[2].valueAsText 175 | number = parameters[3].value 176 | depnumber = parameters[4].value 177 | output_fc = parameters[5].valueAsText 178 | 179 | # Okay finally go ahead and do the work. 180 | try: 181 | set_field_value(input_fc, fieldname, datestamp) 182 | messages.addMessage("Okay, I put \"%s\" in the \"%s\" field in \"%s\"." % 183 | (datestamp, fieldname, output_fc)) 184 | except Exception as e: 185 | arcpy.AddError("Fail. %s" % e) 186 | return 187 | 188 | def describeParameter(self, m, p): 189 | m.addMessage("===Parameter=== %s \"%s\"" % (p.name, p.displayName)) 190 | m.addMessage(" altered? %s" % p.altered) 191 | m.addMessage(" value \"%s\"" % p.valueAsText) 192 | m.addMessage(" datatype %s" % p.datatype) 193 | m.addMessage(" filter %s" % p.filter) 194 | 195 | 196 | # ============================================================================= 197 | if __name__ == "__main__": 198 | # This is an example of how you could set up a unit test for this tool. 199 | # You can run this tool from a debugger or from the command line 200 | # to check it for errors before you try it in ArcGIS. 201 | 202 | class Messenger(object): 203 | def addMessage(self, message): 204 | print(message) 205 | 206 | # Get an instance of the tool. 207 | update_datestamp = Field_Update_tool() 208 | # Read its default parameters. 209 | params = update_datestamp.getParameterInfo() 210 | 211 | # Set some test values into the instance 212 | arcpy.env.workspace = '.\\test_pro\\test_pro.gdb' 213 | params[0].value = os.path.join(arcpy.env.workspace, "testing_data") 214 | params[1].value = "datestamp" 215 | params[2].value = "2021/07/08 12:34" 216 | params[3].value = 100 217 | params[5].value = "testing_output" 218 | 219 | # Run it. 220 | update_datestamp.execute(params, Messenger()) 221 | 222 | # That's all 223 | --------------------------------------------------------------------------------