Fazer o AHK aguardar uma resposta do sistema

Tire suas dúvidas sobre programação em AutoHotkey
Post Reply
RicMic
Posts: 5
Joined: 31 Jul 2018, 13:17

Fazer o AHK aguardar uma resposta do sistema

Post by RicMic » 01 Aug 2018, 11:45

Olá, amiguinhos!

Trabalho com um sistema que não tem tempo de resposta estável aos comandos, podendo variar de instantes até muitos segundos, o que baixa a confiabilidade dos scripts que trabalham com a previsão de tempo de resposta estimada pelo comando "Sleep".

Sei que há uma possibilidade de programar o AHK para "aguardar" uma resposta do sistema, assim como, dependendo do tipo da resposta, executar uma determinada ação.

Trocando em miúdos: preciso fazer que o AHK aguarde a resposta para a próxima ação (diminuindo ou aumentando de acordo com a "velocidade" do sistema no dia), assim como executando determinada ação, correspondente à resposta do sistema, sem ter de ficar fazendo verificações de todas as possibilidades em loop.

Uso um script de reconhecimento de imagem muito bacana (findtext, postado pelo Feiyue), que deu um salto de qualidade nas nossas programações.

Socorro! Alguém dá uma luz?
User avatar
Gio
Posts: 470
Joined: 30 Sep 2013, 10:54
Location: Brazil

Re: Fazer o AHK aguardar uma resposta do sistema

Post by Gio » 01 Aug 2018, 15:19

Boa tarde RicMic.

Seja bem-vindo ao fórum da comunidade do AutoHotkey.

A confiabilidade do script, especialmente quando falamos de automação de outros programas, é um dos objetivos que devem ser buscados desde o projeto inicial do script, quando pensamos na ideia geral. Além disso, o grau de conhecimento da linguagem de programação e de programação em geral influencia bastante no resultado final. Portanto, a dica que dou é dedicar algum tempo para estudar, conhecer e testar o máximo possível de comandos e também buscar sempre outras formas de interagir com os programas alvo (rotinas de importação, acesso a bancos de dados, tecnologias próprias de automação como o COM, etc).

No meu caso, já consegui fazer vários scripts com rotinas de automação praticamente 100% confiáveis aqui na empresa. A minha evolução com esses scripts se deu mais ou menos assim:

1. Quando comecei a programar scripts para reduzir o trabalho manual em alguns setores da empresa, eu costumava escrever scripts que clicavam nas coordenadas dos campos dos programas (Click), escreviam neles (Send) e que faziam tudo isso de forma intercalada com pausas (Sleep) de modo a aumentar a chance de o programa alvo ter respondido ao comando anterior da forma que eu esperava (pois quando isso não acontece, tudo acaba virando uma bagunça).

2. Com mais um pouco de tempo, eu passei a utilizar também os comandos que permitem controlar o estado das janelas (WinExist(), WinActivate, WinWaitActive) aliados à loops (Loop, While) e condicionais genéricas ou específicas (If (expression), IfWinActive) que checavam constantemente os estados das janelas antes de prosseguir.

3. Depois, comecei a escrever os scripts para interagir diretamente com os campos das janelas, escrevendo os valores no campos (ControlSetText, ControlSend, ControlClick) e verificando se haviam sido escritos corretamente (ControlGet, ControlGetText) de modo que não fosse mais necessário sequer que a janela estivesse ativa. (Dica: Enviar um sinal de espaço com o ControlSend ativa a maioria dos botões e alguns outros tipos de controles).

4. Passado algum tempo, comecei então a aprender um pouco sobre outras formas de interação entre programas, e vi que muitos programas de escritórios possuem rotinas próprias de importação de arquivos de texto, csv ou xml, de modo que a o método mais confiável passava a ser escrever os arquivos de importação (FileAppend) no layout definido pelo programa alvo e importar por ele. Além disso, alguns programas, como o Excel, possuem tecnologias que permitem programas as ações (como a tecnologia COM), sendo métodos de alta confiabilidade para automação das rotinas.

5. Finalmente, comecei a aprender a trabalhar diretamente com os bancos de dados (utilizando SQL), o que também permite um alto nível de confiabilidade na automação dos programas (utilizando uma função chamada adosql(), escrita por um membro da comunidade, é possível executar queries de dentro do script).

Nem sempre vamos ter acesso ao banco de dados dos programas (pois muitas vezes os desenvolvedores não querem liberar) ou lidar com programas que tenham rotinas de importação, mas se qualquer dos dois casos for possível, prefira eles, pois o nível de confiabilidade aumenta muito. Se não forem possíveis, você pode utilizar os outros comandos para criar redundâncias de checagem em um nível aceitável, de modo que a confiabilidade do script seja maior. Essas checagens podem ser feitas de vários métodos diferentes (por exemplo, lendo o texto inserido nos campos, na janela, o título da janela, verificando se uma janela abriu, verificando o título da janela, etc), portanto, é importante sempre lembrar que pode haver um método mais confiável do que o outro e igualmente válido.
preciso fazer que o AHK aguarde a resposta para a próxima ação
Essa questão do aguardar uma resposta vai depender um pouco de que ação o script está executando naquele momento e de como o programa reage aquela ação. Um comando que ativa uma tela permite o uso do WinWaitActive, e um comando que abre uma tela, permite o uso do WinWait. Da mesma forma, existem as variantes negativas (esperar que uma janela não esteja mais ativa ou que não existe mais). Fora isso, a forma de fazer a checagem é realmente com o uso de loops, onde a rotina interna do loop vai esperar uma mudança na tela ocorrer para terminar o loop (break). Existem muitas formas de checar alterações na janela, como coletar os dados dos campos (ControlGetText) ou o texto das telas (WinGetText) e compará-los até que surja a alteração esperada (enviando novamente o comando se ela demorar muito).

:arrow: Uma dica final é sempre que terminar o script, dedicar um tempo à otimização dele. Isso significa basicamente testar o script exaustivamente e tentar encontrar gargalos de confiabilidade ou tempo de execução, reescrevendo eles sempre que possível (normalmente, quando o script é longo, uma parte dele costuma ficar mais sensível a ocasionar erros do que o restante).
"What is suitable automation? Whatever saves your day for the greater matters."
Barcoder - Create QR Codes and other Barcodes using only Autohotkey !!
RicMic
Posts: 5
Joined: 31 Jul 2018, 13:17

Re: Fazer o AHK aguardar uma resposta do sistema

Post by RicMic » 03 Aug 2018, 09:31

Nossa, meu horizonte expandiu muito com essa introdução. Há pouco superamos a fase dos clicks em coordenadas na tela.

Quando começamos com o reconhecimento de imagens, passamos a fazer scripts para rodar em máquinas diferentes, uma vez que antes tínhamos que ajustar todas as coordenadas da tela para cada terminal em que executaríamos os scripts.

Agora vou começar com o meu script tester a verificar as melhores possibilidades de otimizar a interação com o sistema. Creio que seja viável associar os comandos de espera com o reconhecimento de imagem para as ações de acordo com a resposta.

Mãos à obra!

Se alguém mais tiver experiências com essas frentes de interação entre sistemas, compartilhem! Vai ser um excelente aprendizado! :dance:
User avatar
Gio
Posts: 470
Joined: 30 Sep 2013, 10:54
Location: Brazil

Re: Fazer o AHK aguardar uma resposta do sistema

Post by Gio » 03 Aug 2018, 12:07

Outra dica importante é utilizar o Window Spy para encontrar o ClassNN dos controles alvo (quando for programar com ControlSend e ControlSetText). O Window Spy é um script do AutoHotkey que vem junto com o pacote de instalação das versões 1.1+. Para acessá-lo, basta executar qualquer script do AutoHotkey que não termine a execução imediatamente (como por exemplo os que tenham janelas ou hotkeys) e depois clicar com o botão direito do mouse no ícone de H verde que fica na bandeja ao lado inferior-direito da tela (as vezes é preciso clicar na seta para exibir os ícones ocultos). Depois é só selecionar a opção Window Spy.

A janela que se abre é uma janela que fica sempre por cima das outras, e ela basicamente contém informações sobre a janela ativa atual e sobre os controles onde você posicionar o mouse encima. Assim, você pode facilmente descobrir o ClassNN do terceiro campo edit da janela (por exemplo), e usar isso para direcionar um ControlSetText.
"What is suitable automation? Whatever saves your day for the greater matters."
Barcoder - Create QR Codes and other Barcodes using only Autohotkey !!
RicMic
Posts: 5
Joined: 31 Jul 2018, 13:17

Re: Fazer o AHK aguardar uma resposta do sistema

Post by RicMic » 06 Aug 2018, 15:07

Muito obrigado pelas dicas até agora!

Iniciei os testes dos comandos e tenho tido dificuldades.

Estou tentando algumas formas de enviar comandos e interagir com as janelas e campos do meu sistema de trabalho (a partir dos dados levantados pelo WindowSpy), mas não tenho conseguido resultado:

Nossos PCs são bloqueados para muitas funções e não tenho privilégio de administrador. Pode ser esse o problema?

F1::
WinGetActiveTitle, Title
ControlFocus, CampoMascara10, ahk_exe programa.exe
Return

F2::
WinGetActiveTitle, Title
ControlClick, CampoMascara10, ahk_class TMenu
Return

F3::
WinGetActiveTitle, Title
ControlGetFocus, CampoMascara10, ahk_class TMenu ;Focus on the control
Return
User avatar
Gio
Posts: 470
Joined: 30 Sep 2013, 10:54
Location: Brazil

Re: Fazer o AHK aguardar uma resposta do sistema

Post by Gio » 06 Aug 2018, 16:29

Boa tarde RicMic.

Os privilégios administrativos podem afetar, mas como você ainda está experimentando com os comandos, o melhor é não presumirmos isso ainda. Não tenho como saber se os dados informados no seu exemplo correspondem aos do seu programa alvo, então vamos fazer o seguinte: vou criar um exemplo que nós dois possamos replicar e quando estivermos certos de que o método está compreendido, movemos para outros programas ok :thumbup:

Primeiro, vamos criar uma GUI própria, que possua um título, alguns campos, uma classe de janela, controles com várias classNNs e etc. Depois vamos verificar como encontrar essas informações no WindowSpy e aí criar hotkeys para interagir com esses controles usando os comandos adequados.


1. Criando a GUI - A gui que vamos criar é bem simples: Ela se chamará "Gui do Barulho" e vai permitir que o usuário insira texto em três campos. O texto do primeiro campo é exibido em uma msgbox com um sinal de erro (vermelho) quando o usuário clicar no botão "Gritar" e o texto do segundo campo é exibido em uma msgbox com um sinal de atenção (amarelo) quando o usuário clicar no botão "berrar". Além disso, tem um texto encima no meio da Gui chamado "Texto Legal". Por fim, o texto do terceiro campo será lido pelo assistente de voz do windows quando você clicar no botão "falar".

Code: Select all

Gui, Font, w700 s10
Gui, add, Text, x0 w600 y10 Center, Texto Legal
Gui, add, Text, x20 y30, Mesagem do Grito
Gui, add, Edit, x20 y50 w200 vCAMPO_DO_GRITO
Gui, add, button, x240 y50 gGRITAR, Gritar
Gui, add, Text, x20 y90, Mesagem do Berro
Gui, add, Edit, x20 y110 w200 vCAMPO_DO_BERRO
Gui, add, button, x240 y110 gBERRAR, Berrar
Gui, add, Text, x20 y150, Mensagem da Fala
Gui, add, Edit, x20 y170 w200 vCAMPO_DA_FALA
Gui, add, Button, x240 y170 gFALAR, Falar
Gui, show, w600 h400, Gui Do Barulho ; O título da tela  foi definido neste comando: ela se chamará Gui Do Barulho

Return


Gritar:
Gui, Submit, nohide
Msgbox, 0x10, Grito, %CAMPO_DO_GRITO%
Return


Berrar:
Gui, submit, nohide
Msgbox, 0x30, Berro, %CAMPO_DO_BERRO%
Return

Falar:
Gui, Submit, nohide
ComObjCreate("SAPI.SpVoice").Speak(CAMPO_DA_FALA)
Return

2. Descobrindo as informações - Agora que a Gui já está criada, vamos usar o Window Spy para descobrir o título da janela e o ClassNN dos controles que queremos manipular. Você pode verificar isso nas imagens abaixo: as setas vemelhas representam o título da janela na própria janela e no Window Spy. As setas em verde representam o campo cujo mouse está encima e o corresponde ClassNN na janela do Window Spy.
Texto encima.png
(20.39 KiB) Not downloaded yet
primeiro campo.png
(21.38 KiB) Not downloaded yet
Primeiro botao.png
(22.18 KiB) Not downloaded yet
3. Escrevendo os comandos a partir dos ClassNN - Agora que já temos os dados necessários, vamos escrever os comandos para interação com a janela. Vou colocá-los nas hotkeys F2, F3, F4, F5, F6, F7, F8 e F9. Cada uma fará uma coisa diferente entre alterar o texto do campo, acionar o botão (de 2 formas) ou trocar o texto centralizado. Veja como os parâmetros obtidos são escritos, e lembre-se que a ordem dos parâmetros nos comandos é importante. Veja também os métodos utilizados em cada situação.

Code: Select all

Gui, Font, w700 s10
Gui, add, Text, x0 w600 y10 Center, Texto Legal
Gui, add, Text, x20 y30, Mesagem do Grito
Gui, add, Edit, x20 y50 w200 vCAMPO_DO_GRITO
Gui, add, button, x240 y50 gGRITAR, Gritar
Gui, add, Text, x20 y90, Mesagem do Berro
Gui, add, Edit, x20 y110 w200 vCAMPO_DO_BERRO
Gui, add, button, x240 y110 gBERRAR, Berrar
Gui, add, Text, x20 y150, Mensagem da Fala
Gui, add, Edit, x20 y170 w200 vCAMPO_DA_FALA
Gui, add, Button, x240 y170 gFALAR, Falar
Gui, show, w600 h400, Gui Do Barulho ; O título da tela  foi definido neste comando: ela se chamará Gui Do Barulho

Return


Gritar:
Gui, Submit, nohide
Msgbox, 0x10, Grito, %CAMPO_DO_GRITO%
Return


Berrar:
Gui, submit, nohide
Msgbox, 0x30, Berro, %CAMPO_DO_BERRO%
Return

Falar:
Gui, Submit, nohide
ComObjCreate("SAPI.SpVoice").Speak(CAMPO_DA_FALA)
Return


F2::
ControlSetText, Edit1, GRITO DA TORCIDA !!, Gui Do Barulho ; ALTERA O TEXTO DO SEGUNDO CAMPO USANDO O CLASSNN EDIT1 E O TÍTULO DA JANELA
Return


F3::
ControlSetText, Edit2, GRITO DA GALERA !!, Gui Do Barulho ; ALTERA O TEXTO DO SEGUNDO CAMPO USANDO O CLASSNN EDIT2 E O TÍTULO DA JANELA.
Return

F4::
ControlClick, Button1, Gui Do Barulho ; ATIVA O BOTÃO "GRITAR" ATRAVÉS DO CLASSNN DO BOTÃO, DO TÍTULO DA JANELA E DO COMANDO CONTROLCLICK.
Return

F5::
ControlSend, Button2, %A_Space%, Gui Do Barulho ; ATIVA O BOTÃO "BERRAR" ATRAVÉS DO CLASSNN DO BOTÃO, DO TÍTULO DA JANELA E DO ENVIO DE UM SINAL DE ESPAÇO.
Return

F6:: 
Gui, Font, s10 w700 cFF0000 ; ALTERA A FONTE DA GUI. VERMELHO FF, VERDE 00, AZUL 00.
GuiControl, Font, Static1 ; ALTERA A FONTE DO CONTROLE PARA USAR A NOVA FONTE DA GUI (E NAO MAIS A QUE ESTAVA ATIVA QUANDO O CONTROLE FOI CRIADO)
ControlSetText, Static1, Texto DIFERENTE, Gui Do Barulho ; ALTERA O TEXTO QUE FICA ENCIMA NA JANELA.
return

F7::
Gui, Font, s10 w700 c009000 ; ALTERA A FONTE DA GUI. VERMELHO 00, VERDE 90, AZUL 00.
GuiControl, Font, Static1 ; ALTERA A FONTE DO CONTROLE PARA USAR A NOVA FONTE DA GUI (E NAO MAIS A QUE ESTAVA ATIVA QUANDO O CONTROLE FOI CRIADO)
ControlSetText, Static1, TEXTO MAIS DIFERENTE AINDA, Gui Do Barulho ; ALTERA O TEXTO QUE FICA ENCIMA NA JANELA.
REturn

F8::
ControlSend, Edit1, ABC, Gui Do Barulho ; Usa o ControlSend ao invés do ControlSetText para adicionar ao campo 1 (note que o texto que existia não se apaga).
Return

F9::
; MÉTODO ALTERNATIVO AO CLASSNN PARA MANIPULAR UM CONTROLE: COLOQUE O MOUSE ENCIMA DE QUALQUER CONTROLE DA TELA E ATIVE ESTA HOTKEY.
MouseGetPos,,,,HANDLE_DO_CONTROLE, 2 ; Usa o MouseGetPos para descobrir o handle do control.
ControlSetText,, TÁ COM O MOUSE AQUI !!, ahk_id %HANDLE_DO_CONTROLE%
Return
Execute o código acima e tecle F2, F3, F4, F5, F6, F7, F8, e F9. Veja o que acontece com a tela em cada caso. Depois, analise como foram escritos os comandos em cada hotkey. Depois, tente reproduzir o método demonstrado, escrevendo alguns dos comandos você mesmo, de modo que eles possam afetar especificamente o terceiro campo e o terceiro botão, que não foram afetados nas hotkeys ainda (exceto F9).

:arrow: OBS: O F9 deve ser usado com o mouse encima de um controle da tela (qualquer controle). Trata-se de um método alternativo para interagir com um controle através do handle (e não do ClassNN).

Uma vez que estiver craque em escrever os comandos para automatizar essa gui própria, ficará mais fácil escrever comandos para automatizar outras GUIs :thumbup:

Dica: Quase sempre tem mais de uma forma de fazer alguma coisa em programação. Quando uma não funciona, devemos sempre procurar suas alternativas (workarounds). O conhecimento que você adquire ao longo dos anos é muito importante aqui, por isso nunca pare de frequentar os fórums e ver o que os outros estão fazendo de inovador. Como exemplo, podemos criar um controle de janela do tipo botão facilmente usando o comando Gui, Add, Button, mas se isso não funcionar, o mesmo também pode ser feito com algumas DllCall()s ou até mesmo de outras formas criativas, como usando um controle do tipo imagem com alguns gatilhos que operem quando o usuário clicar na imagem. Convém lembrar que tudo o que já vimos sendo feito em um computador foi feito através de programação, e nesse sentido, saiba que o AutoHotkey pode fazer quase tudo o que é programável também: desde programas de escritório a jogos 3D com gráficos OpenGl e etc.
"What is suitable automation? Whatever saves your day for the greater matters."
Barcoder - Create QR Codes and other Barcodes using only Autohotkey !!
RicMic
Posts: 5
Joined: 31 Jul 2018, 13:17

Re: Fazer o AHK aguardar uma resposta do sistema

Post by RicMic » 14 Aug 2018, 14:31

Olá novamente!

Após nossa conversa, comecei a fazer testes para aprimorar os scripts e superar a fase do sleep.

Percebi que meu sistema não permite a interação via comandos como "WinWait" ou WinActive", e tb não funciona com comandos do tipo "ControlGetText" e "ControlClick", ou seja, não consigo usar nomes de janelas, nem ClassNN para interagir com meu sistema.

A saída para o impasse foi tentar o reconhecimento de imagens. Capturei partes das telas do sistema e consegui fazer com que o AHK aguarde até que elas apareçam, com o seguinte :

Code: Select all

SetWorkingDir %A_ScriptDir%
Loop
{
  ImageSearch, x, y, 0, 0, 2731, 763, *30 visualizadoc.png
  ifEqual ErrorLevel,0
  {
    Break ;Do do stuff with that image now
  }
  else if ErrorLevel,1
  {
    Sleep 500
    Continue ;Look again
  }
  else ifEqual ErrorLevel,2
  {
    msgbox Imagem nao encontrada ;Something wrong with source image.
  }
}
A partir daí, uso um localizador de imagem que movimenta o cursor até o campo de preenchimento ou botão para prosseguir. Esse problema parece estar resolvido. :superhappy:

A questão agora é que em algumas fases do trabalho, o sistema responde com janelas de avisos; umas 4 diferentes, que podem ou não aparecer conforme a informação lançada (alguns casos aparece 1, outros podem aparecer até 4 em sequência, alguns não aparece nada e, nas que aparecem mais de 1, a ordem não é necessariamente a mesma). Preciso programar a resposta do AHK com a ação correspondente a cada janela (um botão "ok", um botão "fechar" e coisas do tipo). Eu faria uma busca sequencial das alternativas em loop até a sequência do trabalho retomar o andamento.

Percebi que o comando acima é muito eficiente para as janelas que são esperadas, mas não consegui acertar até agora que ele trabalhe com variações, ou seja, janelas que podem ou não aparecer e, caso não encontre, prossiga com o próximo comando. :headwall:

Code: Select all

SetWorkingDir %A_ScriptDir%
Loop 3
{
  ImageSearch, x, y, 0, 0, 2731, 763, *30 recadoamarelo.png
  ifEqual ErrorLevel,0
  {
    Break ;Do do stuff with that image now
  }
  else if ErrorLevel,1
  {
    Sleep 500
    Continue ;Look again
  }
Nos casos em que aparece o tal "recadoamarelo", funcionou tranquilo, mas em alguns casos em que não apareceu, o script travou e ficou procurando a imagem pra sempre.

Será que tem alguma variação de sintaxe mais adequada? :eh:
RicMic
Posts: 5
Joined: 31 Jul 2018, 13:17

Re: Fazer o AHK aguardar uma resposta do sistema

Post by RicMic » 22 Aug 2018, 15:45

Após algumas pesquisas, temos um vencedor!!!

O comando que melhor atendeu nossa demanda:

Code: Select all

CoordMode, Pixel, Screen

SetWorkingDir %A_ScriptDir%
Loop
{
  ImageSearch, x, y, 0, 0, 2731, 763, *50 recado.png
  if (ErrorLevel = 0)
  {
    Break
  }
  if (ErrorLevel =1)
  {
   Sleep, 500
   Continue
  }  
if (ErrorLevel = 2)
{
msgbox, nao consegui procurar
}
}

Nessa configuração, o comando aguarda a imagem "recado aparecer na tela para prosseguir no script.

Também é possível fazer com que aguarde determinada imagem sumir da tela; basta inverter a ação do ErrorLevel 0 com o ErrorLevel 1. Ficou ótimo :dance:

Também pudemos mudar a resposta, para executar uma sequência de comandos novamente ou prosseguir, usando o comando "GoTo, label" nas ações dos ErrorLevel.

Valeu pelas dicas!! :thumbup:
Post Reply

Return to “Ajuda e Suporte Geral”