Python add in- update two fields in one feature based on same fields of another

5653
43
Jump to solution
04-14-2014 11:10 AM
AndrewMosley
New Contributor
I'm totally new to making add ins and quite novice at python itself but I have a script that executes perfectly as a script tool or simply in the python window of ArcMap. I want to make this a quick button click so I looked into making an add-in toolbar with a button.

Just to get the hang of making the addin i made a print "Hello World!" that works as expected.

However, it seems something is going wrong with my code when put into the add-in. I can only assume I'm missing some syntax due to my inexperience with python.

The situation here is I have a percels layer and an address points layer, which both contain a PIN and TMS number. I simply want to have the function select the address points within the parcel geometry, find the value of PIN and TMS from the parcel layer's attribute table, and update the same fields in the address points' attribute table. Again, the script I've written executes perfectly inside the python console in ArcMap but not as an add-in. Thanks for any hints you all might have!

So here's what I've got:

import arcpy import pythonaddins  class BlueBird(object):     """Implementation for BlueBird.button (Button)"""     def __init__(self):         self.enabled = True         self.checked = False     def onClick(self): arcpy.SelectLayerByLocation_management("Address Points","COMPLETELY_WITHIN","Parcels")  #insert if statement checking that only one parcel is selected to prevent selecting all address points  with arcpy.da.SearchCursor("Parcels",'PIN') as cursor:  for row in cursor:   storedPIN = row[0] with arcpy.da.UpdateCursor("Address Points",'PIN') as cursor:  for row in cursor:   row[0] = storedPIN   cursor.updateRow(row)  del cursor  with arcpy.da.SearchCursor("Parcels",'TMS') as cursor:  for row in cursor:   storedTMS = row[0] with arcpy.da.UpdateCursor("Address Points",'TMS') as cursor:  for row in cursor:   row[0] = storedTMS   cursor.updateRow(row)  del cursor, storedPIN, storedTMS
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
RichardFairhurst
MVP Honored Contributor
That is what I thought.  The add-in does not recognize the current map by defualt like a script tool or the python window does.  You have to explicitly tell the add-in to connect to the current map and walk it through the connections to each layer in the map before you can make them work.  In other words, "Address Points" and "Parcels" do not exist as far as the add-in is concerned until it is explicitly connected to the map.  Try this code instead:

import arcpy import pythonaddins  class BlueBird(object):     """Implementation for BlueBird.button (Button)"""     def __init__(self):         self.enabled = True         self.checked = False     def onClick(self):         # Get the current map document and the first data frame.         mxd = arcpy.mapping.MapDocument('current')         df = arcpy.mapping.ListDataFrames(mxd)[0]         for lyr in arcpy.mapping.ListLayers(mxd, "", df):             if lyr.name == "Address Points":                 addressLayer = lyr             if lyr.name == "Parcels":                 parcelsLayer = lyr         arcpy.SelectLayerByLocation_management(addressLayer,"COMPLETELY_WITHIN",parcelsLayer)

View solution in original post

0 Kudos
43 Replies
RichardFairhurst
MVP Honored Contributor
Does it show up if you just try the following?  When something won't work tear it back down to a much more basic point where something works and then rebuild it one logical portion at a time until you find what broke it.

import arcpy
import pythonaddins

class BlueBird(object):
    """Implementation for BlueBird.button (Button)"""
    def __init__(self):
        self.enabled = True
        self.checked = False
    def onClick(self):
        arcpy.SelectLayerByLocation_management("Address Points","COMPLETELY_WITHIN","Parcels")
0 Kudos
AndrewMosley
New Contributor
No dice with the corrected indentation. Still just loads the toolbar with that lovely "missing" icon indicating syntax problems.
0 Kudos
RichardFairhurst
MVP Honored Contributor
Does it show up if you just try the following?  When something won't work tear it back down to a much more basic point where something works and then rebuild it one logical portion at a time until you find what broke it.

import arcpy
import pythonaddins

class BlueBird(object):
    """Implementation for BlueBird.button (Button)"""
    def __init__(self):
        self.enabled = True
        self.checked = False
    def onClick(self):
        arcpy.SelectLayerByLocation_management("Address Points","COMPLETELY_WITHIN","Parcels")


I meant to copy and paste this rather than edit my last post, so sorry for making the thread a little confusing.
0 Kudos
AndrewMosley
New Contributor
Well that broke it straight away!

My dummy I mentioned earlier that I was using to get the process of creating an addin had this code, not to dissimilar, and functioned:

import arcpy
import pythonaddins

class BlueBird(object):
    """Implementation for BlueBird.button (Button)"""
    def __init__(self):
        self.enabled = True
        self.checked = False
    def onClick(self):
        print "Hello world!"


Am I wrong in thinking that the python interpreter is not identifying with arcpy module commands?
I'm coming to this conclusion due to that fact that the above worked fine, but a simple arcpy command is not recognized.

If so, what might be the reason for this?
0 Kudos
RichardFairhurst
MVP Honored Contributor
That is what I thought.  The add-in does not recognize the current map by defualt like a script tool or the python window does.  You have to explicitly tell the add-in to connect to the current map and walk it through the connections to each layer in the map before you can make them work.  In other words, "Address Points" and "Parcels" do not exist as far as the add-in is concerned until it is explicitly connected to the map.  Try this code instead:

import arcpy import pythonaddins  class BlueBird(object):     """Implementation for BlueBird.button (Button)"""     def __init__(self):         self.enabled = True         self.checked = False     def onClick(self):         # Get the current map document and the first data frame.         mxd = arcpy.mapping.MapDocument('current')         df = arcpy.mapping.ListDataFrames(mxd)[0]         for lyr in arcpy.mapping.ListLayers(mxd, "", df):             if lyr.name == "Address Points":                 addressLayer = lyr             if lyr.name == "Parcels":                 parcelsLayer = lyr         arcpy.SelectLayerByLocation_management(addressLayer,"COMPLETELY_WITHIN",parcelsLayer)
0 Kudos
AndrewMosley
New Contributor
That makes such an enormous amount of sense...
I'll give it a go in the morning!
0 Kudos
RichardFairhurst
MVP Honored Contributor
That makes such an enormous amount of sense...
I'll give it a go in the morning!


Avoid group layers when trying this code.  Group layers need special processing to have the tool see the layers inside of them.
0 Kudos
AndrewMosley
New Contributor
Now it works beautifully!

I'm glad that not knowing to explicitly point out the mxd and df was such an easy fix and applies to the other 4 tools I have written as well (that also now function properly!).

Thank you for all of the guidance!
0 Kudos
AndrewMosley
New Contributor
I hoped I could pick your brain a little bit more.
The script as I have it now does what it's supposed to, but only if one and only one parcel polygon is selected.
If multiple polygons are selected, it will simply copy the PIN and TMS fields from the first parcel to all address points within the selection (not good!).
If no polygons are selected, it will select all address points in the layer (yikes!) and behave similarly to above.

So my question is two-fold here:
a) How can we safeguard against hitting the button with no parcels selected and the script running on all address points in the layer?
Given a number of selected polygons (x), if (x > 0) then perform this block of script, else end?

b) Given a number of selected polygons (x > 0), how can I make the script iterate and apply appropriate PIN numbers?
I thought of something like:

import arcpy
import pythonaddins

class BlueBird(object):
    """Implementation for BlueBird.button (Button)"""
    def __init__(self):
        self.enabled = True
        self.checked = False
    def onClick(self):
        #Get the current map document and the first data frame.
        mxd = arcpy.mapping.MapDocument('current')
        df = arcpy.mapping.ListDataFrames(mxd)[0]
        for lyr in arcpy.mapping.ListLayers(mxd, "", df):
            if lyr.name == "Address Points":
                addressLayer = lyr
            if lyr.name == "Parcels":
                parcelslayer = lyr

       #make sure user has at least 1 parcel selected, else end
        if selectedFeatures > 0:
              
              #select by location outside of loop to save time

              arcpy.SelectLayerByLocation_management("Address Points","COMPLETELY_WITHIN","Parcels")          

             #create loop to iterate through number of selected parcels
              selectedFeatures = arcpy.MakeFeatureLayer_Management("Parcels", "selected")
              for rows in selectedFeatures:

              with arcpy.da.SearchCursor("selected",'PIN') as cursor:
                    for row in cursor:
                    storedPIN = row[0]
              with arcpy.da.UpdateCursor("Address Points",'PIN') as cursor:
                   for row in cursor:
                       row[0] = storedPIN
                       cursor.updateRow(row)

              del cursor

             with arcpy.da.SearchCursor("selected",'TMS') as cursor:
                   for row in cursor:
                       storedTMS = row[0]
             with arcpy.da.UpdateCursor("Address Points",'TMS') as cursor:
                   for row in cursor:
                       row[0] = storedTMS
                       cursor.updateRow(row)

             del cursor, storedPIN, storedTMS

       #delete selected layer after looping finished
       del selected

       else:
             MessageBoxCommand(?) "Please select at least one parcel"


I'm sure the above is all sorts of a mess, apologies.
0 Kudos