GVM: Script for hide or show group of objects

Hi there, since I absolutely need a script for hiding/showing groups (a features outliner strangely don’t have) in blender and it seems not to exist, I’m tryng to write it from scratch… It can activate/deactivate the visualizing in the 3d view of all objects in the groups existing, switching it from scene to scene (thanks to an idea of FourMadMen, originally it was layer to layer, a slow solution via python). Please help me improve this script, all suggestion are welcome!
The current status is like alpha… Stay tuned for the next releases for a production level stability.

The next features to be implemented are:

  • More speed up!!!
  • Others idea welcome

Screenshot of the script in action V. 0.4
http://i38.tinypic.com/2vi369c.png

Example blend file
http://rapidshare.com/files/143354970/gvm.blend.html

New!
Added new script: Camera Switcher, another little but useful features I needed, please test it in the example file
If I will achieve it, with some other little script I need I could make a sort of “productivity suite”. Maybe when this two become stable and usable. What do you think?

Code of Group Visibility Manager:

# Group Visibility Manager by gls

# Many thanks to FourMadMen for his very appreciated help

# V. 0.4



import Blender

from Blender import *

from Blender.BGL import *

from Blender.Draw import *



showScene = Scene.GetCurrent() # Get the current scene name

hideScene = Scene.Get("HideScene")

if not hideScene: hideScene = Scene.New("HideScene")



prefix = [0] # Set the default value for prefix

offset = [0] # Set the start value of offset to 0



hiddenObjects = [ob for ob in hideScene.objects] # List hidden objects when the script start 



grpsli = [] # Create a list with groups and some other data, see below



for i, grp in enumerate(reversed(Group.Get())): # Fill grpsli with: Group object, Toggle ID, Default status for toggle, Solo button ID

    grpsli.append([grp, i, 1, i+1000])



for i, grp in enumerate(grpsli): # exclude from grpsli the hidden groups

    if hiddenObjects.count(grp[0].objects[0]) == 1: grpsli[i][2] = 0 # check if the first object in all group is hidden or not



def event(evt,val):

    if evt == ESCKEY: Exit() # Quit with ESK key pressed

    if evt == WHEELUPMOUSE: offset[0] = offset[0]+10 ; Redraw(1) # Scroll the GUI up with mousewheel

    if evt == WHEELDOWNMOUSE: offset[0] = offset[0]-10 ; Redraw(1) # Scroll the GUI down with mousewheel



def draw(): # Draw the GUI

    glClear(GL_COLOR_BUFFER_BIT) # Clear the window



    glColor3f(0.000, 0.000, 0.000) # Set color of title text

    glRasterPos2i(10, 10+offset[0]) # Set position of title text

    Text('Group Visibility Manager V 0.4') # Title text



    Toggle('Prefix',10000,10,30+offset[0],45,20,prefix[0],'Show optional number prefix on/off - Name your groups like "01-groupname" if you want to use this') # Self-Explanotory

    Button('<_>',10001,57,30+offset[0],38,20,"Hide All") # Hide All Button

    Button('<o>',10002,97,30+offset[0],38,20,"Show All") # Show All Button

    Button('Exit',10003,137,30+offset[0],45,20,"Exit Camera Switcher") # Exit button



    h = 30 # Set the vertical position of the first toggle

    for grp in grpsli: # Create a toggle for every group

        h = h+22 # Value of the vertical position of the next toggle

        if prefix[0] == 0:

            Toggle(grp[0].name[3:],grp[1],10,h+offset[0],150,20,grp[2],"Hide") # Create the Hide/Show toggle, without displaiyng prefix

        else:

            Toggle(grp[0].name,grp[1],10,h+offset[0],150,20,grp[2],"Hide") # Create the Hide/Show toggle

        Button('S',grp[3],162,h+offset[0],20,20,"Solo") # Create the Solo button



    Button('^', 10004, 184, 52, 20, 20) # Scroll the GUI up

    Button('v', 10005, 184, 30, 20, 20) # Scroll the GUI down



def button(evt): # Define the behaviour of the buttons

    for i, grp in enumerate(grpsli): # Define the behaviour of every single toggle



        if evt == grp[1]: # Group Hide/Show Toggle 

            for ob in grp[0].objects:

                try: # If all is ok (object not already hidden)...

                    hideScene.objects.link(ob) ; showScene.objects.unlink(ob) # ...hide every object in group

                    grpsli[i][2] = 0 # Change status of toggle to pressed

                except: # If object already hidden...

                    showScene.objects.link(ob) ; hideScene.objects.unlink(ob) # ... show them

                    grpsli[i][2] = 1 # Change status of toggle to not pressed



        if evt == grp[3]: # Group Solo Button

            try:

                allObjects = [ob for ob in showScene.objects] # List all objects in current scene

                for ob in grp[0].objects: allObjects.remove(ob) # Remove objects of current group from allObjects

                for ob in allObjects: hideScene.objects.link(ob) ; showScene.objects.unlink(ob) # Hide every item of allObjects

                for grp in grpsli: grp[2]=0 # Change status for all groups toggle to not pressed

                grpsli[i][2]=1 # Set status for current group toggle to pressed

            except: pass



    if evt == 10000: # Prefix Hide/Show Button

        if prefix[0] == 0: prefix[0] = 1 # Play with prefix value...

        else: prefix[0] = 0



    if evt == 10001: # Hide All Button

        allObjects = [ob for ob in showScene.objects] # List all objects in current scene

        for ob in allObjects: hideScene.objects.link(ob) ; showScene.objects.unlink(ob) # Hide every item of allObjects

        for grp in grpsli: grp[2]=0 # Change status for all groups toggle to not pressed



    if evt == 10002: # Show All Button

        allObjects = [ob for ob in hideScene.objects] # List all objects in hideScene

        for ob in allObjects: showScene.objects.link(ob) ; hideScene.objects.unlink(ob) # Show every item of allObjects

        for grp in grpsli: grp[2]=1 # Change status for all groups toggle to pressed



    if evt == 10003: Exit() # Exit Button

	

    if evt == 10004: offset[0]=offset[0]+10

    if evt == 10005: offset[0]=offset[0]-10



    Blender.Window.Redraw() # Redraw the window



Register(draw,event,button)



# End of script

Code of Camera Switcher:

# Camera Switcher by gls

# V. 0.2



import Blender

from Blender import *

from Blender.BGL import *

from Blender.Draw import *



currentScene = Scene.GetCurrent() # Get the current scene name



camli = [] # Create a list with camera and some other data, see below



prefix = [0] # Set the default value for prefix

offset = [0] # Set the start value of offset to 0



for i, ob in enumerate(reversed(Blender.Scene.GetCurrent().getChildren())): # Fill camli with: Camera object, Button ID

    if ob.type == 'Camera':

        camli.append([ob, i])



def event(evt,val):

    if evt == ESCKEY: Exit() # Quit with ESK key pressed

    if evt== WHEELUPMOUSE: offset[0]=offset[0]+10 ; Redraw(1) # Scroll the GUI up with mousewheel

    if evt == WHEELDOWNMOUSE: offset[0] = offset[0]-10 ; Redraw(1) # Scroll the GUI down with mousewheel



def draw(): # Draw the GUI

    glClear(GL_COLOR_BUFFER_BIT) # Clear the window



    glColor3f(0.000, 0.000, 0.000) # Set color of title text

    glRasterPos2i(10, 10+offset[0]) # Set position of title text

    Text('Camera Switcher V 0.2') # Title text



    Toggle('Prefix',10000,10,30+offset[0],45,20,prefix[0],'Show optional number prefix on/off - Name your camera like "01-camera" if you want to use this') # Self-Explanotory

    Button('Exit',10001,137,30+offset[0],45,20,"Exit Camera Switcher") # Exit button



    h = 30 # Set the vertical position of the first button

    for grp in camli: # Create a button for every camera

        h = h+22 # Value of the vertical position of the next buttons

        if prefix[0] == 0:

            Button(grp[0].name[3:],grp[1],10,h+offset[0],172,20,"View "+grp[0].name[3:]) # Create the change camera button, without displaiyng prefix

        else:

            Button(grp[0].name,grp[1],10,h+offset[0],172,20,"View "+grp[0].name) # Create the change camera button



    Button('^', 10002, 184, 52, 20, 20) # Scroll the GUI up

    Button('v', 10003, 184, 30, 20, 20) # Scroll the GUI down



def button(evt): # Define the behaviour of the buttons

    for grp in camli: # Define the behaviour of every single button

        if evt == grp[1]: currentScene.setCurrentCamera(grp[0])



        if evt == 10000: # Prefix Hide/Show Button

            if prefix[0] == 0: prefix[0] = 1 # Play with prefix value...

            else: prefix[0] = 0



        if evt == 10001: Exit() # Exit Button



    if evt == 10002: offset[0]=offset[0]+10

    if evt == 10003: offset[0]=offset[0]-10



    Blender.Window.Redraw() # Redraw the window



Register(draw,event,button)



# End of script

Please help me!

edit by the author

Made some minor improvement. Updated the example file.
I feel lonely :frowning:

hey, nice idea.

not to throw a spanner in the works, but if you go to the outliner window you can set it to groups and hide and show each one, you can also set them to un-selectable but still visible, can be handy.

Hi marf thanks for your reply! Sadly, the outliner can not hide groups. Please test. For this reason I made this script. The problem is it slow…

Version 0.0.0.4 just published in the first post

I see a few things that I would change here:

First with the following loop:


for a in Group.Get():

        grp= Group.Get(a.name)

    #do something with "grp"

#end for

The for loop is already getting a Group object so you can change that to:


for grp in Group.Get():

    #do something with "grp"

#end for

Secondly, I personally don’t like “import *”. I’d import just the specific items you are using. But I don’t know enough of the internals to say if it would really save any cycles (and if it does you won’t notice it).

Thirdly, The button event function loops, again, through all the group objects. I would re-write it such that the index of the needed Group object is derived from the event number.

Having said all that you won’t speed the script to the point you’ll want it. While using “Move” from the Blender UI is near instantaneous, the same can not be said for setting object layers via a Python script. Perhaps it’s some inefficiency in the way this is handled internally. Don’t know.

What exactly are you trying to accomplish here? Besides the obvious task of moving objects to other layers. i.e. what is the goal from a workflow perspective?

Hi FourMadMen, thanks for your inputs.
First: You’re right, corrected in the 0.0.0.5
Second: You’re right, Im working on it
Third: I don’t understand very well, can you explain more your idea?

Python is a bottleneck… I know, but I can’t do more of a python script, C is for me like Blender four year ago :smiley: The ideal solution would be upgrading the outliner… Someone available for this task?

An example of workflow: Working on a architectural project, I have to visualize (and work on) in the 3d view in the first layer, say, part of the structure of the building. In the second layer another part (several separated objects), in the third ecc ecc. What to do if my work is so complex that a 20 level limit can allow? Scenes are not a solution because I have to activate (visualize) different parts togheter in different times in the 3d view… It would be very good simply activating/disactivating visibility of groups of objects.

Please excuse me for my poor english. I hope to be clear.

Well let’s table #3 as I think we have something more important to discuss.

Let’s try to examine what part of the process is slow. Essentially two things are happening: 1) Iterating through all objects in a group and 2) Setting “layers” on each object.

Since #1 is a great strength of python (syntactically as well as functionally) I doubt that is it. So some things to try or think about.

a) Instead of setting “layers” to a list of ints have you tried setting “Layers”? “Layers” is a single int where each bit represents a single layer. Perhaps the bottleneck is this conversion.

b) What is the performance of setting other object attributes on large numbers of objects? For example setting the selection state to True or False? If these are slow too then no solution will work without changing Blender. If any of these are fast then perhaps there is hope. If only to show where there perhaps is an opportunity to optimize the Python to Blender “pipeline”.

c) I tinkered with your script and in doing so I’ve already found the answers to (a) and (b). I also have a solution that works very fast (at least with the object numbers presented in your blend file). The price for the solution is for you to spend the next little bit working through the answers to (a) and (b) yourself. Additional practice/thinking never hurt anyone. After that I’ll share my © if you have not found it yourself. It was your idea after all – well, sort of anyway.

Now if you will excuse me I have two hurricanes to prepare my property for. Check back in tomorrow.

a) Instead of setting “layers” to a list of ints have you tried setting “Layers”? “Layers” is a single int where each bit represents a single layer. Perhaps the bottleneck is this conversion.

After some digging in the API I’ve been able to understand and made this change in the script, I think its better now.

b) What is the performance of setting other object attributes on large numbers of objects? For example setting the selection state to True or False? If these are slow too then no solution will work without changing Blender. If any of these are fast then perhaps there is hope. If only to show where there perhaps is an opportunity to optimize the Python to Blender “pipeline”.

I don’t find the correct instructions in the API, I’m still searching… If only I can move an entire list of objects instead of every single objects…

c) I tinkered with your script and in doing so I’ve already found the answers to (a) and (b). I also have a solution that works very fast (at least with the object numbers presented in your blend file). The price for the solution is for you to spend the next little bit working through the answers to (a) and (b) yourself. Additional practice/thinking never hurt anyone. After that I’ll share my © if you have not found it yourself. It was your idea after all – well, sort of anyway.

You are a genius… or maybe I have to learn the API :smiley: I hope to find the solution to your enigma. But now I’m quite frustrated :smiley:

Now if you will excuse me I have two hurricanes to prepare my property for. Check back in tomorrow.

:eek:

Oh no, I’m a genius.

We’ll worry about the button event function later…


# Group Visibility Manager by gls
# V. 0.0.0.4 Code name: "A little less awful"

import Blender
from Blender import *
from Blender.BGL import *
from Blender.Draw import *

showScene = Scene.Get("Scene")
hideScene = Scene.Get("HideScene")

def hi(ob):
    #Thing 1
    #ob.layers=[20]
    #Thing 2
    #ob.Layers = 16 # layer 5
    # Thing 3
    #ob.select(True)
    # Thing 4
    hideScene.link(ob)
    showScene.unlink(ob)
    
def sh(ob):
    #Thing 1
    #ob.layers=[1]
    #Thing 2
    #ob.Layers = 1 # layer 1
    #Thing 3
    #ob.select(False)
    #Thing 4
    showScene.link(ob)
    hideScene.unlink(ob)

def event(evt,val):
    if evt == ESCKEY:
        Exit()
        return

def draw():
    glClear(GL_COLOR_BUFFER_BIT)
    glColor3f(0.000, 0.000, 0.000)
    glRasterPos2i(20, 10)
    Text('Group Visibility Manager V 0.0.0.4')
    glColor3f(0.300, 0.300, 0.300)
    glRasterPos2i(105, 30)
    h = 20
    i = 1
    for a in Group.Get():
        grp = Group.Get(a.name)
        h = h+22
        i = i+1
        glColor3f(1.000, 1.000, 1.000)
        glRasterPos2i(20, h+5)
        Text(a.name[3:])
        Button("Hide",i,100,h,40,20,"Hide")
        Button("Show",i+7500,142,h,40,20,"Show")

def button(evt):
    i = 1
    for a in Group.Get():
        grp= Group.Get(a.name)
        i = i+1
        if evt == i:
            filter(hi, grp.objects)
        elif evt == i+7500:
            filter(sh, grp.objects)
    Blender.Window.Redraw()

Blender.Draw.Register(draw,event,button)

# End of script
# To-Do: better comments

You’ll have to manually create two scenes: One called “Scene” (objects by default should be in this one and are moved here when you click “Show”) and one called “HideScene” (moved here when you click “Hide”). Verification and/or creation of these could be scripted, perhaps something you’d like to tackle.

Oh no, I’m a genius.

Oh no, You are completely a genius. Please see the updated first post.

A shorter way to check if that scene name exists in the list of scenes is:


hideSceneExists = "HideScene" in [sc.name for sc in Scene.Get()]

However this should work too:


hideScene = Scene.Get("HideScene")
if not hideScene: hideScene = Scene.New("HideScene")

And as of Python 2.5 this can be condensed using a ternary expression (although it requires two calls to Scene.Get):


hideScene = Scene.Get("HideScene") if Scene.Get("HideScene") else Scene.New("HideScene")

The third solution seems to me the best, only one line of code! Thanks for the hint. This is the new progresses. Inserted working toggle and solo button, but their on off visual status need some working.

# Group Visibility Manager by gls

# Many thanks to FourMadMen for his very appreciated help

# V. 0.1



import Blender

from Blender import *

from Blender.BGL import *

from Blender.Draw import *



CuSc = Scene.GetCurrent() # Get the current scene name



showScene = Scene.Get(CuSc.name) 

hideScene = Scene.Get("HideScene") if Scene.Get("HideScene") else Scene.New("HideScene")



def hi(ob): # Function for hiding objects

    try:

        hideScene.objects.link(ob)

        showScene.objects.unlink(ob)

    except:

        showScene.objects.link(ob)

        hideScene.objects.unlink(ob)



def event(evt,val):

    if evt == ESCKEY: # Quit with ESK key pressed

        Exit()

        return



grpsli = []



for i, grp in enumerate(reversed(Group.Get())):

    grpsli.append([grp, i, 1, i+1000, 1])



def draw(): # Draw the GUI

    glClear(GL_COLOR_BUFFER_BIT)

    glColor3f(0.000, 0.000, 0.000)

    glRasterPos2i(20, 10)

    Text('Group Visibility Manager V 0.1')

    h = 20 # Set the vertical position of the first couple of button

    for grp in grpsli:

        h = h+22 # Value of the vertical position of the next buttons

        Toggle(grp[0].name[3:],grp[1],10,h,150,20,grp[2],"Hide") # Create the Hide/Show button

        Toggle('S',grp[3],162,h,20,20,grp[4],"Solo")



def button(evt): # Define the behaviour of the buttons

    i = 1 # Get the button ID of the first button

    for grp in grpsli: 

        if evt == grp[1]:

            filter(hi, grp[0].objects)

            if grp[2] == 1:

                grp[2] = 0

            else:

                grp[2]=1

        if evt == grp[3]:

            x = [ob for ob in CuSc.objects]

            for ob in grp[0].objects:

                x.remove(ob)

            filter(hi, x)

            if grp[4] == 1:

                grp[4] = 0

            else:

                grp[4]=1

    Blender.Window.Redraw()



Register(draw,event,button)



# End of script

I’d still go with the following:

hideScene = Scene.Get(“HideScene”)
if not hideScene: hideScene = Scene.New(“HideScene”)

Only one call to Get. More efficient.

I took a short look at your script, but why are you linking, unlinking and juggling with scenes instead of using object.restrictDisplay = True
Is it because of speed issues? (sorry, I don’t have time to compare them myself right now)

sorry, your correct about hiding groups…
it just seemed sensible to me that it would be a feature if you can view by groups.

Updated the first post, new version 0.2 and a new script.

@ FourMadMen:
You’re right as always, corrected. What do you think of Crouch’s solution?

@ Crouch:
Hi Crouch, thanks for your input I tried your solution, it seem as fast as the scene-shifting techniques, Im trying to find a system for measuring the execution time and compare the results.

@ Marf:
Dont worry Marf, I was surprised like you when I needed - and didn’t found - it :smiley:

@ All:
The code is not so fast and messed up now because I am a super noob and this is my very first serious attempt at python scripting. If anyone want to contribute any line of code, any system to reduce the amount of lines of code or to reduce the execution time, ideas or whatever, it will be very very welcome

0.3 out

quite usable now…