Во-первых, было бы неплохо прочитать документацию Shady по Concurrency .Первые два примера (под заголовком «Запуск однопоточного») не вызывают проблем, о которых вы беспокоитесь, поскольку экземпляры Stimulus
создаются до того, как на самом деле отображается один кадр.Вот простой пример:
import Shady
w = Shady.World(threaded=False)
# s1 = ...
# s2 = ...
# ...
w.Run()
Помимо этого, вы правы в том, что одно из внутренних изменений парадигмы Шейди заключается в том, что вы никогда не называете flip()
эквивалентом себя.И поначалу это может показаться незнакомым, но будьте уверены: в приложениях, которые мы создали на Shady, мы не пропускаем дни вызова flip()
сами.Обновления параметров стимула выполняются в обратных вызовах, таких как:
так называемый «анимационный обратный вызов», который вы можете дополнительно установить в каждый экземпляр World
или Stimulus
;
динамические (вызываемые) значения, которые можно присвоить любому свойству World
или Stimulus
(они оцениваются сразу после любых обратных вызовов анимации);
обратные вызовы событий, которые реагируют на нажатия клавиш и т. Д.
Изменения, которые вы выполняете в рамках одного и того же обратного вызова, гарантированно будут отображаться в одном кадре, иэто ответ на то, как мы строго контролируем время, даже когда не работает однопоточно.Это правда, что если вы запустите многопоточность и выполните один w.Stimulus()
вызов за другим, они не обязательно появятся в одном кадре (фактически, они гарантированно появятся в различных кадрах, потому что.Stimulus()
вызов в потоке без рисования фактически откладывает реальную работу по созданию стимула в поток рисования, а затем ждет , пока это не будет завершено, прежде чем вернуться).Возможные противоядия: (1) запуск однопоточный;(2) выполнить все вызовы создания w.Stimulus()
в выделенном методе Prepare()
, как во втором примере документации;или (3) убедиться, что стимулы имеют visible=False
при создании, и сделать их видимыми только позже.Во всех этих случаях мы осторожно отделяем создание стимулов (которые могут быть медленными) от манипуляций их свойств.
Первые два типа обратного вызова:наиболее актуально для того, что вы описываете.Они описаны в документации Шейди по «Создание свойств динамического» * 1049 *.В рамках, которые они предоставляют, есть (как всегда в 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