How do I get a RegionView3D object from a Region object?

I'm working on an operator in the quad view of the 3D view and I need to determine which region the user clicked in and the relative position to that specific region

  • I've tried event.mouse_region_x and y but those are relative to the original region that the operator was ran in
  • I've also tried using the ctx object provided to the operator's modal function as well as bpy.context but both seem to be frozen on the original place the operator was ran in?
  • Current solution is to check all areas in bpy.context.screen.areas, find the one clicked in and then find the region in that area. This means I now have a region but I can't get the RegionView3D from it. This is because RegionView3D is accessed from a Space object and that doesn't have any actual positional information on the screen.

A RegionView3D is actually a space, the access path is bpy.context.screen.areas[#].spaces.active.region_3d (area being of type VIEW_3D), or in case of quadview mode, ... spaces.active.region_quadviews[#].

There are no direct references from a region to a RegionView3D.

It's possible to find the right quadview region and its space, here's a (modal timer) example:

import bpy
import math

def get_view_orientation(space, quadview):

    if quadview.view_perspective == 'CAMERA':
        view_orientation = "Camera Persp"

        r = lambda x: round(x, 2)
        view_rot = quadview.view_matrix.to_euler()

        orientation_dict = {(0.0, 0.0, 0.0) : 'TOP',
                            (r(math.pi), 0.0, 0.0) : 'BOTTOM',
                            (r(-math.pi/2), 0.0, 0.0) : 'FRONT',
                            (r(math.pi/2), 0.0, r(-math.pi)) : 'BACK',
                            (r(-math.pi/2), r(math.pi/2), 0.0) : 'LEFT',
                            (r(-math.pi/2), r(-math.pi/2), 0.0) : 'RIGHT'}

        view_orientation =  orientation_dict.get(tuple(map(r, view_rot)), 'USER').capitalize()
        view_orientation += " " + quadview.view_perspective.capitalize()

    if space.local_view is not None:
        view_orientation += " (Local)"

    return view_orientation

def get_quadview_index(context, x, y):
    for area in context.screen.areas:
        if area.type != 'VIEW_3D':
        if len(area.spaces.active.region_quadviews) == 0:
        i = -1
        for region in area.regions:
            if region.type == 'WINDOW':
                i += 1
                if (x >= region.x and
                    y >= region.y and
                    x < region.width + region.x and
                    y < region.height + region.y):

                    return (area.spaces.active, i)
    return (None, None)

class ModalTimerOperator(bpy.types.Operator):
    """Operator which runs its self from a timer"""
    bl_idname = "wm.modal_timer_operator"
    bl_label = "Modal Timer Operator"

    _timer = None

    def modal(self, context, event):
        if event.type in {'RIGHTMOUSE', 'ESC'}:
            return self.cancel(context)

        if event.type == 'TIMER':
            space, i = get_quadview_index(context, event.mouse_x, event.mouse_y)
            if space is not None:
                quadview = space.region_quadviews[i]
                print(get_view_orientation(space, quadview))

        return {'PASS_THROUGH'}

    def execute(self, context):
        wm = context.window_manager
        self._timer = wm.event_timer_add(0.5, context.window)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        wm = context.window_manager
        return {'FINISHED'}

def register():

def unregister():

if __name__ == "__main__":

    # test call
  • $\begingroup$ Is it safe to assume that in area.regions will have regions in the same order every time? Same with spaces.active.region_quadviews? Because then that simplifies things a lot $\endgroup$ – Coburn Jan 19 '15 at 18:51
  • $\begingroup$ I believe they are. You may wanna check the code that many all 4 regions available to python. Here's the related bug report: developer.blender.org/T39028 $\endgroup$ – CodeManX Jan 20 '15 at 9:25

It doesn't actually seem possible.

Using this other topic "Getting the active region" as sort of a jumping off point, you'll have to go through and compare all the regions positions to the indices of the quadviews list (having to assume that quadviews is a fixed list where 0 is Front, 1 is Top, etc.)

Couldn't come up with a better way to sort these but here it is

#| reg 1  (TOP/XY)  | reg 3 (USER/USR) |
#| reg 0 (FRONT/XZ) | reg 2 (RIGHT/YZ) |
#First find reg 0
minReg = None
for reg in ctx.area.regions:
    if reg.type != "WINDOW":
    if not minReg:
        minReg = reg
    if reg.x <= minReg.x and reg.y <= minReg.y:
        minReg = reg

#Place all the other regions with respect to reg 0
regions = [None, None, None, None]
regions[0] = minReg
for reg in ctx.area.regions:
    if reg.type != "WINDOW" or reg == regions[0]:
    if reg.x > regions[0].x and reg.y > regions[0].y:
        regions[3] = reg
    elif reg.x > regions[0].x:
        regions[2] = reg
    elif reg.y > regions[0].y:
        regions[1] = reg

#Make the dictionary
self.viewDict = {
    "XY" : (ctx.space_data.region_quadviews[0], regions[0])
    "XZ" : (ctx.space_data.region_quadviews[1], regions[1])
    "YZ" : (ctx.space_data.region_quadviews[2], regions[2])
    "USER" : (ctx.space_data.region_quadviews[3], regions[3])

Your Answer

