Author Topic: CHALLENGE: Individual attribute edit like DDEDIT  (Read 9311 times)

0 Members and 1 Guest are viewing this topic.

nivuahc

  • Guest
CHALLENGE: Individual attribute edit like DDEDIT
« on: May 09, 2006, 09:23:42 AM »
Okay, here you go.

I got handed a batch of panelboard schedules yesterday that ought to keep me occupied for a while. Anyway, our panelboard schedules consist of lots of blocks with lots of attributes. What's worse, some of the work I'll need to do is edits of existing schedules. That was an annoyance that I needed to resolve and I came up with a way to do it. I thought it would make an interesting challenge so I'll explain what it does here:

Much like the DDEDIT command in AutoCAD works on single line text, this routine should allow the user to select a single attribute in a block and edit accordingly. In other words, a small dialog box with the existing value that, when ENTER is pressed or OK is clicked, changes the value to suit and prompts the user for the next selection... just like DDEDIT.

For a better explanation, type DDEDIT in AutoCAD and edit a line of DTEXT.

Just like that.

Now, the reason why I think this would be a decent challenge is because

1. It can be very useful
2. It was much easier than I thought it would be
3. It should cover a few techniques that an up and coming LISP'er might find very useful


And for those of you who think that I'm presenting this as a challenge so I can get free code, go suck an egg. Like I said, I already wrote it. And, if anyone rises to the challenge, I'll gladly post my solution, crappy formatting, silly comments, faults, and all.

I'm not gonna post what I wrote just yet, because I don't want to taint your thought processes. I will say, however, that I used the following techniques:

Build a temp DCL (a technique I totally ripped off from a post of Mark Thomas')
Retrieve the value of an individual attribute
Replace the value of the selected attribute
The most basic of error handling
A simple loop

So, what ya got?


And, as an added bonus, I'll even post the command-line version I used previously in all it's craptacular beauty!

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16717
  • Superior Stupidity at its best
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #1 on: May 09, 2006, 10:25:18 AM »
Ok .. I'll take this challenge .. although I am sure I could make this a bit more efficient, I just threw something together.

Lets see if it can be improved.

Mind you I cheated by not creating a temp DCL .. I just used the widget already built in.

Code: [Select]
(defun C:DDATT (/ ATTLIST ATTVAL DDATT_DCL)
  (setq ATTLIST   (entget (car (nentsel))) ;attempt to grab the entity
ATTVAL   (cdr (assoc 1 ATTLIST)) ;attempt to grab the text
DDATT_DCL (load_dialog "ACAD") ;load the default AutoCAD dialog definitions
  )
;;; If there was a text or attribute value selected
  (if ATTVAL
    (progn
;;; Invoke the standard text edit dialog assembly
      (new_dialog "acad_txtedit" DDATT_DCL)
;;; Set the edit box to the value we have retrieved from the entity
      (set_tile "text_edit" ATTVAL)
;;; Assign actions to the edit box as well as the cancel button
;;; The ok button is already default allow accept so we don't
;;; need to worry with it. The action for the edit box is upon
;;; "lost focus" so we don't need to grab the value explicitly
;;; except in the action for the edit box.
      (action_tile "text_edit" "(setq ATTVAL $value)")
;;; If we want to cancel, we must reset the value to the original
;;; because the text edit action is on lost focus. Thus the action
;;; fires if you simply click in and out of the text edit box.
      (action_tile "cancel" "(setq ATTVAL (cdr (assoc 1 ATTLIST)))")
;;; initialize our dialog
      (start_dialog)
;;; update the entity and update the block, otherwise it will not
;;; reflect the changes until the next regen
      (entupd (cdr (assoc -1 (entmod (subst (cons 1 ATTVAL) (assoc 1 ATTLIST) ATTLIST)))))
    )
  )
;;; unload our dialog (something that most people forget about when using DCL)
  (unload_dialog DDATT_DCL)
;;; exit quietly
  (princ)
)

*edited code to add comments for easier understanding*
« Last Edit: May 09, 2006, 01:50:26 PM by Keith™ »
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

nivuahc

  • Guest
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #2 on: May 09, 2006, 10:45:16 AM »
Hawt dang, Keith! That's waaaaaaay shorter than mine :D

Tres cool  :kewl:

John Kaul (Se7en)

  • Administrator
  • Needs a day job
  • Posts: 9248
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #3 on: May 09, 2006, 11:35:06 AM »
Talk to CAB.
“Common sense is not so common.” ~Voltaire

--> Donate to TheSwamp.org <--

CAB

  • Global Moderator
  • Seagull
  • Posts: 10362
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #4 on: May 09, 2006, 11:47:00 AM »
Wow Keith, mine is a behemoth compared to your.
Very efficient & nice use of the DCL.

Mine used the edit box, not dcl.
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

nivuahc

  • Guest
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #5 on: May 09, 2006, 12:04:15 PM »
What I had originally:

Code: [Select]
(defun C:AttRep (/ AttSel AttLoc NewVal)
 
  (defun *error* (msg)
    (princ "\nInvalid selection.")
    (princ)
  );_ end defun *error*

  (defun SelectIt (msg)
  (setq AttLoc (cadr (setq AttSel (entsel (strcat "\nPick " msg " to replace: ")))))
    );_ end defun SelectIt

  (defun RepIt (/)
    (setq NewVal (strcase (getstring T "\nNew attribute value: ")))
    (command "-attedit" "y" "" "" "" AttLoc "" "v" "r" NewVal "n")
    );_ end defun WipeIt

  (setvar "cmdecho" 0)
 
  (SelectIt "attribute")
  (RepIt)
  (while AttSel
    (SelectIt "next attribute")
    (if (= (type AttSel) 'list)
      (RepIt)
      (setq AttSel nil)
      ) ;_ end if
    ) ;_ end while
  (prompt "\nWasn't that easy?\n")
  (princ)
  );_ end defun AttRep

What I changed it to yesterday:

Code: [Select]
(defun C:AttRep (/ Sel_Att Att_Val)
  (vl-load-com)

  (defun *error* (msg)
    (princ "\nInvalid selection.")
    (princ)
  ) ;_ end defun *error*

  ;;>>>>>  temporary .dcl file <<<<<
  (defun mktemp-dcl (/ tmpf tmpfo)
    (vl-load-com)
    (setq tmpf (vl-filename-mktemp nil nil ".dcl")
  tmpfo (open tmpf "w")
    )
    (write-line
      (strcat
"attrep : dialog {
     label = \"Replace Attribute\" ;
     initial_focus = \"at_val\" ;
     : edit_box {
     key = \"at_val\" ;
     label = \"Edit Attribute\" ;
     value = \"0\" ;
     edit_width = 60 ;
     allow_accept = true ;
     }
     ok_cancel ;
     }"
      )
      tmpfo
    )
    (close tmpfo)
    tmpf
  )
  ;;>>>>>  temporary .dcl file <<<<<

  ;; get the value of an attribute
  (defun SelectIt (/)
    (setq Att_Val
   (vlax-get-property
     (setq Sel_Att
    (vlax-ename->vla-object
      (car (nentsel))
    )
     )
     'TextString
   )
    )
  ) ;_ end defun SelectIt

  ;; replace the value of an attribute
  (defun RepIt (/)
    (if
      (= (vlax-get-property Sel_Att 'ObjectName) "AcDbAttribute")
       (vlax-put-property Sel_Att 'TextString New_Att_Val)
    ) ;_ end if
  ) ;_ end defun RepIt

  ;; display a simple dialog with the original attribute value
  (defun ShowIt (/ db dcl_id New_Att_Val user_event)
    (setq db (mktemp-dcl)
  dcl_id (load_dialog db)
    )
    (if (not (new_dialog "attrep" dcl_id))
      (exit)
    )

    (set_tile "at_val" Att_Val)

    (action_tile
      "accept"
      (strcat
"(progn (setq New_Att_Val (get_tile \"at_val\"))
      (done_dialog) (setq user_event T))"
      )
    )
    (action_tile "cancel" "(done_dialog)")

    (start_dialog)
    (unload_dialog dcl_id)

    (if user_event
      (progn
(RepIt)
;; put the value of an att
(vl-file-delete db)
      )
    )
  ) ;_ end defun ShowIt

 
  (SelectIt)

  (if
    (= (vlax-get-property Sel_Att 'ObjectName) "AcDbAttribute")
    (ShowIt)
    (setq Sel_Att nil)
    ) ;_ end if

 
  (while Sel_Att
    (SelectIt)
    (if (= (vlax-get-property Sel_Att 'ObjectName) "AcDbAttribute")
      (ShowIt)
      (setq Sel_Att nil)
    ) ;_ end if
  ) ;_ end while
) ;_ end defun AttRep

I am so totally gonna put that widget thinger in my grab bag of goodies, Keith.

MP

  • Seagull
  • Posts: 17400
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #6 on: May 09, 2006, 12:25:48 PM »
Edit 1: Added hililiting when cursor is over a valid attribute entity.
Edit 2: Commented code a bit for others.
Edit 3: Added a little screen grab for fun.
Edit 4: Dumped the edit box label.



This seems like a lot of code but it really isn't -- it's just partitioned into 5 functions. It was however written quick and dirty, so error handling is pretty light.

Code: [Select]
(defun c:EditAttrib ( / _ExeFooAtPoint _Foo _EditAttrib _MakeDCL _Main )

    ;;=================================================================
    ;;
    ;;  utility function that allows one to execute an arbitrary
    ;;  function on a point selected by the user. While the user
    ;;  moves the cursor about it will optionally dynamically hilite
    ;;  entities that match the hilite match string, e.g. ATTRIBS.
    ;;
    ;;=================================================================

    (defun _ExeFooAtPoint ( foo pmt hilite / _Hilite data key value done enames )

        ;;=============================================================
        ;;  foo is an arbitrary function that takes one argument, a
        ;;  point. What is does we care not. It is up to foo to ensure
        ;;  it addresses any and all potential errors.
        ;;=============================================================
        ;;  pmt is a prompt string
        ;;=============================================================
        ;;  hilight is an entity type match string, e.g. "ATTRIB". The
        ;;  function will hilite matching entities as it goes.
        ;;=============================================================

        (defun _Hilite ( point hilite / ename etype match )
            ;;  uses local global enames           
            (cond
                (   (setq ename (car (nentselp point)))
                    (setq etype (cdr (assoc 0 (entget ename))))
                    (cond
                        (   (setq match (wcmatch etype hilite))
                            (cond
                                (   (null (member ename enames))
                                    (redraw ename 3)
                                    (setq enames (cons ename enames))
                                )
                            )
                        )
                        (   enames
                            (foreach ename enames (redraw ename 4))
                            (setq enames nil)
                        )
                    )
                )
                (   enames
                    (foreach ename enames (redraw ename 4))
                    (setq enames nil)
                )
            )
        )

        (setq hilite (strcase hilite))

        (princ pmt)

        (while (not done)
            ;;  stay in the loop until the
            ;;  user presses <enter> or <esc>
            (vl-catch-all-apply
               '(lambda ( )
                    (setq
                        data nil
                        data (grread t 13 2)
                    )
                )
            )
            (setq
                key   (car data)
                value (cadr data)
            )
            (cond
                ;;  user pressed <enter> or <esc> exit quietly
                ((member data '(nil (2 13))) (setq done t value nil))
                ;;  user hit right mouse button, consider done
                ((eq 25 key)(setq done t value nil))
                ;;  user picked a point, let's roll
                ((eq 3 key) (foo value))
                ((eq 5 key) (_Hilite value hilite))
            )
        )

        (foreach ename enames (redraw ename 4))

    )
   
    ;;=================================================================
    ;;
    ;;  utility function used to determine if other functions should be
    ;;  invoked; this function will be passed to the _ExeFooAtPoint
    ;;  function.
    ;;
    ;;=================================================================

    (defun _Foo ( point / ename data )
        (if (setq ename (car (nentselp point)))
            (if (setq data (entget ename))
                (if (eq "ATTRIB" (cdr (assoc 0 data)))
                    (_EditAttrib (vlax-ename->vla-object ename))
                    (princ "<not an attribute>")
                )
                (princ "<no data for entity>")

            )
            (princ "<no entity at point>")
        )
    )

    ;;=================================================================
    ;;
    ;;  allow the user to edit the attribute value via a dialog
    ;;
    ;;=================================================================

    (defun _EditAttrib ( attribObject / _Text _Cancel _Accept text )

        ;;  note, uses local global dclid

        (defun _Text ( value )
            ;;  update local global
            (setq text value)
        )

        (defun _Cancel ( )
            ;;  just bail
            (done_dialog)
        )

        (defun _Accept ( )
            ;;  uses local glabal text
            (vla-put-textstring
                attribObject
                text
            )
            (done_dialog)
        )

        (new_dialog "EdAtt" dclid)

        (set_tile "title"
            (strcat "Edit attribute <"
                (vla-get-tagstring attribObject)
                ">"
            )
        )

        (set_tile "text"
            (setq text
                (vla-get-textstring attribObject)
            )
        )

        (action_tile "text" "(_Text $value)")
        (action_tile "cancel" "(_Cancel)")
        (action_tile "accept" "(_Accept)")
        (start_dialog)

    )

    ;;=================================================================
    ;;
    ;;  create a dcl file on the fly, return the filename to the caller
    ;;
    ;;=================================================================

    (defun _MakeDCL ( / fname handle )

        (setq
            fname  (vl-filename-mktemp "1.dcl")
            handle (open fname "w")
        )

        (mapcar '(lambda (x) (write-line x handle))
           '(
                "EdAtt : dialog {"
                "    label = \"Edit Attribute <TagString>\";"
                "    key = \"title\";"
                "    initial_focus = \"text\";"
                "    spacer;"
                "    :   edit_box {"
                "        key = \"text\";"
                "        edit_width = 40;"
                "        edit_limit = 2048;"
                "        allow_accept = true;"
                "    }"
                "    spacer;"
                "    ok_cancel;"
                "    spacer;"
                "}"
            )
        )

        (close handle)

        fname

    )

    ;;=================================================================
    ;;
    ;;  invoke functions defining the solution
    ;;
    ;;=================================================================

    (defun _Main ( / dclname dclid )
        (cond
            (   (setq dclid
                    (load_dialog
                        (setq dclname (_MakeDCL))
                    )
                )
                (_ExeFooAtPoint
                    _Foo
                    "Pick an attribute <esc|enter to end>: "
                    "ATTRIB"
                )
                (unload_dialog dclid)
                (vl-file-delete dclname)
            )
        )
        (princ)
    )

    ;;=================================================================
    ;;
    ;;  let's roll ...
    ;;
    ;;=================================================================

    (_Main)

)



Try it Chuck, I think you'll like it's behavior.

:)
« Last Edit: May 10, 2006, 09:52:37 AM by MP »
\|// Set goal. Experiment tirelessly until
|Oo| practice has become expertise.  Loop.
|- | LinkedIn | Dropbox

GDF

  • Water Moccasin
  • Posts: 1990
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #7 on: May 09, 2006, 02:34:17 PM »
Michael, I love it, thanks.

You have given me some better ideas as always.
How about adding a zoom to attribute and listing all of the values within the dialog box. [picky as an example only]

This one uses a modified version of the following:
FINDATT.LSP Finds an attribute value
Program by Tony Hotchkiss for AutoCAD 2000.

Gary
Why is there never enough time to do it right, but always enough time to do it over?
BricsCAD 2019x64 Windows 10x64

MP

  • Seagull
  • Posts: 17400
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #8 on: May 09, 2006, 02:52:55 PM »
Thank you Gary. I like the challenge but I don't see the value in the zoom aspect. Selecting attributes based on a box selection (within / crossing etc.) might be a good idea tho.

You and Tony have done a mighty nice job with FINDATT.LSP, yeowza!!
\|// Set goal. Experiment tirelessly until
|Oo| practice has become expertise.  Loop.
|- | LinkedIn | Dropbox

nivuahc

  • Guest
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #9 on: May 09, 2006, 04:59:58 PM »
Michael,

I tried it out and, yes, I do like it's behavior. It's likely my computer, but it runs very slow and choppy. Still like it though :)

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #10 on: May 09, 2006, 05:05:15 PM »
Nice example code Michael.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate to theSwamp<--

CAB

  • Global Moderator
  • Seagull
  • Posts: 10362
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #11 on: May 09, 2006, 05:46:08 PM »
No problems here, nice work as always Michael.
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

MP

  • Seagull
  • Posts: 17400
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #12 on: May 09, 2006, 06:20:20 PM »
Thanks for the favorable comments folks, unexpected as it was bashed out real quick!!

Speaking of ... I rewrote the _Hilite function. It seemed to behave a bit flaky -- I think the redraw calls (of 3 and 4) were not balanced, the upshot being if you swept accross attributes quickly it wouldn't clear the hilighting correctly (would leave some attributes in a hilighted state). Think it's fixed now, tho it wasn't exhaustively tested.

Increased the edit box width too.

<Chuck> Why it would execute slowly I'm not sure -- I don't have a speed demon here and it seems to run just fine <blink> <blink>.

Thanks again for trying it out and commenting folks, nice to have feedback like this.

PS: Why'd I bother? Because we can use this >here< (in other words, exploiting a win-win opportunity).

:)

MP
\|// Set goal. Experiment tirelessly until
|Oo| practice has become expertise.  Loop.
|- | LinkedIn | Dropbox

nivuahc

  • Guest
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #13 on: May 10, 2006, 06:40:47 AM »
Well the possible reason it was running slow for me is that the drawings I'm working on right now are chock full of attributes. Let's see... 1 header with 10 attributes, 42 spaces per schedule, 4 attributes each space, one "calculations" block with 10 attributes and 16 schedules per page... a little over 3000 attributes per drawing. That's a lot of attributes.

Still, I liked the way it functions. I noticed the _Hilite leaving some attributes uncleared thing so I'll have to try out the latest version once I get to the orifice.

There's a lot to learn from all of the posted routines (mine excluded) and it's given me ideas for other things I could write/use. I posted the challenge for the same reason you accepted it. :)

MP

  • Seagull
  • Posts: 17400
Re: CHALLENGE: Individual attribute edit like DDEDIT
« Reply #14 on: May 10, 2006, 07:55:09 AM »
Hey Chuck -- I made a test dwg with 2048 instances of a block with 8 attributes, for a total of 16384 attributes -- still performed snappy for me.

:)
\|// Set goal. Experiment tirelessly until
|Oo| practice has become expertise.  Loop.
|- | LinkedIn | Dropbox