首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Shady中控制逐帧绘制

在Shady中控制逐帧绘制
EN

Stack Overflow用户
提问于 2019-05-29 03:45:41
回答 1查看 38关注 0票数 0

我正在努力理解如何控制Shady在屏幕上绘制的内容的流动。我习惯于使用adding,在这个工具箱中,您可以通过在后台缓冲区上绘制来不断向帧添加内容,然后通过调用flip()将其显式地推送到屏幕上。使用Shady时,事情似乎是自动发生的,因此当您将刺激对象添加到World时,它会尽快绘制。但是如果我必须添加几个刺激物,我如何保证屏幕上没有任何更新,直到它们都被绘制出来?

例如,假设我想要绘制一个空白屏幕,并在屏幕上方的左上角绘制一个小方块。我可以这样做:

代码语言:javascript
复制
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?也许它们可以组合成一个刺激对象,但我希望将它们分开(在我的实际问题中,小方块实际上是用来触发光电池的,所以我需要能够在任务期间的不同时间对其进行闪光处理)。

EN

回答 1

Stack Overflow用户

发布于 2019-05-29 05:15:19

首先,阅读Concurrency上的Shady文档将是一个好主意。前两个示例(标题为“运行单线程”)没有您所担心的问题,因为Stimulus实例是在实际呈现单个帧之前创建的。下面是一个简单的例子:

代码语言:javascript
复制
import Shady
w = Shady.World(threaded=False)
# s1 = ...    
# s2 = ...
# ...    
w.Run()

除此之外,你是对的,Shady固有的范式转变之一就是你自己永远不会调用任何flip()等价物。一开始可能会觉得不熟悉,但请放心:在我们基于Shady构建的应用程序中,我们并不怀念自己调用flip()的日子。对刺激参数的更新在回调中完成,例如:

可调用所谓的“动画回调”,您可以选择将其安装到每个可分配给任何instance;

  • dynamic或Stimulus属性的WorldStimulus instance;

  • dynamic(可调用)值中(这些值在任何响应按键等的动画callbacks);

  • event回调之后立即求值。

你在同一个回调中所做的更改保证会呈现在同一帧上--这就是我们如何在不运行单线程的情况下严格控制计时的答案。确实,如果您运行多线程并一个接一个地发出w.Stimulus()调用,就不能保证它们会出现在同一个框架中(事实上,它们肯定会出现在不同的框架中,因为非绘制线程中的.Stimulus()调用实际上会将创建刺激的实际工作推迟到绘制线程中,然后等到完成后才返回)。可能的解决方案是(1)单线程运行;(2)在专用的Prepare()方法中执行所有w.Stimulus()创建调用,如文档的第二个示例中所示;或(3)确保激励在创建时具有visible=False,并仅在稍后使其可见。在所有这些情况下,我们都要小心地将刺激的创建(可能很慢)与其属性的操作分开。

前两个回调类型与您所描述的内容最相关。Shady在"Making properties dynamic"上的文档中对它们进行了介绍。在他们提供的框架内,有许多不同的方法(在Python和Shady中总是如此)来实现您所描述的目标。就我个人而言,我喜欢使用StateMachine实例作为我的动画回调。下面是我如何创建一个简单的重复呈现的刺激,它的开始是通过一个传感器贴片闪烁的单个帧来预测的:

代码语言:javascript
复制
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
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56349120

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档