Block and BlockReference via RevDate

Discussion in 'AutoCAD' started by Michael Barb, Oct 5, 2004.

  1. Michael Barb

    Michael Barb Guest

    Following is my adventure into AutoCAD VBA.

    I work in a company that has 25 AutoCAD LT seats and one networked full version of AutoCAD. We do electrical schematics so LT is fine 99.9% of the time. On rare occasion someone will need the full version and run a macro.

    Personally, I am mostly a programmer with over 30 years experience. At this point I have forgotten more programming languages than I know.

    Our revision tracking system is based on an LT function called RevDate. All LT seats have a save icon with an "R" on it that runs RevDate on saving. Basically what RevDate does is checks the model space for a block called RevDate and updates its attributes when it finds it. The attributes are User Name, Date, File Name. If the block is not found it creates it and updates it. Since it is a block it is always on the drawing, even when we view the drawings online.

    In AutoDesk's greater wisdom they did not implement RevDate on the full version. To my surprise I could not find this function anywhere. I would have paid for it but no one has this function in their utility packages. I could not find it in any free packages, knowledge base, or forum. I ask the question several times in different forums and never found it. I could not believe it could be that hard and negotiate with my local AutoCAD distributor that he would provide the function as part the purchased a full network license.

    His applications engineer due fully sent me an attempt at the function. It was well written and organized but did not work. After about 5 tries and studying his program and searching the knowledge base and forums for answers I came to the conclusion that he did not fully understand the AutoCAD object model. After reading the forums I came to the conclusion that he has a lot of company.

    I did not understand it either so I purchased a couple of books. These have been discussed in this forum before. I got both Joe Sutphin's and Sham Tickoo's books from Amazon. Sham's book tries to cover to many topics in to few pages and I found it of little use. I may use it later when I want to know a little more about Diesel or scripts but for VBA it has to little detail to be useful. Joe's book concentrates on VBA and is more than twice the size of Sham's. In it on bottom of page 348 I found a few paragraphs on the differences between Block definition and Block Reference an it was like a flash of light. I set down and in about four hours I rewrote 90% of the program and got most of it to work. It was shorter than the original one and handle more contingencies.

    I got stuck on the part of how to edit the attributes in the blockrefence that was on the drawing. The original program deleted and then re-added the block. Upon reading it could change the values in the attributes. This seemed like brute force and I felt there had to be a better way. I studied Joe's book for another 4 hours and could not find it. I started looking at the knowledge base and forum articles I had copied. In one of the forum articles dated 12/21/01 from someone called Andy Star was the answer. The second flash of light. The method GetAttributes returned a reference and allowed editing as well as reading. Based on the name of the method this was not intuitive.

    I went back and read Joe's book and I think he even missed this point. I looked at the help file. They mention the possibility in a couple of lines buried deep in an example. I tried it and it works well but in some ways it still does not make total sense. You need to use variants to get it and search it. There is something about its usage that just does not seem right but I cannot figure out what it is.

    One thing that I think is missing form the AutoCAD's Object Model Methods are the "Is" functions to check the data type of an entity. You have to check the object's EntityName against the ObjectARX name. There is no listing of the names and you have to figure them out from the watch window. Most VBA extensions have methods like IsBlockReference and hide these gory details from the user.

    I found out why AutoDesk did not put RevDate in the full version. In LT there is a system variable called User. In the full version this variable does not exist. I used the login name instead. I really do not like it but cannot find a better alternative.


    It seams like a common thing that people would want to do, search a drawing for a block and update its attributes. I am kind of surprised I could not find a good example of this function. I found bits and pieces but never the entire thing. Following is the program that I developed. I would like comments on how to optimize it better.

    I included the program in the text of this post. Attachments disappear in archiving. It also makes looking at is quicker.

    There are other forum posts that helped that I did not acknowledged. As payback, I hope in the future it helps someone else.

    Yours truly, Michael Barb






    Attribute VB_Name = "RevDate"
    ' This module duplicates in the Full version of AutoCAD the function found in AutoCAD LT
    ' One limitation is the variable UserName does not exist in the Full version. To Substitute I used the LoginName.
    'By: Michael Barb 10/2/04


    Public oRevDateBlockReference As AcadBlockReference

    Public Sub RevDate()

    If Have_Block_Reference Then
    Update_Block_Reference_Attributes
    Else
    If Have_Block Then
    Insert_Block_Reference
    Update_Block_Reference_Attributes
    Else
    Insert_Block
    Insert_Block_Reference
    Update_Block_Reference_Attributes
    End If
    End If

    End Sub


    Private Function Have_Block_Reference() As Boolean
    Dim objEntity As AcadEntity

    For Each objEntity In ThisDrawing.ModelSpace

    If objEntity.EntityName = "AcDbBlockReference" Then
    If UCase(objEntity.Name) = "REVDATE" Then
    Set oRevDateBlockReference = objEntity
    Have_Block_Reference = True
    Exit Function
    End If
    End If
    Next objEntity
    Have_Block_Reference = False

    End Function



    Private Function Have_Block() As Boolean
    Dim objBlock As AcadBlock

    For Each objBlock In ThisDrawing.Blocks
    If UCase(objBlock.Name) = "REVDATE" Then
    Have_Block = True
    Exit Function
    End If
    Next objBlock
    Have_Block = False

    End Function



    Private Sub Insert_Block()
    Dim objBlock As AcadBlock
    Dim objAttribute As AcadAttribute

    Dim pntInsertion(2) As Double
    pntInsertion(0) = 0#
    pntInsertion(1) = 0#
    pntInsertion(2) = 0#

    Set objBlock = ThisDrawing.Blocks.Add(pntInsertion, "RevDate")

    Set objAttribute = objBlock.AddAttribute(0.15, acAttributeModeNormal, "", pntInsertion, "USER", "")
    pntInsertion(0) = 1.25
    Set objAttribute = objBlock.AddAttribute(0.15, acAttributeModeNormal, "", pntInsertion, "REVDATE", "")
    pntInsertion(0) = 4.5
    Set objAttribute = objBlock.AddAttribute(0.15, acAttributeModeNormal, "", pntInsertion, "FNAME", "")

    End Sub


    Private Sub Insert_Block_Reference()
    Dim pntUserSetPoint As Variant
    Dim sPrompt As String

    sPrompt = "Enter insertion point for REVDATE block"
    pntUserSetPoint = ThisDrawing.Utility.GetPoint(, sPrompt)

    Set oRevDateBlockReference = ThisDrawing.ModelSpace.InsertBlock(pntUserSetPoint, "REVDATE", 1#, 1#, 1#, 0)

    End Sub



    Private Sub Update_Block_Reference_Attributes()

    Dim sUname As String
    sUname = ThisDrawing.GetVariable("LOGINNAME")

    Dim sRevDate As String
    sRevDate = Date & " " & Format(Time, "Medium Time")

    Dim sFilename As String
    sFilename = ThisDrawing.Name

    Dim objAttribute As Variant 'AcadAttributeReference
    Dim objAttributeList As Variant 'Array of AcadAttributeReference
    ' The type shown above is what these references are but the For search will not work unless they declared as Variant
    objAttributeList = oRevDateBlockReference.GetAttributes

    For Each objAttribute In objAttributeList

    Select Case UCase(objAttribute.TagString)
    Case "USER"
    objAttribute.TextString = sUname
    Case "REVDATE"
    objAttribute.TextString = sRevDate
    Case "FNAME"
    objAttribute.TextString = sFilename
    End Select

    Next objAttribute
    End Sub
     
    Michael Barb, Oct 5, 2004
    #1
  2. Michael Barb

    TomD Guest

    I'm not sure if this will help you, or not, Michael, but I'll throw it at you. This is the code behind a form, with the intent of modifying title blocks across various layouts, without changing to each one. It has a few minor issues, but works well.

    Again, I don't know if it will help you..............there was an awful lot of ranting in your post. I'd be happy to try clarifying any of the code below, though I can't promise you anything. I wrote this some time ago (2+ years, I think), and as you can see, I didn't comment it as well as I should have.

    Option Explicit
    Dim sBlkNam As String 'Selected block name to manage
    Dim vBlkLst As Variant 'List of found instances of sBlkNam in ObjectID, LayoutName format
    Dim vAttLst() As Variant 'List of attribute, value pairs for currently selected block/layout
    Dim iCurAtt As Integer 'Index of currently selected attribute

    Private Sub UserForm_Initialize()
    Call FillBlockNames
    End Sub

    Private Sub cbtDoUpdate_Click()
    Dim iBlkId As Long, iTmp As Integer
    If Me.chkGlobal.Value = False Then
    'Operate on selected block/layout only
    iBlkId = vBlkLst(Me.cbxLayNam.ListIndex * 2)
    Call UpdateBlockAtts(iBlkId, vAttLst)
    Else
    'Operate on ALL instances selected block
    For iTmp = 1 To UBound(vBlkLst) Step 2
    iBlkId = vBlkLst(iTmp - 1)
    Call UpdateBlockAtts(iBlkId, vAttLst)
    Next
    End If
    Me.lbxAttLst.Clear: Me.cbxLayNam.Clear
    End Sub

    Private Sub cbtUpdLst_Click()
    If IsArrayEmpty(vAttLst) Then Exit Sub
    'Debug.Print "Update list for attribute " & iCurAtt
    vAttLst(1, iCurAtt) = Me.tbxNewVal.Text
    Me.lbxAttLst.Column = vAttLst
    Me.tbxNewVal.Text = "": Me.lbxAttLst.ListIndex = -1
    End Sub

    Private Sub lbxAttLst_Click()
    iCurAtt = Me.lbxAttLst.ListIndex
    If iCurAtt > -1 Then
    'Debug.Print vAttLst(0, iIdx) & ": " & vAttLst(1, iIdx)
    Me.tbxNewVal.Text = vAttLst(1, iCurAtt)
    End If
    End Sub

    Private Sub cbxLayNam_Change()
    Dim iIdx As Integer, lBlkId As Long
    iIdx = Me.cbxLayNam.ListIndex
    If iIdx < 0 Then Exit Sub
    'Debug.Print " current UBound for vBlkLst: " & UBound(vBlkLst)
    If Not IsArrayEmpty(vBlkLst) Then
    lBlkId = vBlkLst(iIdx * 2)
    Call GetAttsForIndex(lBlkId)
    End If
    End Sub

    Private Sub chkGlobal_Change()
    If Me.chkGlobal.Value = False Then
    Me.cbxLayNam.Enabled = True
    Else
    Dim lBlkId As Long
    Me.cbxLayNam.Enabled = False
    lBlkId = vBlkLst(0)
    Call GetAttsNoValues(lBlkId)
    End If
    End Sub

    Private Sub cbxBlkNam_Change()
    Me.cbxLayNam.Clear
    If Not IsArrayEmpty(vBlkLst) Then Erase vBlkLst
    sBlkNam = Me.cbxBlkNam.Text
    vBlkLst = GetTbList(sBlkNam)
    If IsArrayEmpty(vBlkLst) Then
    MsgBox "No references found for selected block!"
    Else
    'Debug.Print "UBound: " & UBound(vBlkLst)
    Call FillLayouts(vBlkLst)
    End If
    End Sub

    '================================================================================
    'Sub and Function set
    '================================================================================

    Private Function GetTbList(BlkName As String) As Variant
    Dim oSet As AcadSelectionSet, vCod(1) As Integer, vFlt(1) As Variant
    Set oSet = SelSetInit("TitleBlockMgr")
    vCod(0) = 0: vFlt(0) = "INSERT"
    vCod(1) = 2: vFlt(1) = BlkName
    oSet.Select acSelectionSetAll, , , vCod, vFlt
    'Debug.Print oSet.Count & " occurences of " & BlkName & " found"
    Dim iTmp As Integer, lBlkId As Long, sLay As String
    Dim vRetVal() As Variant
    For iTmp = 0 To oSet.Count - 1
    'Debug.Print ThisDrawing.ObjectIdToObject(oSet(iTmp).OwnerID).Layout.Name
    lBlkId = oSet(iTmp).OwnerID
    sLay = ThisDrawing.ObjectIdToObject(lBlkId).Layout.Name
    If IsArrayEmpty(vRetVal) Then
    ReDim vRetVal(1)
    Else
    ReDim Preserve vRetVal(UBound(vRetVal) + 2)
    End If
    vRetVal(UBound(vRetVal) - 1) = oSet(iTmp).ObjectID
    vRetVal(UBound(vRetVal)) = sLay
    Next
    GetTbList = vRetVal
    'Debug.Print "vRetVal bounds: " & LBound(vRetVal) & " > " & UBound(vRetVal)
    Set oSet = Nothing
    End Function

    Private Function SelSetInit(sName As String) As AcadSelectionSet
    Dim oRetVal As AcadSelectionSet
    On Error GoTo DeleteAndAdd
    Set oRetVal = ThisDrawing.SelectionSets.Add(sName)
    Set SelSetInit = oRetVal
    Exit Function
    DeleteAndAdd:
    ThisDrawing.SelectionSets.Item(sName).Delete
    Set oRetVal = ThisDrawing.SelectionSets.Add(sName)
    Set SelSetInit = oRetVal
    End Function

    Private Function IsArrayEmpty(varArray As Variant) As Boolean
    ' Determines whether an array contains any elements.
    ' Returns False if it does contain elements, True
    ' if it does not. -Taken from Microsoft web site, TLD 6/6/02
    Dim lngUBound As Long
    On Error Resume Next
    lngUBound = UBound(varArray)
    If Err.Number <> 0 Then
    IsArrayEmpty = True
    Else
    IsArrayEmpty = False
    End If
    End Function

    Private Sub FillBlockNames()
    Dim oBlks As AcadBlocks, oBlk As AcadBlock
    Set oBlks = ThisDrawing.Blocks
    For Each oBlk In oBlks
    If Left(oBlk.Name, 1) <> Chr(42) Then Me.cbxBlkNam.AddItem oBlk.Name
    Next
    Set oBlk = Nothing: Set oBlks = Nothing
    End Sub

    Private Sub FillLayouts(vList As Variant)
    Dim iTmp As Variant
    For iTmp = 0 To UBound(vList) Step 2
    Me.cbxLayNam.AddItem vList(iTmp + 1)
    Next
    End Sub

    Private Function GetAttsForIndex(BlkId As Long) As Variant
    Dim oBlkRef As AcadBlockReference
    Set oBlkRef = ThisDrawing.ObjectIdToObject(BlkId)
    If Not oBlkRef.HasAttributes Then
    MsgBox "Selected block has no attributes!", vbCritical
    Exit Function
    End If
    Dim vAtts As Variant, iTmp As Integer

    vAtts = oBlkRef.GetAttributes
    ReDim vAttLst(1, UBound(vAtts))

    For iTmp = LBound(vAtts) To UBound(vAtts)
    vAttLst(0, iTmp) = vAtts(iTmp).TagString
    vAttLst(1, iTmp) = vAtts(iTmp).TextString
    Next
    Me.lbxAttLst.Column = vAttLst

    Set oBlkRef = Nothing
    End Function

    Private Function GetAttsNoValues(BlkId As Long) As Variant
    Dim oBlkRef As AcadBlockReference
    Set oBlkRef = ThisDrawing.ObjectIdToObject(BlkId)
    If Not oBlkRef.HasAttributes Then
    MsgBox "Selected block has no attributes!", vbCritical
    Exit Function
    End If
    Dim vAtts As Variant, iTmp As Integer

    vAtts = oBlkRef.GetAttributes
    ReDim vAttLst(1, UBound(vAtts))

    For iTmp = LBound(vAtts) To UBound(vAtts)
    vAttLst(0, iTmp) = vAtts(iTmp).TagString
    vAttLst(1, iTmp) = ""
    Next
    Me.lbxAttLst.Column = vAttLst

    Set oBlkRef = Nothing
    End Function

    Private Sub UpdateBlockAtts(BlockId As Long, AttList As Variant)
    Dim eBlkRef As AcadBlockReference, vBlkAtts As Variant, iTmp As Integer
    Set eBlkRef = ThisDrawing.ObjectIdToObject(BlockId)
    vBlkAtts = eBlkRef.GetAttributes
    'Debug.Print UBound(AttList, 1)
    For iTmp = 0 To UBound(AttList, 2)
    'Debug.Print AttList(0, iTmp) & ": " & AttList(1, iTmp) '
    If AttList(1, iTmp) <> "" Then vBlkAtts(iTmp).TextString = AttList(1, iTmp)
    Next
    eBlkRef.Update
    Set eBlkRef = Nothing
    End Sub


    <SNIPPED>
    It seams like a common thing that people would want to do, search a drawing for a block and update its attributes. I am kind of surprised I could not find a good example of this function. I found bits and pieces but never the entire thing. Following is the program that I developed. I would like comments on how to optimize it better.
     
    TomD, Oct 5, 2004
    #2
  3. Michael Barb

    Jeff Mishler Guest

    Michael,
    Although I'm sure your programming skills far exceed mine, I will put in my
    $0.02 just because I still learn from replying to others' posts.

    First, rather than cycling through the entire drawing you should look into
    filtered selection sets. With them Acad does all of the searching for
    whatever you are looking for, and in your case you could create a selecetion
    set of all blocks that have the name "REVDATE". If the SelSet is empty you
    know you need to insert the block, if it's > 0 then it's been inserted more
    than once.

    Also, when cycling through a Collection, there IS a TypeOf function...

    For Each objEntity in Modelspace
    If TypeOf ent Is AcadBlockReference Then
    Set oRevDateBlockReference = objEntity


    Rather than iterating the entire block collection for your block, in a
    drawing with 1000's of blocks this could take some time, I would use
    something like this:

    Private Function Have_Block() As Boolean
    Dim objBlock As AcadBlock

    On Error Resume Next
    Err.Clear
    Set objBlock = ThisDrawing.Blocks.Item("REVDATE")
    If Err.Number <> 0 Then 'it's not in the database
    Have_Block = False
    Err.Clear
    Else
    Have_Block = True
    End If
    On Error GoTo 0

    End Function


    HTH a little,
    Jeff
    check out www.cadvault.com
    ,snip.
    Yours truly, Michael Barb
     
    Jeff Mishler, Oct 5, 2004
    #3
  4. To begin, it was Autodesk's great wisdom that gave you RevDate in LT rather
    than force you to go to the full blown package. Its not in there because it
    is rather simple to do - I'd seek a better reseller! And for what its
    worth, Sham is a Sham!

    Anyhow, your code works but can be streamlined. For starters, you check for
    the blockref first, then if not found check the blocks table. A blockref
    cannot exist without a block entry, so check for the block and then the
    blockref. And NEVER iterate all the blocks or model space searching for
    something. Here's how to approach it:

    'The obvious test is setting an object and testing
    'to see if it succeeded
    Dim oBlk As AcadBlock
    Set oBlk = ThisDrawing.Blocks("REVDATE")
    If Not oBlk Is Nothing Then
    'it did so look for blockrefs
    Else
    'nope so insert it from external source
    End If

    Now when testing for the blockrefs, use a filtered selection set

    Dim oBlkRef As AcadBlockReference
    Dim ssGrabbag As AcadSelectionSet
    Dim iCode(1) As Integer
    Dim vValue(1) As Variant
    iCode(0) = 0
    iCode(1) = 2
    vValue(0) = "INSERT"
    vValue(1) = "REVDATE"
    Set ssGrabbag = ThisDrawing.SelectionSets.Add("RevDate_Search")
    ssGrabbag.Select acSelectionSetAll, _
    filtertype:=iCode, filterdata:=vValue
    'Check to see if items found
    Select Case ssGrabbag.Count
    Case Is = 0 'None found
    Case Is = 1
    'etc....

    For changing the attribute value:

    Dim vAttributes As Variant
    Dim sValue As String
    Dim iIndex As Integer
    vAttributes = oBlkRef.GetAttributes
    For iIndex = LBound(vAttributes) To UBound(vAttributes)
    Select Case vAttributes(iIndex).TagString
    Case "USER"
    vAttribute.TextString = sUname
    Case "REVDATE"
    vAttribute.TextString = sRevDate
    Case "FNAME"
    vAttribute.TextString = sFilename
    End Select
    Next

    Last but not least, CLEAN up after yourself!!!

    If Not myObect Is Nothing Then Set myObject = Nothing

    Any time you want to spend some $, send an email my way =)
    -- Mike
    ___________________________
    Mike Tuersley
    CADalyst's CAD Clinic
    Rand IMAGINiT Technologies
    ___________________________
    the trick is to realize that there is no spoon...
     
    Mike Tuersley, Oct 6, 2004
    #4
  5. Michael Barb

    Michael Barb Guest

    I like the idea of filters it sounds much better. I realize that my method
    of searching is sort of brute force. After seeing yours and others replies
    it looks like there are a lot of better alternatives. I will definitely try
    some out.

    Just because I mastered a very few concepts in the AutoCAD object model does
    not mean I have even begun to learn it.

    Thanks, Michael Barb
     
    Michael Barb, Oct 6, 2004
    #5
  6. Michael Barb

    Michael Barb Guest

    I did the search for a BlockReference fist because I assumed that the vast
    majority of the time (99%) the BlockReference would exist. Searching for
    the block was only to deal with contingencies.

    I considered using a FOR TO loop to search for attributes and found this
    method in several examples. I kind of like the self documenting or
    readability of the FOR EACH IN loop. I am not really sure which is the most
    efficient, probably the FOR TO loop by a small margin.

    Your reply is excellent. I will try your ideas out.

    Any ideas on how make system variable or something like that for user name?

    Thanks, Michael Barb
     
    Michael Barb, Oct 6, 2004
    #6
  7. Michael Barb

    Michael Barb Guest

    This is going to take a while to dig threw but I will take the time.

    Yes, it is a rant but I hope it was readable and entertaining. One point I hope others get is what a valuable resource the archives of the forum is. There is a high probability that others have ask the same question and you often do not have to post it again. Searches for key words work quit well and you will probably find the answer very fast. Personally I find it better than the knowledge base. (more rant).

    Thanks, Michael Barb
     
    Michael Barb, Oct 6, 2004
    #7
  8. Michael Barb

    TomD Guest

    I'd have to agree with you, there. The knowledge base is ok, but for issues such as yours, you really can't beat a good NG.
    This is going to take a while to dig threw but I will take the time.

    Yes, it is a rant but I hope it was readable and entertaining. One point I hope others get is what a valuable resource the archives of the forum is. There is a high probability that others have ask the same question and you often do not have to post it again. Searches for key words work quit well and you will probably find the answer very fast. Personally I find it better than the knowledge base. (more rant).

    Thanks, Michael Barb
     
    TomD, Oct 6, 2004
    #8
  9. Michael Barb

    Ed Jobe Guest

    Would the OS environment variables be more suitable? You could use the vb
    Environ() function.
     
    Ed Jobe, Oct 6, 2004
    #9
  10. Michael Barb

    Michael Barb Guest

    That might be OK but you triggered another idea. What about keeping it in
    the registry. VBA has several methods that let you get, save, create, and
    delete what they call Settings. Settings are numbers or strings saved in
    the registry. I used this several years ago. They are easy to use and it
    worked great. If a setting did not exist when it was needed then it would
    prompt the operator to enter it. After that it always knew. I tied the
    settings to the login name so every time that person logged in it would work
    the way they left it. I had forgotten all about this technique. I wonder
    if I can even find it again.

    Thanks, Michael Barb
     
    Michael Barb, Oct 7, 2004
    #10
  11. What username are you actually looking for? Does LT store USER as the name
    of the person who lasted edited the drawing, or the currently logged in
    user accessing the DWG file?

    If the latter, just use Ed's suggestion of Environ or AutoCAD's
    LOGINNAME...I don't see what adding it to the registry again saves/gains
    you??? If you want a real name vs an abbreviated name, just use a networked
    INI file that will convert the abbrev name to actual - a simple lookup if
    you will.

    If the former, shove the current username [as retrieved above] into a
    dictionary object within the drawing at save time.

    -- Mike
    ___________________________
    Mike Tuersley
    CADalyst's CAD Clinic
    Rand IMAGINiT Technologies
    ___________________________
    the trick is to realize that there is no spoon...
     
    Mike Tuersley, Oct 7, 2004
    #11
  12. Michael Barb

    Michael Barb Guest

    In LT under Tools/Options in the Systems tab there is a field called "User
    Name". There is nothing like this in the full version. Login name is some
    assigned by the IT group for you to access the company network. Most often
    you have no choice and is often cryptic to meaningless. In this company it
    is just the last name. A number is added to the end if there is more than
    one person with that name. Some lucky people get the first name initial
    with the last name. It depends on the IT person that sets up a new
    employee. One company I did some consulting work for used your job title
    and a number. I was Consultant63. The login name is usually not how you
    want to sign or initial your drawings.

    LT is nice and lets you choose the way that RevDate will display your name
    or initials. It is just an ego thing.

    In WindowsXP it is probably saved somewhere in C:\Documents and
    Settings\???????\Application Data\Autodesk\AutoCAD LT 2004\R9\enu\Support
    but I am not sure where. The question marks is usually your login name.
    This way who ever logs into the computer will have their chosen initials.
    INIs are ancient history. I wonder if I could figure out how to make a
    small file here so I would automatically get the right one??

    Yours truly, Michael Barb

    P.S. For the last few years I have been spending about 1/3 of my time in
    Japan, Singapore, ets. I have found that chop sticks or drinking directly
    from the soup bowl work great so I no longer need a spoon.
     
    Michael Barb, Oct 7, 2004
    #12
  13. Michael Barb

    Ed Jobe Guest

    You didn't really say what format you need for the user's name. Something
    user defined?

    ini's are still effective and have their place. If you don't like ini's use
    xml, whatever. The idea is, if the lan login does not suit you, map it to
    some other text and store it. That way, you can use acad's login var or the
    os var to find out who is logged in and then look up their preferred name.
    The os does store the computer owner's name, etc., but if it wasn't set up
    at install or multiple users use that machine, then that's not reliable. You
    come back to making your own system, whether its ini, reg, xml, or just
    plain txt. Personally, I have a database that I use. When the user starts
    acad, I check the login, check the db and see when was the last time the
    user started acad and if they need updates. There's lots of ways to do it.
    Mike just gave you one of the easiest.
     
    Ed Jobe, Oct 7, 2004
    #13
  14. You definitely said it better, Ed.

    Interesting tidbit, Michael. I, too, have spent a lot of time in Singapore.
    Great place to have to spend time!

    -- Mike
    ___________________________
    Mike Tuersley
    CADalyst's CAD Clinic
    Rand IMAGINiT Technologies
    ___________________________
    the trick is to realize that there is no spoon...
     
    Mike Tuersley, Oct 8, 2004
    #14
  15. Hi Michael,
    First I'd just like to say that this is some of the best code that I've seen posted on this newsgroup in a long time; I like your style. I don't have much to add to the advice that you've received, but here's my 0.02 anyway.

    I agree completely with the advice about the TypeOf keyword in lieu of checking the EntityName and the advice about the Item method of the Blocks collection. However, before you go and muddy up your Have_Block_Reference() method with filtered selection set code, please make sure and do some performance tests to see if it's needed.

    Reading through the code everything flowed and fit together until I got down to the Update_Block_Reference_Attributes() method. Some of that code seemed to be asking for refactoring, but considering the small size of the project I probably would have left it right there also. Then after reading further discussions on storing and retrieving the user name it now seems to make sense to do pull out the code that finds the values for the attributes into a new class, or module, or method in the very least.

    If you don't mind sharing, I'd for one would be interested in seeing how it's all turned out. I find all this just as pleasurable as reading a good book :)

    --
    Bobby C. Jones
    Following is my adventure into AutoCAD VBA.

    I work in a company that has 25 AutoCAD LT seats and one networked full version of AutoCAD. We do electrical schematics so LT is fine 99.9% of the time. On rare occasion someone will need the full version and run a macro.

    Personally, I am mostly a programmer with over 30 years experience. At this point I have forgotten more programming languages than I know.

    Our revision tracking system is based on an LT function called RevDate. All LT seats have a save icon with an "R" on it that runs RevDate on saving. Basically what RevDate does is checks the model space for a block called RevDate and updates its attributes when it finds it. The attributes are User Name, Date, File Name. If the block is not found it creates it and updates it. Since it is a block it is always on the drawing, even when we view the drawings online.

    In AutoDesk's greater wisdom they did not implement RevDate on the full version. To my surprise I could not find this function anywhere. I would have paid for it but no one has this function in their utility packages. I could not find it in any free packages, knowledge base, or forum. I ask the question several times in different forums and never found it. I could not believe it could be that hard and negotiate with my local AutoCAD distributor that he would provide the function as part the purchased a full network license.

    His applications engineer due fully sent me an attempt at the function. It was well written and organized but did not work. After about 5 tries and studying his program and searching the knowledge base and forums for answers I came to the conclusion that he did not fully understand the AutoCAD object model. After reading the forums I came to the conclusion that he has a lot of company.

    I did not understand it either so I purchased a couple of books. These have been discussed in this forum before. I got both Joe Sutphin's and Sham Tickoo's books from Amazon. Sham's book tries to cover to many topics in to few pages and I found it of little use. I may use it later when I want to know a little more about Diesel or scripts but for VBA it has to little detail to be useful. Joe's book concentrates on VBA and is more than twice the size of Sham's. In it on bottom of page 348 I found a few paragraphs on the differences between Block definition and Block Reference an it was like a flash of light. I set down and in about four hours I rewrote 90% of the program and got most of it to work. It was shorter than the original one and handle more contingencies.

    I got stuck on the part of how to edit the attributes in the blockrefence that was on the drawing. The original program deleted and then re-added the block. Upon reading it could change the values in the attributes. This seemed like brute force and I felt there had to be a better way. I studied Joe's book for another 4 hours and could not find it. I started looking at the knowledge base and forum articles I had copied. In one of the forum articles dated 12/21/01 from someone called Andy Star was the answer. The second flash of light. The method GetAttributes returned a reference and allowed editing as well as reading. Based on the name of the method this was not intuitive.

    I went back and read Joe's book and I think he even missed this point. I looked at the help file. They mention the possibility in a couple of lines buried deep in an example. I tried it and it works well but in some ways it still does not make total sense. You need to use variants to get it and search it. There is something about its usage that just does not seem right but I cannot figure out what it is.

    One thing that I think is missing form the AutoCAD's Object Model Methods are the "Is" functions to check the data type of an entity. You have to check the object's EntityName against the ObjectARX name. There is no listing of the names and you have to figure them out from the watch window. Most VBA extensions have methods like IsBlockReference and hide these gory details from the user.

    I found out why AutoDesk did not put RevDate in the full version. In LT there is a system variable called User. In the full version this variable does not exist. I used the login name instead. I really do not like it but cannot find a better alternative.


    It seams like a common thing that people would want to do, search a drawing for a block and update its attributes. I am kind of surprised I could not find a good example of this function. I found bits and pieces but never the entire thing. Following is the program that I developed. I would like comments on how to optimize it better.

    I included the program in the text of this post. Attachments disappear in archiving. It also makes looking at is quicker.

    There are other forum posts that helped that I did not acknowledged. As payback, I hope in the future it helps someone else.

    Yours truly, Michael Barb






    Attribute VB_Name = "RevDate"
    ' This module duplicates in the Full version of AutoCAD the function found in AutoCAD LT
    ' One limitation is the variable UserName does not exist in the Full version. To Substitute I used the LoginName.
    'By: Michael Barb 10/2/04


    Public oRevDateBlockReference As AcadBlockReference

    Public Sub RevDate()

    If Have_Block_Reference Then
    Update_Block_Reference_Attributes
    Else
    If Have_Block Then
    Insert_Block_Reference
    Update_Block_Reference_Attributes
    Else
    Insert_Block
    Insert_Block_Reference
    Update_Block_Reference_Attributes
    End If
    End If

    End Sub


    Private Function Have_Block_Reference() As Boolean
    Dim objEntity As AcadEntity

    For Each objEntity In ThisDrawing.ModelSpace

    If objEntity.EntityName = "AcDbBlockReference" Then
    If UCase(objEntity.Name) = "REVDATE" Then
    Set oRevDateBlockReference = objEntity
    Have_Block_Reference = True
    Exit Function
    End If
    End If
    Next objEntity
    Have_Block_Reference = False

    End Function



    Private Function Have_Block() As Boolean
    Dim objBlock As AcadBlock

    For Each objBlock In ThisDrawing.Blocks
    If UCase(objBlock.Name) = "REVDATE" Then
    Have_Block = True
    Exit Function
    End If
    Next objBlock
    Have_Block = False

    End Function



    Private Sub Insert_Block()
    Dim objBlock As AcadBlock
    Dim objAttribute As AcadAttribute

    Dim pntInsertion(2) As Double
    pntInsertion(0) = 0#
    pntInsertion(1) = 0#
    pntInsertion(2) = 0#

    Set objBlock = ThisDrawing.Blocks.Add(pntInsertion, "RevDate")

    Set objAttribute = objBlock.AddAttribute(0.15, acAttributeModeNormal, "", pntInsertion, "USER", "")
    pntInsertion(0) = 1.25
    Set objAttribute = objBlock.AddAttribute(0.15, acAttributeModeNormal, "", pntInsertion, "REVDATE", "")
    pntInsertion(0) = 4.5
    Set objAttribute = objBlock.AddAttribute(0.15, acAttributeModeNormal, "", pntInsertion, "FNAME", "")

    End Sub


    Private Sub Insert_Block_Reference()
    Dim pntUserSetPoint As Variant
    Dim sPrompt As String

    sPrompt = "Enter insertion point for REVDATE block"
    pntUserSetPoint = ThisDrawing.Utility.GetPoint(, sPrompt)

    Set oRevDateBlockReference = ThisDrawing.ModelSpace.InsertBlock(pntUserSetPoint, "REVDATE", 1#, 1#, 1#, 0)

    End Sub



    Private Sub Update_Block_Reference_Attributes()

    Dim sUname As String
    sUname = ThisDrawing.GetVariable("LOGINNAME")

    Dim sRevDate As String
    sRevDate = Date & " " & Format(Time, "Medium Time")

    Dim sFilename As String
    sFilename = ThisDrawing.Name

    Dim objAttribute As Variant 'AcadAttributeReference
    Dim objAttributeList As Variant 'Array of AcadAttributeReference
    ' The type shown above is what these references are but the For search will not work unless they declared as Variant
    objAttributeList = oRevDateBlockReference.GetAttributes

    For Each objAttribute In objAttributeList

    Select Case UCase(objAttribute.TagString)
    Case "USER"
    objAttribute.TextString = sUname
    Case "REVDATE"
    objAttribute.TextString = sRevDate
    Case "FNAME"
    objAttribute.TextString = sFilename
    End Select

    Next objAttribute
    End Sub
     
    Bobby C. Jones, Oct 8, 2004
    #15
  16. Michael Barb

    Michael Barb Guest

    Thanks for the compliment. My code did not always read so well until I had a boss the continually preached:

    "Comments are NOT necessary if you give your variables meaningful names."

    (Today it should probably be objects instead of variables.) He did not mean all comments but about 90% of them. He said it so often that when I think of it I even remember his voice and face. The worst thing about it, and all the times I got beat up, was that he was usually right.

    Yours truly, Michael Barb
     
    Michael Barb, Oct 8, 2004
    #16
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.