Dynamic Actions problem

Moderator: Rathinagiri

Post Reply
User avatar
giuper63
Posts: 11
Joined: Wed Oct 17, 2012 10:40 pm
DBs Used: DBF, mysql, sqlite, PostgresSQL
Location: Montecassiano
Contact:

Dynamic Actions problem

Post by giuper63 »

Hi to all,
I have a problem using dynamic actions.
I hope someone can help me to solve the problem.

I usually create dynamic programs where the components are stored in arrays.
Here is an example:

Code: Select all

***************************************************************************
#include "hmg.ch"
FUNCTION Main()
	DEFINE WINDOW Win1 ;
		AT 0,0 ;
		WIDTH 400 ;
		HEIGHT 300 ;
		TITLE 'Test Dynamic Actions from Array' ;
		MAIN 

		DEFINE MAIN MENU
			POPUP "Test"
				MENUITEM "Test Dynamic Actions" ACTION TestButtons(7)
			END POPUP 			
		END MENU

		DEFINE BUTTON TEST
			ROW		10
			COL		10
			WIDTH		200
			CAPTION		'Click Me!'
			ACTION		TestButtons(5)
		END BUTTON

	END WINDOW
	Center Window Win1
	Activate Window Win1
RETURN NIL

*****************************************************************************
Procedure TestButtons(nNum) 
*****************************************************************************
Local nMax:=20
Private nN, aTest:={}

For nN:=1 to nNum
	AAdd(aTest,{'Msg'+AllTrim(Str(nN)),'MsgBox("'+AllTrim(Str(nN))+'")'})
Next

DEFINE WINDOW HDWin ;
	AT 0,0 ;
	WIDTH 200 ;
	HEIGHT 0 ;
	TITLE 'Test!' ;
	MODAL

	For nN:=1 to Len(aTest)-1
		nMax:=24*(nN-1)

		DEFINE BUTTON &("B"+AllTrim(Str(nN)))
			ROW 10+nMax
			COL 10
			WIDTH HDWin.WIDTH-20
			Height 22
			CAPTION aTest[nN,1]
//			ACTION &("aTest[nN,2]")                   // <<<<<<<<<<<< With this the procedure return nN variable error
//			ACTION &("aTest["+AllTrim(Str(nN))+",2]") // <<<<<<<<<<< With this the procedure do nothing!
//			ACTION aTest[nN,2]                        // <<<<<<<<<<< With this the procedure do nothing!
//			ACTION MsgBox(aTest[nN,2])                // <<<<<<<<<<< With this the procedure always return Len(aTest)+1!!!
			ACTION MsgBox(Str(nN))                    // <<<<<<<<<<< With this the procedure always return Len(aTest)+1!!!
		END BUTTON
	Next

	HDWin.Height:=nMax+40+GetTitleHeight()+GetBorderHeight()

END WINDOW
CENTER WINDOW HDWin
ACTIVATE WINDOW HDWin
Return
***********************************************************************************
The procedures in the aTest array could change depending on parameters passed to the procedure.
The caption is taked correctly from the array, the action not!
I would like that pressing the Msg1 button the message would be "1" and not Len(aTest)+1.
I have tried using "&(aTest[nN,2])", which should solve the action at creation time of the component, but I notice that it solve the action at action time, when the variable is Len(aTest)+1.

Some solution?

Regards
Giuseppe
Italian developer
User avatar
esgici
Posts: 4543
Joined: Wed Jul 30, 2008 9:17 pm
DBs Used: DBF
Location: iskenderun / Turkiye
Contact:

Re: Dynamic Actions problem

Post by esgici »

Hi Giuseppe
giuper63 wrote:...Some solution?...
I hope this will give you an idea :idea:

Code: Select all

   ...
   For nN := 1 to Len( aTest )-1
   
      nMax:=24 * ( nN-1 )
   
      DEFINE BUTTON &("B"+AllTrim(Str(nN)))
         ...
         ACTION Test_2( this.name )         // new line
      END BUTTON
   Next
   ...

PROCEDURE Test_2( cBtnNam )             // New procedure

  LOCAL nBtnNo := VAL( SUBS( cBtnNam, 2 ) )
  
  MsgBox(  LTRIM( STR( nBtnNo  ) ) )
    
RETURN
Regards
Viva INTERNATIONAL HMG :D
User avatar
mol
Posts: 3718
Joined: Thu Sep 11, 2008 5:31 am
Location: Myszków, Poland
Contact:

Re: Dynamic Actions problem

Post by mol »

you should modify a little your function:

Code: Select all


For nN:=1 to Len(aTest)-1
	nMax:=24*(nN-1)
	cAction := "MsgBox(str(" + Str(nN) + "))"
	DEFINE BUTTON &("B"+AllTrim(Str(nN)))
		ROW 10+nMax
		COL 10
		WIDTH HDWin.WIDTH-20
		Height 22
		CAPTION aTest[nN,1]
		// ACTION &("aTest[nN,2]") // <<<<<<<<<<<< With this the procedure return nN variable error
		// ACTION &("aTest["+AllTrim(Str(nN))+",2]") // <<<<<<<<<<< With this the procedure do nothing!
		// ACTION aTest[nN,2] // <<<<<<<<<<< With this the procedure do nothing!
		// ACTION MsgBox(aTest[nN,2]) // <<<<<<<<<<< With this the procedure always return Len(aTest)+1!!!
		Action &cAction // <<<<<<<<<<< With this the procedure always return Len(aTest)+1!!!
	END BUTTON
Next
Regards, Marek
User avatar
giuper63
Posts: 11
Joined: Wed Oct 17, 2012 10:40 pm
DBs Used: DBF, mysql, sqlite, PostgresSQL
Location: Montecassiano
Contact:

Re: Dynamic Actions problem

Post by giuper63 »

Thanks esgici,
you give me the right idea!
This is the final code:

Code: Select all

#include "hmg.ch"
Request PlayAsterisk, Playexclamation, PlayBeep, PlayOK, PlayHand

FUNCTION Main()
Local nN, aTest1:={}, aTest2:={}

// Define an array for button creation
For nN:=1 to 5
	AAdd(aTest1,{'One'+AllTrim(Str(nN)),'{|| MsgBox("First : '+AllTrim(Str(nN))+'")}'})
Next

// Define a second array with different text and actions
AAdd(aTest2,{'First','{|| MsgBox("No sound!")}'}) // Only 1 procedure
AAdd(aTest2,{'Second','{|| PlayBeep(), MsgOkCancel("Simple beep")}'}) // 2 procedures!
AAdd(aTest2,{'Third','{|| PlayAsterisk(), MsgBox("Asterisk")}'})
AAdd(aTest2,{'..and..','{|| PlayExclamation(), MsgBox("Wow")}'})
AAdd(aTest2,{'..so..','{|| PlayOk(), MsgStop("OK?")}'})
AAdd(aTest2,{'..ON..','{|| PlayHand(), MsgYesNo("Hand")}'})


	DEFINE WINDOW Win1 ;
		AT 0,0 ;
		WIDTH 400 ;
		HEIGHT 300 ;
		TITLE 'Test Dynamic Actions from Array' ;
		MAIN 

		DEFINE MAIN MENU

			POPUP "Test"
				MENUITEM "Test Dynamic Actions" ACTION TestButtons(aTest1)
			END POPUP 			

		END MENU

		DEFINE BUTTON TEST
			ROW		10
			COL		10
			WIDTH		200
			CAPTION		'Click Me!'
			ACTION		TestButtons(aTest2)
		END BUTTON

	END WINDOW
	Center Window Win1
	Activate Window Win1
RETURN NIL

*****************************************************************************
Procedure TestButtons(aTest) 
*****************************************************************************
Local nMax:=20, nN

DEFINE WINDOW HDWin ;
	AT 0,0 ;
	WIDTH 200 ;
	HEIGHT 0 ;
	TITLE 'Test!' ;
	MODAL

	For nN:=1 to Len(aTest)-1
		nMax:=24*(nN-1)
		DEFINE BUTTON &("B"+AllTrim(Str(nN)))
			ROW 10+nMax
			COL 10
			WIDTH HDWin.WIDTH-20
			Height 22
			CAPTION aTest[nN,1]
			ACTION Test_2(aTest) // <<< Pass the array to the second function
		END BUTTON
	Next

	HDWin.Height:=nMax+40+GetTitleHeight()+GetBorderHeight()

END WINDOW
CENTER WINDOW HDWin
ACTIVATE WINDOW HDWin
Return

*****************************************************************************
PROCEDURE Test_2(aTest)  // Execution Procedure
*****************************************************************************
Local nEle:=Val(Subst(this.name,2))  // Retrive the element number
If nEle>0
	Eval(&(aTest[nEle,2]))  // Evaluate the code
Endif
Return
Many, many, many thanks for the answer!

Also thanks to mol, but I was searching for a solution where I pass an array to a function (TestButtons).
This function can be called from different procedures with different arrays and different actions.
As you can see I declare 2 arrays in the first part of the program, so I can call the function with different arrays.
Now It's easy to add a button with linked procedure to the list. Just add an element to the array!
And It's easy to create a list of buttons in every part of the program.

Best regards
Giuseppe
Italian developer
User avatar
esgici
Posts: 4543
Joined: Wed Jul 30, 2008 9:17 pm
DBs Used: DBF
Location: iskenderun / Turkiye
Contact:

Re: Dynamic Actions problem

Post by esgici »

Hi Giuseppe

I'm glad by your happiness :)

and good implementation, thanks to sharing :)

By the way, I have a few more suggestions :

- eliminating "request" for internal HMG functions,
- reducing '&' (macro evaluation) usages

I hope you will like :

Code: Select all

#include "hmg.ch"

* Request PlayAsterisk, Playexclamation, PlayBeep, PlayOK, PlayHand

FUNCTION Main()
Local nN, aTest1:={}, aTest2:={}

// Define an array for button creation

For nN := 1 to 5

   AAdd( aTest1,{ 'One' + AllTrim( Str( nN ) ),;
                  { | nNo | MsgBox( "First : " + AllTrim( Str( nNo )) ) } } )
                 
Next

// Define a second array with different text and actions

AAdd( aTest2, {'First',   {|| MsgBox("No sound!")}}) // Only 1 procedure
AAdd( aTest2, {'Second',  {|| PlayBeep(), MsgOkCancel("Simple beep")}}) // 2 procedures!
AAdd( aTest2, {'Third',   {|| PlayAsterisk(), MsgBox("Asterisk")}})
AAdd( aTest2, {'..and..', {|| PlayExclamation(), MsgBox("Wow")}})
AAdd( aTest2, {'..so..',  {|| PlayOk(), MsgStop("OK?")}})
AAdd( aTest2, {'..ON..',  {|| PlayHand(), MsgYesNo("Hand")}})


   DEFINE WINDOW Win1 ;
      AT 0,0 ;
      WIDTH 400 ;
      HEIGHT 300 ;
      TITLE 'Test Dynamic Actions from Array' ;
      MAIN 

      DEFINE MAIN MENU

         POPUP "Test"
            MENUITEM "Test Dynamic Actions" ACTION TestButtons(aTest1)
         END POPUP          

      END MENU

      DEFINE BUTTON TEST
         ROW      10
         COL      10
         WIDTH      200
         CAPTION      'Click Me!'
         ACTION      TestButtons(aTest2)
      END BUTTON

   END WINDOW
   Center Window Win1
   Activate Window Win1
RETURN NIL

*****************************************************************************
Procedure TestButtons(aTest) 
*****************************************************************************
Local nMax:=20, nN

DEFINE WINDOW HDWin ;
   AT 0,0 ;
   WIDTH 200 ;
   HEIGHT 0 ;
   TITLE 'Test!' ;
   MODAL

   For nN:=1 to Len(aTest)-1
      nMax:=24*(nN-1)
      DEFINE BUTTON &("B"+AllTrim(Str(nN)))
         ROW 10+nMax
         COL 10
         WIDTH HDWin.WIDTH-20
         Height 22
         CAPTION aTest[nN,1]
         ACTION Test_2(aTest) // <<< Pass the array to the second function
      END BUTTON
   Next

   HDWin.Height:=nMax+40+GetTitleHeight()+GetBorderHeight()

END WINDOW
CENTER WINDOW HDWin
ACTIVATE WINDOW HDWin
Return

*****************************************************************************
PROCEDURE Test_2(aTest)  // Execution Procedure
*****************************************************************************
Local nEle := Val(Subst(this.name,2))  // Retrive the element number

If nEle > 0
   Eval( aTest[ nEle, 2 ], nEle )  // Evaluate the code
Endif

Return
Happy HMG'ing :D
Viva INTERNATIONAL HMG :D
User avatar
mol
Posts: 3718
Joined: Thu Sep 11, 2008 5:31 am
Location: Myszków, Poland
Contact:

Re: Dynamic Actions problem

Post by mol »

I prefer using a function, which I can call ActionCenter, with one parameter pointing another function to call, eg:

Code: Select all

function ActionCenter
  param xWhatToDo

  local xRetValue := .f.

  do case
  case xWhatToDo == 1
    MsgBox("Action no 1 - return an array")
    xRetValue := {1,2,3}
  case xWhatToDo == 2
    MsgBox("Action no 2")
  case xWhatToDo == 3
    MsgBox("Action no 3")
  case xWhatToDo == 4
    MsgBox("Action no 4")
  endcase
.
.
.
return xRetValue
IMO this code is easier to debug

Marek
Post Reply