10.2 圖 形 設 計
Windows是一個基於圖形用戶界面的操作系統。
若要在窗口上作圖,需要調用Windows 提供的應用程序接口(Application Program Interface,API),還要申請和維護句柄等資源。
Delphi將這一切都封裝在TCanvas類中,通過設置TCanvas類中的屬性,調用其中的方法,就可以實現畫圖功能。
10.2.1 Canvas畫布對象
雖然在任何組件上都可以繪制圖形,但由於很多組件上都有標題之類的文字,所以通常在窗體或面板之類的容器上繪制圖形。
例如,最常用的畫圖容器為Form和PaintBox。這些容器的空白區域稱為畫布(Canvas),使用畫布類TCanvas的方法可在畫布上繪制直線、弧線、矩形或圓形等各種圖形。
Canvas是TCanvas類的一個系統定義對象,稱為畫布對象,通常作為其他控件的一個屬性出現,不單獨使用。每個控件都有Canvas屬性。例如,使用Form的Canvas屬性即可在窗體的工作區內繪圖。由於Canvas是運行時屬性,在程序運行時才能獲得,所以必須寫程序來完成設置。
Canvas的主要屬性有Pen、Brush、Pixels、PenPos等,用於定義繪制圖形的風格。
1.Pen畫筆
Pen畫筆用於控制線條的顏色、模式、風格及寬度。
Pen的類型為TPen類,屬性有Color、Mode、Style及Width。
· Color屬性:控制線條的顏色。
· Mode屬性:控制線條的模式,取值見表10-2。
表10-2 Canvas.Pen.Mode屬性取值
值
含 義
pmBlack
總為黑色
pmWhite
總為白色
pmNop
不改變
pmNot
畫布背景的相反色
pmCopy
用Color屬性指定的畫筆顏色
pmNotCopy
畫筆顏色的相反色
pmMerge
畫筆顏色與畫布背景色的合成顏色
pmXor
畫筆顏色與畫布背景色的異或合成顏色
pmNotXor
pmXor的相反色
· Style屬性:控制線條的風格,包括實線、虛線和點劃線等,取值見表10-3。
表10-3 Canvas.Pen.Style屬性取值
值
含 義
值
含 義
psSolid
實線
psDashDotDot
兩點一劃線
psDash
短劃線
psClear
不劃線,通常用於清除輪廓線
psDot
點線
psInsideFrame
實線,Width屬性大於1時
psDashDot
點劃線
· Width屬性:控制線條的寬度。
Width屬性的值為整型,如果所給的值小於1,則按1處理。注意,Width屬性值會影響Style屬性值,當Width屬性值不是1時,Style屬性值不能是PsDash或PsDashDot。 例如:
Form1.Canvas.Pen.Color:=clRed; //設置線條顏色為白色
Form1.Canvas.Pen.Style:=psDashDot; //設置線風格為點劃線
Form1.Canvas.Pen.Width:=1; //設置線寬度為1,即細線
2.Brush刷子
Brush刷子用於確定圖形填充的顏色及填充方式。
Brush類型為TBrush類,屬性有Color、Style。
· Color屬性:確定圖形的填充顏色。
· Style屬性:確定圖形的填充方式,取值見表10-4。
例如:
Form1.Canvas.Brush.Color:=clRed; //設置填充色為紅色
Form1.Canvas.Brush.Style:=bsSolid; //設置填充方式為實填充
一般情況下,Brush為矩形、圓或多邊形提供內部的填充色和填充圖案,還為文本提供背景色,但它不影響線條的顏色和文本的前景色。
表10-4 Canvas.Brush.Style屬性取值
取 值
含 義
取 值
含 義
bsSolid
實填充
bsCross
十字交叉線填充
bsClear
不填充
bsDiagCross
交叉線填充
bsBDiagonal
右斜線填充
bsHorizontal
水平線填充
bsFDiagonal
左斜線填充
bsVertical
垂直線填充
3.Pixels像素
Pixels屬性用於存儲Canvas中每個像素點的顏色值。定義如下:
property Pixels(x,y;integer):TColor;
Pixels是一個二維數組,下標表示某像素點在屏幕位置的坐標,元素類型是TColor。一個繪圖過程實際上就是改變畫布上有關像素點顏色的過程,從而在視覺效果上得到一幅特定的圖。例如:
Form1.Canvas.Pixels[100,200]:=clYellow; //將窗口內的一點塗上黃色
反之,讀取Pixels數組的元素值則可取得相應坐標點的顏色值。
4.PenPos畫筆位置
PenPos屬性表示當前畫筆的位置,類型為TPoint。例如:
i:=Form1.Canvas.PenPos.x; //獲得當前畫筆位置
j:=Form1.Canvas.PenPos.y;
10.2.2 繪圖方法
TCanvas類提供多種用於繪圖和文字輸出的方法。
1.繪圖的坐標體系
在組件上繪圖的坐標體系與屏幕、窗口相同。
水平方向是X軸,垂直方向是Y軸,左上角起始點坐標是(0,0),區域內任意一點的坐標用(x,y) 表示,右下角點的坐標值取決於屏幕分辨率,例如,屏幕分辨率為800×600,則右下角點的坐標為(799,599)。
2.繪制直線
MoveTo(x,y)將畫筆當前位置設置到點(x,y) 處。畫筆當前位置在PenPos屬性中,改變畫筆當前位置使用MoveTo方法,而不要設法改變PenPos的值。
LineTo(x,y)從當前位置至(x,y) 點畫一條線,並把畫筆當前位置移至(x,y) 處。所繪直線具有已定義Pen畫筆的各項屬性。例如:
Canvas.MoveTo(x1,y1); //定位(x1,y1)
Canvas.LineTo(x2,y2); //在(x1,y1)和(x2,y2)之間畫線
3.繪制矩形
Rectangle方法用當前的Pen和Brush屬性繪制一個矩形,聲明如下:
procedure Rectangle(x1,y1,x2,y2:integer);
其中,(x1,y1) 和(x2,y2) 分別表示矩形左上角和右下角兩點坐標。若|x2 - x1|=|y2 - y1|,則該矩形成為正方形。
4.繪制橢圓
Ellipse方法在指定的矩形內畫一個橢圓,聲明如下:
procedure Ellipse(x1,y1,x2,y2:integer);
其中,(x1,y1) 和(x2,y2) 分別表示矩形左上角和右下角兩點坐標。如果指定矩形為正方形時,橢圓就成為圓。
5.填充多邊形
Polygon方法用當前Pen和Brush屬性繪制並填充任意邊數的多邊形,聲明如下:
procedure Polygon(Points:array of TPoint);
多邊形的多個坐標點存儲在數組Points中,Points數組的實際長度決定多邊形的邊數。一個可填充的多邊形應是封閉的,即首尾兩點坐標一致。
Points數組元素為TPoint,表示一個點的坐標。TPoint聲明如下:
type
TPoint = packed record
X: Longint;
Y: Longint;
end;
Types單元的Point函數將兩個整型值生成一個TPoint對象。Point函數聲明如下:
function Point(AX,AY:Integer):TPoint;
例如,下列程序段在矩形(x1 , y1 , x2 , y2)范圍內畫出一個等腰三角形。
var p:array [0..3] of TPoint;
p[0]:=point(x1 +(x2-x1) div 2,y1);
p[1]:=point(x1,y2);
p[2]:=point(x2,y2);
p[3]:=p[0];
Canvas.Polygon(p);
6.顯示字符串
Textout方法用當前Font屬性在指定位置(x,y)顯示指定字符串Text,聲明如下:
procedure TextOut(x,y;integer;const Text:string);
10.2.3 窗口繪圖事件
在Windows中運行應用程序,窗口的基本操作由Windows控制執行。
當窗口啟動時,Windows需要繪制窗口上全部的圖形圖像;當一個窗口被其他窗口覆蓋後再被激活時,Windows需要重新繪制該窗口上曾被覆蓋部分的圖形圖像。例如,窗口從最大化、最小化狀態還原時,或被其他窗口覆蓋、當其他窗口關閉或移開時,窗口被激活,Windows都將重畫標准控件的圖形圖像。
Delphi的標准控件都具有重畫功能,像窗體、按鈕、編輯框等,它們在窗口被激活時都能夠自動地、完整地顯示自身。
1.Form的OnPaint事件
當窗口啟動或被激活時,觸發窗體的OnPaint事件。如果需要在窗口上繪圖,則必須在Form的OnPaint事件上寫繪圖程序。
當窗口啟動時,Windows顯示窗口及其中控件,執行窗體的OnPaint事件繪制用戶設計的圖形。
當窗口被激活時,Windows會向窗口發送一個WM_Paint消息。Delphi對該消息進行處理,先清除窗口上的圖形,然後觸發OnPaint事件,重新繪制圖形。
2.Repaint方法立即刷新
當程序需要主動刷新圖形時,可以調用Repaint方法來刷新窗口。
Repaint方法自動產生一個WM_Paint消息,先清除窗口上的圖形,再產生OnPaint事件,重新繪制同樣的圖形。
10.2.4 響應鼠標事件
在程序運行過程中,利用鼠標可以實現動態地作圖,就像Windows的畫圖程序一樣。
利用鼠標作圖,需要在程序中識別鼠標位置和鼠標動作,鼠標有3個動作:鼠標按下、鼠標移動和鼠標松開。在Delphi中,對應3個鼠標動作有3個不同的事件:
(1)當鼠標按下時,發生OnMouseDown事件。
(2)當鼠標松開時,發生OnMouseUp事件。
(3)當鼠標移動時,發生OnMouseMove事件。
Form的OnMouseDown事件聲明為:
procedure TForm1.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
其中的5個參數含義如下:
· Sender:探測鼠標動作的對象。
· Button:涉及的鼠標按鈕:mbLeft左鍵,mbMiddle中鍵,mbRight右鍵。
· Shift:鼠標動作時,Alt、Ctrl、Shift鍵的狀態。
· X、Y:事件發生時鼠標的坐標。
OnMouseUp和OnMouseMove事件聲明和參數含義類似。
【例10.1】拖動鼠標畫直線並顯示直線的動態變化情況。
本例通過繪制直線演示3個鼠標響應事件上語句的配合。程序運行時,隨著鼠標的移動,用戶可隨時觀測直線的變化。程序運行窗口如圖10-1所示。
圖10-1 拖動鼠標畫直線
如果要在鼠標移動時隨時觀測直線的變化,則必須首先記住鼠標按下處的起始點位置。程序如下:
[delphi] view plaincopyprint?var Origin,MovePt:TPoint; //直線的起始點與移動點
mouse_down:Boolean; //鼠標是否按下的狀態
procedure TForm1.FormMouseDown(Sender:TObject; //鼠標按下事件
Button:TMouseButton;Shift:TShiftState;X,Y:Integer);
begin
mouse_down:=true; //鼠標已按下
Canvas.MoveTo(X,Y);
Origin:=Point(X,Y); //記載直線的起始點位置
MovePt:=Origin; //記載直線的移動點位置
end;
procedure TForm1.FormMouseMove(Sender:TObject; //鼠標移動事件
Shift:TShiftState;X,Y:Integer);
begin
if mouse_down //鼠標按下時畫直線
then begin
Canvas.Pen.Mode:=pmNotXor; //畫筆線條為異或模式
Canvas.MoveTo(origin.X,origin.Y); //擦除前一點的直線
Canvas.LineTo(MovePt.X,MovePt.Y);
MovePt := Point(X, Y);
Canvas.MoveTo(origin.X,origin.Y); //重畫當前點的直線
Canvas.LineTo(MovePt.X,MovePt.Y);
end;
end;
procedure TForm1.FormMouseUp(Sender:TObject; //鼠標松開事件
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Canvas.LineTo(X,Y); //畫直線
mouse_down:= false; //鼠標已松開
end;
var Origin,MovePt:TPoint; //直線的起始點與移動點
mouse_down:Boolean; //鼠標是否按下的狀態
procedure TForm1.FormMouseDown(Sender:TObject; //鼠標按下事件
Button:TMouseButton;Shift:TShiftState;X,Y:Integer);
begin
mouse_down:=true; //鼠標已按下
Canvas.MoveTo(X,Y);
Origin:=Point(X,Y); //記載直線的起始點位置
MovePt:=Origin; //記載直線的移動點位置
end;
procedure TForm1.FormMouseMove(Sender:TObject; //鼠標移動事件
Shift:TShiftState;X,Y:Integer);
begin
if mouse_down //鼠標按下時畫直線
then begin
Canvas.Pen.Mode:=pmNotXor; //畫筆線條為異或模式
Canvas.MoveTo(origin.X,origin.Y); //擦除前一點的直線
Canvas.LineTo(MovePt.X,MovePt.Y);
MovePt := Point(X, Y);
Canvas.MoveTo(origin.X,origin.Y); //重畫當前點的直線
Canvas.LineTo(MovePt.X,MovePt.Y);
end;
end;
procedure TForm1.FormMouseUp(Sender:TObject; //鼠標松開事件
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Canvas.LineTo(X,Y); //畫直線
mouse_down:= false; //鼠標已松開
end;