I started a Tkinter application but I'm with problems with buffering. I searched the solution but didn't find it.
Correlated links:
- Calling python script with subprocess popen and flushing the data
- Python C program subprocess hangs at "for line in iter"
- Python subprocess standard output to a variable
As an exemple, this app has two buttons: Start
and Stop
.
When I press the button Start
, the spammer.py
is called as a subprocess and when I press the button Stop
the program must be killed.
# spammer.py
import time
counter = 0
while counter < 40: # This process will last 10 second maximum
print("counter = %d" % counter)
counter += 1
time.sleep(0.25) # 4 messages/second
While the PrintNumbers.py
is running, I want the spammer's output be storage in a variable inside the Tkinter to be used in realtime. But once I try to read the buffer with myprocess.stdout.readline
, it stucks and it doesn't continue until the subprocess finish and as consequence for exemple I cannot click on the Stop
button.
I read that the function is waiting for EOF
to continue, and I tried to use tokens as shown here, and the function that should continue when it finds a
or a \n
, but it did not work.
The Tkinter exemple is bellow. After I click at the Start
button, I instantly see the message Started reading stdout
, and after 10 seconds it shows a lot of messages, while I wanted to show every message over time.
# PrintNumbers.py
import Tkinter as tk
import subprocess
class App:
def __init__(self, root):
self.root = root
self.myprocess = None
self.logmessages = []
self.createButtons()
self.timedUpdate()
def createButtons(self):
self.ButtonsFrame = tk.Frame(self.root, width=600, height=400)
self.ButtonsFrame.pack()
self.startbutton = tk.Button(self.ButtonsFrame, text="Start",
command=self.__ClickOnStarButton)
self.stopbutton = tk.Button(self.ButtonsFrame, text="Stop",
command=self.__ClickOnStopButton)
self.startbutton.pack()
self.stopbutton.pack()
self.startbutton["state"] = "normal"
self.stopbutton["state"] = "disable"
def __ClickOnStarButton(self):
print("Click on Start Button")
self.startbutton["state"] = "disable"
self.stopbutton["state"] = "normal"
self.startProcess()
def __ClickOnStopButton(self):
print("Click on Stop Button")
self.startbutton["state"] = "normal"
self.stopbutton["state"] = "disable"
self.killProcess()
def startProcess(self):
command = "python spammer.py"
self.myprocess = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=1)
def killProcess(self):
self.myprocess.terminate()
self.myprocess.wait()
self.myprocess = None
def timedUpdate(self):
if self.myprocess is not None: # There's a process running
self.getLogText() # Will get the info from spammer.py
self.treatOutput() # Do whatever we want with the data
root.after(200, self.timedUpdate) # Every 0.2 seconds we will update
def getLogText(self):
if self.myprocess is None: # There's no process running
return
# The problem is here
print("Started reading stdout")
for line in iter(self.myprocess.stdout.readline, ''):
print(" Inside the loop. line = '%s'" % line)
self.logmessages.append(line)
print("Finished reading stdout")
def treatOutput(self):
# Any function that uses the spammer's output
# it's here just to test
while len(self.logmessages):
line = self.logmessage.pop(0)
line = line.replace("counter = ", "")
mynumber = int(line)
if mynumber % 3:
print(mynumber)
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
root.mainloop()
How can I read the output without getting stuck? I'm still using python 2.7, and I don't know if it's the problem either.