Hello,
I think I've found a limiting issue with ssget 's filter list argument.
I've created a filter list of some entity names to allow the user to select from them only, its form is like that
((-4 . "<OR") (-1 . <Entity name: 7ff4ca605d90>) (-1 . <Entity name: 7ff4ca605da0>) (-4 . "OR>"))
and it was stored in a symbol called lsp.
however,
(ssget lst)
returns nil immediately and doesn't work.
so I tried using handles instead, the filter list was like this
((-4 . "<OR") (5 . "1E9") (5 . "1EA") (-4 . "OR>"))
and was stored in lst,
Again, (ssget lst) returns nil the same as before.
so, I've made two last trials to validate this,
The first one have been with only one entity name association list (enclosed in a list of course), e.g
((-1 . <Entity name: 7ff4ca605d90>))
it failed,
The second and last trial have been with the handle's association list, e.g
((5 . "1E9"))
Also, it failed.
_______________________
Does that mean that we can't set certain "known before" objects "to-be-selected-from" interactively, regardless of their type, color, layer, and their properties in general?
Solved! Go to Solution.
Solved by _gile. Go to Solution.
Solved by Kent1Cooper. Go to Solution.
hi,
You cannot use an ename or a handle in a selection filter.
From the developer's documentation:
The ssget function recognizes all group codes except entity names (group code -1), handles (group code 5), and xdata (group codes greater than 1000)
On the other hand, you do not need to select entites you already know the ename or handle, just directly use the ename (possibly got from the handle with handent) or add it to an existing selection set with ssadd.
Thanks for pointing me to what the documentation says about that.
On the other hand, this means that the interactive feature can't be used in such case, as ssadd doesn't allow the user to select from them interactively the same way as ssget.
You can make a simple (while (ssget ":S") ...) loop where you (sssetfirst) or set 'highlight property to all entities that match your group.
OK, this post (with the "interactive feature" issue added to it by me, coming to it later) is the real background for the idea of "selection of what a program really knows before".
He wants to make some certain blocks with certain block attribute values selected on screen to be able to deal with them in the "properties palette", the somehow easy part is to transfer the elements within the ActiveX Selection Set to an AutoLISP one, then applying sssetfirst on it.
Quoting from him from the 2nd message:
((the problem is that object can be added to selection set for
different reasons, for my case, I was addeing block ref to my selection set
based on certain attribute values. I dont think it can be achieved using
available filters))
so, I though in a hurry that a general quick workaround to add them to the AutoLISP selection set would be by selecting them using their entity names within the filter of ssget, not knowing that ssget doesn't accept entity names and handles as valid filtering values.
However, knowing that ssget can be used to let the user select objects interactively and under the filtering criteria, I thought about how to not completely lose this feature.
I've even made a prototype for that with a function ssfiltered, whose only argument is a previously made AutoLISP selection set (the filter selection set), and the function's task is to allow the user to interactively select objects on screen with a crossing window, and it selects only those objects that are elements in the filter selection set and reside in the crossing window.
This crossing window can be created once (by 2 subsequent left clicks on the mouse), and the function will apply sssetfirst on the filter result.
Here's the code of it
;Author: Serag Hassouna ;|Purpose:a prototype to mimic the interactive feature of ssget function (with window crossing option) The user is allowed to inetactively select any listing of objects, and the command will filter them relative to a previous selection set and returns that filtered selection set. The feature of adding/removing objects muliple times is not added to this proptotype.
The crossing window here is just a yellow rectangle.|; (defun ssfiltered (ssallelems / i allelems bbelems minext maxext curgr playloop counter pt1 pt2 inbbelems ssret ssretlen) ;;inselbox-p : returns true if the object is in the selection box or intersects with it;; (defun inselbox-p (sbpt1 sbpt2 opt1 opt2 / selboxpts opts yup ydwn) (setq selboxpts (vl-sort (list sbpt1 sbpt2) (function (lambda (a b) (< (car a) (car b)))))) (setq sbpt1 (car selboxpts) sbpt2 (cadr selboxpts)) (if (> (cadr sbpt1) (cadr sbpt2)) (progn (setq yup (cadr sbpt1) ydwn (cadr sbpt2)) (setq sbpt1 (list (car sbpt1) ydwn (last sbpt1)) sbpt2 (list (car sbpt2) yup (last sbpt2))) ) );swap y-coordinates to get the upper rightmost and the lower leftmost points (mandatory) (setq opts (vl-sort (list opt1 opt2) (function (lambda (a b) (< (car a) (car b)))))) (setq opt1 (car opts) opt2 (cadr opts)) (cond ( (or (and (< (car opt1) (car sbpt1)) (< (car opt2) (car sbpt1))) (and (> (car opt1) (car sbpt2)) (> (car opt2) (car sbpt2))) (and (< (cadr opt1) (cadr sbpt1)) (< (cadr opt2) (cadr sbpt1))) (and (> (cadr opt1) (cadr sbpt2)) (> (cadr opt2) (cadr sbpt2))) ) nil ) (t t) ) ) ;;__________________________________________________________________________________;; ;;recvecs : draws a rectangle with vectors from its upper and lower opposite points;; (defun recvecs (pt1 pt2 color / pts) (setq pts (vl-sort (list pt1 pt2) (function (lambda (a b) (< (car a) (car b)))))) (setq pt1 (car pts) pt2 (cadr pts)) (grvecs (list color pt1 (list (car pt2) (cadr pt1) 0))) (grvecs (list color pt1 (list (car pt1) (cadr pt2) 0))) (grvecs (list color (list (car pt1) (cadr pt2) 0) pt2)) (grvecs (list color (list (car pt2) (cadr pt1) 0) pt2)) ) ;;__________________________________________________________________________________;; (setq playloop t) (setq counter 2 i 0) (repeat (sslength ssallelems) (setq allelems (cons (ssname ssallelems i) allelems)) (setq i (1+ i)) ) (setq bbelems (mapcar (function (lambda (x) (vla-getboundingbox (vlax-ename->vla-object x) 'minext 'maxext) (setq minext (vlax-safearray->list minext) maxext (vlax-safearray->list maxext)) (list x minext maxext) ) ) allelems ) ) (princ "\nSelect Objects: ") (while (progn (setq curgr (grread t (+ 1 2 4 8))) (cond ((and (= (car curgr) 3) (/= counter 0)) (progn (setq counter (1- counter)) (if (= counter 1) (setq pt1 (cadr curgr)) (setq pt2 (cadr curgr)) ) (if pt2 (progn (setq inbbelems (mapcar (function (lambda (x) (list (car x) (inselbox-p pt1 pt2 (cadr x) (last x))))) bbelems)) (mapcar (function (lambda (x) (redraw (car x) 4))) inbbelems);remove highlight (setq inbbelems (vl-remove-if (function (lambda (x) (= (cadr x) nil))) inbbelems)) ) ) ) );1st condition [left click on mouse] ((= counter 0) (setq playloop nil));2nd condition [the user clicked on 2 points] ((and (= (car curgr) 5) (/= counter 0)) (progn (if pt1 (progn (redraw) (recvecs pt1 (cadr curgr) 2) ; (setq inbbelems (mapcar (function (lambda (x) (list (car x) (inselbox-p pt1 (cadr curgr) (cadr x) (last x))))) bbelems)) (mapcar (function (lambda (x) (if (null (cadr x)) (redraw (car x) 4) (redraw (car x) 3) ) ) ) inbbelems ) ; ) ) );progn );the cursor moving (t nil);debug condition );cond playloop );progn );while (redraw) (if (not (null inbbelems)) (progn (setq ssret (ssadd)) (mapcar (function (lambda (x) (ssadd (car x) ssret))) inbbelems) (setq ssretlen (sslength ssret)) (princ (strcat (itoa ssretlen) " object" (if (= ssretlen 1) " is" "s are") " selected\n")) ) (princ "\nNo object is selected\n") ) (sssetfirst nil ssret) );end defun
and I'm really sorry for length of this reply.
You're right about highlighting, using the Highlight method is wider in its application range than redraw, as it can be applied with groups and ActiveX selection sets.
However, as for looping with ssget, the fundamental limitation issue with "the filter list" is not addressed.
Hence, the choice goes for ssadd to construct the so called "filter selection set", to be used later, i.e. to be selected from.
Then the dynamic effects can be implemented within a grread loop.
I might be missing something here, but what does all this code achieve over a simple (ssget) with a filter?
@serag.hassouna wrote:
.... a function ssfiltered, whose only argument is a previously made AutoLISP selection set (the filter selection set), and the function's task is to allow the user to interactively select objects on screen with a crossing window, and it selects only those objects that are elements in the filter selection set and reside in the crossing window.
.... the function will apply sssetfirst on the filter result.....
Will this do what you're after? It's not limited to using a single crossing window, but allows any kind of selection options, and when done, leaves you with only those selected objects selected/gripped/highlighted that were also in the 'ssbefore'-argument selection set.
(defun ssfiltered (ssbefore / ssnew n ent) (prompt "\nTo get only objects from the existing-selection-set argument within a wider selection,") (if (setq ssnew (ssget)) (progn ; then (repeat (setq n (sslength ssnew)) (setq ent (ssname ssnew (setq n (1- n)))) (if (not (ssmemb ent ssbefore)) (ssdel ent ssnew)) ); repeat (sssetfirst nil ssnew) ); progn ); if ); defun
ssget 's filter is limited, to overcome its limitation, the proposed procedure is to fill a "holistic" selection set with all the elements that match the custom filtering criteria, bearing in mind that this custom filtering criteria has no equivalent filter list to express it. I've provided a vivid example in my previous reply.
This holistic selection set is what I call "filter selection set" when it's supplied to ssfiltered (both, my prototype and @Kent1Cooper 's version ).
Then, you can refine and pick any listing of objects from this holistic one.
Yes, with a small difference, it doesn't highlight the valid objects - in real time - when they're in the selection region.
But all in all, it's more compact & simple.
Really shame on me , instead of constructing a new ssget from the ground and thinking a lot about all the internal low level logic, I didn't think about using another ssget.
The .NET API provides the Editor.SelectionAdded event which allows to remove entities from the selction set in real time.
static PromptSelectionResult GetFilteredSelection(Editor ed, HashSet<ObjectId> allowed) { SelectionAddedEventHandler filter = (_, e) => { var objs = e.AddedObjects; for (int i = 0; i < objs.Count; i++) { if (!allowed.Contains(objs[i])) e.Remove(i); } }; ed.SelectionAdded += filter; var selection = ed.GetSelection(); ed.SelectionAdded -= filter; return selection; }
You can download the attached ZIP file, unblock it, extract the FilteredSelectionLispFunction.dll and NETLOAD it from AutoCAD.
It defines a new ssfiltered LISP function which requires a list of enames as arguments.
Testing example:
(while (setq e (car (entsel))) (setq l (cons e l))) (setq s (ssfiltered l)) (sssetfirst nil s)
The whole C# code defining the LISP function if you prefer compile it by yourself.
using System.Collections.Generic; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; using AcAp = Autodesk.AutoCAD.ApplicationServices.Core.Application; namespace FilteredSelectionLispFunction { public class LispFunctions { static TypedValue nil = new TypedValue((int)LispDataType.Nil); [LispFunction("ssfiltered")] public static TypedValue GetFilteredSelection(ResultBuffer resbuf) { if (resbuf == null) return nil; var args = resbuf.AsArray(); var allowed = new HashSet<ObjectId>(); for (int i = 1; i < args.Length - 1; i++) if (args[i].TypeCode == (int)LispDataType.ObjectId) allowed.Add((ObjectId)args[i].Value); if (allowed.Count == 0) return nil; var ed = AcAp.DocumentManager.MdiActiveDocument.Editor; SelectionAddedEventHandler filter = (_, e) => { var objs = e.AddedObjects; for (int i = 0; i < objs.Count; i++) if (!allowed.Contains(objs[i].ObjectId)) e.Remove(i); }; ed.SelectionAdded += filter; var selection = ed.GetSelection(); ed.SelectionAdded -= filter; if (selection.Status == PromptStatus.OK) return new TypedValue((int)LispDataType.SelectionSet, selection.Value); else return nil; } } }
Well, I ought to thank you twice <here at least>, firstly, because the filtering behavior works well, and secondly because before this replay I had no working background of .NET with AutoCAD, as this replay encouraged me to get my hands on it.
_________
After some trials, I've modified your code a little to enable ssfiltered to take an AutoLISP selection set as a direct argument.
Here it is
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; using AcAp = Autodesk.AutoCAD.ApplicationServices.Core.Application; namespace FilteredSelectionLispFunction { public class LispFunctions { static TypedValue nil = new TypedValue((int)LispDataType.Nil); [LispFunction("ssfiltered")] public static TypedValue GetFilteredSelection(ResultBuffer resbuf) { var ed = AcAp.DocumentManager.MdiActiveDocument.Editor; if (resbuf == null) return nil; var args = resbuf.AsArray(); //selection set var allowed = new HashSet<ObjectId>(); //the members /*Mod Zone*/ if (args.Length > 1) { ed.WriteMessage("\n;error: too many arguments.\n"); return nil; } else { if (args[0].TypeCode == (int)LispDataType.SelectionSet) { SelectionSet selset = (SelectionSet)args[0].Value; if (selset.Count == 0) return nil; ObjectId[] objids = selset.GetObjectIds(); for (int i = 0; i < selset.Count; i++) allowed.Add(objids[i]); if (allowed.Count == 0) return nil; } else { ed.WriteMessage("\n;error: expected a selection set\n"); return nil; } } /*end Mod Zone*/ SelectionAddedEventHandler filter = (_, e) => { var objs = e.AddedObjects; for (int i = 0; i < objs.Count; i++) if (!allowed.Contains(objs[i].ObjectId)) e.Remove(i); }; ed.SelectionAdded += filter; var selection = ed.GetSelection(); ed.SelectionAdded -= filter; if (selection.Status == PromptStatus.OK) return new TypedValue((int)LispDataType.SelectionSet, selection.Value); else return nil; } } }
Again, thanks a lot, I really appreciate your informative effort.
Can't find what you're looking for? Ask the community or share your knowledge.