# How to align modal draw to the middle of the 3d viewport?

I have a question about the opengl modal draw :

How to align text from a modal draw to the middle of the viewport and and keep it automatic between Tool Shelf and Property Shelf, when i changing them?

I want to avoid that the property shelf overlapp the modal draw on the right viewport side, when i change the scale of left toolshelf.

Maybe there is a similar question always done?

• You draw in the window type region of the space you are drawing text on (eg the 3d view). The x, y, width and height attributes of the region take into account the tool / property bars. – batFINGER May 2 '18 at 12:21
• If the text is generated dynamically, major issue is calculating the position based on the width to get it centered (IMHO). Probably related: blender.stackexchange.com/q/57709/31447 – brockmann May 2 '18 at 12:26

According to the API, Area.region collection holds all "regions of the screen area" as well as their dimensions. In case of the 3d Viewport: Header, Transform Panel and N-Panel.

The following code is based on this answer, extended with a simple calculation to get the correct position of the text in the 3d Viewport. Notice that you can also iterate through the collection and sum up the dimensions instead of doing that stepwise.

import bpy
import bgl
import blf

def draw_callback_px(self, context):

bgl.glEnable(bgl.GL_BLEND)

# ...api_current/bpy.types.Area.html?highlight=bpy.types.area
npanel_width = context.area.regions[1].width
transorm_panel_width = context.area.regions[3].width

width = context.area.width - npanel_width - transorm_panel_width

# draw text
text = "Hello World"
font_id = 0  # XXX, need to find out how best to get this.
bgl.glColor4f(*(1.0, 1.0, 1.0, 1))
blf.size(font_id, 70, 72)
dim_x = blf.dimensions(font_id, text)[0]/2
dim_y = blf.dimensions(font_id, text)[1]
blf.position(font_id, width/2-dim_x, height/2-dim_y, 0)
blf.draw(font_id, text)

# restore opengl defaults
bgl.glLineWidth(1)
bgl.glDisable(bgl.GL_BLEND)
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)

class ModalDrawOperator(bpy.types.Operator):
"""Draw a line with the mouse"""
bl_idname = "view3d.modal_operator"
bl_label = "Simple Modal View3D Operator"

def modal(self, context, event):
context.area.tag_redraw()

if event.type in {'RIGHTMOUSE', 'ESC'}:
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
return {'CANCELLED'}

return {'RUNNING_MODAL'}

def invoke(self, context, event):
if context.area.type == 'VIEW_3D':
# the arguments we pass the the callback
args = (self, context)
# Add the region OpenGL drawing callback
# draw in view space with 'POST_VIEW' and 'PRE_VIEW'
self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
return {'RUNNING_MODAL'}
else:
return {'CANCELLED'}

def register():
bpy.utils.register_class(ModalDrawOperator)

def unregister():
bpy.utils.unregister_class(ModalDrawOperator)

if __name__ == "__main__":
register()


To display the text as well as allow the user to interact with the UI, you can change the return statement of the modal function from {'RUNNING_MODAL'} to {'PASS_THROUGH'}:

...

def modal(self, context, event):
context.area.tag_redraw()

if event.type in {'RIGHTMOUSE', 'ESC'}:
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
return {'CANCELLED'}

return {'PASS_THROUGH'}

...


Also see the API on Modal Execution: https://docs.blender.org/api/blender_python_api_current/bpy.types.Operator.html#modal-execution

• Thank you! That's really detailed. Again briefly repeated: >> total window minus tpanel minus npanel results is real area. >> divided by 2 for the center point. >> minus lenght of the text divided also by 2 – MKB May 3 '18 at 11:19