□文 /張海濤
VBA(Visual Basic for Application)是一種面向?qū)ο蟮木幊陶Z(yǔ)言,它的基本語(yǔ)法完全繼承于VB,所以具有語(yǔ)法簡(jiǎn)單易懂、編程邏輯清晰的特點(diǎn),非常適合程序的快速開(kāi)發(fā)。VBA的強(qiáng)大優(yōu)勢(shì)在于將VB的編程環(huán)境與被開(kāi)發(fā)程序同時(shí)運(yùn)行,利用面向?qū)ο缶幊谭椒ǎ∣OP),在接口的庫(kù)中將軟件中的重要部分對(duì)象化、參數(shù)化,在VBA中直接調(diào)用軟件中豐富的對(duì)象進(jìn)行操作以實(shí)現(xiàn)編程目的。因此,VBA作為一種通用型語(yǔ)言,能廣泛應(yīng)用于Windows下的Office系列軟件、AutoCAD等專(zhuān)業(yè)商業(yè)軟件的二次開(kāi)發(fā)、Windows腳本編程(VBS)等。
AutoCAD是目前應(yīng)用最為廣泛的交互式計(jì)算機(jī)輔助繪圖與設(shè)計(jì)軟件,其優(yōu)勢(shì)在于通用性、多工業(yè)標(biāo)準(zhǔn)和開(kāi)放的體系結(jié)構(gòu),廣泛應(yīng)用于土木建筑、裝飾設(shè)計(jì)、機(jī)械工程等領(lǐng)域。雖然AutoCAD功能強(qiáng)大,但在解決一些專(zhuān)業(yè)問(wèn)題,尤其在需要結(jié)合設(shè)計(jì)計(jì)算、數(shù)據(jù)處理、復(fù)雜圖形繪制時(shí)就會(huì)顯得力不從心,這時(shí)候在程序基礎(chǔ)上對(duì)AutoCAD進(jìn)行二次開(kāi)發(fā)成為解決上述問(wèn)題的有效補(bǔ)充手段[1]。AutoCAD常見(jiàn)的二次開(kāi)發(fā)工具包括Auto LISP/Visual LISP、基于ActiveX Automation技術(shù)的VB/VBA、采用C++語(yǔ)言的Object ARX技術(shù)、基于微軟.Net技術(shù)的VB.Net和C#語(yǔ)言開(kāi)發(fā)。這幾種語(yǔ)言各有優(yōu)劣,具體特點(diǎn)及使用方法可參考相關(guān)介紹[2]。相較而言,VBA具有編程簡(jiǎn)單、快速開(kāi)發(fā)、運(yùn)行效率高、功能強(qiáng)大等特點(diǎn)使其擁有不同于其他二次開(kāi)發(fā)工具的特殊優(yōu)勢(shì),也使其在各專(zhuān)業(yè)工程人員中的推廣和繼承具有了生命力。
基于VBA的這些優(yōu)勢(shì),二次開(kāi)發(fā)技術(shù)在橋梁工程中得以廣泛應(yīng)用[3]。本文對(duì)AutoCAD和Excel進(jìn)行聯(lián)合開(kāi)發(fā),利用Excel獲取CAD圖形數(shù)據(jù)生成橋梁工程專(zhuān)業(yè)計(jì)算軟件橋博的腳本,輔助橋博軟件的建模,提高了工作效率。在此基礎(chǔ)上本文著重介紹了包括VBA理論模塊、程序設(shè)計(jì)方法和關(guān)鍵技術(shù),為相關(guān)工程的二次開(kāi)發(fā)提供參考。
首先,二次開(kāi)發(fā)的操作是基于對(duì)AutoCAD提供對(duì)象(Object)的操作,這些對(duì)象的種類(lèi)非常豐富并且包含各種屬性(Properties)和方法(Methods)。對(duì)象本質(zhì)上是一種特殊的變量,可以進(jìn)行定義、賦值、釋放。AutoCAD中的對(duì)象按性質(zhì)可以主要?jiǎng)澐譃橐韵聨追N[4]。
1)圖形對(duì)象,如直線(xiàn)(Line)、文本(Text)、標(biāo)注(Dimensions)等。
2)樣式設(shè)置對(duì)象,如線(xiàn)型(Linetype)、文字樣式(TextStyle)。
3)組織結(jié)構(gòu),如圖層(Layers)、塊(Blocks)。
4)圖形顯示對(duì)象,如視圖(View)和視口(Viewport)。
5)AutoCAD 應(yīng) 用 程 序 (Application) 和 文 檔(Document)。
這些對(duì)象基本涉及到了AutoCAD的各方面并可以在VBA的對(duì)象瀏覽器中查詢(xún)。各對(duì)象之間并不是隨意排列,大多數(shù)都存在父對(duì)象和子對(duì)象的樹(shù)形衍生關(guān)系,常用對(duì)象見(jiàn)圖1。
圖1 AutoCAD對(duì)象
如圖1所示,AutoCAD中的圖形對(duì)象基本都囊括在ModelSpace中,比如常用的各類(lèi)線(xiàn)、文字等,用Add方法可以向AutoCAD的Document對(duì)象中添加各種圖元,完成圖形的繪制,而另外一個(gè)重要的對(duì)象Utility則集合了一系列的內(nèi)部實(shí)用工具,比如獲取兩點(diǎn)間距(GetDistance)、獲取對(duì)象(GetEntity)、獲取坐標(biāo)(GetPoint)等方法函數(shù),用于獲取圖形中的各類(lèi)信息。這兩個(gè)基本類(lèi)可以完成對(duì)AutoCAD的大部分常用操作。關(guān)于其余對(duì)象的定義、屬性、方法及示例都可以在AutoCAD提供的官方幫助文件里查詢(xún)。
使用VBA開(kāi)發(fā)軟件可以分為內(nèi)部調(diào)用和外部調(diào)用兩種方式。AutoCAD內(nèi)置了VB的開(kāi)發(fā)環(huán)境VBE(高版本需要安裝VBA開(kāi)發(fā)包),可以在VBE中直接開(kāi)發(fā)VBA,這樣能夠在程序內(nèi)部直接運(yùn)行程序并查看在窗口中的運(yùn)行結(jié)果,這是VBA相較其他開(kāi)發(fā)語(yǔ)言的優(yōu)勢(shì)之一。缺點(diǎn)是內(nèi)置的VBA沒(méi)有辦法進(jìn)行封裝,只能將代碼部分通過(guò)加密的方式進(jìn)行簡(jiǎn)單保護(hù)。
由于在AutoCAD中無(wú)法通過(guò)內(nèi)部命令的方式直接運(yùn)行內(nèi)部VBA,所以通常會(huì)用LISP給VBA編寫(xiě)一個(gè)調(diào)用命令,可采用如下形式:
(defun c:CM()
(command“_vbarun”“cm_vba”)
)
這樣,在加載這個(gè)lsp文件和dvb格式的源文件后,就可以通過(guò)在命令行中直接輸入“CM”來(lái)內(nèi)部調(diào)用名稱(chēng)為“cm_vba”的VBA程序。
在另外一些情況下,如果將部分代碼封裝為動(dòng)態(tài)鏈接庫(kù)后外部引用或者從其他程序的VBE、VB6.0和Visual Studio等開(kāi)發(fā)工具進(jìn)行外部控制時(shí),則需要進(jìn)行外部調(diào)用,首先要在窗口的“工具”-“引用”中調(diào)用基本庫(kù)[5],然后在代碼部分聲明引用對(duì)象,獲得軟件的控制權(quán)限。
例如,當(dāng)采用外部創(chuàng)建CAD文件時(shí),可采用以下代碼,依次獲得對(duì)軟件和新建文檔的控制權(quán):
DimAcadApp as AcadApplication
DimAcadDoc as AcadDocument
Set AcadApp
=CreateObject(“AutoCAD.Application”)
AcadApp.Visible=True
Set AcadDoc=AcadApp.ActiveDocument
外部調(diào)用的缺點(diǎn)是沒(méi)有辦法直接在AutoCAD中直接運(yùn)行程序查看結(jié)果且需要考慮程序運(yùn)行過(guò)程中輸入、輸出的接口問(wèn)題。但是外部調(diào)用為AutoCAD與其他程序的交互提供了實(shí)施方法。
AutoCAD的優(yōu)勢(shì)在于對(duì)圖形的處理,當(dāng)涉及到其他方面的工作時(shí),單一依賴(lài)AutoCAD或者完全靠VB本身的功能與函數(shù)去構(gòu)建復(fù)雜的程序會(huì)拖累整體的運(yùn)行效率。所以,與其他專(zhuān)業(yè)軟件進(jìn)行交互,是VBA的一項(xiàng)重要功能,見(jiàn)圖2。
圖2 VBA交互體系
Excel是微軟公司開(kāi)發(fā)的專(zhuān)業(yè)辦公軟件,所以O(shè)ffice已經(jīng)內(nèi)置了VBA的開(kāi)發(fā)環(huán)境[6]。Excel協(xié)助AutoCAD能夠完成數(shù)據(jù)提取、整理、運(yùn)算、輸出等一系列工作,結(jié)合Excel和AutoCAD可以避免大量使用VB窗口及控件,從而節(jié)省復(fù)雜的窗口設(shè)計(jì)工作。關(guān)于Excel的對(duì)象結(jié)構(gòu)此處不多做介紹,具體參閱相關(guān)教材與手冊(cè)。
在設(shè)計(jì)程序時(shí),需要選擇VBA基于的軟件主體:如果程序的主要功能是運(yùn)算和數(shù)據(jù)處理,需要從Au-toCAD定向獲取、輸出圖形信息,可以選擇從Excel控制AutoCAD,借用Excel的內(nèi)置功能對(duì)數(shù)據(jù)信息進(jìn)行處理;如果程序的主要任務(wù)是繪圖,只是借助Excel的末端功能完成簡(jiǎn)單數(shù)據(jù)匯總、數(shù)據(jù)傳遞功能等,可以選擇從AutoCAD控制Excel;如果希望編譯獨(dú)立可執(zhí)行程序,可以直接用VB6.0、Visual Studio等開(kāi)發(fā)工具調(diào)用其他VBA兼容軟件。
因?yàn)閂BA語(yǔ)言本身沒(méi)有劃分模塊的語(yǔ)法結(jié)構(gòu),所以在程序設(shè)計(jì)初期就應(yīng)當(dāng)考慮把程序定義成不同模塊,以方便后期對(duì)程序進(jìn)行定向維護(hù)和擴(kuò)展,有以下幾個(gè)基本原則可參考。
1)劃分程序的主次,將具體執(zhí)行各部分功能的操作歸納于不同的子程序中,而主程序用于控制所有子程序的運(yùn)算流程,這里將最終腳本輸出函數(shù)Genegrate_Script()作為主程序,而子程序負(fù)責(zé)各個(gè)部分腳本信息的統(tǒng)計(jì)和生成,見(jiàn)圖3。
圖3 程序模塊
2)將自編的一些通用型功能函數(shù)歸納成各類(lèi)函數(shù)模塊,方便在不同的子程序中直接使用,也方便向其他程序進(jìn)行移植,比如排序函數(shù)、插值函數(shù)、向量計(jì)算函數(shù)等。
Sub Array_Shrink(ByRefarr As Variant)
'清除多段線(xiàn)中重復(fù)節(jié)點(diǎn)
......
For i=2 Tonum-1 Step 2
tmp_array(m)=arr(i)
tmp_array(m+1)=arr(i+1)
Ifarr(i)=arr(i-2)And arr(i+1)=arr(i-1)Then
ReDimPreserve tmp_array(num-2)
Else
m=m+2
End If
Next i
arr=tmp_array
End Sub
3)VB不具備C++/C#等語(yǔ)言方便定義類(lèi)的功能,但是依然可以利用Type語(yǔ)句在“模塊”中自定義數(shù)據(jù)類(lèi)型,可以將一些變量盡量對(duì)象化置于模塊中,以方便對(duì)同對(duì)象變量進(jìn)行識(shí)別,比如此處將箱梁截面定義成對(duì)象,包含截面的序號(hào)、坐標(biāo)、插入點(diǎn)屬性。
Public Type bdSection '定義箱梁截面
indexAs Integer '截面序號(hào)
Pt_List As Variant'截面組成坐標(biāo)
InsertPt As Variant'截面插入點(diǎn)坐標(biāo)
End Type
以Excel為主體,通過(guò)VBA外部控制AutoCAD獲取模型信息,自動(dòng)生成橋博的腳本文件。主程序主要負(fù)責(zé)匯總各部分信息并輸出為橋博腳本dbs類(lèi)型文本。型式參照以下總體信息部分代碼,引用的是init_ZTXX()函數(shù)
Print#1,"http://總體信息*************"
Print#1,"GEN.PrjTitle=""橋梁博士腳本"";"
Print#1,"GEN.CalType="&init_ZTXX.CalType&";" '函數(shù)獲取總體信息
Print#1,"GEN.BPrestress="&init_ZTXX.bPrestress&";"
Print#1,"GEN.SelfHumi=0.8;
Print#1,"GEN.Code="&init_ZTXX.Code&";"
Print#1,"GEN.cU="&init_ZTXX.cU&";"
從Excel內(nèi)部控制AutoCAD需要識(shí)別已打開(kāi)的CAD文件,使用的是GetObject()函數(shù),型式如下
DimAcadApp as AcadApplication
DimAcadObj As AcadDocument
Set AcadApp=GetObject(,"AutoCAD.Application")
Set AcadObj=AcadApp.ActiveDocument
進(jìn)入AutoCAD之后,由于模型空間的圖形類(lèi)型豐富,需要對(duì)選定對(duì)象類(lèi)別進(jìn)行限制,可以用FilterType、FilterData兩個(gè)數(shù)組限定選擇對(duì)象為多段線(xiàn)或者直線(xiàn)等圖元類(lèi)型
DimFilterType(0)As Integer
DimFilterData(0)As Variant
FilterType(0)=0
FilterData(0)="LWPOLYLINE"
Plset.SelectOnScreen FilterType,FilterData
在AutoCAD中通過(guò)鼠標(biāo)點(diǎn)擊獲取坐標(biāo)參考原點(diǎn),使用Utility下的Getpoint()函數(shù),但需要注意由于是外部控制程序,涉及對(duì)象語(yǔ)句必須前置主體對(duì)象A-cadObj,也就是CAD文件本身:
base_pt=AcadObj.Utility.GetPoint(,"選擇參考基點(diǎn):")
這里程序會(huì)返回一個(gè)三維雙精度數(shù)組,表示點(diǎn)在三個(gè)方向的坐標(biāo)值。
通過(guò)選擇獲得多段線(xiàn)對(duì)象后,即可以通過(guò)其Coordinates屬性得到多段線(xiàn)上全部的節(jié)點(diǎn)坐標(biāo),返回值為一個(gè)數(shù)組,然后再返回給Excel
Num=Plset.count
For i=0 Tonum-1
Pl_cdn=plObj(i).Coordinates
Call Array_Shrink(Pl_cdn)
'自定義函數(shù)刪除多段線(xiàn)上重復(fù)節(jié)點(diǎn)
Pl_num= (UBound(Pl_cdn)+1)/2
For j=0 ToPl_num-1
Cells(j+12,4*i+1).Value=j+1
Cells(j+12,4*i+2).Value=Round((Pl_cdn(2*j)-base_pt(0))/1000,3)
Cells(j+12,4*i+3).Value=Round((Pl_cdn(2*j+1)-base_pt(1))/1000,3)
Cells(j+12,4*i+4).Value=0
Next j
Next i
從AutoCAD中得到的圖形信息本質(zhì)就是節(jié)點(diǎn)坐標(biāo),利用Excel的數(shù)據(jù)統(tǒng)計(jì)功能可以方便的對(duì)這些坐標(biāo)做自動(dòng)化處理
DimSec_numAs Integer'截面類(lèi)型數(shù)量
DimSections()As bdSection'截面類(lèi)型
Sec_num=WorksheetFunction.CountA (Range("B4:B100"))
ReDimSections(1 ToSec_num)
這樣就定義了一個(gè)截面對(duì)象數(shù)組,包含全部類(lèi)型截面的坐標(biāo)信息。
VBA編程主要具有以下幾個(gè)優(yōu)點(diǎn):
1)語(yǔ)法簡(jiǎn)單清晰、邏輯性強(qiáng),能夠協(xié)助工程人員快速掌握并開(kāi)發(fā)程序,不需要花費(fèi)大量時(shí)間去學(xué)習(xí)復(fù)雜的語(yǔ)法和數(shù)據(jù)結(jié)構(gòu)原理方面的知識(shí);
2)VBA具有通用性,可以用于二次開(kāi)發(fā)Auto-CAD、Office等一系列軟件,更可以用來(lái)聯(lián)合不同功能的軟件進(jìn)行組合編程,實(shí)現(xiàn)功能上的互補(bǔ);
3)利用二次開(kāi)發(fā)技術(shù),在面對(duì)需要批量化、重復(fù)性、數(shù)據(jù)處理類(lèi)的工作時(shí),能夠明顯提高完成速度。
當(dāng)然,VBA的一些缺點(diǎn)限制了它的應(yīng)用和發(fā)展,比如大部分使用者由于是非編程專(zhuān)業(yè)領(lǐng)域出身的工程人員,加之VB語(yǔ)法本身不夠緊湊,很容易寫(xiě)出龐大繁冗的程序。VBA的類(lèi)定義功能不全,它的優(yōu)勢(shì)在于對(duì)接口提供的類(lèi)庫(kù)進(jìn)行直接應(yīng)用而并不是深化創(chuàng)造,使得它在二次開(kāi)發(fā)領(lǐng)域的應(yīng)用擴(kuò)展性比較受限。
但總而言之,對(duì)于工程領(lǐng)域的從業(yè)者來(lái)說(shuō),VBA實(shí)現(xiàn)的部分功能是一種極為高效簡(jiǎn)便的解決方案,本文簡(jiǎn)單介紹了其在橋博建模中的一種應(yīng)用方法,可為在其他更廣泛領(lǐng)域內(nèi)的使用提供參考。