Select to view content in your preferred language

Any way to toggle layer or model visibility through Python?

5541
18
10-07-2015 06:31 AM
LR
by
Frequent Contributor

I can fetch a layer list, and I can fetch the objects from a certain layer. However, I can't find anything that changes their visibility. Is that not implemented?

If it is, is it possible to run a script that I could use for toggling visibility based on a variable within the script? AFAIK the whole script is always run, so if I define switch variables and change them, they're reset every time I run the script again.

Tags (2)
18 Replies
BenNadler
Esri Contributor

Visibility is managed in three places:

     Layer Visibility (turn all features on / off)

     Definition Query (exclude features)

     Renderer (change symbol to none)

Sounds like you are trying to filter out features based on a selection, you could add the list of selected objectIDS to the layer query definition thus "hiding" them. "...AND NOT OBJECTID IN (#,#,#,#)"

See arcpy layer definition query here...use python to create definition query

0 Kudos
LR
by
Frequent Contributor

The example works for the layers, thanks. However I'm trying to use the visibility in conjunction with an occlusion query. Turning layer visibility off or deleting the model doesn't keep the touches() function from triggering. Only when I don't generate a model through its rule (like case visible == true: extrude(5), else: NIL) does the query work as expected. Should I just give all the objects from the layer a visibility attribute (ce.setAttribute) and change that to false with python before generating or is there a proper way?

diff.png

0 Kudos
DavidWasserman
Frequent Contributor

Hey ZR,

Ohhh, so you want to toggle visibility on the procedural model level.
I think you have few options here.

For one, NIL will not generate geometry so the occlusion functions may not work (not entirely sure on that). What might be as helpful is the parameter approach you suggested toggling the material.opacity attribute.  By toggling this attribute between 1-0 and setting the attribute in python, you should be able to cloak your models while still setting off occlusion functions. Thus this way you get the same effect, without turning off the entire layer. Also you can change the shape attributes opacity ANYWHERE in the shape hierarchy allowing more nuanced control.
Hope this helps,
David

David Wasserman, AICP
0 Kudos
LR
by
Frequent Contributor

Ah no, I want to prevent touches() from triggering when the object is invisible. I'm almost there (?) however I have a little problem. How do I select the appropriate objects? The current code sets the visibility properly but also every object's "generate" to true. I figured there'd be a "select objects by layer name" but I can't find anything. Also how do I set a boolean value as attribute? My true/false isn't a "real" true/false, so I'm not getting a on/off slider.

Here's a screenshot for clarification. Larger cubes: contents of Layer01-04. The cubes have a case for extrusion or NIL, depending on their "generate" attribute. The purple cubes use a rule with the touches() query. The idea is that invisible cubes don't trigger the purple ones.

touch.png

script:

from scripting import *


# List of visible layers
LayerName = ["Layer01", "Layer02", "Touches"]


ce = CE()


@noUIupdate
def main():
    layers = ce.getObjectsFrom(ce.scene, ce.isLayer)
    shapes = ce.getObjectsFrom(ce.scene, ce.isShape)
    for layer in layers:
        if str(layer) in LayerName:
            layer.setVisible(True)
            for shape in shapes:
                ce.setAttribute(shape, 'generate', 'true')
                #ce.setAttribute(shape, 'generate', 'false')


        else:
            for shape in shapes:
                ce.setAttribute(shape, 'generate', 'false')
            layer.setVisible(False) 
    #ce.generateModels(shapes)
    #ce.waitForUIIdle()
            
if __name__ == '__main__':
    main()
0 Kudos
DavidWasserman
Frequent Contributor

Oh got it. Sorry I misunderstood. The opacity method works assuming you still want occlusions to trigger. Now that I see your python script I understand. I think part of the problem is you are iterating through shapes without using a "getObjectsFrom" function. It might help with accessing the objects in question (shapes).

    for layer in layers: #for loop iterates through all objects in scene
        print("Checking objects in layer ", layer)
        objectList=ce.getObjectsFrom(layer)
        for object in objectList:

Another part is I am not sure you want to set the attribute equal to the string "true" or "false", does the rule attribute in your rule take a boolean on or a string for triggering? Because now you are passing it is string it seems. Your rule might have to recognize it as a string.

# So if it is boolean:
case generate:Rule
else: NIL
# If it is a string:
case generate=="true":Rule
else:NIL


Hope this helps with your python code. I see the issue has to do with the layers interaction. I think one question I have is have you just tried deleting and then reinserting the layer again if you need it?

David Wasserman, AICP
0 Kudos
by Anonymous User
Not applicable

I've not read every post in this thread, but if it helps:

Layer visibility will not affect occlusion functions. So if you have models generated on a layer that is not visible, those models will still be included in occlusion tests. I'm 98.6 percent sure of this, unless it was changed in 2015.2, and I see nothing in the change log about it.

Chris

DavidWasserman
Frequent Contributor

I was not sure on this point Chris, thanks (my uncertainty is why I suggested deleting the layer entirely). From Z R's post it seems that different layers can set off occlusion functions, so I guess my question is would deleting the layer entirely work?

David Wasserman, AICP
0 Kudos
LR
by
Frequent Contributor

Thanks. I think I got it now. I built two layer lists and used them to set the right attributes for the objects in the layer groups.

The rule accepts true/false as text because I changed it to do so since I couldn't figure out how to set a boolean true/false . However it still would be nice to set the right attribute type. Deleting the layers isn't feasible, they have to stick around for quick comparisons.

Here's the code, maybe someone else will find it useful:

from scripting import *


# List of visible Layers
VisibleLayersNames = ["Scene Light", "Panorama", "Layer01", "Layer03", "Touches"]


VisibleLayers = []
InvisibleLayers = []
AllLayerNames = []


ce = CE()


@noUIupdate
def main():


    # get visible layers
    for i in range(len(VisibleLayersNames)):
        VisibleLayer = ce.getObjectsFrom(ce.scene, ce.isLayer, ce.withName(VisibleLayersNames))
        VisibleLayers.extend(VisibleLayer)
    
    # get all layers
    AllLayers = ce.getObjectsFrom(ce.scene, ce.isLayer)
        
    #get all layer names
    for j in range(len(AllLayers)):
        LayerName =  ce.getName(AllLayers)
        AllLayerNames.append(LayerName)
    
    # set layer visibility
    for Layer in AllLayers:  
        if str(Layer) in VisibleLayersNames:  
            Layer.setVisible(True)  
        else:  
            Layer.setVisible(False)   
    
    # get invisible layer names
    InvisibleLayersNames = list(set(AllLayerNames).difference(set(VisibleLayersNames)))
    
    # get invisible layers
    for k in range(len(InvisibleLayersNames)):
        InvisibleLayer = ce.getObjectsFrom(ce.scene, ce.isLayer, ce.withName(InvisibleLayersNames))
        InvisibleLayers.extend(InvisibleLayer)
    
    # set generate attribute for visible layers
    for VisibleLayer in VisibleLayers:
        VisibleObjects=ce.getObjectsFrom(VisibleLayer)
        for VisibleObject in VisibleObjects: 
            ce.setAttribute(VisibleObject, 'generate', 'true')
    
    # set generate attribute for invisible layers        
    for InvisibleLayer in InvisibleLayers:
        InvisibleObjects=ce.getObjectsFrom(InvisibleLayer)
        for InvisibleObject in InvisibleObjects: 
            ce.setAttribute(InvisibleObject, 'generate', 'false')
    
if __name__ == '__main__':
    main()
DavidWasserman
Frequent Contributor

Hey ZR,

Glad you got it to pass correctly. The interaction between different data types between languages, GIS, and CGA/CityEngine can be tricky.

David

David Wasserman, AICP
0 Kudos