Adding 'For in Range' loop

Hello,

I’m relatively new to the PsychoPy framework, but have some experience with Python overall. I am stuck however in trying to add a ‘For in Range’ loop into my experiment. I am using the Cedrus Pyxid2 module to send trigger codes across. The loop I have to insert is:

for line in range(1,3)
dev.activate_line(lines=line)
time.sleep(2)

This sends the triggers perfectly at an interval of once every 2 seconds. However, when I add them into the larger task, the screen remains blank, whilst the trigger codes work. The rest of the task follows afterwards, so I’m assuming that I’m likely putting the trigger loop in the wrong place! Here is the remainder of the actual task:

import pygame, sys
from pygame.locals import *
import random, time
import math
import os
import csv

os.environ['SDL_VIDEO_CENTERED'] = '1'

pygame.init()
SCREEN_WIDTH, SCREEN_HEIGHT = pygame.display.Info().current_w, pygame.display.Info().current_h
pygame.joystick.init
pygame.joystick.get_count()
print ("Joystick Count", pygame.joystick.get_count())
pygame.init()

pygame.font.init

size = [SCREEN_WIDTH,SCREEN_HEIGHT-50]
#SCREEN_WIDTH = 1000
#SCREEN_HEIGHT = 1000
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
screen = pygame.display.set_mode(size, pygame.RESIZABLE)

global Trial
Trial = random.randint(1,2)
print('Condition', Trial)
#CALIBRATION_SPEED = (input('Enter multiplier value'))
 
pygame.display.set_caption("Task")
 
# Loop until the user clicks the close button.
done = False
 
clock = pygame.time.Clock()
 
# Starting position of the silhouette
rect_x = 50
rect_y = 50
 
# Speed and direction of rectangle
rect_change_x = 5
rect_change_y = 5
 
# font
pygame.font.init
font = pygame.font.Font(None, 32)

pygame.display.flip()
screen.fill(BLACK)

# Draw the rectangle
pygame.draw.rect(screen, RED, [rect_x, rect_y, 50, 50])

# Move the rectangle starting point
rect_x += rect_change_x
rect_y += rect_change_y

#Setting up FPS 
FPS = 60
FramePerSec = pygame.time.Clock()

#Other Variables for use in the program
#SCREEN_WIDTH = 1000
#SCREEN_HEIGHT = 1000
SPEED = 5
SCORE = 0
COLLISION_SCORE = 0

#Setting up Fonts
font = pygame.font.SysFont("Verdana", 40)
font_small = pygame.font.SysFont("Verdana", 20)

background = pygame.image.load("E:\Task Development\Stimuli/trial.jpeg")

#delay after the instruction screen
pygame.time.wait(1000)

timer = pygame.time.get_ticks()
timer2 = timer + 10000

#TASK GAME WINDOW SIZE
DISPLAYSURF = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.RESIZABLE)
DISPLAYSURF.fill(WHITE)
pygame.display.set_caption("EEG Task")
pygame.mixer.init()

class Enemy(pygame.sprite.Sprite):
      def __init__(self):
        super().__init__() 
        self.image = pygame.image.load("N:\Task Development\Stimuli/greendot.png")
        self.rect = self.image.get_rect()
        self.rect.center = ((SCREEN_WIDTH/2), 0)
      def move(self):
        global SCORE
        self.rect.move_ip(0,SPEED)
        if (self.rect.bottom > SCREEN_HEIGHT-10):
            SCORE += 1
            #pygame.mixer.Sound("D:\Pygame Project\Stimuli/spawn.wav").play()
            self.rect.top = 0
            self.rect.center = ((SCREEN_WIDTH/2), -SCREEN_HEIGHT)
        global COLLISION_SCORE

class Enemy2(pygame.sprite.Sprite):
      def __init__(self):
        super().__init__() 
        self.image = pygame.image.load("N:\Task Development\Stimuli/greendot.png")
        self.rect = self.image.get_rect()
        self.rect.center = ((SCREEN_WIDTH/2), 0)
      def move(self):
        global SCORE
        self.rect.move_ip(0,(-SPEED))
        if (self.rect.top < 10):
            SCORE += 1
            #pygame.mixer.Sound("N:\Task Development\Stimuli/spawn.wav").play()
            self.rect.top = 0
            self.rect.center = ((SCREEN_WIDTH/2), SCREEN_HEIGHT)
        global COLLISION_SCORE

class Player(pygame.sprite.Sprite):
    def __init__(self):

        super().__init__()
 
        # Variables to hold the height and width of the block
        width = 10
        height = 15
 
        # silhouette image
        self.image = pygame.image.load("N:\Task Development/Stimuli/reddot.png")
        
 
        # Fetch the rectangle object that has the dimensions of the image
        self.rect = self.image.get_rect()
 
        # Set initial position of silhouette
        self.rect.x = SCREEN_WIDTH/2-80
        self.rect.y = SCREEN_HEIGHT/2

        # joystick count
        self.joystick_count = pygame.joystick.get_count()
        if self.joystick_count == 0:
            # No joysticks!
            print("Ben, check the USB connection you doughnut!")
        else:
            # Use joystick #0 and initialize it
            self.my_joystick = pygame.joystick.Joystick(0)
            self.my_joystick.init()
 
    def move(self):
        
        if self.joystick_count != 0:
 
            # joystick pos
            horiz_axis_pos = self.my_joystick.get_axis(0)
            vert_axis_pos = self.my_joystick.get_axis(1)
 
            # Move x and y, * speed 
            self.rect.x = self.rect.x+int(horiz_axis_pos*int(CALIBRATION_SPEED))
            self.rect.y = self.rect.y+int(vert_axis_pos*int(CALIBRATION_SPEED))

#Setting up Sprites        
P1 = Player()
E1 = Enemy()
E2 = Enemy2()

#Creating Sprite Groups
enemies = pygame.sprite.Group()
enemies.add(E1)
enemies.add(E2)
all_sprites = pygame.sprite.Group()
all_sprites.add(P1)
all_sprites.add(E1)
all_sprites.add(E2)

#Adding a new User event (speed up)
INC_SPEED = pygame.USEREVENT + 1
pygame.time.set_timer(INC_SPEED, 1000)

#Game Loop
while True:
      
    #Cycles through all events occuring      
for event in pygame.event.get():
        
              
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    

    DISPLAYSURF.blit(background, (0,0))
    scores = font_small.render(str(SCORE), True, BLACK)
    DISPLAYSURF.blit(scores, (10,10))

    COLLISION_SCORE2 = COLLISION_SCORE/20
    
    cscores = font_small.render(str(COLLISION_SCORE2), True, BLACK)
    DISPLAYSURF.blit(cscores, (SCREEN_WIDTH-60, 10))

    

    #Moves and Re-draws all Sprites
    for entity in all_sprites:
        entity.move()
        DISPLAYSURF.blit(entity.image, entity.rect)
        

    #To be run if collision happens
    if pygame.sprite.spritecollideany(P1, enemies):
        pygame.mixer.Sound("N:\Task Development\Stimuli/scream.wav").play()
        COLLISION_SCORE += 1
        
    
        
    timer = pygame.time.get_ticks()

    COLLISIONSCORECONV = repr(COLLISION_SCORE2)

   
        
    #print ((timer2-timer)/1000)
        
    if timer > timer2:
        pygame.display.update()
        #pygame.mixer.Sound("N:\Task Development\Stimuli/scream.wav").play()
        for entity in all_sprites:
                entity.kill()

  
        # get fileName from user
##        filepath = "Trial " + Trial + ".txt"
  
        # Creates a new file
        with open('Participant Scores.csv', 'a+') as file:
            file.write('\n')
            #file.write("Trial Number" + "," + Trial + "," + "Score" + ',' + ',' + (int(COLLISIONSCORECONV)))
            file.write((str(COLLISIONSCORECONV)))
            

   
        #program_list = ["D:\Pygame Project\Participants\Participant 5 - JP\Spider Task.py"]

       # pygame.time.wait(2000)
        #for program in program_list:
           # exec(open(program).read())
            #print("Finished" + program)

        pygame.quit()
        sys.exit()        
        
    pygame.display.update()
    FramePerSec.tick(FPS)

I do apologise that it’s quite messy, I’m just getting annoyed at myself, given it’s likely a tiny fix!

Any help would be so much appreciated, thanks!
Ben

Hi Ben,
Can you edit your post to share the code with the loop as it currently is in context?

Thanks!

Not sure exactly what dev.activate_line does (not even sure how it’s working since it’s not defined, at least in this code block), but I’d guess the problem is the time.sleep(2) halting the program for 4 seconds (two seconds per loop for two iterations). If you want to send a signal every two seconds without halting the program, I’d have a timer to keep track of time throughout the whole gameloop and an if statement to track if two seconds have passed on that timer since the last signal. It doesn’t seem like you’re using psychopy in the program, but since this is the psychopy forum I would do something like:

from psychopy.core import CountdownTimer

timer = CountdownTimer(2)

while True:
    if timer.getTime() < 0:
        for line in range(1, 3):
            dev.activate_line(lines = line)
        timer.reset()

    # Do the rest of your stuff here

EDIT:
If you’re dead set on not using psychopy and instead keeping to using python’s time module, I would do something like:

import time

last_time = time.time()

while True:
    current_time = time.time()
    if current_time - last_time > 2:
        for line in range(1, 3):
            dev.activate_line(lines=line)
        last_time = current_time

    # Do the rest of your stuff here

Thank you so much for your comments and help! It’s much appreciated, it had been driving me mad! I am using PsychoPy, but the current snippet was from a python script I’d been playing around with previously.

I am however, having an issue with the timer that you’ve provided.

timer = CountdownTimer(2)

while True:
    if timer.getTime() < 0: #also doesn't work if the countdown is < 1

with this, the trigger codes don’t actually get passed across to the other PC. When I was exploring, they do work with:

timer = CountdownTimer(2)

while True:
    if timer.getTime() > 1:

but this, naturally, leads them to trigger each second, rather than every two seconds. Does this mean there’s the countdown timer needs tweaking at all?

Thanks again,
Ben

Can you run this in your environment and share the output of the first ~10 seconds? I’m at a loss for why it would not be working:

from psychopy.core import CountdownTimer, Clock

timer = CountdownTimer(2)
clock = Clock()

while True:
    if timer.getTime() < 0:
        print(timer.getTime(), clock.getTime())
        timer.reset()
    if clock.getTime() > 10:
        break

In my environment, the output is as expected (first number close to but less than 0, second number should be increasing by ~2s):

The second code block you shared would constantly send signals for one second, rather than one signal every second. Is this what you were wanting?