BigDaddyO Posted December 11, 2019 Share Posted December 11, 2019 Hello, Before I write my own, I'm wondering if anyone knows of an existing UDF out there that would allow me to find a control that is next to a known control. It's rare when I use it, but I'm looking for something similar to the UFT Visual relation identifier. ex: The control I want is Inline, to the Right of ControlA In this instance, I need to find a scrollbar which is usually ScrollBar2, but there are two large edit boxes that sometimes have scrollbars which throws the numbering off. Thanks Link to comment Share on other sites More sharing options...
Bert Posted December 12, 2019 Share Posted December 12, 2019 Is this a web app? The Vollatran project My blog: http://www.vollysinterestingshit.com/ Link to comment Share on other sites More sharing options...
BigDaddyO Posted December 12, 2019 Author Share Posted December 12, 2019 UFT: aka, Microfocus UFT; aka, Microfocus UFT-One; aka, HP UFT; aka HP Quick Test Pro; aka Mercury Quick Test Pro it's a feature within the object repository to identify a control by saying where it is in relation to another control. again, it's not anything i've had to use often. I've started building my own version. Link to comment Share on other sites More sharing options...
BigDaddyO Posted December 12, 2019 Author Share Posted December 12, 2019 (edited) I've started to write my own and I created a gui to help with debugging but i'm coming across a couple issues i'm hoping someone could help with. 1. If I set the Padding to -25 and run the Find Above {F2} it's not finding the button (Fixed!) 2. The GDIplus lines are not being removed from previous searches. how do I remove lines? not really important as it's just for my demo. I only have the Find Left {F1} and Find Above {F2} working for now. Launch > Hit F1 and it will draw red lines between where it was searching and update the button to FOUND! for the instance specified. You can increase the Padding to widen the area to search Hit F2 and it will draw lines above where it was searching. If you adjust the padding to -25 on the Above it doesn't find anything. Any ideas on how to resolve, or even a better method to do this? expandcollapse popup#include <WinAPI.au3> #include <WindowsConstants.au3> #include <GUIConstantsEx.au3> #include <ColorConstants.au3> #include <GuiEdit.au3> #include <Array.au3> #include <StaticConstants.au3> #include <GDIPlus.au3> Global $hGDI, $hPen, $hGraphic #cs Visual relation identifier Similar to UFT version accessable from the Object Repository Returns the handle to the control Melba23's ArrayMultiColSort to arrange controls #ce ;Create a quick GUI to use as a sample $hGUI = GUICreate("TestApp", 600, 400) $hInfo = GUICtrlCreateLabel("F1 to find Left, F2 to Find Top, F3 to Find Right, F4 to find Bottom", 5, 3, 590, 17, $SS_CENTER) $Edit = GUICtrlCreateEdit("Anchor Control, F1 find Left", 200, 150, 200, 100) GUICtrlCreateLabel("Search Padding: ", 350, 23, 200, 17, $SS_RIGHT) $hPadding = GUICtrlCreateInput("0", 550, 20, 40, 20) GUICtrlCreateUpdown(-1) ;Controls on the left of the Anchor GUICtrlCreateLabel("Label", 10, 133, 80, 17) GUICtrlCreateButton("Button", 95, 130, 100, 20) ;Technically Above, but adding padding should catch this on left GUICtrlCreateButton("Button", 95, 155, 100, 20) GUICtrlCreateButton("Button", 10, 155, 80, 20) GUICtrlCreateButton("Button", 95, 180, 100, 20) GUICtrlCreateButton("Button", 95, 205, 100, 20) GUICtrlCreateButton("Button", 95, 230, 100, 20) GUICtrlCreateButton("Button", 10, 255, 80, 20) ;Technically below, but adding padding should catch this on left GUICtrlCreateButton("Button", 95, 255, 100, 20) ;Technically below, but adding padding should catch this on left ;Controls on the Top of the Anchor GUICtrlCreateLabel("Label", 10, 30, 100, 17) GUICtrlCreateButton("Button", 10, 50, 100, 20) ;Could be Above, or Left with enough padding GUICtrlCreateButton("Button", 10, 75, 100, 20) ;Could be Above, or Left with enough padding GUICtrlCreateButton("Button", 120, 50, 100, 20) ;Could be Above, or Left with enough padding GUICtrlCreateButton("Button", 120, 75, 100, 20) ;Could be Above, or Left with enough padding GUICtrlCreateButton("Button", 230, 50, 100, 20) ;If I tighten the padding to -25 this should be found as Instance 1 GUICtrlCreateButton("Button", 230, 75, 100, 20) GUICtrlCreateButton("Button", 230, 100, 100, 20) GUICtrlCreateButton("Button", 230, 125, 100, 20) GUICtrlCreateButton("Button", 340, 100, 100, 20) ;Could be Above, or Right with enough padding GUICtrlCreateButton("Button", 340, 125, 100, 20) ;Could be Above, or Right with enough padding GUICtrlCreateButton("Button", 450, 50, 100, 20) ;Could be Above, or Right with enough padding GUICtrlCreateButton("Button", 450, 75, 100, 20) ;Could be Above, or Right with enough padding GUICtrlCreateButton("Button", 450, 100, 100, 20) ;Could be Above, or Right with enough padding ;Controls on the Right of the Anchor GUICtrlCreateLabel("Label", 410, 153, 80, 17) GUICtrlCreateButton("Button", 500, 150, 80, 20) GUICtrlCreateButton("Button", 410, 175, 80, 20) GUICtrlCreateButton("Button", 500, 175, 80, 20) GUICtrlCreateButton("Button", 410, 200, 80, 20) GUICtrlCreateButton("Button", 500, 200, 80, 20) GUICtrlCreateButton("Button", 410, 225, 80, 20) GUICtrlCreateButton("Button", 500, 225, 80, 20) ;Controls on the Bottom of the Anchor GUICtrlCreateLabel("Label", 205, 253, 80, 17) GUICtrlCreateButton("Button", 410, 251, 80, 20) GUICtrlCreateButton("Button", 500, 251, 80, 20) GUICtrlCreateButton("Button", 10, 280, 100, 20) GUICtrlCreateButton("Button", 110, 280, 100, 20) GUICtrlCreateButton("Button", 220, 280, 100, 20) GUICtrlCreateButton("Button", 330, 280, 100, 20) GUICtrlCreateButton("Button", 440, 280, 100, 20) GUICtrlCreateButton("Button", 10, 310, 100, 20) GUICtrlCreateButton("Button", 110, 305, 100, 20) ;Y coord is slightly higher, this instance should be before the control above it GUICtrlCreateButton("Button", 440, 350, 100, 20) GUISetState(@SW_SHOW, $hGUI) $hEdit = ControlGetHandle($hGUI, "", $Edit) HotKeySet("{F1}", "_GetLeft") ;Adjust Padding and Instance within the function below HotKeySet("{F2}", "_GetAbove") ;Adjust Padding and Instance within the function below HotKeySet("{F3}", "_GetRight") ;Adjust Padding and Instance within the function below HotKeySet("{F4}", "_GetBelow") ;Adjust Padding and Instance within the function below ; Loop until the user exits. While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd Func _GetLeft() _GUICtrlEdit_AppendText($hEdit, @CRLF & "F1 (Get Left selected)") ;Update the Edit to show what was selected $Foundhwnd = _VisualRelation_Find(1, "Button", 2, $hEdit, GUICtrlRead($hPadding)) ;Find the first button to the left of Anchor where Vertical padding is +- Specified If @error Then ConsoleWrite("Error (" & @error & ") returned" & @CRLF) Return EndIf ControlSetText($hGUI, "", $Foundhwnd, "FOUND!") ;Update the text for the control it identified ConsoleWrite("Handle returned = (" & $Foundhwnd & ")" & @CRLF) GUICtrlSetData($hInfo, "Buttons renamed to order they were found. The requested control changed to FOUND!") EndFunc Func _GetAbove() _GUICtrlEdit_AppendText($hEdit, @CRLF & "F2 (Get Above selected)") ;Update the Edit to show what was selected $Foundhwnd = _VisualRelation_Find(1, "Button", 0, $hEdit, GUICtrlRead($hPadding)) ;Find the first button above the anchor where Horizontal padding is +- specified If @error Then ConsoleWrite("Error (" & @error & ") returned" & @CRLF) Return EndIf ControlSetText($hGUI, "", $Foundhwnd, "FOUND!") ;Update the text for the control it identified ConsoleWrite("Handle returned = (" & $Foundhwnd & ")" & @CRLF) GUICtrlSetData($hInfo, "Buttons renamed to order they were found. The requested control changed to FOUND!") EndFunc Func _GetRight() MsgBox(0, "", "Not ready yet") ;_VisualRelation_Find(1, "Button", 3, $hEdit, 0) EndFunc Func _GetBelow() MsgBox(0, "", "Not ready yet") ;_VisualRelation_Find(1, "Button", 1, $hEdit, 0) EndFunc ;----------------------------------------------------------------------------------------------------- ; Name........... _VisualRelation_Find ; Description.... Find a specific control depending on its location to a known control (Anchor point) ; Syntax......... _VisualRelation_Find($iInstance, $sClass, $sDirection, $hAnchor) ; Parameters .... $iInstance = which instance found from the Anchor ; 1 = First control found in direction specified from Anchor ; 2 = Second control found in the direction specified from Anchor ; etc... ; $sClass = Class name of the control, do not use ClassnameNN or any other type ; $sDirection = 0 = Above Anchor, 1 = Below Anchor, 2 = Left of Anchor, 3 = Right of Anchor ; $hAnchor = hwnd of the known control we are identifying from ; $iPadding = amount to + or - from each side of Anchor size to match control within, Default = 0 ; ex: 10, Above: find the control above the Anchor where x between (Anchor X - 10 and Anchor X + Anchor W + 10) ; ex: 5, Right: find the control to the right of the Anchor where Y between (Anchor Y - 5 and Anchor Y + Anchor H + 5) ; Return values.. Success = Returns the hwnd ; Failure = Sets @error ; 1 = The Anchor was not passed as a hwnd ; 2 = Failed to find the parent window of the Anchor ; 3 = Unable to identify the position of the Anchor control ; 4 = Could not find any of the specified Classes within the Anchor window ; 5 = Could not find any of the specified Classes in the position specified ; 6 = Could not find the Instance specified in the position specified ; Author ........ BigDaddyO ; Modified by.... ; Remarks ....... requires Include: WinAPI.au3, WindowsConstants.au3, Melba23's _ArrayMultiColSort ; Example........ $sClassnameNN = _VisualRelation_Find(1, "Button", 2, $hEdit, 0) ;----------------------------------------------------------------------------------------------------- Func _VisualRelation_Find($iInstance, $sClass, $sDirection, $hAnchor, $iPadding = 0) $bDebug = True ;Used for debugging, change to False once working properly ;Ensure the $hAnchor is a handle If IsHWnd($hAnchor) = 0 Then Return SetError(1) ;Get the handle for the window the $hAnchor control is in $hAnchorWin = _WinAPI_GetAncestor($hAnchor, $GA_ROOT) If IsHWnd($hAnchorWin) = 0 Then Return SetError(2) ;Get the current position of the Anchor control $aAnchorPos = ControlGetPos($hAnchorWin, "", $hAnchor) If @error Then Return SetError(3) Local $iInstanceFound = 0 Local $v = 0 Local $aFound[9999][5] ;[][0] = hwnd, [][1] = X coord, [][2] = Y coord, X2 coord, Y2 coord ;Find every Control with the specified $sClass While 1 $hCtrl = ControlGetHandle($hAnchorWin, "", "[CLASS:" & $sClass & "; INSTANCE:" & $v + 1 & "]") If @error Then If $v = 0 Then Return SetError(4) ;Did not find any of the Class type in the window Else ExitLoop ;No more of this Class were found stop looking and start trying to match the requested area EndIf EndIf $v += 1 $aCtrlPos = ControlGetPos($hAnchorWin, "", $hCtrl) If $bDebug = True Then ConsoleWrite("Found (" & $hCtrl & ") at " & $aCtrlPos[0] & ", " & $aCtrlPos[1] & @CRLF) ControlSetText($hAnchorWin, "", $hCtrl, $sClass & $v) EndIf ;Save this control: Handle, X1, Y1, X2, Y2 $aFound[$v - 1][0] = $hCtrl ;Save the Instance # so we can find again $aFound[$v - 1][1] = $aCtrlPos[0] ;X coord of left side $aFound[$v - 1][2] = $aCtrlPos[1] ;Y coord of top $aFound[$v - 1][3] = $aCtrlPos[0] + $aCtrlPos[2] ;X coord of right side $aFound[$v - 1][4] = $aCtrlPos[1] + $aCtrlPos[3] ;Y coord of bottom WEnd ReDim $aFound[$v][5] Local $aSortData[][] = [[2, 0],[1,0]] _ArrayMultiColSort($aFound, $aSortData) ;Remove any existing graphic from a previous find: doesnt remove old lines! If $bDebug = True Then _GDIPlus_PenDispose($hPen) _GDIPlus_GraphicsDispose($hGraphic) _GDIPlus_Shutdown() EndIf ;Now to get the control at the location we were looking for Local $iFoundInstance = 0 Switch $sDirection Case 0 ;Above Anchor control and within the X padding If $bDebug = True Then ;Draw lines to show where it was looking $hGDI = _GDIPlus_Startup(Default, True) $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hAnchorWin) $hPen = _GDIPlus_PenCreate(0xFFFF0000) _GDIPlus_GraphicsDrawLine($hGraphic, $aAnchorPos[0] - $iPadding, $aAnchorPos[1], $aAnchorPos[0] - $iPadding, 0, $hPen) _GDIPlus_GraphicsDrawLine($hGraphic, $aAnchorPos[0] + $aAnchorPos[2] + $iPadding, $aAnchorPos[1], $aAnchorPos[0] + $aAnchorPos[2] + $iPadding, 0, $hPen) EndIf ;Loop through the control found array For $d = 0 to UBound($aFound) - 1 ;If the right of the control is greater than the left of the Anchor minus the padding, AND the left of the control is less than the Anchor plus the padding If ( $aFound[$d][3] >= ($aAnchorPos[0] - $iPadding) ) and ( $aFound[$d][1] <= ($aAnchorPos[0] + $aAnchorPos[2] + $iPadding) ) Then ;if the top of the control is less than the top of the Anchor If $aFound[$d][2] <= $aAnchorPos[0] Then $iFoundInstance += 1 If $iFoundInstance = $iInstance Then Return $aFound[$d][0] Else ContinueLoop EndIf Else ContinueLoop ;This control is not within the Vertical limits of the Anchor and Padding EndIf Else ContinueLoop ;This control is not within the Horizontal limits of the Anchor and Padding EndIf Next Case 1 ;Below Anchor control and within the X padding Case 2 ;Left of Anchor control and within the Y padding If $bDebug = True Then ;Draw lines to show where it was looking $hGDI = _GDIPlus_Startup(Default, True) $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hAnchorWin) $hPen = _GDIPlus_PenCreate(0xFFFF0000) _GDIPlus_GraphicsDrawLine($hGraphic, $aAnchorPos[0], $aAnchorPos[1] - $iPadding, 0, $aAnchorPos[1] - $iPadding, $hPen) _GDIPlus_GraphicsDrawLine($hGraphic, $aAnchorPos[0], $aAnchorPos[1] + $aAnchorPos[3] + $iPadding, 0, $aAnchorPos[1] + $aAnchorPos[3] + $iPadding, $hPen) EndIf ;Loop through the control found array For $d = 0 to UBound($aFound) - 1 ;If the top of the control is above the bottom of the anchor plus the padding AND the bottom of the control is below the top of the Anchor minus the padding If ( $aFound[$d][2] <= ($aAnchorPos[1] + $aAnchorPos[3] + $iPadding) ) and ( $aFound[$d][4] > ($aAnchorPos[1] - $iPadding) ) Then ;If the left of the control is < the left of the Anchor If $aFound[$d][1] < $aAnchorPos[1] Then $iFoundInstance += 1 If $iFoundInstance = $iInstance Then Return $aFound[$d][0] Else ContinueLoop EndIf Else ContinueLoop ;This control is not within the Horizontal limits of Left of the Anchor EndIf Else ContinueLoop ;This control is not within the Vertical limits of the Anchor and Padding EndIf Next Case 3 ;Right of Anchor control EndSwitch Return SetError(6) ;If we got this far, then we found something but not the instance we wanted. EndFunc ; #INDEX# ======================================================================================================================= ; Title .........: ArrayMultiColSort ; AutoIt Version : v3.3.8.1 or higher ; Language ......: English ; Description ...: Sorts 2D arrays on several columns ; Note ..........: ; Author(s) .....: Melba23 ; Remarks .......: ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ; _ArrayMultiColSort : Sort 2D arrays on several columns ; =============================================================================================================================== ; #INTERNAL_USE_ONLY#================================================================================================= ; __AMCS_SortChunk : Sorts array section ; =============================================================================================================================== ; #FUNCTION# ==================================================================================================================== ; Name...........: _ArrayMultiColSort ; Description ...: Sort 2D arrays on several columns ; Syntax.........: _ArrayMultiColSort(ByRef $aArray, $aSortData[, $iStart = 0[, $iEnd = 0]]) ; Parameters ....: $aArray - The 2D array to be sorted ; $aSortData - 2D array holding details of the sort format ; Format: [Column to be sorted, Sort order] ; Sort order can be either numeric (0/1 = ascending/descending) or a ordered string of items ; Any elements not matched in string are left unsorted after all sorted elements ; $iStart - Element of array at which sort starts (default = 0) ; $iEnd - Element of array at which sort endd (default = 0 - converted to end of array) ; Requirement(s).: v3.3.8.1 or higher ; Return values .: Success: No error ; Failure: @error set as follows ; @error = 1 with @extended set as follows (all refer to $sIn_Date): ; 1 = Array to be sorted not 2D ; 2 = Sort data array not 2D ; 3 = More data rows in $aSortData than columns in $aArray ; 4 = Start beyond end of array ; 5 = Start beyond End ; @error = 2 with @extended set as follows: ; 1 = Invalid string parameter in $aSortData ; 2 = Invalid sort direction parameter in $aSortData ; 3 = Invalid column index in $aSortData ; Author ........: Melba23 ; Remarks .......: Columns can be sorted in any order ; Example .......; Yes ; =============================================================================================================================== Func _ArrayMultiColSort(ByRef $aArray, $aSortData, $iStart = 0, $iEnd = 0) ; Errorchecking ; 2D array to be sorted If UBound($aArray, 2) = 0 Then Return SetError(1, 1, "") EndIf ; 2D sort data If UBound($aSortData, 2) <> 2 Then Return SetError(1, 2, "") EndIf If UBound($aSortData) > UBound($aArray) Then Return SetError(1, 3) EndIf For $i = 0 To UBound($aSortData) - 1 If $aSortData[$i][0] < 0 Or $aSortData[$i][0] > UBound($aArray, 2) -1 Then Return SetError(2, 3, "") EndIf Next ; Start element If $iStart < 0 Then $iStart = 0 EndIf If $iStart >= UBound($aArray) - 1 Then Return SetError(1, 4, "") EndIf ; End element If $iEnd <= 0 Or $iEnd >= UBound($aArray) - 1 Then $iEnd = UBound($aArray) - 1 EndIf ; Sanity check If $iEnd <= $iStart Then Return SetError(1, 5, "") EndIf Local $iCurrCol, $iChunk_Start, $iMatchCol ; Sort first column __AMCS_SortChunk($aArray, $aSortData, 0, $aSortData[0][0], $iStart, $iEnd) If @error Then Return SetError(2, @extended, "") EndIf ; Now sort within other columns For $iSortData_Row = 1 To UBound($aSortData) - 1 ; Determine column to sort $iCurrCol = $aSortData[$iSortData_Row][0] ; Create arrays to hold data from previous columns Local $aBaseValue[$iSortData_Row] ; Set base values For $i = 0 To $iSortData_Row - 1 $aBaseValue[$i] = $aArray[$iStart][$aSortData[$i][0]] Next ; Set start of this chunk $iChunk_Start = $iStart ; Now work down through array For $iRow = $iStart + 1 To $iEnd ; Match each column For $k = 0 To $iSortData_Row - 1 $iMatchCol = $aSortData[$k][0] ; See if value in each has changed If $aArray[$iRow][$iMatchCol] <> $aBaseValue[$k] Then ; If so and row has advanced If $iChunk_Start < $iRow - 1 Then ; Sort this chunk __AMCS_SortChunk($aArray, $aSortData, $iSortData_Row, $iCurrCol, $iChunk_Start, $iRow - 1) If @error Then Return SetError(2, @extended, "") EndIf EndIf ; Set new base value $aBaseValue[$k] = $aArray[$iRow][$iMatchCol] ; Set new chunk start $iChunk_Start = $iRow EndIf Next Next ; Sort final section If $iChunk_Start < $iRow - 1 Then __AMCS_SortChunk($aArray, $aSortData, $iSortData_Row, $iCurrCol, $iChunk_Start, $iRow - 1) If @error Then Return SetError(2, @extended, "") EndIf EndIf Next EndFunc ;==>_ArrayMultiColSort ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: __AMCS_SortChunk ; Description ...: Sorts array section ; Author ........: Melba23 ; Remarks .......: ; =============================================================================================================================== Func __AMCS_SortChunk(ByRef $aArray, $aSortData, $iRow, $iColumn, $iChunkStart, $iChunkEnd) Local $aSortOrder ; Set default sort direction Local $iSortDirn = 1 ; Need to prefix elements? If IsString($aSortData[$iRow][1]) Then ; Split elements $aSortOrder = StringSplit($aSortData[$iRow][1], ",") If @error Then Return SetError(1, 1, "") EndIf ; Add prefix to each element For $i = $iChunkStart To $iChunkEnd For $j = 1 To $aSortOrder[0] If $aArray[$i][$iColumn] = $aSortOrder[$j] Then $aArray[$i][$iColumn] = StringFormat("%02i-", $j) & $aArray[$i][$iColumn] ExitLoop EndIf Next ; Deal with anything that does not match If $j > $aSortOrder[0] Then $aArray[$i][$iColumn] = StringFormat("%02i-", $j) & $aArray[$i][$iColumn] EndIf Next Else Switch $aSortData[$iRow][1] Case 0, 1 ; Set required sort direction if no list If $aSortData[$iRow][1] Then $iSortDirn = -1 Else $iSortDirn = 1 EndIf Case Else Return SetError(1, 2, "") EndSwitch EndIf ; Sort the chunk Local $iSubMax = UBound($aArray, 2) - 1 __ArrayQuickSort2D($aArray, $iSortDirn, $iChunkStart, $iChunkEnd, $iColumn, $iSubMax) ; Remove any prefixes If IsString($aSortData[$iRow][1]) Then For $i = $iChunkStart To $iChunkEnd $aArray[$i][$iColumn] = StringTrimLeft($aArray[$i][$iColumn], 3) Next EndIf EndFunc ;==>__AMCS_SortChunk Edited December 12, 2019 by BigDaddyO Link to comment Share on other sites More sharing options...
BigDaddyO Posted December 12, 2019 Author Share Posted December 12, 2019 Figured out the -25 issue, I was checking against the control X coord instead of Y coord. argumentum 1 Link to comment Share on other sites More sharing options...
junkew Posted December 13, 2019 Share Posted December 13, 2019 I have this logic in the uiawrappers thread in the examples. You can use index or indexrelative. Search in the examples zip file for indexrelative where you can go minus or plus nn objects from a found object. Basically with AutoIt you can handle more then HP UFT exception is mobile and terminal emulation much harder in AutoIt FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now