Join two areas by python (area_join) ? What arguments? (Blender 2.80+)

Hello,
I’m trying to join two areas (split horizontally) but don’t seem to understand why it’s not working…

def execute(self, context):

    area1 = bpy.context.screen.areas[3]
    area2 = bpy.context.screen.areas[-1]
        
    bpy.ops.screen.area_join(cursor=(area1.y, area2.y))

I tried all 16 combinations, some of them joined the areas but worked only once when I ran the code (not when I called the operator, strangely), but if I drag another screen to try again. It’s not working anymore.

What is the “cursor=” expecting as arguments?

Thanks.

That should be a position (both x then y) of a point between the two areas. Here is an ideal place for this in your examples:

But note that there is quite a bit of wriggle room so you don’t have to be too exact. In your example it could be anywhere along that horizontal line and vertically just has to be close.

How do you translate that in code for only 2 arguments?

Should the first and second arguments be x and y coordinates to reach that cross you made? Something like:

bpy.ops.screen.area_join(cursor=(area1.width, int(area2.height /2)))

Probably the first thing that is going to screw you up is that I don’t think we actually have exactly what you want exposed to python, which would be a simple non-interactive join of two areas. That would be useful.

This thing that you are calling is (I think) just going to initiate an interactive join at a particular position, normally actual cursor position but here you are overriding that with that “cursor” argument. To specify that position you’d need both horizontal and vertical values. You can’t just use widths and heights because the left and top edges are probably not at the screen edges. So X is more like area1.x + (area1.width / 2) and the Y is probably just area1.y. Or you could find the (almost) identical values using the top of area2.

Something like that anyway. I have never tried it from python, although I know some people have. Programmatically starting an interactive process seems a bit goofy though, since it will still require a movement of your mouse to finish it.

I tried this, but it didn’t work:

bpy.ops.screen.area_join(cursor=(area1.x + int((area1.width / 2)), area2.y))

I was thinking that if “area_split” allows to non-interactively split the area, then so would “area_join”.
Also there are some examples on the net, but they used to require 4 arguments instead of 2…

There is also this example here, but I can’t replicate it… I’m probably messing up the formula.

I’m not really surprised as that is not what I said to try. Looking at your illustration, with area1 on top and area2 below, this would be a location half-way across horizontally, but on the bottom on area2. Certainly isn’t between them.

So I narrowed down the formulas to join 2 areas!

I printed in the console, the coordinates to have a clearer vision, with the following instructions…

print("Area1 = X: %s \t Y: %s \t W: %s \t H: %s" % (area1.x, area1.y, area1.width, area1.height))
print("Area2 = X: %s \t Y: %s \t W: %s \t H: %s" % (area2.x, area2.y, area2.width, area2.height))

And here are the formulas that work… Kind of…

# VERTICAL SPLIT FORMULA
bpy.ops.screen.area_join(cursor=(area1.x, area1.y + area1.width))

# HORIZONTAL SPLIT FORMULA
bpy.ops.screen.area_join(cursor=(area1.x, area2.y + area2.height))

Now, a very weird thing is happening…
I have attributed the shortcut ‘F1’ to launch the operator (it works from all editors on the screen).

And then I follow these exact steps in that exact order:

1) I run the script 		# From the Text Editor nearby		
2) I call the operator (F1)	# Nothing happens (here I expected the areas to be joined)
3) I re-run the script		# The 2 areas get joined without any interaction on my part... Weird!

Any idea why this is happenning?

If you want to try, here is the full script:
(Just make sure the ‘index’ is correct for your 2 areas: bpy.context.screen.areas[index])

bl_info = {
    "name": "Asset Browser Shelf",
    "blender": (2, 82, 0),
    "category": "Custom"
}

import bpy
from bpy.types import Operator

# ASSET BROWSER SHELF
class Asset_Browser_Shelf(Operator):
    bl_idname = "asset_browser.shelf"
    bl_label = "Asset Browser Shelf"
    bl_description = "Description"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):

        # JOIN 2 AREAS
        area1 = bpy.context.screen.areas[3]
        area2 = bpy.context.screen.areas[-1]
        
        print("--------------------------")
        print("Area1 = X: %s \t Y: %s \t W: %s \t H: %s" % (area1.x, area1.y, area1.width, area1.height))
        print("Area2 = X: %s \t Y: %s \t W: %s \t H: %s" % (area2.x, area2.y, area2.width, area2.height))

        # VERTICAL SPLIT FORMULA
#        bpy.ops.screen.area_join(cursor=(area1.x, area1.y + area1.width))

        # HORIZONTAL SPLIT FORMULA
        bpy.ops.screen.area_join(cursor=(area1.x, area2.y + area2.height))

        return {'FINISHED'}

classes = (
    Asset_Browser_Shelf,
)

def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)

    wm = bpy.context.window_manager
    km = wm.keyconfigs.addon.keymaps.new(name = "Window", space_type='EMPTY', region_type='WINDOW')
    kmi = km.keymap_items.new('asset_browser.shelf', 'F1', 'PRESS')
    kmi.active = True

def unregister():
    from bpy.utils import unregister_class
    for cls in classes:
        unregister_class(cls)

    addon_keymaps = []

    if wm.keyconfigs.addon:
        for km in addon_keymaps:
            for kmi in km.keymap_items:
                km.keymap_items.remove(kmi)

            wm.keyconfigs.addon.keymaps.remove(km)

    addon_keymaps.clear()

if __name__ == "__main__":
    register()