When I run certain scripts (contains tkinter or similar python packages) ArcGIS crashes. When I load or copy/paste the same code and run it in a Python window it runs without error.

Is there a problem in ArcGIS? I am using 10.4.1.

  • Thank you, But I face the same problem when I used "easygui" which is also a GUI for the Python within ArcGIS Oct 1 '16 at 16:20
  • 1
    I would think (based on @RichardFairhurst's comment) that your issues with EasyGUI are for the same reason - they likely conflict with ArcGIS as ArcGIS has its own GUI
    – Midavalo
    Oct 1 '16 at 18:48

Tkinter is not compatible with any version of Arcmap desktop. As a script you are running pure python, and not using the ArcMap desktop application at all, which is why you are seeing different behavior. Esri has determined that tkinter and Arcmap desktop conflict in their messaging designs and they will not resolve the conflict, so tkinter is not supported while using desktop. As far as I know there is no compatible Python GUI that can be used within an ArcMap desktop session. At the User conferences, Esri staff has always answered that the only "GUI" for Python is a geoprocessing tool dialog connected to a Python Add-In or script or a Python Add-In toolbar that contains buttons, text boxes, and combo boxes.

  • 1
    This is what I always thought too. However, just last week a few coworkers actually got a Tkinter GUI to work within the context of a Python Add-In. Rather than including any of the Tkinter code in the Add-In source code, they had called it from a different stand alone module and wrapped it in a class and imported it into the Add-In. It worked like a charm.
    – crmackey
    Oct 3 '16 at 1:34
  • 1
    Please post that code as a possible answer. I'd like to see it. Since it is a workaround that bypasses arcpy, technically my answer is correct, but if a workaround exists then it would be nice to publicize that as an option, whether or not Esri would support or recommend using it. Oct 3 '16 at 14:27
  • I posted a sample solution. I wouldn't call this a workaround per se, but rather just trying to separate the processes. I say that, because if you think about it, when the module is called externally that has the tkinter GUI, it is being ran in the background (I think), while the Python Add-In framework itself (from the pythonaddins module, not arcpy) is being ran in the foreground which is how it displays the toolbar. By keeping tkinter separate, I think it kind of separates the processes on the main UI thread.
    – crmackey
    Oct 3 '16 at 16:15

I think the trick to using tkinter in Python is to run it from an external module, that way it isn't competing with the GUI components from the pythonaddins module. I do not take any credit for the tkinter GUI portion, as it was my colleague who figured it out, but here is a sample.

The GUI is dynamically created based on a list of text elements, and allows you to quickly update them all. In our templates, all of our header elements are prefixed with header_*. So to make this work, he saved out an external module called element_gui and I just imported it into my test add in and it works pretty smoothly. Here's the GUI code:

# Name:        module1
# Purpose:
# Author:      tylerjo
# Created:     08/04/2016
# Copyright:   (c) tylerjo 2016
# Licence:     <your licence>
import sys
import os
import Tkinter as tk
import tkFileDialog, tkMessageBox
import ttk
import csv

N,S,E,W = tk.N,tk.S,tk.E,tk.W

def main(in_els=[]):
    """Main function. Calls GUI.

        in_els -- List of element objects


    gui = GUI(els=in_els)
    gui.master.title("Update Elements")
    gui.master.minsize(width=500, height=150)

    return gui.outDict

class GUI(tk.Frame):

    def __init__(self, master=None, els=[]):
        tk.Grid.rowconfigure(self, 0, weight=1)
        tk.Grid.columnconfigure(self, 0, weight=1)

        self.elementList = els
        self.elDict = {}
        self.outDict = {}


    def __createWidgets(self):

        top = self.winfo_toplevel()
        top.rowconfigure(0, weight=1)
        self.top = top

        self.rowconfigure(1, weight=1)
        self.columnconfigure(0, weight=1)

        self.f1 = tk.Frame(self, height=500, width=500, bg='gray')
        self.f1.grid(row=0, sticky=N+E+W+S, columnspan=2)
        tk.Grid.rowconfigure(self.f1, 0, weight=1)

        r1 = ['Element Name', 'Updated Value']
        for ci,cLabel in enumerate(r1):
            tk.Grid.columnconfigure(self.f1, ci, weight=1)
            tk.Label(self.f1, text=cLabel, borderwidth=1, width=20, relief=tk.GROOVE).grid(column=ci, row=0, sticky=N+E+W)

        for i, el in enumerate(self.elementList,1):
            tk.Label(self.f1, text=el.name, borderwidth=2).grid(column=0, row=i, sticky=E+W)
            self.elDict[el] = tk.StringVar()
            tk.Entry(self.f1, textvariable=self.elDict[el], justify=tk.CENTER).grid(column=1,row=i, sticky=E+W)

        self.saveButton = tk.Button(self, text="Send Updated Text Back To ArcMap", command=self.both_functions, width=30)
        self.saveButton.grid(row=4, column=0)

    def btnClick(self):
        """Get values from StringVar() objects and put into outDict

        for el, newval in self.elDict.items():
            print newval.get()
            self.outDict[el] = newval.get()

        print self.outDict
        return self.outDict

    def _close(self):
        """Destroy GUI"""

    def both_functions(self):
        """For some reason I can't call _close within btnClick function.
        So both are called here


if __name__ == '__main__':

And here is how I consume it in my simple Add-In (just a toolbar with a button):

import arcpy
import pythonaddins
import os
import sys
import element_gui

class ButtonClass3(object):
    """Implementation for UpdateMapElmsTest_addin.button (Button)"""
    def __init__(self):
        self.enabled = True
        self.checked = False
        self.mxd = arcpy.mapping.MapDocument('current')
        print os.path.dirname(__file__)

    def onClick(self):
        elms = arcpy.mapping.ListLayoutElements(self.mxd, 'TEXT_ELEMENT', 'Header*')
        new_heads = element_gui.main(elms)
        for elm,new_text in new_heads.iteritems():
            elm.text = new_text

And here is what it looks like:


I am not sure how stable this will be. It did crash a couple times when I had the Python window open, so it may still be trying to compete with pythonaddins for UI resources.

  • this solution seems to be correct however it didn't work for me, arcmap was still crashing sometimes. Probably I did something wrong, however, my gui class was in separate file but still it launched in main arcgis thread. I've been looking for another way to run separate process and eventually found it with using subprocessing lib. In onClick method I added subprocess.call(['pythonw', "<path_to_gui_script.pyw>"]) and that was it. Gui runned in separate thread. w means running with no console window Jun 6 at 13:47

Not the answer you're looking for? Browse other questions tagged or ask your own question.