0

Shady によって画面に描かれているものの流れを制御する方法を理解するのに苦労しています。私は Psychtoolbox に慣れています。ここでは、バックバッファーに描画してフレームに追加し続け、flip() を呼び出して明示的に画面にプッシュします。Shady を使用すると、Stimulus オブジェクトを World に追加すると、できるだけ早く描画されるように、自動的に行われるように見えます。しかし、いくつかの刺激を追加する必要がある場合、それらがすべて描画されるまで、画面上で何も更新されないことをどのように保証できますか?

たとえば、空白の画面を描画し、その左上隅に小さな正方形を描きたいとします。私は次のようなことができます:

w = Shady.World()
s1 = w.Stimulus(None, 'blank', envelopeSize=[1920, 1200], backgroundColor=[0, 0, 0], z=0)
s2 = w.Stimulus(None, 'square', envelopeSize=[20, 20], x=-950, y=590, backgroundColor=[1, 1, 1], z=1)

しかし、フレーム n 以降から s1 が描画され、フレーム n+1 以降から s2 が描画されないようにするにはどうすればよいでしょうか? おそらくそれらは 1 つの Stimulus オブジェクトに組み合わせることができますが、私はそれらを別々に保ちたいと思います (私の実際の問題では、小さな正方形は実際にフォトセルをトリガーするために使用されるため、単一の刺激のためにそれをフラッシュできる必要があります)。フレーム、タスク中のさまざまな時間)。

4

1 に答える 1

0

まず、Concurrencyに関する Shady のドキュメントを読むことをお勧めします。Stimulus最初の 2 つの例 ("Running single-threaded" という見出しの下)には、1 つのフレームが実際にレンダリングされる前にインスタンスが作成されるため、懸念されている問題はありません。簡単な例を次に示します。

import Shady
w = Shady.World(threaded=False)
# s1 = ...    
# s2 = ...
# ...    
w.Run()

その点を超えると、Shady 固有のパラダイム シフトの 1 つは、flip()自分で同等のものを決して呼び出さないということです。最初はなじみがないと感じるかもしれませんが、安心してください。Shady で構築したアプリケーションでは、flip()自分自身を呼び出す日々を逃すことはありません。スティミュラス パラメータの更新は、次のようなコールバックで行われます。

  • WorldオプションですべてまたはStimulusインスタンスにインストールできる、いわゆる「アニメーション コールバック」 。

  • WorldまたはStimulusプロパティに割り当てることができる動的な (呼び出し可能な) 値(これらは、アニメーション コールバックの直後に評価されます)

  • キー押下などに応答するイベント コールバック。

同じコールバック内で行った変更は、同じフレームでレンダリングされることが保証されます。これが、シングル スレッドで実行していない場合でもタイミングを厳密に制御する方法に対する答え です。マルチスレッドを実行し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
于 2019-05-28T21:15:19.043 に答える