Delphi如何使用基本的繪圖函數(shù)繪制統(tǒng)計(jì)圖
一個(gè)windows自帶的畫圖工具是無論如何也不能滿足我們的畫圖需要的,很多效果都需要我們在另外的工具中來實(shí)現(xiàn)。這些高級(jí)的功能是如何實(shí)現(xiàn)的呢,如何操縱一些基本的屬性和函數(shù),讓它們最終能作出我們想要的效果呢?這里我們以繪制統(tǒng)計(jì)圖來說明這些問題。
解決思路――
這里,我們暫且先撇開具體的問題,綜合地一下討論畫圖的問題。
畫圖工具是基本元素的具體實(shí)現(xiàn),對(duì)于我們初學(xué)者來說,還是有很好的參考價(jià)值的,在delphi 5中有一個(gè)自帶的工程例子“……Borland\Delphi5\Demos\Doc\Graphex”,這個(gè)例子可以實(shí)現(xiàn)一些基本的繪圖功能。對(duì)這個(gè)例子多加修改,一定會(huì)有所收獲的。這里就不列出它的詳細(xì)代碼了,有心的讀者可以自己找到這個(gè)例子。我這里只是想綜合地討論這方面的問題。使用DELPHI編寫繪圖軟件的靈魂就在于操作畫布,畫筆和刷子,盡可能地挖掘它們的屬性和相關(guān)參數(shù)的設(shè)置。
(一)畫布
畫布,畫筆和刷子之間的關(guān)系很明了.其實(shí),畫筆和刷子都是畫布的一個(gè)屬性.而畫布也只是TForm,TImage,TShape等組件對(duì)象的一個(gè)屬性,專門負(fù)責(zé)與圖象相關(guān)的信息打交道.它的主要作用可以概括如下幾點(diǎn):
1.指定使用畫筆,刷子和字體的使用類型;
2.繪制和填充指定形狀的線或圖形;
3.修飾和改變圖象;
畫布的主要屬性有:
Brush--指定填充圖形和背景的樣式
CanvasOrientation--指定畫布的定位類型,有coLeftToRight, coRightToLeft兩個(gè)屬性;
ClipRect--指定剪切矩形的邊界;
CopyMode--指定圖形圖象的復(fù)制模式;
Font--指定畫布上使用的字體;
Handle--為畫布指定窗口GDI對(duì)象的設(shè)備描述表;
LockCount--指定畫布被別的線程鎖定的次數(shù);
Pen--指定畫布上使用的畫筆,具體見下面描述;
PenPos--指定畫筆當(dāng)前的位置;
Pixels--指定當(dāng)前剪切矩形的象素顏色;
TextFlags--指定字體在畫布上的顯示方式,有ETO_CLIPPED,ETO_OPAQUE,ETO_RTLREADING, ETO_GLYPH_INDEX,ETO_IGNORELANGUAGE,ETO_NUMERICSLOCALETO_NUMERICSLATIN等值可選;
畫布相關(guān)的API函數(shù)及其注釋如下:
Arc--按指定方式畫一條弧;
BrushCopy--把位圖復(fù)制到指定的畫布的矩形中,用畫布刷子顏色替換位圖的顏色;
Chord--按指定方式畫弦;
CopyRect--從一個(gè)矩形區(qū)域復(fù)制部分圖象到另一個(gè)矩形區(qū)域;
Draw--用指定參數(shù)在指定位置畫圖;
DrawFocusRect--按指定焦點(diǎn)風(fēng)格,通過異或操作來繪制一焦點(diǎn)矩形;
Ellipse--按指定參數(shù)畫一橢圓;
FillRect--按指定的刷子填充一矩形;
FloodFill--使用當(dāng)前選定的刷子填充指定設(shè)備描述表中的一塊區(qū)域;
FrameRect--使用指定的方式畫一矩形的邊框;
LineTo--使用當(dāng)前畫筆從當(dāng)前位置到指定點(diǎn)畫一條直線;
Lock--防止其它線程在畫布上繪圖;
MoveTo--指定一新的當(dāng)前畫筆位置;
Pie--按指定方式畫餅狀圖;
PolyBezier--按指定方式畫多條貝塞爾線;
PolyBezierTo--按指定方式畫多條貝塞爾線并更新當(dāng)前的畫筆位置值;
Polygon--繪制一個(gè)由多個(gè)頂點(diǎn)的任意序列組成 的多邊形;
Polyline--使用當(dāng)前畫筆畫一系列的多邊形;
Rectangle--繪制矩形;
RoundRect--繪制圓角矩形;
StretchDraw--在指定的矩形區(qū)域通過指定的繪圖參數(shù)來繪制圖形;
TextExtent--返回使用當(dāng)前字體設(shè)置的字符的象素寬度和高度等參數(shù);
TextHeight--返回使用當(dāng)前字體設(shè)置的字符的象素高度;
TextOut--在指定位置繪制文本,并更新畫筆的當(dāng)前位置;
TextRect--在一剪切矩形區(qū)域中繪制文本;
TextWidth--返回使用當(dāng)前字體設(shè)置的字符的象素寬度;
TryLock--對(duì)當(dāng)前沒加鎖的畫布進(jìn)行加鎖;
Unlock--對(duì)當(dāng)前加鎖的畫布進(jìn)行解鎖;
例如以下是兩個(gè)小例子:
procedure TForm1.Button2Click(Sender: TObject);
var
ARect: TRect;
begin //實(shí)現(xiàn)了剪切效果;
with Image1.Canvas do
begin
CopyMode := cmWhiteness; //設(shè)置復(fù)制模式;
ARect := Rect(0, 0, Image1.Width, Image1.Height);
CopyRect(ARect, Image1.Canvas, ARect);
CopyMode := cmSrcCopy; //恢復(fù)復(fù)制模式;
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
var
W: Word;
begin //在窗口中畫一條彩線;
for W := 10 to 200 do
Canvas.Pixels[W, 10] :=RGB(random(255),random(255),random(255));;
end;
靈活使用這些函數(shù)及其內(nèi)部參數(shù)會(huì)讓我們得到意想不到的效果;
(二) 畫筆
畫筆是一個(gè)GDI對(duì)象,定義了繪制直線或輪廓形狀的方法.
畫筆內(nèi)部共有五種屬性:顏色,句柄,模式,風(fēng)格和寬度.
Color--決定指定直線或輪廓形狀的RGB顏色。
Handle--指向了窗口畫筆對(duì)象句柄。
Mode--指定了畫筆以何種方式在畫布(canvas)上畫線,在幫助文檔中的該定義是(全部以pm_開頭):
type TPenMode =( pmBlack, //總是黑色;
pmWhite, //總是白色;
pmNop, //顏色不變;
pmNot, //畫布顏色取反;
pmCopy, //顏色屬性中指定的畫筆顏色;
pmNotCopy, //畫筆顏色取反;
pmMergePenNot, //畫筆顏色和畫布背景色取反后顏色的結(jié)合;
pmMaskPenNot, //畫筆顏色和畫筆背景色取反后顏色共同色的結(jié)合;
pmMergeNotPen, //畫筆顏色取反后和畫布背景色的結(jié)合;
pmMaskNotPen, //畫布顏色和畫筆顏色取反后顏色共同色的結(jié)合;
pmMerge, //畫筆和畫布背景色的結(jié)合;
pmNotMerge, //畫筆顏色和畫布背景色的結(jié)合;
pmMask, //畫筆和畫布背景色共同色的結(jié)合;
pmNotMask, //pmMask取反,畫筆和畫布背景色共同色的結(jié)合;
pmXor, //取畫筆或畫布背景中的任一種顏色;
pmNotXor //pmXor取反,取畫筆或畫布背景中的任一種顏色;
);
Style--則指定了畫筆操作的風(fēng)格,在線文檔中的定義是(全部以ps_開頭):
type TPenStyle=( psSolid, //畫筆是───
psDash, //畫筆是------
psDot, //畫筆是......
psDashDot, //畫筆是_._._.
psDashDotDot, //畫筆是_.._..
psClear, //畫筆是透明色
psInsideFrame //畫筆是實(shí)線,但設(shè)置大于1時(shí)會(huì)抖動(dòng);
);
另外,在windows.pas中還有其他擴(kuò)展的畫筆風(fēng)格定義,只在特殊的支持設(shè)備上
才有效,如PS_ENDCAP_ROUND, PS_JOIN_ROUND等;
Width--指定了待使用畫筆的寬度,單位是象素.
和畫筆相關(guān)的函數(shù)有:
CreatePen--用指定風(fēng)格創(chuàng)建畫筆;
CreatePenIndirect--根據(jù)LOGPEN數(shù)據(jù)結(jié)構(gòu)創(chuàng)建一畫筆;
ExtCreatePen-- 創(chuàng)建帶指定風(fēng)格,寬度和刷子屬性的幾何畫筆;
(三)刷子
刷子定義了區(qū)域填充的GDI對(duì)象,刷子是一個(gè)8×8象素的區(qū)域,它可以被繪制在指定的設(shè)
備上.刷子不僅可以是純色的,也可以由不同的位圖圖案組成.
刷子的屬性有位圖,顏色,句柄和風(fēng)格四種:
Bitmap--是指定一個(gè)外部位圖文件來填充指定的區(qū)域.如果指定的圖象比填充的區(qū)域大,
則只有左上角與填充區(qū)域等大的部分有效,其余的被自動(dòng)裁減了.
Color--指定了刷子的顏色.當(dāng)刷子風(fēng)格為bsClear時(shí),該屬性無效.
Handle--指向指定設(shè)備窗口.
Style--則指定了當(dāng)前刷子的填充風(fēng)格,在線文檔中的定義是(都以bs_開頭):
type TBrushStyle=( bsSolid, //填充格式為實(shí)體填充
bsClear, //填充格式為透明填充
bsHorizontal, //填充格式為------
bsVertical, // 填充格式為|||||
bsFDiagonal, // 填充格式為/////
bsBDiagonal, // 填充格式為\\\\\
bsCross, // 填充格式為+++++
bsDiagCross // 填充格式為xxxxx
);
和刷子有關(guān)的API函數(shù)有:
CreateBrushIndirect--根據(jù)LOGBRUSH創(chuàng)建一刷子;
CreateDIBPatternBrushPt--使用設(shè)備無關(guān)位圖來創(chuàng)建刷子,以便指定刷子的模式;
CreateHatchBrush--創(chuàng)建一帶有陰影模式的刷子,陰影模式為以HS_開頭的常數(shù);
CreatePatternBrush--用位圖來創(chuàng)建刷子,以便指定刷子的模式;
CreateSolidBrush--創(chuàng)建一實(shí)體顏色刷子;
GetBrushOrgEx--獲取指定設(shè)備描述表中當(dāng)前選擇刷子的原點(diǎn);
GetSysColorBrush--獲取和指定顏色索引相關(guān)的邏輯刷子的句柄;
SetBrushOrgEx--設(shè)置指定設(shè)備描述表中當(dāng)前選擇刷子的原點(diǎn);
(四)畫圖和填充相關(guān)的API函數(shù);
BeginPaint--準(zhǔn)備在指定窗口繪畫或?qū)χ付▍^(qū)域進(jìn)行填充;
DrawAnimatedRects--NT支持函數(shù),畫一環(huán)有游動(dòng)邊框的矩形;
DrawCaption--NT支持函數(shù),為指定窗口的標(biāo)題賦值;
DrawEdge--為指定矩形畫一道或多道邊框;
DrawFocusRect--畫焦點(diǎn)矩形;
DrawFrameControl--畫一指定類型和風(fēng)格的邊框控件;
DrawState--NT支持函數(shù),為圖象畫一可視效果標(biāo)明其狀態(tài);
DrawStateProc--NT支持函數(shù),調(diào)用為圖象畫一可視效果標(biāo)明其狀態(tài)的函數(shù);
DrawTextEx--NT支持函數(shù),在指定區(qū)域輸出格式化文本;
EndPaint--結(jié)束繪畫;
ExcludeUpdateRgn--將窗口無效部分(更新區(qū)域)從裁剪區(qū)中排除掉;
GdiFlush--使當(dāng)前GDI閃爍;
GdiGetBatchLimit--獲取緩沖GDI函數(shù)數(shù)量;
GdiSetBatchLimit--設(shè)置緩沖GDI函數(shù)數(shù)量;
GetBkColor--獲取背景顏色;
GetBkMode--獲取背景模式;
GetBoundsRect--獲取邊界矩形;
GetROP2--獲取當(dāng)前繪圖模式;
GetUpdateRect--獲取指定窗口最小的矩形;
GetUpdateRgn--獲取描述窗口中無效區(qū)的區(qū)域;
GetWindowDC--獲取窗口DC;
GetWindowRgn--獲取窗口區(qū)域;
GrayString--在指定位置畫灰色文本;
InvalidateRect--使DC指定的矩形無效;
InvalidateRgn--使DC指定的矩形無效;
LockWindowUpdate--禁止或允許在指定窗口中繪畫;
OutputProc--調(diào)用輸出進(jìn)程,向GrayString輸送文本;
PaintDesktop--NT支持函數(shù),在指定的窗口區(qū)域用指定的桌面顏色或墻紙?zhí)畛洳眉魠^(qū);
RedrawWindow--更新客戶區(qū)的指定區(qū)域或矩形;
SetBkColor--設(shè)置背景顏色;
SetBkMode--設(shè)置背景模式;
SetBoundsRect--設(shè)置邊界矩形;
SetRectRgn--設(shè)置矩形區(qū)域;
SetROP2--設(shè)置當(dāng)前繪圖模式;
SetWindowRgn--設(shè)置窗口區(qū)域;
UpdateWindow--更新窗口;
ValidateRect--使客戶區(qū)中指定矩形有效;
ValidateRgn--使客戶區(qū)中的指定區(qū)域有效;
WindowFromDC--獲取和指定窗口相關(guān)的句柄;
具體實(shí)現(xiàn)――
1.本例以常見的統(tǒng)計(jì)圖來說明問題。該例能實(shí)現(xiàn)對(duì)統(tǒng)計(jì)圖的動(dòng)態(tài)繪制,并且可以自定義設(shè)置統(tǒng)計(jì)圖的形狀和顏色。在說明問題之前,來了解程序用到的一些比較復(fù)雜的函數(shù)或算法:
函數(shù)――
1.Polygon(Points: array of TPoint)
用于繪出指定的多邊形。括號(hào)內(nèi)是預(yù)定點(diǎn)的集合,該集合可以在使用之前定義,也可以在使用時(shí)同時(shí)定義,本例屬于后者;
2.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Longint)
用于繪制餅狀圖,餅狀圖其實(shí)就是橢圓的一部分。在這些參數(shù)中,其中(X1, Y1)和(X2, Y2)定義了框住餅狀圖的矩形,而從橢圓中心發(fā)出的射線經(jīng)過(X3, Y3)和(X4, Y4)兩點(diǎn),就把一個(gè)餅狀圖截出來了。
3.FormatFloat(const Format: string; Value: Extended)
函數(shù)的意義是按指定方式格式化字符串,F(xiàn)ormat指定了格式化的方式,Value則指定了要格式化的文本或其他數(shù)據(jù)。下面列舉了一些范例,可供我們學(xué)習(xí)時(shí)參考:
格式化符號(hào)(Format) 1234 -1234 0.5 0
1234 -1234 0.5 0
0 1234 -1234 1 0
0.00 1234.00 -1234.00 0.50 0.00
#.## 1234 -1234 .5
#,##0.00 1,234.00 -1,234.00 0.50 0.00
#,##0.00;(#,##0.00) 1,234.00 (1,234.00) 0.50 0.00
#,##0.00;;Zero 1,234.00 -1,234.00 0.50 Zero
0.000E+00 1.234E+03 -1.234E+03 5.000E-01 0.000E+00
#.###E-0 1.234E3 -1.234E3 5E-1 0E0
該例是在小數(shù)點(diǎn)后保留兩位小數(shù),因此用"##.##",具體見程序代碼中。
算法――
本例的實(shí)現(xiàn)依賴一定的算法。這里介紹主要的兩點(diǎn):
1)在連接多邊形各點(diǎn)時(shí),我們要注意那幾個(gè)點(diǎn)一定要構(gòu)成一個(gè)閉合的圖形,這就要保證最后一個(gè)點(diǎn)要和第一個(gè)點(diǎn)重合。至于其他的點(diǎn)怎么布局,則要有一定的空間感。
我們先畫一個(gè)矩形,然后再根據(jù)平行關(guān)系確定其他的點(diǎn):
rectangle(50,x,70,220); //畫主視面;
Canvas.Polygon([Point(50, x), Point(70,x-10),Point(90,x-10), Point(70, x),Point(50, x)]); //畫頂面;
Canvas.Polygon([Point(90,x-10), Point(70, x),Point(70,220),Point(90,210),Point(90,x-10)]); //畫側(cè)面;
2)確定圓上指定角度的邊與圓的交點(diǎn)
在該例中畫餅狀圖時(shí),我們要按照一定的比例畫扇形,這就要確定扇形的起始點(diǎn)和終止點(diǎn)。我們把起始點(diǎn)設(shè)為一個(gè)定點(diǎn),而終止點(diǎn)則根據(jù)實(shí)際情況設(shè)置,如圖:
――餅狀圖的數(shù)學(xué)原理――
假設(shè)畫圖時(shí)已知一個(gè)比例數(shù)是K,則在餅狀圖中的角度是θ=(K*360),根據(jù)圖中的關(guān)系(Y軸向下符合屏幕坐標(biāo)系定義),可以用三角函數(shù)知識(shí)求得PX,PY:
PX=op*cosθ
PY=op*sinθ
上述式子的前提條件是O點(diǎn)是原點(diǎn),OP是圓的半徑。如果O點(diǎn)不是原點(diǎn),而是坐標(biāo)系中的一個(gè)點(diǎn)(X0,Y0),則此時(shí)的P點(diǎn)坐標(biāo)是
PX=X0+op*cosθ
PY=Y0+op*sinθ
3.本例的界面布局可以參考程序運(yùn)行的結(jié)果圖,其中代表“刷子類型”的combobox1的items的屬性設(shè)置如圖。該例實(shí)現(xiàn)的主要代碼如下:
procedure TForm1.Button1Click(Sender: TObject);
var
x,i,j:integer;
k:real;
begin
refresh;
//標(biāo)明寫上“Y”軸;
label4.left:=25;
label4.top:=2;
label4.caption:='Y';
label4.Transparent:=true;
//標(biāo)明寫上“X”軸;
label5.left:=395;
label5.top:=227;
label5.caption:='X';
label5.Transparent:=true;
x:=220-round(strtofloat(edit1.text)/strtofloat(edit2.text)*200);
with form1.Canvas do
begin
pen.width:=strtoint(edit3.text); //設(shè)置畫筆寬度;
case combobox1.Items.IndexOf(combobox1.text) of //設(shè)置刷子的填充風(fēng)格;
0: brush.style:=bsSolid;
1: brush.style:=bsClear;
2: brush.style:=bsHorizontal;
3: brush.style:=bsVertical;
4: brush.style:=bsFDiagonal;
5: brush.style:=bsBDiagonal;
6: brush.style:=bsCross;
7: brush.style:=bsDiagCross;
end;
//畫出X軸;
MoveTo(2,220);
LineTo(400,220);
//畫出Y軸;
MoveTo(20,5);
LineTo(20,230);
//畫出Y軸的箭頭方向"∧";
moveto(20,5);
lineto(15,12);
moveto(20,5);
lineto(25,12);
//畫出X軸的箭頭方向"∧";
moveto(400,220);
lineto(395,213);
moveto(400,220);
lineto(395,227);
if checkbox1.Checked then //繪制立體的直方柱圖;
begin
//畫正面的矩形圖;,可以根據(jù)實(shí)際情況動(dòng)態(tài)定義它的高度;
rectangle(50,x,70,220);
//畫頂面,隨著正面矩形的高度變化而變化;
Canvas.Polygon([Point(50, x), Point(70,x-10),Point(90,x-10), Point(70, x),Point(50, x)]);
//畫側(cè)面,隨著正面矩形的高度變化而變化;
Canvas.Polygon([Point(90,x-10), Point(70, x),Point(70,220),Point(90,210),Point(90,x-10)]);
end
else
rectangle(50,x,70,220); //如果沒有選中要以立體形式繪制,則以平面形式繪制的直方柱圖;
//畫餅狀統(tǒng)計(jì)圖
k:=(strtofloat(edit1.text)/strtofloat(edit2.text))*360; //將數(shù)據(jù)按比例轉(zhuǎn)換成;
i:=round(250+100*cos(k*3.14159/180));
j:=round(120+100*sin(k*3.14159/180));
pie(150,20,350,220,i,j,350,120);
label3.caption:='比例是'+formatfloat('##.##',(k/360)*100)+'%'; //設(shè)置比例的函數(shù);
end;
end;
procedure TForm1.Shape1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if colordialog1.Execute then //設(shè)置窗口背景顏色;
shape1.Brush.color:=colordialog1.Color;
form1.color:=ColorDialog1.Color;
end;
procedure TForm1.Shape2MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if colordialog2.Execute then //設(shè)置刷子顏色;
shape2.Brush.color:=colordialog2.Color;
form1.Canvas.Brush.color:=colordialog2.Color;
end;
procedure TForm1.Shape3MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if colordialog3.Execute then //設(shè)置畫筆顏色;
shape3.Brush.color:=colordialog3.Color;
form1.Canvas.Pen.color:=colordialog3.Color;
end;
聯(lián)系客服