1
$\begingroup$

I'm working on an add-on that performs some operations on items in Outliner (actually it enables me to batch rename collections, a feature missing from Blender). The addon works fine if I assign a keyboard shortcut to it, click Outliner and hit the key. However it does not work if I launch it from another window, or from the Edit menu. In these cases I receive the 'Context' object has no attribute 'selected_ids' error.

I tried a few things found on the net, which were usually trying to get the outliner window and override the context. One is this:

   for area in bpy.context.screen.areas:
        if area.type == 'OUTLINER':
            override = bpy.context.copy()
            override['area'] = area

I thought I can just call override.selected_ids instead of bpy.context.selected_ids, but I cannot for an 'dict' object has no attribute 'selected_ids' error.

Have any of you managed to find a way to switch context to Outliner?

I need a solution for Blender 2.92+

$\endgroup$
6
  • $\begingroup$ Suggest add more detail and especially what operator(s) you'd like call. At the moment you're just trying to override the context of the current area rather than switching the area to the outliner. BTW: The blender version doesn't really matter in this case. Out of curiosity: Where selected_ids attribute come from? Also I'd suggest take the tour to learn about how this site works. $\endgroup$
    – brockmann
    May 15, 2021 at 22:33
  • $\begingroup$ Often the case need to override space_data and region also. 99% of qs re context override would be an example. $\endgroup$
    – batFINGER
    May 16, 2021 at 4:16
  • $\begingroup$ @brockmann, thanks for your reply. I just want to grab the objects selected in the Outliner and change their name property. As far as I learned, selected_ids for bpy.context only exists if the Outliner was the active area at the moment of starting the script. I want to be able to use it from other areas as well, and I think I first somehow need to switch to Outliner, so that I can get the result of bpy.context.selected_ids and process it. Let me know if I still miss something from my description. $\endgroup$
    – T1nk-R
    May 17, 2021 at 8:02
  • $\begingroup$ I don't think there is something like selected_ids, see the search results of the api docs: docs.blender.org/api/current/… . Anyway, to avoid confusion I recommend just tell us what you'd like to do because you do not have to "switch" to the outliner in order to rename the selected objects... and again overriding the context and switching the ouliner is a different thing. $\endgroup$
    – brockmann
    May 17, 2021 at 8:08
  • $\begingroup$ @brockmann, sorry but I don't get what details you miss. I can just repeat myself, I'm afraid: I want to change names of collections. Anyway, bpy.context.selected_ids works properly if the active area is the Outliner, and I can get the list of selected collections and objects, but if the active area is different at the moment of launching the script, bpy.context returns a different object, which actually does not have this property defined, and calls to it therefore cause an exception. So to be able to get the list, it seems that I have to switch to the proper context. $\endgroup$
    – T1nk-R
    May 18, 2021 at 9:39

3 Answers 3

7
$\begingroup$

After some more digging, I figured out a better way, that is probably the way intended by the devs:

using bpy.context.temp_override(area=override_area) (documentaiton page)

Here is the same example as above with the new solution:

#Initialize the override_area in case it does not need to be overriden
areaOverride=bpy.context.area    
if bpy.context.area.type!='OUTLINER':
    #If needed find the outliner area
    for area in bpy.context.screen.areas:
        if area.type == 'OUTLINER':
            areaOverride=area

with bpy.context.temp_override(area=areaOverride):
    #Any code you want...
    for obj in bpy.context.selected_ids:
        print(f"Selected: {obj.name} - {type(obj)}") #Notice you also get selected collections, normally impossible outside of Outliner context

With that, no more issues.

$\endgroup$
3
  • $\begingroup$ Note that the use of a context dictionary for an override is deprecated in 3.2 and scheduled to be removed in 3.3, both to be replaced by context.temp_override(). $\endgroup$ Jul 15, 2022 at 22:53
  • $\begingroup$ @MartyFouts I am sorry, I don't have that much experience in python. Could you develop? For example, if we use context.temp_override(override_area) we would no longer have access to its "selected_ids" property? > both to be replaced by context.temp_override() Is that not what I am doing in that code sample above? You seem to imply that this solution will no longer be supported in 3.3 $\endgroup$
    – JulienH
    Jul 18, 2022 at 11:19
  • $\begingroup$ Take a look at this answer and the one it mentions and this section of the manual for an explanation. $\endgroup$ Jul 18, 2022 at 15:02
4
$\begingroup$

I was in the same situation I wanted to reach selected_ids from another context than the Outliner, and it looks like you can use this solution: how to set context to run operations

It is a hack, this solution consists in:

  • Put the outliner in full screen (Forcing the active context to the Outliner)
  • You execute your code in OUTLINER context
  • You revert the action of putting the Outliner in fullscreen. Since it all happens in one frame, the user does not see that the outliner was in full screen.

Noticeable issues:

  • The header of the originally active window context blinks for 1 frame.
  • The collection scrolls vertical scroll gets to its lower position. If anyone figure out a way to prevent that, or reset the scrolls, I would love to hear. (Impossible to use the outliner operators after the view is reverted.)

Here is an example of how to use it:

def setOutlinerContext():
    for area in bpy.context.screen.areas:
        if area.type == 'OUTLINER':
            override = bpy.context.copy()
            override['area'] = area
            bpy.ops.screen.screen_full_area(override, use_hide_panels=False)

def MyFunctionExecutedOutsideOutlinerContext:
    setOutlinerContext() 
    #At this point the Outliner is in full screen and is the active context

    #Execute your code... For example:
    for obj in bpy.context.selected_ids:
        print(f"Selected: {obj.name} - {type(obj)}") #Notice you also get selected collections, normally impossible outside of Outliner context

    #Important: now revert the UI to its original state
    bpy.ops.screen.back_to_previous() 
```
$\endgroup$
1
$\begingroup$

Since Blender 4.0 JulienH's method stopped working - now Blender is also requiring to override the region, not just the area.

import bpy

def get_outliner_area():
    if bpy.context.area.type!='OUTLINER':
        for area in bpy.context.screen.areas:
            if area.type == 'OUTLINER':
                return area

area = get_outliner_area()
region = next(region for region in area.regions if region.type == "WINDOW")

with bpy.context.temp_override(area=area, reigon=region):
    for obj in bpy.context.selected_ids:
        print(f"Selected: {obj.name} - {type(obj)}")
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .