Page 1 of 1
passing a value through a for..next loop
Posted: Thu May 09, 2024 8:25 pm
by Silvio.Falconi
If I have a loop For ..Next
and on this cicle I have create checkboxes ...and on change I call a function passing the value of n of for..next loop
but the n is allway 3
Code: Select all | Expand
For n= 1 to len(aFilters)
@ nRow, nCol CHECKBOX aCheck[n] VAR aFilters[n][1] ;
PROMPT aFilters[n][2] OF oBox[1] SIZE 150,15 PIXEL;
ON CHANGE Filtrare( n, oBrw, oDbf,oBox )
aCheck[n]:lTransparent:=.t.
ncol+=160
if n=3
exit
endif
Next
the problem is n is allways 3 , so how I can make to pass the n different for each checkbox ?
Re: passing a value through a for..next loop
Posted: Fri May 10, 2024 2:56 am
by Antonio Linares
Creating controls in a loop from array is very tricky. It does not work as expected.
Let me show why:
Wrong code:
for n := 1 to 20
@ n * 20, 50 GET aVar[ n ] OF oWnd <clauses>
next n
After this loop the present value of " n " is 21.
When the dialog is activated and controls are to be shown, the value of "n" is 21 and naturally we get runtime error as subscript out of range.
Correct way:
for n := 1 to 20
aGet[ 1 ] := MakeGet( n * 20, 50, aVar, n, oWnd )
next n
ACTIVATE WINDOW ....
//--------------
static function MakeGet( nRow, nCol, aVar, n, oWnd )
local oget
@ nRow, nCol GET oGet VAR aVar[ n ] of oWnd <clauses>
// note this value of "n" remains the same till oGet goes out of scope
// the principle is called "detached locals"
return oGet
Re: passing a value through a for..next loop
Posted: Fri May 10, 2024 6:48 am
by Silvio.Falconi
Yes
But the valute Is the Number of object
On your test there was allways oget[1]
I have oget[n]
Re: passing a value through a for..next loop
Posted: Fri May 10, 2024 7:21 am
by paquitohm
Code: Select all | Expand
For n= 1 to len(aFilters)
b:= {|n|
@ nRow, nCol CHECKBOX aCheck[n] VAR aFilters[n][1] ;
PROMPT aFilters[n][2] OF oBox[1] SIZE 150,15 PIXEL;
ON CHANGE Filtrare( n, oBrw, oDbf,oBox )
}
Eval(b, n)
aCheck[n]:lTransparent:=.t.
ncol+=160
if n=3
exit
endif
Next
Re: passing a value through a for..next loop
Posted: Fri May 10, 2024 2:11 pm
by Silvio.Falconi
paquitohm wrote:Code: Select all | Expand
For n= 1 to len(aFilters)
b:= {|n|
@ nRow, nCol CHECKBOX aCheck[n] VAR aFilters[n][1] ;
PROMPT aFilters[n][2] OF oBox[1] SIZE 150,15 PIXEL;
ON CHANGE Filtrare( n, oBrw, oDbf,oBox )
}
Eval(b, n)
aCheck[n]:lTransparent:=.t.
ncol+=160
if n=3
exit
endif
Next
Compiling 'test.prg'...
test.prg(183) Error E0030 Syntax error "syntax error at '@'"
test.prg(188) Error E0018 EXIT statement with no loop in sight
test.prg(190) Error E0013 NEXT does not match FOR
test.prg(203) Error E0017 Unclosed control structure '{||...}*'
test.prg(203) Error E0017 Unclosed control structure 'FOR*'
test.prg(204) Error E0030 Syntax error "syntax error at '*'"
test.prg(208) Error E0030 Syntax error "syntax error at '*'"
test.prg(358) Error E0030 Syntax error "syntax error at '*'"
8 errors
Re: passing a value through a for..next loop
Posted: Sun May 12, 2024 1:12 pm
by nageswaragunupudi
Mr. paquitohm
Trying to define control this way inside a codeblock will result "variable out of reach" compilation error.
Re: passing a value through a for..next loop
Posted: Sun May 12, 2024 2:57 pm
by nageswaragunupudi
The problem of creating controls using loop variables in a loop is well known and was discussed many times in this forum.
The solution is to create each control in a separate function using the principle of detached locals.
Search for detached locals in this forum.
The sample provided by Mr. Antonio above is the right way to go.
This is a working program using the above principle
Build and run this program in fwh samples folder.
Code: Select all | Expand
#include "fivewin.ch"
REQUEST DBFCDX
function Main()
local oDlg, oBrw
local cFilter := ""
local aFilters := { { .f., "AGE>40" }, { .f., "STATE='NY'" }, { .f., "MARRIED" } }
local aCheck[ 3 ], n
local nRow := 40
local nCol := 40
USE CUSTOMER NEW SHARED VIA "DBFCDX"
DEFINE DIALOG oDlg SIZE 600,400 PIXEL TRUEPIXEL
for n := 1 to Len( aFilters )
aCheck[ n ] := CreateChk( nRow, nCol, oDlg, aFilters, n )
aCheck[ n ]:bChange := { || cFilter := ResetFilter( aFilters ), oDlg:Update() }
aCheck[ n ]:lTransparent := .t.
nCol += 150
if n == 3
EXIT
endif
next
@ 80, 40 SAY { || "FILTER : " + cFilter } SIZE 400, 25 PIXEL OF oDlg CENTER UPDATE
@ 115, 40 XBROWSE oBrw SIZE -40,-40 PIXEL OF oDlg ;
DATASOURCE ALIAS() ;
COLUMNS "ID", "CITY", "STATE", "MARRIED", "AGE" ;
CELL LINES NOBORDER UPDATE
oBrw:SetChecks()
oBrw:CreateFromCode()
ACTIVATE DIALOG oDlg CENTERED
return nil
static function CreateChk( nRow, nCol, oDlg, aFilters, nIndex )
local oChk
@ nRow, nCol CHECKBOX oChk VAR aFilters[ nIndex, 1 ] ;
PROMPT aFilters[ nIndex, 2 ] SIZE 150,15 PIXEL OF oDlg
return oChk
static function ResetFilter( aFilters )
local cFilter := ""
local af := {}
AEval( aFilters, { |a| If( a[ 1 ], AAdd( af, a[ 2 ] ), nil ) } )
cFilter := FW_ArrayAsList( af, " .AND. " )
if Empty( cFilter )
SET FILTER TO
else
SET FILTER TO &cFilter
endif
GO TOP
return cFilter
Re: passing a value through a for..next loop
Posted: Mon May 13, 2024 6:51 am
by paquitohm
nageswaragunupudi wrote:Mr. paquitohm
Trying to define control this way inside a codeblock will result "variable out of reach" compilation error.
Hi to all,
That's right, mr. nageswaragunupudi
IMHO something is wrong in harbor codeblocks because SI should function at least as a LOCAL variable.
The problem is in the double codeblock generated by the fivewin.ch preprocessor, but which harbour, as I commented above, does not know how to solve even though it is helped with the LOCAL
All this quite limits the use of extended codeblocks with commands
Code: Select all | Expand
For n= 1 to len(aFilters)
b:= {|nParam|
LOCAL n:= nParam
@ nRow, nCol CHECKBOX aCheck[n] VAR aFilters[n][1] ;
PROMPT aFilters[n][2] OF oBox[1] SIZE 150,15 PIXEL;
ON CHANGE Filtrare( n, oBrw, oDbf,oBox )
}
Eval(b, n)
aCheck[n]:lTransparent:=.t.
ncol+=160
if n=3
exit
endif
Next
Regards
Re: passing a value through a for..next loop
Posted: Mon May 13, 2024 7:06 am
by Marc Venken
This is a interesting sample for filtering....
Mr. Rao,
Would you please be so kind to enhance this sample so that there is a second combobox selecting ages like (20 - 40 - 50 etc)
and state like (NY, WY etc) Any small sample with more combo's will de ok. Please use the array's as data because I want to fill
the arrys with data from my xbrwowse dbf filter settings.
v Age v State
>20 NY
v>30 <WY
>40
How good will it look that when Age is selected the extra combo is show and when not, no combo.
Re: passing a value through a for..next loop
Posted: Mon May 13, 2024 7:18 am
by Silvio.Falconi
nageswaragunupudi wrote:The problem of creating controls using loop variables in a loop is well known and was discussed many times in this forum.
The solution is to create each control in a separate function using the principle of detached locals.
Search for detached locals in this forum.
The sample provided by Mr. Antonio above is the right way to go.
This is a working program using the above principle
Build and run this program in fwh samples folder.
Code: Select all | Expand
#include "fivewin.ch"
REQUEST DBFCDX
function Main()
local oDlg, oBrw
local cFilter := ""
local aFilters := { { .f., "AGE>40" }, { .f., "STATE='NY'" }, { .f., "MARRIED" } }
local aCheck[ 3 ], n
local nRow := 40
local nCol := 40
USE CUSTOMER NEW SHARED VIA "DBFCDX"
DEFINE DIALOG oDlg SIZE 600,400 PIXEL TRUEPIXEL
for n := 1 to Len( aFilters )
aCheck[ n ] := CreateChk( nRow, nCol, oDlg, aFilters, n )
aCheck[ n ]:bChange := { || cFilter := ResetFilter( aFilters ), oDlg:Update() }
aCheck[ n ]:lTransparent := .t.
nCol += 150
if n == 3
EXIT
endif
next
@ 80, 40 SAY { || "FILTER : " + cFilter } SIZE 400, 25 PIXEL OF oDlg CENTER UPDATE
@ 115, 40 XBROWSE oBrw SIZE -40,-40 PIXEL OF oDlg ;
DATASOURCE ALIAS() ;
COLUMNS "ID", "CITY", "STATE", "MARRIED", "AGE" ;
CELL LINES NOBORDER UPDATE
oBrw:SetChecks()
oBrw:CreateFromCode()
ACTIVATE DIALOG oDlg CENTERED
return nil
static function CreateChk( nRow, nCol, oDlg, aFilters, nIndex )
local oChk
@ nRow, nCol CHECKBOX oChk VAR aFilters[ nIndex, 1 ] ;
PROMPT aFilters[ nIndex, 2 ] SIZE 150,15 PIXEL OF oDlg
return oChk
static function ResetFilter( aFilters )
local cFilter := ""
local af := {}
AEval( aFilters, { |a| If( a[ 1 ], AAdd( af, a[ 2 ] ), nil ) } )
cFilter := FW_ArrayAsList( af, " .AND. " )
if Empty( cFilter )
SET FILTER TO
else
SET FILTER TO &cFilter
endif
GO TOP
return cFilter
Nages,
I use tdatabase and I have modify
if Empty( cFilter )
oDbf:Clearfilter()
else
odbf:dbsetfilter( cFilter)
endif
But Not work
then another question
you made
Code: Select all | Expand
local aFilters := { { .f., "AGE>40" }, { .f., "STATE='NY'" }, { .f., "MARRIED" } }
But I wish insert the possibily to select the states, the age amd if the end user want married or not married
how I must make ?
Re: passing a value through a for..next loop
Posted: Mon May 13, 2024 8:17 am
by nageswaragunupudi
Mr Silvio
The main question was about creating controls in for/next loop.
That is answered satisfactorily.
You can take care of the rest yourself
Re: passing a value through a for..next loop
Posted: Mon May 13, 2024 9:38 am
by Silvio.Falconi
nageswaragunupudi wrote:Mr Silvio
The main question was about creating controls in for/next loop.
That is answered satisfactorily.
You can take care of the rest yourself
Nages
the main question was not about creating controls in for/next loop
but to passa a value to bchange function
I tried to pass nIndex in the for next loop to bchange and the value is always the same
I made modifies on you sample
https://forums.fivetechsupport.com/view ... b6#p270092
Re: passing a value through a for..next loop
Posted: Mon May 13, 2024 6:54 pm
by nageswaragunupudi
I use tdatabase and I have modify
if Empty( cFilter )
oDbf:Clearfilter()
else
odbf:dbsetfilter( cFilter)
endif
But Not work
It should be simple to change and make it work.
I made these 6 minor changes and it works well.
Code: Select all | Expand
#include "fivewin.ch"
REQUEST DBFCDX
function Main()
local oDlg, oBrw
local cFilter := ""
local aFilters := { { .f., "AGE>40" }, { .f., "STATE='NY'" }, { .f., "MARRIED" } }
local aCheck[ 3 ], n
local nRow := 40
local nCol := 40
local oDbf // 1. Add this line
// USE CUSTOMER NEW SHARED VIA "DBFCDX"
oDbf := TDataBase():Open( nil, "CUSTOMER", "DBFCDX", .t. ) // 2. Replace
DEFINE DIALOG oDlg SIZE 600,400 PIXEL TRUEPIXEL
for n := 1 to Len( aFilters )
aCheck[ n ] := CreateChk( nRow, nCol, oDlg, aFilters, n )
aCheck[ n ]:bChange := { || cFilter := ResetFilter( aFilters, oDbf ), oDlg:Update() } // 3. add oDbf param
aCheck[ n ]:lTransparent := .t.
nCol += 150
if n == 3
EXIT
endif
next
@ 80, 40 SAY { || "FILTER : " + cFilter } SIZE 400, 25 PIXEL OF oDlg CENTER UPDATE
@ 115, 40 XBROWSE oBrw SIZE -40,-40 PIXEL OF oDlg ;
DATASOURCE oDbf; // ALIAS() ; // 4. change to oDbf
COLUMNS "ID", "CITY", "STATE", "MARRIED", "AGE" ;
CELL LINES NOBORDER UPDATE
oBrw:SetChecks()
oBrw:CreateFromCode()
ACTIVATE DIALOG oDlg CENTERED
return nil
static function CreateChk( nRow, nCol, oDlg, aFilters, nIndex )
local oChk
@ nRow, nCol CHECKBOX oChk VAR aFilters[ nIndex, 1 ] ;
PROMPT aFilters[ nIndex, 2 ] SIZE 150,15 PIXEL OF oDlg
return oChk
static function ResetFilter( aFilters, oDbf ) // 5. add oDbf
local cFilter := ""
local af := {}
AEval( aFilters, { |a| If( a[ 1 ], AAdd( af, a[ 2 ] ), nil ) } )
cFilter := FW_ArrayAsList( af, " .AND. " )
/*
if Empty( cFilter )
SET FILTER TO
else
SET FILTER TO &cFilter
endif
GO TOP
*/
oDbf:SetFilter( cFilter ) // 6. change here
oDbf:GoTop()
return cFilter
Re: passing a value through a for..next loop
Posted: Mon May 13, 2024 7:07 pm
by nageswaragunupudi
I tried to pass nIndex in the for next loop to bchange and the value is always the same
We need to keep in mind at all times, NOT to use loop variables in building code-blocks inside a loop.
Build the code-block in a separate function using the loop variable as one of the parameters of the function.
Please search for detached locals in this forum.
Keeping this principle in mind, I am sure you can yourself modify the program to use the index in bChange also.
For now, I am modifying the above program.
Code: Select all | Expand
#include "fivewin.ch"
REQUEST DBFCDX
function Main()
local oDlg, oBrw
local cFilter := ""
local aFilters := { { .f., "AGE>40" }, { .f., "STATE='NY'" }, { .f., "MARRIED" } }
local aCheck[ 3 ], n
local nRow := 40
local nCol := 40
local oDbf
oDbf := TDataBase():Open( nil, "CUSTOMER", "DBFCDX", .t. )
DEFINE DIALOG oDlg SIZE 600,400 PIXEL TRUEPIXEL
for n := 1 to Len( aFilters )
aCheck[ n ] := CreateChk( nRow, nCol, oDlg, aFilters, n, oDbf, @cFilter )
aCheck[ n ]:lTransparent := .t.
nCol += 150
if n == 3
EXIT
endif
next
@ 80, 40 SAY { || "FILTER : " + cFilter } SIZE 400, 25 PIXEL OF oDlg CENTER UPDATE
@ 115, 40 XBROWSE oBrw SIZE -40,-40 PIXEL OF oDlg ;
DATASOURCE oDbf;
COLUMNS "ID", "CITY", "STATE", "MARRIED", "AGE" ;
CELL LINES NOBORDER UPDATE
oBrw:SetChecks()
oBrw:CreateFromCode()
ACTIVATE DIALOG oDlg CENTERED
return nil
static function CreateChk( nRow, nCol, oDlg, aFilters, nIndex, oDbf, cFilter )
local oChk
@ nRow, nCol CHECKBOX oChk VAR aFilters[ nIndex, 1 ] ;
PROMPT aFilters[ nIndex, 2 ] SIZE 150,15 PIXEL OF oDlg ;
ON CHANGE ( cFilter := ResetFilter( aFilters, oDbf, nIndex ), oDlg:Update() )
return oChk
static function ResetFilter( aFilters, oDbf, nIndex )
local cFilter := ""
local af := {}
? "you do whatever you want with nIndex = ", nIndex
AEval( aFilters, { |a| If( a[ 1 ], AAdd( af, a[ 2 ] ), nil ) } )
cFilter := FW_ArrayAsList( af, " .AND. " )
oDbf:SetFilter( cFilter )
oDbf:GoTop()
return cFilter
Re: passing a value through a for..next loop
Posted: Tue May 14, 2024 7:13 am
by Silvio.Falconi
nageswaragunupudi wrote:I tried to pass nIndex in the for next loop to bchange and the value is always the same
We need to keep in mind at all times, NOT to use loop variables in building code-blocks inside a loop.
Build the code-block in a separate function using the loop variable as one of the parameters of the function.
Please search for detached locals in this forum.
Keeping this principle in mind, I am sure you can yourself modify the program to use the index in bChange also.
For now, I am modifying the above program.
Code: Select all | Expand
#include "fivewin.ch"
REQUEST DBFCDX
function Main()
local oDlg, oBrw
local cFilter := ""
local aFilters := { { .f., "AGE>40" }, { .f., "STATE='NY'" }, { .f., "MARRIED" } }
local aCheck[ 3 ], n
local nRow := 40
local nCol := 40
local oDbf
oDbf := TDataBase():Open( nil, "CUSTOMER", "DBFCDX", .t. )
DEFINE DIALOG oDlg SIZE 600,400 PIXEL TRUEPIXEL
for n := 1 to Len( aFilters )
aCheck[ n ] := CreateChk( nRow, nCol, oDlg, aFilters, n, oDbf, @cFilter )
aCheck[ n ]:lTransparent := .t.
nCol += 150
if n == 3
EXIT
endif
next
@ 80, 40 SAY { || "FILTER : " + cFilter } SIZE 400, 25 PIXEL OF oDlg CENTER UPDATE
@ 115, 40 XBROWSE oBrw SIZE -40,-40 PIXEL OF oDlg ;
DATASOURCE oDbf;
COLUMNS "ID", "CITY", "STATE", "MARRIED", "AGE" ;
CELL LINES NOBORDER UPDATE
oBrw:SetChecks()
oBrw:CreateFromCode()
ACTIVATE DIALOG oDlg CENTERED
return nil
static function CreateChk( nRow, nCol, oDlg, aFilters, nIndex, oDbf, cFilter )
local oChk
@ nRow, nCol CHECKBOX oChk VAR aFilters[ nIndex, 1 ] ;
PROMPT aFilters[ nIndex, 2 ] SIZE 150,15 PIXEL OF oDlg ;
ON CHANGE ( cFilter := ResetFilter( aFilters, oDbf, nIndex ), oDlg:Update() )
return oChk
static function ResetFilter( aFilters, oDbf, nIndex )
local cFilter := ""
local af := {}
? "you do whatever you want with nIndex = ", nIndex
AEval( aFilters, { |a| If( a[ 1 ], AAdd( af, a[ 2 ] ), nil ) } )
cFilter := FW_ArrayAsList( af, " .AND. " )
oDbf:SetFilter( cFilter )
oDbf:GoTop()
return cFilter
Thanks Nages , I means somethig of this
I insert only a selection ( states)
Code: Select all | Expand
static function ResetFilter( aFilters, oDbf, nIndex )
local cFilter := ""
local af := {}
local cState:= space(25)
local lMarried:=.f.
local nAge:=0
//? "you do whatever you want with nIndex = ", nIndex
If nIndex==1
//Selection_Age(@nAge)
nAge:=40 //sample
cFilter:= 'AGE<'+ltrim(str(nAge))
aFilters[1][2]:= cFilter
elseif nIndex==2
Selection_State( @cstate )
cFilter:= 'STATE='+ "'"+cState+"'"
aFilters[2][2]:= cFilter
elseif nIndex==3
// Selection_Married(@lMarried)
lMarried:=".t." //sample
cFilter:= 'MARRIED='+ lMarried
aFilters[3][2]:= cFilter
endif
AEval( aFilters, { |a| If( a[ 1 ], AAdd( af, a[ 2 ] ), nil ) } )
cFilter := FW_ArrayAsList( af, " .AND. " )
oDbf:SetFilter( cFilter )
oDbf:GoTop()
return cFilter
//----------------------------------------------------------------//
Function Selection_State( cstate )
local oList
local oDlg,oBrw
local lOk := .f.
local nArea := Select()
olist := TDataBase():Open( nil, "STATES", "DBFCDX", .t. )
olist:Gotop()
DEFINE DIALOG oDlg SIZE 600,400 PIXEL TRUEPIXEL
@ 115, 40 XBROWSE oBrw SIZE -40,-40 PIXEL OF oDlg ;
DATASOURCE oList ;
COLUMNS "CODE", "NAME" ;
CELL LINES NOBORDER UPDATE
oBrw:CreateFromCode()
aEval( oBrw:aCols, { |oCol| oCol:bLDClickData := { || lOk := .T., oDlg:End() } } )
ACTIVATE DIALOG oDlg CENTERED
if lOK
cstate :=oList:code
Endif
Select (nArea)
oList:close()
return nil
//-----------------------------------------------------------------//
so the end user can select what he needs to search for, the procedure rewrites the filters and executes them
when you click on a checkbox for example the states is activated true and you can select the state but when you click again from true it becomes false and should not ask you to select the state