- 相關(guān)推薦
酒杯上的碟
交叉平臺(tái)控件(Cross-platform Controls)從windows到Linux,或者相反
Borland處在一個(gè)令人興奮的時(shí)期.并不是自從delphi--這個(gè)Borland的令人興奮的產(chǎn)品的第一聲.我正在談?wù)摰漠?dāng)然是關(guān)于Kylix,這個(gè)把C++Builder和Delphi帶到Linux操作系統(tǒng)的項(xiàng)目.Delphi版本將首先面世,所以在本文余下部分,Kylix指的是Delphi for Linux.
我們正在為Delphi開(kāi)發(fā)嶄新的能夠在Windows和Linux下工作的VCL.這意味著你可以在windows下寫(xiě)一個(gè)應(yīng)用程序,然后把源代碼轉(zhuǎn)移到Linux下面重新編譯--反之亦然.這個(gè)新的VCL叫CLX,意即"交叉平臺(tái)控件庫(kù)(Component Library Cross-Platform)",CLX包含整個(gè)隨Kylix發(fā)布的交叉平臺(tái)庫(kù).在我寫(xiě)本文時(shí)它被分為下面四個(gè)子類(lèi):
BaseCLX就是RTL,包含并且升級(jí)了Classes.pas
VisualCLX包含了用戶界面類(lèi),比如常用的控件
DataCLX包含交叉平臺(tái)的數(shù)據(jù)庫(kù)控件
NetCLX包含Internet部分,比如Apache等等..
在我寫(xiě)這篇文章的時(shí)候(2000年5月之前),Kylix的第一部分測(cè)試已經(jīng)正在進(jìn)行了.當(dāng)你讀到這篇文章的時(shí)候,我正在使用的Kylix和你將要看到的正式版本將會(huì)有很大不同.這為我的工作帶來(lái)很多不便.是簡(jiǎn)單地談一談便罷?還是涉及一下底層的結(jié)構(gòu)?我更傾向于詳細(xì)的討論,這樣無(wú)論如何你能得到一點(diǎn)關(guān)于CLX控件構(gòu)造的頭緒.但是要牢記一點(diǎn): 當(dāng)你閱讀此文的時(shí)候,很可能這篇文章中很多細(xì)節(jié)的討論已經(jīng)改變了.
沒(méi)有更接近的了(No One Else Comes Close)
這篇文章是關(guān)于寫(xiě)定制VisualCLX控件的初級(jí)讀本.從本質(zhì)上說(shuō),VisualCLX就是你所知道并熱愛(ài)的VCL.當(dāng)你這樣認(rèn)為的時(shí)候,"可視構(gòu)件庫(kù)"(Visual Component Library)有一點(diǎn)用詞不當(dāng):還有比"可視構(gòu)件"更多的東西.但是在這篇文章里面,我只談?wù)?可視"控件.類(lèi)似Button, Edit, ListBox, PageControl, StatusBar, ProgressBar等等的控件,都已經(jīng)在交叉平臺(tái)下重新實(shí)現(xiàn).但是目前的VCL如此依賴Windows,我們是怎么做到這些的呢?簡(jiǎn)單地說(shuō),我們剝離了所有的Windows元素,然后把它們用別的工具包(toolkit)代替.
在Linux下,有大量的工具包包含標(biāo)準(zhǔn)windows控件(如Buttons).它們被稱做"widgets".其中GTK和Qt(被發(fā)音成"cute")就是兩個(gè)非常流行的.Qt是一個(gè)工作在windows和Linux下的widgets,因?yàn)樗浅=咏覀兊哪繕?biāo),所以Qt被選擇作為CLX的基礎(chǔ).換句話說(shuō),Qt和CLX就好像Windows API/通用控件和VCL的關(guān)系.對(duì)于Linux下的Delphi的定制構(gòu)件開(kāi)發(fā)者來(lái)說(shuō),Qt有一些明顯的好處:
它是一個(gè)廣泛使用的Linux下的widgets集,被流行的KDE桌面采用.
它的開(kāi)發(fā)和Windows API風(fēng)格非常相似
它的圖形模塊和VCL的圖形模塊相似
它的類(lèi)看上去非常像VCL控件
它引入大量標(biāo)準(zhǔn)widgets,并且具有消息循環(huán)
這將引發(fā)兩個(gè)疑問(wèn):是否這意味著Kylix只支持KDE,而不支持其他的桌面(desktop)?比如Gnome?并且,以Qt為基礎(chǔ)的CLX會(huì)給我?guī)?lái)多大影響? 第一個(gè)問(wèn)題的回答是:kylix應(yīng)用程序?qū)⑦\(yùn)行在所有Linux桌面下,特別是Gnome和KDE. 本文的余下部分將回答第二個(gè)問(wèn)題.
不讓你返回(????)(Don't Want You Back)
我們的目標(biāo)是讓開(kāi)發(fā)者容易地將應(yīng)用程序轉(zhuǎn)移到linux下,并且困難要最小化.大部分(新舊控件)的名字都是一樣的,大部分的屬性也是一樣的.盡管有一些控件的少數(shù)屬性去掉了,增加了一些新的屬性,但對(duì)于絕大部分來(lái)說(shuō),應(yīng)該可以平穩(wěn)的轉(zhuǎn)移你的應(yīng)用程序.
對(duì)控件作者來(lái)說(shuō)有一些不同. 對(duì)于一個(gè)新手,現(xiàn)在沒(méi)有Windows.pas了,也沒(méi)有Windows API了.你可以對(duì)message標(biāo)識(shí)和所有CN,CM通知(notifications)說(shuō)再見(jiàn)了.這些都轉(zhuǎn)換成了動(dòng)態(tài)的(dynamics)(???).在第一版中也不再有dock,BiDi相關(guān)的方法/屬性,輸入法(IME),遠(yuǎn)東語(yǔ)言支持了.當(dāng)然,更不會(huì)有ActiveX,COM或者OLE支持,Windows 3.1控件也去掉了.
Methods
CreateParams
CreateSubClass
CreateWindowHandle
CreateWnd
DestroyWindowHandle
DestroyWnd
DoAddDockClient
DockOver
DoDockOver
DoRemoveDockClient
DoUnDock
GetDeviceContext
MainWndProc
ResetIme
ResetImeComposition
SetIme
SetImeCompositionWindow
WndProc
Properties
Ctl3D
DefWndProc
DockManager
DockSite
ImeMode
ImeName
ParentCtl3D
UseDockManager
WheelAccumulator
附圖1: 從TWidgetControl(和TWinControl相類(lèi)似)里面去掉的Methods和properties.
此刻我打賭你正在想:"還不壞.轉(zhuǎn)移我的應(yīng)用程序聽(tīng)上去不是很難",但是請(qǐng)等等----還有更多的.在寫(xiě)此文的時(shí)候,CLX類(lèi)的名字都被加上了一個(gè)"Q"的前綴,比如StdCtrls變成了QStdCtrls,有些類(lèi)被稍微攪亂了一點(diǎn),在類(lèi)繼承上面只有一些細(xì)微差別.(見(jiàn)附圖 2)
附圖 2: 在類(lèi)繼承上面的細(xì)微區(qū)別.
CLX的這個(gè)"Q"前綴不一定是最終版本的前綴.TWinControl現(xiàn)在變成了TWidgetControl,不過(guò)為了安撫痛苦,我們?yōu)門(mén)WidgetControl添加了一個(gè)TWinControl的別名.TWidgetControl和它的后代都有一個(gè)Handle屬性,隱式地指向Qt對(duì)象,有一個(gè)Hooks屬性指向一個(gè)hook對(duì)象,用來(lái)實(shí)現(xiàn)事件機(jī)制.(Hooks是一個(gè)復(fù)雜的話題,已經(jīng)超出本文的討論范圍)
OwnerDraw將被一種叫做Styles
的方法替代. 基本上Styles是widget或應(yīng)用程序顯示新面孔的一種機(jī)制,類(lèi)似于windows下面的貼圖(skins). 這部分正在開(kāi)發(fā)當(dāng)中,所以本文中我無(wú)法更進(jìn)一步的介紹,我只能說(shuō):它非?!
(新舊控件中)有沒(méi)有什么是一樣的?當(dāng)然有,TCanvas(包括Pens,Brushes等)和你記得的一樣.就像我說(shuō)過(guò)的,類(lèi)的繼承基本上一樣,還有事件,比如OnMouseDown, OnMouseMove, OnClick...等等都還在.
讓我看看內(nèi)涵(Show Me the Meaning)(???)
讓我們進(jìn)入到CLX的軀體,看看它是如何工作的.Qt是一個(gè)C++的工具集,所以所有的widgets都是C++對(duì)象.另一方面,CLX是用Object Pascal寫(xiě)的,并且Object Pascal不能直接和C++對(duì)象對(duì)話.越想簡(jiǎn)單就越難,Qt在幾個(gè)地方使用了多繼承,所以我們建立了一個(gè)接口層(interface layer)來(lái)獲得所有Qt的類(lèi),并且把它們還原成一系列普通的C函數(shù),然后把它們包裝成Windows下的DLL或是Linux下的共享對(duì)象(shared object).
每個(gè)TWidgetControl都有CreateWidget, InitWidget, 和HookEvents虛方法,并且?guī)缀蹩偸潜恢剌d. CreateWidget創(chuàng)建Qt的widget,然后指派Handle到FHandle這個(gè)私有域變量.當(dāng)widget被構(gòu)造(constructed)后,InitWidget被調(diào)用,然后Handle有效.你的一些屬性賦值將從Create這個(gè)構(gòu)造函數(shù)轉(zhuǎn)移到InitWidget.這將能夠做到延遲構(gòu)造(delayed construction)一個(gè)對(duì)象,直到真的需要它的時(shí)候.舉個(gè)例子,你有一個(gè)屬性叫Color,在SetColor里面,你可以通過(guò)HandleAllocated來(lái)檢測(cè)是否你有一個(gè)Qt的Handle,如果handle已經(jīng)分配(allocated),你就可以正確地調(diào)用Qt來(lái)設(shè)置顏色.如果沒(méi)有分配,你可以把值保存在一個(gè)私有域變量中,然后在InitWidget中設(shè)置屬性.
有兩種類(lèi)型的事件(events): Widget事件和系統(tǒng)事件.HookEvents是一個(gè)虛方法(virtual method),它鉤住(hooks)CLX控件的事件方法到一個(gè)特殊的Hook對(duì)象,通過(guò)這個(gè)對(duì)象和Qt對(duì)象通訊.(至少這是我希望看到的)
這個(gè)hook對(duì)象其實(shí)是方法指針的集合.系統(tǒng)事件現(xiàn)在通過(guò)EventHandler,基本上是WndProc的替代品.
比生命還大(Larger Than Life)(????)
所有這些都只是后臺(tái)信息(background information),因?yàn)槟阏娴牟槐貫榱藢?xiě)交叉平臺(tái)的定制控件而知道這些.在CLX的幫助下,寫(xiě)交叉平臺(tái)控件只是小菜一碟(a snap).就像你不必理解Windows API的復(fù)雜性而去寫(xiě)VCL控件一樣.CLX和Qt也是如此. 本文最后展示了一個(gè)用CLX寫(xiě)的定制控件代碼
下面是一個(gè)工程文件CalcTest.dpr. 計(jì)算器控件運(yùn)行在windows下(見(jiàn)附圖4) 和Linux下(見(jiàn)附圖5) 看上去多么像標(biāo)準(zhǔn)的Microsoft Windows 計(jì)算器!
program CalcTest;
uses
SysUtils, Classes, QControls, QForms, QStdCtrls, Qt,
QComCtrls, QCalc, Types;
type
TTestForm = class(TForm)
Calc: TCalculator;
public
constructor Create(AOwner: TComponent); override;
end;
var
TestForm: TTestForm;
{ TTestForm }
constructor TTestForm.Create(AOwner: TComponent);
begin
inherited CreateNew(AOwner);
SetBounds(10,100,640,480);
Calc := TCalculator.Create(Self);
// Don't forget: we have to set the parent.
Calc.Parent := Self;
Calc.Top := 100;
Calc.Left := 200;
// Uncomment these to try other Border effects:
// Calc.BorderStyle := bsEtched;
end;
begin
Application := TApplication.Create(nil);
Application.CreateForm(TTestForm, TestForm);
TestForm.Show;
Application.Run;
end.
附圖 3: CLX計(jì)算器控件的工程文件
附圖 4: 運(yùn)行在Windows下的計(jì)算器控件.
附圖 5: 運(yùn)行在Red Hat Linux下的計(jì)算器控件.
就像你所看到的,TCalculator是TFrameControl的子類(lèi).TFrameControl繼承自TWidgetControl的一個(gè)子類(lèi).它為你的控件提供了一個(gè)框架(frame),我們最感興趣的屬性是BorderStyle:
TBorderStyle = (bsNone, bsSingle, bsDouble, bsRaisedPanel,bsSunkenPanel,
bsRaised3d, bsSUnken3d, bs Etched, bsEmbossed);
在這個(gè)控件(TCalculator)中有兩個(gè)重要的方法.BuildCalc創(chuàng)建所有的按鈕,并且把它們擺放到正確的位置.正如你所看到的,我使用了一個(gè)叫TButtonType的枚舉類(lèi)型來(lái)控制按鈕的"功能(function)",還有少量的信息做為整型保存在Tag屬性里面.我在后面的Calc方法里面會(huì)講到它.所有的計(jì)算器按鈕保存在一個(gè)叫做Btns的受保護(hù)的(protected)記錄數(shù)組里面,類(lèi)型是TButtonRecord.
TButtonRecord = record
Top: Integer;
Left: Integer;
Width: Integer;
Height: Integer;
Caption: string;
Color: TColor;
end;
這樣做能夠
容易的在一個(gè)循環(huán)里面設(shè)置所有的按鈕,而不用寫(xiě)一大串的TButton.Create調(diào)用.注意所有按鈕的OnClick句柄都指派給了TCalculator的Calc方法.直接指派到一個(gè)自定義事件是不錯(cuò)的,因?yàn)樗邪粹o都在計(jì)算器的內(nèi)部,并且這些事件都不用被published(見(jiàn)附圖6)
for i := Low(TButtonType) to High(TButtonType) do
with TButton.Create(Self) do
begin
Parent := Self;
SetBounds(Btns[i].Left, Btns[i].Top, Btns[i].Width,
Btns[i].Height);
Caption := Btns[i].Caption;
Color := Btns[i].Color;
OnClick := Calc;
Tag := Ord(i);
end;
附圖 6: 在這種情況下,直接指派一個(gè)自定義事件是明智的
我有一個(gè)叫FStatus的TLabel控件. TLabel也是TFrameControl的后代,我想在計(jì)算器里面使用它,所以我讓它具有"sunken box"的外觀來(lái)顯示計(jì)算器的存儲(chǔ)記憶,就像Windows里面的計(jì)算器一樣.Qt標(biāo)簽的widget非常像VCL里面的TPanel控件.對(duì)于CLX里面的TLabel,我們沒(méi)有發(fā)布(publish)它的框架(frame)屬性,但是這并不妨礙你繼承使用它.
在BuildCalc里面我做的最后一件事是創(chuàng)建一個(gè)edit控件來(lái)顯示計(jì)算結(jié)果,正像你所看到的,計(jì)算器控件的Text屬性和Edit控件的Text屬性直接掛鉤.
另一個(gè)重要的方法是Calc,它實(shí)質(zhì)上是一個(gè)龐大的case語(yǔ)句,用來(lái)計(jì)算哪一個(gè)按鈕被按下,并且決定該如何去做.我使用了私有域變量FCurrentValue, FLastValue 和 FRepeatValue來(lái)保存計(jì)算的值,所以我不必使用堆棧來(lái)實(shí)現(xiàn).這個(gè)例子只是為了展示如何創(chuàng)建交叉平臺(tái)控件,而不是如何寫(xiě)一個(gè)計(jì)算器.
很好,還記得嗎,我在BuildCalc中使用了Tag屬性來(lái)控制它的功能.在這個(gè)方法里面,我們將參數(shù)Sender強(qiáng)制轉(zhuǎn)化成TButton,再將它的Tag強(qiáng)制轉(zhuǎn)化成TButtonType類(lèi)型,最后賦值給ButtonType. ButonType就是那個(gè)case語(yǔ)句里面的選擇器表達(dá)式.
ButtonType := TButtonType(TButton(Sender).Tag);
對(duì)于我們?nèi)绾伟堰@些轉(zhuǎn)換成交叉平臺(tái)控件,你感到驚奇嗎? 不? 非常好! 這說(shuō)明你已經(jīng)集中注意力了.這些代碼可以同時(shí)在Windows和Linux下面編譯,而不用改動(dòng)任何地方.沒(méi)有任何額外的步驟.正是仰仗于CLX的優(yōu)點(diǎn),控件已經(jīng)全部完工了.
所有不得不交待的(All I Have to Give)(????)
你已經(jīng)看到,寫(xiě)一個(gè)交叉平臺(tái)控件和寫(xiě)一個(gè)VCL控件并不是完全不同.如果你是一個(gè)控件開(kāi)發(fā)的新手,學(xué)習(xí)起來(lái)不會(huì)很難.如果你是一個(gè)經(jīng)驗(yàn)豐富的VCL控件作者,你的大部分知識(shí)都將平滑地轉(zhuǎn)移到Kylix上去.
我前面說(shuō)過(guò),(交叉平臺(tái)控件)會(huì)有一些不同,但是這只會(huì)影響那些依賴于Windows API寫(xiě)控件的開(kāi)發(fā)者.如果你寫(xiě)的控件是從VCL繼承的,是聚合(aggregate)了一些控件的(就像我在TCalculator里面做的),是一個(gè)非可視的不依賴于Windows API的,或者是一個(gè)TGraphic控件,那么轉(zhuǎn)移到Linux下不會(huì)遇到什么麻煩.
這篇文章介紹的軟件產(chǎn)品的功能正在開(kāi)發(fā)中,并且會(huì)在沒(méi)有通知的情況下有些變化.相應(yīng)功能的描述是應(yīng)時(shí)而變的,并且沒(méi)有任何確定的服務(wù)承諾.
做為Delphi最初測(cè)試版的用戶,Robert Kozak從1999年下半年加入Borland的Kylix R&D小組.從他加入Borland開(kāi)始,他就和C++Builder 5和Kylix的開(kāi)發(fā)密切相關(guān).Robert還和TaDDA(多倫多區(qū)Delphi開(kāi)發(fā)者協(xié)會(huì))有關(guān),這個(gè)協(xié)會(huì)后來(lái)和TDUG(多倫多區(qū)Delphi用戶組)合并.Robert在這些用戶社團(tuán)一直很活躍,同時(shí)也經(jīng)常出現(xiàn)在Borland的新聞組里面.
[列表1]
QCalc.pas
{ ***************************************************** }
{ }
{ Borland Delphi Visual Component Library }
{ Borland Delphi Component Library (X)Crossplatform }
{ }
{ Copyright (c) 2000 Inprise/Borland }
{ }
{ ***************************************************** }
unit QCalc;
// This is the very first Custom control written for CLX.
interface
uses
Sysutils, Classes, QT, QControls, QStdCtrls, QComCtrls,
QGraphics;
type
TButtonType = (bt0, bt1, bt2, bt3, bt4, bt5, bt6, bt7,
bt8, bt9, btDecimal, btPlusMinus, btMultiply, btDivide,
btAdd, btSubtract, btSqrt, btPercent, btInverse,
btEquals, btBackspace, btClear, btClearAll,
btMemoryRecall, btMemoryStore, btMemoryClear,
btMemoryAdd);
TCalcState = (csNone, csAdd, csSubtract, csMultiply, csDivide);
TButtonRecord = record
Top: Integer;
Left: Integer;
Width: Integer;
Height: Integer;
Caption: string;
Color: TColor;
end;
TCalculator = class(TFrameControl)
private
FResultEdit: TEdit;
FStatus: TLabel;
FMemoryValue: Single;
FCurrentValue: Single;
FLastValue: Single;
FRepeatValue: Single;
FState: TCalcState;
FBackSpaceValid: Boolean;
protected
Btns: array [TButtonType] of TButtonRecord;
procedure BuildCalc;
procedure Calc(Sender: TObject);
function GetText : string; override;
procedure SetText(const Value : string); override;
public
constructor Create(AOwner: TComponent); override;
property Value : Single read FCurrentValue;
published
property Text : string read GetText write SetText;
property BorderStyle;
property LineWidth;
property Margin;
property MidLineWidth;
property FrameRect;
end;
implementation
function ButtonRecord(aTop, aLeft, aWidth, aHeight: Integer; aCaption: string;
aColor: TColor = clBtnFace): TButtonRecord;
begin
Result.Top := aTop;
Result.Left := aLeft;
Result.Width := aWidth;
Result.Height := aHeight;
Result.Caption := aCaption;
Result.Color := aColor;
end;
{ TCalculator }
constructor TCalculator.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
SetBounds(0,0,250,200);
FMemoryValue := 0;
FCurrentValue := 0;
FLastValue := 0;
FRepeatValue := 0;
BorderStyle := bsRaisedPanel;
BuildCalc;
end;
procedure TCalculator.BuildCalc;
var
i: TButtonType;
begin
Btns[bt7] := ButtonRecord(70, 48, 36, 29, '7');
Btns[bt4] := ButtonRecord(102, 48, 36, 29, '4');
Btns[bt1] := ButtonRecord(134, 48, 36, 29, '1');
Btns[bt0] := ButtonRecord(166, 48, 36, 29, '0');
Btns[bt8] := ButtonRecord(70, 88, 36, 29, '8');
Btns[bt5] := ButtonRecord(102, 88, 36, 29, '5');
Btns[bt2] := ButtonRecord(134, 88, 36, 29, '2');
Btns[btPlusMinus] :=
ButtonRecord(166, 88, 36, 29, '+/-');
Btns[bt9] := ButtonRecord(70, 128, 36, 29, '9');
Btns[bt6] := ButtonRecord(102, 128, 36, 29, '6');
Btns[bt3] := ButtonRecord(134, 128, 36, 29, '3');
Btns[btDecimal] := ButtonRecord(166, 128, 36, 29, '.');
Btns[btDivide] := ButtonRecord(70, 168, 36, 29, '/');
Btns[btMultiply] := ButtonRecord(102, 168, 36, 29, '*');
Btns[btSubtract] := ButtonRecord(134, 168, 36, 29, '-');
Btns[btAdd] := ButtonRecord(166, 168, 36, 29, '+');
Btns[btBackspace] :=
ButtonRecord(37, 49, 63, 25, 'Backspace');
Btns[btClear] := ButtonRecord(37, 115, 63, 25, 'CE');
Btns[btClearAll] := ButtonRecord(37, 181, 63, 25, 'C');
Btns[btsqrt] := ButtonRecord(70, 208, 36, 29, 'sqrt');
Btns[btPercent] := ButtonRecord(102, 208, 36, 29, '%');
Btns[btInverse] := ButtonRecord(134, 208, 36, 29, '1/x');
Btns[btEquals] := ButtonRecord(166, 208, 36, 29, '=');
Btns[btMemoryAdd] := ButtonRecord(166, 5, 36, 29, 'M+');
Btns[btMemoryStore] :=
ButtonRecord(134, 5, 36, 29, 'MS');
Btns[btMemoryRecall] :=
ButtonRecord(102, 5, 36, 29, 'MR');
Btns[btMemoryClear] := ButtonRecord(70, 5, 36, 29, 'MC
');
for i := Low(TButtonType) to High(TButtonType) do
with TButton.Create(Self) do
begin
Parent := Self;
SetBounds(Btns[i].Left, Btns[i].Top, Btns[i].Width,
Btns[i].Height);
Caption := Btns[i].Caption;
Color := Btns[i].Color;
OnClick := Calc;
Tag := Ord(i);
end;
FStatus := TLabel.Create(Self);
with FStatus do
begin
Parent := Self;
SetBounds(10, 38, 25, 25);
BorderStyle := bsRaisedPanel;
end;
FResultEdit := TEdit.Create(Self);
FResultEdit.Parent := Self;
FResultEdit.SetBounds(5, 5, 240, 25);
FResultEdit.Alignment := taRightJustify;
FResultEdit.Font.Height := -13;
FResultEdit.Font.Name := 'Arial';
FResultEdit.Text := '0';
end;
procedure TCalculator.Calc(Sender: TObject);
const
MemoryStoreMap: array [Boolean] of string = (' M','');
var
ButtonType: TButtonType;
Temp: string;
TempValue: Single;
begin
ButtonType := TButtonType(TButton(Sender).Tag);
try
case ButtonType of
bt0..bt9:
begin
FBackSpaceValid := True;
if (FResultEdit.Text = '0') or (FCurrentValue = 0) then FResultEdit.Text := '';
FResultEdit.Text :=
FResultEdit.Text + Btns[ButtonType].Caption;
FCurrentValue := StrToFloat(FResultEdit.Text);
FRepeatValue := 0;
end;
btDecimal:
if Pos('.', FResultEdit.Text) < 1 then
begin
FCurrentValue := StrToFloat(FResultEdit.Text);
FLastValue := 0;
FResultEdit.Text :=
FResultEdit.Text + Btns[ButtonType].Caption;
end;
btPlusMinus:
begin
FCurrentValue := StrToFloat(FResultEdit.Text);
FCurrentValue := FCurrentValue * -1;
FResultEdit.Text := FloatToStr(FCurrentValue);
end;
btClearAll:
begin
FCurrentValue := 0;
FLastValue := 0;
FResultEdit.Text := '0';
FState := csNone;
end;
btClear:
begin
FCurrentValue := 0;
FResultEdit.Text := '0';
end;
btAdd:
begin
FCurrentValue := StrToFloat(FResultEdit.Text);
FState := csAdd;
FLastValue := FCurrentValue;
FCurrentValue := 0;
end;
btSubtract:
begin
FCurrentValue := StrToFloat(FResultEdit.Text);
FState := csSubtract;
FLastValue := FCurrentValue;
FCurrentValue := 0;
end;
btDivide:
begin
FCurrentValue := StrToFloat(FResultEdit.Text);
FState := csDivide;
FLastValue := FCurrentValue;
FCurrentValue := 0;
end;
btMultiply:
begin
FCurrentValue := StrToFloat(FResultEdit.Text);
FState := csMultiply;
FLastValue := FCurrentValue;
FCurrentValue := 0;
end;
btBackSpace:
if FBackSpaceValid then
begin
Temp := FResultEdit.Text;
Delete(Temp, Length(Temp),1);
if Temp = '' then Temp := '0';
FCurrentValue := StrToFloat(Temp);
FResultEdit.Text := FloatToStr(FCurrentValue);
end;
btInverse:
begin
FCurrentValue := StrToFloat(FResultEdit.Text);
FCurrentValue := 1 / FCurrentValue;
FResultEdit.Text := FloatToStr(FCurrentValue);
end;
btPercent:
begin
FCurrentValue := StrToFloat(FResultEdit.Text);
FCurrentValue := FCurrentValue / 100;
FResultEdit.Text := FloatToStr(FCurrentValue);
end;
btSqrt:
begin
FCurrentValue := StrToFloat(FResultEdit.Text);
FCurrentValue := Sqrt(FCurrentValue);
FResultEdit.Text := FloatToStr(FCurrentValue);
end;
btMemoryStore:
begin
FMemoryValue := StrToFloat(FResultEdit.Text);
FMemoryValue := FMemoryValue * 1;
FCurrentValue := 0;
end;
btMemoryAdd:
begin
TempValue := FMemoryValue;
FMemoryValue := StrToFloat(FResultEdit.Text);
FMemoryValue := (FMemoryValue * 1) + TempValue;
end;
btMemoryRecall:
begin
FResultEdit.Text := FloatToStr(FMemoryValue);
FCurrentValue := 0;
end;
btMemoryClear:
begin
FMemoryValue := 0;
end;
btEquals:
if FState <> csNone then
begin
FBackSpaceValid := False;
FCurrentValue := StrToFloat(FResultEdit.Text);
if FRepeatValue = 0 then
begin
FRepeatValue := FCurrentValue;
FCurrentValue := FLastValue;
end;
FLastValue := FRepeatValue;
case FState of
csAdd:
FCurrentValue := FCurrentValue + FLastValue;
csMultiply:
FCurrentValue := FCurrentValue * FLastValue;
csSubtract:
FCurrentValue := FCurrentValue - FLastValue;
csDivide:
FCurrentValue := FCurrentValue / FLastValue;
end;
FLastValue := FCurrentValue;
FResultEdit.Text := FloatToStr(FCurrentValue);
FCurrentValue := 0;
end;
end; // case ButtonType of...
except
on E: Exception do
begin
FResultEdit.Text := E.Message;
FLastValue := 0;
FCurrentValue := 0;
FRepeatValue := 0;
FState := csNone;
end;
end;
FStatus.Caption := MemoryStoreMap[FMemoryValue = 0];
end;
function TCalculator.GetText: string;
begin
Result := FResultEdit.Text;
end;
procedure TCalculator.SetText(const Value: string);
begin
FResultEdit.Text := Value;
end;
end.
【酒杯上的碟】相關(guān)文章:
酒杯優(yōu)秀作文09-05
草原上08-17
上、下08-16
下水(上)08-17
《草原上》說(shuō)課稿07-12
淮上的語(yǔ)錄01-26
指尖上的中國(guó)02-27
在金茂大廈上02-28
上與下作文02-22