Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I'm creating a little Application where I can insert the IP-address of my android phone, and then with one button press. I am wirelessly connected over ADB. This works, now I want to display the log of
adb logcat
in a scrollable, not editable textfield. But here I bump into a problem, I am trying to catch the output of the logcat command like this
p = os.popen("adb logcat")
and then printing it like so
print(p.read())
. This only makes my application (tkinter) freeze, I am guessing this has to do with the fact that the printing never end. Does anybody have an idea on how to display the logcat result.
Code of function
def logcatcommand():
p = os.popen("adb logcat")
print(p.read())
Button:
button2 = tk.Button(text="Open logcat",width=25, height=3, command=logcatcommand)
button2.grid(row=5, column=0)
Do you guys also have an idea on how I could display this information realtime? I think I have to use code like this:
result = scrolledtext.ScrolledText(window, wrap = tk.WORD, width = 40, height = 10, font = ("Times New Roman", 15))
result.grid(column = 1, row=0)
result.insert(tk.INSERT, output)
result.configure(state ='disabled')
where output is the realtime data retrieved from the terminal.
sample lines logcat:
09-10 14:10:28.479 971 3009 I WifiHAL : event received NL80211_CMD_VENDOR, vendor_id = 0x1374, subcmd = 0xd
09-10 14:10:34.779 1526 4567 I BatteryStatsService: In wakeup_callback: resumed from suspend
09-10 14:10:35.321 1526 4567 I BatteryStatsService: In wakeup_callback: suspend aborted
Edit:
Thanks to Javier Gonzalez I was able to print the logcat using:
def logcatcommand():
popen = subprocess.Popen(args="adb logcat", shell=True, stdout=subprocess.PIPE)
return iter(popen.stdout.readline, b"")
def logcatresult():
for line in logcatcommand():
print(line)
But when trying to set the value into a variable or just generally doing anything else (like try to press another button), the only thing I will see is the rainbow spinning wheel from Mac OSX.
Greetz
–
–
I believe this has been asked in a slightly different way here: Python: How to read stdout of subprocess in a nonblocking way
In your code, I would actually use the subprocess module instead of os.popen.
The first time I encountered this issue, I used the select module mentioned in the answer to the question I linked above. A more modern way would be to use asyncio instead. The issue with this is the variety of ways to use asyncio that you can find here in SO. These differ because asyncio has changed significantly in different versions.
EDIT #1
Your underlying problem is that your main thread is blocking. Now you have a loop that never ends inside the logcatresult function, so it never returns. The Tk loop never regains control. That is why it freezes.
Generally speaking, one can do a few things:
do periodical non-blocking calls.
In other words: your main loop needs to periodically call a non-blocking function that returns any output, if there is any.
move any blocking code to a separate thread.
Use asyncio (I will not try to give you an example of this)
#1: The idea of reading the log without blocking goes in line with #1 You would need to implement the logic for the periodic calls.
#2: If the reading loop is moved to a thread, you free up the main thread to do whatever else and be responsive. Then have the logcatresult thread fill your scrolled text. In this case you do not even need what I mentioned above.
The following is a simplified version that might work for you:
from tkinter import *
from tkinter import messagebox
import threading
import subprocess
THREAD = None
PROCESS = None
def read_log(process):
for line in iter(process.stdout.readline, b""):
print(line.decode(), end=''),
def read_log_thread():
""" Button-Event-Handler starting the log reading. """
global THREAD
global PROCESS
PROCESS = subprocess.Popen(["adb logcat"], shell=False, stdout=subprocess.PIPE)
THREAD = threading.Thread(target=read_log, args=(PROCESS,))
THREAD.start()
def hello():
messagebox.showinfo(message='Hello.')
def main():
root = Tk()
Button(master=root, text='Start reading', command=read_log_thread).pack()
buttonX = Button(master=root, text='Hello', command=hello).pack()
root.mainloop()
if PROCESS and PROCESS.poll():
PROCESS.terminate()
THREAD.join()
if __name__ == '__main__':
main()
Things to keep in mind:
This implementation is just a guide. It will have issues (some mentioned below).
I would try not to use globals.
You need to make sure everything is properly cleaned up at the end (all child processes end, threads end, etc)
Nothing prevents you from pressing the button twice, creating multiple (leaked) threads and processes, because the variables are global and no checks are made.
Also note that I used shell=False
so I can PROCESS.terminate()
at the end. Otherwise, your program will not exit until the process ends.
–
–
–
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.