Class 在与自定义对话框一起使用时无法被 DM 识别

Class is not recognized by DM when it is used with a custom dialog

我正在调整一个 DM 脚本来读取 hdf5 文件。有一个名为 "MetaStr2TagGroup" 的用户定义 Class。它在脚本中运行良好。

我正在尝试构建一个自定义对话框,以便以直接的方式为用户提供更多选项。在没有实际脚本部分的情况下,自定义对话框在我的测试中运行良好。

当我将带有 class “MetaStr2TagGroup” 的脚本放在自定义对话框脚本的前面时,就会出现问题。它仍然 运行s 给出一个对话框并允许所有操作。但是一旦我问它 运行 脚本然后它给我一条错误消息 "Cannot find class named ‘metastr2taggroup’" 但是 class 仍然在整个脚本的前面。

我不明白,谁能解释一下为什么会发生这种情况以及如何避免这种情况?

我的简化代码结构如下。 object Local_helperlocal_Helper = Alloc( Helper ) 是我根据 BmyGuest 的评论添加的。到目前为止它没有用。

Class Helper{
    functions1(){}
    functions2(){}
    TagGroup functions3(){
        Call function 1 and function 2}
}
Function4()   {
    Call function3()
}
Function5(){}
Function6(){
    Call function4()
}

class Converter : UIframe   {
    **object Local_helper**
    Action1(){}
    Action2()   {
        **local_Helper = Alloc( Helper )**
        Call Function6()
    }
}

TagGroup MakeFields1{}
TagGroup MakeFields2{}

//Main start here
Call MakeField1()
Call MakeField2()
object dialog_frame = alloc(Converter).init()

您 运行 反对的问题是 "UIframe" 对话框是在不同的线程上创建的。当按下对话框按钮时,原来的 "script" 不再在范围内(只有对话框对象在。)

所以下面的代码运行是正确的:

class CHelper
{
    void DoStuff2( object self ) { OKDialog( "Action!" ); }
}

class CMyDialog : UIframe
{
    void DoStuff( object self )
    {
        Result( "\n Running method from other class: " )
        Alloc( CHelper ).DoStuff2()
    }

    object Init( object self )
    {
        TagGroup dlg, dlgItems
        dlg = DLGCreateDialog( "Test", dlgItems )
        dlgItems.DLGAddElement( DLGCreatePushButton( "Do Action", "DoStuff" ) )
        return self.super.init( dlg )
    }
}

Alloc( CMyDialog ).Init().Display( "My DLG" )

您可以做的是在创建 UI 对象时分配第二个 class 的对象。这可以在 UIclass 的构造方法中完成,也可以将代码放入 Init() 方法中。现在,第二个对象在范围内(因为它是对话框对象的一部分),您可以直接使用它:

此代码 运行 正确:

class CHelper
{
    void DoStuff2( object self ) { OKDialog( "Action!" ); }
}

class CMyDialog : UIframe
{
    object localHelper
    void DoStuff( object self )
    {
        Result( "\n Running method from other class: " )
        localHelper.DoStuff2()
    }

    object Init( object self )
    {
        localHelper = Alloc( CHelper )
        TagGroup dlg, dlgItems
        dlg = DLGCreateDialog( "Test", dlgItems )
        dlgItems.DLGAddElement( DLGCreatePushButton( "Do Action", "DoStuff" ))
        return self.super.init( dlg )
    }
}

Alloc( CMyDialog ).Init().Display( "My DLG" )

另一种选择是将 "second" class 作为库安装。这样,总是 可用于分配。但是,我不推荐该解决方案。

抱歉,如果这越来越广泛(另一个答案是 "short" 对你问题的回答,可能会变成 'accepted' 那个。)但是,我会回答并解释关于您的示例代码的内容。


第一步,我将伪代码修改为语法正确的 :

Class Helper{
    void functions1( object self ){ OKDialog("F1"); }
    void functions2( object self ){ OKDialog("F2"); }
    TagGroup functions3( object self ){
        self.functions1()
        self.functions2()
    }
}

void Function4(){
   Alloc(Helper).functions3()
}

void Function5(){
    OKDialog("F5")
}

void Function6(){
    Function4()
}

class Converter : UIframe   {

    object Local_helper
    void Action1( object self ){ OKDialog("A1"); }
    void Action2( object self ){
        local_Helper = Alloc( Helper )
        function6()
    }
}

TagGroup MakeFields1(){ return NewTagGroup(); }
TagGroup MakeFields2(){ return NewTagGroup(); }

//Main start here
void main()
{
    MakeFields1()
    MakeFields2()
    object dialog_frame = alloc(Converter).init()
}

此代码 "runs" 如果您执行它,但它不会做很多有用的事情。不过,它更像您的代码吗?下面列出了我所做的一些更改以及一些一般性 "object oriented coding" 信息,正如您在评论中指出的那样您可能没有正确理解这个概念。所以请原谅我,如果我宣扬你已经知道的事情。 (我保持简单。):

  • any method (except class constructor/destructor) require a return type, even if it is only void.

  • any class method requires a first parameter of type object which is the self-reference of an object of that class. It is conventionally called self to reflect this, but that is not a requirement.

  • When you declare a 'class' with all the script code, this code is not actually used at all. Only when you allocate a new object (with 'alloc') you actually "create" an object which contains the code. Only at that point does it actually exist in memory. If you allocate two different objects, you have two 'different' sets of that code (like starting a script twice), so whenever you want to call a method in a class, you need to specify of which object the method should be called! This is the reason for for the self variable in all the object-oriented coding.

  • I have also but the "main" script into a separate method and I don't want to go into details here, but you should know that when you create a variable of the type object on the main-level of a script, it will not get out of scope when the script finishes. The object will remain in memory! If you define it within any { } section, it will get out of scope once the section is finished.

好的,回到你的例子。先让我"optimize"说一些事情。虽然可以将 "global" 方法(即在主脚本级别定义的方法)与 "class methods"(即在 class 中定义的方法)结合使用,但我通常会避免使用它,除非确实存在好理由。一般来说,目的是将属于一个对象的所有代码封装到对象的class中。我假设您的 "MakeFields" 方法是创建 UI 的对话框布局?如果有,就把它放进去class!我通常会做类似的事情:

Class myDLG : UIFrame {
    taggroup CreateDlgTgs( object self ){
        taggroup dlg,dlgItems
        dlg = DLGCreateDialog( "Mine", dlgItems )
        dlgItems.DLGAddElement( DLGCreateLabel( "Label" ) )
        return dlg
    }
    object MyInit( object self ){
        taggroup dlg = self.CreateDlgTgs()
        self.Init( dlg )
        return self
    }
}

void main(){
    object myDLG = Alloc( myDLG ).MyInit()
    myDLG.Pose()
}
main()

但我会 "simplify" 不创建新的 MyInit 而是保持简单 Init。如果我自己的 Init 方法的签名与默认的 UIframe:Init 不同——也就是说,如果有任何额外的参数——那么代码就不会真正改变。但是如果我使用same签名,我需要调用对象的parent-method。这就是super关键字进来的原因。它表明我不想调用我的class MyDialog:Init的Init,而是InitInit MyDialog 的父级 class。所以代码变成:

Class myDLG : UIFrame {
    [...]
    object Init( object self ){
        taggroup dlg = self.CreateDlgTgs()
        object dlgObj = self.super.Init( dlg )
        return dlgObj
    }
}

void main(){
    object myDLG = Alloc( myDLG ).Init()
    myDLG.Pose()
}
main()

接下来,请注意 Init() 的 returned 值是脚本对象自身。有了这个 - 并直接使用其他 returned 值,事情可以简化为:

Class myDLG : UIFrame {
    [...]
    object Init( object self ){
        return self.super.Init( self.CreateDlgTgs() )
    }
}

void main(){
    Alloc( myDLG ).Init().Pose()
}
main()

更详细地查看分配行:

  • Alloc 为我创建了类型的对象。命令 returns 对象。
  • 要调用 class 的 Init 方法,需要使用对象作为第一个参数,因为签名 object Init( object self )。但是,方便的是第一个参数可以写在命令的前面,用点分隔。下面两个是等价的:
    ShowImage( img )img.ShowImage()
    因此下面的也是等价的:
    Init( Alloc(MyDialog) )Alloc(MyDialog).Init()
  • 现在,Init() 方法方便地再次 return 对象本身。所以我们可以简单地 "pipeline" 从对象发出的下一个命令: Pose( Init( Alloc(MyDialog) ) ) 变为 Alloc(MyDialog).Init().Pose()

另请注意,我不再有一个 object 变量保存我的对话框 - 因为在这种情况下我不需要它。现在,我已经不用考虑"the object on the main script stays in scope"这种特殊情况了,写成这样也可以:

Class myDLG : UIFrame {
    [...]
}

Alloc( myDLG ).Init().Pose()

当然,甚至可能不需要将 Pose 调用排除在 class 之外。您可以像这样封装所有内容:

Class myDLG : UIFrame {
    [...]

    void RunAll( object self ) {
    self.Init().Pose()
    }
}

Alloc( myDLG ).RunAll()

Without looking at your actual code, I can't advise what you need to do exactly. But most likely you want to incorporate Function4, Function5 and Function6 in a similar manner into either the Helper or the Converter class of yours. I assume it goes into the 'Helper' class here.

这将使你的伪代码变成:

Class Helper{
    void functions1( object self ){ OKDialog("F1"); }
    void functions2( object self ){ OKDialog("F2"); }
    TagGroup functions3( object self ){
        self.functions1()
        self.functions2()
    }
    void Function4( object self ){
        self.functions3()
    }
    void functions5( object self ){ OKDialog("F5"); }
    void Function6( object self ){
        self.function4()
    }
}

class Converter : UIframe   {

    object Local_helper
    void Action1( object self ){ OKDialog("A1"); }
    void Action2( object self ){
        local_Helper = Alloc( Helper )
        local_Helper.function6()
    }
    TagGroup MakeFields1( object self ){
        return NewTagGroup(); 
    }
    TagGroup MakeFields2( object self ){ 
        return NewTagGroup(); 
    }
}

//Main start here
void main()
{
    object dialog_frame = alloc(Converter)
    dialog_frame.MakeFields1()
    dialog_frame.MakeFields2()
    dialog_frame.init()
}

现在我们终于 return 回到原来的问题。我假设您的 Action1 方法是通过按下按钮触发的方法?那时您不能再分配助手 class - 因为当对话框对象位于内存中而没有有关原始源代码的信息(在其 class 之外)时,将调用此方法。本质上,发生的事情是:

1) Script is executed

2) All source code is "run" on the main level. Objects are created

3) The dialog is displayed (The dialog object stays in memory as long as the dialog is open)

4) The script code ends.

5) All objects (images etc.) not somehow 'anchored' in memory (f.e. by being displayed) get removed from memory. The "complete source code" of the script also is no longer in memory.

6) You now press the button.

7) The dialog-invoked method tries to allocate code, which no longer is in memory.

要克服这个问题,您需要按照我在第一个答案中的建议进行操作:在脚本源代码仍然可用时分配 Helper 对象,即在 Init() 阶段,但是 不是 在稍后调用的方法中。 然后您将 Helper 对象作为对话框对象的成员,因此它保持可用并可以被调用。您可以 而不是 像这样保留通用的 'global' 方法。这就是为什么您总是希望将 所有 代码封装在对象中并且根本不使用全局方法或变量。如果确实需要这样的全局方法,那么您必须将它们安装为 DM 的库,以便它们始终可用。