TL;DR: Skip middle paragraph.

I'm trying to make an addon that draws certain info in the view, and have it do that basically all the time that the addon is on.

The way I've been told bgl drawing works is that you put the drawing code in a modal operator. However, all examples I've seen involve starting it from the invoke() method, which is run when the user executes the operator - not what I want. I've also tried putting the bpy.context.window_manager.modal_handler_add(self) code in the register() class method, but because that method does not have access to self, I only got errors with the various arguments I tried (which were: none, the class (its general name), the cls argument that register(cls) gets, and some others. If needed I'll expand on this).

How do I make the operator run "on startup", or how else can I achieve my goal?

  • $\begingroup$ have you tried bpy.app.handlers.load_post.append(load_handler) for running at startup $\endgroup$ – Chebhou Apr 26 '15 at 20:00
  • $\begingroup$ No, I haven't. What would load_handlerbe in that context? $\endgroup$ – Ludwik Apr 26 '15 at 20:26
  • $\begingroup$ Ohh, I'm reading the docs right now, handlers might just be awesome! $\endgroup$ – Ludwik Apr 26 '15 at 20:27
  • $\begingroup$ But hey - couldn't I just use bpy.app.handlers.frame_change_post to draw my stuff and skip the operator altogether? $\endgroup$ – Ludwik Apr 26 '15 at 20:36
  • $\begingroup$ that would be while the animation playing only $\endgroup$ – Chebhou Apr 26 '15 at 20:37

I am afraid this is a limitation and you cannot interactively draw with bgl outside of modal operator. There is no way to initialize the drawing handler because it needs the modal operator for access to events and the modal loop where tag redraw would happen.

Tag redraw could be done but the events are the bummer. Linking similar: Get events without running modal

From blender api user events outside of modal can't be caught. There is probably a possibility to catch them with python in monitor space and from blender areas and window positions get them local (like mouse coords).

From user perspective how blender is made it seems the philosophy is that user should have control over modal operators and start/end them manually ideally with button in ui. So something doesn't run in the background without your knowledge. There is somewhere an answer from Brecht on something similar but couln't find it.

There is possibility to call an operator from handlers, but mostly you will get another limitation:

 "RuntimeError: maximum recursion depth exceeded"

Calling the operator from load_pre handler should be fine though. See similar: My custom timer is cancelled when loading a *.blend file

  • $\begingroup$ Yes, I am beginning to see that this might border on the impossible. However, load_pre is not good either because, from what I know, it only runs wneh I'm opening a new file. Or am I wrong? $\endgroup$ – Ludwik Apr 27 '15 at 10:25
  • 1
    $\begingroup$ @Ludwik you are correct, what we need is to monitor whether the modal operator is running or not in the scene_update_pre handler and call it if its not running. This however gives the runtime error :( .. I once came across same issue when trying to display a code minimap in text editor that would display automatically. $\endgroup$ – Jaroslav Jerryno Novotny Apr 27 '15 at 11:30
  • $\begingroup$ Wrong! I actually just found a way to draw outside a modal operator. Will add my own answer soon. Nevertheless, thanks ;) $\endgroup$ – Ludwik Apr 28 '15 at 9:47
  • $\begingroup$ @Ludwik If you did your will be a hero and I would thank you billion times! $\endgroup$ – Jaroslav Jerryno Novotny Apr 28 '15 at 12:38

It seems impossible to make a modal operator run "on startup", without the user starting it. However, it also seems that the following code adds a drawing function on startup (when the addon is registered) and that that function gets executed indefinitely:

def draw_callback_px():
    #draw whatever you want here, just thought I'd add something

    bgl.glColor4f(0.0, 0.0, 0.0, 0.5)


    bgl.glVertex3f(0, 0, 0)
    bgl.glVertex3f(1, 0, 0)
    bgl.glVertex3f(1, 1, 0)
    bgl.glVertex3f(1, 1, 1)


    # restore opengl defaults
    bgl.glColor4f(0.0, 0.0, 0.0, 1.0)

def register():
    bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_VIEW')

If I now install this addon, the drawing function is appended there both on startup, when installing the addon; even if we're recovering the previous session. At least according to my testing. If someone could test this out to confirm, please do.

...one tiny caveat tho. This does not ever tag the drawing area for redrawing, so to see the drawings, one sometimes has to interact with the 3Dview are at least once after installing the addon.

  • $\begingroup$ maybe adding bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) to force redraw will solve the "caveat" $\endgroup$ – Chebhou Apr 28 '15 at 13:36
  • 1
    $\begingroup$ Well yeah this will be good for static drawing, I've done this also, but couldn't access events, thus mouse coords, which I need for interactivity:( $\endgroup$ – Jaroslav Jerryno Novotny Apr 28 '15 at 13:40
  • $\begingroup$ Yeah, sadly, that's the case. $\endgroup$ – Ludwik Apr 28 '15 at 13:43
  • $\begingroup$ hi, just came across this question, thanks for this ! i was searching for a way to add static draw through python ! but i'm stucked with the unregistering of this handler... it can't pass a handle, like other draw handler. I'm no coder, just enthusiastic and don't have a clue, do you have any idea ? :) thank you very much ! $\endgroup$ – tonton Jan 16 '18 at 17:48

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.