Jump to content
Developer Wiki and Function Reference Links Read more... ×
GioPet

Layer function creates new layer instead of activating the existing one

Recommended Posts

Hi all, 

 

this is a 'back to basics' question.

 

Here is the problem I'm coming across: if the layer name ends with a number, the VS function  Layer creates a new layer by incrementing the last digit.

 

A simple example: 

layerName: 'Design Layer-1';
Layer(layerName);

this will generate a layer named 'Design Layer-2' instead of activating the existing layer.

 

I am using ForEachObject to parse a set of objects through particular function.

Through the function, I need to activate the layer of the referenced object so that I can edit some of its parameters - but this won't work where layers end with a digit.

 

Can anyone suggest a workaround to Activate the layer of an object in this situation??

is there any function to activate the layer through its handle or through a referenced object?

 

thank you!

Share this post


Link to post

I don’t think I’ve ever encountered this, but if Layer() is behaving as you describe, I think it’s a bug. 

 

Are you modifying objects via the script, or selecting them for user interaction?  If the former, just make sure you’re using handle-based functions, and the layer doesn’t need to be active. 

 

And the snarky answer would be, this is another good reason to always rename layers to something specific 😏

Share this post


Link to post

Hi Joshua, 

 

thank you for your reply.

I am modifying the objects through the script - amongst other things I need to Change the class of certain objects and realised that SetClass cannot retain the class of the objects within Groups!!


SetClassN would do the job but it's available only since VW2018.. 

 

To move a Group from a class to another (achieving the equivalent of SetClassN), my workaround is:

  1. Activate the Group's layer
  2. Activate the desired class
  3. Ungroup and Group

But the Ungroup function won't work when I parse the handle of Groups which are not on the active layer - so I tried to use the Layer function to switch to each layer and change the class, but I've hit the problem..

Can you think of any other approach to this??

 

I agree with your answer and I would always ensure layers are properly named, but I need to get this script to work in almost any scenario - including drawings by others..

 

 

Share this post


Link to post

GioPet,
I am not sure how you got that code snippet to run, but the first line is not syntactically correct.

 

layerName: 'Design  Layer-1';
should read:
layerName := 'Design Layer-1';

 

When your variable "layerName" has the value 'Design Layer-1', it will not create 'Design Layer-2' and it will either make 'Design Layer-1' active, or leave it active. BUT, if "layerName" has the empty string value '', it will create 'Design Layer-2'.

 

My guess is that your variable "layerName" is uninitialized when you pass it to the LAYER() procedure.

 

HTH,
Raymond

 

PS - be careful copying code from Safari (my browser), and potentially other browsers, so that you don't introduce INVISIBLE characters to your code. When I copied your two line example, and then copied pieces of it to embellish my answeer, I introduced about a dozen copies of unicode character 0xFEFF. This character is invisible, unless you know how to look for it. Always filter your code through a word processor that can show INVISIBLE characters.

Edited by MullinRJ

Share this post


Link to post

Hi Raymond, 

 

thank you for your response.

I am actually writing this in Python - but it's all using VS functions.

 

Here is a snippet of my code:

for item in ExtendedGroups:
	layHand = vs.GetLayer(item)
	layName = vs.GetLName(layHand)
	vs.Layer(layName)

   	vs.SetSelect(item)
	
   	vs.Ungroup()
	vs.NameClass(TargetClass)
	vs.Group()
	vs.DSelectAll()

This is a for loop that goes through the items (which are handles to Groups) in a list - equivalent to a DYNARRAY in VS.

 

So I actually never spell out the name of the layer in the code - but if I run this on a file with a layer named 'Design Layer-1', the vs.Layer() function does generate a new layer called 'Design Layer-2'.

 

The result of the script should be that it changes the Class of Groups in the file - instead it generates as many new 'Design Layer-x'  as the number of items found in ExtendedGroups.


I can't think of ways around this problem.. but I may be completely unaware of something I should know about this??

 

PS - thanks for the heads up with invisible characters! I use BBedit and turned on my Invisibles.

Share this post


Link to post

GioPet,

   The only way I know of that Layer(), or vs.Layer(), will create another numbered layer is if the name passed to the Layer() procedure is an empty string ( i.e., vs.Layer("") ) and that would occur if the handle variable 'item' is NIL. You should put a trap in your routine to test for 'item' being NIL. Here's one way, albeit an overly simple one.

for item in ExtendedGroups:
	if (item != vs.Handle()):
		layHand = vs.GetLayer(item)
		layName = vs.GetLName(layHand)
		vs.Layer(layName)

		vs.SetSelect(item)
	
		vs.Ungroup()
		vs.NameClass(TargetClass)
		vs.Group()
		vs.DSelectAll()
	else:
		vs.AlrtDialog("item == NIL")

Raymond

Share this post


Link to post

You should also check that ExtendedGroups is indeed a list of handles. You originally mentioned using ForEachObject, which doesn’t implicitly generate a list, so there is a chance for error there. 

 

Another way to skin the cat without changing the active layer:

 

create a new group

set the group to the layer of the existing group with SetParent()

set the class of the new group

set the parent of the old group to the new group

ungroup the old group with HUngroup()

 

Share this post


Link to post

Hi and thank you both!

I found the solution in both the problems you've pointed out.

 

for future reference, here are the steps i've gone through.

The following is to work around the lack of the function SetClassN in VW versions prior 2018:

  1. Create List (or Dynarray if in VS) of Handles to Groups in the file
  2. change the Class of each Group through a for loop that takes each item in the list
  3. If the Layer of the Group is different from the Active Layer, the target layer is activated
  4. The Group is Selected, Ungrouped, Re-Grouped in the TargetClass

 

The following code creates unwanted layers as well as unexpected Groups with different results.

The issue is caused by a NIL handle creeping into the ExtendedGroup list - although I haven't figured out why this happens and its behaviour generates always different results...

ExtendedGroups = []

def BuidlList(h):
	global ExtendedGroups
	GroupLayer = vs.GetLayer(h)
	if GroupLayer != vs.ActLayer():
		ExtendedGroups.append(h)


def Script():
  
  criteria = '(T=GROUP)'
  vs.ForEachObject(BuidlList, criteria)
  
  vs.NameClass('None') #the TargetClass is be specified as intended
  
  for item in ExtendedGroups:
    layName = vs.GetLName(item)
    if layName != vs.GetLName(vs.ActLayer()):
      vs.Layer(layName)
    
    # here follows is a set of function, e.g.
    vs.SetSelect(item)
    vs.Ungroup()
    vs.Group()
    vs.DSelectAll()

Script()

 

As per Raymond indication - my logic would follow to exclude NIL items from being passed on to the for loop, thus adding these:

for item in ExtendedGroups:
  if item != None:
    # all the functions to be performed - as above
  else:
    vs.AlrtDialog('Handle to Group is NIL!')

I may be wrong, but I can't figure out why this logic fails - yet this still leads to unwanted results and additional layers...

 

The only way I got it to work is to wrap the for loop in another loop that runs for each layer in the document:

#after collecting all layer names in the document in a list named 'LayerList'

for Lnames in LayerList:
	for item in ExtendedGroups:
      if item != None:
    	# all the functions to be performed - as above
        else:
          vs.AlrtDialog('Handle to Group is NIL!')

 

Thank you again for the suggestions!!

It had become a headache..

 

 

Share this post


Link to post

Not sure if I'm following these correctly, but are you trying to create a function that sets the class of a group together with its contents?

 

here's a custom function I created prior to 2018,

def SetClassGroup_Contents(handle_to_group, className:str, descend=False):
    if vs.GetTypeN(handle_to_group) == 11: # Only work on groups TypeGroup == 11

        if descend:  # if descend parameter set then cycle through each item in group, default is set to false
            def set_class_objs_in_group(h):
                vs.SetClass(h,className)

            vs.ForEachObjectInList(set_class_objs_in_group,0,1,vs.FInGroup(handle_to_group)) # See Documentation for parameter descriptions

        vs.SetClass(handle_to_group,className) # Set Class of overall group
        vs.ReDrawAll()  # Sometimes my machine needs this to show updated attributes
        vs.ResetObject(handle_to_group) # Updates Object Info Pallette

h = vs.FSActLayer()
SetClassGroup_Contents(h,'NEW CLASS') # Use for apply to group only
SetClassGroup_Contents(h,'NEW CLASS', True) # Use for apply to group and contents

 

I guess you could then use this on ForEachObject call for all the groups you've found.

 

criteria = "(T=GROUP)"

def apply_class(h):
    classNew = "NEW CLASS"
    SetClassGroup_Contents(h,classNew,True)

vs.ForEachObject(apply_class,criteria)

 

Also, I've never come across call vs.Layer('Design Layer-1') creating another 'Design Layer-2' layer. This has never happened to me. I use the vs.Layer() call all the time, especially in Project Sharing files

 

HTH,

Tui

 

Edited by twk

Share this post


Link to post

Tui,

   I just tried your function in VW 2017 and both versions (with descend=TRUE and descend=FALSE) convert the contents of the group to the new class. 

 

vs.SetClass(), when used with a GROUP handle always changes the container and all its contents to the new class name. Have you seen it do differently, and if so, where?

 

Thanks,

Raymond

Share this post


Link to post

Raymond you are correct. I have just tried in 2017.

I will check another machine that has 2014/2015/2016 installed. I've used it before in plugin objects. I wonder what changed.

 

BTW this works in 2018.

 

Shall report back with findings..

Share this post


Link to post

Ok, reporting back,

I dont have access to 2014/2015 anymore, but I have just tried in 2016 and it doesnt do what i thought it did.

 

The class function gets set to both the group and the objects in it. Im stumped.

 

 

Share this post


Link to post

Here I created a custom object class:

(tested and worked in VW2016)

# save work before using

def SetClassGroup_Contents(handle_to_group, className:str, descend=False):

    class CustomObject():
        def __init__(self, handle):
            self.handle = handle

            self.__old_class = vs.GetClass(self.handle)

        @property
        def old_class(self):
            return self.__old_class

        @old_class.setter
        def old_class(self, value):
            self.__old_class = value

        @property
        def obj_class(self):
            return vs.GetClass(self.handle)

        @obj_class.setter
        def obj_class(self, value):
            vs.SetClass(self.handle, value)

        def restore_old_class(self):
            self.obj_class = self.old_class

    if vs.GetTypeN(handle_to_group) == 11: # Only work on groups TypeGroup == 11

        if not descend:  # if descend parameter set then cycle through each item in group, default is set to false
            stored_objects = [] #type: list[CustomObject]

            def set_class_objs_in_group(h):
                stored_objects.append(CustomObject(h))

            vs.ForEachObjectInList(set_class_objs_in_group,0,1,vs.FInGroup(handle_to_group)) # See Documentation for parameter descriptions

            vs.SetClass(handle_to_group,className) # Set Class of overall group

            for object in stored_objects:
                object.restore_old_class()

        else:
            vs.SetClass(handle_to_group, className)  # Set Class of overall group


        vs.ReDrawAll()  # Sometimes my machine needs this to show updated attributes
        vs.ResetObject(handle_to_group) # Updates Object Info Pallette
h = vs.FSActLayer()
SetClassGroup_Contents(h,'NewClass1') # Use for apply to group only
# SetClassGroup_Contents(h,'NEW CLASS', True) # Use for apply to group and contents

 

Share this post


Link to post

Hi twk!

 

thank you for sharing this!

I am a bit new to Python classes so this is a great stab at it - I love how dynamic it is!

 

best

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

 

7150 Riverwood Drive, Columbia, Maryland 21046, USA   |   Contact Us:   410-290-5114

 

© 2018 Vectorworks, Inc. All Rights Reserved. Vectorworks, Inc. is part of the Nemetschek Group.

×