Write Less Code with Database Driven Apps (AutoHotkey Script Design)

Use Simple Database Files to Both Write AutoHotkey Code and Create Flexible Scripts

GraphicSoundsIn the last blog, I introduced a simple AutoHotkey app I call PictureSounds.ahk. When the user clicks on an image, AutoHotkey seeks the name of the sound file in an INI lookup table, then plays it. The script uses the image file name as the INI file Key. After loading a series of images, the script plays a different sound for each image. (It even plays videos!)

Using the INI file as a lookup table saved me from writing a different subroutine (or at least If condition) for each Picture control in the GUI window. Now, I show how to use that same data file to write the command code lines for adding the images to the pop-up files.

You’ll find plenty of useful things to do in AutoHotkey without ever accessing separate files, but, when properly implemented, using database files to save and recall values makes everything easier.

The following script adds images to a GUI window, then uses the image filename as an INI file lookup Key for finding and playing the associated sound file:

Gui, Add, Picture, x50 w50 h-1 section gCtrlEvent
   , Dancing Dogs Small.jpg
Gui, Add, Picture, w50 h-1 gCtrlEvent
   , Cheese Burger Small.jpg
Gui, Add, Picture, w50 h-1 gCtrlEvent
   , Phone Small.jpg
Gui, Show, w150

CtrlEvent(CtrlHwnd, GuiEvent, EventInfo, ErrLevel:="")
{
  GuiControlGet, OutputVar , , %CtrlHwnd%,
; MouseGetPos, , , , OutputVar  ; Alternative command
  IniRead, Sound, PictureSounds.ini, Sounds, %OutputVar%
  SoundPlay, %Sound%
}

The script uses a special built-in function called CtrlEvent() which replaces the gLabel whenever no subroutine by the same name exists.

CtrlEvent(CtrlHwnd, GuiEvent, EventInfo, ErrLevel:="")

V2.0 Note: This function does not exist in AutoHotkey V2.0 but the alternative MouseGetPos command/function works just as well for identifying the clicked GUI control in both versions of AutoHotkey:

MouseGetPos, OutputVarX, OutputVarY
        , OutputVarWin, OutputVarControl, 1|2|3

Since the special CtrlEvent() function does not work in AutoHotkey V2.0, you may prefer to use the MouseGetPos command.

Library Benefits

To switch to the MouseGetPos command, remove the semicolon from the front of that command line and insert it before the GuiControlGet command line. (V2.0 Tip: The GuiControlGet command disappears in V2.0 in favor of the ControlGetText function.)

While on its own this script demonstrates a productive (and expandable) use for a database file, I realized that I could further streamline the script and eliminate even more code by using the same database to write the individual GUI Picture control lines.

Writing Database Driven Code

Although I can add more sounds to the current version of the script by merely appending them to the INI file, each new corresponding image requires another line of code in the script. Since I must add the image filename at the same time as the sound filename, it only makes sense to use the same INI file to write the Gui, Add, Picture command statements. However, the IniRead command won’t help to write the code.

In this case, we use the INI lookup table as a regular text file—ignoring the INI structure. To sequentially insert the new Gui, Add, Picture commands, we must read the file line-by-line. We use the Loop (read file contents) command since that operation reads the file one line at a time:

Loop, Read, InputFile , [OutputFile]

The Loop (read file contents) command doesn’t care that the lookup table uses the INI file format. It treats the INI file the same as it would any other text file reading it one line at a time. Then the StringSplit command gives us a method for extracting the image filename:

Loop, Read, PictureSounds.ini
{
  If A_Index = 1   ; Skip the first Section name "[Sounds]"
    Continue
  StringSplit, picture_array, A_LoopReadLine, = 
  Gui, Add, Picture, x50 w50 h-1 gCtrlEvent, %picture_array1%
}
Gui, show, w150

CtrlEvent(CtrlHwnd, GuiEvent, EventInfo, ErrLevel:="")
{
  GuiControlGet, OutputVar , , %CtrlHwnd%,
  IniRead, Sound, PictureSounds.ini, Sounds, %OutputVar%
  SoundPlay, %Sound%
}

Since the first line of the INI file includes the Section name [Sounds], AutoHotkey skips to the next line using the Continue command. In the order they appear in the INI file, the StringSplit command parses out the image filename using the equals sign (=) as a delimiter, then the script inserts the variable picture_array1 (the image filename) into the Gui, Add, Picture command lines.

The variable A_Index tracks each Loop iteration while reading through the file. After identifying the first loop (A_Index = 1), the Continue command skips to the second iteration (or line). The variable A_LoopReadLine contains the entire text associated with the A_Index line.

Now the user can continue adding Picture/Sound combinations to the INI file without making any changes to the PictureSound.ahk script. Reloading the script accounts for any recent changes to the INI file. (The primary problem arises when the INI file includes so many pairs that the GUI window expands off the screen. We’ll save that problem for another time.)

Switching to the StrSplit() Function

Looking at the SplitString command documentation, you’ll note that it deprecates the command in favor of the StrSplit() function.

StrSplit

Since AutoHotkey V1.1 includes both the command and the new function, I recommend using the function. However, the function uses simple arrays while the deprecated command uses pseudo arrays. While both the command and function work in V1.1, using the function prepares the script for translation to V2.0 (if you decide to ever make the change) and helps familiarize you with how array objects work in place of pseudo-arrays.

Pseudo-arrays create variables by appending a sequential number to the end of the array name (Array%A_Index%). Simple arrays use the array object format which encloses the index number inside square brackets (Array[A_Index]).

In AutoHotkey V1.1, the primary problem you’ll encounter when inserting simple arrays concerns variable evaluation. You can’t evaluate one by enclosing it in percent signs (%Array[A_Index]%). That generates an illegal character error. You must either store the array item to a legal variable or use a % forced expression:

Image := picture_array[1]
Gui, Add, Picture, x50 w50 h-1 gCtrlEvent, %Image%

or

Gui, Add, Picture, x50 w50 h-1 gCtrlEvent, % picture_array[1]

The following example script uses the StrSplit() function:

Loop, Read, PictureSounds.ini
{
  If A_Index = 1
    Continue
  picture_array := StrSplit(A_LoopReadLine, "=") 
  Gui, Add, Picture, x50 w50 h-1 gFunctionTest, % picture_array[1]
}
Gui, show, w150

FunctionTest(CtrlHwnd, GuiEvent, EventInfo, ErrLevel:="")
{
  GuiControlGet, OutputVar , , %CtrlHwnd%,
  IniRead, Sound, PictureSounds.ini, Sounds, %OutputVar%
  SoundPlay, %Sound%
}

By using the INI lookup table to both write the Gui, Add, Picture lines of code and activate the associated sound files, we’ve limited the script to 14 lines of code. To change or add the images or sounds, we merely edit the INI file with any text editor and reload the script.

For the parallel AutoHotkey V2.0 code, see the PictureSounds.ahk2 reference on the Free AutoHotkey Scripts page.

In the future, I plan to demonstrate techniques for preventing the GUI from expanding beyond the edge of the screen after adding numerous entries to the INI lookup table (and, therefore, to the GUI pop-up).

jack

This post was proofread by Grammarly
(Any other mistakes are all mine.)

(Full disclosure: If you sign up for a free Grammarly account, I get 20¢. I use the spelling/grammar checking service all the time, but, then again, I write a lot more than most people. I recommend Grammarly because it works and it’s free.)

 

 

 

 

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s