日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

QT快速入门、三点求圆心实现详解

發(fā)布時間:2024/9/27 c/c++ 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 QT快速入门、三点求圆心实现详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在編程中,會經常用到數學計算,所以C++將常用的數學計算,例如求正余弦等,封裝成函數(正是我們在3.2?數學計算中學習到的),我們只需要寫入簡單的語句就可以執(zhí)行所需要的功能,這正是函數的意義。在這一章的學習,我們會建立起模塊化的思維,小的功能模塊我們會封裝成函數,而一些大的功能模塊,則封裝成類。

剛開始進行函數的編寫,會感覺有些難度。其實,只要稍稍多做一些函數編寫的訓練的時候,就可以很容易找到其中的規(guī)律。這一章會有大量的練習實踐,勤動手是學好這一章內容的關鍵。

7.1?函數

寫一個求中點函數,輸入兩個點坐標——Point1,Point2,求出Point1與Point2的中點坐標。參考表7-1中的程序。如何來完成這個編程要求,先看一下函數的編寫規(guī)范。

#include<iostream> #include<cmath> using namespace std;struct Point{ double x; double y; };Point GetMidpoint(Point P1, Point P2) { Point Midpoint={0,0}; Midpoint.x = (P1.x + P2.x )/2; Midpoint.y = (P1.y + P2.y )/2; return Midpoint; }int main() { Point Point1,Point2; cout<<"請輸入兩個點的坐標"<<endl; cin>>Point1.x>>Point1.y; cin>>Point2.x>>Point2.y;Point Point_MidP1P2; Point_MidP1P2=GetMidpoint(Point1,Point2); cout<<"中點坐標為"<<Point_MidP1P2.x<<','<<Point_MidP1P2.y<<endl; system("pause"); return 0; }

表7-1?求中點函數程序

函數的代碼應該放在主函數前面(如果想放在后面需要聲明,在7.2三點求圓心會講到如果將函數的實現(xiàn)放在主函數后面,應該如何進行聲明),具體如何實現(xiàn)該函數請看7.1.1小節(jié)。

第27行代碼對函數GetMidPoint進行了調用,并將求得的結果賦值給了Point_MidP1P2結構體變量,函數的使用可以回顧3.2數學計算這一節(jié)。區(qū)別只在于3.2數學計算中的函數是C++提供給我們的,而函數GetMidPoint是我們自己編寫的。

測試程序,輸入兩個坐標為(3,5),(5,10)的坐標點,程序自動計算中點坐標值并顯示,如圖7-1所示。

?

圖7-1?求中點函數測試結果示意圖

7.1.1?函數的編寫規(guī)范

首先,先看一下函數的編寫規(guī)范,函數定義的格式為

函數返回值的類型名?函數名(參數1類型 參數1名稱,?參數2類型 ?參數2名稱, ...)

{

函數體

}

要求中點坐標,首先回顧上一章將的內容——結構體,先定義名為Point的結構體類型,并定義名為Point1,Point2,Point3的結構體變量,結構體類型為Point?:

struct Point{

double x;

double y;

};

然后開始進行求中點的函數的編寫,根據易懂的命名方式,將該功能的函數名起名為GetMidpoint,最終求得的結果為一個點,所以原函數返回值的類型名為Point,共有兩個參數,都為Point類型。

Point GetMidPoint(Point P1, Point P2)

第一個Point為函數返回值的類型名,即最終我們想要求得結果的類型,這里我們要求中點坐標,所以最終的結果就是一個Point類型;空格后緊接著是函數名GetMidPoint;小括號內是求中點函數的參數,共有兩個參數,并且都為Point類型,而P1和P2則是參數1名稱,以及參數2名稱。

接下里開始在大括號內進行函數體的編寫。

Point Midpoint={0,0};

Midpoint.x = (P1.x + P2.x )/2;

Midpoint.y = (P1.y + P2.y )/2;

return Midpoint;

先定義了Point類型的名為Midpoint的結構體變量,并賦初值為0,0;然后求得點Midpoint的x坐標值以及y坐標值;函數最終的結果需要使用關鍵字return進行返回。

(當然,如果定義函數返回值的類型名為void的話,即函數什么都不返回,最后的return語句不需要編寫;如果某個函數只執(zhí)行某項功能,需要返回任何值得時候,就可以這樣編寫)

7.1.2?形式參數與實在參數

形式參數是在定義函數時放在函數名稱之后的括號中的參數。在表7-1的程序中11行代碼中的P1,P2為形式參數。在函數的實現(xiàn)部分,該函數還沒有被調用,系統(tǒng)不對形式參數分配內存;函數被調用時(程序的第27行為調用函數的代碼),系統(tǒng)對形式參數進行內存分配,在函數結束后,將這部分內存釋放掉。形式參數屬于局部變量,其作用域限定在函數體內。

實在參數是一個具有確定值的參數。在表7-1的程序中27行代碼中的Point1,Point2為實在參數。調用函數時,將實在參數的值放入形式參數的內存單元當中。

需要注意的是,實在參數的個數以及類型必須和形式參數一致,并且前后排序也要一一對應。

7.1.3?編程練習:求兩點連線

編寫一個函數,輸入兩點坐標,能夠自動計算出這兩點連線的斜率和截距。程序如表7-2所示。

#include<iostream> #include<cmath> using namespace std;struct Point{ double x; double y; };struct Line{ double k; double b; };Line GetLineOfTwoPoints(Point P1,Point P2) { Line StraightLine={0,0}; if(P1.x==P2.x) { P1.x=P2.x-0.0000001; } StraightLine.k = (P2.y - P1.y)/ (P2.x -P1.x); StraightLine.b = P1.y - P1.x * StraightLine.k ; return StraightLine; }int main() { Point Point1,Point2; cout<<"請輸入兩個點的坐標"<<endl; cin>>Point1.x>>Point1.y; cin>>Point2.x>>Point2.y; Line Line1_P1P2; Line1_P1P2=GetLineOfTwoPoints(Point1,Point2); cout<<"兩點連線的斜截式方程為y="<<Line1_P1P2.k<<"x+"<<Line1_P1P2.b<<'.' <<endl; system("pause"); return 0; }

表7-2?求兩點連線函數程序

在初高中的時候我們通常使用斜截式來表示直線,也就是說區(qū)分不同的直線只需要一個斜率k和一個截距b即可;第10至13行定義一個名為Line的結構體類型,成員為double型的斜率k,以及double型的截距b。

15行至25行代碼實現(xiàn)了兩點求連線的函數,函數返回值的類型名為Line,函數名為GetLineOfTwoPoints,參數有兩個,都是Point型,兩個形式參數為P1,P2;函數體內部先定義一個Line型的結構體變量StraightLine,用來求得直線的斜率和截距。注意第18行至21行有一個if語句,該語句是為了在求斜率的時候,防止分母為0;最后將得到的直線結果返回。

在34行進行了函數的調用,實在參數為Point1和Point2,所得連線結果賦值給結構體變量Line1_P1P2。

測試程序,輸入兩點坐標分別為(3,6)、(2,9),最終結果如圖7-2所示。

?

圖7-2?求兩點連線函數測試結果示意圖

7.1.4?編程練習:求中垂線

編寫一個函數,輸入兩點坐標,能夠自動計算出這兩點連線的中垂線的斜率和截距。根據兩點求中垂線,回憶初高中的數學知識,需要先求兩點的中點,然后求得兩點連線的斜率,垂線的斜率與連線的斜率相乘為-1,有了過中垂線的一個點以及斜率就可以求出中垂線了。程序如表7-3所示。

#include<iostream>#include<cmath>using namespace std;struct Point{double x;double y;};struct Line{double k;double b;};Point GetMidpoint(Point P1, Point P2){Point Midpoint={0,0};Midpoint.x = (P1.x + P2.x )/2;Midpoint.y = (P1.y + P2.y )/2;return Midpoint;}Line GetLineOfTwoPoints(Point P1,Point P2){Line StraightLine={0,0};if(P1.x==P2.x){P1.x=P2.x-0.0000001;}if(P1.y==P2.y){P1.y=P2.y-0.0000001;}StraightLine.k = (P2.y - P1.y)/ (P2.x -P1.x);StraightLine.b = P1.y - P1.x * StraightLine.k ;return StraightLine;}Line GetMidperpendicular(Point P,double k){Line StraightLine={0,0};StraightLine.k = -1/k;StraightLine.b = P.y - StraightLine.k * P.x ;return StraightLine;}int main(){Point Point1,Point2;cout<<"請輸入兩個點的坐標"<<endl;cin>>Point1.x>>Point1.y;cin>>Point2.x>>Point2.y;Point Point_MidP1P2;Point_MidP1P2=GetMidpoint(Point1,Point2);Line Line1_P1P2;Line1_P1P2=GetLineOfTwoPoints(Point1,Point2);cout<<"兩點連線的斜截式方程為y="<<Line1_P1P2.k<<"x+" <<Line1_P1P2.b<<'.'<<endl;Line Line3_P1P2_Vertical;Line3_P1P2_Vertical = GetMidperpendicular(Point_MidP1P2,Line1_P1P2.k);cout<<"兩點的中垂線的斜截式方程為y="<<Line3_P1P2_Vertical.k<<"x+" <<Line3_P1P2_Vertical.b<<'.'<<endl;system("pause");return 0;}
  • 表7-3?求中垂線函數程序

    由于要求中垂線,所以需要先求中點坐標以及兩點連線的斜率,所以將直接將前面的寫好的求兩點中點函數以及求兩點連線函數拿來使用。

    需要注意的是求兩點連線的第31至34行防止兩點的y坐標相同;如果兩點y坐標相同,求得的斜率就為0,在求中垂線的時候,連線的斜率作為分母,這樣就會出錯。

    40至46行實現(xiàn)了求中垂線函數,函數返回值的類型名為Line,函數名為?GetMidperpendicular,參數有兩個,一個是Point型,一個是double型,兩個形式參數的名稱為P,k;函數體內部先定義一個Line型的結構體變量StraightLine,用來求得直線的斜率和截距。注意兩個函數的結構體變量StraightLine都是局部參數,兩者是不同的。

    中垂線函數實現(xiàn)后,開始在主函數中進行中垂線求取。首先,求得兩點的中點Point_MidP1P2;然后求得兩點連線Line1_P1P2;最后,將中點Point_MidP1P2以及兩點連線Line1_P1P2的斜率Line1_P1P2.k作為實在參數,調用求中垂線函數GetMidperpendicular,將結果賦值給Line1_P1P2。

    測試程序,輸入兩個點坐標為(1,1)以及(0,2),最終結果如圖7-3所示。

    ?

    圖7-3?求中垂線函數測試結果示意圖

    7.2?編程練習:三點求圓心

    兩點確定一條直線,那么幾個點確定一個圓呢?回憶下高中的數學知識,三個點(不在同一條直線上)確定一個圓。這一節(jié)回顧初中的數學知識,并結合學習的程序語言,對如何將給定的不在同一條直線的三點進行圓心的求解形成一個初步的思路。

    7.2.1?三點求圓心思路

    如圖7-4所示,當給定三個坐標點(x1,y1),(x2,y2),(x3,y3),我們分別稱這三個坐標點為Point1,Point2,Point3;

    我們連接坐標點Point1,Point2以及Point2,Point3,這樣我們有了兩條線段,我們分別稱這兩條線段為Line1_P1P2,Line2_P2P3;

    然后我們分別對這兩條線段Line1_P1P2,Line2_P2P3求中垂線,獲得的中垂線我們稱為Line3_P1P2_Vertical,Line4_P2P3_Vertical;

    最后,我們能夠得到這兩條中垂線的交點——Point6_CenterOfCircle。

    需要注意的是,在求三點求圓心前,需要先確定輸入的三個點都不相同,并且不在一條直線上。判斷不在一條直線上,可以將Line1_P1P2,Line2_P2P3的斜率是否相同,如果相同則意味著三點在一條直線上。

    ?

    圖7-4?三點求圓心坐標示意圖

    7.2.2?程序實現(xiàn)

    接下來我們將以上思路通過程序實現(xiàn)出來,如表7-4所示。

    #include<iostream>#include<cmath>using namespace std;struct Point{double x;double y;};struct Line{double k;double b;};bool IsSameSlope(Line Line1,Line Line2);Line GetLineOfTwoPoints(Point P1,Point P2);bool IsSamePoint(Point P1,Point P2);Point GetMidpoint(Point P1, Point P2);Line GetMidperpendicular(Point P,double k);Point GetPointOfIntersection(Line Line1,Line Line2);double GetDistanceOfTwoPoint(Point P1,Point P2);int main(){Point Point1,Point2,Point3;cout<<"請輸入三個點的坐標"<<endl;cin>>Point1.x>>Point1.y;cin>>Point2.x>>Point2.y;cin>>Point3.x>>Point3.y;bool x;x=IsSamePoint(Point1,Point2)||IsSamePoint(Point2,Point3)||IsSamePoint(Point1,Point3);if(x==1){cout<<"錯誤,輸入的三個點有相同點!"<<endl;system("pause");return 0;}Line Line1_P1P2,Line2_P2P3;Line1_P1P2=GetLineOfTwoPoints(Point1,Point2);Line2_P2P3=GetLineOfTwoPoints(Point2,Point3);bool y;y=IsSameSlope(Line1_P1P2,Line2_P2P3);if(y==1){cout<<"錯誤,輸入的三個點共線!"<<endl;system("pause");return 0;}else{cout<<"這三個點能夠求出圓心!"<<endl;}Point Point4_MidP1P2,Point5_MidP2P3;Point4_MidP1P2 = GetMidpoint(Point1,Point2);Point5_MidP2P3 = GetMidpoint(Point2,Point3);Line Line3_P1P2_Vertical,Line4_P2P3_Vertical;Line3_P1P2_Vertical = GetMidperpendicular(Point4_MidP1P2,Line1_P1P2.k);Line4_P2P3_Vertical = GetMidperpendicular(Point5_MidP2P3,Line2_P2P3.k);Point CenterOfCircle,StartPoint,EndPoint;CenterOfCircle=GetPointOfIntersection(Line3_P1P2_Vertical,Line4_P2P3_Vertical);double Radius;Radius = GetDistanceOfTwoPoint(CenterOfCircle,Point2);cout<<"圓心坐標為"<<CenterOfCircle.x<<", "<<CenterOfCircle.y<<endl;cout<<"半徑為"<<Radius<<endl;system("pause");return 0;}bool IsSameSlope(Line Line1,Line Line2){if (Line1.k==Line2.k){return 1;}else{return 0;}}Line GetLineOfTwoPoints(Point P1,Point P2){Line StraightLine={0,0};if(P1.x==P2.x){P1.x=P2.x-0.0000001;}if(P1.y==P2.y){P1.y=P2.y-0.0000001;}StraightLine.k = (P2.y - P1.y)/ (P2.x -P1.x);StraightLine.b = P1.y - P1.x * StraightLine.k ;return StraightLine;}bool IsSamePoint(Point P1,Point P2){if(P1.x==P2.x&&P1.y==P2.y){return 1;}else{return 0;}}Point GetMidpoint(Point P1, Point P2){Point Midpoint={0,0};Midpoint.x = (P1.x + P2.x )/2;Midpoint.y = (P1.y + P2.y )/2;return Midpoint;}Line GetMidperpendicular(Point P,double k){Line StraightLine={0,0};StraightLine.k = -1/k;StraightLine.b = P.y - StraightLine.k * P.x ;return StraightLine;}Point GetPointOfIntersection(Line Line1,Line Line2){Point PointOfIntersection={0,0};PointOfIntersection.x = (Line2.b-Line1.b)/(Line1.k-Line2.k);PointOfIntersection.y = (Line2.k*Line1.b-Line1.k*Line2.b)/(Line2.k-Line1.k);return PointOfIntersection;}double GetDistanceOfTwoPoint(Point P1,Point P2){double DistanceOfTwoPoint=0;DistanceOfTwoPoint=sqrt((P1.x-P2.x)*(P1.x-P2.x)+(P1.y-P2.y)*(P1.y-P2.y));return DistanceOfTwoPoint;}
  • 表7-4?三點求圓心程序

    函數放在主程序后面,需要在主函數前面聲明。函數聲明很簡單,直接將大括號前面的

    ?

    函數返回值的類型名?函數名(參數1類型 參數1名稱,?參數2類型 ?參數2名稱, ...)

    ?

    加上分號“;”即可。

    該程序共實現(xiàn)了7個函數:

    bool IsSameSlope(Line Line1,Line Line2):判斷兩條直線的斜率是否相同,兩個參數為Line型的結構體變量,如果相同返回1,不同返回0;

    Line GetLineOfTwoPoints(Point P1,Point P2):求兩點連線,兩個參數為Point型的結構體變量,返回值為Line型結構體變量;

    bool IsSamePoint(Point P1,Point P2):判斷兩點是否相同,兩個參數為Point型的結構體變量,如果相同返回1,不同返回0;

    Point GetMidpoint(Point P1, Point P2):求兩點中點,兩個參數為Point型的結構體變量,返回值為Point型結構體變量;

    Line GetMidperpendicular(Point P,double k):求中垂線,兩個參數為Point型的結構體變量和double型變量,返回值為Line型結構體變量;

    Point GetPointOfIntersection(Line Line1,Line Line2):求兩條直線交點,兩個參數為Line型的結構體變量,返回值為Point型結構體變量;

    double GetDistanceOfTwoPoint(Point P1,Point P2):求兩點間距離(求半徑使用,圓心到三點任意一點的距離即半徑),兩個參數為Point型的結構體變量,返回值為double型結構體變量。

    函數實現(xiàn)后,在主函數中參考7.2.1三點求圓心思路對函數進行調用來完成;測試程序,輸入三個點——(0,0)、(5,5)、(10,0),運行結果如圖7-5所示。

    ?

    圖7-5?三點求圓心測試結果示意圖

    7.3?類

    通過函數的編寫,我們已經初步的接觸到模塊化設計的思維;在三點求圓心的實現(xiàn)中,我們一步步定義小模塊(即函數)幫助我們解決三點求圓心這個問題。在程序的設計歷史過程中,需要解決的問題越來越繁雜,這種編程思路會顯得很適應編程需求。這個時候另一種以模塊化為切入點的編程思路應用開來——面對對象編程(OOP,Object Oriented Programming)。

    什么是面向對象編程?面向對象編程面向過程編程相對,面向過程編程是如流水線一般,針對要解決的問題,按照邏輯順序一步步進行程序的設計;而面向對象編程如前文所講,針對要解決的問題,思考可能要用到方法,將一個個方法先進行實現(xiàn),然后在進行問題的解決。

    C++提供了類這一功能,以勝任面對對象編程。

    7.3.1 VS2010環(huán)境下類的添加

    這里以三點求圓心類的實現(xiàn)為例,進行類的講解。

    在VS中創(chuàng)建一個名為CenterOfCircle的項目(名字可以不一樣),然后開始添加一個類,在“解決方案資源管理器”中點擊鼠標右鍵,依次點擊“添加”,“類”,如圖7-6所示。

    ?

    圖7-6?添加類示意圖

    出現(xiàn)“添加類”窗口,默認為“C++類”,無需改變,直接點擊右下角的“添加”鍵。如圖7-7所示。

    ?

    圖7-7?添加類確認示意圖

    此時出現(xiàn)新的窗口——“一般C++類向導”,此時在“類名”這一欄輸入CenterOfCircle,此時,“.h文件”、“.cpp文件”兩欄自動填滿相應的信息,填寫好后,界面如圖7-8所示。

    ?

    圖7-8?“一般C++類向導”界面

    點擊“完成”,完成類文件的添加,然后在“解決方案資源管理”中的“頭文件”以及“源文件”中會分別出現(xiàn)兩個文件——“CenterOfCircle.h”、“CenterOfCircle.cpp”。如圖7-9所示。

    ?

    圖7-9?類添加后資源方案管理界面示意圖

    類添加完成后,就可以進行類的編寫,即分別向“CenterOfCircle.h”、“CenterOfCircle.cpp”兩個文件中寫入相應的代碼,以完成三點求圓心類。

    簡單的來說以.h結尾的文件是類聲明部分——以數據成員的方式描述數據部分,以成員函數(被稱為方法)的方式描述公有接口;以.cpp結尾的文件是類方法定義部分——用來描述如何實現(xiàn)類成員函數。

    7.3.2?編程練習:三點求圓心類

    我們通過上一節(jié)熟悉了在VS2010中類的添加,這一節(jié)我們將對三點求圓心功能進行類的實現(xiàn)和使用。

    在數控機床等操作中,想要進行圓弧插補,需要輸入的點的個數為三個,通過三個點進行圓心坐標以及圓弧走向的求取后,才能進行圓弧插補。所以這一節(jié)的內容是第8章逐點比較法圓弧插補的一個基礎,請大家務必把基礎打好。

    首先,要判斷三點在不在一條直線上。在這一節(jié)當中我們將通過計算機對給定的三個點,求這三個點所在圓的圓心、半徑以及三個點行進的方向——順時針還是逆時針。

    在進行程序編寫前需要首先了解一些有關類編寫的相關知識。

    1.構造函數與解析函數

    首先,再次對類這一概念進行解釋。編寫三點求圓心類,我們不關心要求取的三個點到底是(0,0)、(5,5)、(10,0),或者是(1,2)、(4,5)、(11,0)等等,也就是說這個三點求圓心類是抽象的,不是具體只針對某三個具體的點能夠求得最終的結果;即這個三點求圓心類可以對任意三個點都有效,都能夠求得結果。

    那么,三點求圓心類是如何區(qū)分不同的點的呢?這就是構造函數的作用,構造函數用來創(chuàng)建對象,例如(0,0)、(5,5)、(10,0)三個點,共有6個值,在求取這三個點的時候,只需要將這6個數字放入構造函數的參數中即可,構造出對應這三個點的對象;有了這個對應(0,0)、(5,5)、(10,0)三個點的對象后,就可以使用三點求圓心類下的成員函數進行圓心、半徑等結果的求取了。

    總而言之,類是抽象的,在使用類的時候,放入到構造函數中的參數不同來創(chuàng)建不同的對象,然后該對象調用類下的成員函數來得到不同的結果。

    構造函數的名稱和類的名稱一致,但是要區(qū)分兩者的區(qū)別,構造函數是類的成員函數,雖然名稱一樣,但構造函數出現(xiàn)在成員函數應該出現(xiàn)的位置上,在程序中很容易分別。后面的程序說明中會對此進行更詳細的解釋。

    析構函數通常不需要特別關注,析構函數的作用是完成清理工作,在程序執(zhí)行到創(chuàng)建的對象作用域以外的時候,需要釋放內存。這個過程系統(tǒng)自動完成。析構函數的名稱和類名相似,只在類的名稱前面加上了一個波浪符號——“~”。

    2.訪問控制

    這里介紹下面程序中會出現(xiàn)的兩個關鍵字——public和private,這兩個關鍵字描述了對類成員的訪問控制。

    關鍵字private標識的成員(無論是數據還是成員函數)不能夠通過外界直接訪問,只能夠通過關鍵字public標識下的公有成員函數進行訪問;這樣,共有成員函數成為程序和對象的私有成員之間的橋梁,提供了對象和程序之間的接口。

    為了數據隱藏,通常數據都放在關鍵字private下,類的對外接口的成員函數放在關鍵字public下面。(C++還提供了一個關鍵字——protected,可以自行補充學習)

    ?

    接下來,以三點求圓心類為例,看一下如何實現(xiàn)這個類。

    a.我們先對CenterOfCirlce.h文件進行編寫

    ?(程序說明在后面)

    #pragma once#include<cmath>struct Point{double x;double y;};struct Line{double k; //斜率double b; //截距};struct Result{bool HasSolution; //是否有解Point StartPoint; //圓弧行進起點坐標Point EndPoint; //圓弧行進終點坐標Point CenterOfCircle; //三點確定的圓心坐標bool Direction; //圓弧行進方向(順時針還是逆時針)double Radius; //半徑長度};class CenterOfCircle{private:Point Point1,Point2,Point3; //給定的三點Point Point4_MidP1P2,Point5_MidP2P3; //兩點的中點Line Line1_P1P2,Line2_P2P3; //兩點的連線Line Line3_P1P2_Vertical,Line4_P2P3_Vertical; //兩點連線的中垂線Point Point6_CenterOfCircle; //圓心坐標bool IsSamePoint(Point,Point); //判斷點是否相同Point GetMidpoint(Point,Point); //獲取中點坐標Line GetLineOfTwoPoints(Point,Point); //獲取兩點的連線bool IsSameSlope(Line,Line); //判斷斜率是否相同Line GetMidperpendicular(Point,double); //獲取中垂線Point GetPointOfIntersection(Line,Line); //獲取倆線交點double GetDistanceOfTwoPoint(Point,Point); //獲取兩點距離bool GetDirection(Point,Point,Point); //獲取圓弧走向public:CenterOfCircle(double,double,double,double,double,double); //構造函數的6個參數分別三個點在x與y方向的坐標~CenterOfCircle(void);Result GetInformationOfCircle(); //獲取最終給定三點的結果,有無解,起點,終點,圓心,走向以及半徑};
  • 第一行代碼#pragma once防止頭文件重復編譯。

    在頭文件開頭,我們先包含一個頭文件cmath,因為后面我們會使用函數tan()以及求平方函數sqrt()進行相關的數學計算。

    結構體類型Point表示點,成員為x和y;結構體類型Line表示線,成員為斜率k和截距b。

    結構體類型Result是我們最終求得的結果,其成員分別為圓弧的起點StartPoint,終點EndPoint,三點所確定的圓心坐標CenterOfCircle,圓弧走向Direction(順時針或逆時針)以及圓的半徑Radius;

    還有一個bool型變量HasSolution,表示是否有解,因為要考慮到三點是否有兩個相同點以及三點是否在同一條直線上的問題,這兩種情況下的三個點是無法形成圓弧的。

    然后我們開始對CenterOfCircle類的成員進行聲明,class是關鍵字,表明要創(chuàng)建一個名為CenterOfCircle的類;其中,要求圓心的三個點分別為Point1,Point2,Point3

    求圓心需要先對三點的兩兩連線進行中垂線的求取,在這里我們需要用到兩點的中點,以及兩點連線,還有兩點連線線段的中垂線;

    所以,我們定義Point1與Point2的中點坐標為Point4_MidP1P2,Point2與Point3的中點坐標為Point5_MidP2P3,Point1與Point2連線為Line1_P1P2,Point2與Point3連線為Line2_P2P3,求得相應的中垂線為Line3_P1P2_VerticalLine4_P2P3_Vertical

    最后,我們求兩條中垂線的交點即我們要求的圓心坐標——Point6_CenterOfCircle。

    ?

    在這個過程中,我們需要用到一些簡單的函數,我們把這些函數實現(xiàn)在CenterOfCircle類下,作為CenterOfCircle類的私有成員函數。

    首先,我們要判斷三個點是否有兩個點相同,我們定義這個成員函數名bool IsSamePoint(Point,Point),其中兩個參數是要判斷的兩個點,兩點相同返回值為1,兩點不同返回值為0;

    Point GetMidpoint(Point,Point)用來獲取中點坐標;

    Line GetLineOfTwoPoints(Point,Point)用來獲取兩點連線;

    bool IsSameSlope(Line,Line)判斷斜率是否相同,若兩兩連線的斜率相同即表示三點共線,即無解;

    Line GetMidperpendicular(Point,double)通過斜截式來實現(xiàn)中垂線的獲取,兩個參數分別為點與斜率;

    Point GetPointOfIntersection(Line,Line)獲取倆線交點坐標;

    double GetDistanceOfTwoPoint(Point,Point)獲取兩點距離;

    通過兩點的向量的叉乘的正負來判斷圓弧走向是順時針還是逆時針,我們通過成果函數bool GetDirection(Point,Point,Point)來進行判斷,三個參數分別為給定的三個點。

    在關鍵字public標識下的公有成員函數中,構造函數中的6個參數分別是三個點在x與y方向的坐標;

    我們通過Result GetInformationOfCircle()來獲取最終給定三點的結果,結果中包括有無解,圓弧起點,圓弧終點,三點確定圓的圓心,圓弧走向以及三點確定的圓的半徑;在這個函數中,我們將用到私有的成員變量與成員函數。

    b.接下來我們開始對CenterOfCirlce.cpp文件進行編寫

    (程序說明在后面)

    #include "CenterOfCircle.h"CenterOfCircle::CenterOfCircle(double P1_x,double P1_y,double P2_x,double P2_y,double P3_x,double P3_y){Point1.x=P1_x;Point1.y=P1_y;Point2.x=P2_x;Point2.y=P2_y;Point3.x=P3_x;Point3.y=P3_y;}CenterOfCircle::~CenterOfCircle(void){}Result CenterOfCircle::GetInformationOfCircle(){Result theResult={0,{0,0},{0,0},{0,0},0,0};if(IsSamePoint(Point1,Point2)||IsSamePoint(Point2,Point3)||IsSamePoint(Point1,Point3)){return theResult;}Line1_P1P2 = GetLineOfTwoPoints(Point1,Point2);Line2_P2P3 = GetLineOfTwoPoints(Point2,Point3);if (IsSameSlope(Line1_P1P2,Line2_P2P3)==1){return theResult;}else{theResult.HasSolution=1;}Point4_MidP1P2 = GetMidpoint(Point1,Point2);Point5_MidP2P3 = GetMidpoint(Point2,Point3);Line3_P1P2_Vertical = GetMidperpendicular(Point4_MidP1P2,Line1_P1P2.k);Line4_P2P3_Vertical = GetMidperpendicular(Point5_MidP2P3,Line2_P2P3.k);theResult.CenterOfCircle = GetPointOfIntersection(Line3_P1P2_Vertical,Line4_P2P3_Vertical);theResult.StartPoint = Point1;theResult.EndPoint = Point3;theResult.Radius = GetDistanceOfTwoPoint(theResult.CenterOfCircle,Point2);theResult.Direction = GetDirection(Point1,Point2,Point3);return theResult;}bool CenterOfCircle::IsSamePoint(Point P1,Point P2){if ((P1.x==P2.x)&&(P1.y==P2.y)){return 1;}else{return 0;}}Line CenterOfCircle::GetLineOfTwoPoints(Point P1, Point P2){Line StraightLine={0,0};StraightLine.k = (P2.y - P1.y)/ (P2.x -P1.x);StraightLine.b = P1.y - P1.x * StraightLine.k ;return StraightLine;}bool CenterOfCircle::IsSameSlope(Line Line1,Line Line2){if (Line1.k==Line2.k){return 1;}else{return 0;}}Point CenterOfCircle::GetMidpoint(Point P1, Point P2){Point Midpoint={0,0};Midpoint.x = (P1.x + P2.x )/2;Midpoint.y = (P1.y + P2.y )/2;return Midpoint;}Line CenterOfCircle::GetMidperpendicular(Point P,double k){Line StraightLine={0,0};StraightLine.k = -1/k;StraightLine.b = P.y - StraightLine.k * P.x ;return StraightLine;}Point CenterOfCircle::GetPointOfIntersection(Line Line1,Line Line2){Point PointOfIntersection={0,0};PointOfIntersection.x = (Line2.b-Line1.b)/(Line1.k-Line2.k);PointOfIntersection.y = (Line2.k*Line1.b-Line1.k*Line2.b)/(Line2.k-Line1.k);return PointOfIntersection;}double CenterOfCircle::GetDistanceOfTwoPoint(Point P1,Point P2){double DistanceOfTwoPoint=0;DistanceOfTwoPoint=sqrt((P1.x-P2.x)*(P1.x-P2.x)+(P1.y-P2.y)*(P1.y-P2.y));return DistanceOfTwoPoint;}bool CenterOfCircle::GetDirection(Point P1,Point P2,Point P3){int CrossProduct=0;bool Direction;CrossProduct=(P2.x-P1.x)*(P3.y-P2.y)-(P2.y-P1.y)*(P3.x-P2.x);if(CrossProduct<0){Direction=1; //clockwise}else{Direction=0; //anticlockwise}return Direction;}
  • 在我們創(chuàng)建CenterOfCircle類的時候,VS2010自動給我們生成了一部分代碼,在CenterOfCircle.cpp中,這寫代碼分別是#include "CenterOfCircle.h",構造函數及析構函數。

    我們對構造函數進行改動,如CenterOfCircle.h文件中我們聲明的構造函數一樣,我們對CenterOfCircle(double P1_x,double P1_y,double P2_x,double P2_y,double P3_x,double P3_y)函數的參數設置為P1_x,P1_y,P2_x,P2_y,P3_x,P3_y,六個形參依次代表是三個點。

    在構造函數內部我們將形參依次賦值給我們頭文件聲明過得私有化變量Point1.x,Point1.y,Point2.x,Point2.y,Point3.x,Point3.y,來完成對象的創(chuàng)建;如我們前面學習的構造函數一樣,構造函數用來創(chuàng)建對象,創(chuàng)建后的對象可以調用GetInformationOfCircle()函數,獲取該對象對應的解的信息——有無解,圓弧起點,圓弧終點,三點確定圓的圓心,圓弧走向以及三點確定的圓的半徑。

    析構函數我們不需要進行改動。

    接下來我們將對這類最核心的功能進行實現(xiàn),首先我們要實現(xiàn)這個函數,按照C++編程規(guī) 范,我們在先寫這個函數求得結果的返回值類型,在頭文件當中,我們定義結構體Result,我們求得的結果就放在這個結構體當中;然空格隔開,注意寫上類名,表示是這個類下的函數,然后兩個英文輸入法狀態(tài)下的冒號,最后我們把這個函數的名稱寫上GetInformationOfCircle(),由于對象創(chuàng)建由構造函數完成,我們這里可以直接使用構造函數創(chuàng)建對象對應的三個點Point1,Point2,Point3進行求解即可,所以不需要參數。

    進入到大括號內開始對功能進行實現(xiàn),首先我們先對所求結果的Result結構體創(chuàng)建對象theResult,并且賦初值——{0,{0,0},{0,0},{0,0},0,0};

    參考頭文件對Result結構體的定義,我們能夠看到bool型變量HasSolution,即是否有解,我們初始化為0,即無解;第二個參數和第三個參數分別為圓弧行進起點坐標與終點坐標StartPoint和EndPoint;第四個參數CenterOfCircle,即三點確定的圓心坐標;第五個參數是圓弧行進的方向,bool型變量Direction,后面我們將順時針方向給值為1,逆時針給值為0;最后一個參數為三點確定圓弧的半徑長度Radius;

    在對三個點進行處理前,我們應該首先判斷兩件事情,1.輸入的三個點是否有相同的點;2.這三個點是否在同一條直線上;以上兩種情況無法求得相應結果。

    1.判斷兩點是否相同的函數IsSamePoint的實現(xiàn)

    判斷點與點是否相同我們通過一個函數IsSamePoint實現(xiàn),然后通過調用這個函數來判斷Point1,Point2,Point3兩兩是否相同。

    以下是IsSamePoint函數的實現(xiàn),返回值為bool型,返回值為1代表這兩個點相同,返回值為0代表這兩個點不相同。兩個參數都是Point結構體類型,該結構在頭文件已定義過了,可以參考頭文件中Point結構體的定義。

    在函數內部我們通過if語句判斷實現(xiàn)了兩點是否相同的判定,相同我們返回值為1,否則將返回0。

    2.判斷三個點是否在同一條直線上函數IsSameSlope的實現(xiàn)

    判斷三點是否共線實現(xiàn)的思路分為兩步:

    a)我們將Point1與Point2,Point2與Point3連接起來,得到著兩條直線Line1_P1P2和Line2_P2P3;我們通過斜截式來表示直線。我們通過結構體Line來定義直線,Line結構體的兩個成員分別為斜率k與截距b。

    ?

    b)然后對這兩條直線的斜率進行判定是否相等來得出三點是否共線

    與函數IsSamePoint類似,我們定義一個返回值為bool型的IsSameSlope函數,參數為兩個結構體類型Line,通過對兩個形參的Line1與Line2的斜率k進行判斷是否相等,最終得出三點是否共線。

    ?

    回到CenterOfCircle.cpp中來,第22行到第38行代碼判斷了三點中是否有兩點相同以及三點是否共線,如果三點中沒有兩個相同的點,并且也三點不共線,則有解,我們將theResult的成員HasSolution賦值為1。

    接下來開始對三點所確定圓的圓心進行求解,我們分三步進行:

    1.求取Point1與Point2、Point2與Point3的中點坐標;

    2.求Point1與Point2所在直線Line1_P1P2的中垂線Line3_P1P2_Vertical,以及Point2與Point3所在直線Line_P2P3的中垂線Line4_P2P3_Vertical;

    回憶初高中數學知識,求中垂線,我們使用斜截式進行求解。在函數GetMidperpendicular中,參數定義了兩個,分別為Point結構體變量P,以及double型變量k;這兩個參數分別代表所求線段的中點與斜率。

    3.求兩條中垂線的交點,即圓心CenterOfCircle坐標;

    兩條中垂線獲得后我們可以通過解二元一次方程來求得交點,也就是我們最終要求的CenterOfCircle。

    通過調用這些函數,在CenterOfCircle.cpp程序的39至46行實現(xiàn)了圓心坐標的求取;然后將Point1與Point2分別賦值給圓弧行進的起始點StartPoint與終點EndPoint。

    半徑的求取我們再次通過一個函數GetDistanceOfTwoPoint來實現(xiàn),在程序的49行,我通過計算兩點theResult.CenterOfCircle與Point2得出了半徑長度。這里用到了開平方的函數sqrt,這也是為什么我們在頭文件中要包含cmath的原因。

    最后,還需要確定圓弧的走向。關于圓弧走向的數學證明這里不再累述,具體數學計算方法如下:設=(x1,y1),=(x2,y2),=(x3,y3),向量與的向量積(x2-x1)*(y3-y2)-(y2-y1)*(x3-x2)為正時,走向為逆時針;為負時,走向為順時針;由于三點不在一條直線上,所以兩向量叉乘不會為0。

    至此,我們所求結果result結構體對應的所有成員都有了值。我們寫一個main.cpp來測試以下我們寫的CenterOfCircle類。

    c.通過main.cpp文件進行三點求圓心類的測試

    #include "CenterOfCircle.h"#include <iostream>using namespace std;int main(){CenterOfCircle COC(0,0,5,5,10,0);Result R1;R1=COC.GetInformationOfCircle();cout<<std::fixed<<R1.CenterOfCircle.x<<endl;cout<<std::fixed<<R1.CenterOfCircle.y<<endl;cout<<R1.Direction<<endl;cout<<R1.Radius<<endl;system("pause");return 0;}
  • 第1行代碼我們將CenterOfCircle.h包含進來,相當于我們能夠使用CenterOfCircle類的功能;?進入主函數里面,我們使用CenterOfCircle類創(chuàng)建一個名為COC的對象,六個參數為三個點的x坐標與y坐標;

    第7行定義一個名為R1的Result結構體變量,通過調用COC對象下的成員函數GetInformationOfCircle(),并將所得結果傳遞給R1,我們得到了點(0,0)、點(5,5)與點(10,0)確定的圓的相應信息——有無解,圓弧起點,圓弧終點,三點確定圓的圓心,圓弧走向以及三點確定的圓的半徑;

    第9行至12行我們使用cout函數將相應的結果輸出出來。通過使用std::fixed我們禁用科學計數法,大家可以試一試對比下有無std::fixed最終結果顯示的區(qū)別。

    思考:如果細心的同學會發(fā)現(xiàn),我們對于有些情況沒有考慮進去。例如求解以下三個點(0,5),(5,5),(5,0)時,最終輸出的結果并不能輸出正確的結果。這是由于我們少考慮了兩種情況:1.兩點的連線平行于x軸;2.兩點連線垂直于y軸。如何完善這個類,將以上兩種情況考慮進去,進行正確的計算輸出呢?可參考7.2三點求圓心這一節(jié)的內容進行程序的完善。

    7.3.3?編程練習:逐點比較法直線插補類

    這一小節(jié)我們對第五章的逐點比較法直線插補封裝成類,以進一步加強對函數和類的掌握。創(chuàng)建項目后,添加類名為Interpolator的類,頭文件Interpolator.h程序如表7-5所示。

  • ?

    #pragma once #include <cmath> #include <iostream> using namespace std; class Interpolator { private: int m_xs; int m_ys; int m_xi; int m_yi; int m_xe; int m_ye; int m_Nxy; int m_XieLvChaZhi; int m_i; public: Interpolator(int,int,int,int); ~Interpolator(void); void DoInterpolator(); };
  • 表7-5?頭文件Interpolator.h程序

    將所有的數據(變量)放在關鍵字private下,構造函數設置了4個int型參數,分別代表插補的起始點和終點。

    源文件Interpolator.cpp中的程序如表7-6所示。

    #include "Interpolator.h"Interpolator::Interpolator(int xs,int ys,int xe,int ye) {m_xs=xs;m_ys=ys;m_xe=xe;m_ye=ye;cout<<"起點為"<<m_xs<<","<<m_ys<<endl;cout<<"終點為"<<m_xe<<","<<m_ye<<endl; }Interpolator::~Interpolator(void) { }void Interpolator::DoInterpolator() {m_Nxy =abs( (m_xe - m_xs)) + abs((m_ye - m_ys));m_i=1;m_xi = m_xs;m_yi = m_ys;for(int i=0;i<m_Nxy;i++){m_XieLvChaZhi =(m_xe-m_xs)*(m_yi-m_ys)-(m_xi-m_xs)*(m_ye-m_ys);if(m_xe-m_xs==0){if (m_ye-m_ys>0){m_yi++;}else{m_yi--;}cout<<m_xi<<","<<m_yi<<endl;}if(m_ye-m_ys==0){if (m_xe-m_xs>0){m_xi++;}else{m_xi--;}cout<<m_xi<<","<<m_yi<<endl;}if ((m_xe-m_xs>0)&&(m_ye-m_ys>0)){if (m_XieLvChaZhi>=0){m_xi++;} else{m_yi++;}cout<<m_xi<<","<<m_yi<<endl;}if ((m_xe-m_xs<0)&&(m_ye-m_ys>0)){if (m_XieLvChaZhi<=0){m_xi--;} else{m_yi++;}cout<<m_xi<<","<<m_yi<<endl;}if ((m_xe-m_xs<0)&&(m_ye-m_ys<0)){if (m_XieLvChaZhi<=0){m_yi--;} else{m_xi--;}cout<<m_xi<<","<<m_yi<<endl;}if ((m_xe-m_xs>0)&&(m_ye-m_ys<0)){if (m_XieLvChaZhi>=0){m_yi--;} else{m_xi++;}cout<<m_xi<<","<<m_yi<<endl;}}cout<<"********************************************"<<endl; }

    表7-6?源文件Interpolator.cpp程序

    首先編寫構造函數,通過構造函數的四個參數(即起點坐標與中點坐標)來構造不同的對象,并且創(chuàng)建對象時使用輸出流對象cout進行起始點以及終點的顯示(10行至11行)。

    成員函數DoInterpolator實現(xiàn)了過程點的計算以及輸出顯示,該部分代碼原理在第5章已經有講述,這里不再累述。

    接下來,就可以通過在“源文件”中添加主函數文件進行類的測試。程序如表7-7所示。

    #include "Interpolator.h"int main() {Interpolator chabu1(1,1,5,5);chabu1.DoInterpolator();Interpolator chabu2(2,3,-4,2);chabu2.DoInterpolator();system("pause");return 0; }

    表7-7?源文件main.cpp程序

    可以看到測試程序里面,分別使用Interpolator類創(chuàng)建了兩個對象——chabu1(第5行代碼,參數表示起點坐標為1,1點,終點為5,5點)以及chabu2(第7行代碼,參數表示起點坐標為2,3點,終點為-4,2點)。

    程序測試結果如圖7-10所示。

    ?

    圖7-10?逐點比較法直線插補類測試結果示意圖

    7.4?計算器小軟件

    前面我們學習了大量的C++語言的基礎知識,也通過很多練習加強了對該語言應用的掌握。但大家似乎會疑問,為什么我們最終的程序實現(xiàn)結果是一個黑乎乎的界面,一點都不美觀,感覺和我們常用的軟件相差甚遠。7.4與7.5兩節(jié)我們將開始對Qt進行應用,做兩個小軟件。當然,我們還是采取由簡至難的策略。這一節(jié)我們基于Qt先實現(xiàn)一個簡易的計算器小軟件,該軟件能夠實現(xiàn)對兩個輸入的數字進行加減乘除運算。

    7.4.1 QT APPLICATION項目創(chuàng)建

    接下來需要我們先創(chuàng)建一個Qt Application項目。打開VS2010,依次點擊“文件”、“新建”、“項目”,然后在“新建項目”界面下,點擊“Qt5 Projects”,并點擊選擇該項目下的“Qt Application”,更改項目名稱為“Calculator”,項目名稱和接下來系統(tǒng)默認創(chuàng)建的程序相關,請不要寫錯。如圖7-11所示。

    ?

    圖7-11 Qt Application項目創(chuàng)建示意圖

    然后會自動出現(xiàn)Qt5 GUI Project Wizard窗口,該窗口可以設置項目所需要的組件,我們忽略設置步驟,直接點擊“Finish”即可,如圖7-12所示。

    ?

    圖7-12 Qt5 GUI Project Wizard窗口

    其后,VS2010將自動創(chuàng)建一個Qt Application項目,并且自帶了一部分文件以及程序。按下F5,編譯并運行,顯示一個什么都沒有的窗口,完成第一步項目創(chuàng)建工作。

    7.4.2 UI界面設計

    前面我們已經寫了很多的程序,這里我們將接觸一個全新的編程模式:使用Qt Designer將界面畫出來,然后自動生成對應界面的代碼;當然我們可以不用管生成后的代碼是什么樣的,也可以很好的將目標軟件實現(xiàn)出來。

    雙擊“Calculator”項目下的“calculator.ui”文件,如圖7-13所示。

    ?

    圖7-13 Calculator項目文件示意圖

    系統(tǒng)將自動調用Qt Designer進行界面設計。Qt Designer能夠快速設置窗口需要的組件以及組件的相關屬性,可以說是以“畫圖”的方式來進行軟件設計。其界面如圖7-14所示。

    ?

    ?

    圖7-14 Qt Designer界面示意圖

    Qt Designer上方如其他軟件類似是菜單欄和工具欄,需要特別注意的是最左側的“Widget Box”以及中間部分標題為“Calculator - calculator.ui”的窗口,“Widget Box”里面是軟件需要的一些組件,包括我們經常接觸到的按鈕、標簽、文本框等等。我們直接點擊鼠標不松將需要的組件拖入我們的“Calculator - calculator.ui”窗口當中。我們需要兩個能夠輸入數字并能夠顯示的組件,我用在“Widget Box”中查找,可找到Double Spin Box組件,我們將其拖出,放在我們的“Calculator - calculator.ui”窗口當中;然后找到Push Button組件,拖入四個到“Calculator - calculator.ui”窗口當中;如圖7-15所示。

    ?

    圖7-15?包含三個Double Spin Box以及四個Push Button的窗口示意圖

    我們將對最左側的兩個Double Spin Box進行相應數字的輸入,然后四個Push Button分別代表對這兩個數字的加、減、乘、除計算功能執(zhí)行,右側的Double Spin Box將兩個數字對應的計算結果顯示出來。

    那么,我們程序里面怎么識別我們點擊了哪一個按鈕呢?

    我們把目光轉向圖??的右側的“屬性編輯器”,當點擊組件的時候,右側的“屬性編輯器”將出現(xiàn)組件對應的屬性,其中最重要的就是“objectNmae”,這個是我們寫程序時所需要用到的名稱,以此跟界面連接起來,實現(xiàn)圖形交互。我們點擊圖??最上面的那個“PushButton”,將“objectName”由默認的名稱改為“ButtonAdd”,如圖7-16所示。

    ?

    圖7-16 UI文件按鈕名稱更改示意圖

    其后分別將其他三個按鈕的“objectName”屬性改為“ButtonReduce”、“ButtonMutiply”、“ButtonDivide”;將三個“Double Spin Box”組件的“objectName”屬性改為“DoubleBoxNumber1”、“DoubleBoxNumber2”、“DoubleBoxNumber3”,同時,我們將三個“Double Spin Box”組件的“decimals”屬性改為“6”,這樣該組件就能顯示小數點后6位的數字,同時將“minimum”和“maximum”分別改為-10000和10000,這樣基本上能夠滿足我們的數字顯示范圍(這三個屬性在該組件的屬性編輯器較下面的位置)。

    由于界面顯示了四個相同Push Button,雙擊Push Button組件,我們將顯示的內容改為“+”、“-”、“*”、“/”,如圖7-17所示。

    ?

    圖7-17?已完成的簡易計算器界面圖

    我們會發(fā)現(xiàn)在標題多了一個星號——“Calculator - calculator.ui*”,這是因為我們改動了界面但沒有保存的緣故,我們依次點擊菜單欄的“文件”,“保存”,將完成的界面保存起來。到此為止,ui界面的設計已經完成,接下來我們要編寫程序,來實現(xiàn)計算功能。

    7.4.3?計算功能程序實現(xiàn)

    在創(chuàng)建Qt Application項目后,會自動生成一部分文件與相應程序。我們可以直接在系統(tǒng)已給定的程序基礎上進行程序的編寫。我們依舊先對頭文件進行編寫,如表7-8所示,加下劃線的代碼是添加或修改的。

    #ifndef CALCULATOR_H #define CALCULATOR_H#include <QtWidgets/QMainWindow> #include "ui_calculator.h"class Calculator : public QMainWindow {Q_OBJECTpublic:Calculator(QWidget *parent = 0);~Calculator();private slots:void Add();void Reduce();void Mutiply();void Divide();private:Ui::CalculatorClass ui;double m_Number1;double m_Number2;double m_Number3; };#endif // CALCULATOR_H

    表7-8 calculator.h程序

    首先,我們看1、2、28行的代碼,這三行是一體的,該語句為了防止頭文件重復編譯,類似于#pragma once,第4、5行包含了兩個頭文件,第一個頭文件,聲明我們要使用QMainWindow類,也就是說我們創(chuàng)建的Calculator類是一個能夠實現(xiàn)主窗口的類;第二個頭文件ui_calculator.h?表示我們包含了一個界面文件,也就是我們上文所說的Qt Designer中設計的界面文件編譯后的文件。其中,第7行代碼表明創(chuàng)建了類Calculator,并繼承了QMainWindow的功能,類的繼承我們會在第八章進行講解,這里我們先跳過去;Q_OBJECT為宏,類似于我們學過的?using namespace std;?該語句使用后,方便我們后面會使用connect函數(后面會講);接下來定義了兩個公共函數,分別是構造函數和析構函數。

    接下來我們看這一節(jié)最重要的部分——信號和槽機制。信號和槽機制是?Qt?的核心機制,可以讓編程人員將互不相關的對象綁定在一起,實現(xiàn)對象之間的通信。舉例來說,當用戶點擊我們設計的簡易計算器的“+”按鈕時,軟件內部將執(zhí)行加法功能,而用戶點擊這一行為發(fā)出的就是一個信號,該信號對應的要實現(xiàn)的功能即為槽。所有的槽函數需要在頭文件中單獨在“private slots:”語句下進行聲明。四個函數分別對應接下來要編寫的源文件中的加減乘除四個功能。

    我們需要對兩個數進行計算,所有我們聲明兩個私有變量,以及一個計算后的私有變量,對應程序的23至25行。對于22行代碼,大家可以簡單理解為我們前面所設計的整個界面,后面使用界面里面的組件的時候,需要先輸入22行代碼中所命的名字ui。

    接下來我們進行源文件的編寫,程序如表7-9所示,加粗部分為添加的代碼部分。

    #include "calculator.h"Calculator::Calculator(QWidget *parent) : QMainWindow(parent) {ui.setupUi(this);connect(ui.ButtonAdd,SIGNAL(clicked()),this,SLOT(Add()));connect(ui.ButtonReduce,SIGNAL(clicked()),this,SLOT(Reduce()));connect(ui.ButtonMutiply,SIGNAL(clicked()),this,SLOT(Mutiply()));connect(ui.ButtonDivide,SIGNAL(clicked()),this,SLOT(Divide())); }Calculator::~Calculator() { }void Calculator::Add() {m_Number1=ui.DoubleBoxNumber1->value();m_Number2=ui.DoubleBoxNumber2->value();m_Number3=m_Number1+m_Number2;ui.DoubleBoxNumber3->setValue(m_Number3); }void Calculator::Reduce() {m_Number1=ui.DoubleBoxNumber1->value();m_Number2=ui.DoubleBoxNumber2->value();m_Number3=m_Number1-m_Number2;ui.DoubleBoxNumber3->setValue(m_Number3); }void Calculator::Mutiply() {m_Number1=ui.DoubleBoxNumber1->value();m_Number2=ui.DoubleBoxNumber2->value();m_Number3=m_Number1*m_Number2;ui.DoubleBoxNumber3->setValue(m_Number3); }void Calculator::Divide() {m_Number1=ui.DoubleBoxNumber1->value();m_Number2=ui.DoubleBoxNumber2->value();m_Number3=m_Number1/m_Number2;ui.DoubleBoxNumber3->setValue(m_Number3); }

    表7-9 calculator.cpp程序

    第1行代碼包含相應的頭文件,第3、4、6、13行代碼為構造函數以及析構函數的相關代碼,這部分默認給定的代碼我們通常情況下無需修改。

    我們重點看第7行至第10行,前面我們已經提到了信號和槽機制,這里我們將兩者相連,實現(xiàn)該功能的函數尉connect函數,很容易找到該函數的使用規(guī)則,該函數的共有四個參數,其中第一個為界面中的組件,第二個參數對應于該組件的信號類型,第三個為this,第四個為對應的槽函數。如果想深入了解Qt、信號與槽機制、connect函數等可以學習Qt的幫助文檔,學會看幫助文檔是一個程序員必備的素質。

    第17行至第23行我們實現(xiàn)了加法功能,通過Double Spin Box組件的成員函數value()來獲取DoubleBoxNumber1以及DoubleBoxNumber2值,m_Number1以及m_Number2分別得到到了兩個組件的值后,相加后賦值給m_Number3,,最后使用函數setvalue()將變量m_Number3的值賦給組件DoubleBoxNumber3,并顯示出來。

    我們能夠看到無論是value()函數還是setValue()函數前面都使用了“->”符號,而不是“.”;因為Qt在編譯界面文件的時候,自動將我們所用的所有組件以指針的形式進行創(chuàng)建(大家可以在Calcultor項目下的Generated Files文件夾中ui_calculator.h文件找到),所以ui.DoubleBoxNumber1、ui.DoubleBoxNumber2、ui.DoubleBoxNumber3都是指向類的指針,后面調用成員函數的時候用“->”符號。

    其余三個函數與加法函數沒有太大的區(qū)別,在此不多做解釋。

    最后還是下一個main.cpp文件,這個文件中的程序我們無需改動,具體的程序意義不再累述,因為Qt自身就是一個很龐大的軟件項目,有余力或有興趣的同學可以自行深入地去學習,這些年Qt因為自身的開源、免費、跨平臺特性越來越受到開發(fā)者的熱愛,并且應用也越來越廣泛。

    到這里我們就可以按下鍵盤上的F5鍵,我們就可以測試下自己編寫的簡易計算器是否能夠如愿工作。如圖7-18所示,我們分別輸入“123”和“456”,然后按下按鈕“/”,得到結果為“0.269737”。

    ?

    圖7-18?簡易計算器測試結果圖

    7.5?編程練習:直線插補模擬小軟件

    通過使用Qt完成一個簡易計算器,讓我們對軟件的用戶交互界面實現(xiàn)有一個初步的認識。這一節(jié)我們完成一個直線插補模擬小軟件。

    首先,創(chuàng)建一個名為“Interpolator”的Qt Application項目;雙擊“Interpolator”項目下的“interpolator.ui”文件,開始設計直線插補模擬小軟件的交互界面,如圖7-19所示,我們這一次學習兩個新的組件——Label、Text Browser。我們拖入兩個Label組件,并將顯示內容改為“Start Point:”和“End Point:”,Label組件的主要功能就是在界面上進行提示性顯示,所以對于“objectName”以及其他屬性都無需更改;然后分別在兩個Label組件后面各添加兩個Spin Box組件,Spin Box組件和Double Spin Box的區(qū)別在于前者所能夠輸入輸出的數字為整數,后者則為帶小數的數字,分別將兩組Spin Box組件改名為“spinBox_xs”。放置一個Push Button組件,點擊該按鈕將觸發(fā)對應的直線插補功能,并將過程點輸出在我們Text Browser組件中,我們將Push Button的“objectName”改為“ButtonDoInterpolator”,Text Browser的“objectName”保持“textBrowser”不變。后面我們依舊在程序中使用這些組件的名字來實現(xiàn)界面交互功能。

    ?

    圖7-19?直線插補模擬小軟件示意圖

    然后,我們開始完成頭文件“interpolator.h”以及源文件“interpolator.cpp”,主程序文件“main.cpp”依舊不需要改動。經過了逐點比較法直線插補類以及計算器小軟件的訓練,大家可以自行嘗試寫一寫程序,然后參考下面給出的程序內容進行比對。頭文件“Interpolator.h”程序如表7-10所示。

    #ifndef INTERPOLATOR_H #define INTERPOLATOR_H#include <QtWidgets/QMainWindow> #include "ui_interpolator.h"class Interpolator : public QMainWindow {Q_OBJECTpublic:Interpolator(QWidget *parent = 0);~Interpolator();private slots:void DoInterpolator();private:Ui::InterpolatorClass ui;int m_xs;int m_ys;int m_xe;int m_ye; };#endif // INTERPOLATOR_H

    表7-10 Interpolator.h程序?

    程序中我們只在原有程序的基礎上增加了一個槽函數以及四個int型變量,四個int型變量代表起點和終點坐標。

    然后是源文件Interpolator.cpp,如表7-11所示。在程序中,加入一個connect函數,將按鈕與為一個槽函數DoInterpolator()相連接;第16行代碼比表示清除掉Text Browser中的內容,然后接下來四行將Spin Box組件中的數值提取出來賦值給頭文件中聲明的四個int型變量——m_xs,m_ys,m_xe,m_ye;剩下的大多數代碼已經多次學習,第39至第42行代碼實現(xiàn)了過程點在界面中Text Browser組件中的顯示,Qt的QString類提供了很方便的對字符串操作的接口,注意QString類與我們前面學到的輸入輸出流iostream類的區(qū)別;調用Text Browser組件下的append()函數即可實現(xiàn),對該函數括號中參數(參數類型為QString型)的顯示。想進一步了解Text Browser組件的其他成員函數以及用法可以查看Qt的幫助文檔。

    #include "interpolator.h"Interpolator::Interpolator(QWidget *parent): QMainWindow(parent) {ui.setupUi(this);connect(ui.ButtonDoInterpolator,SIGNAL(clicked()),this,SLOT(DoInterpolator())); }Interpolator::~Interpolator() { }void Interpolator::DoInterpolator() {ui.textBrowser->clear();m_xs=ui.spinBox_xs->value();m_ys=ui.spinBox_ys->value();m_xe=ui.spinBox_xe->value();m_ye=ui.spinBox_ye->value();int Nxy,xi,yi,FM;xi = m_xs;yi = m_ys;Nxy =abs( (m_xe - m_xs)) + abs((m_ye - m_ys));for(int i=0;i<Nxy;i++){FM =(m_xe-m_xs)*(yi-m_ys)-(xi-m_xs)*(m_ye-m_ys);if(m_xe-m_xs==0){if (m_ye-m_ys>0){yi++;}else{yi--;}QString str1 = QString::number(xi,'g',6);QString str2 = QString::number(yi,'g',6);QString str3 = str1+','+str2;ui.textBrowser->append(str3);}if(m_ye-m_ys==0){if (m_xe-m_xs>0){xi++;}else{xi--;}QString str1 = QString::number(xi,'g',6);QString str2 = QString::number(yi,'g',6);QString str3 = str1+','+str2;ui.textBrowser->append(str3);}if ((m_xe-m_xs>0)&&(m_ye-m_ys>0)){if (FM>=0){xi++;} else{yi++;}QString str1 = QString::number(xi,'g',6);QString str2 = QString::number(yi,'g',6);QString str3 = str1+','+str2;ui.textBrowser->append(str3);}if ((m_xe-m_xs<0)&&(m_ye-m_ys>0)){if (FM<=0){xi--;} else{yi++;}QString str1 = QString::number(xi,'g',6);QString str2 = QString::number(yi,'g',6);QString str3 = str1+','+str2;ui.textBrowser->append(str3);}if ((m_xe-m_xs<0)&(m_ye-m_ys<0)){if (FM<=0){yi--;} else{xi--;}QString str1 = QString::number(xi,'g',6);QString str2 = QString::number(yi,'g',6);QString str3 = str1+','+str2;ui.textBrowser->append(str3);}if ((m_xe-m_xs>0)&(m_ye-m_ys<0)){if (FM>=0){yi--;} else{xi++;}QString str1 = QString::number(xi,'g',6);QString str2 = QString::number(yi,'g',6);QString str3 = str1+','+str2;ui.textBrowser->append(str3);}} }

    表7-11 Interpolator.cpp程序

    主程序文件“main.cpp”依舊無需改動,此時點擊鍵盤F5編譯并執(zhí)行程序,我們以起始點(12,34)終點(56,78)為例,測試一下輸出的結果,如圖7-20所示。

    ?

    圖7-20?直線插補模擬小軟件測試結果圖

    版權聲明:本文為qq_36552550原創(chuàng)文章,遵循?CC 4.0 BY-SA?版權協(xié)議,轉載請附上原文出處鏈接和本聲明。

    本文鏈接:七、函數與類——Qt快速入門、三點求圓心實現(xiàn)詳解_Ce Ma的博客-CSDN博客

    總結

    以上是生活随笔為你收集整理的QT快速入门、三点求圆心实现详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。