POST
|
Hi Eric, I was trying to spend a bit of time looking into this, but I cannot get Add-In Extensions working on my computer (Buttons and Menus work OK, but Extensions are never added to the Extensions dialog box for me to activate them)... Anyway, I have a suspicion that the only method being run is the openDocument method, as forcing Arc to close is not necessarily the same thing as closing the MXD... What I was going to test on my computer is the following: import arcpy
import pythonaddins
class testExtension(object):
"""Implementation for arcAddin_addin.testExtension (Extension)"""
def __init__(self):
# For performance considerations, please remove all unused methods in this class.
self.enabled = True
self.testTxt = open('D:\\_TEMP\\testOutput.txt', 'w')
self.testTxt.write('File created')
self.testTxt.flush()
def openDocument(self):
self.testTxt.write('!! openDocument method...')
self.testTxt.flush()
mxd = arcpy.mapping.MapDocument("CURRENT")
if mxd.description == 'Print to PDF':
self.testTxt.write('\t\tDocument contains "Print to PDF" description')
self.testTxt.flush()
arcpy.mapping.PrintMap(mxd, r"Adobe PDF", "PAGE_LAYOUT")
layers = arcpy.mapping.ListLayers(mxd, "", "Layers"):
self.testTxt.write('\t\t%s' % str(layers))
self.testTxt.flush()
self.testTxt.write('\t\tAbout to kill ArcMap')
self.testTxt.flush()
os.system('taskkill /IM Arcmap*')
self.testTxt.write('\t\tThis probably will not appear in the file...')
self.testTxt.flush()
else:
self.testTxt.write('\t\tDocument DOES NOT contain "Print to PDF" description')
self.testTxt.flush()
def beforeCloseDocument(self):
self.testTxt.write('!! beforeCloseDocument method')
self.testTxt.flush()
def closeDocument(self):
self.testTxt.write('!! closeDocument method')
self.testTxt.flush() It writes to a text file as each method occurs, so you can see what the add-in extension is doing. flush() is used to force the writes to the file, as we won't be closing it properly. Are you able to temporarily replace your add-in extension python file with the above (after changing the path of the text file to something more appropriate), run it as you normally would and post the contents of the text file?
... View more
09-12-2013
08:24 PM
|
0
|
0
|
804
|
POST
|
Eric, as I said before: my advice is to leave the Python AddIn Extension until you get something that works from a script. A script is not the same as the command window: the time it takes for you to enter the commands (or copy/paste them) might be enough for Arc to finish the preceding operations. The reason I am asking you to do this is that I have no idea how the Python AddIn Extension works, what its execution order is, if it tries to use multiple threads, etc. Once you have something that works from a script it will be simple to implement it within the Python AddIn Extension. To save and run the script: copy the first block of code from my previous post (the one without ListLayers) into notepad/IDLE save it somewhere as test1.py open a command prompt in the folder where test1.py is residing locate your ArcGIS Python install (10.0 usually places it at C:\Python26\ArcGIS10.0\, 10.1 usually places it at C:\Python27\ArcGIS10.1\) test it by entering the full path to python.exe at the command line, i.e.: C:\Python27\ArcGIS10.1\python.exe this should open the interpreter, and you can test it is the correct install by running import arcpy; if everything is OK it will report nothing, just return you to the python command prompt >>> once you have both the above steps working use exit() to leave the interpreter and run the script like so: C:\Python27\ArcGIS10.1\python.exe test1.py What is the output? Now save the second block of code from my previous post as test2.py and repeat the relevant steps of the above process. What is the output now?
... View more
09-11-2013
02:16 PM
|
0
|
0
|
1714
|
POST
|
Thanks for the info Stacey. I'm able to get the ArcMap process to teminate using [HTML]os.system('taskkill /IM Arcmap*'[/HTML], but I can't seem to get it to run after a delay. You'll see in my posted code, that I'm using an AddIn Extension that runs as soon as the MXD opens. However, the process executes too soon, and tries to kill ArcMap before the MXD fully opens. When I add time.sleep() or similar it just delays the openeing of the document, not the next part of the script which is the arcpy.mapping.PrintMap function. Because of the limitations with AddIn functions, PrintToPDF has to run before the document closes in order to capture all the tables after they've fully loaded. The whole thing has to be timed just right so that the layout fully assembles, then the PritnToPDF function executes. Oh, I failed to read your question properly (...at all...?). Sorry! I also noticed that the very bottom line of code from the initial post contains a pass statement. This should not be there. To find the solution for a problem like this you need to strip everything back to the simplest possible set of operations that works, then add in the complexity. I would suggest that you initially work in a script; if/once you get a series of steps that does work, you can put it back into the class form. Here is a script that, I think, is doing what you want. It probably won't work either, but it is a lot more clear what is happening and when: import arcpy
import os
mxd = arcpy.mapping.MapDocument("CURRENT")
if mxd.description == 'Print to PDF':
arcpy.mapping.PrintMap(mxd, r"Adobe PDF", "PAGE_LAYOUT")
os.system('taskkill /IM Arcmap*') Now, to get ArcMap to finish before closing you could try to get the code to perform some trivial operation the MXD after it is printed, but before it tries to close. This might be enough to force it to complete the print before the trivial operation is allowed to start, i.e. List Layers: import arcpy
import os
mxd = arcpy.mapping.MapDocument("CURRENT")
if mxd.description == 'Print to PDF':
arcpy.mapping.PrintMap(mxd, r"Adobe PDF", "PAGE_LAYOUT")
arcpy.mapping.ListLayers(mxd)
os.system('taskkill /IM Arcmap*')
... View more
09-10-2013
01:46 PM
|
0
|
0
|
1714
|
POST
|
Something like this should work, using the Psutil library for Python: import psutil
# list of process names that we might want to terminate
badList = ['ArcMap.exe', 'AppROT.exe', 'ArcGISConnection.exe', 'ArcGISCacheMgr.exe']
# generate list of processes to terminate
badProcesses = [p for p in psutil.process_iter() if p.name in badList]
# terminate the bad processes
[p.terminate() for p in badProcesses]
It is a bit zealous about terminating all the Arc processes, you can probably get away with changing badList to just: badList = ['ArcMap.exe']
... View more
09-09-2013
02:47 PM
|
0
|
0
|
1714
|
POST
|
For a while I had a spate of problems similar to yours, but it seems to have cleared itself up. Not sure if I am just writing less lock-prone code, or if an ArcGIS patch along the way fixed something up. Here are two things which might help you: Using Exists, Compact, Exists on the GDB in question. This seems to force a refresh and can sometimes clear out the locks. To do it in only one line of code: [INDENT] #input GDB is testWS
[arcpy.Exists(testWS), arcpy.Compact(testWS), arcpy.Exists(testWS)] [/INDENT] I wrote a function that will kill any process, except for the running process, that might have the workspace open. If it is this process that has the workspace open it will repeat the above step indefinitely until the lock disappears. I mostly used this when running scripts that use multiple CPUs at the same time. It can also close ArcMap or Catalog if you accidentally had them open using your workspace. It requires that you import the os library (part of Python), and download, install and import the Psutil library. It is definitely not fool-proof and can raise exceptions if a process is found that has the workspace open but that process closes its handle before the code gets to doing it. To use, add this to the top of your code: [INDENT] import os
import psutil
def clearWSLocks(inputWS):
'''Attempts to clear ArcGIS/Arcpy locks on a workspace.
Two methods:
1: if ANOTHER process (i.e. ArcCatalog) has the workspace open, that process is terminated
2: if THIS process has the workspace open, it attempts to clear locks using arcpy.Exists, arcpy.Compact and arcpy.Exists in sequence
Required imports: os, psutil
'''
# get process ID for this process (treated differently)
thisPID = os.getpid()
# normalise path
_inputWS = os.path.normpath(inputWS)
# get list of currently running Arc/Python processes
p_List = []
ps = psutil.process_iter()
for p in ps:
if ('Arc' in p.name) or ('python' in p.name):
p_List.append(p.pid)
# iterate through processes
for pid in p_List:
try:
p = psutil.Process(pid)
except: # psutil NoSuchProcess...
p = False
# if any have the workspace open
if p and any(_inputWS in pth for pth in [fl.path for fl in p.get_open_files()]):
print ' !!! Workspace open: %s' % _inputWS
# terminate if it is another process
if pid != thisPID:
print ' !!! Terminating process: %s' % p.name
p.terminate()
else:
print ' !!! This process has workspace open...'
# if this process has workspace open, keep trying while it is open...
while any(_inputWS in pth for pth in [fl.path for fl in psutil.Process(thisPID).get_open_files()]):
print ' !!! Trying Exists, Compact, Exists to clear locks: returned %s' % all([arcpy.Exists(_inputWS), arcpy.Compact_management(_inputWS), arcpy.Exists(_inputWS)])
return True then use a function call to try and clear the locks, like so: clearWSLocks(testWS) If it often throws exceptions, you could do this (but it may then not clear the locks...): try:
clearWSLocks(testWS)
except:
pass [/INDENT] Let me know if either of these help! I haven't used the function for a while, and had to adapt it a bit for posting, so there may be errors in it... You can use trial and error to work out where you need to use these within the code.
... View more
09-08-2013
05:20 PM
|
0
|
0
|
247
|
POST
|
Setting the value works for me. You could try printing the values before and after your assignment to confirm this: arcpy.AddMessage(Z: ' + arcpy.env.outputZFlag)
arcpy.env.outputZFlag = "Disabled"
arcpy.AddMessage(Z: ' + arcpy.env.outputZFlag)
arcpy.AddMessage('M: ' + arcpy.env.outputMFlag)
arcpy.env.outputMFlag = "Disabled"
arcpy.AddMessage('M: ' + arcpy.env.outputMFlag) It may be that the variable is being set, but some tool later on is not honoring the setting.
... View more
09-05-2013
01:58 PM
|
0
|
0
|
427
|
POST
|
You can have as many Python installs as you want, but it is important that you know some of the potential issues: I think you can only have one default system Python, that stuff gets installed to or pops up if you enter "python" at the command line. This means you have to know how to install libraries specifically to one or the other. See below. ArcGIS installs its Python behind the scenes, and does not make it the system default Python. I think the Python installer will make its one the default. Python 3 has a different syntax to Python 2, so scripts will not be directly transferable between them. The biggest difference is problably >>> print 'Hello world' (Python 2) vs >>> print('Hello world') (Python 3). If you were using Python 2.7 (installed by/works with ArcGIS 10.1) you can use the statement from __future__ import <package> to use <package> like Python 3. It is possible to convert scripts between Python 2 and 3, for example see here. The two Pythons will be separate. If you install a library, say Parallel Python, to your Python 3 it will not be accessible from your Python 2. If you write a script that requires the installed package (i.e. Parallel Python), and try to run it with the wrong Python, it will fail (ImportError). Importantly: Python 3 will not be able to access Arcpy, which is the ArcGIS library that lets you do geoprocessing from Python, as Arcpy is only compatible with Python 2 at the moment. Scripts that you write using Arcpy will fail (SyntaxError or ImportErrror) if you try to run them from Python 3. Installing libraries to Non-default Python To do this: download the package source (usually a .zip file, not specific to any Python version) Unzip the package Open a command prompt within the extracted package folder and run: [INDENT]python setup.py install [INDENT]where python is the Python you want to install the library to. If it is not on the path (either temporarily or permanently) you must supply the full path to it, i.e. c:\python27\ArcGIS10.1\python.exe[/INDENT][/INDENT] Answers to your specific questions I have ArcGIS 9.3 on Windows Vista 32. I want to study a Python course which requires Python 3.3. Can I install it using the official installer python-3.3.2.msi? Yes. Will something happen to ArcGIS? No. I have scripts for its Python 2.5, will I be able to run them? Not directly; as described point 2 above, the syntax is different. The scripts can be converted, which will work fine as long as you have the same libraries installed to both Pythons; for example if you want to use Numpy it must be installed to Python 3 as well (it is installed to ArcGIS Python by default). Just remember that Python 3 scripts will not be able to access Arcpy. You can even convert your Python 3 scripts back so you can use them in ArcGIS's Python 2, as long as you install any required libraries to it too...
... View more
09-02-2013
07:13 PM
|
2
|
0
|
303
|
POST
|
I have ArcGIS 10.2 on my machine. I just looked in the Start menu and it shows Python 2.7 is installed. I would strongly recommend not installing another Python. You are better to just use the ArcGIS Python as your default system Python - there are no real disadvantages to doing this. It has the advantage that you don't have clashes, installer mixups, etc. This is particularly true now that ESRI have released 64-bit Background Geoprocessing, which gives you a default 64-bit Python (unfortunately you can't then delete the 32-bit Python...). Prior to this I had to run a separate Python just for dealing with large datasets (outside of Arc), now I can use the one Python for everything. For use with ArcGIS there are practically no advantages to using Python 3, so you aren't missing out on anything. Although, if you really want to practice for when ArcGIS does move to Python 3 you can emulate a lot of the behaviour with with import __future__, i.e. (ones less likely to be useful commented out...): from __future__ import division, print_function #, unicode_literals, with_statement
... View more
08-29-2013
02:33 PM
|
0
|
0
|
512
|
POST
|
I think I should include an example of the data that I am using to better explain what I am trying to accomplish Attached: Example.xlsx This is also in a File Geodatabase as a Geodatabase Table This table is composed of around 9500 meter records (METER NUMBER) 1) Go through the table and find METER NUMBER record with the billing period, KWH and KW Demand rows attached. 2) Extract/export/select a unique METER NUMBER with the data attached (billing period, KWH and KW Demand) 3) Take the data and build a graph showing the Billing Period along the "X" Axis, KWH and KW Demand along the "Y" Axis 4) Export the graph to a .PDF with the METER NUMBER as the file name From there I can use the Attachments Geoprocessing tool to take the .PDF and attach it to the Meter Location in the .MXD Long Term Goal is to create a One-Touch custom Button to select a specific METER NUMBER to create a graph described above and automatically create a .PDF. I know this sounds like a huge project - and want to learn how to build it piece by piece to eventually accomplish the Long Term Goal. Thanks for your help in advanced Excellent! It is great that you have written out a plan, many people don't and then get hopelessly lost... It definitely looks possible. Right now we'll get step 1 partly working, just printing to screen. Once that is good we can put the data in a useful data structure, rather than just printing it. I think James' suggestion, of using Pandas, is good. I haven't used it myself, but I think it works pretty well with time data and plotting functions. So there is something else you will need to look up and have a play with... Do you intend to only ever assess one meter at a time? You have a few options, but for now we may as well keep part 1 collecting all the data, and between part 1 and part 2 define the one METER NUMBER to make the graph of.
... View more
08-27-2013
02:08 PM
|
0
|
0
|
570
|
POST
|
Having a hard time getting multiple fields to show up. I have about (4) fields that I need to show: TWACs_NUMBER PERIOD_ENDS KWH_USAGE KW_DEMAND So Far - I can get the "TWACs_NUMBER to "print" to the screen - but I need all of these fields to "print" so I can sort them by TWACs NUMBER A few questions: Please include your code so far. What Arc version are you using? Have you read and played with the examples included in the docs, which I attached in the last post? To include code, simply copy it from your editor or the Python window in ArcMap/Catalog (if that is what you are using) then paste it in your reply. To preserve indentation and identify it as code, select all the code and click the # button on the forum writing tools at the top of the edit box.
... View more
08-27-2013
01:57 PM
|
0
|
0
|
570
|
POST
|
Thanks Stacy! I did some minor tweaking but got it to work. I originally had a script just for adding fields but I think it will be easier if I just add the needed fields and do the required processing in one script (well 3 scripts total since there are 3 fields I need to add and manipulate). Here is what worked for me: # Import Arcpy
import arcpy
from arcpy import env
# Set the Workspace
env.workspace = "J:\\GIS\\Parcel_Data\\Python_Data\\CAMASUMMARY.shp"
# Define Variables
inFeatures = "CAMASUMMARY"
fieldName1 = "Webpage"
fieldLength = 100
# Add the Webpage field to the parcel data
arcpy.AddField_management (inFeatures, fieldName1, "TEXT", "", "", fieldLength)
# Define the shapefile used below (CAMASUMMARY)
fc = "J:\\GIS\\Parcel_Data\\Python_Data\\CAMASUMMARY.shp"
# Update the Webpage field
with arcpy.da.UpdateCursor(fc, ['QUICKREFID', 'Webpage']) as cursor:
for row in cursor:
## row is a list where QUICKREFID is row[0], Webpage is row[1]
row[1] = 'http://www.fbcad.org/Appraisal/PublicAccess/PropertyDetail.aspx?PropertyID=' + str(row[0])
cursor.updateRow(row) Great work! Just looking at your script I noticed a few things: you add the field using env.workspace and feature class name BUT you call the cursor with the full path; one of these is redundant you make the new Webpage field a variable for adding the field, but hard-code it again when you create the cursor None of these problems is serious, but could cause problems in the future if you are copying and pasting code around. Here is your script with the above taken into account (red = delete, blue = change😞 # Import Arcpy
import arcpy
from arcpy import env
# Set the Workspace
env.workspace = "J:\\GIS\\Parcel_Data\\Python_Data\\CAMASUMMARY.shp"
# Define Variables
inFeatures = "CAMASUMMARY"
# Define the shapefile used below (CAMASUMMARY)
fc = "J:\\GIS\\Parcel_Data\\Python_Data\\CAMASUMMARY.shp"
fieldName1 = "Webpage"
fieldLength = 100
# Add the Webpage field to the parcel data
arcpy.AddField_management (fc, fieldName1, "TEXT", "", "", fieldLength)
# Define the shapefile used below (CAMASUMMARY)
fc = "J:\\GIS\\Parcel_Data\\Python_Data\\CAMASUMMARY.shp"
# Update the Webpage field
with arcpy.da.UpdateCursor(fc, ['QUICKREFID', fieldName1]) as cursor:
for row in cursor:
## row is a list where QUICKREFID is row[0], Webpage is row[1]
row[1] = 'http://www.fbcad.org/Appraisal/PublicAccess/PropertyDetail.aspx?PropertyID=' + str(row[0])
cursor.updateRow(row)
... View more
08-26-2013
06:12 PM
|
0
|
0
|
813
|
POST
|
This can definitely be done in Python, but you will have to learn a lot! Here are a few bits to get you started: SearchCursor (10.0), da.SearchCursor (10.2) allows you to iterate through tables and get values. You could store the values (date, reading) within lists in a Python dictionary by meter number. Python datetime module (inbuilt) will be helpful for working with dates. Matplotlib allows you to plot things from Python. I don't know how to make PDFs from Python, but I have no doubt whatsoever that it is possible... I would suggest you start playing around with matplotlib, datetime and searchcursors first, once you are familiar with them, start to think about the data structure that you will use to get the information from the searchcursor, analyse the monthly peak, and pass the result to matplotlib. Happy to help if you have more specific queries!
... View more
08-25-2013
03:32 PM
|
0
|
0
|
570
|
POST
|
One workaround for this, so that you can still distribute an exe file, is to make a wrapper script which gets converted to exe and distribute it with a pyc file that actually does the calculation. For example (very simple, not really recommended, but will work - see below for a better option): Code script: doSomething.py import arcpy
# do some stuff
# do some more stuff Wrapper script: wrapper.py import doSomething You simply have to run wrapper.py, it will import doSomething.py and make a *.pyc file from it. Then you can use py2exe on wrapper.py. Then you simply have to keep the doSomething.pyc file with the exe when you distribute it. RECOMMENDED IMPLEMENTATION Code script: doSomething.py import arcpy
def main():
# do some stuff
# do some more stuff
if __name__ == '__main__':
pass # will do nothing if someone tries to run it directly
Wrapper script: wrapper.py import doSomething
doSomething.main()
... View more
08-25-2013
02:20 PM
|
0
|
1
|
1853
|
POST
|
Hey, you will want to look into the UpdateCursor (if you have 10.0) or da.UpdateCursor (if you have 10.1), this will allow you to set the field value. Assuming you are using da.UpdateCursor this should do the trick: ## pre-calculation here, i.e. adding fields...
# note that fc is the feature class...
with arcpy.da.UpdateCursor(fc, ['PropertyID', 'Webpage']) as cursor:
for row in cursor:
## row is a list where PropertyID is row[0], Webpage is row[1]
row[1] = 'http://www.fbcad.org/Appraisal/PublicAccess/PropertyDetail.aspx?PropertyID=' + str(row[0])
cursor.updateRow(row) Haven't tested, but let me know if it doesn't work!
... View more
08-22-2013
07:55 PM
|
0
|
0
|
813
|
POST
|
Thank you StacyRendall!!!! I was almost there, not sure what happened to the == 1 in that one block, I swear it was there before. So the trick is to make the variable and do the rounding and integer business then get the length of that instead of trying to do it on every if statement, saving all that typing in the process haha. I'm still pretty new to python, only on chapter 7 in my book and that was the first time using the .format and * so thanks for teaching me something new. I'll get it all figured out eventually. Thanks again! Now I just have to figure out how to get them perpendicular to my line. Oh, that worked if someone missed it. Great! I had never used .format() until now either! Cheers, Stacy
... View more
08-14-2013
01:33 PM
|
0
|
0
|
475
|
Title | Kudos | Posted |
---|---|---|
1 | 03-02-2012 11:29 AM | |
1 | 01-13-2014 02:04 PM | |
1 | 09-26-2011 02:52 PM | |
1 | 10-23-2013 05:23 PM | |
1 | 04-01-2012 08:16 PM |
Online Status |
Offline
|
Date Last Visited |
11-11-2020
02:24 AM
|