Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Problem with calling function

Status
Not open for further replies.

OldtTimerDon

Programmer
Oct 6, 2012
34
US
Imagine a personnel file containing fields empno, fname, m_init, lnane, addr, st, city, zip, etc. A related file (Recognition.dbf) contains empno, award_code, license_code, and year achieved. The codes for licenses and awards are 3 characters and are translated in the Awards.dbf file
When the user seeks an employee, either by using find, next, or previous, my "Do ParseIt routine with empno" queries the Awards table for only that employee's data and expands the awards and license codes into an array. That array is then processed for conditions and based on those conditions accumulated to a variable myText which is then returned.

I have tested the ParseIt routine separately and it does exactly what is desired. My problem concerns where the "Do ParseIt routine with empno" should be on the employee form. As aa test, I added it to the click event of the Next button. This failed with the message "no update tables are specified. Use the table property of the cursor."

Note: My employee form has an editbox with a control source of "Thisform.myText" which is a property of the employee form.

I don't understand the error message but I am aware that error messages don't always relate to the problem. To restate the problem, as a user changes records, myText should show what awards and licenses have been achieved. (Some employees will have none.)

VFP is good brain cell medication!

Don Lawrence
Sun City, AZ, USA
 
Don,

The error message refers to a missing property in a local view. Are you using local views in your code? If so, how do they fit in the picture.

It would also help if you can show us the line of code that generated the error.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike,
I am not using a local view. The editbox has a control source of "thisform.myText." The editbox.readonly property is set to true. The click event of cmdNext of the buttonset contains only "Do ParseIt with empno" and "Refresh()". Whether Do ParseIt comes before or after the refresh causes the same error with the message pointing at refresh().

Incidentally, I also tried placing the "Do Parseit" on the GotFocus event of the buttonset. When the Next command was executed, the error still referred to the cmdNext.click refresh.
After eliminating "Do ParseIt" entirely, the form runs fine and steps through one record after another. Eliminating Refresh from cmdNext prevents the screen from re-drawing.

So, the problem still is how to change records and display "thisform.myText." Is the query in the Parseit function (which stores the result in an array and is then processed to return myText) creating the problem?

I don't have much hair left on my head and this problem will probably cause more to disappear...



 
Don,

If you look up the Help for that error message, you will see this:

No base tables are specified in the Tables property of the view.

Set the Tables property to specify the base table or tables that should be updated from the view. For most views you can use the View Designer to set the view update properties and then save the view back into the database. For more information, see CURSORGETPROP( ) Function.

Note my emphasis on the words "of the view". This is why I said that the message relates to local views. Yet you say you are not using local views. So either the message has some other meaning that I'm not aware of, or something has gone very wrong somewhere in your form.

Clearly the error is in the Parseit() function, but without examining the code line by line, it's hard to say what's causing it.

One point springs to mind. You are passing Empno as a parameter to Parseit(). Empno is is field in the Recognition table. However, it's not possible to know for sure if Recognition is the selected table when your Click event is executed. If a different table was selected, your code would throw an error, albeit with a different message.

I suggest you change the call to this:

Code:
DO ParseIt WITH [b]Recognition.[/b]Empno

Even if that doesn't solve the immediate problem, it is a good habit to get into, and will probably help prevent more problems further down the line.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
no update tables are specified. Use the table property of the cursor"

Points to usage of TABLEUPDATE() on a cursor, which is missing a backlink to the database, you set via CursorSetProp("Tables",...) which defines the remote database table name(s) correspnding to (a) cursor name(s) on the VFP side, there are more properties to set on a cursor before you do a TABLEUPDATE().

My assumption would be have a different currently selected workarea and do the tableupdate on a cursor resulting from a SQL query, which is not what you want.

I'd not go into search of TABLEUPDATE() to see if I have a point here, simply do one thing: Put a breakpoint in the click event that finally causes that error and step through the code line by line until you get the error. That should give you more than enough hints to see what goes wrong. A difficulty in this case is you say it happens in a refresh. That's one of the methods working different than normal, if you introduce debugging, as the debugger moves the focus from a currently running form to itself. It's worth a try anyway, nothing but the debugger will best reaveal to you in what line the error happens and what has been done before.

Bye, Olaf.
 
Hi Don.

I think the problem is that you're using Refresh() rather than Thisform.Refresh(). Refresh() is a function that refreshes the content of a view while Thisform.Refresh() updates the Value property of controls on the form.

Doug
 
Doug,
It is a pleasure to have you attempt to assist me. Normally, Mike Lewis and Olaf Doschke are the experts who resolve the dumbness of this seniot who has been out of contact with Foxpro for too many years.

The code that wouldn't work in the Click Event of the cmdNext of buttonset was "Do Parseit with Employee.empno" followed by Refresh(). The error message pointed to Refresh() as the offending line.

I tried replacing Refresh() with "Thisform.Refresh()" and absolutely nothing happened. I deleted the "Do Parseit" and again nothing happened when I ran the form. Finally, I restored the click event using the option of restoring its inherited value. Running the form again, I able to skip thru the records and display them normally.

Problem is I am still not doing my Parseit() function and displaying the return of it's message in an editbox whose control source is THISFORM.mytext.

My library that used to have books that I could investigate for inheritance no longer exists. Should my click event have code like this ?
THISFORM.cmdNext:: && Isn't this the way to say in addition to inherited code, do the following?
DO ParseIt with employee.empno
THISFORM.REFRESH()

Cordially,
Don Lawrence
Sun City, AZ



 
I suggest you get familiar with the command SET STEP ON and using the resultant TRACE window to examine code execution on a line-by-line basis.

Code:
DO ParseIt with employee.empno
SET STEP ON
THISFORM.REFRESH()

The SET STEP ON command (it only executes in Development mode, not compiled) will Suspend code execution and force the TRACE Window to open exposing your code for possible line-by-line execution tracing.

In this instance, after the Suspend, you can also manually go to the COMMAND Window and enter:
THISFORM. (<-- don't forget the last period)
and VFP's Intellisense will show you what is available.

In this instance it will show you that THISFORM.REFRESH is how the syntax needs to appear.

Good Luck,
JRB-Bldr

 
My library that used to have books that I could investigate for inheritance no longer exists. Should my click event have code like this ?
THISFORM.cmdNext:: && Isn't this the way to say in addition to inherited code, do the following?

Don,

That syntax will still work, but the more usual way is to call DODEFAULT(). In brief, it works like this:

- If a method is completely empty, then the corresponding method of the parent class is called (if any).

- If the method contains any code at all (even a comment or white space), then that code is executed, and the corresponding method of the parent class is NOT executed.

- But if the method contains DODEFAULT(), that function will call the parent method, in addition to executing the current method's own code.

So, is this relevant to your problem? Did you put the call to ParseIt and the Refresh in a higher-level class? If not, then none of it is relevant. But if that code is at a higher level, and if you now have code at the lower-level, then that would explain why the higher-level code appears not to do anything. In which case, the solution is to add DODEFAULT() at the lower level.

I hope this makes sense.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Could you check, if ParseIt changes the currently selected workarea?

Code:
MessageBox(ALIAS())
DO ParseIt with employee.empno
MessageBox(ALIAS())

Thisform.Refresh() can cause very much code to run, not only depending on what is in the that form method. A refresh causes a cascade of refresh calls on all form controls.

Bye, Olaf.
 
Mike, Substituting DODEFAULT() for REFRESH() caused the "DO ParseIt with employee.empno" to execute the function.
Olaf: Adding "MessageBox(ALIAS())" before and after Do ParseIt provided confirmed that I was in the employee table before and after.

I easily stepped through the first few records which had no data. When I got to a record which had data in the awards.dbf, the Do ParseIt routine was halted on the line
"MYVAR=ALEN(MYARRAY)" declaring that "Variable 'MYARRAY' is not found.

What's wrong with my code?

BTW, I have added the "Do ParseIt" call to the the click event of the cmdNext button. Wher should I actually have the call since I want it executed for any movement in the record pointer.

This old dog is having difficulty learning new tricks... (But I am stubborn.)

***************************
FUNCTION ParseIt
***************************
LPARAMETERS myx

MYWORD=""
SELECT a.gid_code,b.tbllists,a.myoutput, a.MYVAL from awards a, recognition b WHERE a.g_lib_id=myx AND a.gid_code=b.gid_code ORDER BY 1 INTO ARRAY(MYARRAY)
MYVAR=ALEN(MYARRAY)

IF MYVAR>0
FOR I=1 TO MYVAR STEP 4
XY=0
MYWORD=MYWORD+MYARRAY(I+1)+CHR(10)
IF MYARRAY(I+3)>0
XY=STR(MYARRAY(I+3),3)
MYWORD=ALLTRIM(MYWORD)+": "+XY+CHR(10)
ELSE
MYWORD=ALLTRIM(MYWORD)+": "+MYARRAY(I+2)+CHR(10)
ENDIF
NEXT I
ENDIF
RETURN Myword
ENDFUNC
 
INTO ARRAY(MYARRAY) > INTO ARRAY MYARRAY

Most important this still doesn't explain the error you get when you can Refresh() or Dodefault().

Aside of the wrong syntax with INTO ARRAY arrayname, you also don't get an array with no record matching the query. So you have to test, if an array is generated at all, and you can't use ALEN() for that, as it depends on a generated array. You could do that in several other ways, using TYPE(), VARTYPE(), but in this case I would simply check, whether the sql select has results, via _TALLY. _TALLY has to be >0.

Code:
***************************
Function ParseIt
***************************
   Lparameters myx

   MYWORD=""
   Select a.gid_code,b.tbllists,a.myoutput, a.MYVAL From awards a, recognition b Where a.g_lib_id=myx And a.gid_code=b.gid_code Order By 1 Into Array MYARRAY

   If _Tally>0
      For I=1 To Alen(MYARRAY,2) && Alen(array,2)=number of array rows, query results in 2d array
         MYWORD=MYWORD+MYARRAY(I,2)+Chr(10)
         If MYARRAY(I,4)>0
            MYWORD=Alltrim(MYWORD)+": "+Transform(MYARRAY(I,4))+Chr(10)
         Else
            MYWORD=Alltrim(MYWORD)+": "+MYARRAY(I,3)+Chr(10)
         Endif
      Next I
   Endif
   Return MYWORD
Endfunc

I'd use a cursor, making it much easier to process each result record.
Code:
***************************
Function ParseIt
***************************
Lparameters myx

   Local MyWord
   MyWord=""

   Select a.gid_code,b.tbllists,a.MyOutput, a.MyVal From awards a, recognition b Where a.g_lib_id=myx And a.gid_code=b.gid_code Order By 1 Into Cursor curParse

   Scan
      MyWord=MyWord+curParse.tbllists+Chr(10)
      If curParse.MyVal>0
         MyWord=Alltrim(MyWord)+": "+Transform(curParse.MyVal)+Chr(10)
      Else
         MyWord=Alltrim(MyWord)+": "+curParse.MyOutput+Chr(10)
      Endif
   Endscan

   Return MyWord
Endfunc

No need to check for existance of curParse, this will even be generated empty, if there is no result record. It's eaasier to see which fields you add to MyWord when using a cursor with field names, than when using an array with an element index, isn't it?

This time you WILL have a new alias, after ParseIt has run, because you will have the curParse Cursor. Simply remember the currently selected workarea before a call to ParseIt and reset it afterwards.

Bye, Olaf.
 
Adding "MessageBox(ALIAS())" before and after Do ParseIt provided confirmed that I was in the employee table before and after.

An even better approach would have been to have put the command SET STEP ON before and after Do ParseIt so that you not only could have identified the Selected Alias, but you would have Suspended code execution and could have examined, not just the current Alias(), but instead a whole host of parameters - either within the WATCH Window or in the Data Session Window (VFP Menu - Window - Data Session)

Also in addition to Suspending code execution, it would have allowed you to Single Step (using the TRACE Window menu icons) through your code to watch line-by-line execution and, with the increased visibility, more easily discover where the problem(s) exist.

Good Luck,
JRB-Bldr

 
To jrbbldr: Appreciate you effort but I am having problems trying to figure out how to use SET STEP ON.

To Olafdosche: I have incorporated your func(ParseIT) routine and it executes fine. However, the RETURN of Myword is not being displayed on the employee form which has an editbox whose control source is THISFORM.Myword. When I skip through records, nothing is ever displayed in the editbox. I did trap a particular employee when processed in func(Parseit) and saved the memory variables to a file which verified that the data was processed and stored in Myword correctly.

I am frustrated at not being able to retrieve the output!!!

Also, I believe that the Func(ParseIt) call does not belong in the click event of cmdNext. What I am trying to achieve is that when a user is looking at a particular employee's record, the editbox will display the licenses and awards pertaining to that employee (the contents of Myword). Any effort to change employees (top, previous, next, find) should cause the variable Myword to be displayed in the editbox. Perhaps, for someone who has no licenses or awards, Myword should appear as "N/A".

Is there no solution to my problem?

Don Lawrence
Sun City, AZ
 
Well, your last problem is easy to solve. Your return value is lost, as you call the parseit function without storing it's return value to a variable or property. THISFORM.MyWord is never used, neither in the function, nor in the call. MyWord is a variable in the function, Thisform.MyWord is not a variable but a property of the form. Properties are not variables.

In thread184-1712917 I told you, you should do something like Thisform.Edit1.Value = Translate(Oscar)

So to retrieve a return value you have to call a procedure or function in either of these ways:

1. VARIABLE = function(parameters)
2. do procedure with parameter TO VARIABLE

Bye, Olaf.
 
...and instead of a variable you can also use THISFORM.MyWord, just to state the perhaps not so obvious to you...

Bye, Olaf.
 
I am having problems trying to figure out how to use SET STEP ON.

Everyone has their own approach to debugging code, but for me the SET STEP ON approach has been the most effective for MANY years now.

By inserting a SET STEP ON line of code immediately before the code you want to examine, you will cause the code execution (in Development mode only) to Suspend and it will automatically open the TRACE WINDOW.

Then from either the TRACE Window, the WATCH Window, the DATA SESSION Window or the COMMAND Window you can examine a wide variety of things - variable contents, tables (alias) open and Selected, Object options & values, etc.

And from the examination of the parameter values that they expose MANY (Most ??) code execution challenges can be identified and resolved.

I'm a strong advocate of "Give an individual a Fish (or whatever) and their immediate issue can be resolved. But Teach an individual to Fish (equally whatever) and they can solve their own problems into the future."

So - What is it about using SET STEP ON that has been challenging?

Good Luck,
JRB-Bldr
 
Everyone has their own approach to debugging code, but for me the SET STEP ON approach has been the most effective for MANY years now.

JRB, many times over the years, you (and others) have recommended the use of SET STEP ON. I've always wondered why you prefer doing that, when it is so much simpler to set a breakpoint.

Does SET STEP ON have any advantage that a simple breakpoint doesn't? As far as I know, the end result is always the same. The benefit of a breakpoint is that it is not part of your code, and so won't be present when you distribute your application; and in any case you can remove breakpoints in bulk with a single click.

I'm only asking out of curiosity. I'm not trying to make a point.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike - I've always wondered why you prefer doing that, when it is so much simpler to set a breakpoint.

The only time a Breakpoint works is when the TRACE Window is already open and ready to Suspend code execution.
And running development code with the TRACE Window constantly open seems to result in slower execution.

But a SET STEP ON does not depend on the TRACE Window being already Open since it will open it all by its own execution.

Since I often forget to ACTIVATE WINDOW TRACE prior to running my code being tested or I have had it opened and then later closed it so that the development code will execute faster, that is why I many times prefer the SET STEP ON over a Breakpoint.

Yes, if you forget to remove or comment out the SET STEP ON commands, yes they will be distributed in the code, but they will only execute when in the Development Mode - otherwise they will not execute.

I certainly use both Breakpoints and SET STEP ON for debugging, but many times I have found myself waiting and waiting and waiting for a Breakpoint to Suspend - only to find that I have forgotten to open (or leave open) the TRACE Window (Oops!! - try again).

Don - Good Luck,
JRB-Bldr


 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top