5
$\begingroup$

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?

$\endgroup$
7
  • $\begingroup$ have you tried bpy.app.handlers.load_post.append(load_handler) for running at startup $\endgroup$
    – Chebhou
    Apr 26, 2015 at 20:00
  • $\begingroup$ No, I haven't. What would load_handlerbe in that context? $\endgroup$
    – Ludwik
    Apr 26, 2015 at 20:26
  • $\begingroup$ Ohh, I'm reading the docs right now, handlers might just be awesome! $\endgroup$
    – Ludwik
    Apr 26, 2015 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, 2015 at 20:36
  • $\begingroup$ that would be while the animation playing only $\endgroup$
    – Chebhou
    Apr 26, 2015 at 20:37

2 Answers 2

3
$\begingroup$

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

$\endgroup$
4
  • $\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, 2015 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$ Apr 27, 2015 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, 2015 at 9:47
  • $\begingroup$ @Ludwik If you did your will be a hero and I would thank you billion times! $\endgroup$ Apr 28, 2015 at 12:38
1
$\begingroup$

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.glEnable(bgl.GL_BLEND)
    bgl.glColor4f(0.0, 0.0, 0.0, 0.5)
    bgl.glLineWidth(2)

    bgl.glBegin(bgl.GL_LINE_STRIP)

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

    bgl.glEnd()

    # restore opengl defaults
    bgl.glLineWidth(1)
    bgl.glDisable(bgl.GL_BLEND)
    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.

$\endgroup$
4
  • $\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, 2015 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$ Apr 28, 2015 at 13:40
  • $\begingroup$ Yeah, sadly, that's the case. $\endgroup$
    – Ludwik
    Apr 28, 2015 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, 2018 at 17:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .