开始脚本,只需要把脚本赋值给TatCustomScripter组件的SourceCode属性,并调用Execute方法。下面是一个简单的例子:
Scripter.SourceCode.Text:='ShowMessage(''Hello world!'');';
Scripter.Execute;
现在我们变得灵活一点。在窗口上放一个Tmemo组件,并把代码改为下面这样:
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
现在你可以在程序运行的时候在Memo组件中输入脚本,并运行它了。
在脚本中调用程序中的子程序
如果脚本要直接调用程序中的一个或多个函数和过程,你只要使用ExecuteSubRouti方法就可以了:
Pascal示例
脚本:
procedure DisplayHelloWorld;
begin
ShowMessage('Hello world!');
end;
procedure DisplayByeWorld;
begin
ShowMessage('Bye world!');
end;
代码:
Scripter.ExecuteSubRoutine('DisplayHelloWorld');
Scripter.ExecuteSubRoutine('DisplayByeWorld');
上面的代码将显示“Hello word!”和“Bye world!”消息框。
取得脚本返回的值
如果Delphi要取得脚本中的过程或函数返回的值,使用ExecuteSubRoutine方法。见下例:
Pascal示例
脚本:
function Calculate;
begin
result:=(10+6)/4;
end;
代码:
FunctionValue:=Scripter.ExecuteSubRoutine('Calculate');
如果Delphi要取得脚本所返回的值。见下例:
脚本:
result:=(10+6)/4;
代码:
FunctionValue:=Scripter.Execute;
传递参数到脚本
如果要把Delphi变量的值传递给脚本,还是同样使用ExecuteSubRoutine方法见下例:
Pascal示例
脚本:
function Double(Num);
begin
result:=Num*2;
end;
CODE:
FunctionValue:=Scripter.ExecuteSubRoutine('Double',5);
FunctionValue接收到10。如果你要传递更多的变量,就要使用一个Variant数组:
脚本:
function MaxValue(A,B);
begin
if A>B then
result:=A
else
result:=B;
end;
代码:
FunctionValue:=Scripter.ExecuteSubRoutine('MaxValue',VarArrayOf([5,8]));
脚本不需要参数类型,你只需要声明他们的名字就行了。
访问Delphi对象
注册组件
Scripter工作室最大的特点就是能访问Delphi对象。这使你在脚本中能有很大的灵活性,比如改变Delphi对象的属性,调用对象的方法等等。
但是,每一个Delphi对象都必须经过注册后才能在脚本中访问。例如,你要改变窗口的标题(名字是Form1),如果你试着执行下面的脚本:
脚本:
Form1.Caption:='New caption';
你只会得到“Unknown identifier or variable not declared: Form1”的错误消息。要让脚本能够正常工作,必须使用AddComponent方法:
代码:
Scripter.AddComponent(Form1);
现在你再执行脚本:
Form1.Caption:='New caption';
窗口的标题就被改变了。
类注册结构
脚本可以调用对象的方法和属性,但是所要调用的方法和属性必须在脚本中注册,而管理注册的关键的属性就是TatCustomScripter.Classes属
性,这属性保存所有已注册的类(TatClass对象),它依次序保存所有已登记的属性和方法(TatClass.Methods and TatClass.Properties)。每
种已注册的方法和属性都有一个名字和一个包装方法(Delphi写代码将句柄方法和属性)。
当你已注册一个Form1组件在一个例子上时,脚本自动注册TForm类在Classes属性,和注册所有published属性在它内部,要访问方法和Public
属性,你必须注册它们,参见下列例子:
调用方法
调用一个对象方法,你必须注册它。例如,如果你重新创建了一个名字叫Form2的窗口,并且想要调用Form2窗口的ShowModal方法,因此我们必
须使用AddComponent方法增加窗口到脚本,然后注册ShowModal方法:
代码:
procedure Tform1.ShowModalProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(TCustomForm(CurrentObject).ShowModal);
end;
procedure TForm1.PrepareScript;
begin
Scripter.AddComponent(Form2);
With Scripter.DefineClass(TCustomForm) do
begin
DefineMethod('ShowModal',0,tkInteger,nil,ShowModalProc);
end;
end;
脚本:
ShowResult:=Form2.ShowModal;
这个例子有许多新概念。首先,组件增加了AddComponent方法;然后,调用DefineClass方法注册TCustomForm类。DefineClass方法自动检查TC
ustomForm类是否已经被注册,这样你不需要测试它。
最后,使用DefineMethod方法注册ShowModal。DefineMethod的宣告是:
function DefineMethod(AName:string; AArgCount:integer; AResultDataType: TatTypeKind;
AResultClass:TClass; AProc:TMachineProc; AIsClassMethod:boolean=false): TatMethod;
AName接收“ShowModal” - 它是用于脚本的方法的名字。
AArgCount接收0 - 数字输入意见对于那方法。(没有,ShowModal过程在这情况下)
AResultDataType接收tkInteger -
它是方法返回的数据类型。ShowModal返回一个整数,如果ShowModal方法不是一个函数而是一个过程,AResultDataType将会收到tkNone。
AResultClass接收nil - 如果方法返回一个对象(不是这个情况),那么AResultClass必须包含对象类型。例如,TField。
AProc接收ShowModalProc - 这个方法书写用户包装ShowModal工作的方法。
最后,有ShowModalProc方法,它是一个包装工作的方法:它执行一次ShowModal调用,在这种情况下,它使用一些TatVirtualMachine类的有用
的方法和属性。
属性CurrentObject - 包含对象方法的实例。因此,它包含一个TCustomForm的实例。
在这种情况下,返回值被送回TCustomForm.ShowModal方法。
更多方法调用例子
除了先前的例子,这一个举例说明怎么注册和调用方法所接收到的参数和返回类。
在这个例子,FieldByName:
脚本:
AField:=Table1.FieldByName('CustNo');
ShowMessage(AField.DisplayLabel);
代码:
procedure TForm1.FieldByNameProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(integer(TDataset(CurrentObject).FieldByName(GetInputArgAsString(0))));
end;
procedure TForm1.PrepareScript;
begin
Scripter.AddComponent(Table1);
With Scripter.DefineClass(TDataset) do
begin
DefineMethod('FieldByName',1,tkClass,TField,FieldByNameProc);
end;
end;
类似先前的例子,一些注释:
- FieldByName方法是注册在TDataset类,任何TDataset后代都允许在脚本内使用FieldByName方法。如果FieldByName已注册在一个TTable类
,并且组件是一个TQuery,那么脚本将不认可这个方法。
- DefineMethod调用定义了的FieldByName收到一个参数,它的返回类型是tkClass,和类返回是TField。
-
在FieldByNameProc内,调用GetInputArgAsString方法是为了顺序取得输入参数。索引0表示第一个参数,为了接收2个或多个参数,使用GetIn
putArg(1)、GetInputArg(2),等等。
-
在使用ReturnOutputArg的情况下,我们需要舍弃TField返回的整数值,这必须不返回任何对象,这是因为ReturnOutputArg接收任何一个Varia
nt类型,和对象必须舍弃整数值。
访问非published属性
就和方法一样,非公布的属性必须注册,其注册机制和注册方法很相似,其差别我们必须指出一个包装取得属性值和另外一个设置属性值。在
下列例子,那个TField 类的“AsFloat”属性已登记:
脚本:
AField:=Table1.FieldByName('Company');
ShowMessage(AField.Value);
代码:
procedure TForm1.GetFieldValueProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(TField(CurrentObject).Value);
end;
procedure TForm1.SetFieldValueProc(AMachine: TatVirtualMachine);
begin
With AMachine do
TField(CurrentObject).Value:=GetInputArg(0);
end;
procedure TForm1.PrepareScript;
begin
With Scripter.DefineClass(TField) do
begin
DefineProp('Value',tkVariant,GetFieldValueProc,SetFieldValueProc);
end;
end;
DefineProp是调用经过一个tkVariant标志那值属性是变量类型,然后通过两个方法GetFieldValueProc和SetFieldValueProc,依次读写TField
对象的属性值。注意在SetFieldValueProc方法是使用GetInputArg(代替GetInputArgAsString),这是因为GetInputArg返回一个变量。
注册索引属性
一个属性可以被索引,特别当它是一个TCollection子孙时。这适用于dataset fields, grid columns, string items,等等。TStrings对象的
Strings属性加进其他改变到Memo内容:
脚本:
ShowMessage(Memo1.Lines.Strings[3]);
Memo1.Lines.Strings[3]:=Memo1.Lines.Strings[3]+' with more text added';
//This is a comment
代码:
procedure TForm1.GetStringsProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(TStrings(CurrentObject).Strings[GetArrayIndex(0)]);
end;
procedure TForm1.SetStringsProc(AMachine: TatVirtualMachine);
begin
With AMachine do
TStrings(CurrentObject).Strings[GetArrayIndex(0)]:=GetInputArgAsString(0);
end;
procedure TForm1.PrepareScript;
begin
Scripter.AddComponent(Memo1);
With Scripter.DefineClass(TStrings) do
begin
DefineProp('Strings',tkString,GetStringsProc,SetStringsProc,nil,false,1);
end;
end;
访问Delphi函数、变量和常量
除了能访问Delphi对象,脚本还允许访问全局常量、全局变量、过程和函数,其访问机制和访问Delphi对象非常相似。实际上,脚本在内部认
为过程和函数是方法,全局变量和全局常量是属性。
注册全局常量
注册一个常量在脚本内是一个非常简单的工作:使用AddConstant方法增加一个常量和名字在脚本内。
代码:
Scripter.AddConstant('MaxInt',MaxInt);
Scripter.AddConstant('Pi',pi);
Scripter.AddConstant('MyBirthday',EncodeDate(1992,5,30));
脚本:
ShowMessage('Max integer is '+IntToStr(MaxInt));
ShowMessage('Value of pi is '+FloatToStr(pi));
ShowMessage('I was born on '+DateToStr(MyBirthday));
访问全局变量
要在脚本中注册一个全局变量,你必须使用AddVariable方法,与增加全局常量相类似:传递变量名字和变量自己。另外,你也可以用增加属性
的方法增加变量,使用一个包装方法取得变量值和设置变量值。
代码:
var
MyVar: Variant;
ZipCode: string[15];
procedure TForm1.GetZipCodeProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(ZipCode);
end;
procedure TForm1.SetZipCodeProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ZipCode:=GetInputArgAsString(0);
end;
procedure TForm1.PrepareScript;
begin
Scripter.AddVariable('ShortDateFormat',ShortDateFormat);
Scripter.AddVariable('MyVar',MyVar);
Scripter.DefineProp('ZipCode',tkString,GetZipCodeProc,SetZipCodeProc);
Scripter.AddObject('Application',Application);
end;
procedure TForm1.Run1Click(Sender: TObject);
begin
PrepareScript;
MyVar:='Old value';
ZipCode:='987654321';
Application.Tag:=10;
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
ShowMessage('Value of MyVar variable in Delphi is '+VarToStr(MyVar));
ShowMessage('Value of ZipCode variable in Delphi is '+VarToStr(ZipCode));
end;
脚本:
ShowMessage('Today is '+DateToStr(Date)+' in old short date format');
ShortDateFormat:='dd-mmmm-yyyy';
ShowMessage('Now today is '+DateToStr(Date)+' in new short date format');
ShowMessage('My var value was "'+MyVar+'"');
MyVar:='My new var value';
ShowMessage('Old Zip code is '+ZipCode);
ZipCode:='109020';
ShowMessage('Application tag is '+IntToStr(Application.Tag));
调用函数和过程
在脚本中,函数和过程是增加如同方法,差别是你不能增加过程在任何一个类,但是在脚本自己,使用DefineMethod方法,下面的例子说明如
何增加QuotedStr和StringOfChar方法:
脚本:
ShowMessage(QuotedStr(StringOfChar('+',3)));
代码:
{ TSomeLibrary }
procedure TSomeLibrary.Init;
begin
Scripter.DefineMethod('QuotedStr',1,tkString,nil,QuotedStrProc);
Scripter.DefineMethod('StringOfChar',2,tkString,nil,StringOfCharProc);
end;
procedure TSomeLibrary.QuotedStrProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(QuotedStr(GetInputArgAsString(0)));
End;
procedure TSomeLibrary.StringOfCharProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(StringOfChar(GetInputArgAsString(0)[1],GetInputArgAsInteger(1)));
end;
procedure TForm1.Run1Click(Sender: TObject);
begin
Scripter.AddLibrary(TSomeLibrary);
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
end;
使用Libraries
在脚本中,你可以使用Libraries中已注册的函数和方法,看下面的2段代码,第一段使用库和第二段使用一般的文档机制:
代码1:
type
TExampleLibrary = class(TatScripterLibrary)
protected
procedure CurrToStrProc(AMachine: TatVirtualMachine);
procedure Init; override;
end;
procedure TExampleLibrary.Init;
begin
Scripter.DefineMethod('CurrToStr',1,tkInteger,nil,CurrToStrProc);
end;
procedure TExampleLibrary.CurrToStrProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(CurrToStr(GetInputArgAsFloat(0)));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Scripter.AddLibrary(TExampleLibrary);
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
end;
代码2:
procedure TForm1.PrepareScript;
begin
Scripter.DefineMethod('CurrToStr',1,tkInteger,nil,CurrToStrProc);
end;
procedure TForm1.CurrToStrProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(CurrToStr(GetInputArgAsFloat(0)));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
PrepareScript;
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
end;
两段代码都做同样的事情:增加CurrToStr过程到脚本,注意脚本的初始化方法(代码1的初始化和代码2的PrepareScript)是一样的,和CurrT
oStrProc方法-没有差别。这两段代码间的差别是:
- 类和方法的属于,在代码1,方法属于一个专门的类TExampleLibrary,它是从TatScripterLibrary继承下来的,在代码2,它属于当前窗口
(TForm1)。
- 在代码1,脚本是增加一个类TExampleLibrary到它自己,使用AddLibrary方法,在代码2,PrepareScript方法是直接调用。
除了使用AddLibrary方法,你可以使用RegisterScripterLibrary过程,见例子:
RegisterScripterLibrary(TExampleLibrary);
RegisterScripterLibrary是注册Library到一个全局列表到一个全局过程,所有脚本都会自动应用到Library,如果你想所有脚本组件都能使用
Library,就使用RegisterScripterLibrary。使用AddLibrary只能增加一个Library到指定的组件。
TatPascalFormScripter组件和TatBasicFormScripter组件
TatPascalFormScripter组件和TatBasicFormScripter组件是从TatPascalScripter组件和TatBasicScripter组件继承下来的。它们都拥有祖先
的所有功能,同时又注册了自己所特有的功能。
因此,如果你想要在窗口中使用脚本访问组件,类似于按钮、编辑框等等,你可以在脚本中注册窗口组件。