PDA

View Full Version : something like ginput?



hcrisp
04-01-2009, 03:55 PM
What procedure do I call to get an XY location from the plot via the mouse? I want something like MATLAB's ginput() function:



ginput -
Graphical input from mouse or cursor
Syntax
[x,y] = ginput
Description
ginput enables you to select points from the figure using the mouse for cursor positioning. The figure must have focus before ginput receives input.[x,y] = ginput enables you to select a point from the current axes and returns the x- and y-coordinates

brian
04-01-2009, 05:20 PM
Hello hcrisp,

You may want to try CURSOR:


Reads the position of the interactive graphics cursor from the current graphics device.



WINDOW, XSize=512, YSize=512
CURSOR, x, y, /Normal
print, x
; 0.316406
print, y
; 0.302734


Regards,

brian

hcrisp
04-02-2009, 03:32 PM
Good tip. It works fine, except when you click the "X" exit button on the plot. In my application it causes the WAVE window to hang. I have created a plot inside of a window created using WwMainWindow. Does CURSOR not work with WwMainWindow? What is the correct way to handle the plot exit callback when CURSOR is running?

ed
04-02-2009, 05:26 PM
CURSOR is a blocking procedure meant to be used from the command line. For widgets you can use WtAddHandler using the ButtonPressMask as the event mask. You can see this in action in the Oil & Gas demo in the Gallery (wd_demo17.pro).

brian
04-02-2009, 06:02 PM
Hello hcrisp,

You can try using the /Nowait keyword to circumvent the blocking action.

Rubberband box handler:

Main:


status = WtAddHandler(win2, ButtonPressMask, 'ButtonEventCB', 2)


Callback:


PRO ButtonEventCB, wid, id, nparams, mask, event
COMMON zoomblock, zoomon, zoomwin, rangereduction, multizoom,border,zoomrange
COMMON structures, wids, dataSet1, dataSet2, dataSet3, dataSet4
; INFO,wid,id,nparams,mask,event,/Structures

WSET,id
zoomon=1
zoomwin=id

; Rubberband a selection box till button let up
!Err = 0
CURSOR, cx1, cy1, /Nowait,/Data; /Device
cx2 = cx1
cy2 = cy1
Device, Set_Graphics_Function=6
WHILE (!ERR EQ 1) DO BEGIN
Plots, [cx1, cx2, cx2, cx1, cx1], [cy1, cy1, cy2, cy2, cy1],/Data;,/Normal
CURSOR, cx2, cy2, /Nowait,/Data; /Device
Plots, [cx1, cx2, cx2, cx1, cx1], [cy1, cy1, cy2, cy2, cy1],/Data;,/Normal
Empty
Wait, 0.08
ENDWHILE
Device, Set_Graphics_Function=3

; Wait cursor
status = WtCursor ('WAIT', wid)
; Get points in ascending order
x1 = cx1 < cx2
x2 = cx1 > cx2
y1 = cy1 < cy2
y2 = cy1 > cy2

; print,x1,x2,y1,y2

rangereduction=REFORM([x1,x2,y1,y2])
rangereduction(0:1)=rangereduction(0:1)+zoomrange( 0)
; print,rangereduction
set1_listcb, wids.set1_list
zoomon=0
; End Wait cursor
status = WtCursor ('Default', wid)
END


Regards,

brian

donb
04-02-2009, 09:50 PM
Hcrisp,

The code Brian provided is indeed an excellent example of how the /Nowait keyword can be used to track cursor movements in a non-widget application.
However, if your application is PV-WAVE Widget based, Ed is correct - use WtAddHandler.

The CURSOR command, especially with the /Nowait keyword, is going to try to process all mouse events - at the same time that the window manager event handler is going to try to process all events. This is a huge conflict and invariably leads to confusion by either PV-WAVE or the window manager.

As a general rule, when writing PV-WAVE Widget applications, do not use any of the routines identified as 'blocking' routines (CURSOR, HAK, TVMENU, etc.) in PV-WAVE documentation.

Hope this helps,

Don B.

hcrisp
04-03-2009, 08:34 AM
All very good comments. I was hoping not to go the WtAddHandler route. It is more complex and when I tried it, it did not block execution, thus causing the code to return to MAIN before it received the cursor points.

So how to get the handler to wait until the cursor points are caught before continuing? It won't do to simply process them in the Click callback, because the top level program needs the values to complete its tasks. I.e.:



PRO mymain, input, output
; Create and display plot of input in window
; Call CURSOR or WtAddHandler
; I never get here with WtAddHandler or if use CURSOR and close window
; Here I process the cursor points so they can be passed back through output
END


Is there no way to simply hide the Exit button when creating a window with WwMainWindow?

brian
04-03-2009, 10:23 AM
Hello hcrisp,

Here is a fully functioning PVWAVE Widget/WtAddHandler/CURSOR rubberband box example (I have not seen any ill effects when using /Nowait but I would defer to Don on that issue).:

To display the full data set after zooming press the button again...




PRO DrawCB, wid, data
COMMON structures, wids, mydata
COMMON zoomblock, zoomon, zoomwin, rangereduction, multizoom,border,zoomrange

WSET, 0

IF PARAM_PRESENT(data) THEN zoomon=0

IF(zoomon EQ 0) THEN BEGIN
xmind=0.0
xmaxd=1000
ymind=-1
ymaxd=1
ENDIF ELSE BEGIN
xmind=zoomrange(0)
xmaxd=zoomrange(1)
ymind=zoomrange(2)
ymaxd=zoomrange(3)
ENDELSE

PLOT, mydata.x, mydata.y, Color=0, Background=WoColorConvert(255), Title='Rubberband Example', $
Position=[.1,.1,.9,.9], XRange=[xmind, xmaxd], YRange=[ymind,ymaxd]

zoomon=1
END

PRO ButtonEventCB, wid, id, nparams, mask, event
COMMON structures, wids, mydata
COMMON zoomblock, zoomon, zoomwin, rangereduction, multizoom,border,zoomrange

WSET,id
zoomon=1
zoomwin=id

; Rubberband a selection box till button let up
!Err = 0
CURSOR, cx1, cy1, /Nowait,/Data; /Device
cx2 = cx1
cy2 = cy1
Device, Set_Graphics_Function=6
WHILE (!ERR EQ 1) DO BEGIN
Plots, [cx1, cx2, cx2, cx1, cx1], [cy1, cy1, cy2, cy2, cy1],/Data;,/Normal
CURSOR, cx2, cy2, /Nowait,/Data; /Device
Plots, [cx1, cx2, cx2, cx1, cx1], [cy1, cy1, cy2, cy2, cy1],/Data;,/Normal
Empty
Wait, 0.08
ENDWHILE
Device, Set_Graphics_Function=3

; Wait cursor
status = WtCursor ('WAIT', wid)
; Get points in ascending order
x1 = cx1 < cx2
x2 = cx1 > cx2
y1 = cy1 < cy2
y2 = cy1 > cy2

; print,x1,x2,y1,y2

zoomrange=[x1,x2,y1,y2]
DrawCB
zoomon=0
; End Wait cursor
status = WtCursor ('Default', wid)
END

PRO rubberband_box_ex
COMMON structures, wids, mydata
COMMON zoomblock, zoomon, zoomwin, rangereduction, multizoom,border,zoomrange

TEK_COLOR
!P.font=0
@wtxlib

x=DINDGEN(1000)
y=SIN(x/50)

mydata = {,x : x, $
y : y}

wids={, shell :0L, $
winid :0L}
zoomon=0

title="Rubberband Box Ex"
wids.shell=WwInit(title,'Prototype',mainlayout,/Form,Title=title)
layout1=WwLayout(mainlayout,/Form,/Frame,/Left,/Top, /Bottom)

plabel=[' Plot ']
pbbox=WwButtonBox(layout1, plabel, 'DrawCB')

layout2=WwLayout(mainlayout,/Form,/Frame,Left=layout1,/Right,/Top, /Bottom)
wids.winid = WwDrawing(layout2, 0, DrawCB,[800,460],[800,460], /Left, /Top, /Right, /Bottom,/NoScroll)
status = WtAddHandler(wids.winid, ButtonPressMask, 'ButtonEventCB', 0)

status=WwSetValue(wids.shell,/Display)
WwLoop
END


Regards,

brian

hcrisp
04-07-2009, 04:05 PM
Ed, Brian,

Thanks for your help, but it can not be done the way I want. There are flaws with both approaches:

1) Using WtAddHandler causes mymain() procedure to return without any values in "output" since it completes before the mouse click event is handled.
2) Using CURSOR leaves you vulnerable to clicking the plot "exit" button.

Since I basically want to wrap this whole thing with mymain(), supplying "input" and getting "output", I don't think there is a viable work-around. If you or anyone thinks there is a way, please suggest it.

donb
04-07-2009, 05:54 PM
Hi hcrisp,

It seems we're going back and forth between two very different programming philosophies - event driven and traditional sequential programming. Either can be used and either can address what you're trying to do.

At this point I'd like to recommend a one-on-one discussion to help identify the requirements, decide on the best implementation, and then we can post a definitive resolution for others to review. I'm available by phone at the number below if this approach is of interest to you.

Cheers,

Don B.

totallyunimodular
04-07-2009, 09:21 PM
Hi hcrisp,

Two things: First, I think Don is right in that to answer your question we may need to have a more lengthy discussion off-line, and then add the final solution to the thread for posterity. Second, there may be existing examples in the PV-WAVE code base that are up your alley. Consider this section of the header in wave/lib/std/guitools/wgtext.pro:



; wgtext_output - Contains one variable, which is the text entered by the
; user. When WgText is called stand-alone, it runs in its
; own event loop. Once the user finishes typing their
; input, control is passed back to the calling routine and
; the user input is accessed through the variable chars.
; However, if WgText is called from an existing widget
; application, the tool must use this calling widget's
; event loop. In this case, WgText is executed and control
; is passed back immediately to the calling widget
; application, before the user can enter their string into
; the text field. To allow the widget application that
; calls WgText to have access to the most recent entry by
; the user, include this common block in the calling widget
; application

I have a feeling that the issue discussed in this comment section is what you are up against: how to have the correct sequential flow of information between different analyses when widgets that a user interface with are confined to one event loop. One answer is to use PV-WAVE common blocks to share "state variables" between routines...

For another example of mouse interaction with graphics, see wave/lib/user/examples/widget_zoom_test.pro, which is heavily commented.

Hope this helps.

hcrisp
04-08-2009, 01:56 PM
I know I could implement this with event-driven programming (a la widget_zoom_test) but I am up against an application requirement which demands that I process everything through mymain(). That means I have to return the results through mymain's "output" argument. No one has yet shown me how to do this, even with common variables. As such, I have to abandon the event-driven programming method.

CURSOR seems at first to be a successful solution, but it leaves me very vulnerable to the user clicking the exit button. (MATLAB at least gives a soft error saying the window has been deleted ... why does WAVE have to hang?)

Here's my code (greatly simplified). See if you can make it work:



PRO MyPlotExposeCB, wid, index, nparams, reason, event, handle

COMMON myplot, var
PLOT, var

END

PRO mymain, input, output

COMMON myplot, var
var = input
w_top = WwInit('MyPlot', 'MyPlot', w_workarea, /FORM)
w_drawing = WwDrawing(w_workarea, windex, 'MyPlotExposeCB', $
[500, 500], /NOMETA, $
[500, 500], /NOSCROLL, AREA=w_darea, $
/LEFT, /RIGHT)
st = WwSetValue(w_top, /SHOW)
st = WtProcessEvent(/DRAIN)

CURSOR, x, y, /NORMAL, /DOWN

; Don't close the window before you click or WAVE will hang!

output = [x, y]

END

PRO script

mymain, findgen(100), output
PRINT, output
PRINT, max(output)

END

donb
04-09-2009, 08:54 AM
hcrisp,

I need to point out that user Brian?s post on 4/3/09, 10:23 AM, is wrong and reiterate that you cannot mix blocking PV-WAVE routines, CURSOR in this case, with event-driven PV-WAVE Widget applications.

In the most recent code you posted (4/8/09, 1:56 PM), PV-WAVE is not ?hanging? because CURSOR and PV-WAVE Widgets are in use at the same time. If you use CURSOR without PV-WAVE Widgets and the user closes the window control is returned to the WAVE> prompt or the application. This can be demonstrated with two simple commands at the PV-WAVE prompt:


WAVE> PLOT, INDGEN(10)
WAVE> CURSOR, x, y

Rather than clicking inside the plot window, click on the ?X? to close the plot window. CURSOR returns immediately to the WAVE> prompt. The values returned for x and y are bogus but that?s to be expected. The correct implementation for your application requires that you use PV-WAVE widget event handlers.

Reading though the thread I think UPVAR and ADDVAR accomplish your need to get the results of the cursor x/y location array to other levels. UPVAR accesses a variable that is not on the current program level and ADDVAR creates a variable on the $MAIN$ program level and binds a local variable to it. For details on the use of UPVAR and ADDVAR check the PV-WAVE Reference Manual.

We do not usually provide extensive code debugging or training as part of our Technical Support service. We are more than happy to help users with advice in using PV-WAVE commands and make suggestions on where to look for problems, but not to the point of consultation. We feel this thread has passed this boundary and we will not set precedence by continuing to develop and debug your code. I did extend an offer for you to call me directly to discuss this particular issue outside of the forum.

Just as a reminder to all, we provide this free Forum as an exchange of ideas, and a way to educate users in the use of our products. We will not debug code presented herein. If our expertise in this area is requested we will be more than happy to gather your requirements and provide consultation or training as needed.

I hope the above advice helps you and please let us know how it turns out.

Don B.

hcrisp
04-09-2009, 12:48 PM
I have solved the problem with some off-line help from laksh99. The key was to use WwGenericDialog inside of WwInit. WwInit does not have a NoSystemMenu keyword, but WwDrawing does. (Doesn't anyone on the VNI Technical Support service have extensive knowledge of Ww widgets? A simple suggestion to use WwGenericDialog would have solved my problems a lot sooner.) Note that this does not use event handling nor UPVAR. And it does ensure that good values will be passed back through output. Here is the solution:



FUNCTION MyPlotCloseCB, wid, which

COMMON myplot, var, cursorflag
IF NOT KEYWORD_SET(cursorflag) THEN BEGIN
PRINT, 'Please click on the plot, before closing.'
RETURN, 1
ENDIF ELSE BEGIN
RETURN, 0
ENDELSE

END

PRO MyPlotExposeCB, wid, index, nparams, reason, event, handle

COMMON myplot, var, cursorflag
PLOT, var

END

PRO mymain, input, output

COMMON myplot, var, cursorflag
var = input

topshell = WwInit('MyPlot', 'MyPlot', workarea)
w_top = WwGenericDialog(topshell, w_workarea, ['Close'], $
'MyPlotCloseCB', /NONBLOCK, DISMISS=[1], $
TITLE='MyPlot', /FORM, LAYOUT_NAME='MyPlot', $
BUTTONS=bts, /NoSystemMenu)
w_drawing = WwDrawing(w_workarea, windex, 'MyPlotExposeCB', $
[500, 500], /NOMETA, $
[500, 500], /NOSCROLL, AREA=w_darea, $
/LEFT, /RIGHT)
st = WwSetValue(w_top, /SHOW)
st = WtProcessEvent(/DRAIN)

cursorflag = 0
CURSOR, x, y, /NORMAL, /DOWN
cursorflag = 1

; Now window won't close until you click the plot!

output = [x, y]
PRINT, output
PRINT, max(output)

END

donb
04-09-2009, 02:39 PM
Glad to hear you have something that seems to work in your application hcrisp. Does laksh99 happen to be a co-worker of yours?

I am still concerned with your solution, and with the code you posted. When I run this example it generates multiple error messages to the PV-WAVE window.

We really want to arrive at the best solution for this thread, and once again ask that you call me. A back and forth dialog is a much better approach in this situation.

My phone number is below and await your call,

Don B.

hcrisp
04-09-2009, 05:11 PM
My apologies for sending incomplete code. There were apparently a few other calls in my environment which you need to get it to reproduce. Again, thanks to Laksh99 (we've been known to work together on occasion) for his sheer genius in getting this to work.



; ... code from above

PRO launchcb, wid, which
IF which EQ 2 THEN BEGIN
topshell = WwGetValue (wid, /Userdata)
status = WwSetValue(topshell, /CLOSE)
RETURN
ENDIF
mymain, randomu(s, 200), output
END

PRO launch
topshell = WwInit('MyPlot', 'MyPlot', workarea)
buttonbox=WwButtonBox(workarea, ['Tag...', 'Quit'], $
'LaunchCb', buttons=buttons)
FOR i=0, N_ELEMENTS(buttons)-1 DO BEGIN
status = WwSetValue (buttons(i), Userdata = topshell)
ENDFOR
status = WwSetValue(topshell, /display)
WwLoop
END


Also, I found this relevant statement in the WwGenericDialog Help Example. You'll note that is what I did in MyPlotCloseCB.


Button callback for the dialog created with WwGenericDialog: This function always returns 0. If a 1 was returned instead of 0, the dialog would not be destroyed even if the Dismiss flag was set for the button pressed. This allows the callback to pop up an alert and avoid destroying the dialog if an error occurred.