【问题标题】:Python Tkinter account for lost timePython Tkinter 帐户丢失时间
【发布时间】:2020-07-27 03:45:51
【问题描述】:

我已经阅读了已回答的类似问题:

这是我的窗口:

问题是当计数器还有 20% 的时候歌曲结束。我知道原因主要是由于系统调用检查进程是否仍在运行 pgrep ffplay 每秒 10 次。次要原因仅仅是 Python 和 Tkinter 开销。

为了“创可贴”,我使用1.24 deciseconds 而不是1 每十秒的问题,正如我的代码现在所示:

def play_to_end(self):
    ''' 
    Play single song, checking status every decisecond
    Called from:
        self.play_forever() to start a new song
        self.pp_toggle() to restart song after pausing
    '''
    while True:
        if not self.top2_is_active: return          # Play window closed?
        root.update()                               # Process other events
        if self.pp_state is "Paused": 
            time.sleep(.1)                          # Wait until playing
            continue

        PID = os.popen("pgrep ffplay").read()       # Get PID for ffplay
        if len(PID) < 2:                            # Has song ended?
            return                                  # Song has ended

        #self.current_song_time += .1                # Add decisecond
        self.current_song_time += .124              # Add 1.24 deciseconds
                                                    #  compensatation .24
        self.current_progress.set(str('%.1f' % self.current_song_time) + \
                    " seconds of: " + str(self.DurationSecs))
        root.update()                               # Process other events
        root.after(100)                             # Sleep 1 decisecond

这个创可贴修复的问题在于它高度依赖机器。例如,我的机器是 Skylake。此外,它高度依赖于同时运行的其他进程。测试时我的机器负载相对较轻:

我如何以编程方式计算损失的时间,以便准确地增加经过的时间?

也许有更好的方法来简单地查询ffplay 以了解歌曲进度?

顺便说一句(我知道一次问两个问题是不受欢迎的)为什么我不能简单地检查PID 是否为空?我在.read() 之后尝试了.rstrip().strip() 无济于事,检查PID 等于""None。如果ffplayevery 在10 下有一个进程ID,程序就会出现异常。

【问题讨论】:

  • 你在 tkinter 中尝试过.after() 吗?
  • @jizhihaoSAMA 是的,最初的设计是使用.after() 引用各种框架和标签等。我放弃并使用root.after,它似乎总是有效,并且在最后一行代码。不过,这对时间补偿没有帮助......
  • 如何启动ffplay?使用subprocess.Popen()?
  • @acw1668 第一行是os.popen('ffplay -autoexit ' + '"' + self.current_song_path + '"' 续第二行:+ ' -nodisp 2&gt;' + TMP_CURR_SONG + ' &amp;')
  • 尝试使用subprocess.Popen()而不是os.popen()并将stderr重定向到PIPE,然后您可以阅读stderr以更新进度。

标签: python tkinter elapsedtime wall-time


【解决方案1】:

您可以使用subprocess.Popen()执行ffplay并将stderr重定向到PIPE,然后您可以从stderr读取进度并更新进度标签。

下面是一个例子:

import tkinter as tk
import subprocess as subp
import threading

class MediaPlayer(tk.Tk):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.proc = None
        self.protocol('WM_DELETE_WINDOW', self.quit)

    def init_ui(self):
        self.current_progress = tk.StringVar()
        self.progress = tk.Label(self, textvariable=self.current_progress)
        self.progress.grid(row=0, column=0, columnspan=2)

        btn_close = tk.Button(self, text='Stop', width=20, command=self.stop_playing)
        btn_close.grid(row=1, column=0, sticky='ew')

        btn_play = tk.Button(self, text='Play', width=20, command=self.play_song)
        btn_play.grid(row=1, column=1, sticky='ew')

    def play_to_end(self):
        self.proc = subp.Popen(
            ['ffplay', '-nodisp', '-hide_banner', '-autoexit', self.current_song_path],
            stderr=subp.PIPE, bufsize=1, text=1
        )
        duration = ''
        while self.proc.poll() is None:
            msg = self.proc.stderr.readline().strip()
            if msg:
                if msg.startswith('Duration'):
                    duration = msg.split(',')[0].split(': ')[1]
                else:
                    msg = msg.split()[0]
                    if '.' in msg:
                        elapsed = float(msg)
                        mins, secs = divmod(elapsed, 60)
                        hrs, mins = divmod(mins, 60)
                        self.current_progress.set('Play Progress: {:02d}:{:02d}:{:04.1f} / {}'.format(int(hrs), int(mins), secs, duration))
        print('done')
        self.proc = None

    def play_song(self):
        self.current_song_path = '/path/to/song.mp3'
        if self.proc is None:
            threading.Thread(target=self.play_to_end, daemon=True).start()

    def stop_playing(self):
        if self.proc:
            self.proc.terminate()

    def quit(self):
        self.stop_playing()
        self.destroy()

app = MediaPlayer()
app.mainloop()

【讨论】:

  • 哇,真快。您之前是否编写过部分/大部分代码?
  • 我对 SO 中的另一个问题有类似的回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-08
  • 1970-01-01
  • 1970-01-01
  • 2013-10-08
  • 2021-08-14
相关资源
最近更新 更多