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

ADB Logcat is a command where you can display device information from the connected android device. This is realtime and just reports the current status. This only stops when device is disconnected, command is stopped or a certain time is given. I want it to display the device information as long as it's connected. I will add a few sample lines from the logcat in the post. – Gorter-dev Sep 10, 2020 at 12:11 There could be a lot of reasons why as to this has happened, to understand better can you show some more code? of the part where whole function is getting executed? – Delrius Euphoria Sep 10, 2020 at 12:15

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.
  • Note that I did not answer how to actually display the log, but about the freezing. They are actually two different issues. So you decide which one is the actual question. Once you have a non-blocking function, it can add to whatever widget you decide to display the information. – Javier Gonzalez Sep 10, 2020 at 13:04 Thanks a lot, From using the first link you posted using the second answer I am able to print output in the console. But the application still freezes when trying to set the value into the text window. Edit: Or just in general it still freezes, but I am able to print it now. I will add the code in the post – Gorter-dev Sep 10, 2020 at 13:19 I edited the answer again. I would suggest: 1.- change the title (this is about reading stdout from a child process without blocking your GUI). Perhaps somebody has a better title suggestion. 2.- change tags (e.g.: this is not about android) 3.- it is also not related to how you display it in Tk (at the very least, this can be split into two questions, one about the GUI freezing and one about updating the widget). – Javier Gonzalez Sep 11, 2020 at 18:53

    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.