2

I'm simply trying to programmatically change the background colour of a Dataframe in ArcMap.

I followed the beginning of this solution to access ArcObjects in Python:

import arcpy
import os

from snippets102 import *
from comtypes.client import GetModule, CreateObject

import comtypes.gen.esriFramework as esriFramework
import comtypes.gen.esriArcMapUI as esriArcMapUI
import comtypes.gen.esriCarto as esriCarto
import comtypes.gen.esriDisplay as esriDisplay

mapFile = "C:/MapMaps/myMap.mxd"

# Get map document object
pMapDoc = CreateObject(esriCarto.MapDocument, interface=esriCarto.IMapDocument)
pMapDoc.Open(mapFile)

From the Map Document object, I then tried to get the Active View, the Graphics Container (I don't entirely understand what that is), the Map, the Map Frame, the Background, the SymbolBackground, the Fill Symbol and finally the Color.

# Get Active View
pageLayoutActiveView = CType(pMapDoc.PageLayout, esriCarto.IActiveView)

# Get Graphics Container
graphicsContainer = CType(pageLayoutActiveView, esriCarto.IGraphicsContainer)

# Get Map
map = pMapDoc.Map[0]

# Get Map Frame
mapFrame = graphicsContainer.FindFrame(map)

# Get Background and Symbol Background
bgSymb = CType(mapFrame.Background, esriCarto.ISymbolBackground)

# Finally get to Fill Symbol and Color
print bgSymb.FillSymbol.Color.RGB

However, from here it doesn't seem I'm able to actually modify any of the Background symbol's properties

# Current color settings
print bgSymb.FillSymbol.Color.RGB
print bgSymb.FillSymbol.Color.GetCIELAB()

# Try to change...
bgSymb.FillSymbol.Color.RGB = 12345678
bgSymb.FillSymbol.Color.SetCIELAB(50, 10, -10)

# But settings are same as before!
print bgSymb.FillSymbol.Color.RGB
print bgSymb.FillSymbol.Color.GetCIELAB()

Any suggestions? I also tried creating an RGBColor object and doing bgSymb.FillSymbol.Color = newRGBcol but that did not work either.

  • IPageLayout does not implement ActiveView; what do you get when you print the colours? i.e. what is the output of this script? I have a feeling that to set the background you will need to create an IFillSymbol and set the colour there. – Michael Stimson Sep 28 '17 at 21:58
  • It seems that IGraphicsContainer is implemented by IMap so graphicsContainer = CType(map, esriCarto.IGraphicsContainer) then set your colour NewColor=CreateObject(esriDisplay.RgbColor, interface=esriCarto.IRgbColor) NewColor.red, green and blue channels separately then cast to IColor to set the bgSymb.FillSymbol.Color but don't forget to pageLayoutActiveView.Refresh() to update the contents. – Michael Stimson Sep 28 '17 at 22:56
  • Thanks for your response! I tried this: map = pMapDoc.Map[0] graphicsContainer = CType(map, esriCarto.IGraphicsContainer) mapFrame = graphicsContainer.FindFrame(map) bgSymb = CType(bg, esriCarto.ISymbolBackground) But at that point I got an error: ValueError: NULL COM pointer access Note that the Mapfile in question is just an empty map document with a single blank data frame – Andrew Plowright Sep 28 '17 at 23:25
  • I think I see where this is going wrong, you're getting IMapDocument and IMxDocument mixed up. If you can understand a bit of C# Get PageLayout from ArcMap Snippet resources.arcgis.com/en/help/arcobjects-net/componenthelp/… How to create map grids resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/…, from these I can see mxDoc = CType(pMapDoc,esriArcMapUI.IMxDocument) then pageLayoutActiveView = CType(mxDocument.PageLayout, esriCarto.IActiveView)... hopefully that works as advertised. – Michael Stimson Sep 28 '17 at 23:42
  • Sorry to be so helpless here but I'm not managing to get this to work, I tried pMapDoc = CreateObject(esriCarto.MapDocument, interface=esriCarto.IMapDocument) pMapDoc.Open(mapFile) mxDoc = CType(pMapDoc, esriArcMapUI.IMxDocument) And unfortunately that didn't seem to work. The method I use to check is by doing print dir(mxDoc) and see if any usable members show up. It should be noted that in the original code, it did seem like I was accessing the right object. The GetCIELAB() function would return the correct colour from the frame. I just couldn't modify it for some reason. – Andrew Plowright Sep 29 '17 at 0:27
0

Ok, this time I think I have it for sure..

I wrote, and tested, in C#:

IMapDocument pMapDoc = (IMapDocument)gApp.Document;
IMxDocument mxDoc = (IMxDocument)pMapDoc;
IMap pMap = mxDoc.FocusMap;
IGraphicsContainer pGrphCont = (IGraphicsContainer)mxDoc.PageLayout;
IFrameElement pFrameElem = pGrphCont.FindFrame(pMap);
ISymbolBackground pSymBack = new SymbolBackgroundClass();
IFillSymbol pFillSym = new SimpleFillSymbolClass();
IRgbColor pRGBcol = new RgbColorClass();
pRGBcol.Red = 255;
pFillSym.Color = (IColor)pRGBcol;
pSymBack.FillSymbol = pFillSym;
pFrameElem.Background = (IBackground)pSymBack;
mxDoc.ActiveView.Refresh();

It's quick and nasty but shows how the objects interrelate.. I think the problem is that, unless set, the mapFrame.Background is None (null) so you have to create a new one and apply it. To convert this to python it goes like this:

# Get Active View
pageLayoutActiveView = CType(pMapDoc.PageLayout, esriCarto.IActiveView)

# Get Graphics Container
graphicsContainer = CType(pMapDoc.PageLayout, esriCarto.IGraphicsContainer)

# Get Map
map = pMapDoc.Map[0]

# Get Map Frame
mapFrame = graphicsContainer.FindFrame(map)

# Make some new objects..
pSymBack = CreateObject(esriCarto.SymbolBackground, interface=esriCarto.ISymbolBackground)
pFillSym = CreateObject(esriDisplay.SimpleFillSymbol, interface=esriDisplay.IFillSymbol)
pRGBcol  = CreateObject(esriDisplay.RgbColor, interface=esriDisplay.IRgbColor)

# setup and apply objects
pRGBcol.Red = 255;                                 # set to bright red for this example
pFillSym.Color = CType(pRGBcol,esriDisplay.IColor) # set the color for the fill symbol
pSymBack.FillSymbol = pFillSym                     # apply the new fill symbol to the background
mapFrame.Background = CType(pSymBack,esriCarto.IBackground)
pMapDoc.Save()

From the comments, it seems that the IMxDocument interface isn't valid because the map isn't opened (no ArcMap process is started) so will return None.

  • I think we're close!! It fails at graphicsContainer = CType(mxDoc.PageLayout, esriCarto.IGraphicsContainer) with an error AttributeError: 'NoneType' object has no attribute 'PageLayout'. I guess that means that it doesn't want to create the correct IMxDocument object from pMapDoc. Odd, since that seems what you did in C#! – Andrew Plowright Sep 29 '17 at 17:31
  • So, the mxDoc is None, that's weird. Perhaps python is struggling a bit with that, if you can't cast a MapDocument to IMxDocument in python that is probably worthy of a new question.. Does it improve any to cast graphicsContainer = CType(pMapDoc.PageLayout, esriCarto.IGraphicsContainer)? IMapDocument implements read only PageLayout resources.arcgis.com/en/help/arcobjects-net/componenthelp/… but that doesn't necessarily mean that mapFrame accessed via that interface/property is read only; The PageLayout property of IMxDocument is read/write. – Michael Stimson Oct 2 '17 at 21:53
  • I didn't realize this might be a factor, but I've been running this outside of an ArcMap session. I noticed that in the IMxDocument documentation it mentions: To edit the contents of a map document saved to a file (*.mxd) outside of an ArcMap session or without instantiating a new Application (ArcMap process), see IMapDocument. IMapDocument provides the ability to edit and save the contents of a map document. Could it be that you can't use IMxDocument when ArcMap is closed? – Andrew Plowright Oct 3 '17 at 17:25
  • Quite possibly, I was using an active session of ArcMap. You would think though pMapDoc.Open(mapFile) would generate a session of ArcMap. I have found that mxd = arcpy.mapping.MapDocument('file path') does not start an ArcMap session. If you try the appropriate lines of code from within the python window of ArcMap the background colour should change... perhaps accessing the IMxDocument from arcpy is worth another question, linked to this post for context. – Michael Stimson Oct 3 '17 at 21:12
  • SUCCESS! I needed to 1) forego the IMxDocument 2) Get the IGraphicsContainer from pMapDoc.PageLayout instead 3) Get the map using pMapDoc.Map[0] 4) Use esriDisplay.SimpleFillSymbol instead of esriDisplay.SimpleFillSymbolClass and 5) Add pMapDoc.Save() at the end. Since you basically came up with the solution, would you like to edit the post above and we can mark this one as solved? – Andrew Plowright Oct 3 '17 at 23:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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