まず、Concurrencyに関する Shady のドキュメントを読むことをお勧めします。Stimulus
最初の 2 つの例 ("Running single-threaded" という見出しの下)には、1 つのフレームが実際にレンダリングされる前にインスタンスが作成されるため、懸念されている問題はありません。簡単な例を次に示します。
import Shady
w = Shady.World(threaded=False)
# s1 = ...
# s2 = ...
# ...
w.Run()
その点を超えると、Shady 固有のパラダイム シフトの 1 つは、flip()
自分で同等のものを決して呼び出さないということです。最初はなじみがないと感じるかもしれませんが、安心してください。Shady で構築したアプリケーションでは、flip()
自分自身を呼び出す日々を逃すことはありません。スティミュラス パラメータの更新は、次のようなコールバックで行われます。
同じコールバック内で行った変更は、同じフレームでレンダリングされることが保証されます。これが、シングル スレッドで実行していない場合でもタイミングを厳密に制御する方法に対する答え です。マルチスレッドを実行しw.Stimulus()
、呼び出しを次々に発行する場合、それらが同じフレームに表示されることが保証されないことは事実です (実際、非描画スレッドでの呼び出しは実際に延期されるため、異なるフレームに表示されることが保証されます)。.Stimulus()
描画スレッドへの刺激作成の実際の作業を行い、それが完了するまで待機してから戻ります)。(1) シングルスレッドで実行する。(2) すべてのw.Stimulus()
作成呼び出しを専用で実行するPrepare()
ドキュメントの 2 番目の例のように、メソッド。または (3) 作成時に刺激が存在することを確認し、visible=False
後でのみ表示するようにします。これらのすべてのケースで、刺激の作成(時間がかかる可能性があります)とそのプロパティの操作を慎重に分離します。
最初の 2 つのコールバック タイプは、説明内容に最も関連しています。それらについては、 「Making properties dynamic」に関する Shady のドキュメントで説明されています。彼らが提供するフレームワーク内には、あなたが説明した目標を達成するためのさまざまな方法があります (Python や Shady ではいつものように)。StateMachine
個人的には、アニメーション コールバックとしてインスタンスを使用するのが好きです。以下は、単純な繰り返し提示される刺激を作成する方法です。その開始は、単一フレームのセンサー パッチの点滅によって予告されます。
import random
import Shady
w = Shady.World(canvas=True, gamma=2.2)
# STIMULI
Shady.Stimulus.SetDefault(visible=False)
# let all stimuli be invisible by default when created
gabor = w.Sine(pp=0)
sensorPatch = w.Stimulus(
size = 100, # small,
color = 1, # bright,
anchor = Shady.UPPER_LEFT, # with its top-left corner stuck...
position = w.Place(Shady.UPPER_LEFT), # to the top-left corner of the screen
)
# STATE MACHINE
sm = Shady.StateMachine()
@sm.AddState
class InterTrialInterval(sm.State):
# state names should be descriptive but can be anything you want
def duration(self):
return random.uniform(1.0, 3.0)
next = 'PresentGabor'
@sm.AddState
class PresentGabor(sm.State):
def onset(self):
gabor.visible = True
sensorPatch.visible = Shady.Impulse() # a dynamic object: returns 1.0 the first time it is evaluated, then 0.0 thereafter
duration = 2.0
def offset(self):
gabor.visible = False
next = 'InterTrialInterval'
w.SetAnimationCallback( sm )
# now sm(t) will be called at every new time `t`, i.e. on every frame,
# and this will in turn call `onset()` and `offset()` whenever appropriate