DigitalMicrograph で 2 つのスレッドを作成しましたが、スクリプトが実行されるとすぐに実行されます。
私は何か違うものが欲しい。
スレッド用の 2 つのボタン (スレッドの開始と停止) を考えてみましょう。
ボタンを押したときにのみスレッドをアクティブにするコードを追加するにはどうすればよいですか?
コード例があればとても助かります。
DigitalMicrograph で 2 つのスレッドを作成しましたが、スクリプトが実行されるとすぐに実行されます。
私は何か違うものが欲しい。
スレッド用の 2 つのボタン (スレッドの開始と停止) を考えてみましょう。
ボタンを押したときにのみスレッドをアクティブにするコードを追加するにはどうすればよいですか?
コード例があればとても助かります。
考慮すべき点がいくつかあります。
UIframe オブジェクト内から新しいオブジェクトを割り当てることはできません。(より正確に言うと、UI アクションによって呼び出されるメソッドから。fe は、コンストラクターで、または開始時に Init() メソッドで割り当てることができます。)したがって、事前にそれらを割り当ててから、UIframe オブジェクトにそれらについて知らせます。
UIframe オブジェクトにスレッド オブジェクトを認識させたい場合がよくありますが、スレッド オブジェクトにも UIframe オブジェクトを認識させたい場合があります。(スレッド オブジェクト内の何かが必要な場合に UI を変更できるようにするためです。)
オブジェクトをオブジェクトのメンバー変数として持つことは、少し危険です。これらのオブジェクトは、「保持」オブジェクトが解放された後にしか解放できないためです。2 つのオブジェクトがメンバー変数としてお互いを保持している場合、デッドロック状態になります! このため、弱い参照を使用することをお勧めします。メンバー変数としてobjectID番号のみを保持し、必要に応じてオブジェクトを検索します。
次のコード例は、出発点となるはずです。2 つのクラスとメイン コールで構成されます。コードはこの回答で分割されています。テスト用にコピーして単一のスクリプトファイルに貼り付けてください。
まず、スレッド オブジェクト:
class myThread:Thread
{
number linkedDLG_ID
number externalBreak
myThread( object self )
{
result( self.ScriptObjectGetID() + " created.\n" )
}
~myThread( object self )
{
result( self.ScriptObjectGetID() + " destroyed.\n" )
}
void SetLinkedDialogID( object self, number ID ) { linkedDLG_ID = ID; }
void InterruptAtNextChance( object self ) { externalBreak = 1; }
void RunThread( object self )
{
number maxLoop = 30
object callDLG = GetScriptObjectFromID( linkedDLG_ID )
externalBreak = 0
for( number i=0; i<maxLoop; i++ )
{
sleep( 0.1 )
Result( i + "\n" )
if ( callDLG.ScriptObjectIsValid() )
{
callDLG.DLGSetProgress( "progress", (i+1)/maxLoop )
callDLG.ValidateView()
}
if ( externalBreak )
break;
}
// Cleanup at end of thread
if ( callDLG.ScriptObjectIsValid() )
{
callDLG.DLGSetProgress( "progress", 0 )
callDLG.LookUpElement( "DBevel" ).DLGValue( 0 )
callDLG.ValidateView( )
}
}
}
すべてのスレッド化クラスはクラスThreadから派生します。
クラスには 2 つのメンバー変数があります。1 つは UI オブジェクトの ID を保持し、もう 1 つは実行中のスレッドを停止する「外部」呼び出しを許可する単純なブール値です。
最初の 2 つのメソッドは、コンストラクターとデストラクターです。この例ではそれらは実際には必要ありませんが、スクリプトの開発中にそれらを配置することをお勧めします。これは、そのクラスのオブジェクトがいつ作成され、いつ破棄されるかを結果ウィンドウに示すためです。これは、メモリ リークやデッドロックの状況を追跡するのに役立ちます。
次の 2 つのメソッドは、「外部」呼び出しで 2 つのメンバー変数を設定できるようにします。
RunThreadメソッドは、すべてのThreadクラスの心臓部です。これは、クラスMyThreadの派生元である親クラスThreadの対応するメソッドをオーバーライドするため、正確にこのシグネチャである必要があります。RunThreadメソッドは、メソッド StartThread() が呼び出されると、別のバックグラウンド スレッドで起動されます。( StartThread()はクラスThreadのメソッドです。)
RunThreadの実際のコードは 2 つの部分に分かれています。
ブール変数の値が変更された場合にクイック終了できるようにする「アクション ループ」。これが、外部呼び出しが中断できる方法です。これについては、もう少し下で説明します。
オブジェクトが UI オブジェクトに影響を与えることができる「クリーンアップ」部分。以下でも説明します。
次は UI クラスです。
class myDialog:UIframe
{
object callThread
myDialog( object self )
{
result( self.ScriptObjectGetID() + " created.\n" )
}
~myDialog( object self )
{
result( self.ScriptObjectGetID() + " destroyed.\n")
}
TagGroup CreateDLG( object self )
{
image i := IntegerImage( "", 1, 0, 25, 25)
i = 0; i[ 2 , 2 , 23 , 23 ] = 1;
image onImage, offImage
onImage = RGB( 0*i , 200*i , 0*i )
offImage = RGB( 200*i , 0*i , 0*i )
TagGroup tgItems, tg, button, label, progress
tg = DLGCreateDialog("Dialog",tgItems)
button = DLGCreateDualStateBevelButton( "DBevel", onImage, offImage, "StartPressed" )
progress = DLGCreateProgressBar( "progress" ).DLGfill( "X" )
label = DLGCreateLabel( "start/stop" )
tgItems.DLGAddElement( DLGGroupItems( button , label ).DLGTableLayout( 2 , 1 , 0 ) )
tgItems.DLGAddElement( progress )
return tg
}
object Init(object self, number callThreadID )
{
// Assign thread-object via weak-reference
callThread = GetScriptObjectFromID( callThreadID )
if ( !callThread.ScriptObjectIsvalid() )
Throw( "Invalid thread object passed in! Object of given ID not found." )
// Pass weak-reference to thread object
callThread.SetLinkedDialogID( self.ScriptObjectGetID() )
return self.super.init( self.CreateDLG() )
}
void StartPressed( object self )
{
number active = self.LookupElement( "DBevel" ).DLGGetValue()
if ( active )
callThread.StartThread()
else
callThread.InterruptAtNextChance()
}
}
すべてのダイアログ (UI) クラスは、クラスUIframeから派生します。
このクラスのメンバー変数は 1 つだけです。つまり、スレッド オブジェクトとなるオブジェクトです。
ここでも、デバッグを容易にするためのコンストラクタ/デストラクタ メソッドがあります。
CreateDLGメソッドは、ダイアログを記述する tagGroup を構築します。ここでは詳しく説明しませんが、基本的に、表示されると次のダイアログが作成されます。
Init()メソッドは、オブジェクトを初期化します。基本クラスUIframeのInit()メソッドは、説明的な TagGroup を必要とし、UI オブジェクト自体を返します。拡張されたInit()メソッドの最後の行でこれを呼び出し、クラス メソッドを使用して tagGroup を作成します。
return self.super.init( self.CreateDLG() )
前のコードは、スレッド オブジェクトを UI オブジェクトにリンクするものです。スレッド オブジェクトのオブジェクト ID である数値を渡します。対応するオブジェクトをメモリから取得し、それをローカル メンバー変数に割り当てます。(注: 変数は、オブジェクトのコピーやクローンではなく、オブジェクト自体を保持するようになりました!)
callThread = GetScriptObjectFromID( callThreadID )
すぐに、ルックアップが成功して実際に有効なオブジェクトが返されたかどうかを確認します。そうでない場合は、例外をスローしてスクリプトを停止します。これ以降、UI オブジェクトはスレッド オブジェクトを「含み」、それを使用できるようになります。
次に、バックリンクが来ます。UI オブジェクトが割り当てられたので、オブジェクト ID も持っています。この数値をスレッド オブジェクトに入力します。
callThread.SetLinkedDialogID( self.ScriptObjectGetID() )
これ以降、スレッド オブジェクトは UI オブジェクトに適切にリンクされます。myThreadクラスに戻ると、メソッドでオブジェクトを検索してローカルに格納するという同じトリックを使用していることに気付くでしょうRunThread()
。
StartPressed()は、ダイアログ ボタンにリンクされたメソッドです。このボタンはベベル ボタンであるため、ベベル ボタンが変更された後の状態である状態を照会し、それに応じて動作します。スレッド オブジェクトの RunThread() メソッドをバックグラウンド スレッドとして起動するか、それに応じて「割り込み」メソッドを呼び出します。これは単にブール変数を設定します。
最後にメインスクリプト:
void StartScript()
{
object threadObject = alloc( myThread )
object dlgObject = alloc( myDialog ).Init( threadObject.ScriptObjectGetID() )
dlgObject.display( "Dialog" )
}
StartScript()
ここではあまり進んでいません。最初に myThread クラスの threadObject を作成し、次にダイアログ UI オブジェクトを作成します。
ダイアログ オブジェクトを既存のthreadObject の ID で初期化し、モードレスダイアログとして表示します。
注意すべき点:
DigitalMicrograph スクリプトでオブジェクト変数を使用する場合は常に、それらを構造ブロックに配置する必要があります。これにより、構造ブロックが残っているときに、オブジェクトが範囲外になり、削除されることが保証されます。メイン スクリプトで定義および割り当てられたオブジェクト変数は、スクリプトの最後で破棄されません。このため、メイン スクリプトをメソッド自体にカプセル化しました。
この例では、2 つの異なるリンク方法を使用しています。
Direct : myDialogクラスは実際にはスレッド オブジェクト自体をメンバー変数として保持します。ID のみで初期化しましたが、すぐにオブジェクトをメンバー変数にリンクしました。
弱参照: myThreadクラスは、ダイアログ オブジェクトのオブジェクト ID のみをメンバー変数として保持します。
なぜこれを行ったのですか?myThreadクラスがダイアログ オブジェクトをメンバーとして保持する場合、2 つのオブジェクトはデッドロック状態で互いに保持されます。どちらか一方が原因で破壊されることはありません。しかし、なぜmyDialogクラスに同じものを使用しなかったのでしょうか? ダイアログをバックグラウンド スレッド自体でモードレス ダイアログとして表示したいからです。
メインスクリプトを考えてみましょう:
しかし、スクリプトが終了すると、オブジェクト変数threadObjectとdlgObjectは範囲外になります! 何かがそれらをメモリに保持しない限り、それらはすぐに破壊されます。モードレス ダイアログとして表示したため、dlgObject はメモリ内に残ります。対応するダイアログ ウィンドウが閉じられると、解放されます。しかし、何が threadObject を保持しているのでしょうか? 何もない!RunThread ()メソッドが終了すると、解放され、その後破棄されます。ただし、dlgObjectのメンバーであるため、破棄されません。