Home > Software design >  root.after is not recursing to the same function - Tkinter
root.after is not recursing to the same function - Tkinter

Time:11-26

I'm having a problem with the root.after function in Tkinter.

I'm trying to run a subprocess, and while it runs, I check for the process.poll(), if it's still None, then call back the checking function:

self.PingProcess = subprocess.Popen(
    r'powershell.exe -ExecutionPolicy RemoteSigned -file "file1.ps1"',
    stdout=sys.stdout)
self.PingContinuous() # Checks the PingProcess poll until the process is finished

AceThread = threading.Thread(target=self.CheckAce())
AceThread.start()
DrivesThread = threading.Thread(target=self.CheckDrives())
DrivesThread.start()
self.continuous_check() # Checks the AceThread and DrivesThread (there are
                        # processes in them)

def PingContinuous(self):
    PingProcess = self.PingProcess.poll()
    if PingProcess is None:
        self.ui.root.after(500, self.PingContinuous)
    else:
        print("Finished")

def CheckAce(self):
    self.AceProcess = subprocess.Popen(
        r'powershell.exe -ExecutionPolicy RemoteSigned -file "file.ps1"',
        stdout=sys.stdout)

def CheckDrives(self):
    self.DrivesProcess = subprocess.Popen(
        r'powershell.exe -ExecutionPolicy RemoteSigned -file "file3.ps1"',
        stdout=sys.stdout)

def continuous_check(self):
    AceProcess = self.AceProcess.poll()
    DrivesProcess = self.DrivesProcess.poll()
    if PingProcess is None and DrivesProcess is None:
        self.ui.root.after(500, self.continuous_check)
    else:
        print("Finished Ace and Drives")

The problem is that the PingContinuous() should keep running recursively with the

self.ui.root.after(500, self.PingContinuous)

But instead, it just jumps back to the:

AceThread = threading.Thread(target=self.CheckAce())
AceThread.start()
DrivesThread = threading.Thread(target=self.CheckDrives())
DrivesThread.start()
self.continuous_check() # Checks the AceThread and DrivesThread (there are
                        # processes in them)

and it starts to jump back and forth between self.continuous_check and self.PingContinuous.

What I need is for the PingProcess to finish, and then run the rest of the threads.

I can't use time.sleep or process.wait(), because this will freeze my GUI

CodePudding user response:

PingContinuous isn't blocking. You're calling it (which is presumably working), it sets up a call to itself in a new thread for later on, then carries on into the AceThread stuff.

You could run PingContinuous in a new thread in a loop rather than recursively, and then have your main thread join the new thread before carrying on (so that it'll block until PingContinuous finishes)? Something like this:

def PingContinuous(self):
  while not self.PingProcess.poll():
    # You might want a maximum number of iterations before giving up, too
    # so that you don't loop forever if it's broken
    time.sleep(500)
  print("Finished")

pingThread = threading.Thread(target=self.PingContinuous)
pingThread.start()
pingThread.join()

# Now carry on with AceThread etc. here

...but this may block your GUI as well, depending on how you're running that. You probably need your GUI and your program logic to be in separate threads so that you can block one without blocking the other.

CodePudding user response:

The usage of .after() in your code works fine.

Actually you don't need to run self.CheckAce() and self.CheckDrives() using threads:

...
self.CheckAce()
self.CheckDrives()
self.continuous_check()
...

Also there is typo inside continuous_check(): PingProcess should be AceProcess.

def continuous_check(self):
    AceProcess = self.AceProcess.poll()
    DrivesProcess = self.DrivesProcess.poll()
    # Typo: PingProcess should be AceProcess instead
    if AceProcess is None and DrivesProcess is None:
        self.ui.root.after(500, self.continuous_check)
    else:
        print("Finished Ace and Drives")
  • Related