Jump to content

Repetitive GUI linked to dozens of functions: how to code efficiently


Recommended Posts

I'm hoping to ask for your help optimizing a project before I embark on creating a big mess of nested If statements.

I am building a physical control interface for the Artemis SBS Bridge Simulator, using an Arduino Mega. When a button is pushed on the control panel the Arduino sends a command over serial to the PC where an AutoIT scrip is waiting to execute the corresponding keystroke or mouse movement. I have this working in a basic hard-coded way (using this very helpful forum post from 2018), but I'd now like to build a GUI to make the tool usable without editing code. The user will select from a dropdown which function will run when the button attached a specific pin is pressed. See very basic screenshot attached, but imagine this going on for a large number of buttons.    

The challenge is that there are 54 pins on the Arduino Mega, and probably about 60 game actions each one could do. So doing this just with a bunch of nested IF statements will bloat the code and make it very difficult to maintain. If I add a new function, I'll have to add it as an list item to 54 different GUI controls, and then update 54 "If" statements to make sure that function could be assigned to any pin.

If $NewMsg = "Pin1" then
      If GUICtrlRead($Pin1) = "Warp1" then Warp1()
      If GUICtrlRead($Pin1) = "Warp2" then Warp2()
      If GUICtrlRead($Pin1) = "Warp3" then Warp3()
      If GUICtrlRead($Pin1) = "Warp4" then Warp4()
      If GUICtrlRead($Pin1) = "FullStop" then FullStop()     
      ;and so on 
   EndIf
   
   If $NewMsg = "Pin2" then
      If GUICtrlRead($Pin2) = "Warp1" then Warp1()
      If GUICtrlRead($Pin2) = "Warp2" then Warp2()
      If GUICtrlRead($Pin2) = "Warp3" then Warp3()
      If GUICtrlRead($Pin2) = "Warp4" then Warp4()
      If GUICtrlRead($Pin2) = "FullStop" then FullStop()     
      ;and so on 
   EndIf

There has to be a better way!

Ideally I would:

  • When loading the GUI, step through each dropdown control and add the same "List" items to all of them. 
  • When a new string is received over serial, find the dropdown with Name matching that string, and run the function with name matching the current selection in that dropdown
  • Save the user's dropdown selections (in an .ini file, XML or similar?) and load them next time. 

I don't know how to achieve this in AutoIT. Are there control arrays, or other tricks in AutoIT to make this code cleaner? I appreciate any help to suggest a more elegant method to handle this, or any reading I should do in the AutoIT docs to think about this differently. 

Pins.png

Edited by EricInCanada
Removed lock
Link to post
Share on other sites
  • Moderators

Welcome to the AutoIt forum.

Unfortunately you appear to have missed the Forum rules on your way in. Please read them now - particularly the bit about not discussing game automation - and then you will understand why you will get no help and this thread will now be locked.

See you soon with a legitimate question I hope.

The Moderation team

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to post
Share on other sites
  • Melba23 changed the title to Repetitive GUI linked to dozens of functions: how to code efficiently
  • Melba23 unlocked this topic
  • Moderators

EricInCanada,

After due consideration, sparked by a polite and explanative PM from the OP (take note others!), I have decided that this thread deals essentially with Arduino-AutoIt interfaces and does not specifically automate the game in any way. So I have reopened it.

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to post
Share on other sites

Here is the simplest way I can think of.  Though, I know there is also a way to create dropdowns within a listview which would let you see everything easier but that would involve some forum searching.

 

Opt("GUIOnEventMode", 1)            ;Use Event mode to call functions directly
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <GuiComboBox.au3>

Global $sPins, $sFunctions

$iTotalPins = IniRead(@ScriptDir & "\Config.ini", "Global", "TotalPins", 0)
For $i = 1 to $iTotalPins
    $sPins &= "|" & $i
Next

$aFunctions = IniReadSection(@ScriptDir & "\Config.ini", "Functions")
For $i = 1 to UBound($aFunctions) - 1
    $sFunctions &= "|" & $aFunctions[$i][1]
Next

$hGUI = GUICreate("Pinout Control", 300, 100)
GUISetOnEvent($GUI_EVENT_CLOSE, "_ExitApp")

GUICtrlCreateLabel("Pin#", 10, 13, 60, 17, $SS_CENTER)
$hCmboPIN = GUICtrlCreateCombo("", 10, 30, 60, 20)
GUICtrlSetOnEvent(-1, "_PinSelect")
GUICtrlSetData($hCmboPIN, $sPins)

GUICtrlCreateLabel("Function", 100, 13, 140, 17, $SS_CENTER)
$hCmboFUNC = GUICtrlCreateCombo("", 100, 30, 140, 20)
GUICtrlSetOnEvent(-1, "_FunctionSelect")
GUICtrlSetData($hCmboFUNC, $sFunctions)

GUICtrlCreateButton("Listen", 10, 70, 100, 20)
GUICtrlSetOnEvent(-1, "_Listen")

GUISetState(@SW_SHOW, $hGUI)

While 1
    sleep(100)
WEnd


Func _Listen()
    $aUserFunctions = IniReadSection(@ScriptDir & "\Config.ini", "User Config")
    Local $NewMsg
    While 1
        ;$NewMsg = Get Pin somehow??
        For $i = 1 to UBound($aUserFunctions) - 1
            If $NewMsg = "Pin" & $aUserFunctions[$i][0] Then Call($aUserFunctions[$i][1])
            Sleep(10)
        Next
    WEnd
EndFunc

Func _PinSelect()
    _GUICtrlComboBox_SetEditText($hCmboFUNC, IniRead(@ScriptDir & "\Config.ini", "User Config", GUICtrlRead($hCmboPIN), "Unassigned"))
EndFunc

Func _FunctionSelect()
    IniWrite(@ScriptDir & "\Config.ini", "User Config", GUICtrlRead($hCmboPIN), GUICtrlRead($hCmboFUNC))
EndFunc


Func _ExitApp()
    Exit
EndFunc

 

ini file format "Config.ini"

[Global]
TotalPins=54

[Functions]
0=Unassigned
1=Warp1
2=Warp2
3=Full Stop

[User Config]
1=Warp1
2=Warp2
3=Unassigned
4=Unassigned
5=Full Stop
10=Warp1

 

Edited by BigDaddyO
added listen but don't know how the $NewMsg is populated
Link to post
Share on other sites
  • Moderators

Hi,

4 minutes ago, BigDaddyO said:

a way to create dropdowns within a listview

You might be thinking of my GUIListViewEx UDF - the link is in my sig.

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to post
Share on other sites

This is excellent, thank you.  The Call function is exactly what I needed, because it allows me to call a function with a name that won't be known until runtime. That was a big "Aha!" moment, so I'm making progress now. 

What I've done is build a big array of Combo boxes, where the array index matches the pin number. Every box is populated with the same list of functions: 

For $index = 2 to 25
   $LabelD[$index] = GUICtrlCreateLabel("D" & $index, 8, ($index*20)-7, 24, 12)
   $ControlDefault = IniRead(@ScriptDir & "\Settings.ini", "General", "PinDPress" & $index, "Not Assigned")
   $PinDPress[$index] = GUICtrlCreateCombo($ControlDefault, 32, ($index*20)-15, 112, 25, BitOR($CBS_DROPDOWN,$CBS_AUTOHSCROLL))
   GUICtrlSetData(-1, $ListOfButtonFunctions)
 Next

Once I've parsed the pin number out of the serial message, I can grab the contents of the corresponding Combo box, and then call the matching function:

If StringLeft($NewMsg, 5) = "PinDP" Then 
        $PinNumber = Int(StringMid($NewMsg,6))
        Call(GUICtrlRead($PinDPress[$PinNumber]))
        EndIf
    EndIf

I've also got the INI working based on BigDaddyO's post above (thank you!) and the thing is pretty much functional now. I realize GUI event mode might have made this a little more elegant, I'll keep this in mind in the future. 

ArtemisArduinoScrShot.png

Link to post
Share on other sites

Main thing I'll be dealing with now is smoothing out the Serial coms. Buttons and switches are no problem, they involve so little data.

But a potentiometer has to spam the serial buffer with a lot of packets while it's moving. This should be fine because the buffer on both sides is easily large enough to handle it, and even a 9600 baud connection is pretty quick. But in practice so far it's dodgy: commands are missed and the software state ends up out of sync from the physical control interface. 

Link to post
Share on other sites

Update on the serial issue. Two fixes combined worked to avoid the lag problem:

  1. Manually sent a Carriage Return character ("\r") in every message, which prevents the buffer from filling with two or more packets not separated by a delimitator of any kind. 
  2. Discovered that "Mouse Down" followed by "Mouse Up" executes faster that a "Click", which makes a difference when moving a control across the screen with many clicks in succession. Maybe add Sleep(1) just for good measure. 
  3. Realized that there's no point having the Arduino send instructions faster than the refresh rate of the monitor I'm using, so in the arduino code I added  "Current Milis" count check to only send a new instruction for the control if 33ms has passed since the last time it send one. This means that for a rapid movement of the potentiometer from off to full, it is sending perhaps only a dozen packets, rather than all 1024 in the 10-bit resolution read. 
  if(A0active == true) { 
    NewA0 = (FloatA0*analogRead(A0)) + ((1-FloatA0)*NewA0);
    NewA0 = NewA0;
    unsigned long currentMillis = millis();
    if(NewA0 != OldA0 && currentMillis - previousMillis >= 33) {
      Serial.print("PinA00" + String(NewA0) + "\r");
      previousMillis = currentMillis;
      OldA0 = NewA0;
    }

 

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
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...