Looping Through A list and verifying GUI Input Topic is solved

Get help with using AutoHotkey and its commands and hotkeys
Archandrion
Posts: 31
Joined: 26 May 2018, 22:23

Looping Through A list and verifying GUI Input

09 Jul 2018, 05:26

The code below is supposed to loop through a list of words and verify that each was typed correctly by the user? However it does not break and move on to the next word. It worked with InputBox but I would prefer to have it work with the GUI.

Code: Select all

Wordlist = 
(
Abacus
Abandon
Abidance
)

Gui, Add, Button, x172 y119 w100 h30 Default gConfirmBtn, OK
Gui, Add, Edit, x32 y49 w360 h20 vUserInput,
Gui, Show, x448 y309 h209 w442, New GUI Window
GuiControl, Focus, FUserInput
Return

GuiClose:
ExitApp

Loop, parse, WordList, `n
{
    MsgBox, Type The word %TheCurrentWord%
    TheCurrentWord := A_LoopField
    SpellWord(TheCurrentWord)
}


SpellWord(TheCurrentWord){
    GuiControl, Focus, FUserInput
    Loop
    {
ConfirmBtn:
        Gui, Submit, NoHide
        InputText = %UserInput%
        If (InputText<>TheCurrentWord)
        {
            MsgBox, Incorrect
        } Else {
            MsgBox, Correct
            Break
        }
        Return
    }
    MsgBox Success
    Return
}

Esc::ExitApp
GEV
Posts: 791
Joined: 25 Feb 2014, 00:50

Re: Looping Through A list and verifying GUI Input

09 Jul 2018, 06:41

Code: Select all

StringCaseSense, On

Wordlist = 
(
Abacus
Abandon
Abidance
)

Gui, Add, Button, x172 y119 w100 h30 Default gConfirmBtn, OK
Gui, Add, Edit, x32 y49 w360 h20 vUserInput,
Gui, Show, x448 y309 h209 w442, New GUI Window
GuiControl, Focus, Edit1
Return

GuiClose:
ExitApp

ConfirmBtn:
Correct_Input := false
Gui, Submit, NoHide
Loop, parse, WordList, `n
{
	If (A_LoopField == UserInput)
	{
		Correct_Input := true
		MsgBox, Correct
			Break
	}		
}
If !(Correct_Input)
	MsgBox, Incorrect
GuiControl, Focus, Edit1
return

Esc::ExitApp
Archandrion
Posts: 31
Joined: 26 May 2018, 22:23

Re: Looping Through A list and verifying GUI Input

09 Jul 2018, 13:19

GEV wrote:

Code: Select all

StringCaseSense, On

Wordlist = 
(
Abacus
Abandon
Abidance
)

Gui, Add, Button, x172 y119 w100 h30 Default gConfirmBtn, OK
Gui, Add, Edit, x32 y49 w360 h20 vUserInput,
Gui, Show, x448 y309 h209 w442, New GUI Window
GuiControl, Focus, Edit1
Return

GuiClose:
ExitApp

ConfirmBtn:
Correct_Input := false
Gui, Submit, NoHide
Loop, parse, WordList, `n
{
	If (A_LoopField == UserInput)
	{
		Correct_Input := true
		MsgBox, Correct
			Break
	}		
}
If !(Correct_Input)
	MsgBox, Incorrect
GuiControl, Focus, Edit1
return

Esc::ExitApp
Thanks for replying. However I would still need the current word before pressing the confirm button in order to advise the user as to which word to type as per the part of my code MsgBox, Type The word %TheCurrentWord%.
GEV
Posts: 791
Joined: 25 Feb 2014, 00:50

Re: Looping Through A list and verifying GUI Input

09 Jul 2018, 14:13

Maybe something like this

Code: Select all

Wordlist = 
(
Abacus
Abandon
Abidance
)

Loop, parse, WordList, `n
	MaxIndex := A_Index

Gui, Add, Button, x172 y119 w100 h30 Default gConfirmBtn, OK
Gui, Add, Edit, x32 y49 w360 h20 vUserInput,
Gui, Show, x448 y309 h209 w442, New GUI Window
GuiControl, Focus, Edit1

SetTimer, TypeTheWord, 500 
Return

GuiClose:
ExitApp

TypeTheWord:
n++
Loop, parse, WordList, `n
{
	If (A_Index = n)
	{
		TheCurrentWord := A_LoopField
		Gui, 2: -Caption
		Gui, 2: Font, s11, Cambria bold
		Gui, 2: Add, Text,, Type The word "%A_LoopField%"
		Gui, 2: Show, NoActivate x530 y270
		GuiControl, Focus, Edit1
		SetTimer, TypeTheWord, off 
	}
}
Return

ConfirmBtn:
Gui, 2: destroy
Gui, Submit, NoHide
If (TheCurrentWord == UserInput)
	MsgBox, Correct
else
	MsgBox, Incorrect
GuiControl, Focus, Edit1
SendInput, +{Home}{Del}
If (n < MaxIndex)
	SetTimer, TypeTheWord, on
else
	MsgBox END
return

Esc::ExitApp
?
Archandrion
Posts: 31
Joined: 26 May 2018, 22:23

Re: Looping Through A list and verifying GUI Input

09 Jul 2018, 16:03

GEV wrote:Maybe something like this

Code: Select all

Wordlist = 
(
Abacus
Abandon
Abidance
)

Loop, parse, WordList, `n
	MaxIndex := A_Index

Gui, Add, Button, x172 y119 w100 h30 Default gConfirmBtn, OK
Gui, Add, Edit, x32 y49 w360 h20 vUserInput,
Gui, Show, x448 y309 h209 w442, New GUI Window
GuiControl, Focus, Edit1

SetTimer, TypeTheWord, 500 
Return

GuiClose:
ExitApp

TypeTheWord:
n++
Loop, parse, WordList, `n
{
	If (A_Index = n)
	{
		TheCurrentWord := A_LoopField
		Gui, 2: -Caption
		Gui, 2: Font, s11, Cambria bold
		Gui, 2: Add, Text,, Type The word "%A_LoopField%"
		Gui, 2: Show, NoActivate x530 y270
		GuiControl, Focus, Edit1
		SetTimer, TypeTheWord, off 
	}
}
Return

ConfirmBtn:
Gui, 2: destroy
Gui, Submit, NoHide
If (TheCurrentWord == UserInput)
	MsgBox, Correct
else
	MsgBox, Incorrect
GuiControl, Focus, Edit1
SendInput, +{Home}{Del}
If (n < MaxIndex)
	SetTimer, TypeTheWord, on
else
	MsgBox END
return

Esc::ExitApp
?
This works for displaying the current word but it is also supposed to allow the user to retype the word until they get it right rather than move on to the next word.
GEV
Posts: 791
Joined: 25 Feb 2014, 00:50

Re: Looping Through A list and verifying GUI Input

09 Jul 2018, 16:31

Code: Select all

Wordlist = 
(
Abacus
Abandon
Abidance
)

Loop, parse, WordList, `n
	MaxIndex := A_Index

Gui, Add, Button, x172 y119 w100 h30 Default gConfirmBtn, OK
Gui, Add, Edit, x32 y49 w360 h20 vUserInput,
Gui, Show, x448 y309 h209 w442, New GUI Window
GuiControl, Focus, Edit1

SetTimer, TypeTheWord, 500 
Return

GuiClose:
ExitApp

TypeTheWord:
n++
Loop, parse, WordList, `n
{
	If (A_Index = n)
	{
		TheCurrentWord := A_LoopField
		Gui, 2: -Caption +AlwaysOnTop
		Gui, 2: Font, s11, Cambria bold
		Gui, 2: Add, Text,, Type the word "%TheCurrentWord%"
		Gui, 2: Show, NoActivate x530 y345
		GuiControl, Focus, Edit1
		SetTimer, TypeTheWord, off 
	}
}
Return

ConfirmBtn:
Gui, Submit, NoHide
If (TheCurrentWord == UserInput)
{
	Gui, 2: destroy
	MsgBox, Correct!
	If (n < MaxIndex)
		SetTimer, TypeTheWord, on
	else
	{
		MsgBox Success!!
		Reload ; or ExitApp
	}
}
else
	MsgBox, Incorrect`n`tTry again!
GuiControl, Focus, Edit1
SendInput, {End}+{Home}{Del}
return

Esc::ExitApp
Alibaba
Posts: 479
Joined: 29 Sep 2013, 16:15
Location: Germany

Re: Looping Through A list and verifying GUI Input  Topic is solved

09 Jul 2018, 17:56

Hello GEV, i think the approach with the overlay GUI and timed routine is a bit confusing..?
You can just change the content of existing text labels.

@Archandrion, i would recommend the use of Arrays, they make life much easier, e.g. by using the MaxIndex() method, you never have to worry about the amount of items. See how you can "convert" a linebreak-separated list into an Array:

Code: Select all

Wordlist =
(
Abacus
Abandon
Abidance
)

; Transform your linebreak-separated list into array:
WordArray := []
Loop, Parse, Wordlist, `n
	WordArray[A_Index - 1] := A_LoopField

Gui, Add, Button, x172 y119 w100 h30 Default gConfirmBtn, OK
Gui, Add, Text, x40 y20 w400 h20 vRequestedWord
Gui, Add, Edit, x32 y49 w360 h20 vUserInput,
Gui, Show, x448 y309 h209 w442, New GUI Window
GuiControl, Focus, Edit1

; Adress words by their index:
currentWordIndex := 0
GuiControl, Text, RequestedWord, % "Type the word " . WordArray[currentWordIndex]
return

GuiClose:
ExitApp

ConfirmBtn:
Gui, Submit, NoHide
If (WordArray[currentWordIndex] == UserInput)
{
	MsgBox, Correct!
	currentWordIndex++ ; next word
	; see how arrays make your life easy
	if (currentWordIndex > WordArray.MaxIndex())
	{
		MsgBox Success!!
		Reload ; or ExitApp
	}
	; show text for next word:
	GuiControl, Text, RequestedWord, % "Type the word " . WordArray[currentWordIndex]
} else {
	MsgBox, Incorrect`n`tTry again!
}
GuiControl, Focus, Edit1
SendInput, {End}+{Home}{Del}
return

Esc::ExitApp
Have a fun scripting time!
Alibaba
"Nothing is quieter than a loaded gun." - Heinrich Heine
Archandrion
Posts: 31
Joined: 26 May 2018, 22:23

Re: Looping Through A list and verifying GUI Input

09 Jul 2018, 19:07

Alibaba wrote:Hello GEV, i think the approach with the overlay GUI and timed routine is a bit confusing..?
You can just change the content of existing text labels.

@Archandrion, i would recommend the use of Arrays, they make life much easier, e.g. by using the MaxIndex() method, you never have to worry about the amount of items. See how you can "convert" a linebreak-separated list into an Array:

Code: Select all

Wordlist =
(
Abacus
Abandon
Abidance
)

; Transform your linebreak-separated list into array:
WordArray := []
Loop, Parse, Wordlist, `n
	WordArray[A_Index - 1] := A_LoopField

Gui, Add, Button, x172 y119 w100 h30 Default gConfirmBtn, OK
Gui, Add, Text, x40 y20 w400 h20 vRequestedWord
Gui, Add, Edit, x32 y49 w360 h20 vUserInput,
Gui, Show, x448 y309 h209 w442, New GUI Window
GuiControl, Focus, Edit1

; Adress words by their index:
currentWordIndex := 0
GuiControl, Text, RequestedWord, % "Type the word " . WordArray[currentWordIndex]
return

GuiClose:
ExitApp

ConfirmBtn:
Gui, Submit, NoHide
If (WordArray[currentWordIndex] == UserInput)
{
	MsgBox, Correct!
	currentWordIndex++ ; next word
	; see how arrays make your life easy
	if (currentWordIndex > WordArray.MaxIndex())
	{
		MsgBox Success!!
		Reload ; or ExitApp
	}
	; show text for next word:
	GuiControl, Text, RequestedWord, % "Type the word " . WordArray[currentWordIndex]
} else {
	MsgBox, Incorrect`n`tTry again!
}
GuiControl, Focus, Edit1
SendInput, {End}+{Home}{Del}
return

Esc::ExitApp
Have a fun scripting time!
Alibaba
Thanks this solved my problem as I was able to integrate it into the larger block of code below using a text to speech narrator. Do you have any recommendations for optimizing the code below? The narrator does add a bit of lag but I could not find any faster methods of using text to speech in Autohotkey. I wish to keep the narration because it seems to work well for practicing spelling.

Code: Select all

;SpellingTutor

UniqueIdentifier := "Spelling Tutor " . A_TickCount

GetWordsList(WordsList, "Paste or Type in a list of words and press Enter or click OK")
WordsWrongThisSession := 0
WordsCorrectThisSession := 0
WordCount := 0

; Transform your linebreak-separated list into array:
WordArray := []
Loop, Parse, Wordslist, `n
WordArray[A_Index - 1] := A_LoopField

Gui, InputBox:Add, Edit, x22 y69 w360 h20 vUserInput,
Gui, InputBox:Add, Button, x22 y29 w100 h30 gSayWordBtn, Say Word
Gui, InputBox:Add, Button, x322 y109 w100 h30 gCheckSpellingBtn Default, Check Spelling
Gui, InputBox:Add, Button, x22 y149 w100 h30 gShowWordBtn, Show Word
Gui, InputBox:Add, Text, x23 y109 w290 h30 vSpellCheckMessage, Enter The Word You Heard
Gui, InputBox:Add, Text, x22 y189 w360 h30 vCorrectSpelling,
hIcon := LoadPicture("imageres.dll", "w32 Icon182", _)
Gui, InputBox:Add, Picture, x392 y69 w20 h20 vIconPic, % "HICON:" hIcon
Gui, InputBox:Show, x380 y312 h243 w440, %UniqueIdentifier%
GuiControl, InputBox:Focus, UserInput

; Adress words by their index:
currentWordIndex := 0
IncorrectCount := 0
Retry := 0
Narration := "Type the word" . WordArray[currentWordIndex]
GuiControl, InputBox:Focus, UserInput
GuiControl, InputBox:, UserInput,
SayThis(Narration)
return

InputBoxGuiClose:
ExitApp

SayWordBtn:
{
    SyCurrWord := WordArray[currentWordIndex]
    SayThis(SyCurrWord)
}
Return


ShowWordBtn:
{
    GuiControl, InputBox:, CorrectSpelling,% WordArray[currentWordIndex]
}
Return

CheckSpellingBtn:
Gui, InputBox:Submit, NoHide
If (WordArray[currentWordIndex] == UserInput)
{
    hIcon := LoadPicture("imageres.dll", "w32 Icon233", _)
    GuiControl, InputBox:, IconPic, % "HICON:" hIcon
    GuiControl, InputBox:, SpellCheckMessage, That is correct
    GuiControl, InputBox:, UserInput,
    SayThis("That is correct")
    If (Retry == 0){
        WordsCorrectOnFirstTry += 1
    }
    WordCount += 1
    currentWordIndex++ ; next word
    Retry := 0
    ; see how arrays make your life easy
    If (currentWordIndex > WordArray.MaxIndex())
    {
        Summary_str = 
        (
        You spelt %WordsCorrectOnFirstTry% correctly on your first try.
        There were a total of %WordCount% words in this session and you had problems with %WordsWrongThisSession% of them.
        You can find a list of the words you had the most difficulty with in %A_ScriptDir%\ProblematicWords\ProblematicWordsList.txt
        )

        MsgBox,, Summary, %Summary_str%
        ExitApp
    } Else{
        Narration := "Type the word" . WordArray[currentWordIndex]
        SayThis(Narration)
    }
} else {
    GuiControl, InputBox:, UserInput,
    SayThis("Spelling is incorrect. Try again")
    Retry := 1
    GuiControl, InputBox:, SpellCheckMessage, Spelling is incorrect. Try again
    hIcon := LoadPicture("imageres.dll", "w32 Icon230", _)
    GuiControl, InputBox:, IconPic, % "HICON:" hIcon
    IncorrectCount += 1
    If (IncorrectCount >= 3){
        CurrWrd := WordArray[currentWordIndex]
        GuiControl, InputBox:, CorrectSpelling, % "Type the word " . WordArray[currentWordIndex]
        FileCreateDir, %A_ScriptDir%\ProblematicWords
        FileAppend, %CurrWrd%`r`n, %A_ScriptDir%\ProblematicWords\ProblematicWordsList.txt
    }
    If (IncorrectCount == 1){
        WordsWrongThisSession += 1
    }
}
GuiControl, InputBox:Focus, UserInput
GuiControl, InputBox:, UserInput,
return

GetWordsList(ByRef List, WindowTitle:="Waiting for Input") {
    Global List_Edit
    Gui, NewList:Add, Edit, vList_Edit x2 y2 w396 r4,
    Gui, NewList:Add, Button, gList_OK x1 y63 w199 h30, &OK
    Gui, NewList:Add, Button, gList_Cancel x200 y63 w199 h30, &Cancel
    Gui, NewList:Show, h94 w400, %WindowTitle%
    Goto, List_Wait
    List_OK:
    GuiControlGet, List_Edit
    List_Cancel:
GuiEscape:
    ReturnNow := True
    List_Wait:
    While (!ReturnNow)
    Sleep, 100
    Gui, NewList:Destroy
    List := List_Edit
    Return 
}

SayThis(SomeString:="No text", Voice:="SAPI.SpVoice"){
    ComObjCreate(Voice).speak(SomeString)
    Return
}

Esc::ExitApp

Space::
{
    Control, Check, , Button1, %UniqueIdentifier%
    GuiControl, InputBox:Focus, UserInput
    Return
}
Also GEV thanks, your code did work for the simpler version of my code. I used Alibaba's suggestion of using an array because I found it easier to integrate into the rest of my code as I am new to Autohotkey.
Archandrion wrote:
GEV wrote:Maybe something like this

Code: Select all

Wordlist = 
(
Abacus
Abandon
Abidance
)

Loop, parse, WordList, `n
	MaxIndex := A_Index

Gui, Add, Button, x172 y119 w100 h30 Default gConfirmBtn, OK
Gui, Add, Edit, x32 y49 w360 h20 vUserInput,
Gui, Show, x448 y309 h209 w442, New GUI Window
GuiControl, Focus, Edit1

SetTimer, TypeTheWord, 500 
Return

GuiClose:
ExitApp

TypeTheWord:
n++
Loop, parse, WordList, `n
{
	If (A_Index = n)
	{
		TheCurrentWord := A_LoopField
		Gui, 2: -Caption
		Gui, 2: Font, s11, Cambria bold
		Gui, 2: Add, Text,, Type The word "%A_LoopField%"
		Gui, 2: Show, NoActivate x530 y270
		GuiControl, Focus, Edit1
		SetTimer, TypeTheWord, off 
	}
}
Return

ConfirmBtn:
Gui, 2: destroy
Gui, Submit, NoHide
If (TheCurrentWord == UserInput)
	MsgBox, Correct
else
	MsgBox, Incorrect
GuiControl, Focus, Edit1
SendInput, +{Home}{Del}
If (n < MaxIndex)
	SetTimer, TypeTheWord, on
else
	MsgBox END
return

Esc::ExitApp
?
This works for displaying the current word but it is also supposed to allow the user to retype the word until they get it right rather than move on to the next word.
Last edited by Archandrion on 09 Jul 2018, 22:12, edited 1 time in total.
Alibaba
Posts: 479
Joined: 29 Sep 2013, 16:15
Location: Germany

Re: Looping Through A list and verifying GUI Input

09 Jul 2018, 19:54

Well, optimization is always possible. :D
First I would recommend further use of functions to structure the code. If you take a look at your program, you will see that it consists of logical units. These units are for example:
  • Updating the GUI based on a specific state
  • Letting the TTS engine say something (which is already nicely encapsulated by your SayThis Function)
  • Bulding a summary
  • ...
Functions can help to keep the code nicely structured and readable. They can prevent it from growing into a big spaghetti blob when adding further and further features. Try to find the parts of your code where command sequences are double, maybe even copy pasted. In my opinion if you see a lot of repeating code, it is the best indicator that you are looking at a logical unit that should be wrapped into a function. I you haven't already, also take a look at: https://autohotkey.com/docs/Functions.htm. I know it's quite a book, but one of the most important tools. If you need some help with identifying appropriate units and building functions from them, just let me know i will happily assist you.

I see that you already structured some parts of your code into functions, e.g. with GetWordsList(...), but there are some rules of thumb when working with functions. Try to always always stay away from Goto (this isn't even a rule specific to functions), and don't use labels inside of functions, since it breaks the code structure and is widely seen as bad style. Generally, if you are using a label, you can always use a function instead (except for button routines etc.)

If possible, don't carry out busy waiting --> While (!ReturnNow)
In your script you are doing a busy wait for the user to escape the window. You could instead just "Return" and let the escape event callback "GuiEscape:" handle this situation directly.

Regarding the lag which is introduced by the TTS: "ComObjCreate(Voice).speak(SomeString)" is the culprit which will generate and output audio from the passed string and all the while "blocking" the thread from continuing, even freezing the GUI. The main problem is, that your script runs as a single thread application. A solution to this would be to take the TTS call out into a separate thread. Audio Applications for example always maintain a separate message thread and audio thread to prevent blocking the user interaction. Multithreading in AHK is possible in principle, but for "real" Multithreading you would need to use AutoHotkey_H. Alternatively you can split your program into 2 parallel running scripts and have "pseudo" threads. If you are interested in this, let me know, i can assist.

If you have any questions, feel free to ask. ;)

I hope this helps!
Alibaba
"Nothing is quieter than a loaded gun." - Heinrich Heine
Archandrion
Posts: 31
Joined: 26 May 2018, 22:23

Re: Looping Through A list and verifying GUI Input

09 Jul 2018, 22:17

Alibaba wrote: Alternatively you can split your program into 2 parallel running scripts and have "pseudo" threads. If you are interested in this, let me know, i can assist.
So a pseudo-thread would mean calling another script from the commandline? eg:

Code: Select all

SayThis(SomeString:="No text", Voice:="SAPI.SpVoice"){
SayThisFunctionText =
(
    ComObjCreate("%Voice%").speak("%SomeString%")
    Exitapp
)
FileDelete, %A_ScriptDir%\SayThis.ahk
FileAppend, %SayThisFunctionText%,%A_ScriptDir%\SayThis.ahk
RunWait, %A_AhkPath% "%A_ScriptDir%\SayThis.ahk"
FileDelete, %A_ScriptDir%\SayThis.ahk
Return
}
I've updated the code below with the above function and it was indeed faster. Is there any way to do this without writing a file to the hard drive, using a stream or virtual file in ram or maybe passing the function text directly to an AutoHotkey DLL?

Code: Select all

;SpellingTutor



UniqueIdentifier := "Spelling Tutor " . A_TickCount

GetWordsList(WordsList, "Paste or Type in a list of words and press Enter or click OK")
WordsWrongThisSession := 0
WordsCorrectThisSession := 0
WordCount := 0

; Transform your linebreak-separated list into array:
WordArray := []
Loop, Parse, Wordslist, `n
WordArray[A_Index - 1] := A_LoopField

Gui, InputBox:Add, Edit, x22 y69 w360 h20 vUserInput,
Gui, InputBox:Add, Button, x22 y29 w100 h30 gSayWordBtn, Say Word
Gui, InputBox:Add, Button, x322 y109 w100 h30 gCheckSpellingBtn Default, Check Spelling
Gui, InputBox:Add, Button, x22 y149 w100 h30 gShowWordBtn, Show Word
Gui, InputBox:Add, Text, x23 y109 w290 h30 vSpellCheckMessage, Enter The Word You Heard
Gui, InputBox:Add, Text, x22 y189 w360 h30 vCorrectSpelling,
hIcon := LoadPicture("imageres.dll", "w32 Icon182", _)
Gui, InputBox:Add, Picture, x392 y69 w20 h20 vIconPic, % "HICON:" hIcon
Gui, InputBox:Show, x380 y312 h243 w440, %UniqueIdentifier%
GuiControl, InputBox:Focus, UserInput

; Adress words by their index:
currentWordIndex := 0
IncorrectCount := 0
Retry := 0
GuiControl, InputBox:Focus, UserInput
GuiControl, InputBox:, UserInput,
Narration := "Type the word" . WordArray[currentWordIndex]
SayThis(Narration)
return

InputBoxGuiClose:
FileDelete, %A_ScriptDir%\SayThis.ahk
ExitApp

SayWordBtn:
{
    SyCurrWord := WordArray[currentWordIndex]
    SayThis(SyCurrWord)
}
Return


ShowWordBtn:
{
    GuiControl, InputBox:, CorrectSpelling,% WordArray[currentWordIndex]
}
Return

CheckSpellingBtn:
Gui, InputBox:Submit, NoHide
If (WordArray[currentWordIndex] == UserInput)
{
    hIcon := LoadPicture("imageres.dll", "w32 Icon233", _)
    GuiControl, InputBox:, IconPic, % "HICON:" hIcon
    GuiControl, InputBox:, SpellCheckMessage, That is correct
    GuiControl, InputBox:, UserInput,
    GuiControl, InputBox:, CorrectSpelling,
    SayThis("That is correct")
    If (Retry == 0){
        WordsCorrectOnFirstTry += 1
    }
    WordCount += 1
    currentWordIndex++ ; next word
    Retry := 0
    IncorrectCount := 0
    ; see how arrays make your life easy
    If (currentWordIndex > WordArray.MaxIndex())
    {
        Summary_str = 
        (
        You spelt %WordsCorrectOnFirstTry% correctly on your first try.
        There were a total of %WordCount% words in this session and you had problems with %WordsWrongThisSession% of them.
        You can find a list of the words you had the most difficulty with in %A_ScriptDir%\ProblematicWords\ProblematicWordsList.txt
        )

        MsgBox,, Summary, %Summary_str%
        ExitApp
    } Else{
        GuiControl, InputBox:, UserInput,
        Narration := "Type the word" . WordArray[currentWordIndex]
        SayThis(Narration)
    }
} else {
    GuiControl, InputBox:, UserInput,
    SayThis("Spelling is incorrect. Try again")
    Retry := 1
    GuiControl, InputBox:, SpellCheckMessage, Spelling is incorrect. Try again
    hIcon := LoadPicture("imageres.dll", "w32 Icon230", _)
    GuiControl, InputBox:, IconPic, % "HICON:" hIcon
    IncorrectCount += 1
    If (IncorrectCount >= 3){
        CurrWrd := WordArray[currentWordIndex]
        GuiControl, InputBox:, CorrectSpelling, % "Type the word " . WordArray[currentWordIndex]
        FileCreateDir, %A_ScriptDir%\ProblematicWords
        FileAppend, %CurrWrd%`r`n, %A_ScriptDir%\ProblematicWords\ProblematicWordsList.txt
    }
    If (IncorrectCount == 1){
        WordsWrongThisSession += 1
    }
}
GuiControl, InputBox:Focus, UserInput
return

GetWordsList(ByRef List, WindowTitle:="Waiting for Input") {
    Global List_Edit
    Gui, NewList:Add, Edit, vList_Edit x2 y2 w396 r4,
    Gui, NewList:Add, Button, gList_OK x1 y63 w199 h30, &OK
    Gui, NewList:Add, Button, gList_Cancel x200 y63 w199 h30, &Cancel
    Gui, NewList:Show, h94 w400, %WindowTitle%
    Goto, List_Wait
    List_OK:
    GuiControlGet, List_Edit
    List_Cancel:
GuiEscape:
FileDelete, %A_ScriptDir%\SayThis.ahk
    ReturnNow := True
    List_Wait:
    While (!ReturnNow)
    Sleep, 100
    Gui, NewList:Destroy
    List := List_Edit
    Return 
}

SayThis(SomeString:="No text", Voice:="SAPI.SpVoice"){

SayThisFunctionText =
(
ComObjCreate("%Voice%").speak("%SomeString%")
Exitapp
)
FileDelete, %A_ScriptDir%\SayThis.ahk
FileAppend, %SayThisFunctionText%,%A_ScriptDir%\SayThis.ahk
RunWait, %A_AhkPath% "%A_ScriptDir%\SayThis.ahk"
FileDelete, %A_ScriptDir%\SayThis.ahk
Return
}

;SayThis(SomeString:="No text", Voice:="SAPI.SpVoice"){
;    ComObjCreate(Voice).speak(SomeString)
;    Return
;}

Esc::
ExitApp
FileDelete, %A_ScriptDir%\SayThis.ahk

Space::
{
    Control, Check, , Button1, %UniqueIdentifier%
    GuiControl, InputBox:Focus, UserInput
    Return
}
Regarding the:
Alibaba wrote:If possible, don't carry out busy waiting --> While (!ReturnNow)
I am unsure as to how to make this correction. I've tried putting a return but it did not work as expected could you show me an example and maybe elaborate a little.
GEV
Posts: 791
Joined: 25 Feb 2014, 00:50

Re: Looping Through A list and verifying GUI Input

10 Jul 2018, 00:48

@Alibaba,
welcome back, Großmeister!
Nice to hear from you again!
You're a great enrichment and true asset to AHK forum.
We wish you all the best and we hope you will stay with us for a very long time to come!
Alibaba
Posts: 479
Joined: 29 Sep 2013, 16:15
Location: Germany

Re: Looping Through A list and verifying GUI Input

10 Jul 2018, 06:35

Archandrion wrote: So a pseudo-thread would mean calling another script from the commandline?

I've updated the code below with the above function and it was indeed faster. Is there any way to do this without writing a file to the hard drive, using a stream or virtual file in ram or maybe passing the function text directly to an AutoHotkey DLL?
Yes you don't need to create a file. Of course your current implementation is a good start, but please also notice that for your purpose you should not use "RunWait" since this will also block your main thread from executing until the execution of your TTS is done.
On the bottom of the Run command documentation, there is a very nice example how you can run code dynamically by using a shell object. This way, you don't have to worry about creating and deleting files and you keep your working directory clean. I took the example function and built a little pseudo thread implementation with it:

Code: Select all

SayThis(SomeString:="No text", Voice:="SAPI.SpVoice"){
	;--- New Pseudo Thread: ---
	thread =
	(
		ComObjCreate("%Voice%").speak("%SomeString%")
		; there can be more code here "inside the thread"
		; but make sure that everything is escaped correctly
		; important: you cant use expressions here!
	)
	runThread(thread)
	;--- End of Pseudo Thread ---
}

; you need this function, which is derived from the documentation example:
runThread(threadCode)
{
    shell := ComObjCreate("WScript.Shell")
    exec := shell.Exec("AutoHotkey.exe /ErrorStdOut *")
    exec.StdIn.Write(threadCode)
    exec.StdIn.Close()
}
Archandrion wrote: Regarding the:
Alibaba wrote:If possible, don't carry out busy waiting --> While (!ReturnNow)
I am unsure as to how to make this correction. I've tried putting a return but it did not work as expected could you show me an example and maybe elaborate a little.
Yes of course. What i meant goes hand in hand with structuring your code into functions. Currently, the execution of your script just runs down the file wildly and you use heavy weight busy waiting to prevent it from "overtaking" your word list colleting routine. Try putting a return after this line:

Code: Select all

GetWordsList(WordsList, "Paste or Type in a list of words and press Enter or click OK")
What will happen after the function finishes? Nothing, because the script returned and will be in an idle state. If you encapsulated the initialisation of your GUI "InputBox", the spell checking, the narrating, ... into separate functions, you could just call them from there after your GetWordsList(...). Thereby you get a nice readable sequence of logical steps, while the implementation itself is hidden inside the function bodys. Just like the famous quote goes: "divide et impera"

I hope you understand what i mean :)
"Nothing is quieter than a loaded gun." - Heinrich Heine
Alibaba
Posts: 479
Joined: 29 Sep 2013, 16:15
Location: Germany

Re: Looping Through A list and verifying GUI Input

10 Jul 2018, 06:38

GEV wrote:@Alibaba,
welcome back, Großmeister!
Nice to hear from you again!
You're a great enrichment and true asset to AHK forum.
We wish you all the best and we hope you will stay with us for a very long time to come!
You are honoring me! Well university, job and family were surely consuming my time >100% :D
But thank you very much for the nice and warm welcome after all this time!
"Nothing is quieter than a loaded gun." - Heinrich Heine
Archandrion
Posts: 31
Joined: 26 May 2018, 22:23

Re: Looping Through A list and verifying GUI Input

10 Jul 2018, 08:16

Alibaba wrote: ; you need this function, which is derived from the documentation example:
runThread(threadCode)
{
shell := ComObjCreate("WScript.Shell")
exec := shell.Exec("AutoHotkey.exe /ErrorStdOut *")
exec.StdIn.Write(threadCode)
exec.StdIn.Close()
}
[/code]
It worked although I had to change exec := shell.Exec("AutoHotkey.exe /ErrorStdOut *") to exec := shell.Exec("AutoHotkeyU64.exe /ErrorStdOut *"). Regarding the the GetWordsList function I tried encapsulating all the spelling practice code and changing the code inside GetWordsList but was unsuccessful without using WinWaitClose. As well encapsulating the spelling practice code into a function caused the script to fail to accept the word as correct even if it was spelt correctly. Below is the modified code.

Code: Select all

GetWordsList(WordsList)
PracticWords(WordsList)

PracticWords(WordsList){
WordArray := ListToArray(WordsList)
;Declare Variables
WordsWrongThisSession := 0
WordsCorrectThisSession := 0
WordCount := 0
currentWordIndex := 0
IncorrectCount := 0
Retry := 0
Global UserInput
Global SpellCheckMessage
Global CorrectSpelling
Global IconPic


;DrawGui
    UniqueIdentifier := "Spelling Tutor " . A_TickCount
    Gui, InputBox:Add, Edit, x22 y69 w360 h20 vUserInput,
    Gui, InputBox:Add, Button, x22 y29 w100 h30 gSayWordBtn, Say Word
    Gui, InputBox:Add, Button, x322 y109 w100 h30 gCheckSpellingBtn Default, Check Spelling
    Gui, InputBox:Add, Button, x22 y149 w100 h30 gShowWordBtn, Show Word
    Gui, InputBox:Add, Text, x23 y109 w290 h30 vSpellCheckMessage, Enter The Word You Heard
    Gui, InputBox:Add, Text, x22 y189 w360 h30 vCorrectSpelling,
    hIcon := LoadPicture("imageres.dll", "w32 Icon182", _)
    Gui, InputBox:Add, Picture, x392 y69 w20 h20 vIconPic, % "HICON:" hIcon
    Gui, InputBox:Show, x380 y312 h243 w440, %UniqueIdentifier%
    GuiControl, InputBox:Focus, UserInput
    GuiControl, InputBox:, UserInput,
    Narration := "Type the word" . WordArray[currentWordIndex]
    SayThis(Narration)
    Return
    

    ;Buttons
    InputBoxGuiClose:
    {
        ExitApp
    }

SayWordBtn:
    {
        SyCurrWord := WordArray[currentWordIndex]
        SayThis(SyCurrWord)
    }
    Return


ShowWordBtn:
    {
        GuiControl, InputBox:, CorrectSpelling,% WordArray[currentWordIndex]
    }
    Return


CheckSpellingBtn:
{
Gui, InputBox:Submit, NoHide
If (WordArray[currentWordIndex] == UserInput){
    AnswerCorrect()
    
          If (currentWordIndex > WordArray.MaxIndex()){
          ProvideSummary(WordsCorrectThisSession,WordsWrongThisSession,WordCount)
          }Else{
          ;MoveOnToNextWord
           WordCount += 1
           currentWordIndex++
           ;Reset Retry Count
           If (Retry == 0){
              WordsCorrectOnFirstTry += 1
              }
           Retry := 0
           }
           
    }Else{
    AnswerInCorrect()
    Retry += 1
    If (Retry == 1){
    WordsWrongThisSession += 1
                   }
    If (Retry >= 3){
                     CurrWrd := WordArray[currentWordIndex]
                     GuiControl, InputBox:, CorrectSpelling, % "Type the word " . WordArray[currentWordIndex]
                     FileCreateDir, %A_ScriptDir%\ProblematicWords
                     FileAppend, %CurrWrd%`r`n, %A_ScriptDir%\ProblematicWords\ProblematicWordsList.txt
                   }
    }
}
Return

Return
}



SayThis(SomeString:="No text", Voice:="SAPI.SpVoice"){
    ;--- New Pseudo Thread: ---
    thread =
    (
    ComObjCreate("%Voice%").speak("%SomeString%")
    ; there can be more code here "inside the thread"
    ; but make sure that everything is escaped correctly
    ; important: you cant use expressions here!
    )
    runThread(thread)
    ;--- End of Pseudo Thread ---
}

; you need this function, which is derived from the documentation example:
runThread(threadCode)
{
    shell := ComObjCreate("WScript.Shell")
    exec := shell.Exec("AutoHotkeyU64.exe /ErrorStdOut *")
    exec.StdIn.Write(threadCode)
    exec.StdIn.Close()
}

GetWordsList(ByRef List, WindowTitle:="Waiting for Input") {
    Global List_Edit
    Gui, NewList:+LastFound
    GuiHWND := WinExist() 
    Gui, NewList:Add, Edit, vList_Edit x2 y2 w396 r4,
    Gui, NewList:Add, Button, gList_OK x1 y63 w199 h30, &OK
    Gui, NewList:Add, Button, gList_Cancel x200 y63 w199 h30, &Cancel
    Gui, NewList:Show, h94 w400, %WindowTitle%
    
    WinWaitClose, ahk_id %GuiHWND%  ;--waiting for gui to close
    Return
    
List_OK:
    {
        Gui, NewList:Submit
        List := List_Edit
        Return 
    }
List_Cancel:
    {
GuiEscape:
        ExitApp
    }
}

ListToArray(WordsList){
    ; Transform your linebreak-separated list into array:
    WordArray := []
    Loop, Parse, Wordslist, `n
    WordArray[A_Index - 1] := A_LoopField
    Return WordArray
}

ProvideSummary(WordsCorrectThisSession,WordsWrongThisSession,WordCount){
            Summary_str = 
            (
            You spelt %WordsCorrectOnFirstTry% correctly on your first try.
            There were a total of %WordCount% words in this session and you had problems with %WordsWrongThisSession% of them.
            You can find a list of the words you had the most difficulty with in %A_ScriptDir%\ProblematicWords\ProblematicWordsList.txt
            )

            MsgBox,, Summary, %Summary_str%
Return
}

AnswerCorrect(){
        GuiControl, InputBox:, UserInput,
        GuiControl, InputBox:Focus, UserInput
        GuiControl, InputBox:, SpellCheckMessage, That is correct
        hIcon := LoadPicture("imageres.dll", "w32 Icon233", _)
        GuiControl, InputBox:, IconPic, % "HICON:" hIcon
        GuiControl, InputBox:, CorrectSpelling,
        SayThis("That is correct")
Return
}

AnswerInCorrect(){
        GuiControl, InputBox:, UserInput,
        GuiControl, InputBox:Focus, UserInput
        GuiControl, InputBox:, SpellCheckMessage, Spelling is incorrect. Try again
        hIcon := LoadPicture("imageres.dll", "w32 Icon230", _)
        GuiControl, InputBox:, IconPic, % "HICON:" hIcon
        SayThis("Spelling is incorrect. Try again")
Return
}

Space::
{
    Control, Check, , Button1, %UniqueIdentifier%
    GuiControl, InputBox:Focus, UserInput
    Return
}

Esc::ExitApp

Return to “Ask For Help”

Who is online

Users browsing this forum: Google [Bot], yarsis and 202 guests