1
0
mirror of https://github.com/psychopy/psychojs.git synced 2025-05-10 10:40:54 +00:00

fixed issues: fullscreen mode, size of triangular marker in sliders; new features: window waitblanking

This commit is contained in:
Alain Pitiot 2019-07-31 10:37:51 +02:00
parent e9f249bcde
commit 039a8671c7
31 changed files with 159 additions and 82 deletions

View File

@ -1,4 +1,4 @@
Copyright 2018 Ilixa Ltd.
Copyright 2019 Ilixa Ltd.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -2,7 +2,7 @@
* Manager handling the keyboard and mouse/touch events.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/
@ -343,6 +343,7 @@ export class EventManager {
* @name module:core.EventManager#w3c2pyglet
* @function
* @public
* @static
* @param {string} code - W3C Key Code
* @returns {string} corresponding pyglet key
*/
@ -352,6 +353,22 @@ export class EventManager {
else
return 'N/A';
}
/**
* Convert a keycode to a W3C UI Event code.
* <p>This is for legacy browsers.</p>
*
* @name module:core.EventManager#keycode2w3c
* @function
* @public
* @static
* @param {number} keycode - the keycode
* @returns {string} corresponding W3C UI Event code
*/
static keycode2w3c(keycode) {
return EventManager._keycodeMap[keycode];
}
}
@ -359,6 +376,9 @@ export class EventManager {
* <p>This map provides support for browsers that have not yet
* adopted the W3C KeyboardEvent.code standard for detecting key presses.
* It maps the deprecated KeyboardEvent.keycode values to the W3C UI event codes.</p>
*
* <p>Unfortunately, it is not very fine-grained: for instance, there is no difference between Alt Left and Alt
* Right, or between Enter and Numpad Enter. Use at your own risk (or upgrade your browser...).</p>
*
* @name module:core.EventManager#_keycodeMap
* @readonly
@ -427,10 +447,15 @@ EventManager._keycodeMap = {
194: "NumpadComma",
110: "NumpadDecimal",
111: "NumpadDivide",
13: "NumpadEnter",
12: "NumpadEqual",
106: "NumpadMultiply",
109: "NumpadSubtract",
13: "Enter", // 13 is also Numpad Enter, alas
16: "ShiftLeft", // 16 is also Shift Right, alas
17: "ControlLeft", // 17 is also Control Right, alas
18: "AltLeft", // 18 is also Alt Right, alas
37: "ArrowLeft",
38: "ArrowUp",
39: "ArrowRight",

View File

@ -2,7 +2,7 @@
* Graphic User Interface
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Manager handling the keyboard events.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/
@ -62,9 +62,11 @@ export class Keyboard extends PsychObject {
super(psychoJS);
if (typeof clock === 'undefined')
clock = this._psychoJS._monotonicClock;
clock = new Clock(); //this._psychoJS.monotonicClock;
this._addAttributes(Keyboard, bufferSize, waitForStart, clock, autoLog);
// start recording key events if need be:
this._addAttribute('status', (waitForStart)?PsychoJS.Status.NOT_STARTED:PsychoJS.Status.STARTED);
// setup circular buffer:
this.clearEvents();
@ -72,8 +74,6 @@ export class Keyboard extends PsychObject {
// add key listeners:
this._addKeyListeners();
// start recording key events if need be:
this._status = (waitForStart)?PsychoJS.Status.STOPPED:PsychoJS.Status.STARTED;
}
@ -284,6 +284,7 @@ export class Keyboard extends PsychObject {
this._circularBuffer = new Array(this._bufferSize);
this._bufferLength = 0;
this._bufferIndex = -1;
this._previousKeydownKey = undefined;
// (code => circular buffer index) map of keydown events not yet matched to keyup events:
this._unmatchedKeydownMap = new Map();
@ -314,12 +315,21 @@ export class Keyboard extends PsychObject {
return;
self._previousKeydownKey = event.key;
let code = event.code;
// take care of legacy Microsoft Edge:
if (typeof code === 'undefined')
code = EventManager.keycode2w3c(event.keyCode);
let pigletKey = EventManager.w3c2pyglet(code);
self._bufferIndex = (self._bufferIndex + 1) % self._bufferSize;
self._bufferLength = Math.min(self._bufferLength + 1, self._bufferSize);
self._circularBuffer[self._bufferIndex] = {
code: event.code,
code,
key: event.key,
pigletKey: EventManager.w3c2pyglet(event.code),
pigletKey,
status: Keyboard.KeyStatus.KEY_DOWN,
timestamp
};
@ -340,13 +350,20 @@ console.log(self._circularBuffer[self._bufferIndex]);
self._previousKeydownKey = undefined;
let code = event.code;
// take care of legacy Microsoft Edge:
if (typeof code === 'undefined')
code = EventManager.keycode2w3c(event.keyCode);
let pigletKey = EventManager.w3c2pyglet(code);
self._bufferIndex = (self._bufferIndex + 1) % self._bufferSize;
self._bufferLength = Math.min(self._bufferLength + 1, self._bufferSize);
self._circularBuffer[self._bufferIndex] = {
code: event.code,
code,
key: event.key,
pigletKey: EventManager.w3c2pyglet(event.code),
pigletKey,
status: Keyboard.KeyStatus.KEY_UP,
timestamp
};

View File

@ -2,7 +2,7 @@
* Base class for all stimuli.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/
@ -67,16 +67,17 @@ export class MinimalStim extends PsychObject {
// update the stimulus if need be before we add its PIXI representation to the window container:
this._updateIfNeeded();
if (typeof this._pixi === 'undefined')
// throw {...errorPrefix, error: 'the PIXI representation of the stimulus is unavailable'};
throw Object.assign(response, { error: 'the PIXI representation of the stimulus is unavailable'});
this.win._rootContainer.addChild(this._pixi);
this.win._drawList.push(this);
this.psychoJS.logger.warn('the Pixi.js representation of this stimulus is undefined.');
// throw Object.assign(response, { error: 'the PIXI representation of the stimulus is unavailable'});
else {
this.win._rootContainer.addChild(this._pixi);
this.win._drawList.push(this);
}
} else
{
// the stimulus is already in the list, if it needs to be updated, we remove it
// from the window container, update it, then put it back:
if (this._needUpdate) {
if (this._needUpdate && typeof this._pixi !== 'undefined') {
this.win._rootContainer.removeChild(this._pixi);
this._updateIfNeeded();
this.win._rootContainer.addChild(this._pixi);
@ -112,7 +113,7 @@ export class MinimalStim extends PsychObject {
draw() {
this._updateIfNeeded();
if (this.win && this.win._drawList.indexOf(this) < 0) {
if (this.win && this.win._drawList.indexOf(this) < 0 && typeof this._pixi !== 'undefined') {
this.win._container.addChild(this._pixi);
this.win._drawList.push(this);
}

View File

@ -2,7 +2,7 @@
* Manager responsible for the interactions between the experiment's stimuli and the mouse.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -3,7 +3,7 @@
* Main component of the PsychoJS library.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/
@ -55,8 +55,9 @@ export class PsychoJS {
* @public
*/
constructor({
debug = true,
collectIP = false
debug = true,
collectIP = false,
topLevelStatus = true
} = {}) {
// logging:
this._logger = new Logger((debug) ? log4javascript.Level.DEBUG : log4javascript.Level.INFO);
@ -91,6 +92,11 @@ export class PsychoJS {
// status:
this._status = PsychoJS.Status.NOT_CONFIGURED;
// make the PsychoJS.Status accessible from the top level of the generated experiment script
// in order to accommodate PsychoPy's Code Components
if (topLevelStatus)
this._makeStatusTopLevel();
this.logger.info('[PsychoJS] Initialised.');
}
@ -117,7 +123,9 @@ export class PsychoJS {
* @param {boolean} [options.fullscr] whether or not to go fullscreen
* @param {Color} [options.color] the background color of the window
* @param {string} [options.units] the units of the window
* @param {boolean} [options.autoLog] whether of not to log
* @param {boolean} [options.autoLog] whether or not to log
* @param {boolean} [options.waitBlanking] whether or not to wait for all rendering operations to be done
* before flipping
* @throws {Object.<string, *>} exception if a window has already been opened
*
* @public
@ -127,6 +135,7 @@ export class PsychoJS {
fullscr,
color,
units,
waitBlanking,
autoLog
} = {}) {
this.logger.info('[PsychoJS] Open Window.');
@ -140,6 +149,7 @@ export class PsychoJS {
fullscr,
color,
units,
waitBlanking,
autoLog
});
}
@ -500,6 +510,17 @@ export class PsychoJS {
}
/**
* Make the various Status top level, in order to accommodate PsychoPy's Code Components.
* @private
*/
_makeStatusTopLevel() {
for (const status in PsychoJS.Status) {
window[status] = PsychoJS.Status[status];
}
}
}

View File

@ -2,7 +2,7 @@
* Manager responsible for the communication between the experiment running in the participant's browser and the remote PsychoJS manager running on the remote https://pavlovia.org server.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Window responsible for displaying the experiment stimuli
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/
@ -24,6 +24,8 @@ import { MonotonicClock } from '../util/Clock';
* @param {boolean} [options.fullscr= false] whether or not to go fullscreen
* @param {Color} [options.color= Color('black')] the background color of the window
* @param {string} [options.units= 'pix'] the units of the window
* @param {boolean} [options.waitBlanking= false] whether or not to wait for all rendering operations to be done
* before flipping
* @param {boolean} [options.autoLog= true] whether or not to log
*/
export class Window extends PsychObject {
@ -43,6 +45,7 @@ export class Window extends PsychObject {
fullscr = false,
color = new Color('black'),
units = 'pix',
waitBlanking = false,
autoLog = true
} = {}) {
super(psychoJS, name);
@ -53,7 +56,7 @@ export class Window extends PsychObject {
// list of all elements, in the order they are currently drawn:
this._drawList = [];
this._addAttributes(Window, fullscr, color, units, autoLog);
this._addAttributes(Window, fullscr, color, units, waitBlanking, autoLog);
this._addAttribute('size', []);
@ -114,7 +117,10 @@ export class Window extends PsychObject {
* @public
*/
adjustScreenSize() {
this._windowAlreadyInFullScreen = (!window.screenTop && !window.screenY);
// the following does not seem to work any longer (08.2019) on Google Chrome, there does not seem to be
// reliable ways to test whether the window is already fullscreen.
// this._windowAlreadyInFullScreen = (!window.screenTop && !window.screenY);
this._windowAlreadyInFullScreen = false;
if (this.fullscr && !this._windowAlreadyInFullScreen) {
this._psychoJS.logger.debug('Resizing Window: ', this._name, 'to full screen.');
@ -227,6 +233,10 @@ export class Window extends PsychObject {
// [http://www.html5gamedevs.com/topic/27849-detect-when-view-has-been-rendered/]
this._renderer.gl.readPixels(0, 0, 1, 1, this._renderer.gl.RGBA, this._renderer.gl.UNSIGNED_BYTE, new Uint8Array(4));
// blocks execution until the rendering is fully done:
if (this._waitBlanking)
this._renderer.gl.finish();
// log:
this._writeLogOnFlip();
@ -270,7 +280,7 @@ export class Window extends PsychObject {
// if a stimuli needs to be updated, we remove it from the window container, update it, then put it back
for (const stimulus of this._drawList)
if (stimulus._needUpdate) {
if (stimulus._needUpdate && typeof stimulus._pixi !== 'undefined') {
this._rootContainer.removeChild(stimulus._pixi);
stimulus._updateIfNeeded();
this._rootContainer.addChild(stimulus._pixi);

View File

@ -2,7 +2,7 @@
* Mixin implementing various unit-handling measurement methods.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Experiment Handler
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/
@ -156,21 +156,22 @@ export class ExperimentHandler extends PsychObject {
* @name module:data.ExperimentHandler#nextEntry
* @function
* @public
* @param {Object[]} snapshots - array of loop snapshots
*/
nextEntry(loop) {
if (typeof loop !== 'undefined') {
const attributes = ExperimentHandler._getLoopAttributes(loop);
for (let a in attributes)
if (attributes.hasOwnProperty(a))
this._currentTrialData[a] = attributes[a];
} else {
// fetch data from each (potentially-nested) loop:
for (let loop of this._unfinishedLoops) {
const attributes = ExperimentHandler._getLoopAttributes(loop);
nextEntry(snapshots) {
if (typeof snapshots !== 'undefined') {
// turn single snapshot into a one-element array:
if (!Array.isArray(snapshots))
snapshots = [snapshots];
for (const snapshot of snapshots) {
const attributes = ExperimentHandler._getLoopAttributes(snapshot);
for (let a in attributes)
if (attributes.hasOwnProperty(a))
this._currentTrialData[a] = attributes[a];
}
}
// add the extraInfo dict to the data:
@ -304,19 +305,17 @@ export class ExperimentHandler extends PsychObject {
* @param {Object} loop - the loop
*/
static _getLoopAttributes(loop) {
const loopName = loop.name;
// standard attributes:
// standard trial attributes:
const properties = ['thisRepN', 'thisTrialN', 'thisN', 'thisIndex', 'stepSizeCurrent', 'ran', 'order'];
let attributes = {};
for (const property of properties)
for (const loopProperty in loop)
if (loopProperty === property) {
const key = (property === 'stepSizeCurrent')? loopName + '.stepSize' : loopName + '.' + property;
attributes[key] = loop[property];
}
const loopName = loop.name;
for (const loopProperty in loop)
if (properties.includes(loopProperty)) {
const key = (loopProperty === 'stepSizeCurrent')? loopName + '.stepSize' : loopName + '.' + loopProperty;
attributes[key] = loop[loopProperty];
}
// trial's attributes:
// specific trial attributes:
if (typeof loop.getCurrentTrial === 'function') {
const currentTrial = loop.getCurrentTrial();
for (const trialProperty in currentTrial)

View File

@ -3,7 +3,7 @@
* Trial Handler
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/
@ -173,8 +173,7 @@ export class TrialHandler extends PsychObject {
*/
getSnapshot() {
const currentIndex = this.thisIndex;
return {
const snapshot = {
name: this.name,
nStim: this.nStim,
nTotal: this.nTotal,
@ -187,6 +186,8 @@ export class TrialHandler extends PsychObject {
getCurrentTrial: () => this.getTrial(currentIndex)
};
return snapshot;
}

View File

@ -3,7 +3,7 @@
* Sound stimulus.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Sound player interface
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Tone Player.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Track Player.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Clock component.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Color management.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Color Mixin.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Event Emitter.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Logger
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -3,7 +3,7 @@
* Core Object.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Scheduler.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Various utilities.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Image Stimulus.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/
@ -195,6 +195,9 @@ export class ImageStim extends util.mix(VisualStim).with(ColorMixin)
* @return {boolean} whether or not the image contains the object
*/
contains(object, units) {
if (typeof this._image === 'undefined')
return false;
// get position of object:
let objectPos_px = util.getPositionFromObject(object, units);
if (typeof objectPos_px === 'undefined')

View File

@ -2,7 +2,7 @@
* Movie Stimulus.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Rectangular Stimulus.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -3,7 +3,7 @@
* Basic Shape Stimulus.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Slider Stimulus.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/
@ -531,19 +531,19 @@ export class Slider extends util.mix(VisualStim).with(ColorMixin)
this._marker.moveTo(0, 0);
if (this._isHorizontal()) {
if (this._flip) {
this._marker.lineTo(markerSize_px, markerSize_px);
this._marker.lineTo(-markerSize_px, markerSize_px);
this._marker.lineTo(markerSize_px/2, markerSize_px/2);
this._marker.lineTo(-markerSize_px/2, markerSize_px/2);
} else {
this._marker.lineTo(markerSize_px, -markerSize_px);
this._marker.lineTo(-markerSize_px, -markerSize_px);
this._marker.lineTo(markerSize_px/2, -markerSize_px/2);
this._marker.lineTo(-markerSize_px/2, -markerSize_px/2);
}
} else {
if (this._flip) {
this._marker.lineTo(-markerSize_px, markerSize_px);
this._marker.lineTo(-markerSize_px, -markerSize_px);
this._marker.lineTo(-markerSize_px/2, markerSize_px/2);
this._marker.lineTo(-markerSize_px/2, -markerSize_px/2);
} else {
this._marker.lineTo(markerSize_px, markerSize_px);
this._marker.lineTo(markerSize_px, -markerSize_px);
this._marker.lineTo(markerSize_px/2, markerSize_px/2);
this._marker.lineTo(markerSize_px/2, -markerSize_px/2);
}
}
this._marker.endFill();

View File

@ -2,7 +2,7 @@
* Text Stimulus.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/

View File

@ -2,7 +2,7 @@
* Base class for all visual stimuli.
*
* @author Alain Pitiot
* @version 3.1.4
* @version 3.2.0
* @copyright (c) 2019 Ilixa Ltd. ({@link http://ilixa.com})
* @license Distributed under the terms of the MIT License
*/