Easy AutoGUI for Autohotkey v2.0
Posted: 15 Apr 2023, 18:01
Easy AutoGUI for Autohotkey v2.0
This is a modified version of Easy AutoGUI designed for use with AHKv2. The project is built upon the years of hard work by various individuals not including myself, please check the credits below for reference.
Direct Download Easy_AutoGUI_Portable.exe
https://github.com/samfisherirl/Easy-Auto-GUI-for-AHK-v2/
![Image](https://i.imgur.com/3Fz0s2b.gif)
Clone windows
Constantine
MessageBox creator
Credits:
- Easy AutoGUI was created by Alguimist, the founder of the Adventure IDE here https://sourceforge.net/projects/autogui/
- AHKv2converter was developed by https://github.com/mmikeww and https://github.com/dmtr99 among others.
Their work served as the foundation for this project. I did very little work, just weaving the two solutions together. All the work was done by the creators just mentioned.
How it works:
- Real-Time Conversion: The process of conversion takes place in real-time within the integrated development environment (IDE).
- Rapid Cloning: Cloning windows that contain over 1,000 lines is efficient, completing in less than one second.
- Custom Contingencies: Special measures have been implemented to reduce output errors. These adapt the ahkv2converter output to match the specific requirements of this use case.
- Error-Free Goal: After building a GUI for ahkv2, the expectation is that the code execution should be free of errors. While errors might occur, they are unexpected, and users should report them. The design intent is to achieve error-free output.
- Conversion Function: The code offers a function-call that facilitates conversion to v2 using ahkv2converter.
- Code Modifications: The Changes_of_note folder contains information about key modifications made to the base code, highlighting the unique aspects of the implementation.
Launch_AutoGUI.ahk (still needs github download):
code changed in ahkv2converter output:
This is a modified version of Easy AutoGUI designed for use with AHKv2. The project is built upon the years of hard work by various individuals not including myself, please check the credits below for reference.
Direct Download Easy_AutoGUI_Portable.exe
https://github.com/samfisherirl/Easy-Auto-GUI-for-AHK-v2/
![Image](https://i.imgur.com/3Fz0s2b.gif)
Clone windows
Constantine
MessageBox creator
Credits:
- Easy AutoGUI was created by Alguimist, the founder of the Adventure IDE here https://sourceforge.net/projects/autogui/
- AHKv2converter was developed by https://github.com/mmikeww and https://github.com/dmtr99 among others.
Their work served as the foundation for this project. I did very little work, just weaving the two solutions together. All the work was done by the creators just mentioned.
How it works:
- Real-Time Conversion: The process of conversion takes place in real-time within the integrated development environment (IDE).
- Rapid Cloning: Cloning windows that contain over 1,000 lines is efficient, completing in less than one second.
- Custom Contingencies: Special measures have been implemented to reduce output errors. These adapt the ahkv2converter output to match the specific requirements of this use case.
- Error-Free Goal: After building a GUI for ahkv2, the expectation is that the code execution should be free of errors. While errors might occur, they are unexpected, and users should report them. The design intent is to achieve error-free output.
- Conversion Function: The code offers a function-call that facilitates conversion to v2 using ahkv2converter.
- Code Modifications: The Changes_of_note folder contains information about key modifications made to the base code, highlighting the unique aspects of the implementation.
Launch_AutoGUI.ahk (still needs github download):
Code: Select all
#Requires Autohotkey v2.0
#SingleInstance Force
cwd := A_ScriptDir "\required"
#Include "*i %A_ScriptDir%\required\convert\ConvertFuncs.ahk"
#Include "*i %A_ScriptDir%\required\convert\converterMod.ahk"
#Include "*i %A_ScriptDir%\required\Include\splash.ahk"
#Include "*i %A_ScriptDir%\required\convert\_vars.ahk"
#Include "*i %A_ScriptDir%\required\convert\github.ahk"
; --- Readme ---
; Alguimist's Easy AutoGUI for AHK-v2 is a GUI designer for creating AHK-v2 scripts with a focus on ease of use.
; It is built on AHKv1 but handles real-time conversion to AHKv2, making it user-friendly for both beginners and advanced users.
; This script acts as a launcher for Easy AutoGUI, handling the conversion process.
; Credits:
; - Easy AutoGUI was created by -Alguimist-, creator of the Adventure IDE: https://sourceforge.net/projects/autogui/.
; - The AHKv2 converter by contributors like https://github.com/mmikeww and https://github.com/dmtr99.
; - The Autohotkey.com forum user "Boiler" provided the MessageBox Creator within the tools menu.
; These individuals' hard work laid the foundation for this project. I played a small part in bringing Easy AutoGUI to AHKv2 by weaving these two solutions together.
; ------------------------------
Main()
; Main function
Main() {
global PID
missingFilesPrompt() ; Prompt user if required files are missing
,showSplashScreen() ; Display a splash screen
,setDesignMode() ; Set the design mode
,cleanFiles(FileList) ; Clean temporary files
,Run(launch_autogui_cmd, , , &PID) ; Run the AutoGUI command
,Sleep(1000)
,findEasyAutoGUI(PID) ; Find and wait for Easy AutoGUI to launch
while ProcessExist(PID) {
CheckConversionStatus()
}
ExitApp()
}
ExitApp()
CheckConversionStatus() {
while ProcessExist(PID) {
if FileExist(logsPath) {
status := tryRead(logsPath)
inscript := status != "" ? tryRead(ahkv1CodePath) : ""
if (inscript != "") {
writer("", logsPath) ; Clear log file
try {
ConvertandCompile(inscript, ahkv2CodePath)
}
catch as e {
errorLogHandler(e.Message)
sleep(5)
continue
}
}
} else {
Sleep(5)
continue
}
}
ExitApp()
}
; Convert script from AHK v1 to v2
ConvertandCompile(inscript, ahkv2CodePath) {
script := Convert(inscript)
,final_code := modifyAhkv2ConverterOutput(ahkv1CodePath, script)
,writer(final_code, ahkv2CodePath)
,writer("1", returnStatusPath)
}
; Prompt user about missing files
missingFilesPrompt() {
msg := { text: "", title: "Missing Files", show: false }
if not DirExist(cwd) {
msg.show := true
msg.text := 'The `'/required/`' directory included with this app is missing. Would you like to download the required files?`nOtherwise, this app will exit.'
}
else if not FileExist(cwd "\AutoHotKey Exe\AutoHotkeyV1.exe") {
msg.text := 'The `'\required\AutoHotKey Exe\AutoHotkeyV1.exe`' file included with this app is missing. `n`nIf you downloaded from the Github code page, you`'ll need the release to run this application.`nOr edit the _vars.ahk file with your ahkv1 64bit exe. `n`nWould you like to download the required files?`nOtherwise, this app will exit.'
msg.show := true
}
else
{
return
}
if msg.show = True
{
userResponse := MsgBox(msg.text, msg.title, '52')
if (userResponse = "Yes") {
Run("https://github.com/samfisherirl/Easy-Auto-GUI-for-AHK-v2/releases")
}
ExitApp()
}
}
; Clean temporary files
cleanFiles(FileList)
{
for f in FileList {
writer("", f)
}
}
; Find Easy AutoGUI process
findEasyAutoGUI(PID) {
Loop 10 {
if ProcessExist(PID) {
break
}
else {
Sleep(1000)
}
}
}
; Try reading a file
tryRead(path) {
try {
F := FileOpen(path, "r", "utf-8")
out := F.Read()
F.Close()
return out
}
catch as e {
errorLogHandler(e.Message)
Sleep(2)
return ""
}
}
; Write to a file
writer(str_to_write := "", path := "") {
F := FileOpen(path, "w", "utf-8")
F.Write(str_to_write)
F.Close()
}
; Set design mode options
setDesignMode() {
IniWrite "1", settings, "Options", "DesignMode"
IniWrite "1", settings, "Options", "SnapToGrid"
IniWrite "1", settings, "Editor", "DarkTheme"
IniWrite "0", settings, "Sessions", "AutoLoadLast"
IniWrite "0", settings, "Sessions", "SaveOnExit"
}
; Check for the latest version
versionCheck() {
if not FileExist(versionPath) {
try {
Github.latest("samfisherirl", "Easy-Auto-GUI-for-AHK-v2")
}
}
}
; Handle errors and write to the error log
errorLogHandler(errorMsg) {
logMsg := "error occurred at: " FormatTime() " => " errorMsg "`n`n`n"
writer(logMsg, errorLog)
}
/*
if __name__ = "__main__"
{*/
/*
}
*/
Code: Select all
modifyAhkv2ConverterOutput(FNOut := "path", script := "code") ;outscript_path
{
static lastScript := ""
static lastReturned := ""
if (lastScript = script) {
return lastReturned
} else {
lastScript := script
}
GuiControlVars := [
Map("ctrl", "Button", "event", "Click", "function", "Text"),
Map("ctrl", "DropDownList", "event", "Change", "function", "Text"),
Map("ctrl", "Edit", "event", "Change", "function", "Value"),
Map("ctrl", "DateTime", "event", "Change", "function", "Value"),
Map("ctrl", "MonthCal", "event", "Change", "function", "Value"),
Map("ctrl", "Radio", "event", "Click", "function", "Value"),
Map("ctrl", "CheckBox", "event", "Click", "function", "Value"),
Map("ctrl", "ComboBox", "event", "Change", "function", "Text")
]
LVFunc := "`nLV_DoubleClick(LV, RowNum)`n{`n`tif not RowNum`n`t`treturn`n`tToolTip(LV.GetText(RowNum), 77, 277)`n`tSetTimer () => ToolTip(), -3000`n}`n"
eventList := []
GuiItemCounter := [1, 1, 1, 1, 1, 1, 1, 1]
brackets := 0
new_outscript := ""
buttonFound := 0
itemFound := false
editCount := 0
menuHandler := 0
guiShow := 0
RemoveFunction := false
menuHandle := 0
GuiEsc := 0
FindMenu := 0
FindMenuBar := 0
MenuHandleCount := 0
ctrlcolor := 0
guiname := ""
title := ""
GuiItem_Storage := []
LVFound := 0
Edit_Storage := []
GuiCtrlStorage := []
if FileExist(FNOut) {
FileMove(FNOut, A_ScriptDir "\required\convert\temp.txt", 1)
}
Loop Parse, script, "`n", "`r"
{
if (A_Index = 1)
{
new_outscript := "`n" "#Requires Autohotkey v2`n;AutoGUI 2.5.8 creator: Alguimist autohotkey.com/boards/viewtopic.php?f=64&t=89901`n;AHKv2converter creator: github.com/mmikeww/AHK-v2-script-converter`n;Easy_AutoGUI_for_AHKv2 github.com/samfisherirl/Easy-Auto-GUI-for-AHK-v2`n`n"
}
trimField := Trim(A_LoopField)
if RemoveFunction {
if InStr(trimField, "{") && !InStr(trimField, "}") {
brackets += 1 ; for every opening bracket, remove until equal number of closed brackets found
}
else if InStr(A_LoopField, "}") && !InStr(trimField, "{") {
if (brackets <= 1) {
RemoveFunction := false
brackets := 0
}
else if (brackets > 1) {
brackets := brackets - 1
}
}
continue
}
if (guiname = "")
{
if InStr(A_LoopField, " := Gui()") {
guiname := StrSplit(A_LoopField, " := Gui()")[1]
new_outscript .= A_LoopField "`n"
continue
}
}
; =================== check for gui controls =======================
if InStr(A_LoopField, 'Add("ListView"')
{
LVFound := StrSplit(TrimField, " := ")[1]
}
if InStr(A_LoopField, "Add(") {
ret := checkforGuiItems(A_LoopField, &GuiControlVars, &GuiItemCounter, &GuiCtrlStorage)
; ; loop through and look for GuiControlVars[]
if (ret[1] = 1) {
;button
itemFound := true
lastGuiControl := ret[2]
oldvar := StrSplit(A_LoopField, " := ")[1]
newline := StrReplace(A_LoopField, oldvar, ret[2])
new_outscript .= newline "`n"
continue
}
if (ret[1] = 2) {
new_outscript .= ret[2] " := " A_LoopField "`n"
lastGuiControl := ret[2]
itemFound := true
continue
}
else {
lastGuiControl := StrSplit(A_LoopField, " := ")
}
}
; =================== check for gui controls end =======================
if InStr(A_LoopField, ".Title :=") {
title := A_LoopField
continue
}
else if InStr(trimField, '.Icon("')
{
obj := StrSplit(trimField, '.Icon(')[1]
commaSeparated := StrSplit(StrReplace(StrSplit(trimField,"(")[2], ")", ""), ",")
commaSeparatedCln := []
for str in commaSeparated
{
commaSeparatedCln.Push(StrReplace(StrReplace(StrReplace(str, '``"', ''), '"', ''), " ", ""))
}
if commaSeparatedCln.Length > 2
{
new_outscript .= Format('{}.SetIcon("{}","{}", {})', obj, commaSeparatedCln[1], commaSeparatedCln[2], commaSeparatedCln[3]) "`n"
}
}
else if (menuHandle = 0) && (MenuHandleCount < 1) && InStr(A_LoopField, "MenuHandler") {
; if MenuHandler is found, add a function at the bottom of the app to handle
menuHandle := 1
new_outscript .= A_LoopField . "`n"
}
else if InStr(A_LoopField, "MenuHandler(") {
MenuHandleCount += 1
RemoveFunction := true
}
; else if InStr(A_LoopField, ".Add(`"Button`"") {
; buttonFound := 1
; new_outscript .= A_LoopField "`n"
; variableName := Trim(StrSplit(A_LoopField, ":=")[1])
; ;ogcButtonOK.OnEvent("Click", GuiClose)
; val := variableName ".OnEvent(`"Click`", OnEventHandler)`n"
; new_outscript .= val
; }
else if InStr(A_LoopField, "GuiEscape(*)") && (menuHandler = 0) {
menuHandler := 1
;if END OF SCRIPT found, attempt to append functions
;Function MenuHandler or OnEventHandler
;provide tooltips when buttons are clicked or values are entered
if (menuHandle = 1) && (MenuHandleCount < 2) {
new_outscript .= "`nMenuHandler(*)`n" tooltip_()
GuiEsc := 1
}
if itemFound {
func := "`nOnEventHandler(*)`n"
string := ""
event_control_tooltips := ""
for ctrl in GuiCtrlStorage {
event_control_tooltips .= Format(" `n`t. `"{1} => `" {1}.{2} `"``n`"", ctrl['name'], ctrl['function'])
}
if (event_control_tooltips != "") {
new_outscript .= func . tooltip_(event_control_tooltips)
}
else {
new_outscript .= func . tooltip_()
}
}
break
;if () GuiEsc := 1
}
else if (menuHandle = 1) && (MenuHandleCount >= 1) && InStr(A_LoopField, "MenuHandler(") {
;remove default menuhandler function
RemoveFunction := true
continue
}
else if InStr(A_LoopField, "OnEvent(`"Close`", GuiEscape)") || InStr(A_LoopField, "OnEvent(`"Escape`", GuiEscape)") || InStr(A_LoopField, "Bind(`"Normal`")") || (A_LoopField = "") || InStr(A_LoopField, ".SetFont()") || InStr(A_LoopField, ".hwnd") || InStr(A_LoopField, "+hWnd") {
;remove all if cases
continue
}
else if (Trim(A_LoopField = "Return") || Trim(A_LoopField = "return")) {
continue
}
else if InStr(A_LoopField, "ControlColor(") {
ctrlcolor := 1
RegExMatch(A_LoopField, "0x[a-fA-F0-9]{6}", &match)
if IsObject(match) {
hex := match[0]
if InStr(hex, "0x") {
hex := StrReplace(hex, "0x", "")
}
new_outscript .= lastGuiControl ".Opt(`"Background" hex "`")"
new_outscript .= "`n"
}
}
else if (trimField = "Menu := Menu()") {
;fix naming convension of Menu
new_outscript .= StrReplace(A_LoopField, "Menu := Menu()", "Menu_Storage := Menu()")
new_outscript .= "`n"
FindMenu := 1
}
else if InStr(trimField, ".New(") {
;fix naming convension of Menu
new_outscript .= StrReplace(A_LoopField, ".New(", ".Opt(") "`n"
Continue
}
else if (FindMenu = 1) && (InStr(trimField, "Menu.Add(")) {
;fix naming convension of Menu
if (StrSplit(trimField, "(")[1] = "Menu.Add") {
new_outscript .= StrReplace(A_LoopField, "Menu.Add(", "Menu_Storage.Add(") "`n"
}
}
else if (trimField = "MenuBar := Menu()") {
;fix naming convension of MenuBar
new_outscript .= StrReplace(A_LoopField, "MenuBar := Menu()", "MenuBar_Storage := MenuBar()")
new_outscript .= "`n"
FindMenuBar := 1
}
else if (FindMenuBar = 1) && InStr(trimField, "MenuBar.Add(") {
if (StrSplit(trimField, "(")[1] = "MenuBar.Add") {
new_outscript .= StrReplace(A_LoopField, "MenuBar.Add(", "MenuBar_Storage.Add(") "`n"
}
}
else if InStr(A_LoopField, ".MenuToolbar := MenuBar") {
;fix naming convension of MenuToolbar
new_outscript .= StrReplace(A_LoopField, "MenuToolbar := MenuBar", "MenuBar := MenuBar_Storage") "`n"
}
else if InStr(A_LoopField, ".Show(`"") && (guiShow = 0) {
guiShow := 1
;look for line before `return` (GuiShow)
;if found, and NO [submit] button is used
;user will get tooltips on value changes
;instead of submittion
if LVFound != 0 {
new_outscript .= LVFound '.Add(,"Sample1")`n' LVFound '.OnEvent("DoubleClick", LV_DoubleClick)`n'
}
if itemFound
{
eventsStringified := []
for ctrl in GuiCtrlStorage {
skip := false
; ctrl.event := eventList[A_Index]
if eventsStringified.Length > 0 {
for eventstr in eventsStringified {
if ctrl['name'] = eventstr {
skip := true
}
}
}
if !skip {
eventsStringified.Push(ctrl['name'])
new_outscript .= ctrl['name'] ".OnEvent(`"" ctrl['event'] "`", OnEventHandler)`n"
}
}
}
new_outscript .= guiname ".OnEvent('Close', (*) => ExitApp())`n" . title . "`n" A_LoopField . "`n"
if LVFound {
new_outscript .= LVFunc
}
}
else {
new_outscript .= A_LoopField . "`n"
}
}
new_outscript := InStr(new_outscript, "ListviewListview") ? StrReplace(new_outscript, "ListviewListview", "_Listview") : new_outscript
new_outscript := InStr(new_outscript, "ogc") ? StrReplace(new_outscript, "ogc", "") : new_outscript
lastReturned := new_outscript
return new_outscript
}
checkforGuiItems(LoopField, &GuiControlVars, &GuiItemCounter, &GuiCtrlStorage) {
for ctrl in GuiControlVars
{
if InStr(LoopField, Format(".Add(`"{1}`"", ctrl["ctrl"]))
{
if InStr(LoopField, " := ") {
var := StrSplit(LoopField, " := ")[1]
if not IsAlnum(var) {
var := cleanAlpha(var) GuiItemCounter[A_Index]
}
GuiCtrlStorage.Push(Map( 'name', var, 'event', ctrl['event'], 'function', ctrl['function'] ))
return [1, var]
}
else {
var := ctrl["ctrl"] GuiItemCounter[A_Index]
GuiCtrlStorage.Push(Map('name', var, 'event', ctrl['event'], 'function', ctrl['function']))
GuiItemCounter[A_Index] += 1
return [2, var]
}
}
}
return [0]
}
tooltip_(str := "") {
if (str != "") {
str := "`n`t. `"Active GUI element values include:``n`" " . str
}
return "{`n`tToolTip(`"Click! This is a sample action.``n`"" str ", 77, 277)`n`tSetTimer () => ToolTip(), -3000 `; tooltip timer`n}`n"
}
cleanAlpha(StrIn) {
len := StrLen(StrIn)
newVar := ""
loop len {
char := SubStr(StrIn, A_Index, 1)
if IsAlpha(char) {
newVar .= char
}
}
return newVar
}