Jump to content

Avoiding recursive calls when updating GUI Controls


Recommended Posts

I'm trying to write a GUI that interfaces with a piece of equipment through sending and receiving serial commands.

 

I would like to have a combo box that lists the various operating modes of the equipment.  When the user selects a mode the code needs to send out a serial command. (The example code just writes a message to the text box)

 

However after some time interval the equipment may switch modes on its own.  When this happens I would like to update the combo box.  Therefore, when GUIGetMsg() doesn't return an event  the program needs to request information from the equipment and update the combo box. (In the example I have to program always setting the combo box value back to MODE 1 and displaying the elapsed time in the text box)

 

The problem I’m having is that when the program updates the combo box by calling ControlCommand(), it generates and event which triggers the Case $idMsg = $idComboBox_MODE to execute on the next loop. This in turn sends another command out to the equipment.

 

I would like to avoid this and only send out a command if the combo box was changed by the user.  Is there a way to distinguish between a user selection of the combo box and an update by the program? Or is there a way to suppress the next event when the program updates the combo box? 

 

#include <GuiConstantsEx.au3>
#include <ComboConstants.au3>
#include <EditConstants.au3>

_Main()

Func _Main()

  Local $GUI_Title = "Temp"
  Local $hGUI = GUICreate($GUI_Title, 400, 200,-1,-1)
  Local $idComboBox_MODE = GUICtrlCreateCombo("MODE 1", 10, 50, 100, 21,$CBS_DROPDOWNLIST)
               GUICtrlSetData($idComboBox_MODE, "MODE 2|MODE 3|MODE 4","MODE 1")
  Local $idInput_STATUS  = GUICtrlCreateInput(" ",    120, 50, 200, 21,$ES_READONLY)
  Local $ts = TimerInit()
  Local $td = TimerDiff($ts)

  GUISetState()
  While 1
    $idMsg = GUIGetMsg()
    Select
      Case $idMsg = $GUI_EVENT_CLOSE
        ExitLoop
      Case $idMsg = $idComboBox_MODE
        $iMenustate = GUICtrlRead($idComboBox_MODE)
        GUICtrlSetData($idInput_STATUS,"Command Sent "&$iMenustate)
        ;Code to send command to instrument goes here
        Sleep(1000)
    EndSelect

    $td = TimerDiff($ts)
    If $td > 3000 Then
      ;code to read status of the instrument including its current mode goes here
      ;For this example I will just set the mode to MODE 1 and display the elapsed time
      GUICtrlSetData($idInput_STATUS,StringFormat("Status Received: %#.6g ms", $td))
      ControlCommand($hGUI, $GUI_Title, $idComboBox_MODE,"SetCurrentSelection", 0)
      Sleep(1000)
      $ts = TimerInit()
    EndIf
  WEnd
  Exit
EndFunc;==>_Main

 

Edited by Queso
Link to comment
Share on other sites

Is there a way to distinguish between a user selection of the combo box and an update by the program?

You could try using WM_COMMAND to check the $CBN_DROPDOWN or  $CBN_CLOSEUP  notifications
These are sent if the combo is updated by the user only
Please have a look at the example in the helpfile for _GUICtrlComboBox_Create

Link to comment
Share on other sites

Thanks again. This is exactly what I needed! 

I like that the program can pause the update of the GUI when $CBN_DROPDOWN is active and then send the commands only when $CBN_CLOSEUP is activate. Looks like I will have to rewrite my actual GUI to use these library functions rather than the standard GUI functions. :)

I would like to do a similar process for check boxes and input fields.  Which looks like a similar substitution... 

GUICtrlCreateInput  --> _GUICtrlEdit_Create
GUICtrlCreateCheckbox  -->  _GUICtrlButton_Create

I have one additional question. In order to send the events back to the _Main() function I used a global variable $g_sTemp.  It works but I guess it isn't the most elegant solution.  Is there a better way to return this information?  

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <EditConstants.au3>
#include <GuiComboBoxEx.au3>

Global $g_hCombo_MODE
Global $g_hCombo_CMD
Global $g_sTemp = ""

_Main()

Func _Main()
  Local $GUI_Title = "Temp"
  Local $hGUI = GUICreate($GUI_Title, 400, 200,-1,-1)
  Local $idInput_STATUS  = GUICtrlCreateInput(" ",    120, 50, 200, 21,$ES_READONLY)
  Local $ts = TimerInit()
  Local $td = TimerDiff($ts)
  local $sText = ""

  $g_hCombo_MODE  = _GUICtrlComboBox_Create($hGUI, "MODE 1|MODE 2|MODE 3|MODE 4", 10, 50, 100, 21,$CBS_DROPDOWNLIST)
  $g_hCombo_CMD = _GUICtrlComboBox_Create($hGUI, "CMD 1|CMD 2|CMD 3", 10, 100, 100, 21,$CBS_DROPDOWNLIST)

    GUISetState(@SW_SHOW)
  GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

  While 1
    $idMsg = GUIGetMsg()
    Select
      Case $idMsg = $GUI_EVENT_CLOSE
        ExitLoop
    EndSelect
    Select
      Case $g_sTemp = "UPDATE MODE"
        ; Get Item Text
        _GUICtrlComboBoxEx_GetItemText($g_hCombo_MODE, _GUICtrlComboBox_GetCurSel($g_hCombo_MODE), $sText)
        GUICtrlSetData($idInput_STATUS,"Command Sent "&$sText)
        ;Code to send command to instrument goes here
        $g_sTemp = ""
        $ts = TimerInit()
      Case $g_sTemp = "UPDATE CMD"
        _GUICtrlComboBoxEx_GetItemText($g_hCombo_CMD, _GUICtrlComboBox_GetCurSel($g_hCombo_CMD), $sText)
        GUICtrlSetData($idInput_STATUS,"Command Sent "&$sText)
        ;Code to send command to instrument goes here
        $g_sTemp = ""
        $ts = TimerInit()
    EndSelect
    If $g_sTemp <> "WAIT" Then
      $td = TimerDiff($ts)
      If $td > 2000 Then
        ;code to read status of the instrument including its current mode goes here
        ;For this example I will just set the mode to 'MODE 1', CMD to 'CMD 1' and display the elapsed time
        GUICtrlSetData($idInput_STATUS,StringFormat("Status Received: %#.6g ms", $td))
        _GUICtrlComboBox_SetCurSel( $g_hCombo_MODE,0)
        _GUICtrlComboBox_SetCurSel( $g_hCombo_CMD,0)
        $ts = TimerInit()
      EndIf
    EndIf
  WEnd
  Exit
EndFunc   ;==>Example

Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
  #forceref $hWnd, $iMsg
  Local $hWndFrom, $iIDFrom, $iCode
  $hWndFrom = $lParam
  $iIDFrom = BitAND($wParam, 0xFFFF) ; Low Word
  $iCode = BitShift($wParam, 16) ; Hi Word
  Switch $hWndFrom
    Case $g_hCombo_MODE
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          $g_sTemp = "UPDATE MODE"
        Case $CBN_DROPDOWN; Sent when the list box of a combo box is about to be made visible
          $g_sTemp = "WAIT"
      EndSwitch
    Case $g_hCombo_CMD
      Switch $iCode
        Case $CBN_CLOSEUP ; Sent when the list box of a combo box has been closed
          $g_sTemp = "UPDATE CMD"
        Case $CBN_DROPDOWN; Sent when the list box of a combo box is about to be made visible
          $g_sTemp = "WAIT"
      EndSwitch
  EndSwitch
  Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND

 

Link to comment
Share on other sites

In order to send the events back to the _Main() function I used a global variable $g_sTemp.  It works but I guess it isn't the most elegant solution.  Is there a better way to return this information?

​Not sure, the way you use seems good as it makes the return from WM_COMMAND as fast as possible (which is recommended)
Probably the  $g_sTemp events in the main While loop could be optimized a little   :)

Link to comment
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
 Share

  • Recently Browsing   0 members

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