Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I find myself woefully lacking in understanding of when, why, and how (which type) to do screen draw/refresh/partial refresh. For instance, in a standalone Python script outside the application boundary, I have a function that is trying to draw graphic elements in the data view. Specifically, the elements will be a TextElement on top of a RectangleElement. The function loads the two elements into a GroupElement, which is in turn added to the ActiveView's GraphicsContainer for drawing on the screen. (As far as I can tell the graphic elements are valid.) I am really not understanding what Draw/Refresh/ParitalRefresh methods to put where. If anyone can give/point to a good explanation or tutorial about screen drawing/refreshing I would appreciate it. Below is a bit of selected code from my function. I have a PartialRefresh in the last line, but I don't know if that is correct or not.

pGroupElement = NewObj(esriCarto.GroupElement, esriCarto.IGroupElement3)
pTextElement = NewObj(esriCarto.TextElement, esriCarto.ITextElement)
pRectElement = NewObj(esriCarto.RectangleElement, esriCarto.IElement)
.
.
(configure TextElement)  [tedious code removed]
.
.
(configure RectElement)
.
.
ptxtElem = CType(pTextElement, esriCarto.IElement)

pGroupElement.AddElement(ptxtElem)
pGroupElement.AddElement(pRectElement)

pContainer = mxDoc.ActiveView.GraphicsContainer

pGrpElem = CType(pGroupElement, esriCarto.IElement)

pContainer.AddElement(pGrpElem, 0)

mxDoc.ActiveView.PartialRefresh(esriCarto.esriViewGraphics, None, None)

Added: So, is creating objects with the following code - as I do now - not creating them in process?

def NewObj(MyClass, MyInterface):
    """Creates a new comtypes POINTER object where\n\
    MyClass is the class to be instantiated,\n\
    MyInterface is the interface to be assigned"""
    from comtypes.client import CreateObject
    try:
        ptr = CreateObject(MyClass, interface=MyInterface)
        return ptr
    except:
        return None

If not, I gather I must use IObjectFactory. Assuming that my current objects are not in process, I must say I haven't seen any great performance hit, but I guess I'm just asking for trouble forcing lots of interprocess calls.

Thank you, all, for the help. I shall experiment when next I get the chance and let you know the results.

share|improve this question
    
>>TextElement on top of a RectangleElement -- are you looking for a balloon callout / text balloon? –  patrick Jan 11 '11 at 19:20
    
The PartialRefresh call is ok (you might also consider to refresh the graphics selection afterwards - esriViewGraphicSelection). Does it not work as expected? What behavior do you get? –  Petr Krebs Jan 11 '11 at 22:04
    
@iterationx - Well, I wasn't originally for various reasons, but I have been lately. The needs of the project are changing, and a balloon callout/text balloon might be called for. Maybe a simple text background would work. I'll fess up: these graphics are headed for storage in annotation features. I know it sounds "cloak-and-dagger" to say so, but that's all I am at liberty to say about it. –  celticflute Jan 12 '11 at 2:21
    
@petr k. - I tried refreshing with esriViewGraphicSelection, too, but it didn't help ... No, it does not work as expected. No graphic shows on screen. Then when I try to pan the screen it goes blank for about four seconds then redraws exactly as it was before I run the script. Then it is stuck there. Redrawing or panning just causes a redraw from cache with exactly the same scene. The View can no longer move (at least interactively in ArcMap). –  celticflute Jan 12 '11 at 2:28
add comment

3 Answers 3

up vote 5 down vote accepted

So, in the light of the tips in Kirk's link (ArcMap Automation: Man the Message Pumps!) and the knowledge that IObjectFactory must be used your scenario, here's my take on Python implementation of the discussed workaround.

To reiterate, the problem is that RPC calls, coming from a different process (python) might get stalled in ArcMap. The general idea described in the aforementioned article is that by pummeling ArcMap's message loop with WM_NULL messages, you force it to process your RPC calls as well.

I am not versed in Python, in fact this might only be my fifth piece of Python code or so, if memory serves. If anyone wants to improve/fix it in any way, go ahead. I ran it on Python 2.6.5.

import ctypes
import threading

class EsriAppMessageHelper(threading.Thread):
    def __init__(self, windowHandle):
        threading.Thread.__init__(self)
        self.running = True
        self.windowHandle = windowHandle

    def stop(self):
        self.running = False

    def run(self):
        while self.running:
            ctypes.windll.user32.SendMessageA(self.windowHandle, 0, 0, 0)
            #print 'WM_NULL to window %d sent' % self.windowHandle

    def __enter__(self):
        self.start()

    def __exit__(self, type, value, traceback):
        self.stop()

Basically, it's a thread which sends WM_NULL (0) messages to the given window. The __enter__ and __exit__ functions support Python's with statement. Here's how it's used (the ArcObjects related functions are taken from this post in a different question):

# Get the ArcMap's IApplication reference.
# We're in a different process than ArcMap's, so it gets resolved using IAppROT.
app = GetApp()

# WM_NULL messages will be sent from another thread while
# this block is executing
with EsriAppMessageHelper(app.hWnd):

    objectFactory = CType(app, esriFramework.IObjectFactory) # use IObjectFactory
    mxDoc = CType(app.Document, esriArcMapUI.IMxDocument)
    graphicsContainer = mxDoc.ActiveView.GraphicsContainer

    textElement = CType(objectFactory.Create("esriCarto.TextElement"), esriCarto.ITextElement)

    # initialize the symbols and elements, assign their geometries
    # etc...

    graphicsContainer.AddElement(element, 0)
    mxDoc.ActiveView.PartialRefresh(esriCarto.esriViewGraphics, None, None)

# At this point, we're out of the WITH block.
# The separate thread is stopped and no more WM_NULL messages are being sent.
print "DONE"

Try it out. I could not fully test it with my ArcMap 10 setup because I did not have any issues with the active view refresh before (it executed immediately and refresh was successful), but the WM_NULL messages do get sent.

share|improve this answer
    
Thank you, petr. I will try it and report back here on the results. Once again, very slick! –  celticflute Jan 14 '11 at 5:22
    
Petr, I have implemented your above solution, and the refresh still isn't working. Might the SendMessageA function in your message helper be at fault? This is the ANSI version of the function. Should I be using the Unicode (SendMessageW) version instead? As far as I can tell, the Python 2.6.5 version that ships with ArcGIS is the unicode version. –  celticflute Jan 17 '11 at 18:55
1  
Yes you might try that. Also, consider adding a time.sleep() call after PartialRefresh() or Refresh(), but still within the WITH block, so that the message pumping continues for a while before the code block ends. –  Petr Krebs Jan 17 '11 at 19:02
    
Still, as I said, I would love to help you debugging it, but for some reasons I have no problems with the PartialRefresh call, without needing any workarounds. –  Petr Krebs Jan 17 '11 at 19:04
    
Thank you, again, Petr. I, too, starting having no problems with the PartialRefresh. Then, just I got used to that, I got an RPC server unavailable message. I cranked up the message pump and everything works fine. I set things up to give me a message when the pump is used. Although not often, it does still kick in now and then. I hate to use such big hammers, but if it works ... –  celticflute Jan 18 '11 at 13:14
show 1 more comment

If you're "in a standalone Python script outside the application boundary" I think you'll need to use IObjectFactory. See page 13 of http://www.pierssen.com/arcgis/upload/misc/python_arcobjects.pdf

Update Also, if you're trying to refresh the display across a process boundary, you may need to send some extra windows messages to force a refresh. Here's a good explanation.

share|improve this answer
    
That's right, it's not strictly needed, but more than recommended as it reduces the amount of interprocess COM calls to a minimum. –  Petr Krebs Jan 11 '11 at 21:59
    
@Kirk Kuykendall - So, if I don't use IObjectFactory (not strictly needed as petr k. says), does this mean that the objects I am creating are not in process, and that's why they are not showing up on screeen? –  celticflute Jan 12 '11 at 1:59
    
@Kirk Kuykendall - see my addition to my original post above. –  celticflute Jan 12 '11 at 2:53
1  
@celticflute: The article offers a good solution but does not explain what's going on very thoroughly. Basically, DCOM RPC calls are (between processes or even machines) are marshalled so that they execute inside the ArcMap's process, but on a different thread spawned by the RPC infrastructure. ArcMap runs in a single thread and to synchronize the calls, they are pushed as window messages onto the ArcMap's window message queue. This queue is then processed by the application's main thread. The solution in Kirk's link forces ArcMap to process its message loop as soon as possible. –  Petr Krebs Jan 12 '11 at 9:28
1  
@Kirk: well, the extension would need to provide some kind of context object or methods, which would allow you to bracket the background message pumping only to a limited code-span (just like the disposable class in the article). I do not think constant pumping, taking up ArcMap's CPU processing these messages, would be a good thing. –  Petr Krebs Jan 12 '11 at 18:28
show 7 more comments

It has been a while since I have done anything like this, but, if the elements are located on the page layout instead of within a data frame, they are handled differently. See Working with PageLayout elements and Working with map elements

Also see How to redraw the display

share|improve this answer
    
The Working with map elements page pretty much describes exactly what I am doing. The VB .NET code example is essentially exactly like my code. I assume the example code functions as intended, so that's got me thinking about the whole IObjectFactory thing. Hmmm. –  celticflute Jan 12 '11 at 2:09
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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