Sign in to follow this  
Followers 0
tonedeaf

Creating/Managing multiple forms in AutoIt3 � Part 1

10 posts in this topic

Creating/Managing multiple forms in AutoIt3 Part 1

When creating multiple forms in AutoIt, there is no provision for creating a custom FORM data type (like in Visual Basic) and there is no way to access the controls on the form using dot (.) notation. For eg. If you have a text box: txtYourName on a form: frmMain, Visual Basic allows you to access the text in the text box using the dot notation:

sName = frmMain.txtYourName

An obvious advantage of having a FORM data type is that its possible to have different forms with same control names. For eg. An OK button called cmdOK could be both on the main window and on any sub window (or child window) in the application.

Moreover, this way ensures that only one variable remains global (the form variable is global even in Visual Basic) and the rest remain local (to be referenced using the form variable).

With AutoIt, we have to think unique names for all the forms, and the controls on the forms. And all controls and form variables are declared Global.

Managing multiple forms, may not be difficult for one or two forms. But with an application having many windows (like GUIBuilder by Cyberslug/LiveWire), the code can become complex to manage.

So for AutoIt programmers facing this issue, Id like this topic to be a discussion for ways to manage multiple forms in AutoIt by implementing a FORM data type.

Solution (Part - I)

Ive seen code posts implementing complex data structures (like binary trees) using arrays in AutoIt.

Since AutoIt doesnt support user defined data types, arrays are the closest (besides DllStructs) when it comes to defining custom data types.

Implementing a FORM Datatype using Arrays:

1) Design form using KODA FormDesigner (by Lookfar/Lazycat) or GUIBuilder (by Cyberslug/LiveWire)

Posted Image

2) Determine Form and Control variable names

Posted Image

3) Convert the code generated by FormDesigner/GUIBuilder into an array mimicking a FORM data type.

FormDesigner generated code:

#include <GUIConstants.au3>

$frmResetPassword = GUICreate("Reset Password", 318, 111, 390, 279)
GUICtrlCreateLabel("New passsword:", 11, 13, 131, 13)
$txtNewPwd = GUICtrlCreateInput("", 143, 11, 165, 23)
GUICtrlCreateLabel("Confirm new passsword:", 11, 13, 131, 13)
$txtConfirmPwd = GUICtrlCreateInput("", 143, 41, 165, 23)
$cmdOK = GUICtrlCreateButton("OK", 152, 75, 75, 23)
$cmdCancel = GUICtrlCreateButton("Cancel", 233, 75, 75, 23)

GUISetState(@SW_SHOW)

While 1
    $msg = GuiGetMsg()
    Select
    Case $msg = $GUI_EVENT_CLOSE
        ExitLoop
    Case Else
    ;;;;;;;
    EndSelect
Wend

Exit

Converted code representing a FORM data type using an array:

#include <GUIConstants.au3>

Global $afrmResetPassword

CreateForm_frmResetPassword($afrmResetPassword)

GUISetState(@SW_SHOW, $afrmResetPassword[0][1])

While 1
    $msg = GuiGetMsg()
    Select
    Case $msg = $GUI_EVENT_CLOSE
        ExitLoop
    Case Else
    ;;;;;;;
    EndSelect
Wend

Exit


Func CreateForm_frmResetPassword(ByRef $afrmNew)
    Dim $afrmNew[5][2]

    $afrmNew[0][0] = "$frmResetPassword"
    $afrmNew[0][1] = GUICreate("Reset Password", 318, 111, 390, 279)
    
    GUICtrlCreateLabel("New passsword:", 11, 13, 131, 13)
    $afrmNew[1][0] = "$txtNewPwd"
    $afrmNew[1][1] = GUICtrlCreateInput("", 143, 11, 165, 23)
    GUICtrlCreateLabel("Confirm new passsword:", 11, 44, 131, 13)
    $afrmNew[2][0] = "$txtConfirmPwd"
    $afrmNew[2][1] = GUICtrlCreateInput("", 143, 41, 165, 23)

    $afrmNew[3][0] = "$cmdOK"
    $afrmNew[3][1] = GUICtrlCreateButton("OK", 152, 75, 75, 23)
    $afrmNew[4][0] = "$cmdCancel"
    $afrmNew[4][1] = GUICtrlCreateButton("Cancel", 233, 75, 75, 23)
EndFunc

The FORM array[][0] stores the text representation of the controls internal name and array[][1] stores the handle returned by the _GuiCtrlCreate function for the control. This FORM array[][] also stores the form handle which is always stored as the first array element

Array[0][0] , stores the form internal name

Array[0][1], stores the form handle.

Notice that for controls we do not want to manipulate in code, we skip storing them in the FORM array.

Whenever we want to refer to the form handle we, refer to $afrmResetPassword[0][1] as the form variable is always stored as the first element in the array. This would get a form up and running, but this approach is not useful for referring controls on the form as fixing array positions for controls would make the code very difficult to read.

Eg. GUICtrlSetData($afrmResetPassword[1][1], "!1234ABCD#$") would not be intitutive enough to imply that were assigning a value to $txtNewPwd.

So we write another function to take advantage of control variable names stored in array[][0] elements.

The function _GetCtrl() takes the following parameters:

Parameter 1: Text representation of the control name

Parameter 2: FORM array to search the control in.

Return Value:

Success: Returns the handle for the control

Failure: If the control was not found in the FORM array, displays a Critical error message box and ends program.

Func _GetCtrl($sCtrlName, $afrmArray)
    For $i = 0 to UBound($afrmArray, 1) - 1
        If $afrmArray[$i][0] = $sCtrlName Then
            Return $afrmArray[$i][1]
        EndIf
    Next
    
; Error Handler
    If $i > UBound($afrmArray, 1) - 1 Then
        Application_ErrorHandler("Control reference invalid ", _
                                "Control Name: " & $sCtrlName  & @CRLF & _
                                "Form Name: " & $afrmArray[0][0], "Fatal")
        SetError(1)
    EndIf
EndFunc

Application_ErrorHandler() function (Creates the Message Box)

Func Application_ErrorHandler($sError, $sSolution, $sImpact)
    If $sImpact = "Non-Fatal" Then
        MsgBox(8256, "Reset Password", $sError & @CRLF & @CRLF & $sSolution)
    ElseIf $sImpact = "Fatal" Then  
        MsgBox(8208, "Reset Password", $sError & @CRLF & @CRLF & $sSolution)
        Exit
    EndIf
EndFunc

So, if we now assign a value to $txtNewPwd, the new syntax would look like:

GUICtrlSetData(_GetCtrl("$txtNewPwd, $afrmResetPassword), "!1234ABCD#$")

Although the syntax is more lengthy for a simple assignment, the benefits of this approach begin to pay off as we start coding multiple forms.

We make the Cancel button functional by using this approach:

While 1
    $msg = GuiGetMsg()
    Select
    Case $msg = $GUI_EVENT_CLOSE
        ExitLoop
    Case $msg = _GetCtrl("$cmdCancel", $afrmResetPassword)
        ExitLoop
    Case Else
    ;;;;;;;
    EndSelect
Wend

The finished code would look like:

#include <GUIConstants.au3>

Global $afrmResetPassword

CreateForm_frmResetPassword($afrmResetPassword)

GUISetState(@SW_SHOW, $afrmResetPassword[0][1])

While 1
    $msg = GuiGetMsg()
    Select
    Case $msg = $GUI_EVENT_CLOSE
        ExitLoop
    Case $msg = _GetCtrl("$cmdCancel", $afrmResetPassword)
        ExitLoop
    Case Else
    ;;;;;;;
    EndSelect
Wend

Exit

Func _GetCtrl($sCtrlName, $afrmArray)
    For $i = 0 to UBound($afrmArray, 1) - 1
        If $afrmArray[$i][0] = $sCtrlName Then
            Return $afrmArray[$i][1]
        EndIf
    Next
    
; Error Handler
    If $i > UBound($afrmArray, 1) - 1 Then
        Application_ErrorHandler("Control reference invalid ", _
                                "Control Name: " & $sCtrlName  & @CRLF & _
                                "Form Name: " & $afrmArray[0][0], "Fatal")
        SetError(1)
    EndIf
EndFunc

; Application wide central message handler.
; Displays an error msg and exits the application if the error is fatal.
Func Application_ErrorHandler($sError, $sSolution, $sImpact)
    If $sImpact = "Non-Fatal" Then
        MsgBox(8256, "Reset Password", $sError & @CRLF & @CRLF & $sSolution)
    ElseIf $sImpact = "Fatal" Then  
        MsgBox(8208, "Reset Password", $sError & @CRLF & @CRLF & $sSolution)
        Exit
    EndIf
EndFunc


Func CreateForm_frmResetPassword(ByRef $afrmNew)
    Dim $afrmNew[5][2]

    $afrmNew[0][0] = "$frmResetPassword"
    $afrmNew[0][1] = GUICreate("Reset Password", 318, 111, 390, 279)
    
    GUICtrlCreateLabel("New passsword:", 11, 13, 131, 13)
    $afrmNew[1][0] = "$txtNewPwd"
    $afrmNew[1][1] = GUICtrlCreateInput("", 143, 11, 165, 23)
    GUICtrlCreateLabel("Confirm new passsword:", 11, 44, 131, 13)
    $afrmNew[2][0] = "$txtConfirmPwd"
    $afrmNew[2][1] = GUICtrlCreateInput("", 143, 41, 165, 23)

    $afrmNew[3][0] = "$cmdOK"
    $afrmNew[3][1] = GUICtrlCreateButton("OK", 152, 75, 75, 23)
    $afrmNew[4][0] = "$cmdCancel"
    $afrmNew[4][1] = GUICtrlCreateButton("Cancel", 233, 75, 75, 23)
EndFunc

In the next topic Ill discuss ways to separate the code for multiple forms in separate .au3 files and trapping control events.

Any comments/suggestions are welcome.

Share this post


Link to post
Share on other sites



why would you want to make something so complex ?

what is wrong with a normal variable (like that comes uit GUIbuilder)

maybe you should read his post.

Share this post


Link to post
Share on other sites

@w0uter

why would you want to make something so complex ?

what is wrong with a normal variable (like that comes uit GUIbuilder)

Its not really complex, but its more involved than using the normal methords.

The problem with normal GUI creation methords is that after a while it becomes increasing difficult to manage multiple forms. As soon as you exceed two-three forms in your application and the no. of controls increase, it becomes more than apparent that you need some way of implementing a data structure containing the controls for a form.

It may not be useful for a program with only one/two forms. But I'm targeting AutoIt programs which can have any number of forms and it would not be complex to manage them.

Share this post


Link to post
Share on other sites

couple of Q's

1. how to reuse the same gui for multiple instance eg. multiple download window, separate personal chat window etc.

2. how to make the gui skinnable (covers all the forms created)


i think

Share this post


Link to post
Share on other sites

This is magic. What I am wondering is why there is so little activity on this thread.  I think this should be included as an advanced tutorial topic.

Are there other Help topics dealing with more than basics? And showing how to split a project into multiple include files?  This deals with so many different concepts all in one place.

Thank you.

 
 
 

Skysnake

Why is the snake in the sky?

Share this post


Link to post
Share on other sites

skysnake, why would you resurrect a 9 year old thread rather than starting a new one?! The language has grown just a touch since 2005; what worked then may not do so now.


√-1 2^3 ∑ π, and it was delicious!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0