PYTHON - how to use a loop without stopping the Tkinter

0

I have this program which is a kind of 'AirForce' (I automated a click process in a place, type a number, and click elsewhere with @Pyautogui), I need to use a loop but when the loop starts Tkinter, so I could not think of how to do something to pause the 'loop'! the code:

from tkinter import *
import pyautogui
import time

class ForceBrute:
    def __init__(self,master=None):
#faz com que a janela não possa ser redimensionada pelo usuario
        master.resizable(width=False,height=False)

#Containers para os itens da interface
        self.con1 = Frame(master)
        self.con1['pady'] = 8
        self.con1.pack()

        self.con2 = Frame(master)
        self.con2['pady'] = 15
        self.con2.pack()


        self.con3 = Frame(master)
        self.con3['pady'] = 30
        self.con3.pack()


        self.con4 = Frame(master)
        self.con4['pady'] = 10
        self.con4.pack()


#itens da interface
        self.tTitle = Label(self.con1)
        self.tTitle['font'] = ('Roboto','30')
        self.tTitle['text'] = 'SiriusForce'
        self.tTitle.pack()

#recebe o valor de aparti de quando deve iniciar
        self.eDe = Entry(self.con2,justify = 'center',relief=RIDGE,width=3)
        self.eDe['font'] = ('Roboto', '20')
        self.eDe.pack(side=LEFT)
#coloca 100 no campo como padrão
        self.eDe.insert(0,'100')

        self.tAte = Label(self.con2)
        self.tAte['font'] = ('Roboto', '20')
        self.tAte['text'] = 'Ate:'
        self.tAte.pack(side=LEFT)

#recebe ate que valor deve ir
        self.eAte = Entry(self.con2,justify = 'center',relief=RIDGE,width=3)
        self.eAte['font'] = ('Roboto', '20')
        self.eAte.pack(side=LEFT)
        self.eAte.insert(0,'999')

#botão para iniciar 
        self.bIniciar = Button(self.con3,relief=RIDGE,fg='blue')
        self.bIniciar['font'] =('Roboto', '25')
        self.bIniciar['text'] = 'Iniciar'
        self.bIniciar['command'] = self.iniciar
        self.bIniciar.pack(side=LEFT)


        self.bPausar = Button(self.con3,relief=RIDGE,fg='red')
        self.bPausar['font'] =('Roboto', '25')
        self.bPausar['text'] = 'Pausar'
#      self.bPausar['command'] =           seria para pausar o 'laço' 
        self.bPausar.pack(side=LEFT)


#exibe em que numero parou
        self.eCont = Entry(self.con4,justify= 'center',relief=RIDGE,width=3,bg='black',fg='green')
        self.eCont['font'] = ('Roboto', '30')
        self.eCont.pack()




        self.cod = 0


    def iniciar(self):
        self.de = int(self.eDe.get()) -1 
        self.ate = int(self.eAte.get()) 

        print('Iniciado De:',self.de,'Ate:',self.ate) # só para visualizar 

        self.cod = self.de

        while(self.cod < self.ate):

            self.cod +=1

            pyautogui.doubleClick(697,363)
            pyautogui.typewrite(str(self.cod))

            print(self.cod)

            self.eCont.delete(0,END)
            self.eCont.insert(0,self.cod)

            pyautogui.click(880,516)

            time.sleep(1.5)


root = Tk()
root.geometry('220x350+400+100')
root.title('Generico')
root.attributes('-topmost',True)
ForceBrute(root)
root.mainloop()

I have seen in some places that I can use 'root.after ()' but could not implement.

    
asked by anonymous 06.02.2018 / 21:37

1 answer

2

The program runs on a single thread - then either it runs the tkinter loop, responsible for receiving and executing mouse events, keyboard, etc ... or stays in its while...time.sleep(1.5) .

This type of program (graph) always has to be based on responding to events: what is running all the time is the event loop - in the case of tkinter, which is activated by mainloop . The code snippets we write have to execute a task, and return processing to the main loop (also goes for Javascript code embedded in web pages, for example).

What has to be done is, along with the processing of your code, schedule an event to call the function again. This scheduling event is what is done by calling the method .after(intervalo, objeto chamável)

This callable event will rotate your code that has to be repeated once, and schedule your next call - and not create a while. In the case of your code, the most appropriate is to place the body of your while as a separate method, which will be called directly by tkinter, using .after :

class ForceBrute:
    def __init__(self,master=None):
        # Guarda uma referência ao root:
        self.master = master
        #faz com que a janela não possa ser redimensionada pelo usuario
        master.resizable(width=False,height=False)
        ...

    ...

    def iniciar(self):
        self.de = int(self.eDe.get()) -1 
        self.ate = int(self.eAte.get()) 

        print('Iniciado De:',self.de,'Ate:',self.ate) # só para visualizar 

        self.cod = self.de
        self.verifica()

    def verifica(self):

        self.cod +=1

        pyautogui.doubleClick(697,363)
        pyautogui.typewrite(str(self.cod))

        print(self.cod)

        self.eCont.delete(0,END)
        self.eCont.insert(0,self.cod)

        pyautogui.click(880,516)

        if (self.cod < self.ate):
            self.master.after(1500, self.verifica)

Then - the only other thing that was needed there was to save a reference to the program window within the class - because that is where the .after method is.

And the other important detail: when calling this type of method in which a callback function is passed, the parentheses are never placed after the name of the function or method. This would make the function run there, and its return result be sent as a parameter to after . Without parentheses, functions and methods are treated like any other variable by Python.

(e, 1500 is only 1.5 seconds rewritten in milliseconds)

    
07.02.2018 / 12:42