Outliner GSoC Follow-up Feedback

outliner-wtf

Same goes if I just select the nodes in the tree.

I don’t know why it’s not implemented yet.
Maybe because performance issue but it can be easily done with simple script
(even without area.type = 'OUTLINER')

1 Like

Can someone test it. Should I report this as a bug?
Short description to reproduce:

  1. create new file
  2. select cube and add one more material
  3. expand cube in outliner to the materials list
  4. minimize cube list
  5. then click on any other green icon of any object
  6. cube become highlights only by hovering on object icon, and if click on cube name you get material list popup
    Only in 2.91.0.
    2.90 and 2.92 is ok, so no worry about it…
1 Like

Hi @jamez, I am replying here to keep related discussions together.

The custom object sorting worked in most cases during GSoC, and it would be technically possible to commit it. However, we decided to delay it with no timeline at the moment. We would prefer to do sorting on a UI level, rather than on a data level. The collections and modifiers you mention are a good example of the difference.

Modifiers are sorted on a data level, meaning that reordering the modifiers in the outliner actually rearranges the list in Blender’s internal database. This is good because the order matters during modifier evaluation. In the current implementation, collections are also reordered in the internal database lists, but we would prefer not to do this, primarily because there isn’t any reason to.

The preferred solution would be to custom sort objects, collections, (and other data) on an interface level. This means that the internal databases are untouched. The downside is this (UI level sorting) would be a new concept in Blender so it’s a bigger project. I would eventually like to work on this, but at the moment I don’t have the time.

Another problem with the GSoC implementation was it would be very difficult to do custom sorting of bones and object children in the outliner by actually reordering the internal lists.

6 Likes

@natecraddock thanks a lot for the detailed reply, it is appreciated! I really hope that the Blender Foundation can hire you in some capacity to finish the great work you did on the outliner. Maybe you are also planning on doing the GSOC again this year for further UI development ?

2 Likes

With new changes to 2.92 it’s possible now have a list what we selected in Outliner (can be multiple Collections selected also)
https://wiki.blender.org/wiki/Reference/Release_Notes/2.92/Python_API
and with python I was able to add new entries for Outliner context for Objects and for Collections selected (with my basic knowledge) such as:
For objects:

  • Duplicate’ for multiple selected objects
  • Duplicate Hierarchy’ for multiple selected objects without selecting parented elements
  • Duplicate Instance’ for multiple selected objects
  • ’Duplicate Instance Hierarchy’ for multiple selected objects without selecting parented elements
  • Select Hierarchy Multi’ for multiple selected objects
    (for this no needs in new ‘selected_ids’ btw)

For collections (possibile with new ‘selected_ids’ feature):

  • Select Objects’ for multiple selected collections
  • ’Duplicate Collections’ for multiple selected collections (thanks for people from blender.stackexchange)
  • Duplicate Linked’ for multiple selected collections

Maybe we can expect some rewriting for Outliner context in 2.93 now?

My little investigation in the form of addon for Outliner if someone want to test it and maybe improve it, you are welcome

Code here, since I can't upload .py file
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

#https://blender.stackexchange.com/questions/64951/add-menu-entry-to-generic-right-click-menu
#https://stackoverflow.com/questions/27265915/get-list-of-selected-objects-as-string-blender-python
#https://blender.stackexchange.com/questions/132825/python-selecting-object-by-name-in-2-8/132829
#https://blenderartists.org/t/python-scripted-button-select-hierarchy-parent-and-children-at-same-time/1132109/4

# Add-on info
bl_info = {
    "name": "Outliner Menu Entries",
    "author": "APEC",
    "version": (0, 1, 0),
    "blender": (2, 92, 0),
    "location": "Outliner > Objects RMB or Collections RMB",
    "description": "Adds menu entries for objects and collections in Outliner", 
    "doc_url": "",
    "tracker_url": "",      
    "category": "Outliner"
}

import bpy
from bpy.types import Operator
from collections import  defaultdict

###########################################################################################
################################### Functions #############################################
###########################################################################################

class OUTLINER_OT_duplicate(Operator):    
    bl_idname = "outliner.duplicate"
    bl_label = "Duplicate"
    bl_description = '''Duplicate selected objects'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):
        bpy.ops.object.mode_set(mode = 'OBJECT')    
        bpy.ops.object.duplicate()
        #dupli_obj = bpy.context.object
                
        return { 'FINISHED' }   

class OUTLINER_OT_duplicate_hierarchy(Operator):    
    bl_idname = "outliner.duplicate_hierarchy"
    bl_label = "Duplicate Hierarchy"
    bl_description = '''Duplicate selected objects with respecting the hierarchy'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):   
        bpy.ops.object.mode_set(mode = 'OBJECT')

        selection_names = [obj.name for obj in bpy.context.selected_objects]

        for o in selection_names:
            obj = bpy.context.scene.objects.get(o)
            bpy.context.view_layer.objects.active = obj
            obj.select_set(True)
            bpy.ops.object.select_grouped(extend=True, type='CHILDREN_RECURSIVE')

        bpy.ops.object.duplicate()
        
        return { 'FINISHED' } 

class OUTLINER_OT_duplicate_instance(Operator):    
    bl_idname = "outliner.duplicate_instance"
    bl_label = "Duplicate Instance"
    bl_description = '''Duplicate selected objects with instances'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):   
        bpy.ops.object.mode_set(mode = 'OBJECT')
        bpy.ops.object.duplicate(linked=True)
        #dupli_obj = bpy.context.object
                
        return { 'FINISHED' } 

class OUTLINER_OT_duplicate_instance_hierarchy(Operator):    
    bl_idname = "outliner.duplicate_instance_hierarchy"
    bl_label = "Duplicate Instance Hierarchy"
    bl_description = '''Duplicate selected objects with instances respecting the hierarchy'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):
        bpy.ops.object.mode_set(mode = 'OBJECT')

        selection_names = [obj.name for obj in bpy.context.selected_objects]

        for o in selection_names:
            obj = bpy.context.scene.objects.get(o)
            bpy.context.view_layer.objects.active = obj
            obj.select_set(True)
            bpy.ops.object.select_grouped(extend=True, type='CHILDREN_RECURSIVE')

        bpy.ops.object.duplicate(linked=True)

        return { 'FINISHED' } 

class OUTLINER_OT_select_hierarchy(Operator):    
    bl_idname = "outliner.select_hierarchy"
    bl_label = "Select Hierarchy Multi"
    bl_description = '''Selects hierarchy and parent for multiple objects'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):   
        bpy.ops.object.mode_set(mode = 'OBJECT')

        selection_names = [obj.name for obj in bpy.context.selected_objects]

        for o in selection_names:
            obj = bpy.context.scene.objects.get(o)
            bpy.context.view_layer.objects.active = obj
            obj.select_set(True)
            bpy.ops.object.select_grouped(extend=True, type='CHILDREN_RECURSIVE')
    
        return { 'FINISHED' } 

#https://blenderartists.org/t/how-to-select-all-objects-of-a-known-collection-with-python/1195742/3
class OUTLINER_OT_select_collection_objects(Operator):    
    bl_idname = "outliner.select_collection_objects"
    bl_label = "Select Objects Multi"
    bl_description = '''Selects all objects for multiple collections'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context): 

        collection_names = [col.name for col in bpy.context.selected_ids]

        # use only collections whose names are in collection_names
        collections = [col for col in bpy.data.collections if col.name in collection_names]

        for col in collections:
            for obj in col.all_objects:
                obj.select_set(True)
    
        return { 'FINISHED' } 


def copy_objects(from_col, to_col, linked, dupe_lut):
    for o in from_col.objects:
        dupe = o.copy()
        if not linked and o.data:
            dupe.data = dupe.data.copy()
        to_col.objects.link(dupe)
        dupe_lut[o] = dupe

def copy(parent, collection, linked=False):
    dupe_lut = defaultdict(lambda : None)
    def _copy(parent, collection, linked=False):
        cc = bpy.data.collections.new(collection.name)
        copy_objects(collection, cc, linked, dupe_lut)

        for c in collection.children:
            _copy(cc, c, linked)

        parent.children.link(cc)
    
    _copy(parent, collection, linked)
    print(dupe_lut)
    for o, dupe in tuple(dupe_lut.items()):
        parent = dupe_lut[o.parent]
        if parent:
            dupe.parent = parent

#https://blender.stackexchange.com/questions/157828/python-collection-duplicate-help
class OUTLINER_OT_duplicate_collections(Operator):    
    bl_idname = "outliner.duplicate_collections"
    bl_label = "Duplicate Collection Multi"
    bl_description = '''Duplicate multiple selected collections'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context): 

        context = bpy.context
        scene = context.scene

        collection_names = [col.name for col in bpy.context.selected_ids]

        for col_name in collection_names:
            col = bpy.data.collections.get(col_name)
            print(col, scene.collection)
            assert(col is not scene.collection)
            parent_col = context.scene.collection

            copy(scene.collection, col)
            # and linked copy
            #copy(scene.collection, col, linked=True)
                        
        return { 'FINISHED' } 

class OUTLINER_OT_duplicate_collections_linked(Operator):    
    bl_idname = "outliner.duplicate_collections_linked"
    bl_label = "Duplicate Linked Multi"
    bl_description = '''Duplicate multiple selected collections with linked object data'''
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context): 

        context = bpy.context
        scene = context.scene

        collection_names = [col.name for col in bpy.context.selected_ids]

        for col_name in collection_names:
            col = bpy.data.collections.get(col_name)
            print(col, scene.collection)
            assert(col is not scene.collection)
            parent_col = context.scene.collection

            copy(scene.collection, col, linked=True)
                        
        return { 'FINISHED' }         
###########################################################################################
#####################################    UI    ############################################
########################################################################################### 

def duplicate_menu_outliner(self, context):
    layout = self.layout
    layout.separator()
    layout.operator(OUTLINER_OT_duplicate.bl_idname)
    layout.operator(OUTLINER_OT_duplicate_hierarchy.bl_idname)
    layout.separator()
    layout.operator(OUTLINER_OT_duplicate_instance.bl_idname)
    layout.operator(OUTLINER_OT_duplicate_instance_hierarchy.bl_idname)
    layout.separator()
    layout.operator(OUTLINER_OT_select_hierarchy.bl_idname)

def select_colection_obects_menu_outliner(self, context):
    layout = self.layout
    layout.separator()
    layout.operator(OUTLINER_OT_duplicate_collections.bl_idname)
    layout.operator(OUTLINER_OT_duplicate_collections_linked.bl_idname)
    layout.separator()
    layout.operator(OUTLINER_OT_select_collection_objects.bl_idname)
   
###########################################################################################
##################################### Register ############################################
########################################################################################### 	

def register():
    bpy.utils.register_class(OUTLINER_OT_duplicate)
    bpy.utils.register_class(OUTLINER_OT_duplicate_hierarchy)
    bpy.utils.register_class(OUTLINER_OT_duplicate_instance)
    bpy.utils.register_class(OUTLINER_OT_duplicate_instance_hierarchy)
    bpy.utils.register_class(OUTLINER_OT_select_hierarchy)
    bpy.types.OUTLINER_MT_object.append(duplicate_menu_outliner)
    
    bpy.utils.register_class(OUTLINER_OT_select_collection_objects)
    bpy.utils.register_class(OUTLINER_OT_duplicate_collections)
    bpy.utils.register_class(OUTLINER_OT_duplicate_collections_linked)
    bpy.types.OUTLINER_MT_collection.append(select_colection_obects_menu_outliner)
    
def unregister():
    bpy.utils.unregister_class(OUTLINER_OT_duplicate)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_hierarchy)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_instance)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_instance_hierarchy)
    bpy.utils.unregister_class(OUTLINER_OT_select_hierarchy)
    bpy.types.OUTLINER_MT_object.remove(duplicate_menu_outliner)
    
    bpy.utils.unregister_class(OUTLINER_OT_select_collection_objects)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_collections)
    bpy.utils.unregister_class(OUTLINER_OT_duplicate_collections_linked)
    bpy.types.OUTLINER_MT_collection.remove(select_colection_obects_menu_outliner)
    
if __name__ == "__main__":
    register()
7 Likes

Have anyone real examples where no need sync selected (highlighted) objects in Outliner?

I’m confused because if I have multiple Outliners and select something in 3D View it automatically highlights in multiple Outliners identically,
but if I select objects in one Outliner area, then in other, it leaves highlighted but my current selection is chenged in last selected Outliner.
Example:


It also affect selecting in Outliner in different workspaces.

It looks like you are confusing the selection of items displayed in outliner with selection of objects.

Synchronization with 3Dviewport is a simplification thought for a default workspace that only contains one outliner.
But you don’t have interest in keeping it for all outliners if you have several of them.

In theory, if you create 3 different outliners in your workspace : it is not to display the same things.
If they are not displaying the same things, there is no reason to expect them to have same selection.
What would be the interest of a synchronization between outliners ?

Imagine that using filters, you have one outliner showing only cameras, another one showing only meshes and another one showing only lights.
What would be the benefit to loose last selection of cameras and lights in 2 outliners, just because you made a selection of meshes in the other outliner ?
In this case, it is easy to understand that selections in outliners are not referring to same thing, although we are still talking about same datablock type.

I accept to have multiple outliners with filters: one with only cameras, second with only lights, third with all other, but
how highlighted cameras or lights in other outliners will help if properties displaying only for active object?
And all your highlights will loose after you click or select something in 3dView (I’m talking about default enabled “Sync Selection”).

So maybe I dont understand all benefits with highlighted selection in other outliners, that’s why I’m asking for real examples.

Example: multiple outliners, select something in each of them, then start to edit mesh, select other mesh - loose all highlights…


or you will always select mesh in outliner and then edit it? …will only work like this.

And I am saying that you should not keep it enabled if you create multiple outliners to deal with different objects types.

Yes. Current behavior with multiple outliners does not make sense with Synchronization because synchronization was not made for this case.

But the fact to change it to synchronize all outliners will not improve anything.
It is just complicating behavior by adding a synchronization between outliners that doesn’t exist for no gain, except a cosmetic coherence.

If selection are different in different outliners, you can apply to them all operations relative to Outliner’s right click menu and Outliner’s shorctuts. That has nothing to do with Properties Editor.
You can use one Outliner to only display Collections and enable/disable them with E, Alt E shortcuts. And another outliner to toggle states of modifiers.

So, except being confused by something that has no importance. Does it really worth it to ask a developer to work on that to the detriment of more useful stuff ?
Why do you create multiple outliners ? In what way that would help you to have same selection in all outliners ?

Example that confusing me every time:
I modify the model in edit mode, then switch to UV Editing workspace, edit selected mesh uv, then select other meshes (they highlighted), edit they uv, then switch back to my workspace and expect to see highlighted last selected meshes, but it highlights only my first mesh before switching to UV Edit workspace.

Video

You are right. That is confusing.
That was a bad idea to make those highlighted lines more obvious than selected objects.
Now, because of synchronization, everybody is expecting them to always indicate selected objects.

For me, that is still just selection done in the editor. So, I continue not to care about it and just focus on what has a yellow/orange name.

There is no refreshment of outliner’s view, simply because there is no event to trigger it.
If you look at synchronization between Outliner and Properties Editor : that is not a reciprocal relation.
You select a data in Outliner. Active Properties Tab is updated.
You select a Tab in Properties Editor. Data is not highlighted in Outliner.

Here, there is a synchronization to make selection of items in Outliner triggering a selection of objects.
But selection of objects do not always trigger a selection of items in Outliner.

Selection in Viewport is doing that. But that is the only editor who does that.
If you select an object in Animation Editors, that does not change highlighting in Outliner, either.
There is simply no recursive update. Selection of objects is just updating names colors in Outliner.

I suppose that kind of recursive updates would have a cost in terms of responsiveness.
I don’t know what would be the cost of an update during workspace switches.

But basically, problem is there because it was neglected or thought as negligible. Not because there would be an intention to keep that for a specific usecase.

Yes. Synchronization is partial.
If you are searching selected objects by searching blue lines, you will be annoyed.
But if you only trust yellow/orange names, you are not annoyed.

1 Like

Are there any enhancement plans for the Outliner for 2.93? Considering bcon1 ends next week I imagine we can’t expect any new feature that was discussed or requested, but is it feasible to have e.g. some polishing of the context menu?

Just noticed that I’m browsing the hierarchy and renaming objects with arrow keys and F2, without touching the mouse or looking away from the monitor. There’s always something to add, but it’s a bit of good UI right here. Efficient and not draining attention capacity.

1 Like

@natecraddock Sorry to bother you. I suddenly remembered about manual sorta being something that was not able to make it in in previous releases. I was curious, is this still a target for the future?

Is the Outliner GSoC abandoned?

I don’t think we have an outliner GSOC this year.

Last years one was pretty successful, but things like manual sorting or child highlighting in collapsed hierarchies are still on a lot of peoples wish list I guess.

Maybe next year.

1 Like

I could be mistaken, but I think a participant can only do the same topic twice. There might also be a rule that a single participant can only be on GSoC twice. I don’t remember which it is, or if it is both.

Either way Nathan has done 2 GSoC events so he would not be able to do another Outliner project. If there would be another Outliner GSoC it would have to be from someone else. Or so I believe.

That’s interesting. Makes sense I guess.

It’d be cool to see a community powered alternative to GSoC. Not necessarily this limited and open to everyone.

Hey all, I love seeing enthusiasm for this project still!

It’s true, that after doing two GSoC projects I am no longer eligible for participation, and there isn’t an Outliner project for this summer. So currently there isn’t a large focus on fixing the outliner that I’m aware of. Though some new operators are being added with the asset browser which is nice.

Regarding the unfinished features from last summer: I absolutely would love to keep working on the outliner! The way things worked out this summer though, I took a job during the break from school that is taking the majority of my time, along with a bunch of other projects. There’s only so much I can do as a student and volunteer contributor, but I enjoy when I do get to help. It’s been hard for me to be away from contributing to Blender this summer, but I do want to come back soon and keep making the outliner better. I can’t make any promises, but maybe within the next month I’ll have freed up some time to contribute again.

Also, I think any future work would be better discussed elsewhere, so I’ll close this.

11 Likes