Jump to content

Input Masks "on-the-fly"


Manimal
 Share

Recommended Posts

Motivation
 
   Hi guys.
 
   Coming from a more commercial environment, always seen with difficulty the fact of AutoIt (and other languages) do not accept masks during data entry.
 
   Of course, we have the EDIT fields (GUICtrlCreateInput), but normally are open fields that accept any kind of information. I noticed here and there some attempts to filter or restrict a few characters, allow others and so on. But always there was the need to customize the script for every situation, what I think is a waste of time and code. And especially the fact that prevent multiple programmers to present a more professional aspect to your work, because it required additional programming and knowledge.
 
   Understand that when we talk about business systems, data entry is very significant because the amount of data to be entered is too large. When we carry out this data entry, any interaction between keyboard and mouse delays the release of information. Think of how much time is lost between take your hand from the keyboard, get the mouse, find the cursor, click on the option (or options), click on the next field and finally back to the keyboard. It may seem silly, but this process repeated several times a day makes a huge difference. The solution is not to "eliminate" the mouse, but minimize his use, maximizing the keyboard as a whole, in the process of data entry!
 
   Another situation that bothers me is the fact that every control you need in addition to the initial and final coordinates, size and width of the field (that annoying thing be adjusting this). And not to mention that this "width" and "height" are still dependent on the font used. Sometimes, you adjust the size with a font, then decides to change (increase, decrease, put bold, etc) and there goes the entire service to the trash.
 
   At least when you create a label you can skip these steps, yet few know that. I spent a lot of time doing screens and stating the size and height of the label when not needed. As the Help is optional, but the examples and other codes always reported those numbers and I went along for the ride... Recently I had to change the font of a screen ever assembled and truncated all! I thought to myself that it couldn't be that hard and I was looking for, then I found out that the size and width of the label are optional, that is, it adjusts according to the font used at the time of opening the window.
 
   But if on the one hand eased enough to create the labels, the same cannot be said for the inputs. Theoretically (on the Help) you can leave blank these options too but the result is awful!
   Slowly was assembling a pattern in my head. Ideally, that formatting might follow some rules:
 
  1. be optional (use who wants and when it wants to) not in the way the other fields, including to allow retro-compatibility
  2. being optional, you can use only for fields that you need or require additional formatting
  3. need to adjust the size of the field to the chosen font (and its characteristics such as size, bold, height, etc)
  4. finally, allow creating a system of masks that were easily used (little training but highly flexible)
  5. format "on-the-fly", i.e. will formatting as you typing
 
 
Challenge
 
   The biggest challenge was to assemble the mask system. Regular expressions (RegExp) were right, but not everyone is familiar enough to use. But it was a starting point. Then I created a second pattern that allowed both the use of RegExp and a facilitator for the most common cases. I got to the following pattern, which I called macros:
  • 9 - only allows numbers fro 0 to 9. In RegExp \d
  • A - only allows letters from A to Z. In RegExp [a-zA-Z]
  • X - allows letters (from A to Z) and number (from 0 to 9). In RegExp [0-9a-zA-Z]
  • H - allows hexadecimals (numbers from 0 to 9 and letters from A to F). In RegExp [0-9a-fA-F]
  • @ - allows any character between ASC code 32 (space) until ASC code 126 [tilde]. In RegExp [\x20-\x7E]. Basically anything you can type (in Latin alphabet).
   With these settings (macros) to create a series of masks that can be easily adapted to various situations and fields. For example (using Brazilian common inputs):
 
CPF:                                         11 digits with some markers                            999.999.999-99
CNPJ:                                        14 digits with markers                                 99.999.999/9999-99
Vehicle ID:                                  3 letters and 4 numbers with markers                   AAA-9999
Zip Code:                                    8 digits with markers                                  99.999-999
Brazil cellphone:                            11 digits (area code, prefix and number)               = (99) 99999-9999
Internacional phone:                         13 digits (country code, area code, prefix and number) +99 (99) 99999-9999
MAC Address (MAC code from network card):    hexa group by 2                                        HH:HH:HH:HH:HH:HH
and so on...
 
   In addition, any field can be described using RegExp too, which makes the system fully flexible and adaptable to any situation. For example:
Vehicle ID (macro style) = AAA-999
RegExp style             = [A-Z][A-Z][A-Z]-\d\d\d\d
or just like this        = [A-Z]3-[\d]4
   But using macros is easier to understand and work right? By the way, still can not mix standard RegExp with macros. Maybe later...
 
   Let's complicate a bit? If you need a field which can only accept a character from B to J and then 2 numbers and ends with 3 more letters, looks like this:
[B-J]\d\d[A-Z][A-Z][A-Z]
   It is harsh to read, but it works!
 
   Another point to be explored are the styles combined with masks. The styles are the last parameters of the GUICtrlCreateInput function and are very important to complement the subject. There are styles to align to the right and to the left, center it, or capitalize, accept only password fields or read-only. An ideal setting for the masks involves these two factors and the programmer know that!
 
To make a field to accept only uppercase letters => GUICtrlCreateInput("", 10, 35, 300, 20, $ES_UPPERCASE)
Only lowecase                                    => GUICtrlCreateInput("", 10, 35, 300, 20, $ES_LOWERCASE)
Center                                           => GUICtrlCreateInput("", 10, 35, 300, 20, $ES_CENTER)
If you want to mix can also using BitOr function => GUICtrlCreateInput("", 10, 35, 300, 20, (BitOr($ES_UPPERCASE, $ES_CENTER))
 
   I know I'm raining in the wet here because all of this is on Help and you guys already know, but it never hurts to remember!
   Mainly because when mounting a mask makes a difference. Imagine setting a mask like [A-Z] and put $ES_ LOWERCASE in style? It's not going to work.
 
 
Conclusion
 
   So it was set that there would be 3 (three) types of fields, all formatting "on-the-fly" as are typed:
 
  1. TEXT field: accepts masks
  2. NUMBER field: like a calculator, from right to left
  3. DATE field: accepts only numbers but that converts it to a format of "expanded date" (dd/MMM/yyyy) when leaving the field and with various options of format (DMY, MDY, etc). With automatic validation.
   The DMY format and its variants (MDY, YMD, etc) are to allow the entry of dates in other standards according to each country. In the USA the default is MDY, in Brazil and England is DMY. Military use YMD and so on.
   You can use the GUICtrlCreateDate command to create a data input "text plus calendar", but in this case, only the size calculation (font) will be made.
 
   So the function ended up like this:
 
_GUICtrlSetMask(<ControlID>, <Input Type>, <Mask>[, <Extra Info>])
 
   where:
 
ControlID  = control code to be formatted. You can use -1 if you want it to be the last created
Input Type = 0 ($INPUT_DATE), 1 ($INPUT_NUMBER), 2 ($INPUT_TEXT)
Mask       = Mentioned masks. If you use macros (@, 9, A, X, H) need to put # at the beginning and end
Extra Info = depends on the chosen format may contain additional information
 
   Examples of each type:
 
Numbers ($INPUT_NUMBER):
_GUICtrlSetMask($nCampo_Codigo, $INPUT_NUMBER, 6)     ; Setting for number with 6 (six) digits WITHOUT decimals (999,999)
_GUICtrlSetMask($nCampo_Preco,  $INPUT_NUMBER, 9, 2)  ; Setting for number with 9 (nine) digits PLUS 2 (two) decimals) (999,999,999.99)
Date ($INPUT_DATE):
_GUICtrlSetMask($nCampo_Aniversario, $INPUT_DATE, "DMY")      ; Setting for date and format DAY, MONTH and YEAR, with standard marker / (stroke)
_GUICtrlSetMask($nCampo_NF_Saida,    $INPUT_DATE, "YMD", "-") ; Setting for date and format YEAR, MONTH and DAY with marker - (minus)
Text ($INPUT_TEXT):
_GUICtrlSetMask($nCampo_Nome, $INPUT_TEXT, "#[@]50#")              ; Setting the text field for 50 (fifty) characters max
_GUICtrlSetMask($nCampo_Nome, $INPUT_TEXT, "#[@]50#", 20)          ; Setting the text field for 50 (fifty) characters max, but with max widht of 20 (twenty) characters and activating horizontal scroll
_GUICtrlSetMask($nCampo_CNPJ, $INPUT_TEXT, "#99.999.999/9999-99#") ; Setting for text field with CNPJ mask (Brazilian corporation)
_GUICtrlSetMask($nCampo_CEP,  $INPUT_TEXT, "#99.999-999#")         ; Setting for text field with Brazilian ZIP code mask
 
Comments
 
  • When to use text type, don't forget to put the mask between # (hashtag).
  • When using date type, don't forget the date format, because it must be of the same input format.
  • Usually, we use the _NowCalcDate() function to we initialize our date variables, but you have to remember that in this case the content is YMD format.
  • In this case, we need to change the date format using a helper function _MUDA_FORMATO_DATA selecting the format you wish.
  • Example.: $sData = _MUDA_FORMATO_DATA(_NowCalcDate(), "YMD", "DMY") => We initialize with _NowCalcDate (YMD format) and exchanged for Brazil (DMY format)
 
To initialize the masks subsystem it is necessary to call the function INIT_MASCARAS which has 3 (three) parameters, all optional:
 
_INIT_MASCARAS([BackgroundColor][, <PRE-Format>[, <POS-Format>]])
 
   onde:
BackgroundColor  = color to highlight the field is being typed. Default 0xFFFF99 (light yellow)
PRE-Format       = True (default) or False. Is used for when you open the window, pre-format fields properly according to his content
   TRUE  => CEP field, mask 99.999-999, content "90123789", when open the windows shows "90.123-789"
   FALSE => CEP field, mask 99.999-999, content "90123789", when open the windows shows "90123789"
POS-Format   = True or False (default). Is used for when you open the window, unformat the fields or eliminate the masks applied
   TRUE  => CEP field, mask 99.999-999, content "90.123-789", when close the window keep the content as "90.123-789"
   FALSE => CEP field, mask 99.999-999, content "90.123-789", when close the window change the content to "90123789"
 
These definitions are for when they are read with the command fields GUICtrlRead, come as the AutoIt (without formatting).
 
Finally, after closing the window, release the subsystem via function
_RELEASE_MASCARAS()
 
Also when we talk about data entry, as far as possible to the ideal is that the ENTER key (in addition to the TAB) can be used as the terminator entry and move to the next field. Could do this internally, but I realized that it is very personal. Hence I prefer to let the position of each, but the template is quite simple and is in the example.
You can also do "individual" validation of each field, which would complete the input formatting.
For example, when you finish receiving a CNPJ or CPF, can validate that the code. Or if given date is allowed and so on.
 
Of course, the function is not perfect nor is never complete, but I'm counting on the participation of colleagues to me indicate the errors, your suggestions and criticism.
Thanks in advance! Sorry for my bad English, I'm just a Brazilian guy.
PS. Check out our AutoIt Brazilian forum => http://forum.autoitbrasil.com/index.php
 
 
Demonstration
 
Example
#include "Mascaras.au3"

PRINCIPAL()
Exit

Func PRINCIPAL()
   _INIT_MASCARAS()

   Local $nTamanho_Fonte_Input = 12
   Local $nMsg, $nControle, $hJanela = GUICreate("Input Masks", 620, 460)
   GUISetFont(11, $FW_NORMAL, $GUI_FONTNORMAL, "Arial", $hJanela, $CLEARTYPE_QUALITY)

   Local $Codigo = 1234
   Local $Valor = 12345.6
   Local $Nome = "User Name"
   Local $CPF = "12345678901"
   Local $CNPJ = "12345678000901"
   Local $MAC = "7B891506F1C7"
   Local $CEP = "90040123"
   Local $sFormato_Data = "MDY"
   Local $Data = _MUDA_FORMATO_DATA(_NowCalcDate(), "YMD", $sFormato_Data)
   Local $Placa = "ABC1234"
   Local $Telefone = "11999706050"
   Local $Internacional = "1123456789"

   GUICtrlCreateLabel("Code:", 50, 50)
   Local $Campo_Codigo = GUICtrlCreateInput($Codigo, 100, 45)
   _GUICtrlSetMask(-1, $INPUT_NUMBER, 6, 0)
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("Value:", 350, 50)
   Local $Campo_Valor = GUICtrlCreateInput($Valor, 400, 45)
   _GUICtrlSetMask(-1, $INPUT_NUMBER, 9, 2)
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("Name:", 50, 100)
   Local $Campo_Nome = GUICtrlCreateInput($Nome, 100, 95, Default, Default, BitOR($ES_UPPERCASE, $ES_AUTOHSCROLL))
   _GUICtrlSetMask(-1, $INPUT_TEXT, "#[@]60#", 40)
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("Date1:", 50, 150)
   Local $Campo_Data1 = GUICtrlCreateInput($Data, 100, 145)
   _GUICtrlSetMask(-1, $INPUT_DATE, $sFormato_Data)
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("Date2:", 350, 150)
   Local $Campo_Data2 = GUICtrlCreateDate(_NowCalcDate(), 400, 145)
   _GUICtrlSetMask(-1, $INPUT_DATE, "dd/MMM/yyyy")
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("CNPJ:", 50, 200)
   Local $Campo_CNPJ = GUICtrlCreateInput($CNPJ, 100, 195)
   _GUICtrlSetMask(-1, $INPUT_TEXT, "#99.999.999/9999-99#")
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("CPF:", 350, 200)
   Local $Campo_CPF = GUICtrlCreateInput($CPF, 400, 195)
   _GUICtrlSetMask(-1, $INPUT_TEXT, "#999.999.999-99#")
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("MAC:", 50, 250)
   Local $Campo_MAC = GUICtrlCreateInput($MAC, 100, 245, Default, Default, $ES_UPPERCASE)
   _GUICtrlSetMask(-1, $INPUT_TEXT, "#HH:HH:HH:HH:HH:HH#")
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("ZIP:", 350, 250)
   Local $Campo_CEP = GUICtrlCreateInput($CEP, 400, 245)
   _GUICtrlSetMask(-1, $INPUT_TEXT, "#99.999-999#")
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("License:", 80, 300)
   Local $Campo_Placa = GUICtrlCreateInput($Placa, 100, 295, Default, Default, $ES_UPPERCASE)
   _GUICtrlSetMask(-1, $INPUT_TEXT, "#AAA-9999#")
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("License:", 380, 300)
   Local $Campo_Placa1 = GUICtrlCreateInput($Placa, 400, 295)
   _GUICtrlSetMask(-1, $INPUT_TEXT, "[a-zA-Z]3-[0-9]4")
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("Phone:", 60, 350)
   Local $Campo_Telefone = GUICtrlCreateInput($Telefone, 100, 345)
   _GUICtrlSetMask(-1, $INPUT_TEXT, "#(99) 99999-9999#")
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   GUICtrlCreateLabel("Phone:", 360, 350)
   Local $Campo_FoneIntl = GUICtrlCreateInput($Internacional, 400, 345)
   _GUICtrlSetMask(-1, $INPUT_TEXT, "#+55 (99) 9999-9999#")
   GUICtrlSetFont(-1, $nTamanho_Fonte_Input, $FW_BOLD, $GUI_FONTNORMAL, "Tahoma", $CLEARTYPE_QUALITY)

   Local $Ok = GUICtrlCreateButton("Ok", 145, 400, 75, 25, $WS_GROUP)
   Local $Cancelar = GUICtrlCreateButton("Cancel", 360, 400, 75, 25, $WS_GROUP)

   Local $nMyDummy = GUICtrlCreateDummy()
   Local $aHotkey = [ [ "{ENTER}", $nMyDummy ], [ "{TAB}", $nMyDummy ] ]
   GUISetAccelerators($aHotkey)

   GUISetState(@SW_SHOW, $hJanela)
   While True
      $nMsg = GUIGetMsg()
      If $nMsg = $GUI_EVENT_CLOSE Then ExitLoop
      If $nMsg = $Ok or $nMsg = $Cancelar Then ExitLoop
      If $nMsg = $nMyDummy Then
         $nControle = _GuiCtrlGetFocus($hJanela)
         If $nControle = $Ok or $nControle = $Cancelar Then
            ExitLoop
         ElseIf $nControle = $Campo_Nome Then
            If GUICtrlRead($Campo_Nome) = "" Then ContinueLoop ; do not allow empty name
         ElseIf $nControle = $Campo_Data1 Then
            If GUICtrlRead($Campo_Data1) = "" Then ContinueLoop ; do not allow empty date
         ElseIf $nControle = $Campo_CPF Then
;~             If not VERIF_CPF(GUICtrlRead($Campo_CPF)) Then ContinueLoop
         ElseIf $nControle = $Campo_CNPJ Then
;~             If not VERIF_CNPJ(GUICtrlRead($Campo_CNPJ)) Then ContinueLoop
         EndIf
         GUISetAccelerators("")
         ControlSend($hJanela, "", $nControle, "{TAB}")
         GUISetAccelerators($aHotkey)
      EndIf
   WEnd
   GUISetState(@SW_HIDE, $hJanela)

   ConsoleWrite("[" & GUICtrlRead($Campo_Codigo) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_Valor) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_Nome) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_CPF) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_CNPJ) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_MAC) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_CEP) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_Data1) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_Data2) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_Placa) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_Placa1) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_Telefone) & "]" & @CRLF)
   ConsoleWrite("[" & GUICtrlRead($Campo_FoneIntl) & "]" & @CRLF)

   GUIDelete($hJanela)
   _RELEASE_MASCARAS()
EndFunc   ;==>PRINCIPAL

Attention!!!

   Due to changes between the versions 3.3.14.2 e 3.3.14.3 of AutoIt there are 2 versions of this function to download!

   Check and download the correct version for your installation.

   The sample program is the same for both versions.

 

   The only difference between the two versions are the #includes at the beginning of the file.

   In version 14.2 it's like that:

#include <Date.au3>
#include <Array.au3>
#include <String.au3>
#include <WinAPILocale.au3>
#include <EditConstants.au3>
#include <FontConstants.au3>
#include <MenuConstants.au3>
#include <ColorConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

   And in vesion 14.3 it's like that:

#include <Date.au3>
#include <Array.au3>
#include <String.au3>
#include <WinAPIConv.au3>
#include <WinAPIGdiDC.au3>
#include <WinAPILocale.au3>
#include <WinAPISysWin.au3>
#include <EditConstants.au3>
#include <FontConstants.au3>
#include <MenuConstants.au3>
#include <ColorConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Summarizing, the latest version has the following extra #includes:

#include <WinAPIConv.au3>
#include <WinAPIGdiDC.au3>
#include <WinAPISysWin.au3>

Otherwise it's all the same.

Happy coding everyone.

 

To Do:

- remove all the errors (LOL - never happen)

- better coding

- validate function for every field (not allowing to go to another field without validation)

Mascaras v14.2.au3

Mascaras v14.3.au3

Edited by Manimal
Link to comment
Share on other sites

  • 4 months later...

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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...