Hello all, I would love any help you can give. The following lisp uses a dialog box to select plant types, input quantity and select size of each plant and then creates a schedule. I want to be able to insert only the blocks that have been "toggled" not all of them. The final list will be about 30 items long. I was thinking of using a condition statement to select all items that have been toggled (l1, l2, l3, etc) and then pass the 2 attribute variables to the insert function for each plant. Not quite sure how to do it, but it sounds good ;-) I will also have to figure out how to set the insertion points automatically based on the number of items to insert, maybe adding the spacing value to the point value and redefining the variable over and over again as necessary? Any thoughts? Nick --------------------------------------------------------------------------- (defun C:hulcdiag () ;define function (setq NAMES '("3\" B&B" "2 1/2\" B&B" "2\" B&B" "1 1/2\" B&B" "24\" Box" "4'-6' B&B" "6'-8' B&B" "20 Gal" "15 Gal" "5 Gal" "1 Gal" "4\" Pot") ;define list ) ;setq (setq dcl_id (load_dialog "hulcdiag.dcl")) ;load dialog (if (not (new_dialog "hulcdiag" dcl_id) ;test for dialog ) ;not (exit) ;exit if no dialog ) ;if (start_list "l1s") ;start the list box (mapcar 'add_list NAMES) ;fill the list box (end_list) ;end list (start_list "l2s") (mapcar 'add_list NAMES) (end_list) (start_list "l3s") (mapcar 'add_list NAMES) (end_list) (action_tile "l1" "(setq l1 $value)") ;get plant 1 toggle value (action_tile "l2" "(setq l2 $value)") (action_tile "l3" "(setq l3 $value)") (action_tile "l1q" "(setq l1q $value)") ;get plant 1 quantity (action_tile "l2q" "(setq l2q $value)") (action_tile "l3q" "(setq l3q $value)") (action_tile "l1s" "(setq l1s $value)") ;get plant 1 size (action_tile "l2s" "(setq l2s $value)") (action_tile "l3s" "(setq l3s $value)") (action_tile "cancel" ;if cancel button pressed "(done_dialog) (setq userclick nil)" ;close dialog, set flag ) ;action_tile (action_tile "accept" ;if O.K. pressed "(done_dialog) (setq userclick T)" ;close dialog, set flag ) ;action tile (start_dialog) ;start dialog (unload_dialog dcl_id) ;unload (progn (setq l1s (nth (atoi l1s) names)) (setq l2s (nth (atoi l2s) names)) (setq l3s (nth (atoi l3s) names)) ) ;progn (setq pt1 (getpoint "\nPick Landscape Legend insertion point:")) (setq spc 0.5) (setq pt2 (list (car pt1) (- (cadr pt1) (* spc 2.5)))) ;set pt2 1.25" below pt1 (setq pt3 (list (car pt2) (- (cadr pt2) spc))) ;set pt3 1/2" below pt2 (setq pt4 (list (car pt3) (- (cadr pt3) spc))) (command "-insert" "hd-main" pt1 1 1 0) (command "-insert" "hd-l1" pt2 1 1 0 l1q l1s) (command "-insert" "hd-l2" pt3 1 1 0 l2q l2s) (command "-insert" "hd-l3" pt4 1 1 0 l3q l3s) (princ) );defun C:hulcdiag
Any thoughts?<<< Why not just use one list box and multiple select? Code: testlist : dialog { label = "Test List" ; :list_box { key = "plantlist" ; label = "Plants" ; height = 10 ; width = 16 ; fixed_width = true ; alignment = centered ; multiple_select = true ; } ok_cancel ; } Code: (defun set_names (/ item numlist) (setq numlist (get_tile "plantlist") ) (while (setq item (read numlist)) (setq plantlist (cons (nth item names) plantlist) numlist (substr numlist (+ (strlen (itoa item)) 2)) ) ) (setq plantlist (reverse plantlist)) ) (defun c:testlist (/ dcl_id plantlist) (setq dcl_id (load_dialog "testlist.dcl")) (if (not (new_dialog "testlist" dcl_id ""))(exit)) (setq NAMES '("3\" B&B" "2 1/2\" B&B" "2\" B&B" "1 1/2\" B&B" "24\" Box" "4'-6' B&B" "6'-8' B&B" "20 Gal" "15 Gal" "5 Gal" "1 Gal" "4\" Pot" ) ) (start_list "plantlist") (mapcar 'add_list NAMES) (end_list) (action_tile "accept" "(set_names)(done_dialog 1)") (setq an (start_dialog)) (unload_dialog dcl_id) (cond ((= an 1) plantlist ;do stuff here ) ) ) Bill
Bill, the only problem with this is that I want the user to be able to select, let's say 15 different plants, each plant will have a different quantity and a different size. If I use a single list box, I would not be able to select the quantity and size for each plant. Right? Nick
Nick, Sounds like you need to pick the Plant, then have a popup_list for quantity's, and sizes. So, they would pick a Plant Type, quantity, and size (in that order). Foreach Plant Type. Also, include a 'save' button and make up a List of Plant Type, Quantity, Size..i.e. ("Large Bush" "20" "6foot"). Then, for each 'sublist' in the List, do your inserts - based on the info. Bob
I think Bob's got the right idea. Pick a plant off the list, enter a quantity into an edit box, have a list of sizes in a popup list or list box. Then have a botton "build list" with an action tile that reads all 3 tiles and builds a list with ( "Plant type Plant size Quantity") on each line then dynamically list the entries in another list box that updates with each entry. HTH Bill
Bob and Bill, below is the DCL code I have so far. The users like it because all of the plants will be listed at once. I think picking the plant and quantity and size and then repeating could lead to confusion as to which plants had already been selected and saved. What I have below and is the lisp is definately code heavy after I insert all 30 plants. What I was trying to do was use the toggle code to justify saving the data and then insert all plants that have data, one right below the other. Nick --------------------------------------------------------------------------------------- hulcdiag : dialog { label = "Landscape Legend - Insert"; : boxed_column { label = "Plant Type"; // : row { : toggle { label = "Cercocarpus ledifolios"; key = "l1"; width = 30; } //end toggle : edit_box { label = "Quantity:"; key = "l1q"; edit_width = 5; alignment = right; } //end edit box : popup_list { label = "Sizes"; key = "l1s"; value = "1"; edit_width = 11; alignment = right; } //end list } //end row // : row { : toggle { label = "Cercis canadensis"; key = "l2"; width = 30; } //end toggle : edit_box { label = "Quantity:"; key = "l2q"; edit_width = 5; alignment = right; } //end edit box : popup_list { label = "Sizes"; key = "l2s"; value = "1"; edit_width = 11; alignment = right; } //end list } //end row // : row { : toggle { label = "Chilopsis linearis"; key = "l3"; width = 30; } //end toggle : edit_box { label = "Quantity:"; key = "l3q"; edit_width = 5; alignment = right; } //end edit box : popup_list { label = "Sizes"; key = "l3s"; value = "1"; edit_width = 11; alignment = right; } //end list } //end row } //end boxed column ok_cancel_help_errtile; } //end dialog
Your method will work to a point. I think you will run out of space (vertically) in the Dialog, with about 12 plants - then you max out. With a 'list_box', it will automatically expand when needed, and no 'code' - just variable names to deal with - and just (1) token of info - plant name..along with (2) tokens for quantity and size. With your method, if you wanted to add in a different plant, it would be 'code' time again...never ends. Just my thoughts. Bob
Bob, maybe the dialog box is not the way to go. Another way I could do this is to do an attribute extraction for all the plant types in the drawing. Then build the legend off of this information only. Again the information would be the plant type (block name) number of insertions and the size of the plant (an attribute in the block). This would be much cleaner, but I have no idea how to do it. That is why I was trying to do the dialog box, seemed easier at the time :-( Nick
Me thinks, Upon 2nd thought, your method could do say (10) plants at a time (using 'text' instead of Label..for the Plants), and have an index to the (entire) plant list... Then, (10) toggles, and (10) Edit Boxes.....Add a "Next group", and "Previous group" buttons, resetting the index, and refresh the dcl.. with Plant names (as the text1, text2, text3) tiles, and when toggle1 is on, get plant name, quantity, size tiles.... and make up a 'append' list with the data.. hmmm.......should work. Bob
Oh, I thought you wanted to get a 'list' of the plants, then insert them. If you want to go the other way, inserting plants, then extract a list of what plants are there, you will need to have at least (1) attribute in each plant. (Size). Then, it is relatively easy to get blockname (plant), size, and count how many of each you have. Is that the route you want to go ? Bob
This is what I had originally wanted to do but had no idea how to do it. I already have a simple attribute extraction that is used to count the number of plant types. Each block has a size attribute. So....all the information is there in the drawing. The nice idea about going this way is that the user could just select a button in the menu "Create Legend". I would prefer to go this way if possible, but would need a lot of help. Nick
Bob, below is a list of the 48 blocks. The first name "lcbldp02" is the block to scan for in the drawing (each block has 2 attrubutes, an "item" and "size" attribute, we only need the "size" value). The second name "L4" will be the block that inserts into the legend with an attribute for quantity and for size. lcbldp02-L4 lcblsp01-L7 lcblsp02-L28 lcblsp03-L8 lcblsp04--L14 lcblsp08-L39 lcblsp09-L6 lcblsp13-L1 lcblsp18-L12 lcblsp20-L2 lcblsp24-L13 lcblsp25-L15 lcblsp26-L9 lcblsp28-L3 lcblsp40-L5 lccnsp01-L30 lccnsp02-L10 lccnsp06-L16 lccnsp07-L17 lccnsp09-L11 lcpcsp03-L29 lcpcsp04-L47 lcshdp01-L37 lcshdp02-L25 lcshdp11-L45 lcshdp17-L20 lcshdp23-L48 lcshsp01-L31 lcshsp02-L42 lcshsp03-L38 lcshsp04-L44 lcshsp05-L43 lcshsp06-L22 lcshsp07-L27 lcshsp08-L23 lcshsp09-L40 lcshsp11-L35 lcshsp12-L24 lcshsp14-L41 lcshsp15-L46 lcshsp17-L19 lcshsp18-L21 lcshsp19-L33 lcshsp20-L26 lcbldp01-L18 lcbldp06-L32 lcbldp11-L34 lcblsp11-L36 Thanks for offering to help Bob! Nick
Ok, I think we need to go off-line to finish up. Please give me an E-Mail at: (remove the nospam + ".") I will need a sample .dwg with a few of your blocks inserted, and a sample legend - or at least the dimensions for placements.. Sounds like a fun little project. Bob
Nick, Thanks for the E-Mail. Here is a working version. (defun clant_legend () ; extract plant information, make a list of quantity / plants / sizes ; Build a 'list' of the plant (block names) (setq plant_blocknames (list "LCBLDP02" "LCBLSP01" "LCBLSP02" "LCBLSP03" "LCBLSP04" "LCBLSP08" "LCBLSP09" "LCBLSP13" "LCBLSP18" "LCBLSP20" "LCBLSP24" "LCBLSP25" "LCBLSP26" "LCBLSP28" "LCBLSP40" "LCCNSP01" "LCCNSP02" "LCCNSP06" "LCCNSP07" "LCCNSP09" "LCPCSP03" "LCPCSP04" "LCSHDP01" "LCSHDP02" "LCSHDP11" "LCSHDP17" "LCSHDP23" "LCSHSP01" "LCSHSP02" "LCSHSP03" "LCSHSP04" "LCSHSP05" "LCSHSP06" "LCSHSP07" "LCSHSP08" "LCSHSP09" "LCSHSP11" "LCSHSP12" "LCSHSP14" "LCSHSP15" "LCSHSP17" "LCSHSP18" "LCSHSP19" "LCSHSP20" "LCBLDP01" "LCBLDP06" "LCBLDP11" "LCBLSP11" )) (setq legend_blocknames (list "L4" "L7" "L28" "L8" "L14" "L39" "L6" "L1" "L12" "L2" "L13" "L15" "L9" "L3" "L5" "L30" "L10" "L16" "L17" "L11" "L29" "L47" "L37" "L25" "L45" "L20" "L48" "L31" "L42" "L38" "L44" "L43" "L22" "L27" "L23" "L40" "L35" "L24" "L41" "L46" "L19" "L21" "L33" "L26" "L18" "L32" "L34" "L36" )) ;; ;; define dxf function ;; (defun dxf (code elist) (cdr (assoc code elist)); Find association pair, strip first element ); end function ;; ; Function to sort the list, make final list.. (defun sort_the_list () (setq blocks_lst (list)); empty list (foreach blname plant_blocknames (setq Q 0 types_lst nil); quantity / types (foreach sublst all_plants (setq block (car sublst)) (setq typex (car (cdr sublst))) (if (= block blname) (progn (if (member (strcat block typex) types_lst) (setq Q (+ Q 1)); count matching types ); if (if (not (member (strcat block typex) types_lst)) (progn (setq types_lst (cons (strcat block typex) types_lst)); add to list (setq Q 1 otypex typex); first of this type ); progn ); if ); progn ); if ); foreach sublst (setq types_lst nil) (if (> Q 0) (progn (setq Q (itoa Q)) (setq final_lst (cons (list blname otypex Q) final_lst)) (setq Q 0 otypex "") ); progn ); if ); foreach ); function ;; Begin Process ;; Init variables used (setq final_lst nil types_lst nil all_plants nil) ;; Foreach plant name in the 'list', get type, make a list (foreach plant plant_blocknames (setq ss nil) (setq ss (ssget "X" (list (cons 2 plant)))) (if ss (progn (setq c 0 lst nil bn plant) (repeat (sslength ss) (setq plant_info (entget (ssname ss c))) (setq attr (entget (entnext (dxf -1 plant_info)))) (setq size (cdr (assoc 1 attr))) (setq all_plants (cons (list bn size) all_plants)) (setq c (+ c 1)) ); repeat ); progn ); if ); foreach ;; Now that we have some plants in 'all_plants' list, let's sort that (if all_plants (sort_the_list) ); if ;; And if we have the final_lst list, let's build a Legend.. (if final_lst (progn ; Insert the Header first, drag mode.. (setq P1 (getpoint "\nPick a Point for the Legend Header Block:")) (if P1 (progn (command "_-insert" "l-main" P1 "1" "1" "0"); Header (setq IP (list (+ (car P1) 0.0)(- (cadr P1) 1.125))); First 'data' IP (command "_limits" "_off"); case we run off the bottom of the sheet.. (foreach data final_lst (setq block_to_insert ""); unless on list.. (setq blockname (car data)); blockname of insert (setq typex (cadr data)); type (setq Quant (caddr data)); string format of Q (setq c 0) (foreach blk plant_blocknames (if (= blk blockname) (setq block_to_insert (nth C legend_blocknames)); Link to 'L4' name ); if (setq c (+ c 1)); counter ); foreach (if (/= block_to_insert "") (progn (command "_-insert" block_to_insert IP "1" "1" "0" Quant typex); Insert Legend (setq IP (list (+ (car IP) 0.0)(- (cadr IP) 0.500))) ); progn ); if ); foreach ); progn ); if ); progn ); if ); function plant_legend (princ); silent load (clant_legend); and self-run it --------------------- Cheers: ) Bob Shaw www.bobscadshop.com
Bob, thank you for all of your hard work on this. There is no way I could have done this so fast, if at all! In simplistic terms, I believe the routine does the following: Creates 2 lists of block names. Searches drawing for first list block names associates the first list to the second gather the number of occurances for each block gather the attribute value to fill in sort the list insert the Header insert the data, resetting the insertion point for each block insertion as many times as is necessary. Most of the commands I have used in various routines before except "foreach". The way I read it, it allows you to assign the same variable to all items in a list over and over again as it steps through each list item? Is that correct? Nick
Nick, Glad you like it. I think you have a good handle on what it is up to. Yes, makes (2) list of blocknames, gathers selection set(s) of plant blocks, gets the legend block (as same index number) to legend block list, foreach 'block' in plant_blocknames..gathers the attribute required, places all info into the all_lst, which is then sorted to make quantities of each 'blockname' and 'type', and finally, if there is a final_lst..builds the legend, and fills in the Quantity and 'type'. Having said that, yes, you are correct on what the foreach.. function does. It loops from top to bottom of a 'list' of items, and references each item. Example: Foreach lay layers_lst .... would set 'lay' to the first value of 'layers_lst', then you perform checks / changes on 'lay', then, the foreach keeps on going, getting each item of 'layers_lst', in turn.. Kinda like a [For I = 0 to 10] loop in Basic, where I is incremented 0, 1, 2, 3..etc. Bob