📄 chap09.htm
字号:
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312"><META NAME="Generator" CONTENT="Microsoft Word 97"><TITLE> </TITLE><META NAME="Template" CONTENT="C:\MSOffice\Template\jjhou.dot"></HEAD><BODY><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>第</FONT><FONT SIZE=2>XX</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>章</FONT><FONT SIZE=2> </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>具体而微的绘图程式</P><P> </P><P>在本章中我将为你示范如何在</FONT><FONT SIZE=2>C++Builder</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>中撰写一个完整的绘图程式。藉由这个程式的撰写,你会更加了解</FONT><FONT SIZE=2>C++Builder</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>的</FONT><FONT SIZE=2> Canvas </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>绘图精神,而在撰写这个程式的同时,我们也可将相关的技术做一个整体的检阅。此绘图程式的执行结果如下:</P><P> </P><P><IMG SRC="Image7.gif" WIDTH=492 HEIGHT=387></P><P> </P><P> </P><P>在此程式中我会以循序渐进的方式一步一步地带领你完成整个程式,基本上这个程式和</FONT><FONT SIZE=2>C++Builder</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>内附的范例程式有几分类似,但我必须要说明的是:在</FONT><FONT SIZE=2> C++Builder</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>中所附的范例程式是直接由原先在</FONT><FONT SIZE=2>Delphi</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>内以</FONT><FONT SIZE=2> Object Pascal </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>所撰写的范例程式修改而成,所以有部份程式的写法大为违背</FONT><FONT SIZE=2>C++ </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>式物件导向精神,在迈入</FONT><FONT SIZE=2>C++Builder </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>的新世纪之後,我们当然希望写出的程式是『系出名门,血统纯正』的</FONT><FONT SIZE=2>C++ </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>式的物件导向程式。而这就是我在本章中希望带领你完成的程式。</P><P> </P></FONT><FONT SIZE=2><P>XX-01 </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>关於滑鼠事件(</FONT><FONT SIZE=2>Mouse Event</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>)</P><P> </P><P>撰写绘图程式,首先要了解滑鼠事件,在</FONT><FONT SIZE=2>Windows</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>中定义了许多的滑鼠讯息(</FONT><FONT SIZE=2>Message</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>),而这些滑鼠讯息在</FONT><FONT SIZE=2>BCB</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>中就成为滑鼠事件了,为了要处理滑鼠事件,我们必须要选写滑鼠事件处理程式:</P><P> </P></FONT><FONT FACE="华康细圆体,???" LANG="ZH-TW" SIZE=1><P>在</FONT><FONT SIZE=1>Windows</FONT><FONT FACE="华康细圆体,???" LANG="ZH-TW" SIZE=1>中定义的滑鼠讯息列表</P></FONT><FONT SIZE=1><P>WM_CAPTURECHANGED</P><P>WM_LBUTTONDBLCLK</P><P>WM_LBUTTONDOWN</P><P>WM_LBUTTONUP</P><P>WM_MBUTTONDBLCLK</P><P>WM_MBUTTONDOWN</P><P>WM_MBUTTONUP</P><P>WM_MOUSEACTIVATE</P><P>WM_MOUSEMOVE</P><P>WM_NCHITTEST</P><P>WM_NCLBUTTONDBLCLK</P><P>WM_NCLBUTTONDOWN</P><P>WM_NCLBUTTONUP</P><P>WM_NCMBUTTONDBLCLK</P><P>WM_NCMBUTTONDOWN</P><P>WM_NCMBUTTONUP</P><P>WM_NCMOUSEMOVE</P><P>WM_NCRBUTTONDBLCLK</P><P>WM_NCRBUTTONDOWN</P><P>WM_NCRBUTTONUP</P><P>WM_RBUTTONDBLCLK</P><P>WM_RBUTTONDOWN</P><P>WM_RBUTTONUP</P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>表</FONT><FONT SIZE=2>XX-01 Windows</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>内滑鼠相关</FONT><FONT SIZE=2> Message</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>。</P><P> </P><P>虽然在</FONT><FONT SIZE=2>Windows</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>作业系统中定义了非常多的讯息,但是在</FONT><FONT SIZE=2>C++Builder </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>中已经把庞大的讯息系统作适度的简化了,并且不再以讯息的方式存在,而改以事件</FONT><FONT SIZE=2> (Event</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>)的处理方式,在本章的绘图程式中,我们只要处理以下的几个事件即可:</P><P> </P></FONT><FONT SIZE=2><P>OnMouseDown </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>滑鼠键按下事件</P></FONT><FONT SIZE=2><P>OnMouseMove </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>滑鼠移动事件</FONT><FONT SIZE=2> </P><P>OnMouseUp </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>滑鼠键放开事件</P></FONT><FONT SIZE=2><P>OnClick </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>任何滑鼠的点取</P><P> </P><P>在此,你可以很明显地发现,在</FONT><FONT SIZE=2>C++Builder</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>的事件中并未将左右滑鼠键分别定义,而是以合并处理的方式,因此在收到以上滑鼠事件时,若你要分辨左右滑鼠事件时,必须在事件处理程式中判断左右键。</P><P> </P><P>具备了基本的滑鼠事件认知後,我们开始进行後续的程式探索吧!</P><P> </P><P>为了让你实际了解程式的细节,我希望将程式撰写的步骤细节交代楚,在往下进行之前,我们先建立一个新的专案档,并将其命名为</FONT><FONT SIZE=2> DrawMain</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>,同时将</FONT><FONT SIZE=2>Form</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>的</FONT><FONT SIZE=2>Color</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>性质设为黑色(</FONT><FONT SIZE=2>clBlack</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>),以便直接在上面画图。</P><P> </P></FONT><FONT SIZE=2><P>XX-02</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>滑鼠事件的处理</P><P> </P><P>当</FONT><FONT SIZE=2>C++ Builder</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>应用程式侦测到物件滑鼠事件时,它会检查你是否定义该物件相对应的滑鼠事件处理程式,然後呼叫该函数,将相关参数传给它。以</FONT><FONT SIZE=2>OnMouseDown</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件为例,它的事件处理程式模版如下:</P></FONT><FONT SIZE=2><P>void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,</P><P>	TShiftState Shift, int X, int Y)</P><P>{</P><P>}</P><P> </P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>它总共接收了以下几个参数:</P></FONT><FONT SIZE=2><P>Sender </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>引发该事件的软体元件。</P></FONT><FONT SIZE=2><P>Button </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>表示滑鼠的按键。它的值可为</FONT><FONT SIZE=2>mbLeft</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>(左键),</FONT><FONT SIZE=2>mbRight</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>(右键),</FONT><FONT SIZE=2>mbMiddle</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>(中间键)。</P></FONT><FONT SIZE=2><P>Shift </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>用以表示事件发生的同时</FONT><FONT SIZE=2>Alt</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>,</FONT><FONT SIZE=2>Shift</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>及</FONT><FONT SIZE=2>Ctrl</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>叁键的状态。</P></FONT><FONT SIZE=2><P>X,Y </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>用以表示事件发生时之座标位置。</P><P> </P><P>在大多数的情况下,滑鼠事件的(</FONT><FONT SIZE=2>X,Y</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>)座标值是我们最为感兴趣的项目,不过,有时候我们也需要靠</FONT><FONT SIZE=2>Button</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>键来判断滑鼠的按键,或是需要利用</FONT><FONT SIZE=2>Shift</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>来取得特殊键的状态,而做一些额外的程式处理。</P><P> </P></FONT><FONT SIZE=2><P>XX-02-01 OnMouseDown</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件的处理</P><P> </P><P>首先我们先以一个最基本的画线程式来说明</FONT><FONT SIZE=2>OnMouseDown</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件的处理,当使用者按下滑鼠时,我们希望将笔移至事件发生时的坐标,因此我们可将程式写成如下:</P><P> </P></FONT><FONT SIZE=2><P>void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,</P><P>	TShiftState Shift, int X, int Y)</P><P>{</P><P>	Canvas->MoveTo(X,Y);</P><P>}</P><P>XX-02-03 OnMouseUp</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件的处理</P><P> </P><P>同样地,我们可以再为这个</FONT><FONT SIZE=2>Form</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>加上</FONT><FONT SIZE=2>OnMouseUp</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>的事件处理函式,在收到</FONT><FONT SIZE=2>OnMouseUp</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件时,由滑鼠点下的坐标,画一条直线至现在的坐标。</P><P> </P></FONT><FONT SIZE=2><P>void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,</P><P>	TShiftState Shift, int X, int Y)</P><P>{</P><P>	Canvas->LineTo(X,Y);</P><P>}</P><P> </P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>在写完了以上两个事件处理函式之後,我们就可以在</FONT><FONT SIZE=2>Form</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>上面作画了,你可以用滑鼠在</FONT><FONT SIZE=2>Form</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>上面拖戈出一条条直线。其执行结果大致如图</FONT><FONT SIZE=2>XX-01</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>:</P><P> </P><P><IMG SRC="Image8.gif" WIDTH=348 HEIGHT=240></P><P>图</FONT><FONT SIZE=2>XX-01</P><P>XX-02-02 OnMouseMove</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件的处理</P><P> </P><P>在加上了</FONT><FONT SIZE=2>OnMouseDown</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>及</FONT><FONT SIZE=2>OnMouseUp</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>处理函式之後,我们只能画出一条条直线,若是我们想要以滑鼠画出不规则线段时,就必须再处理</FONT><FONT SIZE=2>OnMouseMove</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件,利用</FONT><FONT SIZE=2>OnMouseMove</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件,我们可以追纵到滑鼠移动的位置,简单的</FONT><FONT SIZE=2>OnMouseMove</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件处理函式如下:</P><P> </P></FONT><FONT SIZE=2><P>void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X,</P><P>	int Y)</P><P>{</P><P>	Canvas->LineTo(X,Y);</P><P>}</P><P> </P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>此程式的意义即在於将滑鼠所经过的每个点,以线条连接起来,在加上</FONT><FONT SIZE=2>OnMouseMove </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>事件处理函式之後,它的执行结果会变成图</FONT><FONT SIZE=2>XX-02</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>:</P><P><IMG SRC="Image9.gif" WIDTH=348 HEIGHT=240></P><P>图</FONT><FONT SIZE=2>XX-02</P><P>XX-02-03	</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>滑鼠的处理的加强</P><P> </P><P>前面的程式对於滑鼠的移动处理有部份考虑的不够周详,因为它在滑鼠移动时不分青红皂白就将线画在萤慕上,造成萤幕上的线条混乱,这并不是正规的处理方法,正确的处理方法应该如下:</P><P> </P></FONT><FONT SIZE=2><P>(1) </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>滑鼠键按下时,将记录滑鼠按下的旗标设为</FONT><FONT SIZE=2>True.</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>同时将该点记录下来,谓之原点。</P><OL START=2><LI>滑鼠移动时,判断滑鼠按下的旗标是否设为</FONT><FONT SIZE=2> True</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>,若为</FONT><FONT SIZE=2> True</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>,则移动至原点,并画一条由原点至目前所在点的线。同时更新原点位置至目前所在之点。</LI><LI VALUE=3>滑鼠放开时,将记录滑鼠按下的旗标设为</FONT><FONT SIZE=2>False</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>。</LI></OL><P> </P><P>以下就是关於叁个滑鼠事件的处理程式码。</P><P> </P></FONT><FONT SIZE=2><P>// </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>滑鼠按下的事件处理函式</P></FONT><FONT SIZE=2><P>// 1. </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>将旗标设为</FONT><FONT SIZE=2>True</P><P>// 2. </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>记录原点位置</P></FONT><FONT SIZE=2><P>void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,</P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>	</FONT><FONT SIZE=2>TShiftState Shift, int X, int Y)</P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>{</P></FONT><FONT SIZE=2><P> m_bDraw = TRUE;</P><P> m_nOrgX=X;</P><P> m_nOrgY=Y;</P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>}</P><P> </P></FONT><FONT SIZE=2><P>// </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>滑鼠移动的事件处理函式</P></FONT><FONT SIZE=2><P>// 1. </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>判断旗标是否为</FONT><FONT SIZE=2>True</FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>。若是则进行以下动作。</P></FONT><FONT SIZE=2><P>// 2. </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>移动至原点。</P></FONT><FONT SIZE=2><P>// 3. </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>画一条由原点至目前所在点的线条。</P></FONT><FONT SIZE=2><P>// 4. </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>更改原点位置。</P><P> </P></FONT><FONT SIZE=2><P>void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,</P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>	</FONT><FONT SIZE=2>int X, int Y)</P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>{</P><P>	</FONT><FONT SIZE=2>if (m_bDraw)</P><P> {</P><P> 	Canvas->MoveTo(m_nOrgX,m_nOrgY);</P><P> Canvas->LineTo(X,Y);</P><P> m_nOrgX = X;</P><P> m_nOrgY = Y;</P></FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2><P>	}</P><P>}</P><P> </P></FONT><FONT SIZE=2><P>// </FONT><FONT FACE="新细明体" LANG="ZH-TW" SIZE=2>滑鼠放开的事件处理函式</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -