Phyton wait pause

Asked

Viewed 508 times

1

I want to automate a task, as the software has no api that allows this, I will control the mouse of the OS and click using Phyton.

I intend to create validations with images to ensure that I am on the right screen and clicking on the right place, even so I would like extra protection, which would stop the execution by pressing P on the keyboard and continue by clicking U.

  • My question is, if you have how to do this without having to repeat the check to each line of the program, wait for the click and stop the execution when receiving the user info.

I’m new to Phyton, first try.

Edited

Adjusting the question to answer some questions posed by @jsbueno:

  • I’m using Windows 10.
  • It is a program with only lines of code, edited in VSCODE.
  • I’m importing pyautogui (not yet actually), curses and threading.

Now in the morning I’ve been studying about this and I’ve already got some results, it’s stopping and continuing correctly, but when giving the stop() to kill the thread, it doesn’t release the terminal, it gets stuck without asking for a key or anything, follows the code:

from threading import Thread
from threading import Event
from time import sleep
import curses


class xx(Thread):
    _stop = 0

    def __init__(self):
        self._active = Event()
        self._active.set()
        Thread.__init__(self)

    def run(self):
        global x
        global f
        y = 1
        while True:
            if self._stop != 0:
                return
            self._active.wait()
            x += 1
            y += 1
            sleep(1)
            f = open("output.txt", "a+")
            f.write("x: " + str(x) + " - y: " + str(y) + "\n")
            f.close()
            if x == 10:
                return

    def pause(self):
        self._active.clear()

    def play(self):
        self._active.set()

    def stop(self):
        self._stop = 1


x = 0
f = open("output.txt", "w+")
f.write("Inicio\n")
f.close()


def main(win):
    f = open("output.txt", "a+")
    f.write("Main Start\n")
    f.close()
    instx = xx()
    instx.start()
    key = ""
    while True:
        win.clear()
        win.addstr("Detected key:")
        win.addstr(str(key))
        if key == 't':
            instx.stop()
            return
        elif key == 'p':
            instx.pause()
        elif key == 'u':
            instx.play()
        key = win.getkey()
    instx.join()


curses.wrapper(main)

I’m having trouble debugging the program, what you Python developers use for this ?

And if you have any book tips or courses to start in the language I am grateful.

To generate the above code, I took information from the following sources:

Thread work
* How to create a pause method and one that resumes a Thread?
* https://stackoverflow.com/questions/15063963/python-is-thread-still-running
* https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/

Keypad
* https://stackoverflow.com/questions/24072790/detect-key-press-in-python

Working with files ( for debug )
* https://www.guru99.com/reading-and-writing-files-in-python.html

Concatenate string and int
* https://www.journaldev.com/23642/python-concatenate-string-and-int

  • 1

    if you are using "threading", nothing will be able to debug the program. Without threads, it is only for a "breakpoint()" and follow the execution by the terminal.

2 answers

1

In short: in the end, yes, for what you want, you will have to call a function to check the keyboard between each line of the program.

It’s very difficult to give a concrete answer to an abstract question. You said nothing about the interface you will use for your program - To be able to vanity screen images, control the mouse, and etc...I assume you will be using the pyautogui which is a library that does this.

Also, by the text of the question, I assume that it is a program with no graphical interface, only with several lines with a call to the library that generates keyboard/mouse events after another.

In programs with graphical interface - such as Tkinter, or Qt, the framework itself provides a mechanism for detecting events and means for a function of its being called when the keyboard is used. In a program without a graphical interface, you are responsible for this -and yet, to read the keyboard in real time you have to use a library like curses or some other.

By the way, you haven’t even said whether you’re on Windows, Linux or Macos - keyboard control on the terminal, and library compatibility changes between these systems - makes the answer even harder.

Anyway, even in a GUI framework, in general, lines within a function are executed one after the other, without interruption.

The solution - with or without a graphical interface, seems to be to take advantage of the fact that you want to pause between a command and another, and write a function that pauses, always checking the keyboard. (I went to look at the pyautogui doc, he alone has nothing to read the keyboard - just to send keys).

But then, let’s assume you’re using the function time.sleep to pause between your commands - your program would already be something like this:

funcao_pra_clicar_em_ok()
time.sleep(0.5)
funcao_para_preencher_campo_nome()
funcao_para_preencher_senha()
time.sleep(0.2)
funcao_para_clicar_em_ok()

The output is you ciraruma another function for pause, after finding a function that checks the keyboard in real time in your environment, and instead of calling the team.Leep direct, call a function like:

def pausa(tempo_total):
   inicio = time.time()
   passo = 0.05
   while True:
      teclas = verifica_teclas()
      if teclas["P"]:
          # codigo para pausa
      ...
      if time.time() - inicio >= tempo_total:
          break
      time.sleep(passo)

(there are yes, ways to avoid having to call the pause function to each line, but it would be something quite sophisticated - see if you can bring more data about your environment, and maybe it is possible to improve the answer)

  • Thank you for the answer and feedback regarding the quality of the question.

1

Over a Pattern to interlink a function call to each line

I put general considerations about your problem in the other answer. To address more directly the question of "how to repeat a task after each line of code", I preferred to open another answer -

Even with frameworks that have event ties, or a multi-threading program, it’s pretty hard to ensure that a particular code will run after each "execution stretch".

Event loops in Frameworks like Tkinter and Qt will only check events when their function ends. Threads give no guarantee of when the check is done. In fact, the great "paradigm shift" to asynchronous programming - which introduced a whole different way of programming from Python 3.4 (and new keywords from Python 3.5), is precisely to be able to say, within a function, in what points the execution of the function can be "paused" so that another code can run in parallel. This is essentially what the "asyncio" does, all the other forms will either change to the other code (which checks the events) at the end of the function, or will change where you have no control over it (whether the change happens or not).

The module asyncio would allow you to organize pause+verification at every step of your program, but you would have to restructure your entire program to work with it, and turn all your calls that make the task step by step into co-routines. Then your program body would have to put the keyword await before each call. (And even then it would still give a correct trabalinho for the keyboard check to actually run with a pause)

Using asyncio is not what I would recommend in this case. I put an example at the end of the face that the code would have.

Place tasks to be called into a data structure

The simplest and least repetitive way to perform multiple tasks and ensure that some code is executed between each one, can be to specify tasks as data in a list, instead of program lines - and then have some code that coordinates the tasks' calls.

So instead of the code like this:

def executar()

    funcao_pra_clicar_em_ok(parametro1, parametro2)
    time.sleep(0.5)
    verificar_teclas()
    funcao_para_preencher_campo_nome()
    time.sleep(0.2)
    verificar_teclas() 
    funcao_para_preencher_senha()
    time.sleep(0.2)
    verificar_teclas() 
    funcao_para_clicar_em_ok()

You can take advantage that in Python, functions are objects like any other, and can be treated as data until the moment they are called -and then you can write something like this:

# Tarefas a serem executadas, como uma lista em que 
# cada elemento é uma sequência com a função a ser chamada
# e os parâmetros a passar:

lista_de_tarefas = [
   (funcao_para_clicar_em_ok, (parametro1, parametro2)),
   (funcao_para_preencher_campo_nome, ()),
   (funcao_para_preencher_senha, ()),
   (funcao_para_clicar_em_ok, ()),
]

def executar(tarefas):
    for tarefa in tarefas():
       tarefa[0](*tarefa[1])
       pausa()
       verifica_teclas()

executar(lista_de_tarefas)

example with Asyncio

Using asyncio the code could look something like:

encerrado = False
async def main():
    global encerrado
    
    await funcao_pra_clicar_em_ok()
    await funcao_para_preencher_campo_nome()
    await funcao_para_preencher_senha()
    await funcao_para_clicar_em_ok()

    encerrado = True

async def pausas():
   while not encerrado:
       teclas = verifica_teclado()
       if pressionado("P", teclas):
            # codigo de pausa
            ...
       time.sleep(0.5)  # Pausa real de 0.5 segundo
       await asyncio.sleep(0)  # Pausa falsa pra forçar mudança de contexto asyncio.

import asyncio
loop = asyncio.get_event_loop()
def start(loop):
    await loop.gather([main(), pausas()])
    
loop.run_until_complete(start())

Browser other questions tagged

You are not signed in. Login or sign up in order to post.