Select to view content in your preferred language

Python geoprocessing script and tkinter?

1374
5
Jump to solution
09-13-2022 02:21 PM
AustinBachurski
Occasional Contributor

I'm putting together a tool to help a volunteer where I work with a weekly workflow.  The tool processes a couple spreadsheets into a csv and then puts the points on a map web layer.  I've got everything working using tkinter for the GUI, but running the geoprocessing model locks up the GUI until complete.  Tried threading it but all it does is crash as soon as the geoprocessing model is ran.  I found several old posts (2012, 2014, etc.) that simply say that Esri and tkinter don't play well together.  I wanted to check in and see if there's any work arounds for this or if anyone has had success with another GUI?  Thanks.

0 Kudos
2 Solutions

Accepted Solutions
dslamb2022
Occasional Contributor

Short answer is no. ArcGIS Pro/Map never like it when you try to do anything with Python outside the thread. I've tried with Matplotlib and there really is no work around. Map used to have some GUI tools available for Python through their add-in framework, but that is not available in Pro.

 

You could create a stand alone tool that sits outside of ArcMap and run arcpy that way. You would need to convert your Model to an arcpy script (I think you can even export it as Python code). Then you are not inside the ArcGIS environment.

View solution in original post

AustinBachurski
Occasional Contributor

Thanks for the replies, pretty clear from the responses that yall are way above my current skill level, haha.  I wound up getting it to do what I wanted in the long run, might be a bit of a mess, but from the GUI script I used subprocess.Popen() to call separate python scripts for each geoprocessing tool I wanted to run.  Passed our portal login credentials as subprocess arguments so my user doesn't have to enter their login info more than once.  Then in the GUI script set a variable and use subprocess capture_output  to let me know when the process was done.  Finally I've got a while loop that checks the status of the capture_output variables to provide "currently doing X" updates to my user as well as run each geoprocessing script sequentially.  Using subprocess createflags=CREATE_NO_WINDOW, the geoprocessing windows are hidden and the calls to subprocess are threaded in the GUI script so there's no hangups, visually you'd never know it's not running in the main tool (at least to my eye...).  Might not be the best way of doing it but I'm quite pleased with how it's turned out.  Link if anyone's curious.  Thanks again for the responses.

View solution in original post

0 Kudos
5 Replies
dslamb2022
Occasional Contributor

Short answer is no. ArcGIS Pro/Map never like it when you try to do anything with Python outside the thread. I've tried with Matplotlib and there really is no work around. Map used to have some GUI tools available for Python through their add-in framework, but that is not available in Pro.

 

You could create a stand alone tool that sits outside of ArcMap and run arcpy that way. You would need to convert your Model to an arcpy script (I think you can even export it as Python code). Then you are not inside the ArcGIS environment.

AustinBachurski
Occasional Contributor

Yeah exporting it to a python script and copying that code into the GUI script is what I was doing.  Tried it with Kivy last night but that GUI locks up too...  Looks like I'm going to have to just trigger the GIS script on it's own, hoping I can somehow share a third file between the two so I can still provide GUI updates to progress.  Sigh, so much to look up when you're learning, lol.  Thanks for the response.

0 Kudos
by Anonymous User
Not applicable

I was able to do a PyQT application that performed well for the most part, but found the ArcGIS Pro Corehost application in the Pro SDK a lot more stable and it worked better for several reasons. Managing the python env was easier, created better word documents, it was easier to deploy/ install, and it crashed less on the application state parts. Couldn't manage to get the threading to work so it would lock up the UI during GP tasks. The pickeling was an issue and I couldn't return features/ results from the GP. The garbage collector in python was too aggressive and would delete objects that were still needed (responses from modal prompts), and I couldn't get them to persist after the modal was closed.

Take a look at the Corehost,

or as an Add-in...

DeanAnderson2
Frequent Contributor

Used  to drive python scripts from tkinter in ArcMap (for years - was stable).  Did a quick test and it still works in Arcpro.  Made the following "VERY SIMPLE Hack" of a tikinter form in python to demonstrate.  Root (form) calls a function "runit" that counts the number of records for a layer in geodb.  This shows a simple way to drive ArcPy stuff from python.  Also nice goes pandas runs well outside of ArcGIS as well.  Here is code.  What it looks like is below (also an attachment as textfile).  Again this was quick and dirty but shows how it works. 

# TestTkInter.py
#
# Demo to show how to drive ArcPy from tkinter form.
# Set Geodatabase in variable
# Prompt user to enter layer name with form and use mssagebox to display number records
# User can continue to enter layers names etc... until they press Quit butt
#
# THIS IS A TEST and it does not check if layer exists in database!
#
# GeoDb = Geodatabase
# Layer = Layer you want to count records for
#
#

import os,sys,shutil,tkinter as tk,arcpy,pandas as pd,datetime,traceback
from tkinter import *
from tkinter import ttk
from tkinter import messagebox

def runit():

print ("runit")
LayerRunIt = Layer.get()

print ("GeoDB:" + GeoDb)
print ("Layer:" + LayerRunIt)

LayeraPath = GeoDb + "\\" + LayerRunIt

arcpy.MakeFeatureLayer_management(LayeraPath, "LayerLyr")
numRecords = int(arcpy.GetCount_management("LayerLyr").getOutput(0))
print ("Records: " + str(numRecords))
messagebox.showinfo(LayerRunIt,"Records: " + str(numRecords))
arcpy.management.Delete("LayerLyr")

# main

root = tk.Tk()
root.title ("Test")

#GeoDb = StringVar()
Layer = StringVar()

GeoDb = "C:\\ORMAP3.0\\T7-4\\Fabric\\TownEd.gdb"

#frm = ttk.Frame(root, padding=10)
#frm.grid()
tk.Label(root, text="Test").grid(column=0, row=0)

tk.Label(root, text="GeoDb").grid(column=0, row=1,sticky=E)
tk.Label(root, text="Layer").grid(column=0, row=2,sticky=E)

tk.Label(root,text=GeoDb,width=40).grid(row=1,column=1,sticky=W)
tk.Entry(root,textvariable=Layer,width=20).grid(row=2,column=1,sticky=W)

tk.Button(root,text="Quit", command=root.destroy).grid(column=0, row=4)
tk.Button(root,text="RunIt",command=lambda: runit(),font = "Verdana 10 bold").grid(column=1,row=4)

root.mainloop()

Form looks like this: 

DeanAnderson2_0-1663170683360.png

 

tkinter messagebox returns this... 

DeanAnderson2_1-1663170713679.png

 

 

AustinBachurski
Occasional Contributor

Thanks for the replies, pretty clear from the responses that yall are way above my current skill level, haha.  I wound up getting it to do what I wanted in the long run, might be a bit of a mess, but from the GUI script I used subprocess.Popen() to call separate python scripts for each geoprocessing tool I wanted to run.  Passed our portal login credentials as subprocess arguments so my user doesn't have to enter their login info more than once.  Then in the GUI script set a variable and use subprocess capture_output  to let me know when the process was done.  Finally I've got a while loop that checks the status of the capture_output variables to provide "currently doing X" updates to my user as well as run each geoprocessing script sequentially.  Using subprocess createflags=CREATE_NO_WINDOW, the geoprocessing windows are hidden and the calls to subprocess are threaded in the GUI script so there's no hangups, visually you'd never know it's not running in the main tool (at least to my eye...).  Might not be the best way of doing it but I'm quite pleased with how it's turned out.  Link if anyone's curious.  Thanks again for the responses.

0 Kudos