Converting Drag and Drop code from Python to JS

Hello :slightly_smiling_face:

(Updated 17.11.2020: Following is the URL for anyone who want to help me with this: Sign in · GitLab). Any help is very much appreciated!!

In my experiment I have included a Drag and Drop feature (Code by @dvbridges). The Python version in Builder works perfectly, the automatically translated JS code not so much. This will throw back the error:typeError: movePicked is not a function

I have already tried to look for another translation (since I am super duper new to JS and all), and I found nw from @wineandsushi I tried: Click and drag images to lock in a specified area - #6 by wineandsushi. But this also throws back an error: ReferenceError: movePicked is not defined (see picture)

The JS code is as follows:
Begin Experiement:

**Begin Experiement:**
//automatically translated JS code which gives back the Error: typeError: movePicked is not a function

function createPiece(piece, pos, name) {
    return new visual.ImageStim(win, {"image": piece.image, "name": name, "size": piece.size, "pos": pos});
}
function drawPicked(picked) {
    for (var each, _pj_c = 0, _pj_a = picked, _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
        each = _pj_a[_pj_c];
        each.draw();
    }
}
function movePicked(picked, mouseExpIndiv, grabbed) {
    if (((grabbed !== undefined) && mouseExpIndiv.isPressedIn(grabbed))) {
        grabbed.pos = mouseExpIndiv.getPos();
        return grabbed;
    } else {
        for (var piece, _pj_c = 0, _pj_a = picked, _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
            piece = _pj_a[_pj_c];
            if ((mouseExpIndiv.isPressedIn(piece) && (grabbed === undefined))) {
                return piece;
            }
        }
    }
}


//Also **Begin Experiment:** Code from wineandsushi, which gives back the error: ReferenceError: movePicked is not defined
function createPiece(piece, pos, name){
  return new visual.ImageStim({win : psychoJS.window,
                                image: piece.image, 
                                name: name,
                                size: piece.size, 
                                pos: pos})
}

function drawPicked(picked, draw) {
  if (picked.length > 0) {
    for(let each of picked) {
      each.autoDraw = draw;
    }
  }
}

function movePicked(picked, mouseExpIndiv, grabbed) {
  if (grabbed != 'undefined' &&  mouseExpIndiv.getPressed()[0] === 1) {
    grabbed.pos = mouseExpIndiv.getPos();
    return grabbed
  } else {
      for (let piece of picked) {
        if (piece.contains(mouseExpIndiv) &&  mouseExpIndiv.getPressed()[0] === 1 && grabbed === 'undefined'){
          piece.pos = mouseExpIndiv.getPos();
          return piece;
        }
      }
   return 'undefined'
  }
}

Begin Routine:

//**Begin Routine:**
pieces = [redPieceExInd, greenPieceExInd];
picked = [];
movingPiece = 'undefined';
polygonExpIndiv.setFillColor('undefined');
polygonExpIndiv.setLineColor('undefined');
polygonWeiterExampleIndiv.setAutoDraw(false);
polygonWeiterExampleIndivOn = false;
polygonWeiterExpIndivSelected = false;
polygonWeiterExampleIndiv.setFillColor('undefined');
polygonWeiterExampleIndiv.setLineColor('undefined');

Each frame:

//**Each frame:**
for (let piece of pieces) {
    if (piece.contains(mouseExpIndiv) && mouseExpIndiv.getPressed()[0] === 1) {
        picked.push(piece)
    }
    movingPiece = movePicked(picked, mouseExpIndiv, movingPiece);
    drawPicked(picked);
    if (mouseExpIndiv.isPressedIn(piece)) {
        if ((polygonExpIndiv.contains(mouseExpIndiv) && mouseExpIndiv.isPressedIn(redPieceExInd))) {
            thisExp.addData("ExampleIndivAnswer", "No");
            polygonWeiterExampleIndiv.setAutoDraw(true);
            polygonWeiterExampleIndiv.setFillColor([(- 1.0), 0.004, 0.506], "rgb");
            polygonWeiterExampleIndiv.setLineColor([(- 1.0), 0.004, 0.506], "rgb");
            polygonWeiterExampleIndivOn = true;
        } else {
            if ((polygonExpIndiv.contains(mouseExpIndiv) && mouseExpIndiv.isPressedIn(greenPieceExInd))) {
                thisExp.addData("ExampleIndivAnswer", "Yes");
                polygonWeiterExampleIndiv.setAutoDraw(true);
                polygonWeiterExampleIndiv.setFillColor([(- 1.0), 0.004, 0.506], "rgb");
                polygonWeiterExampleIndiv.setLineColor([(- 1.0), 0.004, 0.506], "rgb");
                polygonWeiterExampleIndivOn = true;
            }
        }
    }
    if ((polygonWeiterExampleIndivOn && mouseExpIndiv.isPressedIn(polygonWeiterExampleIndiv))) {
        polygonWeiterExpIndivSelected = true;
    }
    if (polygonWeiterExpIndivSelected) {
        continueRoutine = false;
    }
}

End Routine

//**End Routine::**
piece = drawPicked(picked, false, piece) //added from wineandsushi, but I don't know what it does
polygonWeiterExampleIndiv.setAutoDraw(false);
polygonWeiterExampleIndivOn = false;
polygonWeiterExpIndivSelected = false;
polygonWeiterExampleIndiv.setFillColor('undefined');
polygonWeiterExampleIndiv.setLineColor('undefined');

I don’t quite understand why movePicked is not defined or not a function, because it is defiend in Begin Experiment or BEgin Routine? Can anybody help me with that please?

1 Like

Hi There,

Just to check - did you start with the drag and drop demo in builder view or pavlovia? this version seems to work smoothly online for me :slight_smile: https://gitlab.pavlovia.org/demos/draganddrop

Becca

Hi Becca,

I did try out with the Builder Demo. I checked with the version you mentioned and adjusted small things in my Each frame tab but this does not seem to change the error. It still tells me that “movePicked is not a function” and references to the line in the picture above.

The one thing that does differ between both version is the ability to create new Pieces continously. I only need one piece to be able to drag around, so I deleted all information which with ‘newPiece’ in it. For Python this worked fine…

Each frame tab:

for (let piece of pieces) {
    if (piece.contains(mouseExpIndiv) && mouseExpIndiv.getPressed()[0] === 1) {
        picked.push(piece)
    }
}
movingPiece = movePicked(picked, mouseExpIndiv, movingPiece) //moved it out of the loop
drawPicked(picked, true) //added the 'true' like in pavlovias Demo
if (mouseExpIndiv.isPressedIn(piece)) {
    if ((polygonExpIndiv.contains(mouseExpIndiv) && mouseExpIndiv.isPressedIn(redPieceExInd))) {
        thisExp.addData("ExampleIndivAnswer", "No");
        polygonWeiterExampleIndiv.setAutoDraw(true);
        polygonWeiterExampleIndiv.setFillColor(blue);
        polygonWeiterExampleIndiv.setLineColor(blue);
        polygonWeiterExampleIndivOn = true;
    } else {
        if ((polygonExpIndiv.contains(mouseExpIndiv) && mouseExpIndiv.isPressedIn(greenPieceExInd))) {
            thisExp.addData("ExampleIndivAnswer", "Yes");
            polygonWeiterExampleIndiv.setAutoDraw(true);
            polygonWeiterExampleIndiv.setFillColor(blue);
            polygonWeiterExampleIndiv.setLineColor(blue);
            polygonWeiterExampleIndivOn = true;
        }
    }
}
if ((polygonWeiterExampleIndivOn && mouseExpIndiv.isPressedIn(polygonWeiterExampleIndiv))) {
    polygonWeiterExpIndivSelected = true;
}
if (polygonWeiterExpIndivSelected) {
    continueRoutine = false;
}

I don’t know if these changes help at all, because I think everything should be inside the for loop and not seperated like it is now.

Hi @Rika, with the drag-and-drop demo, the code was manually translated, where the Python code was not written to optimally auto-translate to JS, so if you start with a fresh copy of the demo and can manually make your changes to both Python and JS with auto-translate turned off in the code component, things should work ok.

The error occured because PsychoPy does not give function declarations e.g., function movePicked(x, y){} global scope. Instead, you would have to use a function expression so the function is stored in a variable name, which PsychoPy will automatically declare with global scope e.g., movedPicked = function(x, y){}, meaning it can be used anywhere in the experiment. If you are sticking with auto-translate, the way around this error is to define a new variable in the Python code holding the function, so that the translator declares this variable / function name in the JS code e.g.,

def movePicked(etc):
    # move picked code
movePicked = movePicked

1 Like

Thank you so much! That was exactly the problem. By rewriting the function as movePicked = function (x,z) etc. (and all the others in the same way) I got it to work!

1 Like