GioPet Posted December 1, 2018 Share Posted December 1, 2018 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! Quote Link to comment
JBenghiat Posted December 1, 2018 Share Posted December 1, 2018 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 😏 Quote Link to comment
GioPet Posted December 1, 2018 Author Share Posted December 1, 2018 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: Activate the Group's layer Activate the desired class 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.. Quote Link to comment
MullinRJ Posted December 2, 2018 Share Posted December 2, 2018 (edited) 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 December 2, 2018 by MullinRJ Quote Link to comment
GioPet Posted December 2, 2018 Author Share Posted December 2, 2018 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. Quote Link to comment
MullinRJ Posted December 3, 2018 Share Posted December 3, 2018 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 Quote Link to comment
JBenghiat Posted December 3, 2018 Share Posted December 3, 2018 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() Quote Link to comment
GioPet Posted December 3, 2018 Author Share Posted December 3, 2018 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: Create List (or Dynarray if in VS) of Handles to Groups in the file change the Class of each Group through a for loop that takes each item in the list If the Layer of the Group is different from the Active Layer, the target layer is activated 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.. Quote Link to comment
twk Posted December 4, 2018 Share Posted December 4, 2018 (edited) 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 December 4, 2018 by twk Quote Link to comment
MullinRJ Posted December 4, 2018 Share Posted December 4, 2018 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 Quote Link to comment
twk Posted December 4, 2018 Share Posted December 4, 2018 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.. Quote Link to comment
twk Posted December 11, 2018 Share Posted December 11, 2018 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. Quote Link to comment
twk Posted December 11, 2018 Share Posted December 11, 2018 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 Quote Link to comment
GioPet Posted December 18, 2018 Author Share Posted December 18, 2018 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 Quote Link to comment
The Hamma Posted April 30, 2020 Share Posted April 30, 2020 Using part of twk's script I have tried to create a script that will sort sheet layers and their objects classes. The goal is to have all objects in the sheet layer in the following classes Borders, Veiwports, Groups and other objects on sheet layer = 'SheetClass' Viewport Annotations including components of groups with in the annotations = 'Annotations' Drawing Label = 'Drawing Labels' Everything works accept the components of groups within the annotations of a Viewport. Any suggestions? Python Script #collect handles of selected VPs on active layer def GetHandle(h): vs.SetClass(h,'SheetClass') if vs.GetTypeN(h) == 122: if vs.GetParent(h) == vs.ActLayer(): hVP.append(h) hVP = [] vs.ForEachObjectInLayer( GetHandle,0,2,0 ) #Traverse into the annotations group of selected VPs set classes for VP in hVP: anno = vs.GetVPGroup(VP, 2) h = vs.FInGroup(anno) objs = [h] h = vs.NextObj(h) while h != vs.Handle(0): objs.append(h) h = vs.NextObj(h) for obj in objs: if vs.GetObjectVariableString(obj,1166) == 'Drawing Label': vs.SetClass(obj,'Drawing Labels') else: 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(obj)) # See Documentation for parameter descriptions vs.SetClass(handle_to_group,'annotation') # Set Class of overall group for object in stored_objects: object.restore_old_class() else: vs.SetClass(obj,'annotation') Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.