How to catch user interface event trigger error - events

How to catch error from ser interface event trigger.
For example, in the code below, f6 is updating the data in the ttnom temporary table. In the fill_tt procedure, if the table is empty, PROGRESS.lang.apperror('Temp-table is empty') is thrown.
Is it possible to return it from the F6 event to the main block of the program so that it is caught there by the catch block, and not by the catch inside the event.
BLOCK-LEVEL ON ERROR UNDO, THROW.
DEFINE VARIABLE inomtr AS i NO-UNDO.
DEFINE VARIABLE lexit AS L NO-UNDO.
DEFINE TEMP-TABLE ttnom NO-UNDO
FIELD uid AS int64
INDEX idx1 IS unique uid.
DEFINE FRAME fupd
SKIP(1)
inomtr FORMAT '>>>>>>>>>>>9' NO-LABEL
SKIP(1)
WITH VIEW-AS DIALOG-BOX SIDE-LABELS.
ON GO OF FRAME fupd
DO:
_t1:
DO ON ERROR UNDO, RETURN NO-APPLY:
ASSIGN inomtr.
IF CAN-FIND (FIRST ttnom NO-LOCK WHERE ttnom.uid = inomtr) THEN
LEAVE _t1.
MESSAGE 'inomtr not found' VIEW-AS ALERT-BOX.
RETURN NO-APPLY.
END.
END.
ON F3 OF inomtr IN FRAME fupd
DO:
RUN choose_nom(OUTPUT inomtr).
END.
ON F6 of FRAME fupd ANYWHERE
DO:
DO ON ERROR UNDO, THROW :
RUN fill_tt.
CATCH ERROR AS PROGRESS.lang.error:
message error:GetMessage(1) + return-value VIEW-AS ALERT-BOX.
lexit = YES.
STOP.
END CATCH.
END.
RETURN NO-APPLY.
END.
_tr:
DO ON ERROR UNDO, THROW
ON STOP UNDO, LEAVE
ON ENDKEY UNDO, LEAVE:
RUN fill_tt.
DO WHILE TRUE ON ERROR UNDO, throw
ON STOP UNDO , RETRY
ON ENDKEY UNDO , RETRY :
IF RETRY OR lexit = yes THEN
DO:
IF lexit = NO THEN
MESSAGE 'Exit?' VIEW-AS ALERT-BOX BUTTONS YES-NO UPDATE lexit.
IF lexit = YES THEN
LEAVE _tr.
END.
DISPLAY WITH FRAME fupd.
ENABLE inomtr WITH FRAME fupd.
WAIT-FOR GO OF FRAME fupd.
RUN do_nomtr(inomtr).
END.
CATCH ERROR AS PROGRESS.lang.error:
message ERROR:GetMessage(1) + RETURN-VALUE VIEW-AS ALERT-BOX.
END CATCH.
END.
PROCEDURE do_nomtr:
DEFINE INPUT PARAM inomtr AS i NO-UNDO.
MESSAGE inomtr VIEW-AS ALERT-BOX.
END PROCEDURE.
PROCEDURE fill_tt:
DO ON ERROR UNDO, THROW:
DISPLAY 'Loading temp-table' WITH FRAME ftt VIEW-AS DIALOG-BOX.
EMPTY TEMP-TABLE ttnom.
FOR EACH link NO-LOCK WHERE link.type = 3 AND link.class-code = '1B' AND
link.file-name = 'r-goods' AND link.arch = no ON ERROR UNDO, THROW:
IF CAN-FIND(FIRST ttnom WHERE ttnom.uid = link.link-num) THEN
NEXT.
CREATE ttnom.
ASSIGN ttnom.uid = link.link-num.
END.
HIDE FRAME ftt NO-PAUSE.
IF NOT TEMP-TABLE ttnom:HAS-RECORDS THEN
UNDO, THROW NEW PROGRESS.lang.apperror('Temp-table is empty').
END.
END PROCEDURE.

The main-block cannot catch an error from a trigger. You should have a catch block in every trigger-block.
If you want to go beyond the wait-for (in the main-block) you need to apply the GO event.

Related

visual Foxpro releasing form without executing remaining code

I have an old application built in Visual Foxpro 9. I want to terminate the form without executing remaining code. Form.Release event release the form after executing remaining code in event it called from. I want to use this when any error occurred, can anyone please help?
I may have other forms open and dont want to close other forms in event of error on one form. Cancel would close all open forms, so I cant use it.
Main.Prg
On Error Do ErrHandle With Error( ), Message( ), Program( ), Lineno( )
Do Form form1
Do Form form2
Do form formN
Read Events
Procedure ErrHandle(tnerror, tcMessage, tcProg, tnLineNo)
Local lcError
TEXT to m.lcError textmerge noshow
Error No: << m.tnerror >>. Message: << m.tcMessage >>
Source: << m.tcProg >> at line << m.tnLineNo >>
ENDTEXT
Messagebox(m.lcError, 0+4096, "Error", 5000)
Local loForm
loForm = _vfp.ActiveForm
If !Isnull(m.loForm)
m.loForm.Release()
Endif
Endproc
This would just release the active form.
Main.prg
Do Form form1
Do Form form2
Do form formN
Read Events
Errhandle.prg
lparameters tnerror, tcMessage, tcProg, tnLineNo
Local lcError
TEXT to m.lcError textmerge noshow
Error No: << m.tnerror >>. Message: << m.tcMessage >>
Source: << m.tcProg >> at line << m.tnLineNo >>
ENDTEXT
Messagebox(m.lcError, 0+4096, "Error", 5000)
Form method where error is expected:
Local loForm
loForm = thisform
On Error ;
return ErrHandle(Error( ), Message( ), Program( ), Lineno( )) ;
and m.loForm.Release()
local lc
lc = "Hello"
If m.lc = 1 && explicit error
EndIf
_screen.Caption = "This code wouldn't run"
On Error
Note: This is sort of using try ... catch blocks. I personally don't use try ... catch because unlike on error it doesn't catch all errors.
On the Form's Error event place the following code:
LPARAMETERS nError, cMethod, nLine
*Handle the error/ clean up/ MessageBox / Log error to file or table / etc.
thisform.release
RETURN TO MASTER
** Hope this helps

Writing text into entry field in tcl/tk dialog

In a tcl/tk dialog I need to get a text input from the user.
proc add_entry { command } {
global TestValue
entry .dialog_TC.enText -textvariable TestValue
grid .dialog_TC.enText -row 1 -column 1 -columnspan 2 -pady 1 -padx 1
}
The problem:
Whenever the user writes a single letter, into the entry-field, the dialog is closed immediately.
I'm guessing that you've got a trace elsewhere on the TestValue variable (possibly due to vwait or tkwait variable) that is detecting the change to the variable and destroying the widget when that happens, possibly by killing the whole dialog. You don't include the code, but it's probably something like:
proc make-me-a-dialog {} {
toplevel .dialog_TC
# ...
add_entry { something ... }
# ...
vwait TestValue
destroy .dialog_TC
return $TestValue
}
That's a guess, and probably greatly simplified too. But if that's the case, the first event to change the value in the variable (i.e., most key-presses in the entry) will cause the vwait to stop waiting and trigger the cascade of destruction.
You need to stop waiting on the contents of the entry. You don't want to trigger every time something is altered in it, but rather only when the user says “I'm done and want to make my changes, OK” or “I'm done and don't want to make my changes, Cancel”. Or, depending on interaction style, “I'm done; my changes are already live. Close this window”. With plenty of experience, the events that you are actually needing to listen for are the closing of the window, the press of Return and the press of Escape.
Let's fix.
proc make-me-a-dialog {}
global waiting
toplevel .dialog_TC
# ...
add_entry { something ... }
# ...
set dlg .dialog_TC
bind $dlg <Return> [list set waiting($dlg) 1]
bind $dlg <Escape> [list set waiting($dlg) 0]
# Trapping a window manager message; slightly different to normal events for historical reasons
wm protocol $dlg WM_DELETE_WINDOW [list set waiting($dlg) 0]
vwait waiting($dlg)
if {waiting($dlg)} {
return $ValueIndicatingOK
} else {
return $ValueIndicatingCancel
}
}
Ok, I did not think about my shortkeys, that I have also in that script. Whenever one of those letters is written into the entry field the window gets closed. I have to combine keys like ...
bind . <Control-Key-a> \
{ tk_messageBox -message "You pressed Control+A" } ;#Control+a

Spss (mrScriptBasic) error handling obtain code where error occured

Hello I am currently using IBM Spss, and I was wondering if there is a way when the code throws an exception to be able to obtain the line of code where the exception occurred? I am able to obtain the line number is there a way to access the stack of the lines that were executed and pull this information from there? Any ideas would be great Thank you in advance!
For Example:
Dim x, y, z
On Error Goto ErrorHandler
x = 30
y = 0
z = x / y 'would like to grab this code since this is where the error occurred
Exit
ErrorHandler:
debug.Log(Err.Description + _
" error occurred on line number " + _
CText(Err.LineNumber))
No, there is no way to map the line number to the actual code unless you keep your own index, allowing for macro expansion etc.
What you can do, though, is to submit the code in blocks and catch the exception for that block, which narrows down the location. In the limit, if each line is its own block, you will know exactly where the error occurred.

Not able to read property at runtime

When I tried to read the "Left property" of a control, it is giving me the error,
"Left cannot be read at run time"
Here is my code,
for each ctrl in me.controls
if ctrl.left > 2490 then
'app logic
end if
next
what is wrong in this code. It worked without error in another computer.
Can anyone tell me what is wrong
You may have a design-time only placeable control on your form like a timer for example, that does not have a run-time left property. You could check the type of control to ensure only TextBox, Label, Button, etc. get checked, or just use an on error resume next:
Check for object type using TypeOf:
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeOf ctrl Is Timer Then
Else
If ctrl.Left > 2490 Then
'app logic
End If
End If
Next
Check for object type using TypeName:
Dim ctrl As Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "Timer" Then
Else
If ctrl.Left > 2490 Then
'app logic
End If
End If
Next
Using On Error Resume Next:
Dim ctrl As Control
On Error Resume Next
for each ctrl in me.controls
if ctrl.left > 2490 then
'app logic
end if
Next
If you use the last method, it's important to handle errors inline reraising any unexpected error. Otherwise if you get any different error than the one you're expecting you could have a very tough time finding it. So:
Dim ctrl As Control
On Error Resume Next
for each ctrl in me.controls
if ctrl.left > 2490 then
Select Case Err.Number
Case 0 'No Error, ignore
Case 393 'The error you want to ignore
Err.Clear 'Reset for next iteration
Case Else
On Error Goto 0
Err.Raise Err.Number 'Reraise any unexpected errors
End Select
'app logic
end if
Next
On Error Goto 0

Pebble JS app_message_outbox_send() in a for loop

I am using sdk2 for pebble, with the js appmessage features:
I am trying to send consecutive messages to pebble js on the phone, for each one of my menu items. A variable movie_count = 5 exists, and I use this for looping, It gets logged out as 5 as the code shows below, so it should be getting to all 5 , logging errors at least, but it just doesn't log anything after the first time:
static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
int i;
APP_LOG(APP_LOG_LEVEL_DEBUG, "movie_count int %u", movie_count);
for(i = 0;i<movie_count;i++){
Tuplet build_menu_tuple = TupletInteger(BUILD_MENU_KEY, 1); // just a flag for the appmessage js code
Tuplet menu_id_tuple = TupletInteger(MENU_ID_KEY, i);
DictionaryIterator *iter;
app_message_outbox_begin(&iter);
if (iter == NULL) {
return;
}
dict_write_tuplet(iter, &build_menu_tuple);
dict_write_tuplet(iter, &menu_id_tuple);
dict_write_end(iter);
app_message_outbox_send();
}
}
in JS appmessage
this code in the js is executed, although only once, I have logged the output in my in_receiver() callback in my watchapp, and my first item gets logged, but the logger just quits after that... Is this because the watchapp cannnot send blutooth messages in a loop like this? Is there a way to make sure the message is sent, or pause the execution so it sends at a slower pace? (the movies_json exists above the code below, i left it out for brevity, but it is there, a json object with an inner array of movies)
if(e.payload.build_menu){
var menu_id = e.payload.menu_id;
console.log("menu_id" + menu_id);
Pebble.sendAppMessage({"title":movies_json.movies[menu_id].title,
"stars":movies_json.movies[menu_id].stars,
"menu_id":menu_id
});
console.log("movie title:" + movies_json.movies[i].title);
}
in_recived_handler callback code to handle messages from the js
this code is in the callback that takes in messsages from the phones js... it only gets to the first item, logs just the first item's menu_id and title, and then logging just stops.
if(menu_id_tuple){
int menu_id;
menu_id = menu_id_tuple->value->int32;
char movie_title[30];
strncpy(movie_title, movie_title_tuple->value->cstring, 30);
APP_LOG(APP_LOG_LEVEL_DEBUG, "In received handler movie_title: %s" , movie_title);
APP_LOG(APP_LOG_LEVEL_DEBUG, "In received handler menu_id: %u" , menu_id);
}
You need to wait until the first message is sent to send the next one.
The proper way to do this is to register a callback for the outbox_sent event and to queue the next message in this event.
Explanation
There is only one bluetooth buffer on pebble and it can only hold one message at a time. If you send messages in a for loop, this buffer gets filled with the first message and all the other messages are rejected.
You would see the error messages if you checked the return value of app_message_outbox_send(). You should also implement a AppMessageOutboxFailed handler.

Resources