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
header_height = context.area.regions[0].height # 26px
npanel_width = context.area.regions[1].width
transorm_panel_width = context.area.regions[3].width
width = context.area.width - npanel_width - transorm_panel_width
height = context.area.height + header_height
# 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')
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "View3D not found, cannot run operator")
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