#!/usr/bin/env python # -*- coding: utf-8 -*- """ This experiment was created using PsychoPy3 Experiment Builder (v2020.2.10), on April 28, 2021, at 10:54 If you publish work using this script the most relevant publication is: Peirce J, Gray JR, Simpson S, MacAskill M, Höchenberger R, Sogo H, Kastman E, Lindeløv JK. (2019) PsychoPy2: Experiments in behavior made easy Behav Res 51: 195. https://doi.org/10.3758/s13428-018-01193-y """ from __future__ import absolute_import, division from psychopy import locale_setup from psychopy import prefs from psychopy import sound, gui, visual, core, data, event, logging, clock from psychopy.constants import (NOT_STARTED, STARTED, PLAYING, PAUSED, STOPPED, FINISHED, PRESSED, RELEASED, FOREVER) 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 import sys # to get file system encoding from psychopy.hardware import keyboard # 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 psychopyVersion = '2020.2.10' expName = 'scroll-text' # from the Builder filename that created this script expInfo = {'participant': '', 'session': '001'} dlg = gui.DlgFromDict(dictionary=expInfo, sortKeys=False, title=expName) if dlg.OK == False: core.quit() # user pressed cancel expInfo['date'] = data.getDateStr() # add a simple timestamp expInfo['expName'] = expName expInfo['psychopyVersion'] = psychopyVersion # Data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc filename = _thisDir + os.sep + u'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='C:\\Users\\p0071480\\Documents\\Pavlovia\\vespr\\scroll-text\\scroll-text_lastrun.py', 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 frameTolerance = 0.001 # how close to onset before 'same' frame # Start Code - component code to be run after the window creation # Setup the Window win = visual.Window( size=(1024, 768), fullscr=True, screen=0, winType='pyglet', allowGUI=False, allowStencil=False, monitor='testMonitor', color=[0,0,0], colorSpace='rgb', blendMode='avg', useFBO=True, units='height') # store frame rate of monitor if we can measure it expInfo['frameRate'] = win.getActualFrameRate() if expInfo['frameRate'] != None: frameDur = 1.0 / round(expInfo['frameRate']) else: frameDur = 1.0 / 60.0 # could not measure, so guess # create a default keyboard (e.g. to check for escape) defaultKeyboard = keyboard.Keyboard() # Initialize components for Routine "start" startClock = core.Clock() mouse = event.Mouse(win=win) x, y = [None, None] mouse.mouseClock = core.Clock() # Initialize components for Routine "trial" trialClock = core.Clock() screenRatio = win.size[0]/win.size[1] fontSize=.03 mouseY=0 trackLength=.9 trackWidth=.04 scrollText = visual.TextStim(win=win, name='scrollText', text="Faculty of Health and Life Sciences\nDepartment of Psychology, Health and Professional Development\nOxford Brookes University, Headington, OXFORD OX3 0BP.\n\nResearcher: *STUDENT NAME*, Undergraduate, Email: *EMAIL ADDRESS*\n\nSupervisor: *SUPERVISOR NAME*, Email: *EMAIL ADDRESS*, Tel: 01865 48*INTERNAL NUMBER*\n\nPlease take time to read the following information carefully.\n\n###What is the purpose of the study?\n*The background and the aim of the study should be given here. You should outline the overall design of the study. Again, you can be vague where giving away too much might bias your participants responses. Avoid technical or over-academic language*\n\n###Why have I been invited to participate?\n*You should explain how the individual was chosen to take part in the study and how many other people will be asked to participate. You should explain how the individual was chosen to take part in the study, and mention relevant inclusion or exclusion criteria. For example... This study is on adults in the UK aged between 18 and 70 who can read English fluently. A total of between 150 and 200 people will be asked to participate via invitations posted on social media.*\n\n###Do I have to take part?\nNo. It is up to you to decide whether or not to take part. If you do decide to take part you are still free to withdraw unprocessed data at any time by closing the browser before pressing the 'submit' button or by contacting the researcher with a unique code that will be displayed on screen after you submit. \n\n###What will happen to me if I take part?\nIf you choose to take part in the study, you will be presented with an online questionnaire, which should take no longer than *15-20* minutes to complete. *You should explain your methods of data collection, including what the individual will be asked to do and how much time will be involved. This description should be general enough that the participant will not be surprised by the questions. If information is withheld, ask your supervisor what to put here.*\n\n###What are the possible benefits or disadvantages of taking part?\nThere are no direct benefits of taking part in this study. However, we hope that you will find the questions interesting and that outcomes of this study will help to further develop research on this topic area. Other than the time you spend answering the questionnaire, there are no disadvantages of taking part in this study. *Please edit if there are any other disadvantages or risks to the participants, such as induction of a sad mood. Outline any direct benefits for the individual. There are usually none to anyone other than the researcher for their dissertation but if appropriate mention that they might find the study interesting or the activity enjoyable.*\n\n###Will what I say in this study be kept confidential?\nYes. You will not be asked to give any information that could be used to identify you personally (e.g., your name, date of birth, IP address). Confidentiality, privacy and anonymity will be ensured in the collection, storage and publication of research material (subject to legal limitations). Research data will be stored in Qualtrics and Google Drive, for which the University has security agreements, or uploaded to a repository so that it can be shared for teaching and research purposes. \n\n###What should I do if I want to take part?\nIf you would like to take part, please click on the Continue to Experiment button at the bottom of this page before proceeding.\n\n###What will happen to the results of the research study?\nThe results of this study will be written up and submitted as a piece of work to fulfil the requirements of a module at Oxford Brookes University. The results may also be published in an academic journal, presented at a conference or used for future research or teaching on the topic.\nOR\nThe results of the research will be used in an undergraduate psychology dissertation. If a publication is planned, the data generated by the study will be retained by the supervisor in accordance with the University's policy on Academic Integrity and may be kept securely for a period of ten years after the completion of the research project. If no publication is proposed the data will be deleted in *INSERT MONTH and YEAR*.\n\n###Who has reviewed the study?\nThis research is being conducted by a Psychology student at Oxford Brookes University as part of the requirement for their course. The study procedures have been reviewed and approved by the Psychology Research Ethics Committee, Oxford Brookes University. If you have any concerns about how the study has been conducted, please contact the Psychology Research Ethics Officer, Dr Emma Davies, on edavies@brookes.ac.uk\n \n###Contact for Further Information\nPlease contact the researcher or their supervisor (contact details above) if you would like any further information about this study. \n \nIf you wish to obtain a summary of the findings when they have been written up please e-mail *STUDENT NAME* at *EMAIL ADDRESS*@brookes.ac.uk after *RESULTS DAY*.\n\nThank you for taking time to read the information sheet.\n\n**Date**\n*TODAY'S DATE*\n\n\n\n", font='Arial', pos=[0,0], height=fontSize, wrapWidth=screenRatio-.2, ori=0, color='white', colorSpace='rgb', opacity=1, languageStyle='LTR', depth=-1.0); import math scrollBar = visual.Rect( win=win, name='scrollBar', width=(trackWidth, trackLength+trackWidth)[0], height=(trackWidth, trackLength+trackWidth)[1], ori=0, pos=(screenRatio*.475, 0), lineWidth=2, lineColor=[1,1,1], lineColorSpace='rgb', fillColor=[0,0,0], fillColorSpace='rgb', opacity=1, depth=-3.0, interpolate=True) scrollButton = visual.Rect( win=win, name='scrollButton', width=(trackWidth, trackWidth)[0], height=(trackWidth, trackWidth)[1], ori=0, pos=(screenRatio*.475, trackLength/2), lineWidth=2, lineColor=[1,1,1], lineColorSpace='rgb', fillColor=[1,1,1], fillColorSpace='rgb', opacity=1, depth=-4.0, interpolate=True) bottomCrop = visual.Rect( win=win, name='bottomCrop', width=(screenRatio, .1)[0], height=(screenRatio, .1)[1], ori=0, pos=(-.1, -.475), lineWidth=1, lineColor=[0,0,0], lineColorSpace='rgb', fillColor=[0,0,0], fillColorSpace='rgb', opacity=1, depth=-5.0, interpolate=True) continueText = visual.TextStim(win=win, name='continueText', text=' ', font='Arial', pos=(0, -.45), height=fontSize*1.5, wrapWidth=None, ori=0, color='white', colorSpace='rgb', opacity=1, languageStyle='LTR', depth=-6.0); # Initialize components for Routine "end" endClock = core.Clock() text = visual.TextStim(win=win, name='text', text='Fin', font='OpenSans-Italic', pos=(0, 0), height=0.1, wrapWidth=None, ori=0, color='white', colorSpace='rgb', opacity=1, languageStyle='LTR', depth=0.0); # 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 "start"------- continueRoutine = True routineTimer.add(0.500000) # update component parameters for each repeat # setup some python lists for storing info about the mouse gotValidClick = False # until a click is received scrollText.fontFiles = ['OpenSans-Italic.ttf'] # load file(s) scrollText.font='OpenSans-Italic' # set to font # keep track of which components have finished startComponents = [mouse] for thisComponent in startComponents: thisComponent.tStart = None thisComponent.tStop = None thisComponent.tStartRefresh = None thisComponent.tStopRefresh = None if hasattr(thisComponent, 'status'): thisComponent.status = NOT_STARTED # reset timers t = 0 _timeToFirstFrame = win.getFutureFlipTime(clock="now") startClock.reset(-_timeToFirstFrame) # t0 is time of first possible flip frameN = -1 # -------Run Routine "start"------- while continueRoutine and routineTimer.getTime() > 0: # get current time t = startClock.getTime() tThisFlip = win.getFutureFlipTime(clock=startClock) tThisFlipGlobal = win.getFutureFlipTime(clock=None) frameN = frameN + 1 # number of completed frames (so 0 is the first frame) # update/draw components on each frame # check for quit (typically the Esc key) if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]): core.quit() # check if all components have finished if not continueRoutine: # a component has requested a forced-end of Routine break continueRoutine = False # will revert to True if at least one component still running for thisComponent in startComponents: if hasattr(thisComponent, "status") and thisComponent.status != FINISHED: continueRoutine = True break # at least one component has not yet finished # refresh the screen if continueRoutine: # don't flip if this routine is over or we'll get a blank screen win.flip() # -------Ending Routine "start"------- for thisComponent in startComponents: if hasattr(thisComponent, "setAutoDraw"): thisComponent.setAutoDraw(False) # store data for thisExp (ExperimentHandler) thisExp.addData('mouse.started', mouse.tStart) thisExp.addData('mouse.stopped', mouse.tStop) thisExp.nextEntry() # ------Prepare to start Routine "trial"------- continueRoutine = True # update component parameters for each repeat scroll = 0 lines = scrollText.text.split('\n') maxScroll = 0 for line in lines: maxScroll += math.ceil(len(line)*fontSize/screenRatio) viewAll=0 dragging=0 moving=0 scrollText.setPos((-.45*screenRatio, .45)) maxScroll-=round(1.5/fontSize)-1 scrollText.anchorHoriz='left' scrollText.anchorVert='top' scrollText.alignText='left' # keep track of which components have finished trialComponents = [scrollText, scrollBar, scrollButton, bottomCrop, continueText] for thisComponent in trialComponents: thisComponent.tStart = None thisComponent.tStop = None thisComponent.tStartRefresh = None thisComponent.tStopRefresh = None if hasattr(thisComponent, 'status'): thisComponent.status = NOT_STARTED # reset timers t = 0 _timeToFirstFrame = win.getFutureFlipTime(clock="now") trialClock.reset(-_timeToFirstFrame) # t0 is time of first possible flip frameN = -1 # -------Run Routine "trial"------- while continueRoutine: # get current time t = trialClock.getTime() tThisFlip = win.getFutureFlipTime(clock=trialClock) tThisFlipGlobal = win.getFutureFlipTime(clock=None) frameN = frameN + 1 # number of completed frames (so 0 is the first frame) # update/draw components on each frame keys=event.getKeys() if len(keys): if viewAll==1 and 'space' in keys: continueRoutine=False elif 'down' in keys: scroll+=1 moving=1 elif 'up' in keys: scroll-=1 moving=1 elif mouse.isPressedIn(scrollBar): mouseY=mouse.getPos()[1] scroll = (trackLength/2-mouseY)*maxScroll/trackLength moving=1 elif mouse.getPressed()[0] == 1: if dragging==0: dragging=1 mouseY=mouse.getPos()[1] else: scroll+=(mouse.getPos()[1]-mouseY)/fontSize/1.08 mouseY=mouse.getPos()[1] moving=1 elif dragging==1: dragging=0 if moving==1: if scroll <0: scroll=0 elif scroll>=maxScroll: scroll=maxScroll if viewAll == 0: viewAll=1 continueText.text='Press space to continue' scrollText.setPos((-.45*screenRatio, .45+scroll*fontSize*1.08)) scrollButton.setPos((screenRatio*.475, trackLength/2-trackLength*scroll/maxScroll)) bottomCrop.setPos((-.1+.01*(scroll%2), -.475)) continueText.setOpacity(1-.01*(scroll%2)) moving=0 # *scrollText* updates if scrollText.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance: # keep track of start time/frame for later scrollText.frameNStart = frameN # exact frame index scrollText.tStart = t # local t and not account for scr refresh scrollText.tStartRefresh = tThisFlipGlobal # on global time win.timeOnFlip(scrollText, 'tStartRefresh') # time at next scr refresh scrollText.setAutoDraw(True) # *scrollBar* updates if scrollBar.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance: # keep track of start time/frame for later scrollBar.frameNStart = frameN # exact frame index scrollBar.tStart = t # local t and not account for scr refresh scrollBar.tStartRefresh = tThisFlipGlobal # on global time win.timeOnFlip(scrollBar, 'tStartRefresh') # time at next scr refresh scrollBar.setAutoDraw(True) # *scrollButton* updates if scrollButton.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance: # keep track of start time/frame for later scrollButton.frameNStart = frameN # exact frame index scrollButton.tStart = t # local t and not account for scr refresh scrollButton.tStartRefresh = tThisFlipGlobal # on global time win.timeOnFlip(scrollButton, 'tStartRefresh') # time at next scr refresh scrollButton.setAutoDraw(True) # *bottomCrop* updates if bottomCrop.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance: # keep track of start time/frame for later bottomCrop.frameNStart = frameN # exact frame index bottomCrop.tStart = t # local t and not account for scr refresh bottomCrop.tStartRefresh = tThisFlipGlobal # on global time win.timeOnFlip(bottomCrop, 'tStartRefresh') # time at next scr refresh bottomCrop.setAutoDraw(True) # *continueText* updates if continueText.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance: # keep track of start time/frame for later continueText.frameNStart = frameN # exact frame index continueText.tStart = t # local t and not account for scr refresh continueText.tStartRefresh = tThisFlipGlobal # on global time win.timeOnFlip(continueText, 'tStartRefresh') # time at next scr refresh continueText.setAutoDraw(True) # check for quit (typically the Esc key) if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]): core.quit() # check if all components have finished if not continueRoutine: # a component has requested a forced-end of Routine 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 # refresh the screen if continueRoutine: # don't flip if this routine is over or we'll get a blank screen win.flip() # -------Ending Routine "trial"------- for thisComponent in trialComponents: if hasattr(thisComponent, "setAutoDraw"): thisComponent.setAutoDraw(False) thisExp.addData('continueText.started', continueText.tStartRefresh) thisExp.addData('continueText.stopped', continueText.tStopRefresh) # the Routine "trial" was not non-slip safe, so reset the non-slip timer routineTimer.reset() # ------Prepare to start Routine "end"------- continueRoutine = True # update component parameters for each repeat # keep track of which components have finished endComponents = [text] for thisComponent in endComponents: thisComponent.tStart = None thisComponent.tStop = None thisComponent.tStartRefresh = None thisComponent.tStopRefresh = None if hasattr(thisComponent, 'status'): thisComponent.status = NOT_STARTED # reset timers t = 0 _timeToFirstFrame = win.getFutureFlipTime(clock="now") endClock.reset(-_timeToFirstFrame) # t0 is time of first possible flip frameN = -1 # -------Run Routine "end"------- while continueRoutine: # get current time t = endClock.getTime() tThisFlip = win.getFutureFlipTime(clock=endClock) tThisFlipGlobal = win.getFutureFlipTime(clock=None) frameN = frameN + 1 # number of completed frames (so 0 is the first frame) # update/draw components on each frame # *text* updates if text.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance: # keep track of start time/frame for later text.frameNStart = frameN # exact frame index text.tStart = t # local t and not account for scr refresh text.tStartRefresh = tThisFlipGlobal # on global time win.timeOnFlip(text, 'tStartRefresh') # time at next scr refresh text.setAutoDraw(True) # check for quit (typically the Esc key) if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]): core.quit() # check if all components have finished if not continueRoutine: # a component has requested a forced-end of Routine break continueRoutine = False # will revert to True if at least one component still running for thisComponent in endComponents: if hasattr(thisComponent, "status") and thisComponent.status != FINISHED: continueRoutine = True break # at least one component has not yet finished # refresh the screen if continueRoutine: # don't flip if this routine is over or we'll get a blank screen win.flip() # -------Ending Routine "end"------- for thisComponent in endComponents: if hasattr(thisComponent, "setAutoDraw"): thisComponent.setAutoDraw(False) # the Routine "end" was not non-slip safe, so reset the non-slip timer routineTimer.reset() # Flip one final time so any remaining win.callOnFlip() # and win.timeOnFlip() tasks get executed before quitting win.flip() # these shouldn't be strictly necessary (should auto-save) thisExp.saveAsWideText(filename+'.csv', delim='auto') thisExp.saveAsPickle(filename) logging.flush() # make sure everything is closed down thisExp.abort() # or data files will save again on exit win.close() core.quit()