如何实时更新 DM 中的框字段

how to live update box field in DM

我想创建一个框,其中的字段由另一个函数更新(在我的示例中是一个循环)。 每次值更改时,显示都应更改。

我第一次尝试创建一个仅显示最后一个值的框。

    Class MyTestDialog: UIFrame
{
    TagGroup CreateMyDialog(Object self)
    {
        TagGroup DialogTG = DLGCreateDialog("useless text")
        TagGroup Fields,FieldItems
        Fields = DLGCreateBox("Current",FieldItems)
        FieldItems.DLGAddElement(DLGCreateRealField(42,15,3).DLGIdentifier("#RField"))
        DialogTG.DLGAddElement(Fields.DLGTableLayOut(3,1,0))
        Return DialogTG
    }

    Void Doit(Object self,number count)
    {
        for (count=0; count<5; count++)
        {
            self.LookUpElement("#RField").DLGValue(sum(getfrontimage())*count)
            self.Display("Text at top of box window")
            sleep(3)
        }
    }
    Object Init(Object self) return self.super.Init(self.CreateMyDialog())
}
Object MyBeamCurrentDisplay = Alloc(MyTestDialog).Init()
MyBeamCurrentDisplay.Display("Text at top of box window")
sleep(3)
MyBeamCurrentDisplay.Doit(5)

您的脚本确实有效(除非您不想每次都 re-display 对话框。),但因为 DM-script 本身以及 UI 更新调用在 DM 的主线程上处理,您看不到更新。

对话框更新了 5 次,但是对话框的 display 只有在脚本完成后才会更新,给 main-thread CPU 周期处理 UI 更新。

如果您将脚本放在 background-thread 上,您会看到它有效:

// $BACKGROUND$
Class MyTestDialog: UIFrame
{
    TagGroup CreateMyDialog(Object self)
    {
        TagGroup DialogTG = DLGCreateDialog("useless text")
        TagGroup Fields,FieldItems
        Fields = DLGCreateBox("Current",FieldItems)
        FieldItems.DLGAddElement(DLGCreateRealField(42,15,3).DLGIdentifier("#RField"))
        DialogTG.DLGAddElement(Fields.DLGTableLayOut(3,1,0))
        Return DialogTG
    }

    Void Doit(Object self,number count)
    {
        for (count=0; count<5; count++)
        {
            self.LookUpElement("#RField").DLGValue(sum(getfrontimage())*count)
            // self.Display("Text at top of box window")
            sleep(0.1)
        }
    }
    Object Init(Object self) return self.super.Init(self.CreateMyDialog())
}
Object MyBeamCurrentDisplay = Alloc(MyTestDialog).Init()
MyBeamCurrentDisplay.Display("Text at top of box window")
sleep(0.2)
MyBeamCurrentDisplay.Doit(20)

(代码的第一行必须 正好 // $BACKGROUND

另一种方法是将脚本保留在主线程上,但在 sleep(0.1) 之前添加 doEvents() 以便为主应用程序完成排队任务(如更新)提供一些 CPU 周期UI的。


我还尝试了一些更新和不同的线程,我认为您可能会发现以下示例很有用:

number TRUE = 1
number FALSE = 0

Class MyTestDialog: UIFrame
{
    object messageQueue
    object stoppedSignal
    number updateTimeSec

    void OnUpdateStateChanged(object self, taggroup checkboxTG )
    {
        if ( TRUE == checkboxTG.DLGGetValue() )
            self.StartThread( "RegularFrontImageUpdate" )
        else 
            messageQueue.PostMessage( Alloc(object) )   // Single message: Stop, so any object will do
    }
    
    number AboutToCloseDocument( object self, number verify )
    {
        result("\nAbout to close")
        messageQueue.PostMessage( Alloc(object) )   // Single message: Stop, so any object will do
        stoppedSignal.WaitOnSignal( infinity(), NULL )  // Ensure we don't kill the UI object while the update thread is still accessing it
        return FALSE
    }

    TagGroup CreateMyDialog(Object self)
    {
        TagGroup DialogTG = DLGCreateDialog("useless text")
        TagGroup Fields,FieldItems
        Fields = DLGCreateBox("Current Front Image Sum",FieldItems)
        FieldItems.DLGAddElement(DLGCreateStringField("No front image",30).DLGIdentifier("#StrField").DLGEnabled(FALSE))
        FieldItems.DLGAddElement(DLGCreateRealField(0,15,3).DLGIdentifier("#RField").DLGEnabled(FALSE))
        FieldItems.DLGAddElement(DLGCreateCheckbox("Update", TRUE, "OnUpdateStateChanged").DLGIdentifier("#ToggleCheck"))
        DialogTG.DLGAddElement(Fields.DLGTableLayOut(3,1,0))
        Return DialogTG
    }

    void RegularFrontImageUpdate(Object self)
    {
        stoppedSignal.ResetSignal()
        self.SetElementIsEnabled("#RField",TRUE)
        self.SetElementIsEnabled("#StrField",TRUE)
        result("\nEnabling auto-update")    
        while(TRUE)
        {
            object message = messageQueue.WaitOnMessage( updateTimeSec, null )
            if ( message.ScriptObjectIsValid() )
            {
                // Handle message. We just use 'empty' objects in this example
                //  as we only have a "single" message: Stop updating now!
                self.LookUpElement("#ToggleCheck").DLGValue( 0 )                    
                self.LookUpElement("#RField").DLGValue( 0 )
                self.SetElementIsEnabled("#RField",FALSE)
                self.SetElementIsEnabled("#StrField",FALSE)
                result("\nDisabling auto-update")   
                break;
            }
            else
            {

                // Timeout of waiting time. Just update             
                image front := FindFrontImage()
                
                if ( front.ImageIsValid() )
                {
                    self.LookUpElement("#RField").DLGValue( sum(front) )
                    self.LookUpElement("#StrField").DLGValue( front.GetName() )
                }
                else
                    self.LookUpElement("#ToggleCheck").DLGValue( 0 )
            }       
        }     
        stoppedSignal.SetSignal()   
    }
    object Launch(object self)
    {
        updateTimeSec = 0.1
        messageQueue = NewMessageQueue()
        stoppedSignal = NewSignal( false )
        self.Init(self.CreateMyDialog()).Display("Title")
        self.StartThread( "RegularFrontImageUpdate" )       
        return self
    }    
}
Alloc(MyTestDialog).Launch()