Wednesday, February 2, 2011

Dragging and drop objects

Recently someone asked me whether PEBL supports dragging and dropping images around on the screen.  I had never really done this before, but it is pretty simple to do. To show how its done, I made a little demo script that works sort of like magnetic poetry.  You can download the script to run on your computer here

There are probably a lot of card-sorting-type tasks that could use this, especially the type where the subject or consumer must form their own stacks of categories.  I would caution that if on just needs
a card to move from one pile to another, probably a better way is to just have the subject click on the pile and move the card smoothly.  I do this is the Tower of London test and the WCST.  Making a drag-drop
just makes it more difficult, especially if your participants are not expert mouse users, and if you are using a touch screen, it is easier to just click on the end point.


To make magnetic poetry in PEBL, basically, put a bunch of words of words on the screen and let the user move them around until they are done.

The challenges here:

  1. How do you get and select words?
  2. How do I make the word labels look like magnets?
  3. How do I drag these things around?
  4. How do you enable dragging but also allow for some exit button?


Problem 1 is simple. somewhere I found a list of magnetic poetry words (about 500), and so I just created a list in PEBL with all the words.  This requires a bit of mangling to get quotes and commas between every word, so you can also read them in from another file using FileReadList().  I sample a subset of these for each run of the script so you are not overwhelmed.

Problem 2 seems simple.  It is trivial to put a bunch of labels on the screen at random locations.  For example, if mywords contains a list of words:

  stim <- []
  ##Make words as text boxes, along with background rectangles for depth
  loop(i,mywords) 
   {
     lab <-  MakeLabel(" "+i+" ",font)
     Move(lab, 50+Random()*(gVideoWidth-100),30+Random()*(gVideoHeight-200))
     AddObject(lab,gWin)
     stim <- Append(stim,lab)
   }

Now, the words are placed randomly around the upper part of the screen, with a little buffer from the edge.  Notice that I pad the word with spaces on either side to get the larger textbox.

This looks OK, but not  too great:


What can be done to improve it?  One thought is to create a graphic, to put behind that looks like one of those magnetic strips.  Then I'd need to resize it, or somehow figure out how to stretch it.
Instead, I'll try adding just a some black rectangles behind the labels, offset by two pixels to the right and below.  Here is what it looks like, blown up:




This makes the drag-dropping a little more difficult, because you have to move two objects around, and do one with an offset.

Problem 3 is a bit trickier.    The workhorse function WaitForClickOnTarget will do the dirty work.  You give it a list of objects and an output list, and when you click on the target object, the corresponding output will be returned.  Then, I set up a little loop that checks for the mouse button to un-click, and move the target/backing around with the mouse location.  The part that is least elegant is that you need to write the update loop within PEBL  Hopefully in the next version of PEBL, I'll be adding a function (MoveTarget) to do this to PEBL so it will be sort of transparent to the user.  To do it, you need to also figure out how to exit out of the drag-and-drop mode.  This is problem #4.

So, how will this function work?  You give it three things:  a list of targets, a list of identifiers of those targets, and an exit object.  When you click on a target object, it drags it until you release the mouse.  If you click on the exit object, it just returns

define MoveTarget(set,ids,exit)
{

  #just click on the top
  clickon <- First(Transpose(set))

  a <- WaitForClickOnTarget(Merge([exit],clickon),Sequence(0,gNumWords,1))

  if(a != 0)
   {
    drag <- Nth(set,a)

   ##Make a drag point hold, instead of recentering.
   xy <- GetMouseCursorPosition()
   front <- First(drag)  #Pick the label out
   offx <- front.x-First(xy)  ##Get the offset
   offy <- front.y-Second(xy)
   set <- ResetList(set,a)    ##rearrange to move click to top
   Draw()
   test <-WaitForMouseButtonWithTimeout(50)
   while("" == First(test))
   {
     xy <- GetMouseCursorPosition()
     Move(First(drag), First(xy)+offx+2,Second(xy)+offy+2)
     Move(Second(drag), First(xy)+offx,Second(xy)+offy)
     Draw()
     test <- WaitForMouseButtonWithTimeout(50)
    }
  }

  return [a, set]
}





So, the magnetic poetry demo just makes for a good example of how to move targets around.  The demo also takes a screenshot of the final 'poem' that it saves for you (at least on windows and linux--I don't think screenshots currently work for OSX).  Video screenshot below.






No comments: