Progressbar для отображения количества воспроизводимой музыки

#python #python-2.7 #tkinter

#python #python-2.7 #tkinter

Вопрос:

Предположим, я использую mp3play модуль для воспроизведения файлов mp3, и, используя ttk.Progressbar , я хочу показать количество (продолжительность) воспроизводимой музыки. Есть ли какой-либо код для его достижения?

Я также хочу, чтобы таймер a показывал продолжительность воспроизводимой музыки.

 import ttk
import mp3play
self.music = mp3play.load('filename')
self.fr=ttk.Frame()
self.fr.pack(expand=True, fill=BOTH, side=TOP)
self.seek=ttk.Progressbar(self.fr, orient='horizontal', mode='determinate', length=500)
self.seek.place(x=50, y=325)
self.seek.start()
  

Ответ №1:

Просматривая код mp3play модуля, mp3play.load() возвращает AudioClip объект. Этот объект имеет методы seconds() и milliseconds() , которые обеспечивают длину клипа в секундах или миллисекундах соответственно.

Вы можете сохранить время начала воспроизведения и сравнить его с текущим временем и общей длиной клипа, чтобы определить состояние progressbar.

 # assuming time would me measured in milliseconds
start = time()
while playing:
    # progress measured in percentages
    progress = 100 * (time() - start)/ clip.milliseconds()
  

Комментарии:

1. Неработающий человек, не могли бы вы объяснить мне правильный код, и еще 1 вещь mp3play.load.seconds() возвращает неправильную продолжительность времени, например, я тестировал mp3-файл продолжительностью 4: 36 минут, и он вернул 6: 29 минут @Nysten

Ответ №2:

Похоже, что модуль mp3play использует библиотеку Windows winmm. В частности, он использует функцию mciSendString для управления мультимедийной системой.

Одним из вариантов достижения желаемого было бы использовать status команду для периодического извлечения текущей позиции воспроизведения и отображения ее по своему усмотрению.

Казалось бы, наиболее целесообразно изменить обе версии AudioClip класса из библиотеки mp3play.


Изменение библиотеки mp3play

Класс AudioClip в windows.py

Сначала в строке 59 windows.py вы можете увидеть функцию, которая использует команду status

     def _mode(self):
        err, buf = self._mci.directsend('status %s mode' % self._alias)
        return buf
  

Мы можем написать новую функцию на основе этого примера, которая получит текущую позицию воспроизведения:

     def current_position(self):
        err, buf = self._mci.directsend('status %s position' % self._alias)
        return buf
  

Эта функция вернет строку, кодирующую число, представляющее текущую позицию в миллисекундах.

Класс AudioClip в init.py

Следующий шаг — изменить AudioClip класс в строке 12 __init__py, добавив следующую функцию:

     def current_position(self):
        """Get current position of clip in milliseconds."""
        return int(self._clip.current_position())
  

Пример CLI

Теперь мы можем протестировать его с помощью простого скрипта:

 import time
import mp3play

clip = mp3play.load(r'test.mp3')

clip.play()

for i in range(5):
    print "Position: %d / %d" % (clip.current_position(), clip.milliseconds())
    time.sleep(1)

clip.stop()
  

Вывод на консоль выглядит следующим образом:

 >python test.py
Position: 0 / 174392
Position: 905 / 174392
Position: 1906 / 174392
Position: 2906 / 174392
Position: 3907 / 174392
  

Пример графического интерфейса

 import tkinter as tk
from tkinter import ttk

import mp3play

# ================================================================================

def format_duration(ms):
    total_s = ms / 1000
    total_min = total_s / 60
    remain_s = total_s % 60
    return "
:d" % (total_min, remain_s)

# ================================================================================

class SimplePlayer(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        # Variables we use to dynamically update the text of the labels
        self.music_title = tk.StringVar()
        self.music_progress = tk.StringVar()

        self.fr=ttk.Frame()
        self.fr.pack(expand=True, fill=tk.BOTH, side=tk.TOP)

        # Label to display the song title
        self.title_lbl = ttk.Label(self.fr, textvariable = self.music_title)
        self.title_lbl.pack()

        # Playback progress bar
        self.progress_bar = ttk.Progressbar(self.fr, orient='horizontal', mode='determinate', length=500)
        self.progress_bar.pack()

        # Shows the progress numerically
        self.progress_lbl = ttk.Label(self.fr, textvariable = self.music_progress)
        self.progress_lbl.pack()


    def start(self, music_file):
        self.music = mp3play.load(music_file)

        # Update the title
        self.music_title.set(music_file)

        # Start playback
        self.music.play()

        # Start periodically updating the progress bar and progress label
        self.update_progress()

    def update_progress(self):
        pos_ms = self.music.current_position()
        total_ms = self.music.milliseconds()
        progress_percent = pos_ms / float(total_ms) * 100

        # Update the label
        label_text = "%s / %s   (%0.2f %%)" % (format_duration(pos_ms), format_duration(total_ms), progress_percent)
        self.music_progress.set(label_text)

        # Update the progress bar
        self.progress_bar["value"] = progress_percent

        # Schedule next update in 100ms        
        self.after(100, self.update_progress)

# ================================================================================

app = SimplePlayer()

app.start('test.mp3')
app.mainloop()
  

Скриншот:

Скриншот примера запущенного графического интерфейса

Комментарии:

1. не могли бы вы, пожалуйста, объяснить мне, например, код файла и как включить его в progressbar @DanMasek

2. @TigerKing Хорошо, вот так.

3. не могли бы вы, пожалуйста, объяснить «self.music.current_position()», потому что у mp3play нет такого атрибута, как current_position() @DanMasek

4. @TigerKing Вам необходимо изменить библиотеку mp3play и добавить эти функции, как описано в первой части ответа.

5. спасибо за всю вашу помощь, но все же осталась одна ошибка, вы написали строку в своем коде label_text = "%s / %s (%0.2f %%)" % (format_duration(pos_ms), format_duration(total_ms), progress_percent) , но когда я включаю ее в свою программу, python говорит global name 'format_duration' is not defined @DanMasek