Page MenuHome

Python: Allow Area Close via Scripting
ClosedPublic

Authored by Harley Acheson (harley) on Aug 23 2021, 10:50 PM.

Details

Summary

Screen area maintenance "Close" function allowed to be scripted.


This patch allows the new area "close" operator to be called from python, like:

area = bpy.context.screen.areas[0]  # Pick any area.
bpy.ops.screen.area_close({"area": area})

Diff Detail

Repository
rB Blender
Branch
arcpatch-D12307_1 (branched from master)
Build Status
Buildable 16773
Build 16773: arc lint + arc unit

Event Timeline

Harley Acheson (harley) requested review of this revision.Aug 23 2021, 10:50 PM
Harley Acheson (harley) created this revision.

In principle this seems reasonable, notes:

  • A rationale for why this window management operators should be supported from Python (since as far as I can see this is an exception to the rule).
  • Note that this logic is code-path is not used for typical user interactions, that it is separate logic specifically for use with Python.
  • Removing the active area can cause crashes in my tests (no matter what type is is), since a python script my run with any area being active.

Here is an update to this patch which addresses the issues noted:

P2348

  • Always use the active area (no need to search for it based on location).
  • Clear the window on completion so event handling doesn't attempt to access the freed area.
  • An additional sanity check that the area belongs to the current screen.
  • Dalai mentioned this was useful for customizing application templates (noted in comments).
  • Removed redundant checks already ensured by the poll function.

Any issues with applying the updated patch?

Hey @Campbell Barton (campbellbarton) and @Harley Acheson (harley),

thanks for looking in to this! Just to give an example of how this can be used:

Registering a hotkey via python that toggles an area. For example show and hide the timeline. For that you need to be able to somewhat reliably split and join / close areas. Especially for application templates that highly customize blender for a specific need it can be very useful to make this more approachable via the python api.

Hi @Paul Golter (paulgolter) is this patch enough to do the kinds of manipulations you need? Or do other changes need to be made to support toggling areas?

Hey @Campbell Barton (campbellbarton),

Yes I just tested the patch, and it works fine! Thanks a lot.

From a scripting perspective we need to do the usual context overriding which is always a little cumbersome.
I am not sure how much work it would to have the option to give the operator: bpy.ops.screen.area_close(area=my_area) an area instance as input to make it easier to use. Maybe this also goes against the convention, which I am not sure about.

In any way this already helps a lot.

@Campbell Barton (campbellbarton) - Any issues with applying the updated patch?

Sorry but I honestly don't know and am hoping to rely on you a bit here as I just don't use Python so am not used to considering it.

This came about not from anyone directly asking me for scripted "close" but for at least three people in the last couple years asking for "Join" to work from Python.

I was considering that Close and Join not working for them to be a failing of mine to not think of that earlier. I had planned on adding python support for both, but only started with Close because it would be more constructive for me - I wouldn't just make the same mistakes twice.

Join is more problematic in that both a source and destination must be specified, so they'd need not only that confusing "cursor" at the common edge, but also a "direction".

Your fixed version always closes the active area, not an arbitrary one - and works great. I honest don't know if that is enough for them or not. Can they change the active area non-interactively? If so, can we just do this and not do Join?

@Harley Acheson (harley) the active area can be set as follows:

area = bpy.context.screen.areas[0]  # Pick any area.
bpy.ops.screen.area_close({"area": area})

As for more advanced logic some possibilities:

  • A direction argument could be passed in.
  • Edges could be exposed and they could be parsed in (this opens up questions about how much of the screen/WM we want to expose to Python though).
  • This could be moved to the RNA API, with so a more convenient function can be added that takes two areas (or use a method that takes an "other" area).

This could be handled as part of ongoing development.

@Campbell Barton (campbellbarton) - bpy.ops.screen.area_close({"area": area})

Okay, I had no idea that it worked like that. Tested it and works like magic.

This Close then is so simple that we could conceivably commit only this and ignore the more complex "Join" for now? Area split and close should be enough to do anything anyone would want I think. With the only "bad" possibility of the wrong area taking over the closed area's space. But that is not destructive and easy to deal with. And if not we could do Join later?

Harley Acheson (harley) planned changes to this revision.Aug 31 2021, 2:51 AM

Will try to add an optional "Direction" argument to this. That way this one thing can do the job of Join too

A few bits confuse me here, noted inline.

One issue of exposing more advanced screen operations in Python is indeed how to pass the areas to use to the operators. Pointer properties are not an option unfortunately (e.g. bpy.ops.screen.foo(area=somearea)). This workaround via context overrides only works for single areas.
It would be easy to generate some UID for each area (integer or string) that we can pass around. If we want these advanced operations, this is the way I'd suggest going.

source/blender/editors/screen/screen_ops.c
1495

This is already checked by the poll(). Any reason to duplicate this?

1549–1550

Is there a need to keep both invoke() and exec()? I'd say we can just keep this all in exec(). Or if we don't want to run the CTX_wm_window_set() unless needed, could they at least share other code?

One issue of exposing more advanced screen operations in Python is indeed how to pass the areas to use to the operators. Pointer properties are not an option unfortunately (e.g. bpy.ops.screen.foo(area=somearea)). This workaround via context overrides only works for single areas.

Having UUID's feels like an unnecessary indirection.
In this case I'd prefer to expose this via RNA API's

for example, context.area.join(other_area)

Years back there was an choice not to expose windowing operations in the same way we do for scene data (@Nathan Letwory (jesterking) made a patch for RNA/windowing data and it was either rejected/reverted).

This could be re-evaluated, however if operators are "good-enough", we could continue to use them as there are already a lot of projects needing our attention.

Julian Eisel (Severin) edited the summary of this revision. (Show Details)
  • Merge branch 'master' into arcpatch-D12307
  • Merged master, correct base branch

@Julian Eisel (Severin) - This is already checked by the poll(). Any reason to duplicate this?

No, an oversight now fixed. Thanks!

Is there a need to keep both invoke() and exec()? I'd say we can just keep this all in exec(). Or if we don't want to run the CTX_wm_window_set() unless needed, could they at least share other code?

As far as I can see calling CTX_wm_window_set() causes no harm when called interactively, although maybe there is something I'm not aware of with that. So just made this a single exec function instead.

Harley Acheson (harley) edited the summary of this revision. (Show Details)Sep 3 2021, 10:43 PM
This revision is now accepted and ready to land.Sep 3 2021, 11:21 PM
Campbell Barton (campbellbarton) added inline comments.
source/blender/editors/screen/screen_ops.c
1481

Worth adding a note that checking the area is inside the screen is for Python scripts that could pass in an invalid area that doesn't match the screen (since this check isn't done anywhere else).

Although we could lookup the screen from the area, I don't think it's necessary.

Harley Acheson (harley) edited the summary of this revision. (Show Details)

Comments updated.

This revision was automatically updated to reflect the committed changes.