Tuesday, 31 March 2015

arcgis desktop - Alternative to self.Destroy() for wxPython in Arcmap?


I have created a Python Add-in for ArcMap 10.4 that involves a wxPython UI to display upon a click on a map. The UI is populated with default values as well as a combobox displaying the layers on the map. It works perfectly the first time it is run, but the second time, it does not update to show the current layers and displays the last values entered rather than the default values I have specified.


I believe this to be because self.Destroy does not work with wxPython in ArcMap. I had previously worked around this by using self.Show(False), like an example I found here. However, I believe that when being run more than once, the script is only changing the visibility of the UI back to 'True' and not actually opening a new UI.


So, is there any alternative to using self.Destroy() to get rid of my UI?


Here is an example of my script:


import arcpy
import pythonaddins
import wx


class ToolClass2(object):
"""Implementation for DesignTool.tool (Tool)"""
dlg= None
def __init__(self):
self.enabled = True
self.shape = "NONE" # Can set to "Line", "Circle" or "Rectangle" for interactive shape drawing and to activate the onLine/Polygon/Circle event sinks.
self.cursor=3 #set cursor to crosshair
def onMouseDownMap(self, x, y, button, shift):
global laylist

laylist=[]
mxd=arcpy.mapping.MapDocument("CURRENT")
for lyr in arcpy.mapping.ListLayers(mxd):
laylist.append(lyr.name)
del mxd
if self.dlg is None:
self.dlg = TestDialog()
else:
self.dlg.Show(True)
return


class TestDialog(wx.Frame):
def __init__(self):
wxStyle = wx.CAPTION | wx.RESIZE_BORDER | wx.MINIMIZE_BOX |wx.CLOSE_BOX | wx.SYSTEM_MENU | wx.CB_DROPDOWN
wx.Frame.__init__(self, None, -1, "Design Form", style=wxStyle, size=(330, 370))
self.SetMaxSize((330, 370))
self.SetMinSize((330, 370))
self.Bind(wx.EVT_CLOSE, self.OnClose)
panel = wx.Panel(self, -1)
wx.StaticText(panel, -1, "Choose Layer:", pos=(8,64))

self.LayerCombo = wx.ComboBox(panel, -1, value=laylist[1], pos=(180, 64), size=(120,21), choices=laylist)
self.Bind(wx.EVT_BUTTON, self.OnSet, id=self.btnSet.GetId())
self.Show(True)

def OnClose(self, event):
self.Show(False) # self.Destroy() doesn't work

def OnSet(self, event):
Layerpath= str(self.LayerCombo.GetValue())
self.Show(False)


app = wx.PySimpleApp()
app.MainLoop()

Answer



I think you are correct regarding the script only changing the UI. This is because the dlg attribute of your ToolClass2 is always pointing to the same instance of the TestDialog UI class.


When you first run the tool, the TestDialog instance is created and your Add-in works as intended. Next time, since self.dlg is not None, your else clause just shows the current instance and values.


One suggestion to fix this would be to ensure a new instance of TestDialog is created each time:


def onMouseDownMap(self, x, y, button, shift):
global laylist
laylist=[]

mxd=arcpy.mapping.MapDocument("CURRENT")
for lyr in arcpy.mapping.ListLayers(mxd):
laylist.append(lyr.name)
del mxd
self.dlg = TestDialog() # always create a new instance

Or you could stick with this one instance of TestDialog but make sure that when you close it you also reset the values:


def OnClose(self, event):  
self.Show(False) # self.Destroy() doesn't work
# Now reset your values


Hope this helps.


No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...