Jason Scheirer - Jason, I mentioned this issue to you at the Esri UC...and well, it happened again.
I'm having a strange issue with creating Python Add-Ins with comboboxes. I have a pretty simple Python Add-In in which the user can draw a rectangle to select some lines from a specific layer (layer name grabbed from combobox layer list) . By using comtypes and ArcObjects, it will place a text element showing the quadrant bearing of the line at its midpoint in DMS format. However, I am having an issue where the combox.value attribute is getting deleted somehow. This has happened before, and to remedy it I had to delete the addin_assistant folder, re-download it and build the Add-In again from scratch. This fix did not work this time. There is nothing in my code that would delete the value and my code fails here:
print dir(combobox)
layer = combobox.value #point of failure
And my error:
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'df', 'dropdownWidth', 'editable', 'enabled', 'items', 'onFocus', 'refresh', 'width'] # 'value' attribute is missing here, why?
Traceback (most recent call last):
File "C:\Users\calebma\AppData\Local\ESRI\Desktop10.2\AssemblyCache\{01396F93-E4C1-35F1-4056-AE2322E49B90}\LineAngles_addin.py", line 63, in onRectangle
layer = combobox.value
AttributeError: 'ComboBoxClass1' object has no attribute 'value'
Has anyone else experienced this? The problem could be a bad install. I am running 10.2 on a VM. This smells like a corrupted DLL somewhere to me. Here is my full code:
import arcpy
import pythonaddins
import math
import sys
sys.path.append(r'\\Arcserver1\GIS\_Resources\ESRI\Python\BMI_Library')
import arcobjects #custom module
from arcobjects import elements
def makePolyFromExtent(ext, sr):
'''makes an arcpy polygon object from an input extent object.
Returns a polygon geometry object.'''
array = arcpy.Array()
array.add(ext.lowerLeft)
array.add(ext.lowerRight)
array.add(ext.upperRight)
array.add(ext.upperLeft)
array.add(ext.lowerLeft)
return arcpy.Polygon(array, sr)
def get_angle(xy1, xy2):
"""Calculate azimuth angle from two points. (Zero is north.)"""
# function courtesy of Curtis Price
try:
# ArcPy point objects
x1, y1, x2, y2 = xy1.X, xy1.Y, xy2.X, xy2.Y
except:
# xy strings, e.g. "0 0"
x1, y1 = [float(x) for x in xy1.split()]
x2, y2 = [float(x) for x in xy2.split()]
dx, dy = (x2 - x1, y2 - y1)
return 90 - math.degrees(math.atan2(dy, dx))
def quadrant_bearing(line):
"""returns quadrant bearing in DMS"""
deg = '\xb0'
pt1 = line.firstPoint
pt2 = line.lastPoint
angle = abs(get_angle(pt1, pt2) - 180)
d = int(math.floor(angle))
m_float = (angle - d) * 60.0
m = int(m_float)
s = (m_float - m) * 60
return '{0}{1} {2}\' {3:.2f}"'.format(d,deg,m,s)
def midpoint(point_a, point_b):#, sr):
x1 = point_a.X
y1 = point_a.Y
x2 = point_b.X
y2 = point_b.Y
# Find midpoint
x = (x1 + x2) / 2.0
y = (y1 + y2) / 2.0
return (x, y)
class ComboBoxClass1(object):
"""Implementation for LineAngles_addin.combobox (ComboBox)"""
def __init__(self):
self.editable = True
self.enabled = True
self.dropdownWidth = 'W' * 12
self.width = 'W' * 12
def onFocus(self, focused):
if focused:
mxd = arcpy.mapping.MapDocument('current')
df = mxd.activeDataFrame
lyrs = [l.name for l in arcpy.mapping.ListLayers(mxd, '*', df)
if not l.isBroken and l.supports('DATASOURCE')]
self.items = sorted(lyrs)
del mxd
class ToolClass3(object):
"""Implementation for LineAngles_addin.tool (Tool)"""
def __init__(self):
self.enabled = True
self.shape = "Rectangle"
self.cursor = 3
def onRectangle(self, rectangle_geometry):
# get spatial reference
print dir(combobox)
layer = combobox.value
mxd = arcpy.mapping.MapDocument('current')
df = mxd.activeDataFrame
sr = df.spatialReference
# make polygon extent object
poly = makePolyFromExtent(rectangle_geometry, sr)
# get layer's data type
lyr = arcpy.mapping.ListLayers(mxd, layer)[0]
# arcobjects get current app (IApplication interface Pointer)
pApp = arcobjects.GetCurrentApp()
# get dms as text
with arcpy.da.SearchCursor(line, 'SHAPE@') as rows:
for row in rows:
dms = quadrant_bearing(row[0])
# add text element
x,y = midpoint(row[0].firstPoint, row[0].lastPoint) # to place text element at midpoint of line
elements.add_text(pApp, text=dms, name='dms', size=12, bold=True, x=x, y=y, view='data') # add text element to data view
pass
If it were an ArcGIS installation issue it's likely to show up as the add-in not working at all.
Try setting self.value = 'something' in the combobox's __init__. It might just be because it's not initialized, there's no reason it'd ever get deleted.
Thanks Jason, that sounded promising but it did not work. I added a dummy variable to the combobox init, re-ran the makeaddin.py, and re-installed it. To my surprise, the value attribute still did not show up.
class ComboBoxClass1(object):
"""Implementation for LineAngles_addin.combobox (ComboBox)"""
def __init__(self):
self.editable = True
self.enabled = True
self.dropdownWidth = 'W' * 12
self.width = 'W' * 12
self.value = 'something' #dummy variable
in my dir(combobox) return, value is still missing. I have been having really strange issues on my VM lately so I'm hoping it is something related to that. A coworker is running 10.2 on his normal machine (I still have to run 10.0 on my normal machine until we finish up our server upgrade) and stuff works on his machine that fails on my VM. If you want to take a look at it, I can email you the add-in.
I forgot to post this, but I resolved the issue by changing using self in the onFocus method code to this:
def onFocus(self, focused):
if focused:
self.mxd = arcpy.mapping.MapDocument('current')
self.df = self.mxd.activeDataFrame
lyrs = [l.name for l in arcpy.mapping.ListLayers(self.mxd, '*', self.df)
if not l.isBroken and l.isFeatureLayer and
arcpy.Describe(l).shapeType == 'Polyline']
self.items = sorted(lyrs)
del self.mxd