A return must be encountered prior to this } error

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Xerxes77
Posts: 23
Joined: 20 Sep 2014, 07:26
Location: Australia

A return must be encountered prior to this } error

Post by Xerxes77 » 30 Nov 2015, 21:26

Hi guys,

I've been working on a simple excel script, but I seem to be encountering this error. The following code works fine:

Code: Select all

oExcel := ComObjActive("Excel.Application")    ;Connects to the active Excel application
FinishedEdit := "N"

;While (FinishedEdit == "N")
;{

	;Destroy Previous run of the Gui
	Gui, Destroy

	Gui, Add, Text,, Enter in the row (a number) to make amendments:
	Gui, Add, Text,, Enter in the start column (letters):
	Gui, Add, Text,, Enter in the end column (letters):
	Gui, Add, Text,, Enter in the number you want to add (use a negative number to subtract) to each cell:
	Gui, Add, Edit, vRow ym 
	Gui, Add, Edit, vStartCol 
	Gui, Add, Edit, vEndCol
	Gui, Add, Edit, vNumtoAdd
	Gui, Add, Button, default xm+450 w80, OK 
	Gui, Show,, Parameters
	return 

	GuiClose:
	ButtonOK:
	Gui, Submit


	if Row is integer
	{
		ValidRow := 1
	} else {
		ValidRow := 0
	}

	if NumtoAdd is number
	{
		ValidNumToAdd := 1
	} else {
		ValidNumToAdd := 0
	}

	if StartCol is alpha
	{
		if (StartCol != "")
		{
			ValidStartCol := 1
		} else {
			ValidStartCol := 0
		}
	} else {
		ValidStartCol := 0
	}

	if EndCol is alpha
	{
		if (EndCol != "")
		{
			ValidEndCol := 1
		} else {
			ValidEndCol := 0
		}
	} else {
		ValidEndCol := 0
	}

	while (ValidRow == 0)
	{
		InputBox, Row, Enter a number for the Row, Row entered was not a number. Try again:,, 300, 150
		
		if Row is integer
		{
			ValidRow := 1
		}
	}

	while (ValidStartCol == 0)
	{
		InputBox, StartCol, Enter a letter for the Column, Start Column Entered was not a letter. Try again:,, 300, 150
		
		if StartCol is alpha
		{
			if (StartCol != "")
			{
				ValidStartCol := 1
			}
		}
	}

	while (ValidEndCol == 0)
	{
		InputBox, EndCol, Enter a letter for the Column, End Column Entered was not a letter. Try again:,, 300, 150
		
		if EndCol is alpha
		{
			if (EndCol != "")
			{
				ValidEndCol := 1
			}
		}
	}

	while (ValidNumToAdd == 0)
	{
		InputBox, NumtoAdd, Enter a number for the Number to Add, Number to Add entered was not a number. Try again:,, 300, 150
		
		if NumtoAdd is integer
		{
			ValidNumToAdd := 1
		}
	}

	;Converts Start Column and End Column into Column Number
	StartColumn := oExcel.Columns(StartCol).Column
	FinishColumn := oExcel.Columns(EndCol).Column

	Iterations := FinishColumn - StartColumn + 1
	loop, %Iterations%
	{
		if (oExcel.Cells(Row, StartColumn + A_Index -1).Value == "")
		{
			;Set all interested cell values to '0' if blank
			oExcel.Cells(Row, StartColumn + A_Index -1).Value := 0
		}
		oExcel.Cells(Row, StartColumn+A_Index-1).Value := oExcel.Cells(Row, StartColumn+A_Index-1).Value + NumtoAdd
	}
	
	
	
	InputBox, Revert, Are changes ok?, Everything ok? (Y) or Undo changes? (U),, 300, 150

	while ((Revert != "Y") AND (Revert != "U"))
	{
		InputBox, Revert, Invalid Input, Everything ok? (Y) or Undo changes? (U),, 300, 150
	}

	if (Revert == "U")
	{
		loop, %Iterations%
		{
			oExcel.Cells(Row, StartColumn + A_Index-1).Value := oExcel.Cells(Row, StartColumn + A_Index-1).Value - NumtoAdd
		}
	}
	
	InputBox, FinishedEdit, Finished Processing Entries?, Have you finished making changes? (Y/N),, 300, 150
	
	while ((FinishedEdit != "Y") AND (FinishedEdit  != "N"))
	{
		InputBox, FinishedEdit, Invalid Input, Have you finished making changes? (Y/N),, 300, 150
	}
	
;}
ExitApp
However, when I uncomment the outermost while loop I then get the error. Any ideas on what's happening?

Thanks

lexikos
Posts: 9690
Joined: 30 Sep 2013, 04:07
Contact:

Re: A return must be encountered prior to this } error

Post by lexikos » 30 Nov 2015, 22:26

Just like it says, you must end the GuiClose/ButtonOK sub with "return" or "exit" *before* the end of the block which contains it.

What is the loop supposed to do? It's never going to loop, because there's a "return" on the first iteration.

You should probably rethink the logic and layout of your script. In general, it is a mistake to put a subroutine inside another subroutine or the auto execute section.

timelizards
Posts: 20
Joined: 11 Sep 2015, 21:00

Re: A return must be encountered prior to this } error

Post by timelizards » 30 Nov 2015, 22:27

return your g-label subroutines

Code: Select all

ButtonOK:

;do stuff

return

Xerxes77
Posts: 23
Joined: 20 Sep 2014, 07:26
Location: Australia

Re: A return must be encountered prior to this } error

Post by Xerxes77 » 01 Dec 2015, 02:36

Ah, I'm a little confused.

So my understanding is that for GUIs in general, the 'return' is to indicate the end of the autoexecute section. Based on what you're saying, I can't/shouldn't use a GUI inside a loop as every time the user clicks on the control it launches another subroutine? The reason for the outer loop is that I have many sections of an excel spreadsheet where I have to add/subtract different values to. I think I may have misinterpreted what you said.

It's not a big deal if I just run the script as many times as I need to complete the task, but I thought for convenience it would be better to put it inside a loop.

lexikos
Posts: 9690
Joined: 30 Sep 2013, 04:07
Contact:

Re: A return must be encountered prior to this } error

Post by lexikos » 01 Dec 2015, 04:21

So my understanding is that for GUIs in general, the 'return' is to indicate the end of the autoexecute section. Based on what you're saying, I can't/shouldn't use a GUI inside a loop as every time the user clicks on the control it launches another subroutine?
Huh? No, the reason is nothing like that.

The GUI is created by the script, the same way that a message box is created by the MsgBox command. The GUI is not part of the script-code - does not exist within the source code - and has nothing to do with the structure of the program (where the auto-execute section ends, where a subroutine begins and ends, etc.). Although it can cause subroutines like GuiClose to be called, it does not define how the code executes when it is called. So forget about the GUI for the moment.

The auto-execute section is perhaps a misnomer - it is a subroutine. Imagine that there is a label at line 0 of every script:

Code: Select all

the_auto_excecute_section:
When the program starts, it calls gosub the_auto_execute_section. Return does not indicate the end of the auto-execute section; it instructs the program to return from the current subroutine. In other words, stop executing the current subroutine and resume whichever subroutine called it - or if there is none, just exit the thread.

Although return can mark the end of a subroutine, it doesn't have to. Subroutines don't necessarily have clearly defined boundaries.

For instance,

Code: Select all

Random x, 1, 3
if x = 1
   return
MsgBox >1
if x = 2
    return
MsgBox >2
if x = 3
    return
MsgBox ???
Where does the auto-execute section end? The program will continue executing until one of the IF statements yields true, and then it will return. Even though all of the return statements are conditional, the last MsgBox will never be executed because x is always 1, 2 or 3.

All I said about the loop was this: It's never going to loop, because there's a "return" on the first iteration.

What do you think this will do?

Code: Select all

MsgBox x
return
MsgBox y
It can't both return and continue onward to show the second MsgBox.

Now what about this?

Code: Select all

While true
{
    return
}
MsgBox Not gonna happen.
As for subroutines inside subroutines, the following demonstrates why that won't work.

Code: Select all

MsgBox 1
gosub the_label
MsgBox 2
   the_label:
      MsgBox 3
   return
MsgBox 4
return
If you can understand what's happening here, you should understand what was wrong with your original code.
Spoiler
It's not a big deal if I just run the script as many times as I need to complete the task, but I thought for convenience it would be better to put it inside a loop.
You can do that; just don't return or define subroutines inside the loop. However, if you repeat the Gui code like so ...

Code: Select all

While (FinishedEdit == "N")
{
	;Destroy Previous run of the Gui
	Gui, Destroy
	...
	Gui, Show,, Parameters
}
... it will show the GUI and then instantly destroy the GUI and create another one, repeatedly. If you want to create and show multiple GUIs simultaneously, you can use the Gui New command. If you want to wait until the first GUI is closed before creating another one, then wait until the first GUI is closed before creating another one (you need to tell the program to actually do that; you can use WinWaitClose).
Ah, I'm a little confused.
Well, at least you were right about one thing. ;)

Xerxes77
Posts: 23
Joined: 20 Sep 2014, 07:26
Location: Australia

Re: A return must be encountered prior to this } error

Post by Xerxes77 » 02 Dec 2015, 03:27

Thanks Lexikos. I think I have a better idea of what's happening. But based on what you said, it seems I may have to adjust the code a fair bit to get it working the way I want it to.

I was playing around with what I had and I can see how it runs now:

Code: Select all

FinishedEdit := "N"

While (FinishedEdit == "N")
{
	MsgBox 1
	
	;Destroy Previous run of the Gui
	Gui, Destroy

	Gui, Add, Text,, Enter in the row (a number) to make amendments:
	Gui, Add, Text,, Enter in the start column (letters):
	Gui, Add, Text,, Enter in the end column (letters):
	Gui, Add, Text,, Enter in the number you want to add (use a negative number to subtract) to each cell:
	Gui, Add, Edit, vRow ym 
	Gui, Add, Edit, vStartCol 
	Gui, Add, Edit, vEndCol
	Gui, Add, Edit, vNumtoAdd
	Gui, Add, Button, default xm+450 w80, OK 
	Gui, Show,, Parameters
	WinWaitClose, Parameters
	
	MsgBox 4
	
	GuiClose:
	ButtonOK:
	Gui, Submit
	
	MsgBox 2
	return
	
	ExitApp
	MsgBox 3
}
The output of this is 1, 2, 4 and finally 2 - so the instance of this script never stops and 3 never appears. I've taken a look at other posts where others were having problems with GUIs inside a loop, but it seems that the solutions people posted never close the existing GUI until the very end of the script - which I can do but I don't think it will be very elegant for me.

E.g.

Code: Select all

TestCountV = 0

Gui, Add, Text  , vText w100, Test Count is %TestCountV%

Gui, Add, Button, Default   , Continue

Gui, Add, Button, wp        , Cancel



Gui, Show, AutoSize, Title

return

GuiClose:

ButtonCancel:

 ExitApp



ButtonContinue:

 TestCountV++

 GuiControl,,text, Test Count is %TestCountV%

return

timelizards
Posts: 20
Joined: 11 Sep 2015, 21:00

Re: A return must be encountered prior to this } error

Post by timelizards » 02 Dec 2015, 15:11

The output of this is 1, 2, 4 and finally 2
2 comes before 4 because the g-label for the OK button on the GUI (and closing the Parameters window) runs the subroutine with msgbox 4 in it, before the script is able to execute msgbox 2. then 2 shows again becuase there was no return after the gui statements so the script just keeps on running, and hits those labels you set up. msgbox 3 never appears due to exitapp, which exits the script.

still kinda confused on what the outside loop is doing. return and exitapp break the loop on the first iteration.

timelizards
Posts: 20
Joined: 11 Sep 2015, 21:00

Re: A return must be encountered prior to this } error

Post by timelizards » 02 Dec 2015, 15:40

Sorry i edited my first post a few times and just needed to make a new one to not confuse myself.
I was playing around with what I had and I can see how it runs now:
Does this help?

Code: Select all

FinishedEdit := "N"
 

MsgBox A

;Destroy Previous run of the Gui
;Gui, Destroy

Gui, Add, Text,, Enter in the row (a number) to make amendments:
Gui, Add, Text,, Enter in the start column (letters):
Gui, Add, Text,, Enter in the end column (letters):
Gui, Add, Text,, Enter in the number you want to add (use a negative number to subtract) to each cell:
Gui, Add, Edit, vRow ym 
Gui, Add, Edit, vStartCol 
Gui, Add, Edit, vEndCol
Gui, Add, Edit, vNumtoAdd
Gui, Add, Button, default xm+450 w80, OK 
Gui, Show,, Parameters
;WinWaitClose, Parameters  - dont think we need this

return

MsgBox 4 ; we will never see you msgbox 4



ButtonOK: ; engaged when gui button OK is pressed

Gui, Submit, NOHIDE ; added nohide

FinishedEdit := "N"

While (FinishedEdit == "N")
{
	msgbox % "loop index is " . a_index  ;this is where the bulk of the work would happen i assume
	
	if(a_index=5)
		FinishedEdit := "Y?" ; i guess anything no N 
	
}

MsgBox B ; we see this after the while loops runs 5 times and i manually set the variable to "N"

return ;returns from the button press

GuiClose:
MsgBox C ; should only see when closing gui window
ExitApp


Xerxes77
Posts: 23
Joined: 20 Sep 2014, 07:26
Location: Australia

Re: A return must be encountered prior to this } error

Post by Xerxes77 » 02 Dec 2015, 22:37

Ahhh thanks timelizard :) now I see why everyone was confused about what the outer loop was for... I didn't realise that ButtonOK: and GuiClose: were separate subroutines.

I guess this is what happens when you blindly follow the examples without questioning every line of code in them...

Here is my edited script for anyone who is interested:

Code: Select all

oExcel := ComObjActive("Excel.Application")    ;Connects to the active Excel application

Gui, Add, Text,, Enter in the row (a number) to make amendments:
Gui, Add, Text,, Enter in the start column (letters):
Gui, Add, Text,, Enter in the end column (letters):
Gui, Add, Text,, Enter in the number you want to add (use a negative number to subtract) to each cell:
Gui, Add, Edit, vRow ym 
Gui, Add, Edit, vStartCol 
Gui, Add, Edit, vEndCol
Gui, Add, Edit, vNumtoAdd
Gui, Add, Button, default xm+450 w80, OK 
Gui, Show,, Parameters
return

ButtonOK: ;Subroutine engaged when gui button OK is pressed
Gui, Submit, NOHIDE ; added nohide

ValidRow := 0
ValidNumToAdd := 0
ValidStartCol := 0
ValidEndCol := 0

if Row is integer
{
	ValidRow := 1
}
if NumtoAdd is number
{
	ValidNumToAdd := 1
}
if StartCol is alpha
{
	if (StartCol != "")
	{
		ValidStartCol := 1
	}
}

if EndCol is alpha
{
	if (EndCol != "")
	{
		ValidEndCol := 1
	}
}
while (ValidRow == 0)
{
	InputBox, Row, Enter a number for the Row, Row entered was not a number. Try again:,, 300, 150
	
	if Row is integer
	{
		ValidRow := 1
	}
}
while (ValidStartCol == 0)
{
	InputBox, StartCol, Enter a letter for the Column, Start Column Entered was not a letter. Try again:,, 300, 150
	
	if StartCol is alpha
	{
		if (StartCol != "")
		{
			ValidStartCol := 1
		}
	}
}
while (ValidEndCol == 0)
{
	InputBox, EndCol, Enter a letter for the Column, End Column Entered was not a letter. Try again:,, 300, 150
	
	if EndCol is alpha
	{
		if (EndCol != "")
		{
			ValidEndCol := 1
		}
	}
}

while (ValidNumToAdd == 0)
{
	InputBox, NumtoAdd, Enter a number for the Number to Add, Number to Add entered was not a number. Try again:,, 300, 150
	
	if NumtoAdd is integer
	{
		ValidNumToAdd := 1
	}
}
StringUpper, StartCol, StartCol
StringUpper, EndCol, EndCol

;Converts Start Column and End Column into Column Number
StartColumn := oExcel.Columns(StartCol).Column
FinishColumn := oExcel.Columns(EndCol).Column
Iterations := FinishColumn - StartColumn + 1

loop, %Iterations%
{
	if (oExcel.Cells(Row, StartColumn + A_Index -1).Value == "")
	{
		;Set all interested cell values to '0' if blank
		oExcel.Cells(Row, StartColumn + A_Index -1).Value := 0
	}
	oExcel.Cells(Row, StartColumn+A_Index-1).Value := oExcel.Cells(Row, StartColumn+A_Index-1).Value + NumtoAdd
}

InputBox, Revert, Are changes ok?, Everything ok? (Y) or Undo changes? (U),, 300, 150
while ((Revert != "Y") AND (Revert != "U"))
{
	InputBox, Revert, Invalid Input, Everything ok? (Y) or Undo changes? (U),, 300, 150
}

StringUpper, Revert, Revert
if (Revert == "U")
{
	loop, %Iterations%
	{
		oExcel.Cells(Row, StartColumn + A_Index-1).Value := oExcel.Cells(Row, StartColumn + A_Index-1).Value - NumtoAdd
	}
} else if (Revert == "Y")
{
	loop, %Iterations%
	{
		oExcel.Cells(Row, StartColumn + A_Index-1).Interior.ColorIndex := 4
	}
}

;Clear fields
ControlSetText, Edit1, , Parameters
ControlSetText, Edit2, , Parameters
ControlSetText, Edit3, , Parameters
ControlSetText, Edit4, , Parameters

return ;returns from the ButtonOK Subroutine

GuiClose:
;Subroutine engaged when Closing the window
ExitApp
Thanks for all your help.

Post Reply

Return to “Ask for Help (v1)”