Leaderboard
Popular Content
Showing content with the highest reputation on 05/10/2019 in all areas
-
$RunningTime = TimerInit() Sleep(1234) MsgBox(0, "Running Time", Round(TimerDiff($RunningTime) / 1000, 2) & " seconds") MsgBox(0, "Running Time", StringFormat("%.2f", TimerDiff($RunningTime) / 1000) & " seconds") Pick your choice.2 points
-
I have had a need for quite a while to redact sensitive information in screenshots before sending them off to 3rd party auditors. Previously I have done this by using HP UFT which has built in ocr to find the text, and then pass that to my AutoIt script to redact that information and take the screenshot. Using Tesseract and the UDF provided by seangriffin as a great starting point I was able to finally incorporate all of this into just AutoIt. This requires the Tesseract v3.0.5+ as it uses the tsv option. I wrote this using v4.0.0 which is available here I’ve never needed to use OCR to find text to click on but if you just want to use this for that, then the array returned by _TesseractFindCoords() will provide that. For some reason ScITE refreshes the screen a lot while running which can delete the redaction so it’s best to have ScITE on a different screen than what Explorer will open on when running this example. Run Script, Pick 1 or more files in a single folder then click Open. The script will then ShellExecute to open that selected folder and a screenshot will be taken. OCR will be performed and the selected files will be redacted, then a screenshot will be taken of that redacted screen. The redacted screenshot will then be displayed. The main functions that I’d love some feedback on are the _TesseractCaptureText() and _TesseractFindCoords(). Also, If you know of a way to prevent refreshes from deleting the _WinAPI_DrawLine in the Redaction I’d love to hear it, though it doesn’t seem to be a problem when I run as compiled. #include <WindowsConstants.au3> #include <File.au3> ;Required to create a unique ~temp file #include <ScreenCapture.au3> ;Required to capture image Global $aOCR_IMG_ORI ;Global Var that will store the location of the image that was taken to perform OCR on Global $tesseract_temp_path = @TempDir & "\" ;Location of the image that is taken, and the .tsv file that is generated from that Global $tesseract_path = @ScriptDir & "\Tesseract-OCR\" ;Need to have the Tesseract-OCR folder in the sub-folder of the script thats running Global $sPath, $aFileName ;For this example, you will browse to a folder and select a file. that files name will be the text to search for ; That files location will then be opened in Windows Explorer and OCR will be performed on that to find the file within that folder ; Note: file name must be visible to find so pick a folder without too many files or file names that are longer than can be displayed ;Pick file(s) to OCR find within Explorer $sFile = FileOpenDialog("Pick a file", @ScriptDir, "All (*.*)", 4) $aFiles = StringSplit($sFile, "|") If $aFiles[0] > 1 Then ;Multiple files were selected. ;[1] will contain the folder ;[2]+ will contain the individual files Global $aFileName[$aFiles[0] - 1] $sPath = $aFiles[1] & "\" For $i = 2 to $aFiles[0] $aFileName[$i - 2] = $aFiles[$i] Next Else ;Only 1 file was selected, split it up into parts so the array will be setup same as if multiple files were selected Local $sDrive, $sDir, $sFileName, $sExtension $aPathSplit = _PathSplit($sFile, $sDrive, $sDir, $sFileName, $sExtension) $sPath = $sDrive & $sDir Global $aFileName[1] = [$sFileName & $sExtension] EndIf SplashTextOn("MyOCR_Example", "This can take a few minutes..." & @CRLF & "Opening Location", 500, 40, 0, 0) ShellExecute($sPath, "open") WinWaitActive("[CLASS:CabinetWClass]", StringLeft($sPath, StringLen($sPath) - 1), 15) ;Wait until WinExplorer is opened ControlSetText("MyOCR_Example", "", "Static1", "Found WinExplorer") ;Capture all text from the main area of Windows Explorer ControlSetText("MyOCR_Example", "", "Static1", "Getting all text") $aTSV = _TesseractCaptureText("[CLASS:CabinetWClass]", "", "[CLASS:DirectUIHWND; INSTANCE:3]", 2, 0, 0, 0, 0, 1, 1) ;Capture text from the explorer item within windows explorer ;From the captured text, find what we are looking for ;Must not do anything that would cause screen refreshes in the loop or before taking screenshot as that can erase redaction, ControlSetText in SplashText can break it too... For $i = 0 to UBound($aFileName) - 1 $sFileName = StringLeft($aFileName[$i], StringInStr($aFileName[$i], ".", 0, -1) - 1) ;Strip off the Extension since some systems hide extensions ConsoleWrite("Looking for " & $sFileName) $aCoords = _TesseractFindCoords($aTSV, $sFileName, 0, 1, 2) ;Returns x,y,w,h of requested text ConsoleWrite(@TAB & "Drawing a black line from " & $aOCR_IMG_ORI[0] + $aCoords[0] & "," & $aOCR_IMG_ORI[1] + $aCoords[1] & " to " & $aCoords[2] & "," & $aCoords[3] & @CRLF) _Redact($aOCR_IMG_ORI[0] + $aCoords[0], $aOCR_IMG_ORI[1] + $aCoords[1], $aCoords[2], $aCoords[3], 0x000000) ;Black out/Redact the selected text Next ;Now that everything is redacted, take the screenshot $hwnd2 = ControlGetHandle("[CLASS:CabinetWClass]", "", "[CLASS:DirectUIHWND; INSTANCE:3]") _ScreenCapture_CaptureWnd(@ScriptDir & "\Redacted.jpg", $hwnd2) MsgBox(0, "", "Redacted" & @CRLF & "Click OK to display screenshot") ShellExecute(@ScriptDir & "\Redacted.jpg") ;=============================================================================== ; Name...........: _TesseractFindCoords() ; Description ...: Retreives the Coordinates for specific text from the text returned by a Tesseract OCR TSV function ; Syntax.........: _TesseractFindCoords($aTSV, $sFind, $iFindType = 0, $iMatch = 1) ; Parameters ....: $aTSV - Array returned from one of the _Tesseract-CapturePOS() functions ; $sFind - String value of the text to find, spacing will be ignored as Tesseract seperates array by words ignoring spaces. ; $iFindType - How to search for the text ; 0 = Contains anywhere (StringInStr) (default) ; 1 = Starts with (StringLeft) ; 2 = Ends with (StringRight) ; 3 = Exact Match (=) ; $iMatch - If multiple matches may exist, which of them do we want to return ; 1 = Return the first one we find (default) ; -1 = Return the last one we find ; # = Return the # item that we find ; $scale - This must be the same scale as used in the _TesseractCaptureText() function as it will be used to adjust the coordinates ; ; 2nd to last Column = conf. Confidence % that it's actually the word, may want to incorporate this as an additional option ; ; Return values .: On Success - Returns an array of coordinates for the matched text. ; On Failure - Sets @error, returns empty array ; ; Author ........: BigDaddyO ; Modified.......: ; ; Remarks .......: Array returned from the _TesseractCaptureText() functions contains the text and their location ; This function finds the $sFind text within that array. ; Note: All text from the image is broken down into individual words, so if the $sFind contains any spaces then it will ; be split up into an array itself and will be matched as concurrent items in the array. ; The Coordinates of each word are then added together to make coords for a single box including calculated space width ; ; Related .......: Requires Tesseract 3.05+ to use TSV commandline option ; Link ..........: ; Example .......: ;========================================================================================== Func _TesseractFindCoords($aTSV, $sTextToFind, $iFindType = 0, $iMatch = 1, $scale = 2) If $iMatch = 0 Then ConsoleWrite("Error, $iMatch 0 is not valid" & @CRLF) Return SetError(1) EndIf local $aCoords[4] ;Create the array that will store the coordinates Local $iFoundCount = 0 $sTextToFind = StringStripWS($sTextToFind, 7) ;Remove any Trailing, leading, or double spaces so it will split and match up properly If $sTextToFind = "" Then ConsoleWrite("Error, the $sTextToFind is empty" & @CRLF) Return SetError(1, 0, $aCoords) EndIf If IsArray($aTSV) = 0 Then ConsoleWrite("Error, $aTSV is not an array" & @CRLF) Return SetError(1, 0, $aCoords) EndIf If UBound($aTSV, 2) <> 12 Then ConsoleWrite("OCR Text array size = ([" & UBound($aTSV) & "][" & UBound($aTSV, 2) & "]), does not match the expected [#][12]. Ensure you have the $tsv flag set to 1 in the _TesseractCaptureText() Function" & @CRLF) Return SetError(2, 0, $aCoords) EndIf $aSearchText = StringSplit($sTextToFind, " ") ;Split up the $sTextToFind into individual words as Tesseract stores them Switch $iFindType Case 0 ;Looks anywhere for the word/sentence to start and end, words within a sentence must be exact For $i = 1 to UBound($aTSV) - 1 ;Search the contents of the OCR text array If $aSearchText[0] > 1 Then ;This is a sentence, not just a word, so ensure this ends with the first word, not just InStr If StringRight($aTSV[$i][11], StringLen($aSearchText[1])) <> $aSearchText[1] Then ContinueLoop ;This is not the text we are looking for EndIf Else ;Looking for just a single word, so it could be anywhere If StringInStr($aTSV[$i][11], $aSearchText[1]) = 0 Then ContinueLoop ;This is not the text we are looking for EndIf EndIf Local $aTmpCoords[4] ;Create array to store the coords until we are sure it's a full match Local $iFoundAt = $i ;Save the # we found the first word at, incase this isn't the whole sentence Local $iCount = 0 ;Used to count the current word in the search text array we are on For $t = $i to $i + ($aSearchText[0] - 1) ;Looks like this is what we want, verify everything matches. $iCount += 1 Select Case $iCount = $aSearchText[0] ;This is the last word in the text we are looking for If $iCount = 1 Then ;If this is actually the first & last then we need to save the Coords directly If StringInStr($aTSV[$t][11], $aSearchText[($t - $iFoundAt) + 1]) Then ;This is the first & Last word to find, so do an InStr $aTmpCoords[0] = $aTSV[$t][6] ;Save the left value into the Temp Coords array $aTmpCoords[1] = $aTSV[$t][7] ;Save the Top value into the Temp Coords array $aTmpCoords[2] = $aTSV[$t][8] ;Save the Width value into the Temp Coords array $aTmpCoords[3] = $aTSV[$t][9] ;Save the Height value into the Temp Coords array Else ContinueLoop(2) ;not found, so continue looking for full match EndIf Else ;This is the last but not the first, so we need to do a StringLeft instead of InStr to ensure the sentence is continues. If StringLeft($aTSV[$t][11], StringLen($aSearchText[($t - $iFoundAt) + 1])) = $aSearchText[($t - $iFoundAt) + 1] Then If $aTmpCoords[1] > $aTSV[$t][7] Then $aTmpCoords[1] = $aTSV[$t][7] ;Use the top most coord avail $aTmpCoords[2] += $aTSV[$t][8] ;Add this words width to what we already have If $aTmpCoords[3] < $aTSV[$t][9] Then $aTmpCoords[3] = $aTSV[$t][9] ;Use the value with the greatest height Else ContinueLoop(2) EndIf EndIf $iFoundCount += 1 ;This is a full match, so Increase the successful match by 1 $aCoords[0] = $aTmpCoords[0] ;Save the Temp Coordinates $aCoords[1] = $aTmpCoords[1] $aCoords[2] = $aTmpCoords[2] $aCoords[3] = $aTmpCoords[3] If $iMatch = $iFoundCount Then ;See if this is the match we want, defaults to the first match found ;If there are multiple words, need to identify how many pixels to include where the spaces should have been If $aSearchText[0] > 1 Then $iSpace = $aTSV[$t][8] / StringLen($aTSV[$t][11]) ;Get width of this word, then divide by the character length to get pixels per character $sTxtOnly = StringStripWS($sTextToFind, 8) $iSpace = $aCoords[2] / StringLen($sTxtOnly) ;Divide the total width by the number of characters to get Pixels per character $aCoords[2] += ($iSpace * ($aSearchText[0] - 1)) EndIf ;Convert the coordinates to onscreen coords according to the scale $aCoords[0] = $aCoords[0] / $scale $aCoords[1] = $aCoords[1] / $scale $aCoords[2] = $aCoords[2] / $scale $aCoords[3] = $aCoords[3] / $scale Return $aCoords Else ContinueLoop(2) ;We found the full search text, but we don't want this one. EndIf Case $iCount = 1 If $aSearchText[0] > 1 Then ;This is a sentence, not just a word, so ensure this ends with the first word, not just InStr If StringRight($aTSV[$t][11], StringLen($aSearchText[($t - $iFoundAt) + 1])) <> $aSearchText[($t - $iFoundAt) + 1] Then ContinueLoop(2) ;This is not the text we are looking for EndIf Else If StringInStr($aTSV[$t][11], $aSearchText[($t - $iFoundAt) + 1]) = 0 Then ContinueLoop(2) ;This is not the text we are looking for EndIf EndIf $aTmpCoords[0] = $aTSV[$t][6] ;Save the left value into the Temp Coords array $aTmpCoords[1] = $aTSV[$t][7] ;Save the top value into the Temp Coords array $aTmpCoords[2] = $aTSV[$t][8] ;Save the width value into the Temp Coords array $aTmpCoords[3] = $aTSV[$t][9] ;Save the height value into the Temp Coords array ContinueLoop ;Look for the next word Case Else ;This is a middle word in the search text, so do an = If $aTSV[$t][11] = $aSearchText[$t - $iFoundAt + 1] Then ;Leave the [0] coord as-is If $aTmpCoords[1] > $aTSV[$t][7] Then $aTmpCoords[1] = $aTSV[$t][7] ;Use the top most coord avail $aTmpCoords[2] += $aTSV[$t][8] ;Add this words width to what we already have If $aTmpCoords[3] < $aTSV[$t][9] Then $aTmpCoords[3] = $aTSV[$t][9] ;Use the value with the greatest height ContinueLoop ;Possible that there is only 1 search word, so need to continue loop so the Start and End don't get triggered Else $i = $iFoundAt ContinueLoop(2) ;if we didn't find the last word in the search text, start the search over EndIf EndSelect Next Next Case 1 ;The word/sentence starts with the specified text For $i = 1 to UBound($aTSV) - 1 ;Search the contents of the OCR text array If $aSearchText[0] > 1 Then ;This is a sentence, not just a word, so ensure the entire first word matches If $aTSV[$i][11] <> $aSearchText[1] Then ContinueLoop EndIf Else ;Looking for just a single word, so ensure it starts with this If StringLeft($aTSV[$i][11], StringLen($aSearchText[1])) <> $aSearchText[1] Then ContinueLoop ;This is not the text we are looking for EndIf EndIf Local $aTmpCoords[4] ;Create array to store the coords until we are sure it's a full match Local $iFoundAt = $i ;Save the # we found the first word at, incase this isn't the whole sentence Local $iCount = 0 ;Used to count the current word in the search text array we are on For $t = $i to $i + ($aSearchText[0] - 1) ;Looks like this is what we want, verify everything matches. $iCount += 1 Select Case $iCount = $aSearchText[0] ;This is the last word in the text we are looking for If $iCount = 1 Then ;If this is actually the first & last then we need to save the Coords directly If StringLeft($aTSV[$i][11], StringLen($aSearchText[1])) = $aSearchText[($t - $iFoundAt) + 1] Then ;This is the first & Last word to find, so ensure it starts with $aTmpCoords[0] = $aTSV[$t][6] ;Save the left value into the Temp Coords array $aTmpCoords[1] = $aTSV[$t][7] ;Save the Top value into the Temp Coords array $aTmpCoords[2] = $aTSV[$t][8] ;Save the Width value into the Temp Coords array $aTmpCoords[3] = $aTSV[$t][9] ;Save the Height value into the Temp Coords array Else ContinueLoop(2) ;not found, so continue looking for full match EndIf Else ;This is the last but not the first, so we need to do a StringLeft to ensure the sentence is continues. If StringLeft($aTSV[$t][11], StringLen($aSearchText[($t - $iFoundAt) + 1])) = $aSearchText[($t - $iFoundAt) + 1] Then If $aTmpCoords[1] > $aTSV[$t][7] Then $aTmpCoords[1] = $aTSV[$t][7] ;Use the top most coord avail $aTmpCoords[2] += $aTSV[$t][8] ;Add this words width to what we already have If $aTmpCoords[3] < $aTSV[$t][9] Then $aTmpCoords[3] = $aTSV[$t][9] ;Use the value with the greatest height Else ContinueLoop(2) EndIf EndIf $iFoundCount += 1 ;This is a full match, so Increase the successful match by 1 $aCoords[0] = $aTmpCoords[0] ;Save the Temp Coordinates $aCoords[1] = $aTmpCoords[1] $aCoords[2] = $aTmpCoords[2] $aCoords[3] = $aTmpCoords[3] If $iMatch = $iFoundCount Then ;See if this is the match we want, defaults to the first match found ;If there are multiple words, need to identify how many pixels to include where the spaces should have been If $aSearchText[0] > 1 Then $iSpace = $aTSV[$t][8] / StringLen($aTSV[$t][11]) ;Get width of this word, then divide by the character length to get pixels per character $sTxtOnly = StringStripWS($sTextToFind, 8) $iSpace = $aCoords[2] / StringLen($sTxtOnly) ;Divide the total width by the number of characters to get Pixels per character $aCoords[2] += ($iSpace * ($aSearchText[0] - 1)) EndIf ;Convert the coordinates to onscreen coords according to the scale $aCoords[0] = $aCoords[0] / $scale $aCoords[1] = $aCoords[1] / $scale $aCoords[2] = $aCoords[2] / $scale $aCoords[3] = $aCoords[3] / $scale Return $aCoords Else ContinueLoop(2) ;We found the full search text, but we don't want this one. EndIf Case $iCount = 1 If $aSearchText[0] > 1 Then ;This is a sentence, not just a word, so ensure this matches the first word exactly If $aTSV[$t][11] <> $aSearchText[($t - $iFoundAt) + 1] Then ContinueLoop(2) ;This is not the text we are looking for EndIf Else If StringLeft($aTSV[$t][11], StringLen($aSearchText[($t - $iFoundAt) + 1])) <> $aSearchText[($t - $iFoundAt) + 1] Then ContinueLoop(2) ;This is not the text we are looking for EndIf EndIf $aTmpCoords[0] = $aTSV[$t][6] ;Save the left value into the Temp Coords array $aTmpCoords[1] = $aTSV[$t][7] ;Save the top value into the Temp Coords array $aTmpCoords[2] = $aTSV[$t][8] ;Save the width value into the Temp Coords array $aTmpCoords[3] = $aTSV[$t][9] ;Save the height value into the Temp Coords array ContinueLoop ;Look for the next word Case Else ;This is a middle word in the search text, so do an = If $aTSV[$t][11] = $aSearchText[$t - $iFoundAt + 1] Then ;Leave the [0] coord as-is If $aTmpCoords[1] > $aTSV[$t][7] Then $aTmpCoords[1] = $aTSV[$t][7] ;Use the top most coord avail $aTmpCoords[2] += $aTSV[$t][8] ;Add this words width to what we already have If $aTmpCoords[3] < $aTSV[$t][9] Then $aTmpCoords[3] = $aTSV[$t][9] ;Use the value with the greatest height ContinueLoop ;Possible that there is only 1 search word, so need to continue loop so the Start and End don't get triggered Else $i = $iFoundAt ContinueLoop(2) ;if we didn't find the last word in the search text, start the search over EndIf EndSelect Next Next Case 2 ;The word/sentence ends with the specified text For $i = 1 to UBound($aTSV) - 1 ;Search the contents of the OCR text array If StringRight($aTSV[$i][11], StringLen($aSearchText[1])) <> $aSearchText[1] Then ;Ensure the first word is the last part ContinueLoop ;This is not the text we are looking for EndIf Local $aTmpCoords[4] ;Create array to store the coords until we are sure it's a full match Local $iFoundAt = $i ;Save the # we found the first word at, incase this isn't the whole sentence Local $iCount = 0 ;Used to count the current word in the search text array we are on For $t = $i to $i + ($aSearchText[0] - 1) ;Looks like this is what we want, verify everything matches. $iCount += 1 Select Case $iCount = $aSearchText[0] ;This is the last word in the text we are looking for If $iCount = 1 Then ;If this is actually the first & last then we need to save the Coords directly If StringRight($aTSV[$i][11], StringLen($aSearchText[1])) = $aSearchText[($t - $iFoundAt) + 1] Then ;This is the first & Last word to find, so ensure it Ends with $aTmpCoords[0] = $aTSV[$t][6] ;Save the left value into the Temp Coords array $aTmpCoords[1] = $aTSV[$t][7] ;Save the Top value into the Temp Coords array $aTmpCoords[2] = $aTSV[$t][8] ;Save the Width value into the Temp Coords array $aTmpCoords[3] = $aTSV[$t][9] ;Save the Height value into the Temp Coords array Else ContinueLoop(2) ;not found, so continue looking for full match EndIf Else ;This is the last but not the first, so we need to match exact since nothing should be before or after. If $aTSV[$t][11] = $aSearchText[($t - $iFoundAt) + 1] Then If $aTmpCoords[1] > $aTSV[$t][7] Then $aTmpCoords[1] = $aTSV[$t][7] ;Use the top most coord avail $aTmpCoords[2] += $aTSV[$t][8] ;Add this words width to what we already have If $aTmpCoords[3] < $aTSV[$t][9] Then $aTmpCoords[3] = $aTSV[$t][9] ;Use the value with the greatest height Else ContinueLoop(2) EndIf EndIf $iFoundCount += 1 ;This is a full match, so Increase the successful match by 1 $aCoords[0] = $aTmpCoords[0] ;Save the Temp Coordinates $aCoords[1] = $aTmpCoords[1] $aCoords[2] = $aTmpCoords[2] $aCoords[3] = $aTmpCoords[3] If $iMatch = $iFoundCount Then ;See if this is the match we want, defaults to the first match found ;If there are multiple words, need to identify how many pixels to include where the spaces should have been If $aSearchText[0] > 1 Then $iSpace = $aTSV[$t][8] / StringLen($aTSV[$t][11]) ;Get width of this word, then divide by the character length to get pixels per character $sTxtOnly = StringStripWS($sTextToFind, 8) $iSpace = $aCoords[2] / StringLen($sTxtOnly) ;Divide the total width by the number of characters to get Pixels per character $aCoords[2] += ($iSpace * ($aSearchText[0] - 1)) EndIf ;Convert the coordinates to onscreen coords according to the scale $aCoords[0] = $aCoords[0] / $scale $aCoords[1] = $aCoords[1] / $scale $aCoords[2] = $aCoords[2] / $scale $aCoords[3] = $aCoords[3] / $scale Return $aCoords Else ContinueLoop(2) ;We found the full search text, but we don't want this one. EndIf Case $iCount = 1 If StringRight($aTSV[$i][11], StringLen($aSearchText[($t - $iFoundAt) + 1])) = $aSearchText[($t - $iFoundAt) + 1] Then $aTmpCoords[0] = $aTSV[$t][6] ;Save the left value into the Temp Coords array $aTmpCoords[1] = $aTSV[$t][7] ;Save the top value into the Temp Coords array $aTmpCoords[2] = $aTSV[$t][8] ;Save the width value into the Temp Coords array $aTmpCoords[3] = $aTSV[$t][9] ;Save the height value into the Temp Coords array ContinueLoop ;Look for the next word Else ContinueLoop(2) EndIf Case Else ;This is a middle word in the search text, so do an = If $aTSV[$t][11] = $aSearchText[$t - $iFoundAt + 1] Then ;Leave the [0] coord as-is If $aTmpCoords[1] > $aTSV[$t][7] Then $aTmpCoords[1] = $aTSV[$t][7] ;Use the top most coord avail $aTmpCoords[2] += $aTSV[$t][8] ;Add this words width to what we already have If $aTmpCoords[3] < $aTSV[$t][9] Then $aTmpCoords[3] = $aTSV[$t][9] ;Use the value with the greatest height ContinueLoop ;Possible that there is only 1 search word, so need to continue loop so the Start and End don't get triggered Else ContinueLoop(2) ;if we didn't find the last word in the search text, start the search over EndIf EndSelect Next Next Case 3 ;The word/sentence match exactly with what is specified For $i = 1 to UBound($aTSV) - 1 ;Search the contents of the OCR text array If $aTSV[$i][11] <> $aSearchText[1] Then ;Ensure the first word matches ContinueLoop ;This is not the text we are looking for EndIf Local $aTmpCoords[4] ;Create array to store the coords until we are sure it's a full match Local $iFoundAt = $i ;Save the # we found the first word at, incase this isn't the whole sentence Local $iCount = 0 ;Used to count the current word in the search text array we are on For $t = $i to $i + ($aSearchText[0] - 1) ;Looks like this is what we want, verify everything matches. $iCount += 1 Select Case $iCount = $aSearchText[0] ;This is the last word in the text we are looking for If $iCount = 1 Then ;If this is actually the first & last then we need to save the Coords directly If $aTSV[$i][11] = $aSearchText[($t - $iFoundAt) + 1] Then ;This is the first & Last word to find, so ensure it matches exact $aTmpCoords[0] = $aTSV[$t][6] ;Save the left value into the Temp Coords array $aTmpCoords[1] = $aTSV[$t][7] ;Save the Top value into the Temp Coords array $aTmpCoords[2] = $aTSV[$t][8] ;Save the Width value into the Temp Coords array $aTmpCoords[3] = $aTSV[$t][9] ;Save the Height value into the Temp Coords array Else ContinueLoop(2) ;not found, so continue looking for full match EndIf Else ;This is the last but not the first, so we need to match exact since nothing should be before or after. If $aTSV[$t][11] = $aSearchText[($t - $iFoundAt) + 1] Then If $aTmpCoords[1] > $aTSV[$t][7] Then $aTmpCoords[1] = $aTSV[$t][7] ;Use the top most coord avail $aTmpCoords[2] += $aTSV[$t][8] ;Add this words width to what we already have If $aTmpCoords[3] < $aTSV[$t][9] Then $aTmpCoords[3] = $aTSV[$t][9] ;Use the value with the greatest height Else ContinueLoop(2) EndIf EndIf $iFoundCount += 1 ;This is a full match, so Increase the successful match by 1 $aCoords[0] = $aTmpCoords[0] ;Save the Temp Coordinates $aCoords[1] = $aTmpCoords[1] $aCoords[2] = $aTmpCoords[2] $aCoords[3] = $aTmpCoords[3] If $iMatch = $iFoundCount Then ;See if this is the match we want, defaults to the first match found ;If there are multiple words, need to identify how many pixels to include where the spaces should have been If $aSearchText[0] > 1 Then $iSpace = $aTSV[$t][8] / StringLen($aTSV[$t][11]) ;Get width of this word, then divide by the character length to get pixels per character $sTxtOnly = StringStripWS($sTextToFind, 8) $iSpace = $aCoords[2] / StringLen($sTxtOnly) ;Divide the total width by the number of characters to get Pixels per character $aCoords[2] += ($iSpace * ($aSearchText[0] - 1)) EndIf ;Convert the coordinates to onscreen coords according to the scale $aCoords[0] = $aCoords[0] / $scale $aCoords[1] = $aCoords[1] / $scale $aCoords[2] = $aCoords[2] / $scale $aCoords[3] = $aCoords[3] / $scale Return $aCoords Else ContinueLoop(2) ;We found the full search text, but we don't want this one. EndIf Case $iCount = 1 If $aTSV[$i][11] = $aSearchText[($t - $iFoundAt) + 1] Then $aTmpCoords[0] = $aTSV[$t][6] ;Save the left value into the Temp Coords array $aTmpCoords[1] = $aTSV[$t][7] ;Save the top value into the Temp Coords array $aTmpCoords[2] = $aTSV[$t][8] ;Save the width value into the Temp Coords array $aTmpCoords[3] = $aTSV[$t][9] ;Save the height value into the Temp Coords array ContinueLoop ;Look for the next word Else ContinueLoop(2) EndIf Case Else ;This is a middle word in the search text, so do an = If $aTSV[$t][11] = $aSearchText[$t - $iFoundAt + 1] Then ;Leave the [0] coord as-is If $aTmpCoords[1] > $aTSV[$t][7] Then $aTmpCoords[1] = $aTSV[$t][7] ;Use the top most coord avail $aTmpCoords[2] += $aTSV[$t][8] ;Add this words width to what we already have If $aTmpCoords[3] < $aTSV[$t][9] Then $aTmpCoords[3] = $aTSV[$t][9] ;Use the value with the greatest height ContinueLoop ;Possible that there is only 1 search word, so need to continue loop so the Start and End don't get triggered Else ContinueLoop(2) ;if we didn't find the last word in the search text, start the search over EndIf EndSelect Next Next Case Else ConsoleWrite("Invalid $iFindType value" & @CRLF) Return SetError(1, 0, $aCoords) EndSwitch If $iMatch = -1 Then ;If the $iMatch = -1 then we want the last match found which should now be set to the $aCoords array, so return that $aCoords[0] = $aCoords[0] / $scale $aCoords[1] = $aCoords[1] / $scale $aCoords[2] = $aCoords[2] / $scale $aCoords[3] = $aCoords[3] / $scale Return $aCoords EndIf Return SetError(1, 0, $aCoords) ;If we got this far, then text was not found EndFunc ;_TesseractFindCoords() ; #FUNCTION# =============================================================================== ; ; Name...........: _TesseractCaptureText() ; Description ...: Captures text from a control or Window ; Syntax.........: _TesseractCaptureText($win_title, $win_text = "", $ctrl_id = "", $scale = 2, $left_indent = 0, $top_indent = 0, $right_indent = 0, $bottom_indent = 0, $tsv = 0) ; Parameters ....: $win_title - The title of the window to capture text from. ; $win_text - Optional: The text of the window to capture text from. ; $ctrl_id - Optional: The ID of the control to capture text from. ; The text of the window will be returned if one isn't provided. ; $scale - Optional: The scaling factor of the screenshot prior to text recognition. ; Increase this number to improve accuracy. ; The default is 2. ; $left_indent - A number of pixels to indent the capture from the ; left of the control. ; $top_indent - A number of pixels to indent the capture from the ; top of the control. ; $right_indent - A number of pixels to indent the capture from the ; right of the control. ; $bottom_indent - A number of pixels to indent the capture from the ; bottom of the control. ; $tsv - What type of return you want ; 0 = Return text in a 1D array ; 1 = Returns 2D array showing text and the exact position found within the control ; ; Return values .: On Success - Returns an array of text that was captured. Global Var ($aOCR_IMG_ORI) is the starting coord the OCR image was captured from ; On Failure - Returns an empty array. ; Author ........: seangriffin - Was origionally _TesseractControlCapture() function ; ; Modified.......: BigDaddyO - Included new ability to capture coords using $tsv (requires 3.05+) ; - Removed the dropdown and listbox stuff as they should allow their text to be pulled directly without OCR ; - Removed the Cleanup option as that wasn't compatible with the new tsv options. ; - Removed the scrolling option as it added a lot of complications and could just call this function +1 times to do the same if text not found ; ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: ; ;========================================================================================== Func _TesseractCaptureText($win_title, $win_text = "", $ctrl_id = "", $scale = 2, $left_indent = 0, $top_indent = 0, $right_indent = 0, $bottom_indent = 0, $tsv = 0, $Debug = 0) if Not IsDeclared($aOCR_IMG_ORI) Then Global $aOCR_IMG_ORI Local $aArray, $hwnd ; if a control ID is specified, then get it's HWND if StringCompare($ctrl_id, "") <> 0 Then $hwnd = ControlGetHandle($win_title, $win_text, $ctrl_id) EndIf ; Perform the text recognition WinActivate($win_title) $capture_filename = _TempFile($tesseract_temp_path, "~", ".tif") $ocr_filename = StringLeft($capture_filename, StringLen($capture_filename) - 4) $Capture = CaptureToTIFF($win_title, $win_text, $hwnd, $capture_filename, $scale, $left_indent, $top_indent, $right_indent, $bottom_indent) If @error Then ConsoleWrite("Error Capturing TIFF for OCR (" & $Capture & ")" & @CRLF) EndIf $aOCR_IMG_ORI = $Capture ConsoleWrite("Area that will be scanned for OCR: " & $Capture[0] & "," & $Capture[1] & "," & $Capture[2] & "," & $Capture[3] & @CRLF) If $tsv = 1 Then $ocr_filename_and_ext = $ocr_filename & ".tsv" ShellExecuteWait($tesseract_path & "tesseract.exe", $capture_filename & " " & $ocr_filename & " tsv", $tesseract_path, "open", @SW_HIDE) ;This will return a tab seperated file Else $ocr_filename_and_ext = $ocr_filename & ".txt" ShellExecuteWait($tesseract_path & "tesseract.exe", $capture_filename & " " & $ocr_filename, $tesseract_path, "open", @SW_HIDE) EndIf If $tsv = 1 Then _FileReadToArray($ocr_filename_and_ext, $aArray, 0, @TAB) ;Build a 2D array Else _FileReadToArray($ocr_filename_and_ext, $aArray, 0) ;This will return each line of text in a 1D array EndIf FileDelete($ocr_filename & ".*") Return $aArray EndFunc ;Draw the black line over the text we found prior to taking a screenshot ; Note, if running from ScITE consolewrites and some other things within the script can refresh the screen and delete the redactions ; Best if you can move ScITE to a different screen than the redaction is running on, or just run as compiled. Func _Redact($iX, $iY, $iWidth, $iHeight, $iColor) Local $hDC = _WinAPI_GetWindowDC(0) ;DC of entire screen (desktop) Local $hPen = _WinAPI_CreatePen($PS_SOLID, $iHeight, $iColor) ;Create a solid pen object with the color provided Local $oSelect = _WinAPI_SelectObject($hDC, $hPen) ;Select the $hDC and use the $hPen for this line _WinAPI_DrawLine($hDC, $iX, $iY + ($iHeight / 2), $iX + $iWidth, $iY + ($iHeight / 2)) ;Draw the line in the middle, Pen size should cover everything _WinAPI_DeleteObject($hPen) _WinAPI_SelectObject($hDC, $oSelect) _WinAPI_ReleaseDC(0, $hDC) EndFunc ;==>_WinAPI_DrawRect ; #FUNCTION# ;=============================================================================== ; ; Name...........: CaptureToTIFF() ; Description ...: Captures an image of the screen, a window or a control, and saves it to a TIFF file. ; Syntax.........: CaptureToTIFF($win_title = "", $win_text = "", $ctrl_id = "", $sOutImage = "", $scale = 1, $left_indent = 0, $top_indent = 0, $right_indent = 0, $bottom_indent = 0) ; Parameters ....: $win_title - The title of the window to capture an image of. ; $win_text - Optional: The text of the window to capture an image of. ; $ctrl_id - Optional: The ID of the control to capture an image of. ; An image of the window will be returned if one isn't provided. ; $sOutImage - The filename to store the image in. ; $scale - Optional: The scaling factor of the capture. ; $left_indent - A number of pixels to indent the screen capture from the ; left of the window or control. ; $top_indent - A number of pixels to indent the screen capture from the ; top of the window or control. ; $right_indent - A number of pixels to indent the screen capture from the ; right of the window or control. ; $bottom_indent - A number of pixels to indent the screen capture from the ; bottom of the window or control. ; Return values .: None ; Author ........: seangriffin ; Modified.......: BigDaddyO - Returns the Position of the object we are scanning for text ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: No ; ; ;========================================================================================== Func CaptureToTIFF($win_title = "", $win_text = "", $ctrl_id = "", $sOutImage = "", $scale = 1, $left_indent = 0, $top_indent = 0, $right_indent = 0, $bottom_indent = 0) Local $hWnd, $hwnd2, $hDC, $hBMP, $hImage1, $hGraphic, $CLSID, $tParams, $pParams, $tData, $i = 0, $hImage2, $pos[4], $screenpos[4] Local $Ext = StringUpper(StringMid($sOutImage, StringInStr($sOutImage, ".", 0, -1) + 1)) Local $giTIFColorDepth = 24 Local $giTIFCompression = $GDIP_EVTCOMPRESSIONNONE ; If capturing a control if StringCompare($ctrl_id, "") <> 0 Then $hwnd2 = ControlGetHandle($win_title, $win_text, $ctrl_id) $pos = WinGetPos($hwnd2) ;WinGetPos works for controls as well if you pass the handle as the window title Else ; If capturing a window if StringCompare($win_title, "") <> 0 Then $hwnd2 = WinGetHandle($win_title, $win_text) $pos = WinGetPos($win_title, $win_text) Else ; If capturing the desktop $hwnd2 = "" $pos[0] = 0 $pos[1] = 0 $pos[2] = @DesktopWidth $pos[3] = @DesktopHeight EndIf EndIf If $pos[2] < 1 or $pos[3] < 1 Then Return SetError(1, 0, "No Width or Height found for specified item") ; Capture an image of the window / control if IsHWnd($hwnd2) Then WinActivate($win_title, $win_text) $hBitmap2 = _ScreenCapture_CaptureWnd("", $hwnd2, 0, 0, -1, -1, False) Else $hBitmap2 = _ScreenCapture_Capture("", 0, 0, -1, -1, False) EndIf _GDIPlus_Startup () ; Convert the image to a bitmap $hImage2 = _GDIPlus_BitmapCreateFromHBITMAP ($hBitmap2) $hWnd = _WinAPI_GetDesktopWindow() $hDC = _WinAPI_GetDC($hWnd) $hBMP = _WinAPI_CreateCompatibleBitmap($hDC, ($pos[2] * $scale) - ($right_indent * $scale), ($pos[3] * $scale) - ($bottom_indent * $scale)) _WinAPI_ReleaseDC($hWnd, $hDC) $hImage1 = _GDIPlus_BitmapCreateFromHBITMAP ($hBMP) $hGraphic = _GDIPlus_ImageGetGraphicsContext($hImage1) _GDIPLus_GraphicsDrawImageRect($hGraphic, $hImage2, 0 - ($left_indent * $scale), 0 - ($top_indent * $scale), ($pos[2] * $scale) + $left_indent, ($pos[3] * $scale) + $top_indent) $CLSID = _GDIPlus_EncodersGetCLSID($Ext) ; Set TIFF parameters $tParams = _GDIPlus_ParamInit(2) $tData = DllStructCreate("int ColorDepth;int Compression") DllStructSetData($tData, "ColorDepth", $giTIFColorDepth) DllStructSetData($tData, "Compression", $giTIFCompression) _GDIPlus_ParamAdd($tParams, $GDIP_EPGCOLORDEPTH, 1, $GDIP_EPTLONG, DllStructGetPtr($tData, "ColorDepth")) _GDIPlus_ParamAdd($tParams, $GDIP_EPGCOMPRESSION, 1, $GDIP_EPTLONG, DllStructGetPtr($tData, "Compression")) If IsDllStruct($tParams) Then $pParams = DllStructGetPtr($tParams) ; Save TIFF and cleanup _GDIPlus_ImageSaveToFileEx($hImage1, $sOutImage, $CLSID, $pParams) _GDIPlus_ImageDispose($hImage1) _GDIPlus_ImageDispose($hImage2) _GDIPlus_GraphicsDispose ($hGraphic) _WinAPI_DeleteObject($hBMP) _GDIPlus_Shutdown() $pos[0] += $left_indent $pos[1] += $top_indent $pos[2] -= $left_indent - $right_indent ;Subtract the left and right indent from the overall width $pos[3] -= $top_indent - $bottom_indent ;Subtract the top and bottom indent from the overall height Return $pos ;Return the expected size of the control/window that was captured EndFunc1 point
-
Thanks everyone and supraaxdd! Appreciate it.1 point
-
If the number of buttons is always the same, you could use the index to access directly to the object : Local $nButton = 2 ; 0 based number = 3rd button _IEAction(_IETagNameGetCollection($oIE, "button", $nButton), "click")1 point
-
For $m=1 To 12 ConsoleWrite($m & @TAB & _MonthToValue($m) & @CRLF) Next Func _MonthToValue($mon) Return Mod((Int(($mon+1)/3)),4)+1 EndFunc1 point
-
scroll left and right
hendrikhe reacted to FrancescoDiMuro for a topic
@hendrikhe Use IE UDF to create IE object and try to use scrollTo() method to scroll horizontally the webpage1 point -
Observe that the function mention Indices (a plural). When more than one item index are selected, the string returned contains a series on numeric indices separated by |. It's up to you the user to decide which index to feed to other functions, so you have to extract the index you want (each in turn if needed) and perform the conversion.1 point
-
Next update (see 1st post for details).1 point
-
Use AutoIt v5.0.0; it has inbuilt AI that codes everything for you automatically, and also googles the latest stable release telepathically. Plus it tells better jokes than Alexa and Siri.1 point
-
I'm starting with a tool for some collaborative planning between teams (scheduling script), hopefully a MRP (material resource planning) as exposed here. First tab would be 5 multiline listviews to get a view of a week planning (data from a database). The other tabs are listviews using the UDF, with data about resources and dates. Here the collaborative part is editing data from different users (teams). The example code below is only a sketch of the idea, and there seems to be no conflicts between the basic 'blocks' i'd like to use: the GUIListViewEx UDF, some tabs and this "Ownerdrawn multiline ListView" by rover: ;https://www.autoitscript.com/forum/topic/139314-custom-listview-icons-checkboxes-multiline-edit-in-place/?do=findComment&comment=980443 ;rover, April 19, 2012 (edited) ;coded by rover 2k12 #include <GuiConstantsEx.au3> #include <GuiListView.au3> #include <WinAPI.au3> #include <WindowsConstants.au3> #include <FontConstants.au3> #include <GUIListViewEx.au3> Opt("GUIDataSeparatorChar", "|") Global Const $ODA_SELECT = 0x2 Global Const $ODA_FOCUS = 0x4 Global Const $ODS_SELECTED = 0x0001 Global Const $ODT_LISTVIEW = 102 Global Const $ODA_DRAWENTIRE = 0x1 Global $iDllGDI = DllOpen("gdi32.dll") Global $iDllUSER32 = DllOpen("user32.dll") Global $iDllUxtheme = DllOpen("uxtheme.dll") ;global resources for WM_DRAWITEM - optimize speed Global $aFont[2] $aFont[0] = _WinAPI_CreateFont(16, 0, 0, 0, $FW_HEAVY, False, False, False, _ $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $PROOF_QUALITY, $DEFAULT_PITCH, 'Calibri') $aFont[1] = _WinAPI_CreateFont(16, 0, 0, 0, $FW_MEDIUM, False, False, False, _ $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $PROOF_QUALITY, $DEFAULT_PITCH, 'Calibri') Global $hPen1 = _WinAPI_CreatePen($PS_SOLID, 1, 0xF3EAE7) Global $hPen2 = _WinAPI_CreatePen($PS_SOLID, 1, 0xF4E8E6) Global $hBrush = _WinAPI_CreateSolidBrush(0xEEDDBB) ;Theme Parts and States ;http://msdn.microsoft.com/en-us/library/windows/desktop/bb773210%28v=vs.85%29.aspx ;Part - vsstyle.h Global Const $BP_PUSHBUTTON = 1 Global Const $LVP_GROUPHEADER = 6 ;State - vsstyle.h Global Enum $PBS_NORMAL=1,$PBS_HOT,$PBS_PRESSED,$PBS_DISABLED,$PBS_DEFAULTED Global Enum $LVGH_CLOSEHOT=10,$LVGH_CLOSESELECTED,$LVGH_CLOSESELECTEDHOT,$LVGH_CLOSESELECTEDNOTFOCUSED ;this allows you to theme individual items for your own needs, not just if items are selected ;the same as customdrawing item and subitem colours ;XP+ Global $sTheme = 'Button' Global $iThemePart = $BP_PUSHBUTTON Global $iThemeState = $PBS_NORMAL ; Vista+ ;Global $sTheme = 'Listview' ;Global $iThemePart = $LVP_GROUPHEADER ;Global $iThemeState = $LVGH_CLOSEMIXEDSELECTION Global $hTheme = DllCall($iDllUxtheme, 'ptr', 'OpenThemeData', 'hwnd', 0, 'wstr', $sTheme) $hTheme = $hTheme[0] ;global structs for WM_DRAWITEM - optimize speed ;rect, text buffer and LVITEM structures Global $tLVRect = DllStructCreate($tagRECT) Global $tLVText = DllStructCreate("wchar[4096]") Global $tLVITEM = DllStructCreate($tagLVITEM) Global $pLVITEM = DllStructGetPtr($tLVITEM) DllStructSetData($tLVITEM, "TextMax", 4096) DllStructSetData($tLVITEM, "SubItem", 0) DllStructSetData($tLVITEM, "Text", DllStructGetPtr($tLVText)) ; WM_MEASUREITEM allows setting the row height Global $iListView_row_height = 130 Global $hListView_1, $hListView_2 ;must be declared before listview created GUIRegisterMsg($WM_MEASUREITEM, "WM_MEASUREITEM") ; place before listview creation - message sent once for each ownerdrawn control created GUIRegisterMsg($WM_DRAWITEM, "WM_DRAWITEM") ;placed here, WM_MEASUREITEM can now be unregistered in the handler (deleting or adding items after unregistering maintains row height setting this way) ; --------------------------------------------------- Global $GuiWidth = @DesktopWidth-2, $GuiHight = @DesktopHeight-77 $hGUI = GUICreate("Ownerdrawn multiline ListView", $GuiWidth, $GuiHight) $cTab = GUICtrlCreateTab(2, 2, $GuiWidth - 8, $GuiHight - 30) $cTab_0 = GUICtrlCreateTabItem("Tab 0") $cListView_1 = GUICtrlCreateListView("", 8, 32, 320, $GuiHight-112, BitOR($LVS_REPORT, $LVS_NOCOLUMNHEADER, $LVS_OWNERDRAWFIXED, $LVS_SHOWSELALWAYS)) $hListView_1 = GUICtrlGetHandle($cListView_1) _GUICtrlListView_SetExtendedListViewStyle($hListView_1, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT)) ;double buffer improves performance _GUICtrlListView_AddColumn($hListView_1, "", 298) GUIRegisterMsg($WM_MEASUREITEM, "WM_MEASUREITEM") ; place before listview creation - message sent once for each ownerdrawn control created $cListView_2 = GUICtrlCreateListView("", 330, 32, 320, $GuiHight-112, BitOR($LVS_REPORT, $LVS_NOCOLUMNHEADER, $LVS_OWNERDRAWFIXED, $LVS_SHOWSELALWAYS)) $hListView_2 = GUICtrlGetHandle($cListView_2) _GUICtrlListView_SetExtendedListViewStyle($hListView_2, BitOR($LVS_EX_DOUBLEBUFFER, $LVS_EX_FULLROWSELECT)) ;double buffer improves performance _GUICtrlListView_AddColumn($hListView_2, "", 298) Global $aTextHdr[4] = ["From:", "Sent:", "To:", "Subject:"] For $row = 1 To 10 _GUICtrlListView_AddItem($hListView_1, "Rover - AutoIt Forums|Thursday, April 19, 2012 05:0" & $row - 1 & "AM|footswitch|Re: Multiline listview like Outlook|" & _ "This is a rough mock-up of that Outlook listview" & @CRLF & "You will need to add code for the text metrics") _GUICtrlListView_AddItem($hListView_2, "Rover - AutoIt Forums|Thursday, April 19, 2012 05:0" & $row - 1 & "AM|footswitch|Re: Multiline listview like Outlook|" & _ "This is a rough mock-up of that Outlook listview" & @CRLF & "You will need to add code for the text metrics") Next ;~ ;adjust listview size for number of items shown - for efficient painting and to eliminate issue of a click on bottom item causing a jump to next item ;~ Local $iY = _GUICtrlListView_ApproximateViewHeight($hListView_1, _GUICtrlListView_GetCounterPage($hListView_1)) ;~ GUICtrlSetPos($cListView_1, 2, 2, 328, $iY + 4) ;~ GUICtrlSetPos($cListView_2, 332, 2, 328, $iY + 4) ;~ Global $cSelect = GUICtrlCreateButton("Themed Select", 2, $iY + 8, 120, 23) ;~ Global $cTheme = GUICtrlCreateCombo("Button", 120+2, $iY + 9, 60) ;~ GUICtrlSetData(-1, "Listview", "Button") ;~ Global $cState = GUICtrlCreateCombo("1 NORMAL", 180+4, $iY + 9, 80) ;~ GUICtrlSetData(-1, "2 HOT|3 PRESSED|4 GREYED|5 DEFAULT", "1 NORMAL") ;~ Global $cLabel = GUICtrlCreateLabel("Select Item"&@CRLF&"Style", 260+8, $iY + 8) Global $Label = GUICtrlCreateLabel("Selected: ", 654, 32, 500, 300) GUICtrlSetFont(-1, 11, Default, Default, "Segoe UI") #cs ;Now trying to detect the index of highlighted item in the multiline ListView ;From 'A Beginner’s Guide To Melba23’s GUIListViewEx UDF': The UDF registers these 4 messages automatically – if you already have handlers for these messages in your script, do not register them again using the UDF function (set the relevant parameter of _GUIListViewEx_MsgRegister to False) but call the relevant UDF handler function (_GUIListViewEx_WM_NOTIFY_Handler) from within your existing handler. Note that the UDF handler should be called as the final action of the existing handler and the return value should be that returned from the UDF handler #ce GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY") ;~ ;prevent the UDF from initially registering the WM_NOTIFY ;~ _GUIListViewEx_MsgRegister(False) $cTab_1 = GUICtrlCreateTabItem("Tab 1") $cLV = GUICtrlCreateListView("Col 0|Col 1", 8, 32, 350, 300) _GUICtrlListView_SetColumnWidth($cLV, 0, 250) _GUICtrlListView_SetColumnWidth($cLV, 1, 550) ; Column wider than ListView <<<<<<<<<<<<<<<<<<<<<<<<<<< Global $aContent[5] For $i = 0 To 4 $aContent[$i] = "Item " & $i & "-0|Item " & $i & "-1" GUICtrlCreateListViewItem($aContent[$i], $cLV) Next $iLV_Index = _GUIListViewEx_Init($cLV, $aContent) _GUIListViewEx_SetEditStatus($iLV_Index, "1") ; Wide column editable <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $cTab_2 = GUICtrlCreateTabItem("Tab 2") ; Create ListView Global $cListView_Left = GUICtrlCreateListView("Tom|Dick|Harry", 8, 32, 300, 300, $LVS_SHOWSELALWAYS) _GUICtrlListView_SetExtendedListViewStyle($cListView_Left, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_CHECKBOXES)) _GUICtrlListView_SetColumnWidth($cListView_Left, 0, 93) _GUICtrlListView_SetColumnWidth($cListView_Left, 1, 93) _GUICtrlListView_SetColumnWidth($cListView_Left, 2, 93) ; Create ListView Global $cListView_Right = GUICtrlCreateListView("Tom|Dick|Harry", 320, 32, 300, 300, $LVS_SHOWSELALWAYS) _GUICtrlListView_SetExtendedListViewStyle($cListView_Right, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_CHECKBOXES)) _GUICtrlListView_SetColumnWidth($cListView_Right, 0, 93) _GUICtrlListView_SetColumnWidth($cListView_Right, 1, 93) _GUICtrlListView_SetColumnWidth($cListView_Right, 2, 93) ; Create array and fill Left listview Global $aLV_List[10] For $i = 0 To UBound($aLV_List) - 1 $aLV_List[$i] = "Tom " & $i & "|Dick " & $i & "|Harry " & $i GUICtrlCreateListViewItem($aLV_List[$i], $cListView_Left) GUICtrlCreateListViewItem($aLV_List[$i], $cListView_Right) Next ; Initiate LVEx ;Note that using the left/right keys only returns a "selection change" message if the ListView has been set up to allow single cell highlighting Global $iLV_Index_Left = _GUIListViewEx_Init($cListView_Left, $aLV_List) _GUIListViewEx_SetEditStatus($iLV_Index_Left, "*") $iLV_Index_Right = _GUIListViewEx_Init($cListView_Right, $aLV_List) _GUIListViewEx_SetEditStatus($iLV_Index_Right, "*") GUICtrlCreateTabItem("") _GUIListViewEx_SetActive($iLV_Index_Left) ; Register for sorting, dragging and editing _GUIListViewEx_MsgRegister(False) GUISetState() ;~ ;Select multiple items ;~ _GUICtrlListView_SetItemSelected($cListView_1, 1) ;~ _GUICtrlListView_SetItemSelected($cListView_1, 2) ;~ MsgBox(0, "Information", "Selected Indices: " & _GUICtrlListView_GetSelectedIndices($cListView_1)) ;~ ;Show item 2 text ;~ $aItem = _GUICtrlListView_GetItem($cListView_1, 1) ;~ MsgBox(0, "Information", "Item 2 Text: " & $aItem[3]) ; Loop until user exits While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop ;~ Case $cTheme ;~ DllCall($iDllUxtheme, 'uint', 'CloseThemeData', 'ptr', $hTheme) ;~ Switch GUICtrlRead($cTheme) ;~ Case "Button" ;~ $sTheme = "Button" ;~ $iThemePart = $BP_PUSHBUTTON ;~ GUICtrlSetData($cState, "|1 NORMAL|2 HOT|3 PRESSED|4 GREYED|5 DEFAULT", "1 NORMAL") ;~ Case "Listview" ;~ $sTheme = "Listview" ;~ $iThemePart = $LVP_GROUPHEADER ;~ GUICtrlSetData($cState, "|10|11|12|13", "11") ;~ EndSwitch ;~ $hTheme = DllCall($iDllUxtheme, 'ptr', 'OpenThemeData', 'hwnd', 0, 'wstr', GUICtrlRead($cTheme)) ;~ $hTheme = $hTheme[0] ;~ $iThemeState = Number(StringLeft(GUICtrlRead($cState), 2)) ;~ _WinAPI_InvalidateRect($hListView_1) ;~ Case $cState ;~ $iThemeState = Number(StringLeft(GUICtrlRead($cState), 2)) ;~ _WinAPI_InvalidateRect($hListView_1) ;~ Case $cSelect ;~ If Not $hTheme Then ;~ GUICtrlSetData($cSelect, "Themed Select") ;~ $hTheme = DllCall($iDllUxtheme, 'ptr', 'OpenThemeData', 'hwnd', 0, 'wstr', $sTheme) ;~ $hTheme = $hTheme[0] ;~ Else ;~ GUICtrlSetData($cSelect, "Unthemed Select") ;~ DllCall($iDllUxtheme, 'uint', 'CloseThemeData', 'ptr', $hTheme) ;~ $hTheme = 0 ;~ EndIf ;~ _WinAPI_InvalidateRect($hListView_1) EndSwitch $vRet = _GUIListViewEx_EventMonitor() If @error Then MsgBox($MB_SYSTEMMODAL, "Error", "Event error: " & @error) EndIf Switch @extended Case 1 ; This is returned after an edit attempt If $vRet = "" Then MsgBox($MB_SYSTEMMODAL, "Edit", "Edit aborted" & @CRLF) Else MsgBox($MB_SYSTEMMODAL, "Edit", "Successful edit" & @CRLF) EndIf Case 9 ; This is returned after a selection change MsgBox($MB_SYSTEMMODAL, "Selection changed", "New selection:" & @CRLF & "Row: " & $vRet[1] & @CRLF & "Col: " & $vRet[2]) EndSwitch WEnd GUIDelete() DllCall($iDllUxtheme, 'uint', 'CloseThemeData', 'ptr', $hTheme) _WinAPI_DeleteObject($hPen1) _WinAPI_DeleteObject($hPen2) _WinAPI_DeleteObject($hBrush) For $i = 0 To UBound($aFont) - 1 If $aFont[$i] Then _WinAPI_DeleteObject($aFont[$i]) Next Exit ;From: Melba23: Detect item checking ;https://www.autoitscript.com/forum/topic/182492-guilistviewex-new-version-27-may-16/?do=findComment&comment=1325933 ;But you do not need to add this to the UDF code ;just create your own handler as below, do not register $WM_NOTIFY using the UDF function ;(set the relevant parameter of _GUIListViewEx_MsgRegister to False) ;and then call the UDF handler function (_GUIListViewEx_WM_NOTIFY_Handler) from within your own handler. Func _WM_NOTIFY($hWnd, $Msg, $wParam, $lParam) Local $hWndFrom, $iCode, $tNMHDR, $hWndListView, $hTemp, $nLV $tNMHDR = DllStructCreate($tagNMHDR, $lParam) $hWndFrom = HWnd(DllStructGetData($tNMHDR, "HwndFrom")) $iCode = DllStructGetData($tNMHDR, "Code") $hTemp = Int(DllStructGetData($tNMHDR, 1)) If $hTemp = $hListView_1 Then $hWndListView = $hListView_1 $nLV = 1 ElseIf $hTemp = $hListView_2 Then $hWndListView = $hListView_2 $nLV = 2 EndIf Switch $hWndFrom Case $hWndListView Switch $iCode Case $LVN_ITEMCHANGED Local $tInfo = DllStructCreate($tagNMLISTVIEW, $lParam) Local $iItem = DllStructGetData($tInfo, "Item") ;From Zedna, in 'Highlighted item in the ListView?' ;https://www.autoitscript.com/forum/topic/96234-highlighted-item-in-the-listview/?do=findComment&comment=691927 ;if state has changed If BitAND(DllStructGetData($tInfo, "Changed"), $LVIF_STATE) = $LVIF_STATE And DllStructGetData($tInfo, "NewState") <> DllStructGetData($tInfo, "OldState") Then ;take care of only newly selected items (not deselected ones) If BitAND(DllStructGetData($tInfo, "NewState"), $LVIS_SELECTED) = $LVIS_SELECTED Then ConsoleWrite(_GUICtrlListView_GetItemText($hWndListView, $iItem) & @CRLF) ;Help File ;Local $aItem = _GUICtrlListView_GetItem($hWndListView, $iItem) ;MsgBox($MB_SYSTEMMODAL, "Information", $aItem[3]) GUICtrlSetData($Label, "Selected " & $nLV & ": " & _GUICtrlListView_GetItemText($hWndListView, $iItem)) ;Help File ;Local $aItem = _GUICtrlListView_GetItem($hListView, $iItem) ;MsgBox(0, "Information", $aItem[3]) EndIf EndIf Case $NM_DBLCLK Local $tInfo = DllStructCreate($tagNMITEMACTIVATE, $lParam) $Index = DllStructGetData($tInfo, "Index") If DllStructGetData($tInfo, "Index") = -1 Then ConsoleWrite("no item" & @CRLF) Else Local $iItemText = _GUICtrlListView_GetItemText($hWndListView, $Index) ConsoleWrite("Dbclick item " & $iItemText & @CRLF) GUICtrlSetData($Label, $nLV & " " & "Dbclick item " & $iItemText) EndIf EndSwitch EndSwitch ;Return $GUI_RUNDEFMSG ;From Melba23: pass the same parameters to the UDF handler as you received in your WM_NOTIFY handler: ;Note that the UDF handler should be called as the final action of the existing handler and the return value should be that returned from the UDF handler _GUIListViewEx_WM_NOTIFY_Handler($hWnd, $Msg, $wParam, $lParam) EndFunc Func WM_MEASUREITEM($hWnd, $Msg, $wParam, $lParam) Local $tMEASUREITEMS = DllStructCreate("uint cType;uint cID;uint itmID;uint itmW;uint itmH;ulong_ptr itmData", $lParam) If DllStructGetData($tMEASUREITEMS, "cType") <> $ODT_LISTVIEW Then Return $GUI_RUNDEFMSG DllStructSetData($tMEASUREITEMS, "itmH", $iListView_row_height) ; row height GUIRegisterMsg($WM_MEASUREITEM, "") ; unregister message handler call this after last ownerdrawn listview created - message no longer sent Return 1 EndFunc ;==>WM_MEASUREITEM Func WM_DRAWITEM($hWnd, $Msg, $wParam, $lParam) Local $tDRAWITEMSTRUCT, $cID, $itmID, $itmAction, $itmState, $hItm, $hDC $tDRAWITEMSTRUCT = DllStructCreate( _ "uint cType;" & _ "uint cID;" & _ "uint itmID;" & _ "uint itmAction;" & _ "uint itmState;" & _ "hwnd hItm;" & _ "handle hDC;" & _ "long itmRect[4];" & _ "ulong_ptr itmData" _ , $lParam) If DllStructGetData($tDRAWITEMSTRUCT, "cType") <> $ODT_LISTVIEW Then Return $GUI_RUNDEFMSG $cID = DllStructGetData($tDRAWITEMSTRUCT, "cID") $itmID = DllStructGetData($tDRAWITEMSTRUCT, "itmID") $itmAction = DllStructGetData($tDRAWITEMSTRUCT, "itmAction") $itmState = DllStructGetData($tDRAWITEMSTRUCT, "itmState") $hItm = DllStructGetData($tDRAWITEMSTRUCT, "hItm") $hDC = DllStructGetData($tDRAWITEMSTRUCT, "hDC") Switch $cID ; will look for ControlID, not window handle. Case $cListView_1, $cListView_2 Switch $itmAction Case $ODA_DRAWENTIRE __WM_DRAWITEM_ListView($hItm, $tDRAWITEMSTRUCT, $cID, $itmID, $itmAction, $itmState, $hDC) EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_DRAWITEM Func __WM_DRAWITEM_ListView(ByRef $hLV, ByRef $tDRAWITEMSTRUCT, ByRef $cID, ByRef $itmID, ByRef $itmAction, ByRef $itmState, ByRef $hDC) Local $iTxtCol, $bSelected = BitAND($itmState, $ODS_SELECTED), $iTextFormatting = BitOR($DT_LEFT, $DT_WORDBREAK) If Not $bSelected Then $iTxtCol = 0xB79588 Else ;selected $iTxtCol = 0x494949 ;Theme Parts and States ;http://msdn.microsoft.com/en-us/library/windows/desktop/bb773210%28v=vs.85%29.aspx Local $pItemRect = DllStructGetPtr($tDRAWITEMSTRUCT, "itmRect") If $hTheme Then If $sTheme = "Listview" Then Local $tItemRect = DllStructCreate($tagRect, $pItemRect) DllStructSetData($tItemRect, 1, DllStructGetData($tItemRect, 1)+1) DllStructSetData($tItemRect, 2, DllStructGetData($tItemRect, 2)+1) DllStructSetData($tItemRect, 3, DllStructGetData($tItemRect, 3)-1) DllStructSetData($tItemRect, 4, DllStructGetData($tItemRect, 4)-1) $pItemRect = DllStructGetPtr($tItemRect) EndIf DllCall($iDllUxtheme, 'uint', 'DrawThemeBackground', 'ptr', $hTheme, 'hwnd', $hDC, 'int', $iThemePart, 'int', $iThemeState, 'ptr', $pItemRect, 'ptr', 0) Else DllCall($iDllUSER32, "int", "FillRect", "handle", $hDC, "ptr", DllStructGetPtr($tDRAWITEMSTRUCT, "itmRect"), "handle", $hBrush) EndIf EndIf GUICtrlSendMsg($cID, $LVM_GETITEMTEXTW, $itmID, $pLVITEM) Local $aSubItmText = StringSplit(DllStructGetData($tLVText, 1), "|", 2) DllStructSetData($tLVText, 1, "") DllStructSetData($tLVRect, "Top", 0) DllStructSetData($tLVRect, "Left", $LVIR_BOUNDS) GUICtrlSendMsg($cID, $LVM_GETSUBITEMRECT, $itmID, DllStructGetPtr($tLVRect)) Local $iLeft = DllStructGetData($tLVRect, 1) + 6 ;Left Local $iTop = DllStructGetData($tLVRect, 2) ;Top DllStructSetData($tLVRect, 1, $iLeft) ;Left Switch $hLV Case $hListView_1, $hListView_2 Local $iColPrev = __WinAPI_SetTextColor($hDC, 0x000000) ;save previous font and text colour Local $hFontOld = __WinAPI_SelectObject($hDC, $aFont[0]) ;Bold ;------------------------------------------------------------ ;multiline, but no per line indentation or colour ;DllStructSetData($tLVRect, 2, $iTop+2) ;__WinAPI_DrawText($hDC, "From:"&@CRLF&"Sent:"&@CRLF&"To:"&@CRLF&"Subject:", $tLVRect, $iTextFormatting) ;------------------------------------------------------------ DllStructSetData($tLVRect, 2, $iTop + 2) __WinAPI_DrawText($hDC, $aTextHdr[0], $tLVRect, $iTextFormatting) DllStructSetData($tLVRect, 2, $iTop + 20) __WinAPI_DrawText($hDC, $aTextHdr[1], $tLVRect, $iTextFormatting) DllStructSetData($tLVRect, 2, $iTop + 38) __WinAPI_DrawText($hDC, $aTextHdr[2], $tLVRect, $iTextFormatting) DllStructSetData($tLVRect, 2, $iTop + 56) __WinAPI_DrawText($hDC, $aTextHdr[3], $tLVRect, $iTextFormatting) __WinAPI_SelectObject($hDC, $aFont[1]) ;Normal ;------------------------------------------------------------ ;multiline, but no per line indentation or colour ;__WinAPI_SetTextColor($hDC, 0x494949) ;DllStructSetData($tLVRect, 1, $iLeft+50) ;DllStructSetData($tLVRect, 2, $iTop+2) ;__WinAPI_DrawText($hDC, $aSubItmText[0]&@CRLF&$aSubItmText[1]&@CRLF&$aSubItmText[2]&@CRLF&$aSubItmText[3], $tLVRect, $iTextFormatting) ;------------------------------------------------------------ __WinAPI_SetTextColor($hDC, 0xFF0000) DllStructSetData($tLVRect, 1, $iLeft + __GUICtrlListView_GetStringWidth($cID, $aTextHdr[0]) + 5) DllStructSetData($tLVRect, 2, $iTop + 2) __WinAPI_DrawText($hDC, $aSubItmText[0], $tLVRect, $iTextFormatting) __WinAPI_SetTextColor($hDC, 0x494949) DllStructSetData($tLVRect, 1, $iLeft + __GUICtrlListView_GetStringWidth($cID, $aTextHdr[1]) + 5) DllStructSetData($tLVRect, 2, $iTop + 20) __WinAPI_DrawText($hDC, $aSubItmText[1], $tLVRect, $iTextFormatting) DllStructSetData($tLVRect, 1, $iLeft + __GUICtrlListView_GetStringWidth($cID, $aTextHdr[2]) + 5) DllStructSetData($tLVRect, 2, $iTop + 38) __WinAPI_DrawText($hDC, $aSubItmText[2], $tLVRect, $iTextFormatting) DllStructSetData($tLVRect, 1, $iLeft + __GUICtrlListView_GetStringWidth($cID, $aTextHdr[3]) + 5) DllStructSetData($tLVRect, 2, $iTop + 56) __WinAPI_DrawText($hDC, $aSubItmText[3], $tLVRect, $iTextFormatting) __WinAPI_SetTextColor($hDC, $iTxtCol) DllStructSetData($tLVRect, 1, $iLeft + 2) DllStructSetData($tLVRect, 2, $iTop + 80) __WinAPI_DrawText($hDC, $aSubItmText[4], $tLVRect, $iTextFormatting) If $bSelected And $hTheme Then Return ;optional - don't paint lines when item selected - in this example only for benefit of themed selected items Local $obj_orig = __WinAPI_SelectObject($hDC, $hPen1) __WinAPI_DrawLine($hDC, $iLeft, DllStructGetData($tLVRect, 4) - 2, 320 - ($iLeft * 2) - 16, DllStructGetData($tLVRect, 4) - 2) __WinAPI_SelectObject($hDC, $hPen2) __WinAPI_DrawLine($hDC, $iLeft, DllStructGetData($tLVRect, 4) - 3, 320 - ($iLeft * 2) - 16, DllStructGetData($tLVRect, 4) - 3) __WinAPI_SelectObject($hDC, $obj_orig) ;__WinAPI_SelectObject($hDC, $hFontOld) ;__WinAPI_SetTextColor($hDC, $iColPrev) EndSwitch Return EndFunc ;==>__WM_DRAWITEM_ListView Func __WinAPI_DrawLine($hDC, $iX1, $iY1, $iX2, $iY2) DllCall($iDllGDI, "bool", "MoveToEx", "handle", $hDC, "int", $iX1, "int", $iY1, "ptr", 0) If @error Then Return SetError(@error, @extended, False) DllCall($iDllGDI, "bool", "LineTo", "handle", $hDC, "int", $iX2, "int", $iY2) If @error Then Return SetError(@error, @extended, False) Return True EndFunc ;==>__WinAPI_DrawLine Func __WinAPI_DrawText(ByRef $hDC, $sText, ByRef $tRect, ByRef $iFlags) DllCall($iDllUSER32, "int", "DrawTextW", "hwnd", $hDC, "wstr", $sText, "int", StringLen($sText), "struct*", $tRect, "int", $iFlags) EndFunc ;==>__WinAPI_DrawText Func __WinAPI_SetTextColor($hDC, $iColor) Local $aResult = DllCall($iDllGDI, "INT", "SetTextColor", "handle", $hDC, "dword", $iColor) If @error Then Return SetError(@error, @extended, -1) Return $aResult[0] EndFunc ;==>__WinAPI_SetTextColor Func __WinAPI_SelectObject($hDC, $hGDIObj) Local $aResult = DllCall($iDllGDI, "handle", "SelectObject", "handle", $hDC, "handle", $hGDIObj) If @error Then Return SetError(@error, @extended, False) Return $aResult[0] EndFunc ;==>__WinAPI_SelectObject Func __GUICtrlListView_GetStringWidth($hWnd, $sString) Local $tBuffer = DllStructCreate("wchar Text[" & StringLen($sString) + 1 & "]") DllStructSetData($tBuffer, "Text", $sString) Local $iRet = GUICtrlSendMsg($hWnd, $LVM_GETSTRINGWIDTHW, 0, DllStructGetPtr($tBuffer)) Return $iRet EndFunc ;==>__GUICtrlListView_GetStringWidth1 point
-
Mbee, I see no abuse in that post, merely a sensible comment that asking us to guess what might be the cause of a specific problem in a very large script is not really a reasonable thing to do. Your comment about being asked to isolate "snippets" of code is exactly what you are asking us to do - but without even sight of the code, which makes it several orders of magnitude more difficult. I suggest you extract the following code from your magnum opus: The GUI (including the various controls on which you want to drop) and the basics of the handler you use to recognise a drop - plus sufficient other lines to make it actually runnable. We do not need all the fancy functions etc - just the bare bones code. Then we can see if there is something fundamentally wrong with the way in which you are coding the whole process - or whether the problem lies elsewhere in these thousands of lines. This incremental approach is the only sensible manner to manage such a problem, so please cool down and help us to help you. M231 point
-
@Danp2 I'm curious: Why you are using: If $_WD_DEBUG Then ConsoleWrite($sFuncName & ': ' & $sResponse & @CRLF) EndIf instead: If $_WD_DEBUG Then _ ConsoleWrite($sFuncName & ': ' & $sResponse & @CRLF) IMHO: If you do not plan to add more lines in this IF THE ENDIF statement then make them as SINGLE LINE, as in compiled version it will be less code.1 point