Python Addin – Adding Items Dynamically into One Python Addin Combobox from Another

arcpycombo-boxpython-addin

I have two combo boxes. I am trying to add items dynamically into the second combobox from the first combobox's onSelChange(self, selection) event.
due to some reason, this is not working.

Here is my sample code:

import arcpy
import pythonaddins

class ComboBoxClass1(object):
    """Implementation for TestCombo_addin.combobox (ComboBox)"""
    def __init__(self):
        self.items = ["cb1item1", "cb1item2"]
        self.editable = True
        self.enabled = True
        self.dropdownWidth = 'WWWWWW'
        self.width = 'WWWWWW'
        self.cb2=ComboBoxClass2()

    def onSelChange(self, selection):
        pythonaddins.MessageBox(selection,"Message",0)
        self.cb2.items.append(selection)
        self.cb2.refresh()
    def onEditChange(self, text):
        pass
    def onFocus(self, focused):
        pass
    def onEnter(self):
        pass
    def refresh(self):
        pass

class ComboBoxClass2(object):
    """Implementation for TestCombo_addin.combobox_1 (ComboBox)"""
    def __init__(self):
        self.items = ["cb2item1", "cb2item2"]
        self.editable = True
        self.enabled = True
        self.dropdownWidth = 'WWWWWW'
        self.width = 'WWWWWW'

    def onSelChange(self, selection):
        pass
    def onEditChange(self, text):
        pass
    def onFocus(self, focused):
        pass
    def onEnter(self):
        pass
    def refresh(self):
        pass

Best Answer

I think setting a hook to a class instance to be accessed through a class property (as opposed to a property of an instance of that class) is bad practice and while it works fine here will cause you grief if you use this construct in other code.

E.g. If you write a class and create more than one instance of it, the hook property of the class will get overwritten each time an instance is created.

What I do is use the instances of the classes, not the classes themselves.

For example, in config.xml you will have something like:

<?xml version="1.0"?>
<ESRI.Configuration etc...>
  <etc...>
  <AddIn language="PYTHON" library="ComboBox_addin.py" namespace="ComboBox_addin">
    <ArcMap>
      <Commands>
          <ComboBox caption="CB1" category="Blah" class="ComboBoxClass1" id="ComboBox_addin.combobox1" etc...>etc...</ComboBox>
          <ComboBox caption="CB2" category="Blah" class="ComboBoxClass2" id="ComboBox_addin.combobox2" etc...>etc...</ComboBox>
      </Commands>
      <Extensions></Extensions>
      <Toolbars>
        <Toolbar caption="Blah blah" category="Blah" id="ComboBox_addin.toolbar" showInitially="true">
          <Items>
            <ComboBox refID="ComboBox_addin.combobox1"/>
            <ComboBox refID="ComboBox_addin.combobox2"/>
          </Items>
        </Toolbar>
      </Toolbars>
      <Menus></Menus>
    </ArcMap>
  </AddIn>
</ESRI.Configuration>

Note the id and refID are the same. These will refer to instances of these classes.

Then you just replace references to ComboBoxClass2 to combobox2 in your code:

import arcpy
import pythonaddins

class ComboBoxClass1(object):
    """Implementation for ComboBox_addin.combobox1 (ComboBox)"""
    def __init__(self):
        arcpy.env.workspace = r"C:\polygeo\QGIS"
        self.items = arcpy.ListFeatureClasses()
        self.editable = True
        self.enabled = True
        self.dropdownWidth = 'WWWWWWWWWWWWWWWWWWWWWWWWWWWW'
        self.width = 'WWWWWWWWWWWWWWWWWWWWWWWWWWWW'
    def onSelChange(self, selection):
        combobox2.items = [x.name for x in arcpy.ListFields(selection)] # <================
    def onEditChange(self, text):
        pass
    def onFocus(self, focused):
        pass
    def onEnter(self):
        pass
    def refresh(self):
        pass

class ComboBoxClass2(object):
    """Implementation for ComboBox_addin.combobox2 (ComboBox)"""
    def __init__(self):
        self.items = []
        self.editable = True
        self.enabled = True
        self.dropdownWidth = 'WWWWWWWWWWWW'
        self.width = 'WWWWWWWWWWWW'

    def onSelChange(self, selection):
        pass
    def onEditChange(self, text):
        pass
    def onFocus(self, focused):
        pass
    def onEnter(self):
        pass
    def refresh(self):
        pass

If you run into a NameError (an issue with earlier ArcGIS versions, not sure if still a problem), put the following at the bottom of ComboBox_addin.py: :

##Workaround for 'NameError: global name 'combobox2' is not defined' - https://geonet.esri.com/thread/76833
combobox1 = ComboBoxClass1()
combobox2 = ComboBoxClass2()
Related Question