2017-07-20 39 views
0

我的目标是从shell中的youtube-dl获得状态的实时输出,并将其作为tkinter中的标签。这可能是这样做的最糟糕的方式(即使它现在不工作),所以我不介意有人想出一个更好的方法来做到这一点。Python 3 - 在Tkinter中获取cmd的实时输出

我用另一个问题尝试了几件事(Getting realtime output using subprocess),但我没有得到它的工作。

import subprocess 
import sys 
import tkinter as tk 
from threading import Thread 

master = tk.Tk() 

link = "https://www.youtube.com/watch?v=AC-3RJHzEU8" 

def start_thread(): 
    t = Thread(target = download) 
    t.start() 

def download(): 
    global text_var 
    cmd = "youtube-dl -x --audio-format mp3 {0}".format(link) 

    process = subprocess.Popen(cmd, 
       stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell = True) 

    while True: 
     out = process.stdout.read(1) 
     if out == '' and process.poll() != None: 
      break 
     if out != '': 
      text_var.set(sys.stdout.write(out.decode('utf-8'))) 
      sys.stdout.flush() 

text_var = tk.StringVar() 
text_var.set("Status") 
tk.Button(master, text = "Download", command = start_thread).pack() 
tk.Label(master, textvariable = text_var).pack() 

tk.mainloop() 

解决方法:

我需要改变了一点,但它与杜尚Atanackovic的答案工作。 (我也用了Textwidget,因为它的方式比标签更好)

import subprocess 
import tkinter as tk 
from threading import Thread 

master = tk.Tk() 

def sudo(cmd, terminal): 

    p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, universal_newlines=True, shell = True) 
    p.poll() 

    while True: 
     line = p.stdout.readline() 
     terminal.insert(tk.END, line) 
     terminal.see(tk.END) 
     if not line and p.poll is not None: break 

    while True: 
     err = p.stderr.readline() 
     terminal.insert(tk.END, err) 
     terminal.see(tk.END) 
     if not err and p.poll is not None: break 
    terminal.insert(tk.END, '\n Finished download') 

textfield = tk.Text(master, font = "Arial 15") 
textfield.pack() 

link = "https://www.youtube.com/watch?v=s8XIgR5OGJc" 
a = "youtube-dl --extract-audio --audio-format mp3 '{0}'".format(link) 

t = Thread(target = lambda: sudo(a, textfield)) 
t.start() 

tk.mainloop() 
+1

'sys.stdout.write函数(...)“可能不会返回您认为它所做的事情。也许你应该将结果保存到一个变量中,然后在调用'text_var.set'之前检查该变量以验证其中的内容。 –

+0

嘿,谢谢你的回答。仍然没有得到正确的输出。 :/ – HealYouDown

+1

调试的第一步总是验证您的假设。 –

回答

1

您可以通过两种方式,一是为YT较好地解决,这是使用pafy库,pafy的基础是ytdl因此它可以做到因为我做了这一点,但它很慢,我得到了另一种想法,我'现在开发,并与稍加修改就可以用Tkinter的wodget.Second解决方案连接pafys输出

def sudo(self, cmnd, terminal, top): # 1 

     sudo_password = 'your sudo code' + '\n' 
     sudos = ['sudo', '-S'] 

     terminal.delete('1.0', END) 

     for item in eval(cmnd): 
      cmd = sudos + item.split() 

      p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, universal_newlines=True) 
      p.stdin.write(sudo_password) 
      p.poll() 

      while True: 
       line = p.stdout.readline() 
       terminal.insert(END, line) 
       terminal.see(END) 
       top.updates() 
       if not line and p.poll is not None: break 

      while True: 
       err = p.stderr.readline() 
       terminal.insert(END, err) 
       terminal.see(END) 
       top.updates() 
       if not err and p.poll is not None: break 
      terminal.insert(END, '\n * END OF PROCESS *') 

cmnd - list of commands you want to execute, ['youtube-dl some link'], with even one command it should be LIST 

terminal - thats Text widget in my app, but you can use any wiget as well, only you would have to change all lines terminal.insert(END, 'some text') to terminal.insert(0, 'some text') - END to 0 

top is scrollbar container for my app which you can remove if you don't need it 

of course you have to provide root=Tk(), parents and other containers for the terminal widget .