How to loop tweens without repeating similar code segments? - animation

I'm oscillating my character between 2 positions as such:
Idle -> Run & move right -> Idle -> Run & move left -> (Repeat)
extends Sprite
func _ready():
var tween = get_tree().create_tween().set_loops()
## Idle 1 ##
tween.tween_callback(animation_player,"play",["Idle"]) # plays idle animation
tween.tween_interval(2)
##
## Running 1 ##
tween.tween_callback(animation_player,"play",["Running"]) # starts running animation
tween.tween_property(self,"flip_h", false, 0)
tween.tween_property(self,"position:x", 500.0, 2) # move position to 1000
##
## Idle 2 ##
tween.tween_callback(animation_player,"play",["Idle"]) # plays idle animation
tween.tween_interval(2)
##
## Running 2 ##
tween.tween_callback(animation_player,"play",["Running"]) # starts running animation
tween.tween_property(self,"flip_h", true, 0)
tween.tween_property(self,"position:x", -500.0, 2) # move position to 1000
##
and it works fine but the issue is that I have to write the Idle & Run Segment twice, which is really annoying
I tried this:
func _ready():
var tween = get_tree().create_tween().set_loops()
## Idle ##
tween.tween_callback(animation_player,"play",["Idle"]) # plays idle animation
tween.tween_interval(2) # pause for 2 seconds
##
## Running ##
tween.tween_callback(animation_player,"play",["Running"]) # starts running animation
tween.tween_property(self,"flip_h", !flip_h, 0)
tween.tween_property(self,"position:x", position.x*-1, 2) # move position to 1000
##
but it seems that the literal value of the variable gets passed instead of the new one each time the loop runs
Is there anything I'm missing? or is there no work-around for this?

This is working as intended. When you create the tweens the values are recorded. So that !flip_h is recorded, and that position.x*-1 is recorded.
As per workaround… This is what I have been able to come up with:
var flip_position:float
var advancement:float setget set_advancement
func set_advancement(mod_value:float) -> void:
advancement = mod_value
position.x = flip_position + mod_value * (-1.0 if flip_h else 1.0)
func do_flip() -> void:
flip_h = !flip_h
flip_position = position.x
advancement = 0.0
func _ready() -> void:
var tween := get_tree().create_tween().set_loops()
## Idle ##
tween.tween_callback(animation_player,"play",["Idle"])
tween.tween_interval(2)
##
## Running ##
tween.tween_callback(animation_player,"play",["Running"])
tween.tween_callback(self,"do_flip")
tween.tween_property(self,"advancement", 500.0, 2)
##
Here I'm using a do_flip method so flip_h can change value. Furthermore, I'm using flip_h to know what direction it should run, and in flip_h I store from where it should run. Then I can make an advancement property which moves the character from the recorded position.

I found this solution but it seems like a hack, please let me know if you have any proper method
extends Sprite
var running_direction=0 # 0=Right, 1=Left
var running_location:float=400
func toggle_run_direction():
running_location*=-1
if(running_direction): # left
self.flip_h=true
running_direction=0
else: # right
self.flip_h=false
running_direction=1
# looping animations
func loop_run_demo():
var tween = get_tree().create_tween()
## Idle ##
tween.tween_callback(animation_player,"play",["Idle"]) # plays idle animation
tween.tween_interval(2) # pause for 2 seconds
##
tween.tween_callback(self,"toggle_run_direction")
## Running ##
tween.tween_callback(animation_player,"play",["Running"])
tween.tween_property(self,"position:x", running_location, 1.5)
##
tween.connect("finished",self,"loop_run_demo")
func _ready():
loop_run_demo()

Related

Why cannot see error bar cap , when I move the position using ax.axes[0].lines[2].set_xdata()

I try to move the bar and error bar to the center of tickmark. using ax.axes.patches, move bar by bar.set_x(current_pos+(current_width)), and move the error bar by ax.axes.lines[0].set_xdata(current_pos+(current_width*1.5)).
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
%matplotlib inline
df = sns.load_dataset("penguins")
ax = sns.barplot(data=df, x="island", y="body_mass_g", ci = "sd", capsize = 0.2, hue = "species")
for i, bar in enumerate(ax.axes.patches):
# move the missing to the centre
current_width = bar.get_width()
current_pos = bar.get_x()
if i == 0:
#move bar
bar.set_x(current_pos+(current_width))
#change color and move error bar
ax.axes.lines[0].set_color("red")
ax.axes.lines[0].set_xdata(current_pos+(current_width*1.5))
#change color of error bar upper cap
ax.axes.lines[2].set_color("red")
plt.show();
when I change the position of the error bar upper cap (ax.axes.lines2.set_xdata(current_pos+(current_width*1.5))), the cap is gone.
How can I set error cap visible or move the error cap?
ax = sns.barplot(data=df, x="island", y="body_mass_g", ci = "sd", capsize = 0.2, hue = "species")
for i, bar in enumerate(ax.axes.patches):
# move the missing to the centre
current_width = bar.get_width()
current_pos = bar.get_x()
if i == 0:
#move bar
bar.set_x(current_pos+(current_width))
#change color and move error bar
ax.axes.lines[0].set_color("red")
ax.axes.lines[0].set_xdata(current_pos+(current_width*1.5))
#change color of error bar upper cap
ax.axes.lines[2].set_color("red")
#change position of error bar upper cap
ax.axes.lines[2].set_xdata(current_pos+(current_width*1.5))
plt.show();
The errorbar lines have two coordinates for start and end. For the two horizontal lines (line1 and line[2]), you should be using the get_xdata() and then adding the offset. In the below code, I have updated the the same by reading the data into a temp variable called newline, then updating it and finally updating it back using set_xdata(). I think you might want to do it for the vertical line (line[0]) as well, but since it works without this, perhaps you can leave it as is. Below is the code...
df = sns.load_dataset("penguins")
ax = sns.barplot(data=df, x="island", y="body_mass_g", ci = "sd", capsize = 0.2, hue = "species")
for i, bar in enumerate(ax.axes.patches):
# move the missing to the centre
current_width = bar.get_width()
current_pos = bar.get_x()
if i == 0:
#move bar
bar.set_x(current_pos+(current_width))
#change color and move error bar
ax.axes.lines[0].set_color("red")
ax.axes.lines[0].set_xdata(current_pos+(current_width*1.5))
#change color of error bar upper cap
ax.axes.lines[1].set_color("red")
newline=ax.axes.lines[1].get_xdata() ## Read the xdata
newline[0] += current_width ## Add offset to first parameter
newline[1] += current_width ## Add offset to second parameter
ax.axes.lines[1].set_xdata(newline) ## Set data of the line
ax.axes.lines[2].set_color("red") ##Do the same for second line
newline=ax.axes.lines[2].get_xdata()
newline[0] += current_width
newline[1] += current_width
ax.axes.lines[2].set_xdata(newline)
plt.show();

How do I skip to a position in the animation with an animationplayer in Godot?

I'm making a game in godot and have to store the animation position because the animation gets suspended by another one. I want to continue the animation where it was before and that's why I have to store the animation position and then set it to the stored value.
I've tried setting it (didn't work) and in the documentation and other places in the internet I haven't found anything helpful.
This is the script on it:
extends KinematicBody2D
onready var animation_player = $AnimationPlayer
var hurt_anim_playing: bool = false
var hurt_anim_progress: float = 0
func _ready():
animation_player.play("idle")
pass
func _physics_process(delta):
# for each touching heart, get hurt
for body in hitbox.get_overlapping_bodies():
if body.has_method("heart"):
G.health -= 1
hurt_anim_playing = true
hurt_anim_progress = animation_player.current_animation_position
animation_player.play("hurt")
update_sprite()
body.queue_free()
func die():
dieLayer.visible = true
get_tree().paused = true
func update_sprite():
sprite.frame = G.max_health - G.health
if G.health == 0:
die()
func _on_AnimationPlayer_animation_finished(anim_name):
if anim_name == "hurt":
hurt_anim_playing = false
animation_player.play("idle")
animation_player.current_animation_position = hurt_anim_progress
Actually I wanted to set the animation position and let the animation continue where it stopped, but instead I got an error
The problem is that current_animation_position has only a getter, not a setter:
https://docs.godotengine.org/en/3.1/classes/class_animationplayer.html#class-animationplayer-property-current-animation-position
To set the animation to a specific point, you can use advance(float delta), if you want to handle everything between start and the resuming point or seek(float seconds, bool update=false), if you just want to jump to the new position. You may test out, if you need to set update to true. The documentation says "If update is true, the animation updates too, otherwise it updates at process time."
Your code would simply look like this:
func _physics_process(delta):
# for each touching heart, get hurt
for body in hitbox.get_overlapping_bodies():
if body.has_method("heart"):
G.health -= 1
hurt_anim_playing = true
hurt_anim_progress = animation_player.current_animation_position
animation_player.play("hurt")
update_sprite()
body.queue_free()
func _on_AnimationPlayer_animation_finished(anim_name):
if anim_name == "hurt":
hurt_anim_playing = false
animation_player.play("idle")
animation_player.seek(hurt_anim_progress) #maybe (hurt_anim_progress, true)

Python-ONVIF PTZ control -- Absolute and Relative Move

I am developing an application in Python to control ONVIF-compatible cameras.
Software: Debian Wheezy, Python 2.7, Quatanium python-onvif client
Hardware: Raspberry Pi 2 B, IP camera on local router, wifi/VNC for development
The required PTZ functions include Absolute Move, Relative Move, Continuous Move, Stop and using Preset positions. With the extracted test code below, I have all of that working except Absolute and Relative Moves. All of this code executes without any errors but the camera does not move for the Absolute or Relative Moves. I hope someone can suggest the problem with those two functions. The example is a bit long but I have tried to include enough code to show the contrast between working and non-working (with upper-case comments) portions for reference and test.
A test sketch:
#!/usr/bin/python
#-------------------------------------------------------------------------------
#Test of Python and Quatanium Python-ONVIF with NETCAT camera PT-PTZ2087
#ONVIF Client implementation is in Python
#For IP control of PTZ, the camera should be compliant with ONVIF Profile S
#The PTZ2087 reports it is ONVIF 2.04 but is actually 2.4 (Netcat said text not changed after upgrade)
#------------------------------------------------------------------------------
import onvifconfig
if __name__ == '__main__':
#Do all setup initializations
ptz = onvifconfig.ptzcam()
#*****************************************************************************
# IP camera motion tests
#*****************************************************************************
print 'Starting tests...'
#Set preset
ptz.move_pan(1.0, 1) #move to a new home position
ptz.set_preset('home')
# move right -- (velocity, duration of move)
ptz.move_pan(1.0, 2)
# move left
ptz.move_pan(-1.0, 2)
# move down
ptz.move_tilt(-1.0, 2)
# Move up
ptz.move_tilt(1.0, 2)
# zoom in
ptz.zoom(8.0, 2)
# zoom out
ptz.zoom(-8.0, 2)
#Absolute pan-tilt (pan position, tilt position, velocity)
#DOES NOT RESULT IN CAMERA MOVEMENT
ptz.move_abspantilt(-1.0, 1.0, 1.0)
ptz.move_abspantilt(1.0, -1.0, 1.0)
#Relative move (pan increment, tilt increment, velocity)
#DOES NOT RESULT IN CAMERA MOVEMENT
ptz.move_relative(0.5, 0.5, 8.0)
#Get presets
ptz.get_preset()
#Go back to preset
ptz.goto_preset('home')
exit()
The referenced class:
#*****************************************************************************
#IP Camera control
#Control methods:
# rtsp video streaming via OpenCV for frame capture
# ONVIF for PTZ control
# ONVIF for setup selections
#
# Starting point for this code was from:
# https://github.com/quatanium/python-onvif
#*****************************************************************************
import sys
sys.path.append('/usr/local/lib/python2.7/dist-packages/onvif')
from onvif import ONVIFCamera
from time import sleep
class ptzcam():
def __init__(self):
print 'IP camera initialization'
#Several cameras that have been tried -------------------------------------
#Netcat camera (on my local network) Port 8899
self.mycam = ONVIFCamera('192.168.1.10', 8899, 'admin', 'admin', '/etc/onvif/wsdl/')
#This is a demo camera that anyone can use for testing
#Toshiba IKS-WP816R
#self.mycam = ONVIFCamera('67.137.21.190', 80, 'toshiba', 'security', '/etc/onvif/wsdl/')
print 'Connected to ONVIF camera'
# Create media service object
self.media = self.mycam.create_media_service()
print 'Created media service object'
print
# Get target profile
self.media_profile = self.media.GetProfiles()[0]
# Use the first profile and Profiles have at least one
token = self.media_profile._token
#PTZ controls -------------------------------------------------------------
print
# Create ptz service object
print 'Creating PTZ object'
self.ptz = self.mycam.create_ptz_service()
print 'Created PTZ service object'
print
#Get available PTZ services
request = self.ptz.create_type('GetServiceCapabilities')
Service_Capabilities = self.ptz.GetServiceCapabilities(request)
print 'PTZ service capabilities:'
print Service_Capabilities
print
#Get PTZ status
status = self.ptz.GetStatus({'ProfileToken':token})
print 'PTZ status:'
print status
print 'Pan position:', status.Position.PanTilt._x
print 'Tilt position:', status.Position.PanTilt._y
print 'Zoom position:', status.Position.Zoom._x
print 'Pan/Tilt Moving?:', status.MoveStatus.PanTilt
print
# Get PTZ configuration options for getting option ranges
request = self.ptz.create_type('GetConfigurationOptions')
request.ConfigurationToken = self.media_profile.PTZConfiguration._token
ptz_configuration_options = self.ptz.GetConfigurationOptions(request)
print 'PTZ configuration options:'
print ptz_configuration_options
print
self.requestc = self.ptz.create_type('ContinuousMove')
self.requestc.ProfileToken = self.media_profile._token
self.requesta = self.ptz.create_type('AbsoluteMove')
self.requesta.ProfileToken = self.media_profile._token
print 'Absolute move options'
print self.requesta
print
self.requestr = self.ptz.create_type('RelativeMove')
self.requestr.ProfileToken = self.media_profile._token
print 'Relative move options'
print self.requestr
print
self.requests = self.ptz.create_type('Stop')
self.requests.ProfileToken = self.media_profile._token
self.requestp = self.ptz.create_type('SetPreset')
self.requestp.ProfileToken = self.media_profile._token
self.requestg = self.ptz.create_type('GotoPreset')
self.requestg.ProfileToken = self.media_profile._token
print 'Initial PTZ stop'
print
self.stop()
#Stop pan, tilt and zoom
def stop(self):
self.requests.PanTilt = True
self.requests.Zoom = True
print 'Stop:'
#print self.requests
print
self.ptz.Stop(self.requests)
print 'Stopped'
#Continuous move functions
def perform_move(self, timeout):
# Start continuous move
ret = self.ptz.ContinuousMove(self.requestc)
print 'Continuous move completed', ret
# Wait a certain time
sleep(timeout)
# Stop continuous move
self.stop()
sleep(2)
print
def move_tilt(self, velocity, timeout):
print 'Move tilt...', velocity
self.requestc.Velocity.PanTilt._x = 0.0
self.requestc.Velocity.PanTilt._y = velocity
self.perform_move(timeout)
def move_pan(self, velocity, timeout):
print 'Move pan...', velocity
self.requestc.Velocity.PanTilt._x = velocity
self.requestc.Velocity.PanTilt._y = 0.0
self.perform_move(timeout)
def zoom(self, velocity, timeout):
print 'Zoom...', velocity
self.requestc.Velocity.Zoom._x = velocity
self.perform_move(timeout)
#Absolute move functions --NO ERRORS BUT CAMERA DOES NOT MOVE
def move_abspantilt(self, pan, tilt, velocity):
self.requesta.Position.PanTilt._x = pan
self.requesta.Position.PanTilt._y = tilt
self.requesta.Speed.PanTilt._x = velocity
self.requesta.Speed.PanTilt._y = velocity
print 'Absolute move to:', self.requesta.Position
print 'Absolute speed:',self.requesta.Speed
ret = self.ptz.AbsoluteMove(self.requesta)
print 'Absolute move pan-tilt requested:', pan, tilt, velocity
sleep(2.0)
print 'Absolute move completed', ret
print
#Relative move functions --NO ERRORS BUT CAMERA DOES NOT MOVE
def move_relative(self, pan, tilt, velocity):
self.requestr.Translation.PanTilt._x = pan
self.requestr.Translation.PanTilt._y = tilt
self.requestr.Speed.PanTilt._x = velocity
ret = self.requestr.Speed.PanTilt._y = velocity
self.ptz.RelativeMove(self.requestr)
print 'Relative move pan-tilt', pan, tilt, velocity
sleep(2.0)
print 'Relative move completed', ret
print
#Sets preset set, query and and go to
def set_preset(self, name):
self.requestp.PresetName = name
self.requestp.PresetToken = '1'
self.preset = self.ptz.SetPreset(self.requestp) #returns the PresetToken
print 'Set Preset:'
print self.preset
print
def get_preset(self):
self.ptzPresetsList = self.ptz.GetPresets(self.requestc)
print 'Got preset:'
print self.ptzPresetsList[0]
print
def goto_preset(self, name):
self.requestg.PresetToken = '1'
self.ptz.GotoPreset(self.requestg)
print 'Going to Preset:'
print name
print
#Ottavio, Sorry that I did not make it clear that the camera I used for this test, a Netcat PT-PTZ2084XM-A reported via ONVIF query that it did support Absolute and Relative moves. I have subsequently verified via the onvif.org site that this camera has not been tested and verified to meet onvif standards. I also have verified that the above code does work correctly with a Amcrest IP2M-841B ptz camera. The upshot of all of this is to never trust the claim that a camera is ONVIF 2.x compatible without testing it. Even the Amcrest has problems with both ONVIF and cgi commands for zoom. Neither Netcat nor Amcrest have been very helpful in resolving these technical problems.
AbsoluteMake and RelativeMove in the Profile S specifications are CONDITIONAL MANDATORY, thus it is not guaranteed a-priori that they are supported.
You need to check the camera's features.

World of Warcraft UI - custom frame without addon

In World Of Warcraft I have created a little coords script that outputs current coords:
local function ou(self,elapsed)
px,py=GetPlayerMapPosition("player")
DEFAULT_CHAT_FRAME:AddMessage(format("( %s ) [%f , %f]",GetZoneText(), px *100, py *100))
end
local f = CreateFrame("frame")
f:SetScript("OnUpdate", ou)
This however spams default chat frame...
How would I create custom frame and how would I access it?
(I can't use custom channel with SendChatMessage)
...I would like to do this WITHOUT making an addon, thanks :)
I found a solution in storing frame in global variable, as I don't intend to create a plugin, the whole program requires a few macros (macro's maximum number of characters is 255).
First macro - prepare function that will set frame attributes later
f = input frame that will be set
x = x coordinate of position
y = y coordinate of position
function setMyFrame(f,x,y)
f:SetSize(288,100)
f:SetPoint("TOPLEFT",UIParent,"TOPLEFT",x,y)
f.text = f.text or f:CreateFontString(nil,"ARTWORK","QuestFont_Shadow_Huge")
f.text:SetAllPoints(true)
end
Second macro - prepare coords function that will set current coords as frame's text
ctotel = time elapsed since last update of the frame
creft = how often should the frame be updated in SECONDS - good one is 0.1 - 10 times a second is performance friendly and fast enaugh to coords update
f = input frame that will be updated
i = "how long it's been since the last update call cycle" (you don't set that - it is inherited from the WoW system)
ctotel = 0
creft = 0.1
function myCoords(f,i)
ctotel = ctotel + i
if ctotel >= creft then
px,py=GetPlayerMapPosition("player")
f.text:SetText(format("( %s ) [%f , %f]",GetZoneText(), px *100, py *100))
ctotel = 0
end
end
Third macro - store frame in global variable and set it and run update script with myCoords as callback
myCoordsFrame = CreateFrame("Frame","MyCoordsFrame",UIParent)
setMyFrame(myCoordsFrame, 500, 0)
myCoordsFrame:SetScript("OnUpdate", myCoords)
Of course in game all macros have to be preceeded with /run and have to be inlined - no line breaks - instead of linebreak just make space...
Also you have to run macros in THIS ^^^ order (first=>second=>third)
Advantage in setting frame and creft as globals:
Frames can't be destroyed while in the world (you have to relog to destroy them) so when it's global you can later move it with
/run setMyFrame(myCoordsFrame, NEW_X_COORDINATE, NEW_Y_COORDINATE)
If you would like the coords to update slower/faster you can do it by resetting the creft - e.g. to almost realtime refresh every 0.05 or even 0.01 seconds:
/run creft = 0.05 ... or even /run creft = 0.01
Make Coords movable - draggable by left mouse (credit to Wanderingfox from WoWhead):
myCoordsFrame:SetMovable(true)
myCoordsFrame:EnableMouse(true)
myCoordsFrame:SetScript("OnMouseDown",function() myCoordsFrame:StartMoving() end)
myCoordsFrame:SetScript("OnMouseUp",function() myCoordsFrame:StopMovingOrSizing() end)
...and as copy-paste ingame macro:
/run myCoordsFrame:SetMovable(true) myCoordsFrame:EnableMouse(true) myCoordsFrame:SetScript("OnMouseDown",function() myCoordsFrame:StartMoving() end) myCoordsFrame:SetScript("OnMouseUp",function() myCoordsFrame:StopMovingOrSizing() end)

psychopy polygon on top of image

using psychopy ver 1.81.03 on a mac I want to draw a polygon (e.g. a triangle) on top of an image.
So far, my image stays always on top and thus hides the polygon, no matter the order I put them in. This also stays true if I have the polygon start a frame later than the image.
e.g. see inn the code below (created with the Builder before compiling) how both a blue square and a red triangle are supposed to start at frame 0, but when you run it the blue square always covers the red triangle!?
Is there a way to have the polygon on top? Do I somehow need to merge the image and polygon before drawing them?
Thank you so much for your help!!
Sebastian
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
This experiment was created using PsychoPy2 Experiment Builder (v1.81.03), Sun Jan 18 20:44:26 2015
If you publish work using this script please cite the relevant PsychoPy publications
Peirce, JW (2007) PsychoPy - Psychophysics software in Python. Journal of Neuroscience Methods, 162(1-2), 8-13.
Peirce, JW (2009) Generating stimuli for neuroscience using PsychoPy. Frontiers in Neuroinformatics, 2:10. doi: 10.3389/neuro.11.010.2008
"""
from __future__ import division # so that 1/3=0.333 instead of 1/3=0
from psychopy import visual, core, data, event, logging, sound, gui
from psychopy.constants import * # things like STARTED, FINISHED
import numpy as np # whole numpy lib is available, prepend 'np.'
from numpy import sin, cos, tan, log, log10, pi, average, sqrt, std, deg2rad, rad2deg, linspace, asarray
from numpy.random import random, randint, normal, shuffle
import os # handy system and path functions
# Ensure that relative paths start from the same directory as this script
_thisDir = os.path.dirname(os.path.abspath(__file__))
os.chdir(_thisDir)
# Store info about the experiment session
expName = u'test_triangle_over_square' # from the Builder filename that created this script
expInfo = {'participant':'', 'session':'001'}
dlg = gui.DlgFromDict(dictionary=expInfo, title=expName)
if dlg.OK == False: core.quit() # user pressed cancel
expInfo['date'] = data.getDateStr() # add a simple timestamp
expInfo['expName'] = expName
# Data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
filename = _thisDir + os.sep + 'data/%s_%s_%s' %(expInfo['participant'], expName, expInfo['date'])
# An ExperimentHandler isn't essential but helps with data saving
thisExp = data.ExperimentHandler(name=expName, version='',
extraInfo=expInfo, runtimeInfo=None,
originPath=None,
savePickle=True, saveWideText=True,
dataFileName=filename)
#save a log file for detail verbose info
logFile = logging.LogFile(filename+'.log', level=logging.EXP)
logging.console.setLevel(logging.WARNING) # this outputs to the screen, not a file
endExpNow = False # flag for 'escape' or other condition => quit the exp
# Start Code - component code to be run before the window creation
# Setup the Window
win = visual.Window(size=(1280, 800), fullscr=True, screen=0, allowGUI=False, allowStencil=False,
monitor='testMonitor', color=[0,0,0], colorSpace='rgb',
blendMode='avg', useFBO=True,
)
# store frame rate of monitor if we can measure it successfully
expInfo['frameRate']=win.getActualFrameRate()
if expInfo['frameRate']!=None:
frameDur = 1.0/round(expInfo['frameRate'])
else:
frameDur = 1.0/60.0 # couldn't get a reliable measure so guess
# Initialize components for Routine "trial"
trialClock = core.Clock()
ISI = core.StaticPeriod(win=win, screenHz=expInfo['frameRate'], name='ISI')
square = visual.ImageStim(win=win, name='square',units='pix',
image=None, mask=None,
ori=0, pos=[0, 0], size=[200, 200],
color=u'blue', colorSpace='rgb', opacity=1,
flipHoriz=False, flipVert=False,
texRes=128, interpolate=True, depth=-1.0)
polygon = visual.ShapeStim(win=win, name='polygon',units='pix',
vertices = [[-[200, 300][0]/2.0,-[200, 300][1]/2.0], [+[200, 300][0]/2.0,-[200, 300][1]/2.0], [0,[200, 300][1]/2.0]],
ori=0, pos=[0, 0],
lineWidth=1, lineColor=[1,1,1], lineColorSpace='rgb',
fillColor=u'red', fillColorSpace='rgb',
opacity=1,interpolate=True)
# Create some handy timers
globalClock = core.Clock() # to track the time since experiment started
routineTimer = core.CountdownTimer() # to track time remaining of each (non-slip) routine
#------Prepare to start Routine "trial"-------
t = 0
trialClock.reset() # clock
frameN = -1
# update component parameters for each repeat
# keep track of which components have finished
trialComponents = []
trialComponents.append(ISI)
trialComponents.append(square)
trialComponents.append(polygon)
for thisComponent in trialComponents:
if hasattr(thisComponent, 'status'):
thisComponent.status = NOT_STARTED
#-------Start Routine "trial"-------
continueRoutine = True
while continueRoutine:
# get current time
t = trialClock.getTime()
frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
# update/draw components on each frame
# *square* updates
if frameN >= 0 and square.status == NOT_STARTED:
# keep track of start time/frame for later
square.tStart = t # underestimates by a little under one frame
square.frameNStart = frameN # exact frame index
square.setAutoDraw(True)
# *polygon* updates
if frameN >= 0 and polygon.status == NOT_STARTED:
# keep track of start time/frame for later
polygon.tStart = t # underestimates by a little under one frame
polygon.frameNStart = frameN # exact frame index
polygon.setAutoDraw(True)
# *ISI* period
if t >= 0.0 and ISI.status == NOT_STARTED:
# keep track of start time/frame for later
ISI.tStart = t # underestimates by a little under one frame
ISI.frameNStart = frameN # exact frame index
ISI.start(0.5)
elif ISI.status == STARTED: #one frame should pass before updating params and completing
ISI.complete() #finish the static period
# check if all components have finished
if not continueRoutine: # a component has requested a forced-end of Routine
routineTimer.reset() # if we abort early the non-slip timer needs reset
break
continueRoutine = False # will revert to True if at least one component still running
for thisComponent in trialComponents:
if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
continueRoutine = True
break # at least one component has not yet finished
# check for quit (the Esc key)
if endExpNow or event.getKeys(keyList=["escape"]):
core.quit()
# refresh the screen
if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
win.flip()
else: # this Routine was not non-slip safe so reset non-slip timer
routineTimer.reset()
#-------Ending Routine "trial"-------
for thisComponent in trialComponents:
if hasattr(thisComponent, "setAutoDraw"):
thisComponent.setAutoDraw(False)
win.close()
core.quit()
As per Jonas' comment above, PsychoPy uses a layering system in which subsequent stimuli are drawn on top of previous stimuli (as in his code examples).
In the graphical Builder environment, drawing order is represented by the vertical order of stimulus components: stimuli at the top are drawn first, and ones lower down are progressively layered upon them.
You can change the order of stimulus components by right-clicking on them and selecting "Move up", "move down", etc as required.
Sebastian, has, however, identified a bug here, in that the intended drawing order is not honoured between ImageStim and ShapeStim components. As a work-around, you might be able to replace your ShapeStim with a bitmap representation, displayed using an ImageStim. Multiple ImageStims should draw correctly (as do multiple ShapeStims). To get it to draw correctly on top of another image, be sure to save it as a .png file, which supports transparency. That way, only the actual shape will be drawn on top, as its background pixels can be set to be transparent and will not mask the the underlying image.
For a long-term solution, I've added your issue as a bug report to the PsychoPy GitHub project here:
https://github.com/psychopy/psychopy/issues/795
It turned out to be a bug in the Polygon component in Builder.
This is fixed in the upcoming release (1.82.00). The changes needed to make the fix can be seen at
https://github.com/psychopy/psychopy/commit/af1af9a7a85cee9b4ec8ad5e2ff1f03140bd1a36
which you can add to your own installation if you like.
cheers,
Jon

Resources