Help with E-Basic Mouse-Capture Loop

717 views
Skip to first unread message

Tyler

unread,
Dec 13, 2010, 10:57:14 AM12/13/10
to E-Prime
I have two questions that I hope someone can help me figure out the
answers to.

I'm trying to present a Likert scale on the screen using a Slide and
capture responses using E-Basic. I have working code for this, based
on an example I found on the PST forums. The first issue I am having
is what to do when the user clicks an object that doesn't register as
a scale response. Initially, the code I was using would simply
continue and register the rating as "nothing". However, as I require a
response, I simply used an If... condition that would jump back to a
Label (and start the trial over) unless it was a registered response.
This is the code I have:

------------- BEGIN CODE

'Designate "theState" as the Default Slide State, which is the
'current ActiveState on the Slide object "Stimulus".
Dim theState As SlideState
Set theState = F1DStimulus.States("Default")

Dim theSlideText As SlideText

Dim strHit As String
Dim intRating As Integer
Dim theMouseResponseData As MouseResponseData


'Get the mouse response.
Set theMouseResponseData =
CMouseResponseData(F1DStimulus.InputMasks.Responses(1))

'Was there a response?
If F1DStimulus.InputMasks.Responses.Count > 0 Then

'Determine string name of SlideText object at
'mouse click coordinates. Assign that value to strHit
strHit = theState.HitTest(theMouseResponseData.CursorX,
theMouseResponseData.CursorY)

'Did the subject click one of the SlideText sub-objects?
If strHit <> "" And strHit <> "Question" And strHit <> "Image" And
strHit <> "Instr" Then

'Gain access to the SlideText sub-object selected
'Change appearance of selected sub-object to provide feedback to
the subject.
Set theSlideText =
CSlideText(F1DStimulus.States.Item("Default").Objects(strHit))
theSlideText.BackColor = CColor("red")

'Redraw the Slide to present changes
F1DStimulus.Draw

'Each SlideText is named "Text" followed by a single digit. The Mid
function is
'instructed to return the 5th character (i.e. the digit) of strHit
for logging purposes.
intRating = CInt(Mid(strHit, 5, 1))

'Log rating in the data file under the attribute "Rating"
c.SetAttrib "F1DRating", intRating

Sleep 1000
theSlideText.BackColor = CColor("gray")

'The subject did not click a valid sub-object.
Else
'Restart the trial
GoTo F1DTLabel
End If

'The subject did not respond.
Else
'Restart the trial
GoTo F1DTLabel
End If

Set theMouseResponseData = Nothing
Mouse.ShowCursor FALSE

------------- END CODE

The above code works flawlessly.

The problem I am having now is that I would like to use a Do... Loop
Until in place of the GoTo Label command. With the following code in
place, clicking on a scale position work, but clicking on an invalid
object does not. What happens is that the program fails to capture any
mouse clicks beyond the first one. This is the code I am using:

------------- BEGIN CODE

'This file has been modified by Tyler Burleigh - December 11, 2010

'Designate "theState" as the Default Slide State, which is the
'current ActiveState on the Slide object "Stimulus".
Dim theState As SlideState
Set theState = F1AStimulus.States("Default")

Dim theSlideText As SlideText

Dim strHit As String
Dim intRating As Integer
Dim theMouseResponseData As MouseResponseData

Do
'Get the mouse response.
Set theMouseResponseData =
CMouseResponseData(F1AStimulus.InputMasks.Responses(1))

'Was there a response?
If F1AStimulus.InputMasks.Responses.Count > 0 Then

'Determine string name of SlideText object at
'mouse click coordinates. Assign that value to strHit
strHit = theState.HitTest(theMouseResponseData.CursorX,
theMouseResponseData.CursorY)

'Did the subject click one of the SlideText sub-objects?
If strHit <> "" And strHit <> "Question" And strHit <> "Image" And
strHit <> "Instr" Then

'Gain access to the SlideText sub-object selected
'Change appearance of selected sub-object to provide feedback to
the subject.
Set theSlideText =
CSlideText(F1AStimulus.States.Item("Default").Objects(strHit))
theSlideText.BackColor = CColor("red")

'Redraw the Slide to present changes
F1AStimulus.Draw

'Each SlideText is named "Text" followed by a single digit. The Mid
function is
'instructed to return the 5th character (i.e. the digit) of strHit
for logging purposes.
intRating = CInt(Mid(strHit, 5, 1))

'Log rating in the data file under the attribute "Rating"
c.SetAttrib "F1ARating", intRating

Sleep 1000
theSlideText.BackColor = CColor("gray")

End If
End If

Loop Until (intRating > 0)

Set theMouseResponseData = Nothing
Mouse.ShowCursor FALSE

------------- END CODE

Can someone help me identify what may be causing the program to not
register new mouse clicks? I think it's something simple such as a
value not being re-set somewhere, however I tried placing Set
theMouseResponseData = Nothing and intRating = 0 inside the loop
without any luck.

Second, is it possible for the E-Basic code to "know" (or read-in) the
name of the Procedure it is running? In the code I am working with I
need to make 100+ procedures and adapt the code for each (changing,
for example, F1AStimulus to F1DStimulus), but it would save me
countless hours if I could read in this value. Is this possible?

Thanks,
Tyler

David McFarlane

unread,
Dec 13, 2010, 4:16:30 PM12/13/10
to e-p...@googlegroups.com
Tyler,

Stock reminder: 1) I do not work for PST. 2) PST's trained staff
takes any and all questions at
http://support.pstnet.com/e%2Dprime/support/login.asp , and they
strive to respond to all requests in 24-48 hours -- this is pretty
much their substitute for proper documentation, so make full use of
it. 3) If you do get an answer from PST Web Support, please extend
the courtesy of posting their reply back here for the sake of others.

That said, here is my take...

First some explanation, then some pointers toward solutions...

If I understand correctly the example you found at PST, then your
F1DTLabel lies on the Procedure somewhere before your
F1DStimulus. When the Goto goes back to that label, it re-runs your
F1DStimulus, which then also re-runs its input mask and captures one
more mouse click, which your inline code then processes. But the
Do...Loop code never re-runs the input mask, so you never get another
mouse click. You might get around this by the simple expedient of
replacing your F1DTLabel with a bit of inline code that simply says

Do

and then following up with the approprate Loop Until statement in the
code that follows the stimulus, but I expect you had something more
elegant in mind like handling everything in one inline code object.

So you would need to have some way for your code to get additional
mouse clicks. In addition, if you run this as an ordinary self-paced
presentation (i.e., stimulus Duration of (infinite) and End Action of
(terminate)), then your program does not get to the inline code until
after the first mouse click, so depending on how you want to play
this you might have to also find some way to have EP run your code as
soon as the stimulus is presented.

So much for explanation, on to solutions. I will address the second
issue first, then the first issue.

You have two options for running inline code right after presentation
of a stimulus: either set the stimulus Duration to 0, or set
PreRelease >= Duration. Most users will naturally adopt the first
option, and that indeed works best for self-paced presentations. But
if you want the stimulus to time out or the next stimulus to come at
a controlled time later, then your code would have to also handle the
timing. Most users would handle that with some use of Clock.Read,
but by judicious use of PreRelease you can have your code run and
still let EP automatically handle the stimulus timing for you. (Some
day I will write up a proper explanation of the interactions between
Duration, PreRelease, and NextTargetOnsetTime and post that to the
list.) In your case, if your task involves only a self-paced
presentation (i.e., stimulus remains until a response, then moves
on), then just set stimulus Duration to 0. In any case, to collect
responses while your code runs, you would also have to use "Extended
Input" (see Appendix C of the User's Guide that came with E-Prime),
e.g., set Time Limit to (infinite).

OK, now to the code. It all comes down to the line

Set theMouseResponseData = _
CMouseResponseData(F1AStimulus.InputMasks.Responses(1))

As it stands now, that line gets the first, one, and only response
from the input mask. You want to allow for more mouse clicks. I can
think of three approaches here. First, you could use the Advanced
properties to increase the MaxCount to some suitably high
value. Then you might change that line to something like

Set theMouseResponseData = _
CMouseResponseData( F1AStimulus.InputMasks.Responses(_
F1AStimulus.InputMasks.Responses.Count) )

which will now get the latest mouse click each time. Or you could do
without the input mask and address the Mouse device History directly, as in

Set theMouseResponseData = _
CMouseResponseData( Mouse.History(Mouse.History.Count) )

(if you do this, make sure first that the Mouse.History contains at
least one mouse click, otherwise you will get a run-time error). Or
you could go hard-core and handle the mouse directly (only the mouse
(and perhaps the joystick) allows this level of access), e.g.,

Mouse.GetCursorPos p.x, p.y

with other code changes as appropriate (plus code to debounce the
mouse, get response time, etc.).

I like the second and third approaches since those allow me to write
transportable code that does not need to know anything about the
stimulus object (and then I do not use any input mask on the stimulus
object). I actually used the third approach to write an extensive
library of routines to do exactly what you are trying to do, but that
was before I understood the device History mechanism, now I might
rewrite it to use that approach. (If I ever get that cleaned up
enough then I might submit it to STEP to see if they will post it for
everyone.)

Finally, your code might in fact work by just making the
substitutions I suggest, but as it stands that would repeatedly
re-test each mouse click until the next one comes. So if you want to
look like a real programmer then you will add code to test to see
when a new click arrives, and while you are at it clean up some of
the other sloppiness that was in the original PST example. I leave
that as an exercise for you.


I will address your second question in a separate post.

-- David McFarlane, Professional Faultfinder
"For a successful technology, reality must take precedence over
public relations, for nature cannot be fooled." (Richard Feynman,
Nobel prize-winning physicist)

David McFarlane

unread,
Dec 13, 2010, 4:19:59 PM12/13/10
to e-p...@googlegroups.com
Tyler,

>Second, is it possible for the E-Basic code to "know" (or read-in) the
>name of the Procedure it is running? In the code I am working with I
>need to make 100+ procedures and adapt the code for each (changing,
>for example, F1AStimulus to F1DStimulus), but it would save me
>countless hours if I could read in this value. Is this possible?

Indeed, c.GetAttrib("Procedure") will return a string with the name
of the current running Procedure (unless it is SessionProc, in which
case you get a run-time error). In addition, you can use
Rte.GetObject to retrieve any E-Object by name. Please see the
appropriate topics in the online E-Basic Help.

But this seems a very poor way to get your work done. I don't know
enough about your particular experimental task to see why you need
100+ Procedures, nor do I want to know that much. But I have vast
experience with EP and other platforms, and I cannot think of a time
when some user's large & complex program did not result simply from
their poor structure (<rant> which in turn results from a perverse
refusal of users to get even the most rudimentary formal training in
computer programming </rant>). Time and again users have come to me
with a program where they used a different Procedure for any time
they wanted to use a different stimulus content, instead of simply
changing the stimulus content by using an attribute (from a List or
from code), as is shown in Tutorial 1 of the Getting Started
Guide. This recurring mistake completely violates the very purpose
of a tool like E-Prime, it would be like turning a power drill by
hand. On several occasions I have taken some program from a user and
reduced it down to a single Procedure that covered all the
situtations, controlling contest with attributes, which made the
program much more manageable and understandable.

So I suspect that your 100+ Procedures problem has more to do with
not completely working out the task structure, and rather than adding
more complexity in order to get around this, I would implore you to
fix the underlying structure. That will pay much bigger dividends.

-- David McFarlane, Professional Faultfinder

"When all is said and told, the 'naturalness' with which we use our
native tongues boils down to the ease with which we can use them for
making statements the nonsense of which is not obvious." -- Edsger
W. Dijkstra, "On the foolishness of 'natural language programming'"
(http://www.cs.utexas.edu/users/EWD/transcriptions/EWD06xx/EWD667.html)

David McFarlane

unread,
Dec 13, 2010, 4:28:29 PM12/13/10
to e-p...@googlegroups.com
Hmm, after drafting and posting that lengthy reply, I checked my
E-Prime FAQ and found that I had posted another answer to this at
http://support.pstnet.com/forum/Topic4009-23-1.aspx , so you might
also look there (contains more detailed code, but lacks the detailed
explanation).

As long as I am at it, I should have declared my variable in my third
mouse approach fragment, so here you go:

Dim p as Point
Mouse.GetCursorPos p.x, p.y

>--
>You received this message because you are subscribed to the Google
>Groups "E-Prime" group.
>To post to this group, send email to e-p...@googlegroups.com.
>To unsubscribe from this group, send email to
>e-prime+u...@googlegroups.com.
>For more options, visit this group at
>http://groups.google.com/group/e-prime?hl=en.

Tyler

unread,
Dec 13, 2010, 7:33:29 PM12/13/10
to E-Prime
As I had Inline code beginning my procedure anyway, I simply added a
Do statement to the beginning, and left the Loop Until statement where
it was, and that fixed that problem. As you suggested, I've also
removed the mouse mask instead hard-coding using the GetCursorPos
function. Simultaneously, I am migrating the code I am using now which
uses a Slide object to Canvas objects instead, and that way I can use
conditional statements and drastically economize the number of
procedures I will need.

Thank you for your help, I've learned quite a bit from your response.

Cheers!
Tyler
Reply all
Reply to author
Forward
0 new messages