#!/usr/bin/env python3 import gi, threading gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GObject def time_to_str(stamp = 0): m = stamp // 60 s = stamp % 60 if m < 10: m = f"0{m}" if s < 10: s = f"0{s}" return f"{m}:{s}" class Slider(Gtk.Scale): def __init__(self, width = 256, height = 5): Gtk.Scale.__init__(self) self.set_name('Slider') self.set_range(0, 100) self.set_size_request(width, height) self.set_draw_value(False) class Timebar(Gtk.Fixed): __gsignals__ = { 'seeked': (GObject.SignalFlags.RUN_FIRST, None, (float,)) } def __init__(self): Gtk.Fixed.__init__(self) self.timer = None self.pending = False self._duration = 0 self._time = 0 self.slider = Slider(272) self.curr = Gtk.Label() self.duration = Gtk.Label() self.curr.set_name('text') self.curr.set_selectable(False) self.duration.set_name('text') self.duration.set_selectable(False) self.duration.set_justify(Gtk.Justification.RIGHT) self.duration.set_xalign(1) self.slider.connect('change-value', self.debounce) self.slider.set_sensitive(False) self.put(self.curr, 3, 0) self.put(self.duration, 233, 0) self.put(self.slider, 0, 12) def update_time(self, curr = 0, duration = 0): self._duration = duration self._time = curr self.slider.set_sensitive(duration > 0) if duration == 0: self.curr.set_text('') self.duration.set_text('') self.slider.set_value(0) else: progress = curr * 100 / duration self.curr.set_text(time_to_str(curr)) self.duration.set_text(time_to_str(duration)) # 优化拖拽进度时的更新 if not self.pending: self.slider.set_value(progress) def on_timeupdate(self): self.pending = False p = self.slider.get_value() time = p * self._duration / 100 self.emit('seeked', time) def debounce(self, slider, a, b): if self.timer is not None: self.timer.cancel() self.pending = True self.timer = threading.Timer(0.2, self.on_timeupdate) self.timer.start()