• TMS 脚本中文帮助资料


    Scripter工作室开发者向导

    版权2002 Automa tmssoftware.com

     

    概要

     

    Scripter工作室是把脚本能力加到你的应用程序的一套的Delphi/C++Builder部件。Scripter工作室能使你的最终用户使用可视化工具书写和执行自己的脚本。包含的组件有:

    TatPascalScripter          非视觉组件,执行Pascal语法的脚本。

    TatBasicScripter            非视觉组件,执行Basic语法的脚本。

    TatPascalFormScripter   窗口认识Pascal脚本,继承自TatPascalScripter组件。

    TatBasicFormScripter    窗口认识Basic脚本,继承自TatBasicScripter组件。

    TatScriptDebugDlg        脚本高度的对话组件。

    TAdvMemo                  语法高亮Memo,在程序运行时编辑脚本。

    TatPascalScripterTatBasicScripter都是从TatCustomScripter组件继承下来的,都使用相同的属性和方法来运行脚本。

    脚本有下列特征:

    ·在运行时间解释PascalBasic语言。

    ·脚本能访问任何Delphi对象,包括属性和方法。

    ·脚本支持try...excepttry...finally异常处理。

    ·允许脚本读/Delphi变量和常量。

    ·允许Delphi代码访问(/)脚本变量。

    ·你能建造(Delphi代码)你自己的类,包含属性和方法,并在脚本中使用。

    ·Delphi系统的大多数程序(转化,日期,格式化,字符串操作)已经包括(IntToStr,FormatDateTime,Copy,Delete等等)。

    ·你能保存/装载编译了的代码,这样你不需要每次运行脚本的时候都需要源代码。

    ·调试能力(断点,跟踪,运行到光标,暂停,中断,等等)

    ·安全的线程

    ·COM(微软普通的对象模型)支持

     

    语言特征

     

    Pascal语法

    TatPascalScripter组件执行脚本的Pascal语法。Pascal语法支持:

    · begin .. end

    · procedure and function

    · if .. then .. else

    · for .. to .. do .. step

    · while .. do

    · repeat .. until

    · try .. except and try .. finally

    · case

    · array constructors (x:=[ 1, 2, 3 ];)

    · ^ , * , / , and , + , - , or , <> , >=, <= , = , > , < , div , mod , xor , shl , shr

    · 访问对象属性和方法(ObjectName.SubObject.Property)

     

    脚本结构

    脚本结构用2个主要的块组成:

    1、过程和函数声明;

    2、主要的块。

    两个块是可选择的,但是在脚本中至少要有一个。主要的块不需要begin...end。它可以是一个单一的声明。一些例子:

    脚本1

    procedure DoSomething;

    begin

    CallSomething;

    end;

     

    begin

    CallSomethingElse;

    end;

     

    脚本2

    begin

    CallSomethingElse;

    end;

     

    脚本3

    function MyFunction;

    begin

    result:='Ok!';

    end;

     

    脚本4

    CallSomethingElse;

     

    Pascal一样,声明应该由“;”结尾。begin...end块是允许集合一批声明。

     

    标识符

    在脚本的标识符名字(变量名字,函数和过程名字,等等。)跟随Pascal最普通的规则:

    应该从一个字符开始(a..zA..Z),或“_”,和继之以包含数字字母的字符或“_”字符。

    不能包含任何别的字符或空格。

     

    有效的标识符:

    VarName

    _Some

    V1A2

     

    无效的标识符:

    2Var

    My Name

    Some-more

    This,is,not,valid

     

    赋值声明

    赋值语句和Pascal一样,赋值语句是使用“:=”。例子:

    MyVar:=2;

    Button.Caption:='This ' + 'is ok.';

     

    字符串

    字符串是同pascal一样使用单引号(')字符。双引号(")不能使用。你也能使用(#)字符宣布一个字符在一个字符串的内部,不需要使用(+)操作符增加一个字符到一个字符串。一些例子:

    A:='This is a text';

    Str:='Text '+'concat';

    B:='String with CR and LF char at the end'#13#10;

    C:='String with '#33#34' characters in the middle';

     

    注释

    在脚本内能插入注释。你能使用//字符或(* *){}。使用//字符注释只能注释一行。

    //This is a comment before ShowMessage

    ShowMessage('Ok');

     

    (* This is another comment *)

    ShowMessage('More ok!');

     

    { And this is a comment

    with two lines }

    ShowMessage('End of okays');

     

    变量

    不需要在脚本中声明变数类型。这样,你宣布变量只需要使用var命令和变量的名字。如果脚本的OptionExplicit属性被设置为false,那么就不需要声明变量。在这种情况下,变数是暗示声明。如果你想要脚本上有更多的控制,设置OptionExplicit属性为true。如果OptionExplicit属性为true,而在脚本中使用了变量但不进行声明,将会唤醒一个编译错误。例子:

    脚本1

    procedure Msg;

    var S;

    begin

    S:='Hello world!';

    ShowMessage(S);

    end;

     

    脚本2:

    var A;

    begin

    A:=0;

    A:=A+1;

    end;

     

    脚本3:

    var S;

    S:='Hello World!';

    ShowMessage(S);

     

    如果脚本属性OptionExplicit被设置为false,那么var声明不需要出现在任何脚本的上方。

     

    索引

    字符串,数组和数组属性可以使用索引“[”并且“]”字符。更多例子:

    MyChar:=MyStr[2];

    MyStr[1]:='A';

    MyArray[1,2]:=1530;

    Lines.Strings[2]:='Some text';

     

    数组

    脚本支持数组和variant数组,使用方法参考Pascal。注意:数组的索引是从0开始的。更多例子:

    NewArray := [ 2,4,6,8 ];

    Num:=NewArray[1]; //Num receives "4"

    MultiArray := [ ['green','red','blue'] , ['apple','orange','lemon'] ];

    Str:=MultiArray[0,2]; //Str receives 'blue'

    MultiArray[1,1]:='new orange';

     

    If语句

    if语句有2种形式:if..thenif..then..else。更多例子:

    if J <> 0 then Result := I/J;

    if J = 0 then Exit else Result := I/J;

    if J <> 0 then

    begin

    Result := I/J;

    Count := Count + 1;

    end

    else

    Done := True;

     

    While语句

    Pascal。更多例子:

    while Data[I] <> X do I := I + 1;

    while I > 0 do

    begin

    if Odd(I) then Z := Z * X;

    I := I div 2;

    X := Sqr(X);

    end;

     

    while not Eof(InputFile) do

    begin

    Readln(InputFile, Line);

    Process(Line);

    end;

     

    repeat语句

    Pascal。更多例子:

    repeat

    K := I mod J;

    I := J;

    J := K;

    until J = 0;

     

    repeat

    Write('Enter a value (0..9): ');

    Readln(I);

    until (I >= 0) and (I <= 9);

     

    for语句

    Pascal。更多例子:

    脚本1:

    for c:=1 to 10 do

    a:=a+c;

     

    脚本2:

    for i:=a to b do

    begin

    j:=i^2;

    sum:=sum+j;

    end;

     

    case语句

    Pascal。更多例子:

    case uppercase(Fruit) of

    'lime': ShowMessage('green');

    'orange': ShowMessage('orange');

    'apple': ShowMessage('red');

    else

    ShowMessage('black');

    end;

     

    函数和过程说明

    函数和过程的宣告同Delphi的非常相似,唯一的差别只在于不需要指定变量的类型。函数的返回值是使用暗示的Result变量。参数传地址也能被使用,只是不需要规定变量类型。一些例子:

    procedure HelloWord;

    begin

    ShowMessage('Hello world!');

    end;

     

    procedure UpcaseMessage(Msg);

    begin

    ShowMessage(Uppercase(Msg));

    end;

     

    function TodayAsString;

    begin

    result:=DateToStr(Date);

    end;

     

    function Max(A,B);

    begin

    if A>B then

    result:=A

    else

    result:=B;

    end;

     

    procedure SwapValues(var A, B);

    Var Temp;

    begin

    Temp:=A;

    A:=B;

    B:=Temp;

    end;

     

     

    Basic的语法

     

    TatBasicScripter组件执行Basic语法的脚本。Basic语法支持:

    · sub .. end and function .. end declarations

    · byref and dim directives

    · if .. then .. else .. end constructor

    · for .. to .. step .. next constructor

    · do .. while .. loop and do .. loop .. while constructors

    · do .. until .. loop and do .. loop .. until constructors

    · ^ , * , / , and , + , - , or , <> , >=, <= , = , > , < , div , mod , xor , shl , shr operators

    · try .. except and try .. finally blocks

    · select case .. end select constructor

    · array constructors (x:=[ 1, 2, 3 ];)

    · exit statement

    · access to object properties and methods ( ObjectName.SubObject.Property )

     

    脚本结构

    Pascal的脚本结构。一些例子:

    脚本1

    SUB DoSomething

    CallSomething

    END SUB

     

    CallSomethingElse

     

    脚本2

    CallSomethingElse

     

    脚本3

    FUNCTION MyFunction

    MyFunction = "Ok!"

    END FUNCTION

     

    同正常Basic一样,所声明的一个单行能由“:”字符分开。

     

    标识符

    Basic,标识符一些例子:

    有效的标识符:

    VarName

    _Some

    V1A2

     

    无效的标识符:

    2Var

    My Name

    Some-more

    This,is,not,valid

     

    赋值声明

    使用“=”字符。一些例子:

    MyVar = 2

    Button.Caption = "This " + "is ok."

     

    字符串

    使用(")字符。一些例子:

    A = "This is a text"

    Str = "Text "+"concat"

     

    注释

    使用(')或(REM)字符。一些例子:

    ' This is a comment before ShowMessage

    ShowMessage("Ok")

     

    REM This is another comment

    ShowMessage("More ok!")

     

    ' And this is a comment

    ' with two lines

    ShowMessage("End of okays")

     

    变量

    不需要在脚本中声明变数类型。这样,你宣布变量只需要使用DIM命令和变量的名字。如果脚本的OptionExplicit属性被设置为false,那么就不需要声明变量。在这种情况下,变数是暗示声明。如果你想要脚本上有更多的控制,设置OptionExplicit属性为true。如果OptionExplicit属性为true,而在脚本中使用了变量但不进行声明,将会唤醒一个编译错误。一些例子:

     

    脚本1:

    SUB Msg

    DIM S

    S = "Hello world!"

    ShowMessage(S)

    END SUB

     

    脚本2:

    DIM A

    A = 0

    A = A+1

    ShowMessage(A)

    如果脚本属性OptionExplicit被设置为false,那么DIM声明不需要出现在任何脚本的上方。

     

    索引

    字符串,数组和数组属性可以使用索引“[”并且“]”字符。更多例子:

    MyChar = MyStr[2]

    MyStr[1] = "A"

    MyArray[1,2] = 1530

    Lines.Strings[2] = "Some text"

     

    数组

    脚本支持数组和variant数组,同Pascal脚本的一样。注意:数组的索引是从0开始的。更多例子:

    NewArray = [ 2,4,6,8 ]

    Num = NewArray[1] //Num receives "4"

    MultiArray = [ ["green","red","blue"] , ["apple","orange","lemon"] ]

    Str = MultiArray[0,2] //Str receives 'blue'

    MultiArray[1,1] = "new orange"

     

    If声明

    if语句有2种形式:if...then..end ifif...then...else..end if。更多例子:

    IF J <> 0 THEN Result = I/J END IF

    IF J = 0 THEN Exit ELSE Result := I/J END IF

    IF J <> 0 THEN

    Result = I/J

    Count = Count + 1

    ELSE

    Done = True

    END IF

     

    While声明

    Basic。更多例子:

    WHILE (Data[I] <> X) I = I + 1 END WHILE

    WHILE (I > 0)

    IF Odd(I) THEN Z = Z * X END IF

    X = Sqr(X)

    END WHILE

     

    WHILE (not Eof(InputFile))

    Readln(InputFile, Line)

    Process(Line)

    END WHILE

     

    Loop声明

    支持loop语句,功能同Basic中的loop语句。可能的语法是:

    DO WHILE expr statements LOOP

    DO UNTIL expr statements LOOP

    DO statements LOOP WHILE expr

    DO statement LOOP UNTIL expr

     

    更多例子:

    DO

    K = I mod J

    I = J

    J = K

    LOOP UNTIL J = 0

     

    DO UNTIL I >= 0

    Write("Enter a value (0..9): ")

    Readln(I)

    LOOP

     

    DO

    K = I mod J

    I = J

    J = K

    LOOP WHILE J <> 0

     

    DO WHILE I < 0

    Write("Enter a value (0..9): ")

    Readln(I)

    LOOP

     

    For声明

    Basic。更多例子:

    脚本1:

    FOR c = 1 TO 10 STEP 2

    a = a + c

    NEXT

     

    脚本2:

    FOR I = a TO b

    j = i ^ 2

    sum = sum + j

    NEXT

     

    Select声明

    功能同Basic中的Select语句。可能的语法是:

    SELECT CASE selectorExpression

    CASE caseexpr1

    statement1

    CASE caseexprn

    statementn

    CASE ELSE

    elsestatement

    END SELECT

     

    更多例子:

    SELECT CASE uppercase(Fruit)

    CASE "lime" ShowMessage("green")

    CASE "orange"

    ShowMessage("orange")

    CASE "apple" ShowMessage("red")

    CASE ELSE

    ShowMessage("black")

    END SELECT

     

    函数和过程声明

    函数和附属的声明与Basic相似。函数的返回值是使用暗示的变量,该变量同函数名一样。参数传地址也能被使用,使用BYREF命令。一些例子:

    SUB HelloWord

    ShowMessage("Hello world!")

    END SUB

     

    SUB UpcaseMessage(Msg)

    ShowMessage(Uppercase(Msg))

    END SUB

     

    FUNCTION TodayAsString

    TodayAsString = DateToStr(Date)

    END FUNCTION

     

    FUNCTION Max(A,B)

    IF A>B THEN

    MAX = A

    ELSE

    MAX = B

    END IF

    END FUNCTION

     

    SUB SwapValues(BYREF A, B)

    DIM TEMP

    TEMP = A

    A = B

    B = TEMP

    END SUB

     

    脚本的工作

     

    现在我们讲到了脚本的具体使用方法。

     

    开始

    开始脚本,只需要把脚本赋值给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;

    Basic示例

    脚本:

    sub DisplayHelloWorld

    ShowMessage(“Hello world!”);

    end sub

     

    sub DisplayByeWorld

    ShowMessage(“Bye world!”);

    end sub

    代码:

    Scripter.ExecuteSubRoutine('DisplayHelloWorld');

    Scripter.ExecuteSubRoutine('DisplayByeWorld');

    上面的代码将显示“Hello word!”和“Bye world!”消息框。

     

    取得脚本返回的值

    如果Delphi要取得脚本中的过程或函数返回的值,使用ExecuteSubRoutine方法。见下例:

    Pascal示例

    脚本:

    function Calculate;

    begin

    result:=(10+6)/4;

    end;

    Basic示例

    脚本:

    function Calculate

    Calculate = (10+6)/4

    end function

     

    代码:

    FunctionValue:=Scripter.ExecuteSubRoutine('Calculate');

     

    如果Delphi要取得脚本所返回的值。见下例:

    脚本:

    result:=(10+6)/4;

     

    代码:

    FunctionValue:=Scripter.Execute;

     

    Basic中要达到同样的效果,就要使用保留字“MAIN”:"MAIN = (10+6)/4"

     

    传递参数到脚本

    如果要把Delphi变量的值传递给脚本,还是同样使用ExecuteSubRoutine方法见下例:

    Pascal示例

    脚本:

    function Double(Num);

    begin

    result:=Num*2;

    end;

    Basic示例

    脚本:

    function Double(Num)

    Double = Num*2

    End function

     

    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';

    窗口的标题就被改变了。

     

    访问published属性

    在增加一个组件后,你可以访问它的所有Published属性,这也是为什么窗口的标题可以改变的原因。否则你必须注册属性,实际上,Published属性已经被注册,除非你不在脚本中使用它。

     

    类注册结构

    脚本可以调用对象的方法和属性,但是所要调用的方法和属性必须在脚本中注册,而管理注册的关键的属性就是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方法自动检查TCustomForm类是否已经被注册,这样你不需要测试它。

    最后,使用DefineMethod方法注册ShowModalDefineMethod的宣告是:

    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个或多个参数,使用GetInputArg(1)GetInputArg(2),等等。

    在使用ReturnOutputArg的情况下,我们需要舍弃TField返回的整数值,这必须不返回任何对象,这是因为ReturnOutputArg接收任何一个Variant类型,和对象必须舍弃整数值。

     

    访问非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标志那值属性是变量类型,然后通过两个方法GetFieldValueProcSetFieldValueProc,依次读写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方法,下面的例子说明如何增加QuotedStrStringOfChar方法:

    脚本:

    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的初始化和代码2PrepareScript)是一样的,和CurrToStrProc方法-没有差别。这两段代码间的差别是:

    类和方法的属于,在代码1,方法属于一个专门的类TExampleLibrary,它是从TatScripterLibrary继承下来的,在代码2,它属于当前窗口(TForm1)。

    在代码1,脚本是增加一个类TExampleLibrary到它自己,使用AddLibrary方法,在代码2PrepareScript方法是直接调用。

     

    除了使用AddLibrary方法,你可以使用RegisterScripterLibrary过程,见例子:

    RegisterScripterLibrary(TExampleLibrary);

    RegisterScripterLibrary是注册Library到一个全局列表到一个全局过程,所有脚本都会自动应用到Library,如果你想所有脚本组件都能使用Library,就使用RegisterScripterLibrary。使用AddLibrary只能增加一个Library到指定的组件。

     

    TatPascalFormScripter组件和TatBasicFormScripter组件

    TatPascalFormScripter组件和TatBasicFormScripter组件是从TatPascalScripter组件和TatBasicScripter组件继承下来的。它们都拥有祖先的所有功能,同时又注册了自己所特有的功能。

    因此,如果你想要在窗口中使用脚本访问组件,类似于按钮、编辑框等等,你可以在脚本中注册窗口组件。

     

    调试脚本

    Scripter工作室包含组件和方法允许在程序运行时间调试脚本。主要有2种方法调试脚本:1、使用脚本组件方法和属性;2、使用调试组件。使用方法和属性能留下更多的自由给程序员,并且你可以使用他们建立一个属于你自己的调试环境。使用组件可以进行更高级别的调试,在大多数情况下你只需要拖动一个组件和在开始调试前调动一个方法就行了。

     

    使用调试的方法和属性

    脚本组件有几个允许调试脚本的方法和属性,在Delphi代码内你可以按照自己的需要使用他们,他们的列表在下面:

    property Running: boolean;

    /写属性,当正在执行脚本的时候,RunningTrue。注意脚本可以暂停,但它仍然在运行,设置Running属性为True与调用Execute方法的效果一样。

     

    property Paused: boolean read GetPaused write SetPaused;

    /写属性,使用它可以暂停脚本运行或取得脚本运行的状态。

     

    procedure DebugTraceIntoLine;

    执行当前行,如果当前行包含一个子程序调用,执行指针转到子程序的第一行。

     

    procedure DebugStepOverLine;

    执行当前行和执行指针转到下一行代码,如果当前行包含一个子程序调用,它运行完整个子程序,和Delphi中的Step Over选项相似。

     

    procedure DebugRunUntilReturn;

    执行代码直到当前子程序(过程、函数或主脚本块)是完成,执行指针停止在调用子程序的下一行。

     

    procedure DebugRunToLine(ALine:integer);

    执行脚本直到指定行,类似于DelphiRun to Cursor选项。

     

    function DebugToggleBreakLine(ALine:integer):pSimplifiedCode;

    激活/禁止一个指定行的断点,如果断点设置是True,执行停止在断点所设置的行。

     

    function DebugExecutionLine:integer;

    返回调试行的计数值。

     

    procedure Halt;

    停止脚本执行,不管执行指针在那里。

     

    property Halted: boolean read GetHalted;

    在调用Halt方法或脚本已经停止后,该属性返回True

     

    property BreakPoints: TatScriptBreakPoints read GetBreakPoints;

    包含脚本中设置的断点,你可以使用Items[Index]属性或者BreakPointByLine(ALine: integer)方法访问断点,一旦你访问断点,你可以设置属性Enabled(如果断点是活跃的,就会显示出来)和PassCount(它显示有多少次执行经过断点,直到执行结束)。

     

    property OnDebugHook: TNotifyEvent read GetOnDebugHook write SetOnDebugHook;

    在调试期间(step overstep into等等),每执行一行都会调用OnDebugHook事件。

     

    property OnPauseChanged: TNotifyEvent read GetOnPauseChanged write SetOnPauseChanged;

    property OnRunningChanged: TNotifyEvent read GetOnRunningChanged write SetOnRunningChanged;

    只要pausedrunning属性改变,事件都会被调用。

     

    使用调试组件

    Scripter工作室有一个特定的调试组件,它是TatScriptDebugDlg,它的使用非常简单:拖动它到窗口上和访问它的Scripter属性在一个存在的脚本组件,调用Execute方法和debug对话框将出现,显示脚本源代码和一个工具条出现在窗口顶端,你可以使用工具条上的按钮或快捷键执行调试行为(run, pause, step over, and so on),快捷键和Delphi的一样。

    F4 – Run to cursor

    F5 – Toggle breakpoint

    F7 – Step into

    F8 – Step Over

    F9 – Run

    Shift+F9 - Pause

    Ctrl+F2 – Reset

    Shift+F11 – Run until return

     

  • 相关阅读:
    OOP3(继承中的类作用域/构造函数与拷贝控制/继承与容器)
    OOP2(虚函数/抽象基类/访问控制与继承)
    OOP1(定义基类和派生类)
    拷贝控制3(对象移动)
    拷贝控制2(拷贝控制和资源管理/交换操作/动态内存管理)
    拷贝控制1(拷贝、赋值与销毁)
    动态内存2(动态数组)
    python--numpy模块、spicy模块、 matplotlib模块
    Java--23种设计模式之decorator模式
    Android开发---开发文档翻译
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/2940960.html
Copyright © 2020-2023  润新知